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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <sys/varargs.h>
36 #include <libvrrpadm.h>
39 static vrrp_handle_t vrrp_vh
= NULL
;
40 typedef void cmd_func_t(int, char *[], const char *);
42 static cmd_func_t do_create
, do_delete
, do_enable
, do_disable
,
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>]" }
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'},
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'},
82 static ofmt_cb_t sfunc_vrrp_conf
;
85 * structures for 'dladm show-link -s' (print statistics)
110 * structures for 'vrrpadm show-router'
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
},
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);
141 static void warn(const char *, ...);
142 static void err_exit(const char *, ...);
143 static void opterr_exit(int, int, const char *);
146 main(int argc
, char *argv
[])
152 (void) setlocale(LC_ALL
, "");
153 (void) textdomain(TEXT_DOMAIN
);
158 if ((err
= vrrp_open(&vrrp_vh
)) != VRRP_SUCCESS
)
159 err_exit("operation failed: %s", vrrp_err2str(err
));
161 for (i
= 0; i
< sizeof (cmds
) / sizeof (cmd_t
); i
++) {
163 if (strcmp(argv
[1], cp
->c_name
) == 0) {
164 cp
->c_fn(argc
- 1, &argv
[1], cp
->c_usage
);
166 return (EXIT_SUCCESS
);
171 return (EXIT_FAILURE
);
175 do_create(int argc
, char *argv
[], const char *usage
)
179 uint32_t create_mask
= 0, mask
;
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
;
195 while ((c
= getopt_long(argc
, argv
, ":V:l:p:i:o:A:f", lopts
,
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
);
206 if (create_mask
& VRRP_CONF_INTERVAL
)
207 err_exit("duplicate '-i' option");
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");
219 if (create_mask
& VRRP_CONF_PRIORITY
)
220 err_exit("duplicate '-p' option");
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");
232 if (str2opt(optarg
, &mask
,
233 &conf
.vvc_preempt
, &conf
.vvc_accept
) != 0) {
234 err_exit("invalid options: %s", optarg
);
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");
243 if (conf
.vvc_vrid
!= VRRP_VRID_NONE
)
244 err_exit("duplicate '-V' option");
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");
254 if (conf
.vvc_af
!= AF_UNSPEC
)
255 err_exit("duplicate '-A' option");
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
;
262 err_exit("invalid address family");
265 opterr_exit(optopt
, c
, usage
);
269 if (argc
- optind
> 1)
270 err_exit("usage: %s", gettext(usage
));
272 if (optind
!= argc
- 1)
273 err_exit("VRRP name not specified");
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
]);
280 if (conf
.vvc_vrid
== VRRP_VRID_NONE
)
281 err_exit("VRID not specified");
283 if (conf
.vvc_af
== AF_UNSPEC
)
284 err_exit("address family not specified");
286 if (strlen(conf
.vvc_link
) == 0)
287 err_exit("link name not specified");
289 if (!conf
.vvc_accept
&& conf
.vvc_pri
== VRRP_PRI_OWNER
)
290 err_exit("accept_mode must be true for virtual IP owner");
293 if ((err
= vrrp_create(vrrp_vh
, &conf
)) == VRRP_SUCCESS
)
296 err_exit("create-router failed: %s", vrrp_err2str(err
));
300 do_delete(int argc
, char *argv
[], const char *use
)
305 err_exit("usage: %s", gettext(use
));
307 if ((err
= vrrp_delete(vrrp_vh
, argv
[1])) != VRRP_SUCCESS
)
308 err_exit("delete-router failed: %s", vrrp_err2str(err
));
312 do_enable(int argc
, char *argv
[], const char *use
)
317 err_exit("usage: %s", gettext(use
));
319 if ((err
= vrrp_enable(vrrp_vh
, argv
[1])) != VRRP_SUCCESS
)
320 err_exit("enable-router failed: %s", vrrp_err2str(err
));
324 do_disable(int argc
, char *argv
[], const char *use
)
329 err_exit("usage: %s", gettext(use
));
331 if ((err
= vrrp_disable(vrrp_vh
, argv
[1])) != VRRP_SUCCESS
)
332 err_exit("disable-router failed: %s", vrrp_err2str(err
));
336 do_modify(int argc
, char *argv
[], const char *use
)
340 uint32_t modify_mask
= 0, mask
;
344 while ((c
= getopt_long(argc
, argv
, ":i:p:o:", lopts
, NULL
)) != EOF
) {
347 if (modify_mask
& VRRP_CONF_INTERVAL
)
348 err_exit("duplicate '-i' option");
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");
361 if (str2opt(optarg
, &mask
, &conf
.vvc_preempt
,
362 &conf
.vvc_accept
) != 0) {
363 err_exit("Invalid options");
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");
372 if (modify_mask
& VRRP_CONF_PRIORITY
)
373 err_exit("duplicate '-p' option");
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");
384 opterr_exit(optopt
, c
, use
);
388 if (argc
- optind
> 1)
389 err_exit("usage: %s", gettext(use
));
391 if (optind
!= argc
- 1)
392 err_exit("VRRP name not specified.");
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
]);
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");
405 if (modify_mask
== 0)
408 err
= vrrp_modify(vrrp_vh
, &conf
, modify_mask
);
409 if (err
!= VRRP_SUCCESS
)
410 err_exit("modify-router failed: %s", vrrp_err2str(err
));
414 * 'show-router' one VRRP router.
417 do_show_router(const char *vn
, ofmt_handle_t ofmt
)
419 vrrp_queryinfo_t
*vq
;
422 if ((err
= vrrp_query(vrrp_vh
, vn
, &vq
)) != VRRP_SUCCESS
)
425 ofmt_print(ofmt
, vq
);
427 return (VRRP_SUCCESS
);
431 do_show(int argc
, char *argv
[], const char *use
)
434 char *fields_str
= NULL
;
435 char *names
= NULL
, *router
;
436 uint32_t i
, in_cnt
= 0, out_cnt
;
439 uint_t ofmt_flags
= 0;
440 vrrp_err_t err
= VRRP_SUCCESS
;
441 boolean_t P_opt
, x_opt
;
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";
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.
454 static char *ext_parsable_fields_str
=
455 "NAME,STATE,PRV_STAT,STAT_LAST,VNIC,PRIMARY_IP,VIP_CNT,"
458 P_opt
= x_opt
= B_FALSE
;
459 fields_str
= dft_fields_str
;
460 while ((c
= getopt_long(argc
, argv
, ":Pxpo:", l_show_opts
,
467 ofmt_flags
|= OFMT_PARSABLE
;
471 fields_str
= peer_fields_str
;
475 fields_str
= ext_fields_str
;
478 opterr_exit(optopt
, c
, use
);
483 err_exit("incompatible -P and -x options");
486 * If parsable output is requested, add VIP_CNT into the output
487 * for extended output.
489 if ((ofmt_flags
& OFMT_PARSABLE
) && (fields_str
== ext_fields_str
))
490 fields_str
= ext_parsable_fields_str
;
492 if ((oferr
= ofmt_open(fields_str
, show_print_fields
, ofmt_flags
,
493 0, &ofmt
)) != OFMT_SUCCESS
) {
494 char buf
[OFMT_BUFSIZE
];
497 * If some fields were badly formed in human-friendly mode, we
498 * emit a warning and continue. Otherwise exit immediately.
500 (void) ofmt_strerror(ofmt
, oferr
, buf
, sizeof (buf
));
501 if (oferr
!= OFMT_EBADFIELDS
|| (ofmt_flags
& OFMT_PARSABLE
)) {
509 /* Show one router */
510 if (optind
== argc
- 1) {
511 err
= do_show_router(argv
[optind
], ofmt
);
516 * Show all routers. First set in_cnt to 0 to find out the number
520 if ((in_cnt
!= 0) && (names
= malloc(in_cnt
* VRRP_NAME_MAX
)) == NULL
) {
526 if ((err
= vrrp_list(vrrp_vh
, VRRP_VRID_NONE
, NULL
, AF_UNSPEC
,
527 &out_cnt
, names
)) != VRRP_SUCCESS
) {
533 * The VRRP routers has been changed between two vrrp_list()
536 if (out_cnt
> in_cnt
) {
543 * Each VRRP router name is separated by '\0`
546 for (i
= 0; i
< in_cnt
; i
++) {
547 (void) do_show_router(router
, ofmt
);
548 router
+= strlen(router
) + 1;
556 if (err
!= VRRP_SUCCESS
)
557 err_exit(vrrp_err2str(err
));
561 * Callback function to print fields of the configuration information.
564 sfunc_vrrp_conf(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
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
;
576 (void) snprintf(buf
, bufsize
, "%s", conf
->vvc_name
);
579 (void) snprintf(buf
, bufsize
, "%d", conf
->vvc_vrid
);
582 (void) snprintf(buf
, bufsize
, "%s", conf
->vvc_link
);
585 (void) snprintf(buf
, bufsize
, "IPv%d",
586 conf
->vvc_af
== AF_INET
? 4 : 6);
589 (void) snprintf(buf
, bufsize
, "%d", conf
->vvc_pri
);
591 case ROUTER_ADV_INTV
:
592 (void) snprintf(buf
, bufsize
, "%d", conf
->vvc_adver_int
);
595 (void) strlcpy(buf
, "-----", bufsize
);
596 if (conf
->vvc_enabled
)
598 if (conf
->vvc_pri
== VRRP_PRI_OWNER
)
600 if (conf
->vvc_preempt
)
602 if (conf
->vvc_accept
)
606 (void) snprintf(buf
, bufsize
, "%s",
607 vrrp_state2str(sinfo
->vs_state
));
609 case ROUTER_PRV_STAT
:
610 (void) snprintf(buf
, bufsize
, "%s",
611 vrrp_state2str(sinfo
->vs_prev_state
));
613 case ROUTER_STAT_LAST
:
614 (void) timeval_since_str(tinfo
->vt_since_last_tran
, buf
,
618 /* LINTED E_CONSTANT_CONDITION */
619 VRRPADDR2STR(conf
->vvc_af
, &peer
->vp_addr
,
620 buf
, bufsize
, B_FALSE
);
623 (void) snprintf(buf
, bufsize
, "%d", peer
->vp_prio
);
626 (void) snprintf(buf
, bufsize
, "%d", peer
->vp_adver_int
);
628 case ROUTER_P_ADV_LAST
:
629 (void) timeval_since_str(tinfo
->vt_since_last_adv
, buf
,
632 case ROUTER_M_DOWN_INTV
:
633 (void) snprintf(buf
, bufsize
, "%d", tinfo
->vt_master_down_intv
);
636 (void) snprintf(buf
, bufsize
, "%s",
637 strlen(ainfo
->va_vnic
) == 0 ? "--" : ainfo
->va_vnic
);
639 case ROUTER_PRIMARY_IP
:
640 /* LINTED E_CONSTANT_CONDITION */
641 VRRPADDR2STR(conf
->vvc_af
, &ainfo
->va_primary
,
642 buf
, bufsize
, B_FALSE
);
644 case ROUTER_VIRTUAL_IPS
: {
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
);
657 (void) snprintf(buf
, bufsize
, "%d", ainfo
->va_vipcnt
);
672 (void) fprintf(stderr
, "%s",
673 gettext("usage: vrrpadm <sub-command> <args> ...\n"));
675 for (i
= 0; i
< sizeof (cmds
) / sizeof (cmd_t
); i
++) {
677 if (cp
->c_usage
!= NULL
)
678 (void) fprintf(stderr
, " %-10s %s\n",
679 gettext(cp
->c_name
), gettext(cp
->c_usage
));
687 warn(const char *format
, ...)
691 format
= gettext(format
);
692 (void) fprintf(stderr
, gettext("warning: "));
694 va_start(alist
, format
);
695 (void) vfprintf(stderr
, format
, alist
);
697 (void) putc('\n', stderr
);
701 err_exit(const char *format
, ...)
705 format
= gettext(format
);
706 va_start(alist
, format
);
707 (void) vfprintf(stderr
, format
, alist
);
709 (void) putc('\n', stderr
);
715 opterr_exit(int opt
, int opterr
, const char *use
)
719 err_exit("option '-%c' requires a value\nusage: %s", opt
,
724 err_exit("unrecognized option '-%c'\nusage: %s", opt
,
731 timeval_since_str(int mill
, char *str
, size_t len
)
737 min
= sec
> 60 ? sec
/ 60 : 0;
741 (void) snprintf(str
, len
, "%4dm%2ds", min
, sec
);
743 (void) snprintf(str
, len
, "%4d.%03ds", sec
, msec
);
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
753 * Returns 0 on success, errno on failures.
755 * Used by do_create() and do_modify().
757 * Note that "opts" could be modified internally in this function.
760 str2opt(char *opts
, uint32_t *modify_mask
, boolean_t
*preempt
,
766 enum { o_preempt
= 0, o_un_preempt
, o_accept
, o_no_accept
};
767 static char *myopts
[] = {
775 while (*opts
!= '\0') {
776 switch ((opt
= getsubopt(&opts
, myopts
, &value
))) {
779 if (mask
& VRRP_CONF_PREEMPT
)
782 mask
|= VRRP_CONF_PREEMPT
;
783 *preempt
= (opt
== o_preempt
);
787 if (mask
& VRRP_CONF_ACCEPT
)
790 mask
|= VRRP_CONF_ACCEPT
;
791 *accept
= (opt
== o_accept
);
798 *modify_mask
|= mask
;