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
58typedef vrrp_err_t vrrp_cmd_func_t(int, void *);
59
60static boolean_t
61vrrp_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
77static vrrp_err_t
78vrrp_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 */
110static void
111vrrp_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
125static vrrp_err_t
126vrrp_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
197done:
198	(void) close(sock);
199	return (err);
200}
201
202/*
203 * public APIs
204 */
205const char *
206vrrp_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
266const char *
267vrrp_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
283vrrp_err_t
284vrrp_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
299void
300vrrp_close(vrrp_handle_t vh)
301{
302	if (vh != NULL) {
303		dladm_close(vh->vh_dh);
304		free(vh);
305	}
306}
307
308boolean_t
309vrrp_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*/
326vrrp_err_t
327vrrp_create(vrrp_handle_t vh, vrrp_vr_conf_t *conf)
328{
329	vrrp_cmd_create_t	cmd;
330	vrrp_err_t		err;
331
332again:
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*/
361vrrp_err_t
362vrrp_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*/
385vrrp_err_t
386vrrp_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*/
407vrrp_err_t
408vrrp_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*/
429vrrp_err_t
430vrrp_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
450typedef struct vrrp_cmd_list_arg {
451	uint32_t	*vfl_cnt;
452	char		*vfl_names;
453} vrrp_cmd_list_arg_t;
454
455static vrrp_err_t
456vrrp_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*/
514vrrp_err_t
515vrrp_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
551static vrrp_err_t
552vrrp_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*/
602vrrp_err_t
603vrrp_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	 */
626again:
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
653done:
654	return (err);
655}
656
657struct 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 */
670boolean_t
671vrrp_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
688static int
689lookup_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 */
713vrrp_err_t
714vrrp_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