4823 don't open-code NSEC2MSEC and MSEC2NSEC
[illumos-gate.git] / usr / src / cmd / cmd-inet / usr.sbin / ipmpstat / ipmpstat.c
blobc9cbade0794608fef54906dedb1b3f64bdbb0809
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
21 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
25 #include <alloca.h>
26 #include <arpa/inet.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <ipmp_admin.h>
30 #include <ipmp_query.h>
31 #include <libintl.h>
32 #include <libnvpair.h>
33 #include <libsysevent.h>
34 #include <locale.h>
35 #include <netdb.h>
36 #include <ofmt.h>
37 #include <signal.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/sysevent/eventdefs.h>
44 #include <sys/sysevent/ipmp.h>
45 #include <sys/sysmacros.h>
46 #include <sys/termios.h>
47 #include <sys/types.h>
50 * ipmpstat -- display IPMP subsystem status.
52 * This utility makes extensive use of libipmp and IPMP sysevents to gather
53 * and pretty-print the status of the IPMP subsystem. All output formats
54 * except for -p (probe) use libipmp to create a point-in-time snapshot of the
55 * IPMP subsystem (unless the test-special -L flag is used), and then output
56 * the contents of that snapshot in a user-specified manner. Because the
57 * output format and requested fields aren't known until run-time, three sets
58 * of function pointers and two core data structures are used. Specifically:
60 * * The ipmpstat_walker_t function pointers (walk_*) iterate through
61 * all instances of a given IPMP object (group, interface, or address).
62 * At most one ipmpstat_walker_t is used per ipmpstat invocation.
63 * Since target information is included with the interface information,
64 * both -i and -t use the interface walker (walk_if()).
66 * * The ofmt_sfunc_t function pointers (sfunc_*) obtain a given value
67 * for a given IPMP object. Each ofmt_sfunc_t is passed a buffer to
68 * write its result into, the buffer's size, and an ipmpstat_sfunc_arg_t
69 * state structure. The state structure consists of a pointer to the
70 * IPMP object to obtain information from (sa_data), and an open libipmp
71 * handle (sa_ih) which can be used to do additional libipmp queries, if
72 * necessary (e.g., because the object does not have all of the needed
73 * information).
75 * * The ofmt_field_t arrays (*_fields[]) provide the supported fields for
76 * a given output format, along with output formatting information
77 * (e.g., field width) and a pointer to an ofmt_sfunc_t function that
78 * can obtain the value for a given IPMP object. One ofmt_field_t array
79 * is used per ipmpstat invocation, and is passed to ofmt_open() (along
80 * with the output fields and modes requested by the user) to create an
81 * ofmt_t.
83 * * The ofmt_t structure is a handle that tracks all information
84 * related to output formatting and is used by libinetutil`ofmt_print()
85 * (indirectly through our local ofmt_output() utility routine) to
86 * output a single line of information about the provided IPMP object.
88 * * The ipmpstat_cbfunc_t function pointers (*_cbfunc) are called back
89 * by the walkers. They are used both internally to implement nested
90 * walks, and by the ipmpstat output logic to provide the glue between
91 * the IPMP object walkers and the ofmt_output() logic. Usually, a
92 * single line is output for each IPMP object, and thus ofmt_output()
93 * can be directly invoked (see info_output_cbfunc()). However, if
94 * multiple lines need to be output, then a more complex cbfunc is
95 * needed (see targinfo_output_cbfunc()). At most one cbfunc is used
96 * per ipmpstat invocation.
100 * Data type used by the sfunc callbacks to obtain the requested information
101 * from the agreed-upon object.
103 typedef struct ipmpstat_sfunc_arg {
104 ipmp_handle_t sa_ih;
105 void *sa_data;
106 } ipmpstat_sfunc_arg_t;
109 * Function pointers used to iterate through IPMP objects.
111 typedef void ipmpstat_cbfunc_t(ipmp_handle_t, void *, void *);
112 typedef void ipmpstat_walker_t(ipmp_handle_t, ipmpstat_cbfunc_t *, void *);
115 * Data type used to implement nested walks.
117 typedef struct ipmpstat_walkdata {
118 ipmpstat_cbfunc_t *iw_func; /* caller-specified callback */
119 void *iw_funcarg; /* caller-specified arg */
120 } ipmpstat_walkdata_t;
123 * Data type used by enum2str() to map an enumerated value to a string.
125 typedef struct ipmpstat_enum {
126 const char *e_name; /* string */
127 int e_val; /* value */
128 } ipmpstat_enum_t;
131 * Data type used to pass state between probe_output() and probe_event().
133 typedef struct ipmpstat_probe_state {
134 ipmp_handle_t ps_ih; /* open IPMP handle */
135 ofmt_handle_t ps_ofmt; /* open formatted-output handle */
136 } ipmpstat_probe_state_t;
139 * Options that modify the output mode; more than one may be lit.
141 typedef enum {
142 IPMPSTAT_OPT_NUMERIC = 0x1,
143 IPMPSTAT_OPT_PARSABLE = 0x2
144 } ipmpstat_opt_t;
147 * Indices for the FLAGS field of the `-i' output format.
149 enum {
150 IPMPSTAT_IFLAG_INDEX, IPMPSTAT_SFLAG_INDEX, IPMPSTAT_M4FLAG_INDEX,
151 IPMPSTAT_BFLAG_INDEX, IPMPSTAT_M6FLAG_INDEX, IPMPSTAT_DFLAG_INDEX,
152 IPMPSTAT_HFLAG_INDEX, IPMPSTAT_NUM_FLAGS
155 #define IPMPSTAT_NCOL 80
156 #define NS2FLOATMS(ns) (NSEC2MSEC((float)(ns)))
157 #define MS2FLOATSEC(ms) ((float)(ms) / 1000)
159 static const char *progname;
160 static hrtime_t probe_output_start;
161 static ipmpstat_opt_t opt;
162 static ofmt_handle_t ofmt;
163 static ipmpstat_enum_t addr_state[], group_state[], if_state[], if_link[];
164 static ipmpstat_enum_t if_probe[], targ_mode[];
165 static ofmt_field_t addr_fields[], group_fields[], if_fields[];
166 static ofmt_field_t probe_fields[], targ_fields[];
167 static ipmpstat_cbfunc_t walk_addr_cbfunc, walk_if_cbfunc;
168 static ipmpstat_cbfunc_t info_output_cbfunc, targinfo_output_cbfunc;
169 static ipmpstat_walker_t walk_addr, walk_if, walk_group;
171 static int probe_event(sysevent_t *, void *);
172 static void probe_output(ipmp_handle_t, ofmt_handle_t);
173 static void ofmt_output(ofmt_handle_t, ipmp_handle_t, void *);
174 static void enum2str(const ipmpstat_enum_t *, int, char *, uint_t);
175 static void sockaddr2str(const struct sockaddr_storage *, char *, uint_t);
176 static void sighandler(int);
177 static void usage(void);
178 static void die(const char *, ...);
179 static void die_ipmperr(int, const char *, ...);
180 static void warn(const char *, ...);
181 static void warn_ipmperr(int, const char *, ...);
184 main(int argc, char **argv)
186 int c;
187 int err;
188 const char *ofields = NULL;
189 ofmt_status_t ofmterr;
190 ofmt_field_t *fields = NULL;
191 uint_t ofmtflags = 0;
192 ipmp_handle_t ih;
193 ipmp_qcontext_t qcontext = IPMP_QCONTEXT_SNAP;
194 ipmpstat_cbfunc_t *cbfunc;
195 ipmpstat_walker_t *walker;
196 char errbuf[OFMT_BUFSIZE];
198 if ((progname = strrchr(argv[0], '/')) == NULL)
199 progname = argv[0];
200 else
201 progname++;
203 (void) setlocale(LC_ALL, "");
204 (void) textdomain(TEXT_DOMAIN);
206 while ((c = getopt(argc, argv, "nLPo:agipt")) != EOF) {
207 if (fields != NULL && strchr("agipt", c) != NULL)
208 die("only one output format may be specified\n");
210 switch (c) {
211 case 'n':
212 opt |= IPMPSTAT_OPT_NUMERIC;
213 break;
214 case 'L':
215 /* Undocumented option: for testing use ONLY */
216 qcontext = IPMP_QCONTEXT_LIVE;
217 break;
218 case 'P':
219 opt |= IPMPSTAT_OPT_PARSABLE;
220 ofmtflags |= OFMT_PARSABLE;
221 break;
222 case 'o':
223 ofields = optarg;
224 break;
225 case 'a':
226 walker = walk_addr;
227 cbfunc = info_output_cbfunc;
228 fields = addr_fields;
229 break;
230 case 'g':
231 walker = walk_group;
232 cbfunc = info_output_cbfunc;
233 fields = group_fields;
234 break;
235 case 'i':
236 walker = walk_if;
237 cbfunc = info_output_cbfunc;
238 fields = if_fields;
239 break;
240 case 'p':
241 fields = probe_fields;
242 break;
243 case 't':
244 walker = walk_if;
245 cbfunc = targinfo_output_cbfunc;
246 fields = targ_fields;
247 break;
248 default:
249 usage();
250 break;
254 if (argc > optind || fields == NULL)
255 usage();
258 * Open a handle to the formatted output engine.
260 ofmterr = ofmt_open(ofields, fields, ofmtflags, IPMPSTAT_NCOL, &ofmt);
261 if (ofmterr != OFMT_SUCCESS) {
263 * If some fields were badly formed in human-friendly mode, we
264 * emit a warning and continue. Otherwise exit immediately.
266 (void) ofmt_strerror(ofmt, ofmterr, errbuf, sizeof (errbuf));
267 if (ofmterr != OFMT_EBADFIELDS || (opt & IPMPSTAT_OPT_PARSABLE))
268 die("%s\n", errbuf);
269 else
270 warn("%s\n", errbuf);
274 * Obtain the window size and monitor changes to the size. This data
275 * is used to redisplay the output headers when necessary.
277 (void) sigset(SIGWINCH, sighandler);
279 if ((err = ipmp_open(&ih)) != IPMP_SUCCESS)
280 die_ipmperr(err, "cannot create IPMP handle");
282 if (ipmp_ping_daemon(ih) != IPMP_SUCCESS)
283 die("cannot contact in.mpathd(1M) -- is IPMP in use?\n");
286 * If we've been asked to display probes, then call the probe output
287 * function. Otherwise, snapshot IPMP state (or use live state) and
288 * invoke the specified walker with the specified callback function.
290 if (fields == probe_fields) {
291 probe_output(ih, ofmt);
292 } else {
293 if ((err = ipmp_setqcontext(ih, qcontext)) != IPMP_SUCCESS) {
294 if (qcontext == IPMP_QCONTEXT_SNAP)
295 die_ipmperr(err, "cannot snapshot IPMP state");
296 else
297 die_ipmperr(err, "cannot use live IPMP state");
299 (*walker)(ih, cbfunc, ofmt);
302 ofmt_close(ofmt);
303 ipmp_close(ih);
305 return (EXIT_SUCCESS);
309 * Walks all IPMP groups on the system and invokes `cbfunc' on each, passing
310 * it `ih', the ipmp_groupinfo_t pointer, and `arg'.
312 static void
313 walk_group(ipmp_handle_t ih, ipmpstat_cbfunc_t *cbfunc, void *arg)
315 int err;
316 uint_t i;
317 ipmp_groupinfo_t *grinfop;
318 ipmp_grouplist_t *grlistp;
320 if ((err = ipmp_getgrouplist(ih, &grlistp)) != IPMP_SUCCESS)
321 die_ipmperr(err, "cannot get IPMP group list");
323 for (i = 0; i < grlistp->gl_ngroup; i++) {
324 err = ipmp_getgroupinfo(ih, grlistp->gl_groups[i], &grinfop);
325 if (err != IPMP_SUCCESS) {
326 warn_ipmperr(err, "cannot get info for group `%s'",
327 grlistp->gl_groups[i]);
328 continue;
330 (*cbfunc)(ih, grinfop, arg);
331 ipmp_freegroupinfo(grinfop);
334 ipmp_freegrouplist(grlistp);
338 * Walks all IPMP interfaces on the system and invokes `cbfunc' on each,
339 * passing it `ih', the ipmp_ifinfo_t pointer, and `arg'.
341 static void
342 walk_if(ipmp_handle_t ih, ipmpstat_cbfunc_t *cbfunc, void *arg)
344 ipmpstat_walkdata_t iw = { cbfunc, arg };
346 walk_group(ih, walk_if_cbfunc, &iw);
350 * Walks all IPMP data addresses on the system and invokes `cbfunc' on each.
351 * passing it `ih', the ipmp_addrinfo_t pointer, and `arg'.
353 static void
354 walk_addr(ipmp_handle_t ih, ipmpstat_cbfunc_t *cbfunc, void *arg)
356 ipmpstat_walkdata_t iw = { cbfunc, arg };
358 walk_group(ih, walk_addr_cbfunc, &iw);
362 * Nested walker callback function for walk_if().
364 static void
365 walk_if_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
367 int err;
368 uint_t i;
369 ipmp_groupinfo_t *grinfop = infop;
370 ipmp_ifinfo_t *ifinfop;
371 ipmp_iflist_t *iflistp = grinfop->gr_iflistp;
372 ipmpstat_walkdata_t *iwp = arg;
374 for (i = 0; i < iflistp->il_nif; i++) {
375 err = ipmp_getifinfo(ih, iflistp->il_ifs[i], &ifinfop);
376 if (err != IPMP_SUCCESS) {
377 warn_ipmperr(err, "cannot get info for interface `%s'",
378 iflistp->il_ifs[i]);
379 continue;
381 (*iwp->iw_func)(ih, ifinfop, iwp->iw_funcarg);
382 ipmp_freeifinfo(ifinfop);
387 * Nested walker callback function for walk_addr().
389 static void
390 walk_addr_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
392 int err;
393 uint_t i;
394 ipmp_groupinfo_t *grinfop = infop;
395 ipmp_addrinfo_t *adinfop;
396 ipmp_addrlist_t *adlistp = grinfop->gr_adlistp;
397 ipmpstat_walkdata_t *iwp = arg;
398 char addr[INET6_ADDRSTRLEN];
399 struct sockaddr_storage *addrp;
401 for (i = 0; i < adlistp->al_naddr; i++) {
402 addrp = &adlistp->al_addrs[i];
403 err = ipmp_getaddrinfo(ih, grinfop->gr_name, addrp, &adinfop);
404 if (err != IPMP_SUCCESS) {
405 sockaddr2str(addrp, addr, sizeof (addr));
406 warn_ipmperr(err, "cannot get info for `%s'", addr);
407 continue;
409 (*iwp->iw_func)(ih, adinfop, iwp->iw_funcarg);
410 ipmp_freeaddrinfo(adinfop);
414 static boolean_t
415 sfunc_nvwarn(const char *nvname)
417 warn("cannot retrieve %s\n", nvname);
418 return (B_FALSE);
421 static boolean_t
422 sfunc_addr_address(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
424 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
425 ipmp_addrinfo_t *adinfop = arg->sa_data;
427 sockaddr2str(&adinfop->ad_addr, buf, bufsize);
428 return (B_TRUE);
431 static boolean_t
432 sfunc_addr_group(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
434 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
435 int err;
436 ipmp_addrinfo_t *adinfop = arg->sa_data;
437 ipmp_groupinfo_t *grinfop;
439 err = ipmp_getgroupinfo(arg->sa_ih, adinfop->ad_group, &grinfop);
440 if (err != IPMP_SUCCESS) {
441 warn_ipmperr(err, "cannot get info for group `%s'",
442 adinfop->ad_group);
443 return (B_FALSE);
445 (void) strlcpy(buf, grinfop->gr_ifname, bufsize);
446 ipmp_freegroupinfo(grinfop);
447 return (B_TRUE);
450 static boolean_t
451 sfunc_addr_state(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
453 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
454 ipmp_addrinfo_t *adinfop = arg->sa_data;
456 enum2str(addr_state, adinfop->ad_state, buf, bufsize);
457 return (B_TRUE);
460 static boolean_t
461 sfunc_addr_inbound(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
463 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
464 ipmp_addrinfo_t *adinfop = arg->sa_data;
466 (void) strlcpy(buf, adinfop->ad_binding, bufsize);
467 return (B_TRUE);
470 static boolean_t
471 sfunc_addr_outbound(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
473 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
474 int err;
475 uint_t i, nactive = 0;
476 ipmp_ifinfo_t *ifinfop;
477 ipmp_iflist_t *iflistp;
478 ipmp_addrinfo_t *adinfop = arg->sa_data;
479 ipmp_groupinfo_t *grinfop;
481 if (adinfop->ad_state == IPMP_ADDR_DOWN)
482 return (B_TRUE);
485 * If there's no inbound interface for this address, there can't
486 * be any outbound traffic.
488 if (adinfop->ad_binding[0] == '\0')
489 return (B_TRUE);
492 * The address can use any active interface in the group, so
493 * obtain all of those.
495 err = ipmp_getgroupinfo(arg->sa_ih, adinfop->ad_group, &grinfop);
496 if (err != IPMP_SUCCESS) {
497 warn_ipmperr(err, "cannot get info for group `%s'",
498 adinfop->ad_group);
499 return (B_FALSE);
502 iflistp = grinfop->gr_iflistp;
503 for (i = 0; i < iflistp->il_nif; i++) {
504 err = ipmp_getifinfo(arg->sa_ih, iflistp->il_ifs[i], &ifinfop);
505 if (err != IPMP_SUCCESS) {
506 warn_ipmperr(err, "cannot get info for interface `%s'",
507 iflistp->il_ifs[i]);
508 continue;
511 if (ifinfop->if_flags & IPMP_IFFLAG_ACTIVE) {
512 if (nactive++ != 0)
513 (void) strlcat(buf, " ", bufsize);
514 (void) strlcat(buf, ifinfop->if_name, bufsize);
516 ipmp_freeifinfo(ifinfop);
518 ipmp_freegroupinfo(grinfop);
519 return (B_TRUE);
522 static boolean_t
523 sfunc_group_name(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
525 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
526 ipmp_groupinfo_t *grinfop = arg->sa_data;
528 (void) strlcpy(buf, grinfop->gr_name, bufsize);
529 return (B_TRUE);
532 static boolean_t
533 sfunc_group_ifname(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
535 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
536 ipmp_groupinfo_t *grinfop = arg->sa_data;
538 (void) strlcpy(buf, grinfop->gr_ifname, bufsize);
539 return (B_TRUE);
542 static boolean_t
543 sfunc_group_state(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
545 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
546 ipmp_groupinfo_t *grinfop = arg->sa_data;
548 enum2str(group_state, grinfop->gr_state, buf, bufsize);
549 return (B_TRUE);
552 static boolean_t
553 sfunc_group_fdt(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
555 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
556 ipmp_groupinfo_t *grinfop = arg->sa_data;
558 if (grinfop->gr_fdt == 0)
559 return (B_TRUE);
561 (void) snprintf(buf, bufsize, "%.2fs", MS2FLOATSEC(grinfop->gr_fdt));
562 return (B_TRUE);
565 static boolean_t
566 sfunc_group_interfaces(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
568 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
569 int err;
570 uint_t i;
571 char *active, *inactive, *unusable;
572 uint_t nactive = 0, ninactive = 0, nunusable = 0;
573 ipmp_groupinfo_t *grinfop = arg->sa_data;
574 ipmp_iflist_t *iflistp = grinfop->gr_iflistp;
575 ipmp_ifinfo_t *ifinfop;
577 active = alloca(bufsize);
578 active[0] = '\0';
579 inactive = alloca(bufsize);
580 inactive[0] = '\0';
581 unusable = alloca(bufsize);
582 unusable[0] = '\0';
584 for (i = 0; i < iflistp->il_nif; i++) {
585 err = ipmp_getifinfo(arg->sa_ih, iflistp->il_ifs[i], &ifinfop);
586 if (err != IPMP_SUCCESS) {
587 warn_ipmperr(err, "cannot get info for interface `%s'",
588 iflistp->il_ifs[i]);
589 continue;
592 if (ifinfop->if_flags & IPMP_IFFLAG_ACTIVE) {
593 if (nactive++ != 0)
594 (void) strlcat(active, " ", bufsize);
595 (void) strlcat(active, ifinfop->if_name, bufsize);
596 } else if (ifinfop->if_flags & IPMP_IFFLAG_INACTIVE) {
597 if (ninactive++ != 0)
598 (void) strlcat(inactive, " ", bufsize);
599 (void) strlcat(inactive, ifinfop->if_name, bufsize);
600 } else {
601 if (nunusable++ != 0)
602 (void) strlcat(unusable, " ", bufsize);
603 (void) strlcat(unusable, ifinfop->if_name, bufsize);
606 ipmp_freeifinfo(ifinfop);
609 (void) strlcpy(buf, active, bufsize);
611 if (ninactive > 0) {
612 if (nactive != 0)
613 (void) strlcat(buf, " ", bufsize);
615 (void) strlcat(buf, "(", bufsize);
616 (void) strlcat(buf, inactive, bufsize);
617 (void) strlcat(buf, ")", bufsize);
620 if (nunusable > 0) {
621 if (nactive + ninactive != 0)
622 (void) strlcat(buf, " ", bufsize);
624 (void) strlcat(buf, "[", bufsize);
625 (void) strlcat(buf, unusable, bufsize);
626 (void) strlcat(buf, "]", bufsize);
628 return (B_TRUE);
631 static boolean_t
632 sfunc_if_name(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
634 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
635 ipmp_ifinfo_t *ifinfop = arg->sa_data;
637 (void) strlcpy(buf, ifinfop->if_name, bufsize);
638 return (B_TRUE);
641 static boolean_t
642 sfunc_if_active(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
644 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
645 ipmp_ifinfo_t *ifinfop = arg->sa_data;
647 if (ifinfop->if_flags & IPMP_IFFLAG_ACTIVE)
648 (void) strlcpy(buf, "yes", bufsize);
649 else
650 (void) strlcpy(buf, "no", bufsize);
651 return (B_TRUE);
654 static boolean_t
655 sfunc_if_group(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
657 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
658 int err;
659 ipmp_ifinfo_t *ifinfop = arg->sa_data;
660 ipmp_groupinfo_t *grinfop;
662 err = ipmp_getgroupinfo(arg->sa_ih, ifinfop->if_group, &grinfop);
663 if (err != IPMP_SUCCESS) {
664 warn_ipmperr(err, "cannot get info for group `%s'",
665 ifinfop->if_group);
666 return (B_TRUE);
669 (void) strlcpy(buf, grinfop->gr_ifname, bufsize);
670 ipmp_freegroupinfo(grinfop);
671 return (B_TRUE);
674 static boolean_t
675 sfunc_if_flags(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
677 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
678 int err;
679 ipmp_ifinfo_t *ifinfop = arg->sa_data;
680 ipmp_groupinfo_t *grinfop;
682 assert(bufsize > IPMPSTAT_NUM_FLAGS);
684 (void) memset(buf, '-', IPMPSTAT_NUM_FLAGS);
685 buf[IPMPSTAT_NUM_FLAGS] = '\0';
687 if (ifinfop->if_type == IPMP_IF_STANDBY)
688 buf[IPMPSTAT_SFLAG_INDEX] = 's';
690 if (ifinfop->if_flags & IPMP_IFFLAG_INACTIVE)
691 buf[IPMPSTAT_IFLAG_INDEX] = 'i';
693 if (ifinfop->if_flags & IPMP_IFFLAG_DOWN)
694 buf[IPMPSTAT_DFLAG_INDEX] = 'd';
696 if (ifinfop->if_flags & IPMP_IFFLAG_HWADDRDUP)
697 buf[IPMPSTAT_HFLAG_INDEX] = 'h';
699 err = ipmp_getgroupinfo(arg->sa_ih, ifinfop->if_group, &grinfop);
700 if (err != IPMP_SUCCESS) {
701 warn_ipmperr(err, "cannot get broadcast/multicast info for "
702 "group `%s'", ifinfop->if_group);
703 return (B_TRUE);
706 if (strcmp(grinfop->gr_m4ifname, ifinfop->if_name) == 0)
707 buf[IPMPSTAT_M4FLAG_INDEX] = 'm';
709 if (strcmp(grinfop->gr_m6ifname, ifinfop->if_name) == 0)
710 buf[IPMPSTAT_M6FLAG_INDEX] = 'M';
712 if (strcmp(grinfop->gr_bcifname, ifinfop->if_name) == 0)
713 buf[IPMPSTAT_BFLAG_INDEX] = 'b';
715 ipmp_freegroupinfo(grinfop);
716 return (B_TRUE);
719 static boolean_t
720 sfunc_if_link(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
722 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
723 ipmp_ifinfo_t *ifinfop = arg->sa_data;
725 enum2str(if_link, ifinfop->if_linkstate, buf, bufsize);
726 return (B_TRUE);
729 static boolean_t
730 sfunc_if_probe(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
732 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
733 ipmp_ifinfo_t *ifinfop = arg->sa_data;
735 enum2str(if_probe, ifinfop->if_probestate, buf, bufsize);
736 return (B_TRUE);
739 static boolean_t
740 sfunc_if_state(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
742 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
743 ipmp_ifinfo_t *ifinfop = arg->sa_data;
745 enum2str(if_state, ifinfop->if_state, buf, bufsize);
746 return (B_TRUE);
749 static boolean_t
750 sfunc_probe_id(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
752 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
753 uint32_t probe_id;
754 nvlist_t *nvl = arg->sa_data;
756 if (nvlist_lookup_uint32(nvl, IPMP_PROBE_ID, &probe_id) != 0)
757 return (sfunc_nvwarn("IPMP_PROBE_ID"));
759 (void) snprintf(buf, bufsize, "%u", probe_id);
760 return (B_TRUE);
763 static boolean_t
764 sfunc_probe_ifname(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
766 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
767 char *ifname;
768 nvlist_t *nvl = arg->sa_data;
770 if (nvlist_lookup_string(nvl, IPMP_IF_NAME, &ifname) != 0)
771 return (sfunc_nvwarn("IPMP_IF_NAME"));
773 (void) strlcpy(buf, ifname, bufsize);
774 return (B_TRUE);
777 static boolean_t
778 sfunc_probe_time(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
780 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
781 hrtime_t start;
782 nvlist_t *nvl = arg->sa_data;
784 if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_START_TIME, &start) != 0)
785 return (sfunc_nvwarn("IPMP_PROBE_START_TIME"));
787 (void) snprintf(buf, bufsize, "%.2fs",
788 (float)(start - probe_output_start) / NANOSEC);
789 return (B_TRUE);
792 static boolean_t
793 sfunc_probe_target(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
795 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
796 uint_t nelem;
797 struct sockaddr_storage *target;
798 nvlist_t *nvl = arg->sa_data;
800 if (nvlist_lookup_byte_array(nvl, IPMP_PROBE_TARGET,
801 (uchar_t **)&target, &nelem) != 0)
802 return (sfunc_nvwarn("IPMP_PROBE_TARGET"));
804 sockaddr2str(target, buf, bufsize);
805 return (B_TRUE);
808 static boolean_t
809 sfunc_probe_rtt(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
811 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
812 hrtime_t start, ackproc;
813 nvlist_t *nvl = arg->sa_data;
814 uint32_t state;
816 if (nvlist_lookup_uint32(nvl, IPMP_PROBE_STATE, &state) != 0)
817 return (sfunc_nvwarn("IPMP_PROBE_STATE"));
819 if (state != IPMP_PROBE_ACKED)
820 return (B_TRUE);
822 if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_START_TIME, &start) != 0)
823 return (sfunc_nvwarn("IPMP_PROBE_START_TIME"));
825 if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_ACKPROC_TIME, &ackproc) != 0)
826 return (sfunc_nvwarn("IPMP_PROBE_ACKPROC_TIME"));
828 (void) snprintf(buf, bufsize, "%.2fms", NS2FLOATMS(ackproc - start));
829 return (B_TRUE);
832 static boolean_t
833 sfunc_probe_netrtt(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
835 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
836 hrtime_t sent, ackrecv;
837 nvlist_t *nvl = arg->sa_data;
838 uint32_t state;
840 if (nvlist_lookup_uint32(nvl, IPMP_PROBE_STATE, &state) != 0)
841 return (sfunc_nvwarn("IPMP_PROBE_STATE"));
843 if (state != IPMP_PROBE_ACKED)
844 return (B_TRUE);
846 if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_SENT_TIME, &sent) != 0)
847 return (sfunc_nvwarn("IPMP_PROBE_SENT_TIME"));
849 if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_ACKRECV_TIME, &ackrecv) != 0)
850 return (sfunc_nvwarn("IPMP_PROBE_ACKRECV_TIME"));
852 (void) snprintf(buf, bufsize, "%.2fms", NS2FLOATMS(ackrecv - sent));
853 return (B_TRUE);
856 static boolean_t
857 sfunc_probe_rttavg(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
859 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
860 int64_t rttavg;
861 nvlist_t *nvl = arg->sa_data;
863 if (nvlist_lookup_int64(nvl, IPMP_PROBE_TARGET_RTTAVG, &rttavg) != 0)
864 return (sfunc_nvwarn("IPMP_PROBE_TARGET_RTTAVG"));
866 if (rttavg != 0)
867 (void) snprintf(buf, bufsize, "%.2fms", NS2FLOATMS(rttavg));
868 return (B_TRUE);
871 static boolean_t
872 sfunc_probe_rttdev(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
874 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
875 int64_t rttdev;
876 nvlist_t *nvl = arg->sa_data;
878 if (nvlist_lookup_int64(nvl, IPMP_PROBE_TARGET_RTTDEV, &rttdev) != 0)
879 return (sfunc_nvwarn("IPMP_PROBE_TARGET_RTTDEV"));
881 if (rttdev != 0)
882 (void) snprintf(buf, bufsize, "%.2fms", NS2FLOATMS(rttdev));
883 return (B_TRUE);
886 /* ARGSUSED */
887 static void
888 probe_enabled_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
890 uint_t *nenabledp = arg;
891 ipmp_ifinfo_t *ifinfop = infop;
893 if (ifinfop->if_probestate != IPMP_PROBE_DISABLED)
894 (*nenabledp)++;
897 static void
898 probe_output(ipmp_handle_t ih, ofmt_handle_t ofmt)
900 char sub[MAX_SUBID_LEN];
901 evchan_t *evch;
902 ipmpstat_probe_state_t ps = { ih, ofmt };
903 uint_t nenabled = 0;
906 * Check if any interfaces are enabled for probe-based failure
907 * detection. If not, immediately fail.
909 walk_if(ih, probe_enabled_cbfunc, &nenabled);
910 if (nenabled == 0)
911 die("probe-based failure detection is disabled\n");
913 probe_output_start = gethrtime();
916 * Unfortunately, until 4791900 is fixed, only privileged processes
917 * can bind and thus receive sysevents.
919 errno = sysevent_evc_bind(IPMP_EVENT_CHAN, &evch, EVCH_CREAT);
920 if (errno != 0) {
921 if (errno == EPERM)
922 die("insufficient privileges for -p\n");
923 die("sysevent_evc_bind to channel %s failed", IPMP_EVENT_CHAN);
927 * The subscriber must be unique in order for sysevent_evc_subscribe()
928 * to succeed, so combine our name and pid.
930 (void) snprintf(sub, sizeof (sub), "%d-%s", getpid(), progname);
932 errno = sysevent_evc_subscribe(evch, sub, EC_IPMP, probe_event, &ps, 0);
933 if (errno != 0)
934 die("sysevent_evc_subscribe for class %s failed", EC_IPMP);
936 for (;;)
937 (void) pause();
940 static int
941 probe_event(sysevent_t *ev, void *arg)
943 nvlist_t *nvl;
944 uint32_t state;
945 uint32_t version;
946 ipmpstat_probe_state_t *psp = arg;
948 if (strcmp(sysevent_get_subclass_name(ev), ESC_IPMP_PROBE_STATE) != 0)
949 return (0);
951 if (sysevent_get_attr_list(ev, &nvl) != 0) {
952 warn("sysevent_get_attr_list failed; dropping event");
953 return (0);
956 if (nvlist_lookup_uint32(nvl, IPMP_EVENT_VERSION, &version) != 0) {
957 warn("dropped event with no IPMP_EVENT_VERSION\n");
958 goto out;
961 if (version != IPMP_EVENT_CUR_VERSION) {
962 warn("dropped event with unsupported IPMP_EVENT_VERSION %d\n",
963 version);
964 goto out;
967 if (nvlist_lookup_uint32(nvl, IPMP_PROBE_STATE, &state) != 0) {
968 warn("dropped event with no IPMP_PROBE_STATE\n");
969 goto out;
972 if (state == IPMP_PROBE_ACKED || state == IPMP_PROBE_LOST)
973 ofmt_output(psp->ps_ofmt, psp->ps_ih, nvl);
974 out:
975 nvlist_free(nvl);
976 return (0);
979 static boolean_t
980 sfunc_targ_ifname(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
982 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
983 ipmp_targinfo_t *targinfop = arg->sa_data;
985 (void) strlcpy(buf, targinfop->it_name, bufsize);
986 return (B_TRUE);
989 static boolean_t
990 sfunc_targ_mode(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
992 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
993 ipmp_targinfo_t *targinfop = arg->sa_data;
995 enum2str(targ_mode, targinfop->it_targmode, buf, bufsize);
996 return (B_TRUE);
999 static boolean_t
1000 sfunc_targ_testaddr(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
1002 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
1003 ipmp_targinfo_t *targinfop = arg->sa_data;
1005 if (targinfop->it_targmode != IPMP_TARG_DISABLED)
1006 sockaddr2str(&targinfop->it_testaddr, buf, bufsize);
1007 return (B_TRUE);
1010 static boolean_t
1011 sfunc_targ_targets(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
1013 ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
1014 uint_t i;
1015 char *targname = alloca(bufsize);
1016 ipmp_targinfo_t *targinfop = arg->sa_data;
1017 ipmp_addrlist_t *targlistp = targinfop->it_targlistp;
1019 for (i = 0; i < targlistp->al_naddr; i++) {
1020 sockaddr2str(&targlistp->al_addrs[i], targname, bufsize);
1021 (void) strlcat(buf, targname, bufsize);
1022 if ((i + 1) < targlistp->al_naddr)
1023 (void) strlcat(buf, " ", bufsize);
1025 return (B_TRUE);
1028 static void
1029 info_output_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
1031 ofmt_output(arg, ih, infop);
1034 static void
1035 targinfo_output_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
1037 ipmp_ifinfo_t *ifinfop = infop;
1038 ipmp_if_targmode_t targmode4 = ifinfop->if_targinfo4.it_targmode;
1039 ipmp_if_targmode_t targmode6 = ifinfop->if_targinfo6.it_targmode;
1042 * Usually, either IPv4 or IPv6 probing will be enabled, but the admin
1043 * may enable both. If only one is enabled, omit the other one so as
1044 * to not encourage the admin to enable both. If neither is enabled,
1045 * we still print one just so the admin can see a MODE of "disabled".
1047 if (targmode4 != IPMP_TARG_DISABLED || targmode6 == IPMP_TARG_DISABLED)
1048 ofmt_output(arg, ih, &ifinfop->if_targinfo4);
1049 if (targmode6 != IPMP_TARG_DISABLED)
1050 ofmt_output(arg, ih, &ifinfop->if_targinfo6);
1054 * Outputs one row of values. The values to output are obtained through the
1055 * callback function pointers. The actual values are computed from the `ih'
1056 * and `arg' structures passed to the callback function.
1058 static void
1059 ofmt_output(const ofmt_handle_t ofmt, ipmp_handle_t ih, void *arg)
1061 ipmpstat_sfunc_arg_t sfunc_arg;
1063 sfunc_arg.sa_ih = ih;
1064 sfunc_arg.sa_data = arg;
1065 ofmt_print(ofmt, &sfunc_arg);
1069 * Uses `enums' to map `enumval' to a string, and stores at most `bufsize'
1070 * bytes of that string into `buf'.
1072 static void
1073 enum2str(const ipmpstat_enum_t *enums, int enumval, char *buf, uint_t bufsize)
1075 const ipmpstat_enum_t *enump;
1077 for (enump = enums; enump->e_name != NULL; enump++) {
1078 if (enump->e_val == enumval) {
1079 (void) strlcpy(buf, enump->e_name, bufsize);
1080 return;
1083 (void) snprintf(buf, bufsize, "<%d>", enumval);
1087 * Stores the stringified value of the sockaddr_storage pointed to by `ssp'
1088 * into at most `bufsize' bytes of `buf'.
1090 static void
1091 sockaddr2str(const struct sockaddr_storage *ssp, char *buf, uint_t bufsize)
1093 int flags = NI_NOFQDN;
1094 socklen_t socklen;
1095 struct sockaddr *sp = (struct sockaddr *)ssp;
1098 * Sadly, getnameinfo() does not allow the socklen to be oversized for
1099 * a given family -- so we must determine the exact size to pass to it.
1101 switch (ssp->ss_family) {
1102 case AF_INET:
1103 socklen = sizeof (struct sockaddr_in);
1104 break;
1105 case AF_INET6:
1106 socklen = sizeof (struct sockaddr_in6);
1107 break;
1108 default:
1109 (void) strlcpy(buf, "?", bufsize);
1110 return;
1113 if (opt & IPMPSTAT_OPT_NUMERIC)
1114 flags |= NI_NUMERICHOST;
1116 (void) getnameinfo(sp, socklen, buf, bufsize, NULL, 0, flags);
1119 static void
1120 sighandler(int sig)
1122 assert(sig == SIGWINCH);
1124 ofmt_update_winsize(ofmt);
1127 static void
1128 usage(void)
1130 const char *argstr = gettext("[-n] [-o <field> [-P]] -a|-g|-i|-p|-t");
1132 (void) fprintf(stderr, gettext("usage: %s %s\n"), progname, argstr);
1133 (void) fprintf(stderr, gettext("\n"
1134 " output modes:\t -a display IPMP data address information\n"
1135 "\t\t -g display IPMP group information\n"
1136 "\t\t -i display IPMP-related IP interface information\n"
1137 "\t\t -p display IPMP probe information\n"
1138 "\t\t -t display IPMP target information\n\n"
1139 " options:\t -n display IP addresses numerically\n"
1140 "\t\t -o display only the specified fields, in order\n"
1141 "\t\t -P display using parsable output mode\n"));
1143 exit(EXIT_FAILURE);
1146 /* PRINTFLIKE1 */
1147 static void
1148 warn(const char *format, ...)
1150 va_list alist;
1151 int error = errno;
1153 format = gettext(format);
1154 (void) fprintf(stderr, gettext("%s: warning: "), progname);
1156 va_start(alist, format);
1157 (void) vfprintf(stderr, format, alist);
1158 va_end(alist);
1160 if (strchr(format, '\n') == NULL)
1161 (void) fprintf(stderr, ": %s\n", strerror(error));
1164 /* PRINTFLIKE2 */
1165 static void
1166 warn_ipmperr(int ipmperr, const char *format, ...)
1168 va_list alist;
1170 format = gettext(format);
1171 (void) fprintf(stderr, gettext("%s: warning: "), progname);
1173 va_start(alist, format);
1174 (void) vfprintf(stderr, format, alist);
1175 va_end(alist);
1177 (void) fprintf(stderr, ": %s\n", ipmp_errmsg(ipmperr));
1180 /* PRINTFLIKE1 */
1181 static void
1182 die(const char *format, ...)
1184 va_list alist;
1185 int error = errno;
1187 format = gettext(format);
1188 (void) fprintf(stderr, "%s: ", progname);
1190 va_start(alist, format);
1191 (void) vfprintf(stderr, format, alist);
1192 va_end(alist);
1194 if (strchr(format, '\n') == NULL)
1195 (void) fprintf(stderr, ": %s\n", strerror(error));
1197 exit(EXIT_FAILURE);
1200 /* PRINTFLIKE2 */
1201 static void
1202 die_ipmperr(int ipmperr, const char *format, ...)
1204 va_list alist;
1206 format = gettext(format);
1207 (void) fprintf(stderr, "%s: ", progname);
1209 va_start(alist, format);
1210 (void) vfprintf(stderr, format, alist);
1211 va_end(alist);
1212 (void) fprintf(stderr, ": %s\n", ipmp_errmsg(ipmperr));
1214 exit(EXIT_FAILURE);
1217 static ofmt_field_t addr_fields[] = {
1218 { "ADDRESS", 26, 0, sfunc_addr_address },
1219 { "STATE", 7, 0, sfunc_addr_state },
1220 { "GROUP", 12, 0, sfunc_addr_group },
1221 { "INBOUND", 12, 0, sfunc_addr_inbound },
1222 { "OUTBOUND", 23, 0, sfunc_addr_outbound },
1223 { NULL, 0, 0, NULL }
1226 static ofmt_field_t group_fields[] = {
1227 { "GROUP", 12, 0, sfunc_group_ifname },
1228 { "GROUPNAME", 12, 0, sfunc_group_name },
1229 { "STATE", 10, 0, sfunc_group_state },
1230 { "FDT", 10, 0, sfunc_group_fdt },
1231 { "INTERFACES", 30, 0, sfunc_group_interfaces },
1232 { NULL, 0, 0, NULL }
1235 static ofmt_field_t if_fields[] = {
1236 { "INTERFACE", 12, 0, sfunc_if_name },
1237 { "ACTIVE", 8, 0, sfunc_if_active },
1238 { "GROUP", 12, 0, sfunc_if_group },
1239 { "FLAGS", 10, 0, sfunc_if_flags },
1240 { "LINK", 10, 0, sfunc_if_link },
1241 { "PROBE", 10, 0, sfunc_if_probe },
1242 { "STATE", 10, 0, sfunc_if_state },
1243 { NULL, 0, 0, NULL }
1246 static ofmt_field_t probe_fields[] = {
1247 { "TIME", 10, 0, sfunc_probe_time },
1248 { "INTERFACE", 12, 0, sfunc_probe_ifname },
1249 { "PROBE", 7, 0, sfunc_probe_id },
1250 { "NETRTT", 10, 0, sfunc_probe_netrtt },
1251 { "RTT", 10, 0, sfunc_probe_rtt },
1252 { "RTTAVG", 10, 0, sfunc_probe_rttavg },
1253 { "TARGET", 20, 0, sfunc_probe_target },
1254 { "RTTDEV", 10, 0, sfunc_probe_rttdev },
1255 { NULL, 0, 0, NULL }
1258 static ofmt_field_t targ_fields[] = {
1259 { "INTERFACE", 12, 0, sfunc_targ_ifname },
1260 { "MODE", 10, 0, sfunc_targ_mode },
1261 { "TESTADDR", 20, 0, sfunc_targ_testaddr },
1262 { "TARGETS", 38, 0, sfunc_targ_targets },
1263 { NULL, 0, 0, NULL }
1266 static ipmpstat_enum_t addr_state[] = {
1267 { "up", IPMP_ADDR_UP },
1268 { "down", IPMP_ADDR_DOWN },
1269 { NULL, 0 }
1272 static ipmpstat_enum_t group_state[] = {
1273 { "ok", IPMP_GROUP_OK },
1274 { "failed", IPMP_GROUP_FAILED },
1275 { "degraded", IPMP_GROUP_DEGRADED },
1276 { NULL, 0 }
1279 static ipmpstat_enum_t if_link[] = {
1280 { "up", IPMP_LINK_UP },
1281 { "down", IPMP_LINK_DOWN },
1282 { "unknown", IPMP_LINK_UNKNOWN },
1283 { NULL, 0 }
1286 static ipmpstat_enum_t if_probe[] = {
1287 { "ok", IPMP_PROBE_OK },
1288 { "failed", IPMP_PROBE_FAILED },
1289 { "unknown", IPMP_PROBE_UNKNOWN },
1290 { "disabled", IPMP_PROBE_DISABLED },
1291 { NULL, 0 }
1294 static ipmpstat_enum_t if_state[] = {
1295 { "ok", IPMP_IF_OK },
1296 { "failed", IPMP_IF_FAILED },
1297 { "unknown", IPMP_IF_UNKNOWN },
1298 { "offline", IPMP_IF_OFFLINE },
1299 { NULL, 0 }
1302 static ipmpstat_enum_t targ_mode[] = {
1303 { "disabled", IPMP_TARG_DISABLED },
1304 { "routes", IPMP_TARG_ROUTES },
1305 { "multicast", IPMP_TARG_MULTICAST },
1306 { NULL, 0 }