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
54typedef vrrp_err_t vrrp_cmd_func_t(int, void *);
55
56static boolean_t
57vrrp_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
73static vrrp_err_t
74vrrp_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 */
106static void
107vrrp_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
121static vrrp_err_t
122vrrp_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
193done:
194	(void) close(sock);
195	return (err);
196}
197
198/*
199 * public APIs
200 */
201const char *
202vrrp_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
262const char *
263vrrp_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
279vrrp_err_t
280vrrp_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
295void
296vrrp_close(vrrp_handle_t vh)
297{
298	if (vh != NULL) {
299		dladm_close(vh->vh_dh);
300		free(vh);
301	}
302}
303
304boolean_t
305vrrp_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*/
322vrrp_err_t
323vrrp_create(vrrp_handle_t vh, vrrp_vr_conf_t *conf)
324{
325	vrrp_cmd_create_t	cmd;
326	vrrp_err_t		err;
327
328again:
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*/
357vrrp_err_t
358vrrp_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*/
381vrrp_err_t
382vrrp_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*/
403vrrp_err_t
404vrrp_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*/
425vrrp_err_t
426vrrp_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
446typedef struct vrrp_cmd_list_arg {
447	uint32_t	*vfl_cnt;
448	char		*vfl_names;
449} vrrp_cmd_list_arg_t;
450
451static vrrp_err_t
452vrrp_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*/
510vrrp_err_t
511vrrp_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
547static vrrp_err_t
548vrrp_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*/
598vrrp_err_t
599vrrp_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	 */
622again:
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
649done:
650	return (err);
651}
652
653struct 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 */
666boolean_t
667vrrp_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
684static int
685lookup_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 */
708vrrp_err_t
709vrrp_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