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