xref: /illumos-gate/usr/src/cmd/vrrpadm/vrrpadm.c (revision 9b9d39d2a32ff806d2431dbcc50968ef1e6d46b2)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/varargs.h>
29 #include <getopt.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <strings.h>
33 #include <errno.h>
34 #include <locale.h>
35 #include <libintl.h>
36 #include <libvrrpadm.h>
37 #include <ofmt.h>
38 
39 static vrrp_handle_t	vrrp_vh = NULL;
40 typedef void cmd_func_t(int, char *[], const char *);
41 
42 static cmd_func_t do_create, do_delete, do_enable, do_disable,
43     do_modify, do_show;
44 
45 typedef struct {
46 	char		*c_name;
47 	cmd_func_t	*c_fn;
48 	const char	*c_usage;
49 } cmd_t;
50 
51 static cmd_t cmds[] = {
52 	{ "create-router",	do_create,
53 	    "-V <vrid> -l <link> -A {inet | inet6} [-p <priority>] "
54 	    "[-i <adv_interval>] [-o <flags>] <router_name>" },
55 	{ "delete-router",	do_delete,	"<router_name>"		},
56 	{ "enable-router",	do_enable,	"<router_name>"		},
57 	{ "disable-router",	do_disable,	"<router_name>"		},
58 	{ "modify-router",	do_modify,
59 	    "[-p <priority>] [-i <adv_interval>] [-o <flags>] <router_name>" },
60 	{ "show-router",	do_show,
61 	    "[-P | -x] [-o field[,...]] [-p] [<router_name>]"	}
62 };
63 
64 static const struct option lopts[] = {
65 	{"vrid",		required_argument,	0, 'V'},
66 	{"link",		required_argument,	0, 'l'},
67 	{"address_family",	required_argument,	0, 'A'},
68 	{"priority",		required_argument,	0, 'p'},
69 	{"adv_interval",	required_argument,	0, 'i'},
70 	{"flags",		required_argument,	0, 'o'},
71 	{ 0, 0, 0, 0 }
72 };
73 
74 static const struct option l_show_opts[] = {
75 	{"peer",	no_argument,		0, 'P'},
76 	{"parsable",	no_argument,		0, 'p'},
77 	{"extended",	no_argument,		0, 'x'},
78 	{"output",	required_argument,	0, 'o'},
79 	{ 0, 0, 0, 0 }
80 };
81 
82 static ofmt_cb_t sfunc_vrrp_conf;
83 
84 /*
85  * structures for 'dladm show-link -s' (print statistics)
86  */
87 enum {
88 	ROUTER_NAME,
89 	ROUTER_VRID,
90 	ROUTER_LINK,
91 	ROUTER_VNIC,
92 	ROUTER_AF,
93 	ROUTER_PRIO,
94 	ROUTER_ADV_INTV,
95 	ROUTER_MODE,
96 	ROUTER_STATE,
97 	ROUTER_PRV_STAT,
98 	ROUTER_STAT_LAST,
99 	ROUTER_PEER,
100 	ROUTER_P_PRIO,
101 	ROUTER_P_INTV,
102 	ROUTER_P_ADV_LAST,
103 	ROUTER_M_DOWN_INTV,
104 	ROUTER_PRIMARY_IP,
105 	ROUTER_VIRTUAL_IPS,
106 	ROUTER_VIP_CNT
107 };
108 
109 /*
110  * structures for 'vrrpadm show-router'
111  */
112 static const ofmt_field_t show_print_fields[] = {
113 /* name,	field width,	index,			callback */
114 { "NAME",		8,	ROUTER_NAME,		sfunc_vrrp_conf	},
115 { "VRID",		5,	ROUTER_VRID,		sfunc_vrrp_conf	},
116 { "LINK",		8,	ROUTER_LINK,		sfunc_vrrp_conf },
117 { "VNIC",		8,	ROUTER_VNIC,		sfunc_vrrp_conf },
118 { "AF",			5,	ROUTER_AF,		sfunc_vrrp_conf },
119 { "PRIO",		5,	ROUTER_PRIO,		sfunc_vrrp_conf },
120 { "ADV_INTV",		9,	ROUTER_ADV_INTV,	sfunc_vrrp_conf },
121 { "MODE",		6,	ROUTER_MODE,		sfunc_vrrp_conf	},
122 { "STATE",		6,	ROUTER_STATE,		sfunc_vrrp_conf },
123 { "PRV_STAT",		9, 	ROUTER_PRV_STAT,	sfunc_vrrp_conf	},
124 { "STAT_LAST",		10,	ROUTER_STAT_LAST,	sfunc_vrrp_conf },
125 { "PEER",		20,	ROUTER_PEER,		sfunc_vrrp_conf	},
126 { "P_PRIO",		7,	ROUTER_P_PRIO,		sfunc_vrrp_conf	},
127 { "P_INTV",		9,	ROUTER_P_INTV,		sfunc_vrrp_conf	},
128 { "P_ADV_LAST",		11,	ROUTER_P_ADV_LAST,	sfunc_vrrp_conf	},
129 { "M_DOWN_INTV",	12,	ROUTER_M_DOWN_INTV,	sfunc_vrrp_conf	},
130 { "PRIMARY_IP",		20,	ROUTER_PRIMARY_IP,	sfunc_vrrp_conf	},
131 { "VIRTUAL_IPS",	40,	ROUTER_VIRTUAL_IPS,	sfunc_vrrp_conf	},
132 { "VIP_CNT",		7,	ROUTER_VIP_CNT,		sfunc_vrrp_conf	},
133 { NULL,			0, 	0,			NULL}}
134 ;
135 
136 static vrrp_err_t do_show_router(const char *, ofmt_handle_t);
137 static int str2opt(char *opts, uint32_t *, boolean_t *, boolean_t *);
138 static char *timeval_since_str(int, char *, size_t);
139 
140 static void usage();
141 static void warn(const char *, ...);
142 static void err_exit(const char *, ...);
143 static void opterr_exit(int, int, const char *);
144 
145 int
146 main(int argc, char *argv[])
147 {
148 	vrrp_err_t	err;
149 	int		i;
150 	cmd_t		*cp;
151 
152 	(void) setlocale(LC_ALL, "");
153 	(void) textdomain(TEXT_DOMAIN);
154 
155 	if (argv[1] == NULL)
156 		usage();
157 
158 	if ((err = vrrp_open(&vrrp_vh)) != VRRP_SUCCESS)
159 		err_exit("operation failed: %s", vrrp_err2str(err));
160 
161 	for (i = 0; i < sizeof (cmds) / sizeof (cmd_t); i++) {
162 		cp = &cmds[i];
163 		if (strcmp(argv[1], cp->c_name) == 0) {
164 			cp->c_fn(argc - 1, &argv[1], cp->c_usage);
165 			vrrp_close(vrrp_vh);
166 			return (EXIT_SUCCESS);
167 		}
168 	}
169 
170 	usage();
171 	return (EXIT_FAILURE);
172 }
173 
174 static void
175 do_create(int argc, char *argv[], const char *usage)
176 {
177 	vrrp_vr_conf_t		conf;
178 	int			c;
179 	uint32_t		create_mask = 0, mask;
180 	char			*endp;
181 	vrrp_err_t		err;
182 
183 	/*
184 	 * default value
185 	 */
186 	bzero(&conf, sizeof (vrrp_vr_conf_t));
187 	conf.vvc_vrid = VRRP_VRID_NONE;
188 	conf.vvc_af = AF_UNSPEC;
189 	conf.vvc_pri = VRRP_PRI_DEFAULT;
190 	conf.vvc_adver_int = VRRP_MAX_ADVER_INT_DFLT;
191 	conf.vvc_preempt = B_TRUE;
192 	conf.vvc_accept = B_TRUE;
193 	conf.vvc_enabled = B_TRUE;
194 
195 	while ((c = getopt_long(argc, argv, ":V:l:p:i:o:A:f", lopts,
196 	    NULL)) != EOF) {
197 		switch (c) {
198 		case 'l':
199 			if (strlcpy(conf.vvc_link, optarg,
200 			    sizeof (conf.vvc_link)) >=
201 			    sizeof (conf.vvc_link)) {
202 				err_exit("invalid data-link name %s", optarg);
203 			}
204 			break;
205 		case 'i':
206 			if (create_mask & VRRP_CONF_INTERVAL)
207 				err_exit("duplicate '-i' option");
208 
209 			create_mask |= VRRP_CONF_INTERVAL;
210 			conf.vvc_adver_int = (uint32_t)strtol(optarg, &endp, 0);
211 			if ((*endp) != '\0' ||
212 			    conf.vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
213 			    conf.vvc_adver_int > VRRP_MAX_ADVER_INT_MAX ||
214 			    (conf.vvc_adver_int == 0 && errno != 0)) {
215 				err_exit("invalid advertisement interval");
216 			}
217 			break;
218 		case 'p':
219 			if (create_mask & VRRP_CONF_PRIORITY)
220 				err_exit("duplicate '-p' option");
221 
222 			create_mask |= VRRP_CONF_PRIORITY;
223 			conf.vvc_pri = strtol(optarg, &endp, 0);
224 			if ((*endp) != '\0' || conf.vvc_pri < VRRP_PRI_MIN ||
225 			    conf.vvc_pri > VRRP_PRI_OWNER ||
226 			    (conf.vvc_pri == 0 && errno != 0)) {
227 				err_exit("invalid priority");
228 			}
229 			break;
230 		case 'o':
231 			mask = 0;
232 			if (str2opt(optarg, &mask,
233 			    &conf.vvc_preempt, &conf.vvc_accept) != 0) {
234 				err_exit("invalid options: %s", optarg);
235 			}
236 			if (mask & create_mask & VRRP_CONF_PREEMPT)
237 				err_exit("duplicate '-o preempt' option");
238 			else if (mask & create_mask & VRRP_CONF_ACCEPT)
239 				err_exit("duplicate '-o accept' option");
240 			create_mask |= mask;
241 			break;
242 		case 'V':
243 			if (conf.vvc_vrid != VRRP_VRID_NONE)
244 				err_exit("duplicate '-V' option");
245 
246 			conf.vvc_vrid = strtol(optarg, &endp, 0);
247 			if ((*endp) != '\0' || conf.vvc_vrid < VRRP_VRID_MIN ||
248 			    conf.vvc_vrid > VRRP_VRID_MAX ||
249 			    (conf.vvc_vrid == 0 && errno != 0)) {
250 				err_exit("invalid VRID");
251 			}
252 			break;
253 		case 'A':
254 			if (conf.vvc_af != AF_UNSPEC)
255 				err_exit("duplicate '-A' option");
256 
257 			if (strcmp(optarg, "inet") == 0)
258 				conf.vvc_af = AF_INET;
259 			else if (strcmp(optarg, "inet6") == 0)
260 				conf.vvc_af = AF_INET6;
261 			else
262 				err_exit("invalid address family");
263 			break;
264 		default:
265 			opterr_exit(optopt, c, usage);
266 		}
267 	}
268 
269 	if (argc - optind > 1)
270 		err_exit("usage: %s", gettext(usage));
271 
272 	if (optind != argc - 1)
273 		err_exit("VRRP name not specified");
274 
275 	if (strlcpy(conf.vvc_name, argv[optind],
276 	    sizeof (conf.vvc_name)) >= sizeof (conf.vvc_name)) {
277 		err_exit("Invalid router name %s", argv[optind]);
278 	}
279 
280 	if (conf.vvc_vrid == VRRP_VRID_NONE)
281 		err_exit("VRID not specified");
282 
283 	if (conf.vvc_af == AF_UNSPEC)
284 		err_exit("address family not specified");
285 
286 	if (strlen(conf.vvc_link) == 0)
287 		err_exit("link name not specified");
288 
289 	if (!conf.vvc_accept && conf.vvc_pri == VRRP_PRI_OWNER)
290 		err_exit("accept_mode must be true for virtual IP owner");
291 
292 done:
293 	if ((err = vrrp_create(vrrp_vh, &conf)) == VRRP_SUCCESS)
294 		return;
295 
296 	err_exit("create-router failed: %s", vrrp_err2str(err));
297 }
298 
299 static void
300 do_delete(int argc, char *argv[], const char *use)
301 {
302 	vrrp_err_t	err;
303 
304 	if (argc != 2)
305 		err_exit("usage: %s", gettext(use));
306 
307 	if ((err = vrrp_delete(vrrp_vh, argv[1])) != VRRP_SUCCESS)
308 		err_exit("delete-router failed: %s", vrrp_err2str(err));
309 }
310 
311 static void
312 do_enable(int argc, char *argv[], const char *use)
313 {
314 	vrrp_err_t	err;
315 
316 	if (argc != 2)
317 		err_exit("usage: %s", gettext(use));
318 
319 	if ((err = vrrp_enable(vrrp_vh, argv[1])) != VRRP_SUCCESS)
320 		err_exit("enable-router failed: %s", vrrp_err2str(err));
321 }
322 
323 static void
324 do_disable(int argc, char *argv[], const char *use)
325 {
326 	vrrp_err_t	err;
327 
328 	if (argc != 2)
329 		err_exit("usage: %s", gettext(use));
330 
331 	if ((err = vrrp_disable(vrrp_vh, argv[1])) != VRRP_SUCCESS)
332 		err_exit("disable-router failed: %s", vrrp_err2str(err));
333 }
334 
335 static void
336 do_modify(int argc, char *argv[], const char *use)
337 {
338 	vrrp_vr_conf_t	conf;
339 	vrrp_err_t	err;
340 	uint32_t	modify_mask = 0, mask;
341 	char		*endp;
342 	int		c;
343 
344 	while ((c = getopt_long(argc, argv, ":i:p:o:", lopts, NULL)) != EOF) {
345 		switch (c) {
346 		case 'i':
347 			if (modify_mask & VRRP_CONF_INTERVAL)
348 				err_exit("duplicate '-i' option");
349 
350 			modify_mask |= VRRP_CONF_INTERVAL;
351 			conf.vvc_adver_int = (uint32_t)strtol(optarg, &endp, 0);
352 			if ((*endp) != '\0' ||
353 			    conf.vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
354 			    conf.vvc_adver_int > VRRP_MAX_ADVER_INT_MAX ||
355 			    (conf.vvc_adver_int == 0 && errno != 0)) {
356 				err_exit("invalid advertisement interval");
357 			}
358 			break;
359 		case 'o':
360 			mask = 0;
361 			if (str2opt(optarg, &mask, &conf.vvc_preempt,
362 			    &conf.vvc_accept) != 0) {
363 				err_exit("Invalid options");
364 			}
365 			if (mask & modify_mask & VRRP_CONF_PREEMPT)
366 				err_exit("duplicate '-o preempt' option");
367 			else if (mask & modify_mask & VRRP_CONF_ACCEPT)
368 				err_exit("duplicate '-o accept' option");
369 			modify_mask |= mask;
370 			break;
371 		case 'p':
372 			if (modify_mask & VRRP_CONF_PRIORITY)
373 				err_exit("duplicate '-p' option");
374 
375 			modify_mask |= VRRP_CONF_PRIORITY;
376 			conf.vvc_pri = strtol(optarg, &endp, 0);
377 			if ((*endp) != '\0' || conf.vvc_pri < VRRP_PRI_MIN ||
378 			    conf.vvc_pri > VRRP_PRI_OWNER ||
379 			    (conf.vvc_pri == 0 && errno != 0)) {
380 				err_exit("invalid priority");
381 			}
382 			break;
383 		default:
384 			opterr_exit(optopt, c, use);
385 		}
386 	}
387 
388 	if (argc - optind > 1)
389 		err_exit("usage: %s", gettext(use));
390 
391 	if (optind != argc - 1)
392 		err_exit("VRRP name not specified.");
393 
394 	if (strlcpy(conf.vvc_name, argv[optind], sizeof (conf.vvc_name)) >=
395 	    sizeof (conf.vvc_name)) {
396 		err_exit("invalid router name %s", argv[optind]);
397 	}
398 
399 	if ((modify_mask & VRRP_CONF_ACCEPT) && !conf.vvc_accept &&
400 	    (modify_mask & VRRP_CONF_PRIORITY) &&
401 	    conf.vvc_pri == VRRP_PRI_OWNER) {
402 		err_exit("accept_mode must be true for virtual IP owner");
403 	}
404 
405 	if (modify_mask == 0)
406 		usage();
407 
408 	err = vrrp_modify(vrrp_vh, &conf, modify_mask);
409 	if (err != VRRP_SUCCESS)
410 		err_exit("modify-router failed: %s", vrrp_err2str(err));
411 }
412 
413 /*
414  * 'show-router' one VRRP router.
415  */
416 static vrrp_err_t
417 do_show_router(const char *vn, ofmt_handle_t ofmt)
418 {
419 	vrrp_queryinfo_t	*vq;
420 	vrrp_err_t		err;
421 
422 	if ((err = vrrp_query(vrrp_vh, vn, &vq)) != VRRP_SUCCESS)
423 		return (err);
424 
425 	ofmt_print(ofmt, vq);
426 	free(vq);
427 	return (VRRP_SUCCESS);
428 }
429 
430 static void
431 do_show(int argc, char *argv[], const char *use)
432 {
433 	int			c;
434 	char			*fields_str = NULL;
435 	char			*names = NULL, *router;
436 	uint32_t		i, in_cnt = 0, out_cnt;
437 	ofmt_status_t		oferr;
438 	ofmt_handle_t		ofmt;
439 	uint_t			ofmt_flags = 0;
440 	vrrp_err_t		err = VRRP_SUCCESS;
441 	boolean_t		P_opt, x_opt;
442 
443 	static char		*dft_fields_str =
444 	    "NAME,VRID,LINK,AF,PRIO,ADV_INTV,MODE,STATE,VNIC";
445 	static char		*ext_fields_str =
446 	    "NAME,STATE,PRV_STAT,STAT_LAST,VNIC,PRIMARY_IP,VIRTUAL_IPS";
447 	static char		*peer_fields_str =
448 	    "NAME,PEER,P_PRIO,P_INTV,P_ADV_LAST,M_DOWN_INTV";
449 	/*
450 	 * If parsable output is requested, add VIP_CNT into the output
451 	 * for extended output. It is not needed for human-readable
452 	 * output as it is obvious from the VIRTUAL_IPS list.
453 	 */
454 	static char		*ext_parsable_fields_str =
455 	    "NAME,STATE,PRV_STAT,STAT_LAST,VNIC,PRIMARY_IP,VIP_CNT,"
456 	    "VIRTUAL_IPS";
457 
458 	P_opt = x_opt = B_FALSE;
459 	fields_str = dft_fields_str;
460 	while ((c = getopt_long(argc, argv, ":Pxpo:", l_show_opts,
461 	    NULL)) != EOF) {
462 		switch (c) {
463 		case 'o':
464 			fields_str = optarg;
465 			break;
466 		case 'p':
467 			ofmt_flags |= OFMT_PARSABLE;
468 			break;
469 		case 'P':
470 			P_opt = B_TRUE;
471 			fields_str = peer_fields_str;
472 			break;
473 		case 'x':
474 			x_opt = B_TRUE;
475 			fields_str = ext_fields_str;
476 			break;
477 		default:
478 			opterr_exit(optopt, c, use);
479 		}
480 	}
481 
482 	if (x_opt && P_opt)
483 		err_exit("incompatible -P and -x options");
484 
485 	/*
486 	 * If parsable output is requested, add VIP_CNT into the output
487 	 * for extended output.
488 	 */
489 	if ((ofmt_flags & OFMT_PARSABLE) && (fields_str == ext_fields_str))
490 		fields_str = ext_parsable_fields_str;
491 
492 	if ((oferr = ofmt_open(fields_str, show_print_fields, ofmt_flags,
493 	    0, &ofmt)) != OFMT_SUCCESS) {
494 		char buf[OFMT_BUFSIZE];
495 
496 		/*
497 		 * If some fields were badly formed in human-friendly mode, we
498 		 * emit a warning and continue.  Otherwise exit immediately.
499 		 */
500 		(void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
501 		if (oferr != OFMT_EBADFIELDS || (ofmt_flags & OFMT_PARSABLE)) {
502 			ofmt_close(ofmt);
503 			err_exit(buf);
504 		} else {
505 			warn(buf);
506 		}
507 	}
508 
509 	/* Show one router */
510 	if (optind == argc - 1) {
511 		err = do_show_router(argv[optind], ofmt);
512 		goto done;
513 	}
514 
515 	/*
516 	 * Show all routers. First set in_cnt to 0 to find out the number
517 	 * of vrrp routers.
518 	 */
519 again:
520 	if ((in_cnt != 0) && (names = malloc(in_cnt * VRRP_NAME_MAX)) == NULL) {
521 		err = VRRP_ENOMEM;
522 		goto done;
523 	}
524 
525 	out_cnt = in_cnt;
526 	if ((err = vrrp_list(vrrp_vh, VRRP_VRID_NONE, NULL, AF_UNSPEC,
527 	    &out_cnt, names)) != VRRP_SUCCESS) {
528 		free(names);
529 		goto done;
530 	}
531 
532 	/*
533 	 * The VRRP routers has been changed between two vrrp_list()
534 	 * calls, try again.
535 	 */
536 	if (out_cnt > in_cnt) {
537 		in_cnt = out_cnt;
538 		free(names);
539 		goto again;
540 	}
541 
542 	/*
543 	 * Each VRRP router name is separated by '\0`
544 	 */
545 	router = names;
546 	for (i = 0; i < in_cnt; i++) {
547 		(void) do_show_router(router, ofmt);
548 		router += strlen(router) + 1;
549 	}
550 
551 	free(names);
552 
553 done:
554 	ofmt_close(ofmt);
555 
556 	if (err != VRRP_SUCCESS)
557 		err_exit(vrrp_err2str(err));
558 }
559 
560 /*
561  * Callback function to print fields of the configuration information.
562  */
563 static boolean_t
564 sfunc_vrrp_conf(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
565 {
566 	vrrp_queryinfo_t	*qinfo = ofmtarg->ofmt_cbarg;
567 	uint_t			ofmtid = ofmtarg->ofmt_id;
568 	vrrp_vr_conf_t		*conf = &qinfo->show_vi;
569 	vrrp_stateinfo_t	*sinfo = &qinfo->show_vs;
570 	vrrp_peer_t		*peer = &qinfo->show_vp;
571 	vrrp_timerinfo_t	*tinfo = &qinfo->show_vt;
572 	vrrp_addrinfo_t		*ainfo = &qinfo->show_va;
573 
574 	switch (ofmtid) {
575 	case ROUTER_NAME:
576 		(void) snprintf(buf, bufsize, "%s", conf->vvc_name);
577 		break;
578 	case ROUTER_VRID:
579 		(void) snprintf(buf, bufsize, "%d", conf->vvc_vrid);
580 		break;
581 	case ROUTER_LINK:
582 		(void) snprintf(buf, bufsize, "%s", conf->vvc_link);
583 		break;
584 	case ROUTER_AF:
585 		(void) snprintf(buf, bufsize, "IPv%d",
586 		    conf->vvc_af == AF_INET ? 4 : 6);
587 		break;
588 	case ROUTER_PRIO:
589 		(void) snprintf(buf, bufsize, "%d", conf->vvc_pri);
590 		break;
591 	case ROUTER_ADV_INTV:
592 		(void) snprintf(buf, bufsize, "%d", conf->vvc_adver_int);
593 		break;
594 	case ROUTER_MODE:
595 		(void) strlcpy(buf, "-----", bufsize);
596 		if (conf->vvc_enabled)
597 			buf[0] = 'e';
598 		if (conf->vvc_pri == VRRP_PRI_OWNER)
599 			buf[1] = 'o';
600 		if (conf->vvc_preempt)
601 			buf[2] = 'p';
602 		if (conf->vvc_accept)
603 			buf[3] = 'a';
604 		break;
605 	case ROUTER_STATE:
606 		(void) snprintf(buf, bufsize, "%s",
607 		    vrrp_state2str(sinfo->vs_state));
608 		break;
609 	case ROUTER_PRV_STAT:
610 		(void) snprintf(buf, bufsize, "%s",
611 		    vrrp_state2str(sinfo->vs_prev_state));
612 		break;
613 	case ROUTER_STAT_LAST:
614 		(void) timeval_since_str(tinfo->vt_since_last_tran, buf,
615 		    bufsize);
616 		break;
617 	case ROUTER_PEER:
618 		/* LINTED E_CONSTANT_CONDITION */
619 		VRRPADDR2STR(conf->vvc_af, &peer->vp_addr,
620 		    buf, bufsize, B_FALSE);
621 		break;
622 	case ROUTER_P_PRIO:
623 		(void) snprintf(buf, bufsize, "%d", peer->vp_prio);
624 		break;
625 	case ROUTER_P_INTV:
626 		(void) snprintf(buf, bufsize, "%d", peer->vp_adver_int);
627 		break;
628 	case ROUTER_P_ADV_LAST:
629 		(void) timeval_since_str(tinfo->vt_since_last_adv, buf,
630 		    bufsize);
631 		break;
632 	case ROUTER_M_DOWN_INTV:
633 		(void) snprintf(buf, bufsize, "%d", tinfo->vt_master_down_intv);
634 		break;
635 	case ROUTER_VNIC:
636 		(void) snprintf(buf, bufsize, "%s",
637 		    strlen(ainfo->va_vnic) == 0 ? "--" : ainfo->va_vnic);
638 		break;
639 	case ROUTER_PRIMARY_IP:
640 		/* LINTED E_CONSTANT_CONDITION */
641 		VRRPADDR2STR(conf->vvc_af, &ainfo->va_primary,
642 		    buf, bufsize, B_FALSE);
643 		break;
644 	case ROUTER_VIRTUAL_IPS: {
645 		uint32_t i;
646 
647 		for (i = 0; i < ainfo->va_vipcnt; i++) {
648 			/* LINTED E_CONSTANT_CONDITION */
649 			VRRPADDR2STR(conf->vvc_af, &(ainfo->va_vips[i]),
650 			    buf, bufsize, B_TRUE);
651 			if (i != ainfo->va_vipcnt - 1)
652 				(void) strlcat(buf, ",", bufsize);
653 		}
654 		break;
655 	}
656 	case ROUTER_VIP_CNT:
657 		(void) snprintf(buf, bufsize, "%d", ainfo->va_vipcnt);
658 		break;
659 	default:
660 		return (B_FALSE);
661 	}
662 
663 	return (B_TRUE);
664 }
665 
666 static void
667 usage()
668 {
669 	int	i;
670 	cmd_t	*cp;
671 
672 	(void) fprintf(stderr, "%s",
673 	    gettext("usage:  vrrpadm <sub-command> <args> ...\n"));
674 
675 	for (i = 0; i < sizeof (cmds) / sizeof (cmd_t); i++) {
676 		cp = &cmds[i];
677 		if (cp->c_usage != NULL)
678 			(void) fprintf(stderr, "          %-10s %s\n",
679 			    gettext(cp->c_name), gettext(cp->c_usage));
680 	}
681 
682 	vrrp_close(vrrp_vh);
683 	exit(EXIT_FAILURE);
684 }
685 
686 static void
687 warn(const char *format, ...)
688 {
689 	va_list alist;
690 
691 	format = gettext(format);
692 	(void) fprintf(stderr, gettext("warning: "));
693 
694 	va_start(alist, format);
695 	(void) vfprintf(stderr, format, alist);
696 	va_end(alist);
697 	(void) putc('\n', stderr);
698 }
699 
700 static void
701 err_exit(const char *format, ...)
702 {
703 	va_list alist;
704 
705 	format = gettext(format);
706 	va_start(alist, format);
707 	(void) vfprintf(stderr, format, alist);
708 	va_end(alist);
709 	(void) putc('\n', stderr);
710 	vrrp_close(vrrp_vh);
711 	exit(EXIT_FAILURE);
712 }
713 
714 static void
715 opterr_exit(int opt, int opterr, const char *use)
716 {
717 	switch (opterr) {
718 	case ':':
719 		err_exit("option '-%c' requires a value\nusage: %s", opt,
720 		    gettext(use));
721 		break;
722 	case '?':
723 	default:
724 		err_exit("unrecognized option '-%c'\nusage: %s", opt,
725 		    gettext(use));
726 		break;
727 	}
728 }
729 
730 static char *
731 timeval_since_str(int mill, char *str, size_t len)
732 {
733 	int	sec, msec, min;
734 
735 	msec = mill % 1000;
736 	sec = mill / 1000;
737 	min = sec > 60 ? sec / 60 : 0;
738 	sec %= 60;
739 
740 	if (min > 0)
741 		(void) snprintf(str, len, "%4dm%2ds", min, sec);
742 	else
743 		(void) snprintf(str, len, "%4d.%03ds", sec, msec);
744 
745 	return (str);
746 }
747 
748 /*
749  * Parses options string. The values of the two options will be returned
750  * by 'preempt' and 'accept', and the mask 'modify_mask' will be updated
751  * accordingly.
752  *
753  * Returns 0 on success, errno on failures.
754  *
755  * Used by do_create() and do_modify().
756  *
757  * Note that "opts" could be modified internally in this function.
758  */
759 static int
760 str2opt(char *opts, uint32_t *modify_mask, boolean_t *preempt,
761     boolean_t *accept)
762 {
763 	char		*value;
764 	int		opt;
765 	uint32_t	mask = 0;
766 	enum { o_preempt = 0, o_un_preempt, o_accept, o_no_accept };
767 	static char	*myopts[] = {
768 		"preempt",
769 		"un_preempt",
770 		"accept",
771 		"no_accept",
772 		NULL
773 	};
774 
775 	while (*opts != '\0') {
776 		switch ((opt = getsubopt(&opts, myopts, &value))) {
777 		case o_preempt:
778 		case o_un_preempt:
779 			if (mask & VRRP_CONF_PREEMPT)
780 				return (EINVAL);
781 
782 			mask |= VRRP_CONF_PREEMPT;
783 			*preempt = (opt == o_preempt);
784 			break;
785 		case o_accept:
786 		case o_no_accept:
787 			if (mask & VRRP_CONF_ACCEPT)
788 				return (EINVAL);
789 
790 			mask |= VRRP_CONF_ACCEPT;
791 			*accept = (opt == o_accept);
792 			break;
793 		default:
794 			return (EINVAL);
795 		}
796 	}
797 
798 	*modify_mask |= mask;
799 	return (0);
800 }
801