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 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
31 #include <sys/types.h>
33 #include <sys/socket.h>
35 #include <sys/varargs.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <net/if.h> /* LIFNAMSIZ */
47 #include <netinet/vrrp.h>
49 #include <libdlvnic.h>
50 #include <libdlvlan.h>
51 #include <libdllink.h>
54 #include <libvrrpadm.h>
56 #define VRRP_SERVICE "network/vrrp:default"
58 typedef vrrp_err_t
vrrp_cmd_func_t(int, void *);
61 vrrp_svc_isonline(char *svc_name
)
64 boolean_t isonline
= B_FALSE
;
66 if ((s
= smf_get_state(svc_name
)) != NULL
) {
67 if (strcmp(s
, SCF_STATE_STRING_ONLINE
) == 0)
75 #define MAX_WAIT_TIME 15
82 if (vrrp_svc_isonline(VRRP_SERVICE
))
83 return (VRRP_SUCCESS
);
85 if (smf_enable_instance(VRRP_SERVICE
, 0) == -1) {
86 if (scf_error() == SCF_ERROR_PERMISSION_DENIED
)
93 * Wait up to MAX_WAIT_TIME seconds for the VRRP service being brought
96 for (i
= 0; i
< MAX_WAIT_TIME
; i
++) {
97 if (vrrp_svc_isonline(VRRP_SERVICE
))
101 if (i
== MAX_WAIT_TIME
)
102 return (VRRP_ENOSVC
);
104 return (VRRP_SUCCESS
);
108 * Disable the VRRP service if there is no VRRP router left.
111 vrrp_disable_service_when_no_router()
116 * Get the number of the existing routers. If there is no routers
117 * left, disable the service.
119 if (vrrp_list(NULL
, VRRP_VRID_NONE
, NULL
, AF_UNSPEC
, &cnt
,
120 NULL
) == VRRP_SUCCESS
&& cnt
== 0) {
121 (void) smf_disable_instance(VRRP_SERVICE
, 0);
126 vrrp_cmd_request(void *cmd
, size_t csize
, vrrp_cmd_func_t func
, void *arg
)
128 struct sockaddr_un to
;
130 size_t len
, cur_size
= 0;
134 if ((sock
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0)
138 * Set it to be non-blocking.
140 flags
= fcntl(sock
, F_GETFL
, 0);
141 (void) fcntl(sock
, F_SETFL
, (flags
| O_NONBLOCK
));
143 (void) memset(&to
, 0, sizeof (to
));
144 to
.sun_family
= AF_UNIX
;
145 (void) strlcpy(to
.sun_path
, VRRPD_SOCKET
, sizeof (to
.sun_path
));
150 if (connect(sock
, (const struct sockaddr
*)&to
, sizeof (to
)) < 0) {
152 return (VRRP_ENOSVC
);
158 while (cur_size
< csize
) {
159 len
= write(sock
, (char *)cmd
+ cur_size
, csize
- cur_size
);
160 if (len
== (size_t)-1 && errno
== EAGAIN
) {
162 } else if (len
> 0) {
167 return (VRRP_ENOSVC
);
171 * Expect the ack, first get the error code.
174 while (cur_size
< sizeof (vrrp_err_t
)) {
175 len
= read(sock
, (char *)&ret
+ cur_size
,
176 sizeof (vrrp_err_t
) - cur_size
);
178 if (len
== (size_t)-1 && errno
== EAGAIN
) {
180 } else if (len
> 0) {
188 if ((err
= ret
.vr_err
) != VRRP_SUCCESS
)
192 * The specific callback gets the rest of the information.
195 err
= func(sock
, arg
);
206 vrrp_err2str(vrrp_err_t err
)
210 return (dgettext(TEXT_DOMAIN
, "success"));
212 return (dgettext(TEXT_DOMAIN
, "not enough memory"));
213 case VRRP_EINVALVRNAME
:
214 return (dgettext(TEXT_DOMAIN
, "invalid router name"));
216 return (dgettext(TEXT_DOMAIN
, "no primary IP"));
218 return (dgettext(TEXT_DOMAIN
, "already exists"));
220 return (dgettext(TEXT_DOMAIN
, "no virtual IPs"));
222 return (dgettext(TEXT_DOMAIN
, "ip configuration failure"));
224 return (dgettext(TEXT_DOMAIN
, "data-link configuration "
227 return (dgettext(TEXT_DOMAIN
, "configuration update error"));
229 return (dgettext(TEXT_DOMAIN
, "invalid state"));
231 return (dgettext(TEXT_DOMAIN
, "VRRP router already exists"));
233 return (dgettext(TEXT_DOMAIN
, "not enough space"));
234 case VRRP_EINSTEXIST
:
235 return (dgettext(TEXT_DOMAIN
, "router name already exists"));
237 return (dgettext(TEXT_DOMAIN
, "VRRP router not found"));
238 case VRRP_EINVALADDR
:
239 return (dgettext(TEXT_DOMAIN
, "invalid IP address"));
241 return (dgettext(TEXT_DOMAIN
, "invalid IP address family"));
242 case VRRP_EINVALLINK
:
243 return (dgettext(TEXT_DOMAIN
, "invalid data-link"));
245 return (dgettext(TEXT_DOMAIN
, "permission denied"));
247 return (dgettext(TEXT_DOMAIN
, "system error"));
249 return (dgettext(TEXT_DOMAIN
, "try again"));
251 return (dgettext(TEXT_DOMAIN
, "operation already in progress"));
253 return (dgettext(TEXT_DOMAIN
, "VRRP VNIC has not been "
256 return (dgettext(TEXT_DOMAIN
, "the data-link does not exist"));
258 return (dgettext(TEXT_DOMAIN
, "the VRRP service cannot "
262 return (dgettext(TEXT_DOMAIN
, "invalid argument"));
267 vrrp_state2str(vrrp_state_t state
)
270 case VRRP_STATE_NONE
:
271 return (dgettext(TEXT_DOMAIN
, "NONE"));
272 case VRRP_STATE_INIT
:
273 return (dgettext(TEXT_DOMAIN
, "INIT"));
274 case VRRP_STATE_MASTER
:
275 return (dgettext(TEXT_DOMAIN
, "MASTER"));
276 case VRRP_STATE_BACKUP
:
277 return (dgettext(TEXT_DOMAIN
, "BACKUP"));
279 return (dgettext(TEXT_DOMAIN
, "INVALID"));
284 vrrp_open(vrrp_handle_t
*vh
)
288 if (dladm_open(&dh
) != DLADM_STATUS_OK
)
289 return (VRRP_EDLADM
);
291 if ((*vh
= malloc(sizeof (struct vrrp_handle
))) == NULL
) {
293 return (VRRP_ENOMEM
);
296 return (VRRP_SUCCESS
);
300 vrrp_close(vrrp_handle_t vh
)
303 dladm_close(vh
->vh_dh
);
309 vrrp_valid_name(const char *name
)
314 * The legal characters in a valid router name are:
315 * alphanumeric (a-z, A-Z, 0-9), underscore ('_'), and '.'.
317 for (c
= name
; *c
!= '\0'; c
++) {
318 if ((isalnum(*c
) == 0) && (*c
!= '_'))
327 vrrp_create(vrrp_handle_t vh
, vrrp_vr_conf_t
*conf
)
329 vrrp_cmd_create_t cmd
;
334 * Enable the VRRP service if it is not already enabled.
336 if ((err
= vrrp_enable_service()) != VRRP_SUCCESS
)
339 cmd
.vcc_cmd
= VRRP_CMD_CREATE
;
340 (void) memcpy(&cmd
.vcc_conf
, conf
, sizeof (vrrp_vr_conf_t
));
342 err
= vrrp_cmd_request(&cmd
, sizeof (cmd
), NULL
, NULL
);
343 if (err
== VRRP_ENOSVC
) {
345 * This may be due to another process is deleting the last
346 * router and disabled the VRRP service, try again.
349 } else if (err
!= VRRP_SUCCESS
) {
351 * If router cannot be created, check if the VRRP service
352 * should be disabled, and disable if needed.
354 vrrp_disable_service_when_no_router();
362 vrrp_delete(vrrp_handle_t vh
, const char *vn
)
364 vrrp_cmd_delete_t cmd
;
368 * If the VRRP service is not enabled, we assume there is no router
371 if (!vrrp_svc_isonline(VRRP_SERVICE
))
372 return (VRRP_ENOTFOUND
);
374 cmd
.vcd_cmd
= VRRP_CMD_DELETE
;
375 if (strlcpy(cmd
.vcd_name
, vn
, VRRP_NAME_MAX
) >= VRRP_NAME_MAX
)
376 return (VRRP_EINVAL
);
378 err
= vrrp_cmd_request(&cmd
, sizeof (cmd
), NULL
, NULL
);
379 if (err
== VRRP_SUCCESS
)
380 vrrp_disable_service_when_no_router();
386 vrrp_enable(vrrp_handle_t vh
, const char *vn
)
388 vrrp_cmd_enable_t cmd
;
392 * If the VRRP service is not enabled, we assume there is no router
395 if (!vrrp_svc_isonline(VRRP_SERVICE
))
396 return (VRRP_ENOTFOUND
);
398 cmd
.vcs_cmd
= VRRP_CMD_ENABLE
;
399 if (strlcpy(cmd
.vcs_name
, vn
, VRRP_NAME_MAX
) >= VRRP_NAME_MAX
)
400 return (VRRP_EINVAL
);
402 err
= vrrp_cmd_request(&cmd
, sizeof (cmd
), NULL
, NULL
);
408 vrrp_disable(vrrp_handle_t vh
, const char *vn
)
410 vrrp_cmd_disable_t cmd
;
414 * If the VRRP service is not enabled, we assume there is no router
417 if (!vrrp_svc_isonline(VRRP_SERVICE
))
418 return (VRRP_ENOTFOUND
);
420 cmd
.vcx_cmd
= VRRP_CMD_DISABLE
;
421 if (strlcpy(cmd
.vcx_name
, vn
, VRRP_NAME_MAX
) >= VRRP_NAME_MAX
)
422 return (VRRP_EINVAL
);
424 err
= vrrp_cmd_request(&cmd
, sizeof (cmd
), NULL
, NULL
);
430 vrrp_modify(vrrp_handle_t vh
, vrrp_vr_conf_t
*conf
, uint32_t mask
)
432 vrrp_cmd_modify_t cmd
;
436 * If the VRRP service is not enabled, we assume there is no router
439 if (!vrrp_svc_isonline(VRRP_SERVICE
))
440 return (VRRP_ENOTFOUND
);
442 cmd
.vcm_cmd
= VRRP_CMD_MODIFY
;
444 (void) memcpy(&cmd
.vcm_conf
, conf
, sizeof (vrrp_vr_conf_t
));
446 err
= vrrp_cmd_request(&cmd
, sizeof (cmd
), NULL
, NULL
);
450 typedef struct vrrp_cmd_list_arg
{
453 } vrrp_cmd_list_arg_t
;
456 vrrp_list_func(int sock
, void *arg
)
458 vrrp_cmd_list_arg_t
*list_arg
= arg
;
459 uint32_t in_cnt
= *(list_arg
->vfl_cnt
);
462 size_t len
, cur_size
= 0;
465 * Get the rest of vrrp_ret_list_t besides the error code.
467 cur_size
= sizeof (vrrp_err_t
);
468 while (cur_size
< sizeof (vrrp_ret_list_t
)) {
469 len
= read(sock
, (char *)&ret
+ cur_size
,
470 sizeof (vrrp_ret_list_t
) - cur_size
);
472 if (len
== (size_t)-1 && errno
== EAGAIN
) {
474 } else if (len
> 0) {
481 *(list_arg
->vfl_cnt
) = out_cnt
= ret
.vrl_cnt
;
482 out_cnt
= (in_cnt
<= out_cnt
) ? in_cnt
: out_cnt
;
485 while (cur_size
< VRRP_NAME_MAX
* out_cnt
) {
486 len
= read(sock
, (char *)list_arg
->vfl_names
+ cur_size
,
487 VRRP_NAME_MAX
* out_cnt
- cur_size
);
489 if (len
== (size_t)-1 && errno
== EAGAIN
) {
491 } else if (len
> 0) {
497 return (VRRP_SUCCESS
);
501 * Looks up the vrrp instances that matches the given variable.
503 * If the given cnt is 0, names should be set to NULL. In this case, only
504 * the count of the matched instances is returned.
506 * If the given cnt is non-zero, caller must allocate "names" whose size
507 * is (cnt * VRRP_NAME_MAX).
509 * Return value: the current count of matched instances, and names will be
510 * points to the list of the current vrrp instances names. Note that
511 * only MIN(in_cnt, out_cnt) number of names will be returned.
515 vrrp_list(vrrp_handle_t vh
, vrid_t vrid
, const char *intf
, int af
,
516 uint32_t *cnt
, char *names
)
520 vrrp_cmd_list_arg_t list_arg
;
522 if ((cnt
== NULL
) || (*cnt
!= 0 && names
== NULL
))
523 return (VRRP_EINVAL
);
525 cmd
.vcl_ifname
[0] = '\0';
526 if (intf
!= NULL
&& (strlcpy(cmd
.vcl_ifname
, intf
,
527 LIFNAMSIZ
) >= LIFNAMSIZ
)) {
528 return (VRRP_EINVAL
);
532 * If the service is not online, we assume there is no router
535 if (!vrrp_svc_isonline(VRRP_SERVICE
)) {
537 return (VRRP_SUCCESS
);
540 cmd
.vcl_cmd
= VRRP_CMD_LIST
;
544 list_arg
.vfl_cnt
= cnt
;
545 list_arg
.vfl_names
= names
;
547 err
= vrrp_cmd_request(&cmd
, sizeof (cmd
), vrrp_list_func
, &list_arg
);
552 vrrp_query_func(int sock
, void *arg
)
554 vrrp_queryinfo_t
*qinfo
= arg
;
555 size_t len
, cur_size
= 0, total
;
556 uint32_t in_cnt
= qinfo
->show_va
.va_vipcnt
;
560 * Expect the ack, first get the vrrp_ret_t.
562 total
= sizeof (vrrp_queryinfo_t
);
563 while (cur_size
< total
) {
564 len
= read(sock
, (char *)qinfo
+ cur_size
, total
- cur_size
);
565 if (len
== (size_t)-1 && errno
== EAGAIN
) {
567 } else if (len
> 0) {
574 out_cnt
= qinfo
->show_va
.va_vipcnt
;
577 * Even if there is no IP virtual IP address, there is always
578 * space in the vrrp_queryinfo_t structure for one virtual
581 out_cnt
= (out_cnt
== 0) ? 1 : out_cnt
;
582 out_cnt
= (in_cnt
< out_cnt
? in_cnt
: out_cnt
) - 1;
583 total
+= out_cnt
* sizeof (vrrp_addr_t
);
585 while (cur_size
< total
) {
586 len
= read(sock
, (char *)qinfo
+ cur_size
, total
- cur_size
);
587 if (len
== (size_t)-1 && errno
== EAGAIN
) {
589 } else if (len
> 0) {
595 return (VRRP_SUCCESS
);
599 * *vqp is allocated inside this function and must be freed by the caller.
603 vrrp_query(vrrp_handle_t vh
, const char *vn
, vrrp_queryinfo_t
**vqp
)
605 vrrp_cmd_query_t cmd
;
606 vrrp_queryinfo_t
*qinfo
;
611 if (strlcpy(cmd
.vcq_name
, vn
, VRRP_NAME_MAX
) >= VRRP_NAME_MAX
)
612 return (VRRP_EINVAL
);
615 * If the service is not online, we assume there is no router
618 if (!vrrp_svc_isonline(VRRP_SERVICE
))
619 return (VRRP_ENOTFOUND
);
621 cmd
.vcq_cmd
= VRRP_CMD_QUERY
;
624 * Allocate enough room for virtual IPs.
627 size
= sizeof (vrrp_queryinfo_t
);
628 size
+= (vipcnt
== 0) ? 0 : (vipcnt
- 1) * sizeof (vrrp_addr_t
);
629 if ((qinfo
= malloc(size
)) == NULL
) {
634 qinfo
->show_va
.va_vipcnt
= vipcnt
;
635 err
= vrrp_cmd_request(&cmd
, sizeof (cmd
), vrrp_query_func
, qinfo
);
636 if (err
!= VRRP_SUCCESS
) {
642 * If the returned number of virtual IPs is greater than we expected,
643 * allocate more room and try again.
645 if (qinfo
->show_va
.va_vipcnt
> vipcnt
) {
646 vipcnt
= qinfo
->show_va
.va_vipcnt
;
657 struct lookup_vnic_arg
{
659 datalink_id_t lva_linkid
;
662 vrrp_handle_t lva_vh
;
663 char lva_vnic
[MAXLINKNAMELEN
];
667 * Is this a special VNIC interface created for VRRP? If so, return
668 * the linkid the VNIC was created on, the VRRP ID and address family.
671 vrrp_is_vrrp_vnic(vrrp_handle_t vh
, datalink_id_t vnicid
,
672 datalink_id_t
*linkidp
, uint16_t *vidp
, vrid_t
*vridp
, int *afp
)
674 dladm_vnic_attr_t vattr
;
676 if (dladm_vnic_info(vh
->vh_dh
, vnicid
, &vattr
, DLADM_OPT_ACTIVE
) !=
681 *vridp
= vattr
.va_vrid
;
682 *vidp
= vattr
.va_vid
;
684 *linkidp
= vattr
.va_link_id
;
685 return (vattr
.va_vrid
!= VRRP_VRID_NONE
);
689 lookup_vnic(dladm_handle_t dh
, datalink_id_t vnicid
, void *arg
)
693 datalink_id_t linkid
;
695 struct lookup_vnic_arg
*lva
= arg
;
697 if (vrrp_is_vrrp_vnic(lva
->lva_vh
, vnicid
, &linkid
, &vid
, &vrid
,
698 &af
) && lva
->lva_vrid
== vrid
&& lva
->lva_linkid
== linkid
&&
699 (lva
->lva_vid
== VLAN_ID_NONE
|| lva
->lva_vid
== vid
) &&
701 if (dladm_datalink_id2info(dh
, vnicid
, NULL
, NULL
, NULL
,
702 lva
->lva_vnic
, sizeof (lva
->lva_vnic
)) == DLADM_STATUS_OK
) {
703 return (DLADM_WALK_TERMINATE
);
706 return (DLADM_WALK_CONTINUE
);
710 * Given the primary link name, find the assoicated VRRP vnic name, if
711 * the vnic does not exist yet, return the linkid, vid of the primary link.
714 vrrp_get_vnicname(vrrp_handle_t vh
, vrid_t vrid
, int af
, char *link
,
715 datalink_id_t
*linkidp
, uint16_t *vidp
, char *vnic
, size_t len
)
717 datalink_id_t linkid
;
719 uint16_t vid
= VLAN_ID_NONE
;
720 datalink_class_t
class;
721 dladm_vlan_attr_t vlan_attr
;
722 dladm_vnic_attr_t vnic_attr
;
723 struct lookup_vnic_arg lva
;
726 if ((strlen(link
) == 0) || dladm_name2info(vh
->vh_dh
,
727 link
, &linkid
, &flags
, &class, &media
) !=
728 DLADM_STATUS_OK
|| !(flags
& DLADM_OPT_ACTIVE
)) {
729 return (VRRP_EINVAL
);
732 if (class == DATALINK_CLASS_VLAN
) {
733 if (dladm_vlan_info(vh
->vh_dh
, linkid
, &vlan_attr
,
734 DLADM_OPT_ACTIVE
) != DLADM_STATUS_OK
) {
735 return (VRRP_EINVAL
);
737 linkid
= vlan_attr
.dv_linkid
;
738 vid
= vlan_attr
.dv_vid
;
739 if ((dladm_datalink_id2info(vh
->vh_dh
, linkid
, NULL
,
740 &class, &media
, NULL
, 0)) != DLADM_STATUS_OK
) {
741 return (VRRP_EINVAL
);
745 if (class == DATALINK_CLASS_VNIC
) {
746 if (dladm_vnic_info(vh
->vh_dh
, linkid
, &vnic_attr
,
747 DLADM_OPT_ACTIVE
) != DLADM_STATUS_OK
) {
748 return (VRRP_EINVAL
);
750 linkid
= vnic_attr
.va_link_id
;
751 vid
= vnic_attr
.va_vid
;
755 * Only VRRP over vnics, aggrs and physical ethernet links is supported
757 if ((class != DATALINK_CLASS_PHYS
&& class != DATALINK_CLASS_AGGR
&&
758 class != DATALINK_CLASS_VNIC
) || media
!= DL_ETHER
) {
759 return (VRRP_EINVAL
);
768 * Find the assoicated vnic with the given vrid/vid/af/linkid
773 lva
.lva_linkid
= linkid
;
775 lva
.lva_vnic
[0] = '\0';
777 (void) dladm_walk_datalink_id(lookup_vnic
, vh
->vh_dh
, &lva
,
778 DATALINK_CLASS_VNIC
, DATALINK_ANY_MEDIATYPE
, DLADM_OPT_ACTIVE
);
779 if (strlen(lva
.lva_vnic
) != 0) {
780 (void) strlcpy(vnic
, lva
.lva_vnic
, len
);
781 return (VRRP_SUCCESS
);
784 return (VRRP_ENOVNIC
);