xref: /illumos-gate/usr/src/lib/libvrrpadm/common/libvrrpadm.c (revision 7ab4e62e3b5c454f248a38bec0d489e8f5543324)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/socket.h>
34 #include <sys/mman.h>
35 #include <sys/varargs.h>
36 #include <sys/vlan.h>
37 #include <errno.h>
38 #include <ctype.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <net/if.h>	/* LIFNAMSIZ */
47 #include <netinet/vrrp.h>
48 #include <libdladm.h>
49 #include <libdlvnic.h>
50 #include <libdlvlan.h>
51 #include <libdllink.h>
52 #include <libintl.h>
53 #include <libscf.h>
54 #include <libvrrpadm.h>
55 
56 #define	VRRP_SERVICE	"network/vrrp:default"
57 
58 typedef vrrp_err_t vrrp_cmd_func_t(int, void *);
59 
60 static boolean_t
61 vrrp_svc_isonline(char *svc_name)
62 {
63 	char		*s;
64 	boolean_t	isonline = B_FALSE;
65 
66 	if ((s = smf_get_state(svc_name)) != NULL) {
67 		if (strcmp(s, SCF_STATE_STRING_ONLINE) == 0)
68 			isonline = B_TRUE;
69 		free(s);
70 	}
71 
72 	return (isonline);
73 }
74 
75 #define	MAX_WAIT_TIME	15
76 
77 static vrrp_err_t
78 vrrp_enable_service()
79 {
80 	int	i;
81 
82 	if (vrrp_svc_isonline(VRRP_SERVICE))
83 		return (VRRP_SUCCESS);
84 
85 	if (smf_enable_instance(VRRP_SERVICE, 0) == -1) {
86 		if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
87 			return (VRRP_EPERM);
88 		else
89 			return (VRRP_ENOSVC);
90 	}
91 
92 	/*
93 	 * Wait up to MAX_WAIT_TIME seconds for the VRRP service being brought
94 	 * up online
95 	 */
96 	for (i = 0; i < MAX_WAIT_TIME; i++) {
97 		if (vrrp_svc_isonline(VRRP_SERVICE))
98 			break;
99 		(void) sleep(1);
100 	}
101 	if (i == MAX_WAIT_TIME)
102 		return (VRRP_ENOSVC);
103 
104 	return (VRRP_SUCCESS);
105 }
106 
107 /*
108  * Disable the VRRP service if there is no VRRP router left.
109  */
110 static void
111 vrrp_disable_service_when_no_router()
112 {
113 	uint32_t	cnt = 0;
114 
115 	/*
116 	 * Get the number of the existing routers. If there is no routers
117 	 * left, disable the service.
118 	 */
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);
122 	}
123 }
124 
125 static vrrp_err_t
126 vrrp_cmd_request(void *cmd, size_t csize, vrrp_cmd_func_t func, void *arg)
127 {
128 	struct sockaddr_un	to;
129 	int			sock, flags;
130 	size_t			len, cur_size = 0;
131 	vrrp_ret_t		ret;
132 	vrrp_err_t		err;
133 
134 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
135 		return (VRRP_ESYS);
136 
137 	/*
138 	 * Set it to be non-blocking.
139 	 */
140 	flags = fcntl(sock, F_GETFL, 0);
141 	(void) fcntl(sock, F_SETFL, (flags | O_NONBLOCK));
142 
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));
146 
147 	/*
148 	 * Connect to vrrpd
149 	 */
150 	if (connect(sock, (const struct sockaddr *)&to, sizeof (to)) < 0) {
151 		(void) close(sock);
152 		return (VRRP_ENOSVC);
153 	}
154 
155 	/*
156 	 * Send the request
157 	 */
158 	while (cur_size < csize) {
159 		len = write(sock, (char *)cmd + cur_size, csize - cur_size);
160 		if (len == (size_t)-1 && errno == EAGAIN) {
161 			continue;
162 		} else if (len > 0) {
163 			cur_size += len;
164 			continue;
165 		}
166 		(void) close(sock);
167 		return (VRRP_ENOSVC);
168 	}
169 
170 	/*
171 	 * Expect the ack, first get the error code.
172 	 */
173 	cur_size = 0;
174 	while (cur_size < sizeof (vrrp_err_t)) {
175 		len = read(sock, (char *)&ret + cur_size,
176 		    sizeof (vrrp_err_t) - cur_size);
177 
178 		if (len == (size_t)-1 && errno == EAGAIN) {
179 			continue;
180 		} else if (len > 0) {
181 			cur_size += len;
182 			continue;
183 		}
184 		(void) close(sock);
185 		return (VRRP_ESYS);
186 	}
187 
188 	if ((err = ret.vr_err) != VRRP_SUCCESS)
189 		goto done;
190 
191 	/*
192 	 * The specific callback gets the rest of the information.
193 	 */
194 	if (func != NULL)
195 		err = func(sock, arg);
196 
197 done:
198 	(void) close(sock);
199 	return (err);
200 }
201 
202 /*
203  * public APIs
204  */
205 const char *
206 vrrp_err2str(vrrp_err_t err)
207 {
208 	switch (err) {
209 	case VRRP_SUCCESS:
210 		return (dgettext(TEXT_DOMAIN, "success"));
211 	case VRRP_ENOMEM:
212 		return (dgettext(TEXT_DOMAIN, "not enough memory"));
213 	case VRRP_EINVALVRNAME:
214 		return (dgettext(TEXT_DOMAIN, "invalid router name"));
215 	case VRRP_ENOPRIM:
216 		return (dgettext(TEXT_DOMAIN, "no primary IP"));
217 	case VRRP_EEXIST:
218 		return (dgettext(TEXT_DOMAIN, "already exists"));
219 	case VRRP_ENOVIRT:
220 		return (dgettext(TEXT_DOMAIN, "no virtual IPs"));
221 	case VRRP_EIPADM:
222 		return (dgettext(TEXT_DOMAIN, "ip configuration failure"));
223 	case VRRP_EDLADM:
224 		return (dgettext(TEXT_DOMAIN, "data-link configuration "
225 		    "failure"));
226 	case VRRP_EDB:
227 		return (dgettext(TEXT_DOMAIN, "configuration update error"));
228 	case VRRP_EBADSTATE:
229 		return (dgettext(TEXT_DOMAIN, "invalid state"));
230 	case VRRP_EVREXIST:
231 		return (dgettext(TEXT_DOMAIN, "VRRP router already exists"));
232 	case VRRP_ETOOSMALL:
233 		return (dgettext(TEXT_DOMAIN, "not enough space"));
234 	case VRRP_EINSTEXIST:
235 		return (dgettext(TEXT_DOMAIN, "router name already exists"));
236 	case VRRP_ENOTFOUND:
237 		return (dgettext(TEXT_DOMAIN, "VRRP router not found"));
238 	case VRRP_EINVALADDR:
239 		return (dgettext(TEXT_DOMAIN, "invalid IP address"));
240 	case VRRP_EINVALAF:
241 		return (dgettext(TEXT_DOMAIN, "invalid IP address family"));
242 	case VRRP_EINVALLINK:
243 		return (dgettext(TEXT_DOMAIN, "invalid data-link"));
244 	case VRRP_EPERM:
245 		return (dgettext(TEXT_DOMAIN, "permission denied"));
246 	case VRRP_ESYS:
247 		return (dgettext(TEXT_DOMAIN, "system error"));
248 	case VRRP_EAGAIN:
249 		return (dgettext(TEXT_DOMAIN, "try again"));
250 	case VRRP_EALREADY:
251 		return (dgettext(TEXT_DOMAIN, "operation already in progress"));
252 	case VRRP_ENOVNIC:
253 		return (dgettext(TEXT_DOMAIN, "VRRP VNIC has not been "
254 		    "created"));
255 	case VRRP_ENOLINK:
256 		return (dgettext(TEXT_DOMAIN, "the data-link does not exist"));
257 	case VRRP_ENOSVC:
258 		return (dgettext(TEXT_DOMAIN, "the VRRP service cannot "
259 		    "be enabled"));
260 	case VRRP_EINVAL:
261 	default:
262 		return (dgettext(TEXT_DOMAIN, "invalid argument"));
263 	}
264 }
265 
266 const char *
267 vrrp_state2str(vrrp_state_t state)
268 {
269 	switch (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"));
278 	default:
279 		return (dgettext(TEXT_DOMAIN, "INVALID"));
280 	}
281 }
282 
283 vrrp_err_t
284 vrrp_open(vrrp_handle_t *vh)
285 {
286 	dladm_handle_t	dh;
287 
288 	if (dladm_open(&dh) != DLADM_STATUS_OK)
289 		return (VRRP_EDLADM);
290 
291 	if ((*vh = malloc(sizeof (struct vrrp_handle))) == NULL) {
292 		dladm_close(dh);
293 		return (VRRP_ENOMEM);
294 	}
295 	(*vh)->vh_dh = dh;
296 	return (VRRP_SUCCESS);
297 }
298 
299 void
300 vrrp_close(vrrp_handle_t vh)
301 {
302 	if (vh != NULL) {
303 		dladm_close(vh->vh_dh);
304 		free(vh);
305 	}
306 }
307 
308 boolean_t
309 vrrp_valid_name(const char *name)
310 {
311 	const char	*c;
312 
313 	/*
314 	 * The legal characters in a valid router name are:
315 	 * alphanumeric (a-z,  A-Z,  0-9), underscore ('_'), and '.'.
316 	 */
317 	for (c = name; *c != '\0'; c++) {
318 		if ((isalnum(*c) == 0) && (*c != '_'))
319 			return (B_FALSE);
320 	}
321 
322 	return (B_TRUE);
323 }
324 
325 /*ARGSUSED*/
326 vrrp_err_t
327 vrrp_create(vrrp_handle_t vh, vrrp_vr_conf_t *conf)
328 {
329 	vrrp_cmd_create_t	cmd;
330 	vrrp_err_t		err;
331 
332 again:
333 	/*
334 	 * Enable the VRRP service if it is not already enabled.
335 	 */
336 	if ((err = vrrp_enable_service()) != VRRP_SUCCESS)
337 		return (err);
338 
339 	cmd.vcc_cmd = VRRP_CMD_CREATE;
340 	(void) memcpy(&cmd.vcc_conf, conf, sizeof (vrrp_vr_conf_t));
341 
342 	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
343 	if (err == VRRP_ENOSVC) {
344 		/*
345 		 * This may be due to another process is deleting the last
346 		 * router and disabled the VRRP service, try again.
347 		 */
348 		goto again;
349 	} else if (err != VRRP_SUCCESS) {
350 		/*
351 		 * If router cannot be created, check if the VRRP service
352 		 * should be disabled, and disable if needed.
353 		 */
354 		vrrp_disable_service_when_no_router();
355 	}
356 
357 	return (err);
358 }
359 
360 /*ARGSUSED*/
361 vrrp_err_t
362 vrrp_delete(vrrp_handle_t vh, const char *vn)
363 {
364 	vrrp_cmd_delete_t	cmd;
365 	vrrp_err_t		err;
366 
367 	/*
368 	 * If the VRRP service is not enabled, we assume there is no router
369 	 * configured.
370 	 */
371 	if (!vrrp_svc_isonline(VRRP_SERVICE))
372 		return (VRRP_ENOTFOUND);
373 
374 	cmd.vcd_cmd = VRRP_CMD_DELETE;
375 	if (strlcpy(cmd.vcd_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX)
376 		return (VRRP_EINVAL);
377 
378 	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
379 	if (err == VRRP_SUCCESS)
380 		vrrp_disable_service_when_no_router();
381 	return (err);
382 }
383 
384 /*ARGSUSED*/
385 vrrp_err_t
386 vrrp_enable(vrrp_handle_t vh, const char *vn)
387 {
388 	vrrp_cmd_enable_t	cmd;
389 	vrrp_err_t		err;
390 
391 	/*
392 	 * If the VRRP service is not enabled, we assume there is no router
393 	 * configured.
394 	 */
395 	if (!vrrp_svc_isonline(VRRP_SERVICE))
396 		return (VRRP_ENOTFOUND);
397 
398 	cmd.vcs_cmd = VRRP_CMD_ENABLE;
399 	if (strlcpy(cmd.vcs_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX)
400 		return (VRRP_EINVAL);
401 
402 	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
403 	return (err);
404 }
405 
406 /*ARGSUSED*/
407 vrrp_err_t
408 vrrp_disable(vrrp_handle_t vh, const char *vn)
409 {
410 	vrrp_cmd_disable_t	cmd;
411 	vrrp_err_t		err;
412 
413 	/*
414 	 * If the VRRP service is not enabled, we assume there is no router
415 	 * configured.
416 	 */
417 	if (!vrrp_svc_isonline(VRRP_SERVICE))
418 		return (VRRP_ENOTFOUND);
419 
420 	cmd.vcx_cmd = VRRP_CMD_DISABLE;
421 	if (strlcpy(cmd.vcx_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX)
422 		return (VRRP_EINVAL);
423 
424 	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
425 	return (err);
426 }
427 
428 /*ARGSUSED*/
429 vrrp_err_t
430 vrrp_modify(vrrp_handle_t vh, vrrp_vr_conf_t *conf, uint32_t mask)
431 {
432 	vrrp_cmd_modify_t	cmd;
433 	vrrp_err_t		err;
434 
435 	/*
436 	 * If the VRRP service is not enabled, we assume there is no router
437 	 * configured.
438 	 */
439 	if (!vrrp_svc_isonline(VRRP_SERVICE))
440 		return (VRRP_ENOTFOUND);
441 
442 	cmd.vcm_cmd = VRRP_CMD_MODIFY;
443 	cmd.vcm_mask = mask;
444 	(void) memcpy(&cmd.vcm_conf, conf, sizeof (vrrp_vr_conf_t));
445 
446 	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
447 	return (err);
448 }
449 
450 typedef struct vrrp_cmd_list_arg {
451 	uint32_t	*vfl_cnt;
452 	char		*vfl_names;
453 } vrrp_cmd_list_arg_t;
454 
455 static vrrp_err_t
456 vrrp_list_func(int sock, void *arg)
457 {
458 	vrrp_cmd_list_arg_t	*list_arg = arg;
459 	uint32_t		in_cnt = *(list_arg->vfl_cnt);
460 	uint32_t		out_cnt;
461 	vrrp_ret_list_t		ret;
462 	size_t			len, cur_size = 0;
463 
464 	/*
465 	 * Get the rest of vrrp_ret_list_t besides the error code.
466 	 */
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);
471 
472 		if (len == (size_t)-1 && errno == EAGAIN) {
473 			continue;
474 		} else if (len > 0) {
475 			cur_size += len;
476 			continue;
477 		}
478 		return (VRRP_ESYS);
479 	}
480 
481 	*(list_arg->vfl_cnt) = out_cnt = ret.vrl_cnt;
482 	out_cnt = (in_cnt <= out_cnt) ? in_cnt : out_cnt;
483 	cur_size = 0;
484 
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);
488 
489 		if (len == (size_t)-1 && errno == EAGAIN) {
490 			continue;
491 		} else if (len > 0) {
492 			cur_size += len;
493 			continue;
494 		}
495 		return (VRRP_ESYS);
496 	}
497 	return (VRRP_SUCCESS);
498 }
499 
500 /*
501  * Looks up the vrrp instances that matches the given variable.
502  *
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.
505  *
506  * If the given cnt is non-zero, caller must allocate "names" whose size
507  * is (cnt * VRRP_NAME_MAX).
508  *
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.
512  */
513 /*ARGSUSED*/
514 vrrp_err_t
515 vrrp_list(vrrp_handle_t vh, vrid_t vrid, const char *intf, int af,
516     uint32_t *cnt, char *names)
517 {
518 	vrrp_cmd_list_t		cmd;
519 	vrrp_err_t		err;
520 	vrrp_cmd_list_arg_t	list_arg;
521 
522 	if ((cnt == NULL) || (*cnt != 0 && names == NULL))
523 		return (VRRP_EINVAL);
524 
525 	cmd.vcl_ifname[0] = '\0';
526 	if (intf != NULL && (strlcpy(cmd.vcl_ifname, intf,
527 	    LIFNAMSIZ) >= LIFNAMSIZ)) {
528 		return (VRRP_EINVAL);
529 	}
530 
531 	/*
532 	 * If the service is not online, we assume there is no router
533 	 * configured.
534 	 */
535 	if (!vrrp_svc_isonline(VRRP_SERVICE)) {
536 		*cnt = 0;
537 		return (VRRP_SUCCESS);
538 	}
539 
540 	cmd.vcl_cmd = VRRP_CMD_LIST;
541 	cmd.vcl_vrid = vrid;
542 	cmd.vcl_af = af;
543 
544 	list_arg.vfl_cnt = cnt;
545 	list_arg.vfl_names = names;
546 
547 	err = vrrp_cmd_request(&cmd, sizeof (cmd), vrrp_list_func, &list_arg);
548 	return (err);
549 }
550 
551 static vrrp_err_t
552 vrrp_query_func(int sock, void *arg)
553 {
554 	vrrp_queryinfo_t	*qinfo = arg;
555 	size_t			len, cur_size = 0, total;
556 	uint32_t		in_cnt = qinfo->show_va.va_vipcnt;
557 	uint32_t		out_cnt;
558 
559 	/*
560 	 * Expect the ack, first get the vrrp_ret_t.
561 	 */
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) {
566 			continue;
567 		} else if (len > 0) {
568 			cur_size += len;
569 			continue;
570 		}
571 		return (VRRP_ESYS);
572 	}
573 
574 	out_cnt = qinfo->show_va.va_vipcnt;
575 
576 	/*
577 	 * Even if there is no IP virtual IP address, there is always
578 	 * space in the vrrp_queryinfo_t structure for one virtual
579 	 * IP address.
580 	 */
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);
584 
585 	while (cur_size < total) {
586 		len = read(sock, (char *)qinfo + cur_size, total - cur_size);
587 		if (len == (size_t)-1 && errno == EAGAIN) {
588 			continue;
589 		} else if (len > 0) {
590 			cur_size += len;
591 			continue;
592 		}
593 		return (VRRP_ESYS);
594 	}
595 	return (VRRP_SUCCESS);
596 }
597 
598 /*
599  * *vqp is allocated inside this function and must be freed by the caller.
600  */
601 /*ARGSUSED*/
602 vrrp_err_t
603 vrrp_query(vrrp_handle_t vh, const char *vn, vrrp_queryinfo_t **vqp)
604 {
605 	vrrp_cmd_query_t	cmd;
606 	vrrp_queryinfo_t	*qinfo;
607 	vrrp_err_t		err;
608 	size_t			size;
609 	uint32_t		vipcnt = 1;
610 
611 	if (strlcpy(cmd.vcq_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX)
612 		return (VRRP_EINVAL);
613 
614 	/*
615 	 * If the service is not online, we assume there is no router
616 	 * configured.
617 	 */
618 	if (!vrrp_svc_isonline(VRRP_SERVICE))
619 		return (VRRP_ENOTFOUND);
620 
621 	cmd.vcq_cmd = VRRP_CMD_QUERY;
622 
623 	/*
624 	 * Allocate enough room for virtual IPs.
625 	 */
626 again:
627 	size = sizeof (vrrp_queryinfo_t);
628 	size += (vipcnt == 0) ? 0 : (vipcnt - 1) * sizeof (vrrp_addr_t);
629 	if ((qinfo = malloc(size)) == NULL) {
630 		err = VRRP_ENOMEM;
631 		goto done;
632 	}
633 
634 	qinfo->show_va.va_vipcnt = vipcnt;
635 	err = vrrp_cmd_request(&cmd, sizeof (cmd), vrrp_query_func, qinfo);
636 	if (err != VRRP_SUCCESS) {
637 		free(qinfo);
638 		goto done;
639 	}
640 
641 	/*
642 	 * If the returned number of virtual IPs is greater than we expected,
643 	 * allocate more room and try again.
644 	 */
645 	if (qinfo->show_va.va_vipcnt > vipcnt) {
646 		vipcnt = qinfo->show_va.va_vipcnt;
647 		free(qinfo);
648 		goto again;
649 	}
650 
651 	*vqp = qinfo;
652 
653 done:
654 	return (err);
655 }
656 
657 struct lookup_vnic_arg {
658 	vrid_t		lva_vrid;
659 	datalink_id_t	lva_linkid;
660 	int		lva_af;
661 	uint16_t	lva_vid;
662 	vrrp_handle_t	lva_vh;
663 	char		lva_vnic[MAXLINKNAMELEN];
664 };
665 
666 /*
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.
669  */
670 boolean_t
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)
673 {
674 	dladm_vnic_attr_t	vattr;
675 
676 	if (dladm_vnic_info(vh->vh_dh, vnicid, &vattr, DLADM_OPT_ACTIVE) !=
677 	    DLADM_STATUS_OK) {
678 		return (B_FALSE);
679 	}
680 
681 	*vridp = vattr.va_vrid;
682 	*vidp = vattr.va_vid;
683 	*afp = vattr.va_af;
684 	*linkidp = vattr.va_link_id;
685 	return (vattr.va_vrid != VRRP_VRID_NONE);
686 }
687 
688 static int
689 lookup_vnic(dladm_handle_t dh, datalink_id_t vnicid, void *arg)
690 {
691 	vrid_t			vrid;
692 	uint16_t		vid;
693 	datalink_id_t		linkid;
694 	int			af;
695 	struct lookup_vnic_arg	*lva = arg;
696 
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) &&
700 	    lva->lva_af == af) {
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);
704 		}
705 	}
706 	return (DLADM_WALK_CONTINUE);
707 }
708 
709 /*
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.
712  */
713 vrrp_err_t
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)
716 {
717 	datalink_id_t		linkid;
718 	uint32_t		flags;
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;
724 	uint32_t		media;
725 
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);
730 	}
731 
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);
736 		}
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);
742 		}
743 	}
744 
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);
749 		}
750 		linkid = vnic_attr.va_link_id;
751 		vid = vnic_attr.va_vid;
752 	}
753 
754 	/*
755 	 * Only VRRP over vnics, aggrs and physical ethernet links is supported
756 	 */
757 	if ((class != DATALINK_CLASS_PHYS && class != DATALINK_CLASS_AGGR &&
758 	    class != DATALINK_CLASS_VNIC) || media != DL_ETHER) {
759 		return (VRRP_EINVAL);
760 	}
761 
762 	if (linkidp != NULL)
763 		*linkidp = linkid;
764 	if (vidp != NULL)
765 		*vidp = vid;
766 
767 	/*
768 	 * Find the assoicated vnic with the given vrid/vid/af/linkid
769 	 */
770 	lva.lva_vrid = vrid;
771 	lva.lva_vid = vid;
772 	lva.lva_af = af;
773 	lva.lva_linkid = linkid;
774 	lva.lva_vh = vh;
775 	lva.lva_vnic[0] = '\0';
776 
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);
782 	}
783 
784 	return (VRRP_ENOVNIC);
785 }
786