17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5ba2e4443Sseb  * Common Development and Distribution License (the "License").
6ba2e4443Sseb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*f4b3ec61Sdh  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
28*f4b3ec61Sdh #include <ctype.h>
297c478bd9Sstevel@tonic-gate #include <unistd.h>
307c478bd9Sstevel@tonic-gate #include <stropts.h>
317c478bd9Sstevel@tonic-gate #include <errno.h>
320ba2cbe9Sxc #include <fcntl.h>
330ba2cbe9Sxc #include <strings.h>
340ba2cbe9Sxc #include <dirent.h>
357c478bd9Sstevel@tonic-gate #include <net/if.h>
360ba2cbe9Sxc #include <sys/stat.h>
370ba2cbe9Sxc #include <sys/dld.h>
380ba2cbe9Sxc #include <libdlpi.h>
390ba2cbe9Sxc #include <libdevinfo.h>
400ba2cbe9Sxc #include <libdladm_impl.h>
4133343a97Smeem #include <libintl.h>
42*f4b3ec61Sdh #include <sys/vlan.h>
437c478bd9Sstevel@tonic-gate 
44aae21359Skrgopi typedef struct dladm_dev {
45aae21359Skrgopi 	char			dd_name[IFNAMSIZ];
46aae21359Skrgopi 	struct dladm_dev	*dd_next;
47aae21359Skrgopi } dladm_dev_t;
48aae21359Skrgopi 
49aae21359Skrgopi typedef struct dladm_walk {
50aae21359Skrgopi 	dladm_dev_t		*dw_dev_list;
51aae21359Skrgopi } dladm_walk_t;
52aae21359Skrgopi 
530ba2cbe9Sxc static char		dladm_rootdir[MAXPATHLEN] = "/";
540ba2cbe9Sxc 
557c478bd9Sstevel@tonic-gate /*
567c478bd9Sstevel@tonic-gate  * Issue an ioctl to the specified file descriptor attached to the
577c478bd9Sstevel@tonic-gate  * DLD control driver interface.
587c478bd9Sstevel@tonic-gate  */
590ba2cbe9Sxc int
60210db224Sericheng i_dladm_ioctl(int fd, int ic_cmd, void *ic_dp, int ic_len)
617c478bd9Sstevel@tonic-gate {
627c478bd9Sstevel@tonic-gate 	struct strioctl	iocb;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	iocb.ic_cmd = ic_cmd;
657c478bd9Sstevel@tonic-gate 	iocb.ic_timout = 0;
667c478bd9Sstevel@tonic-gate 	iocb.ic_len = ic_len;
67210db224Sericheng 	iocb.ic_dp = (char *)ic_dp;
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate 	return (ioctl(fd, I_STR, &iocb));
707c478bd9Sstevel@tonic-gate }
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate /*
737c478bd9Sstevel@tonic-gate  * Return the attributes of the specified datalink from the DLD driver.
747c478bd9Sstevel@tonic-gate  */
757c478bd9Sstevel@tonic-gate static int
767c478bd9Sstevel@tonic-gate i_dladm_info(int fd, const char *name, dladm_attr_t *dap)
777c478bd9Sstevel@tonic-gate {
787c478bd9Sstevel@tonic-gate 	dld_ioc_attr_t	dia;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 	if (strlen(name) >= IFNAMSIZ) {
817c478bd9Sstevel@tonic-gate 		errno = EINVAL;
827c478bd9Sstevel@tonic-gate 		return (-1);
837c478bd9Sstevel@tonic-gate 	}
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	(void) strlcpy(dia.dia_name, name, IFNAMSIZ);
867c478bd9Sstevel@tonic-gate 
87210db224Sericheng 	if (i_dladm_ioctl(fd, DLDIOCATTR, &dia, sizeof (dia)) < 0)
887c478bd9Sstevel@tonic-gate 		return (-1);
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	(void) strlcpy(dap->da_dev, dia.dia_dev, MAXNAMELEN);
91210db224Sericheng 	dap->da_max_sdu = dia.dia_max_sdu;
927c478bd9Sstevel@tonic-gate 	dap->da_vid = dia.dia_vid;
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate 	return (0);
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate /*
987c478bd9Sstevel@tonic-gate  * Adds a datalink to the array corresponding to arg.
997c478bd9Sstevel@tonic-gate  */
1007c478bd9Sstevel@tonic-gate static void
1017c478bd9Sstevel@tonic-gate i_dladm_nt_net_add(void *arg, char *name)
1027c478bd9Sstevel@tonic-gate {
103aae21359Skrgopi 	dladm_walk_t	*dwp = arg;
104aae21359Skrgopi 	dladm_dev_t	*ddp = dwp->dw_dev_list;
105aae21359Skrgopi 	dladm_dev_t	**lastp = &dwp->dw_dev_list;
106aae21359Skrgopi 
107aae21359Skrgopi 	while (ddp) {
108aae21359Skrgopi 		/*
109aae21359Skrgopi 		 * Skip duplicates.
110aae21359Skrgopi 		 */
111aae21359Skrgopi 		if (strcmp(ddp->dd_name, name) == 0)
1127c478bd9Sstevel@tonic-gate 			return;
113aae21359Skrgopi 
114aae21359Skrgopi 		lastp = &ddp->dd_next;
115aae21359Skrgopi 		ddp = ddp->dd_next;
1167c478bd9Sstevel@tonic-gate 	}
1177c478bd9Sstevel@tonic-gate 
118aae21359Skrgopi 	if ((ddp = malloc(sizeof (*ddp))) == NULL)
119aae21359Skrgopi 		return;
120aae21359Skrgopi 
121aae21359Skrgopi 	(void) strlcpy(ddp->dd_name, name, IFNAMSIZ);
122aae21359Skrgopi 	ddp->dd_next = NULL;
123aae21359Skrgopi 	*lastp = ddp;
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate /*
1277c478bd9Sstevel@tonic-gate  * Walker callback invoked for each DDI_NT_NET node.
1287c478bd9Sstevel@tonic-gate  */
1297c478bd9Sstevel@tonic-gate static int
1307c478bd9Sstevel@tonic-gate i_dladm_nt_net_walk(di_node_t node, di_minor_t minor, void *arg)
1317c478bd9Sstevel@tonic-gate {
1327c478bd9Sstevel@tonic-gate 	dl_info_ack_t	dlia;
1337c478bd9Sstevel@tonic-gate 	char		name[IFNAMSIZ];
1347c478bd9Sstevel@tonic-gate 	int		fd;
1357c478bd9Sstevel@tonic-gate 	char		*provider;
1367c478bd9Sstevel@tonic-gate 	uint_t		ppa;
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	provider = di_minor_name(minor);
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	if ((fd = dlpi_open(provider)) < 0)
1417c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) < 0) {
1447c478bd9Sstevel@tonic-gate 		(void) dlpi_close(fd);
1457c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1467c478bd9Sstevel@tonic-gate 	}
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	if (dlia.dl_provider_style == DL_STYLE1) {
1497c478bd9Sstevel@tonic-gate 		i_dladm_nt_net_add(arg, provider);
1507c478bd9Sstevel@tonic-gate 		(void) dlpi_close(fd);
1517c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1527c478bd9Sstevel@tonic-gate 	}
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	ppa = di_instance(node);
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	if (dlpi_attach(fd, -1, ppa) < 0) {
1577c478bd9Sstevel@tonic-gate 		(void) dlpi_close(fd);
1587c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 	(void) snprintf(name, IFNAMSIZ - 1, "%s%d", provider, ppa);
1617c478bd9Sstevel@tonic-gate 	i_dladm_nt_net_add(arg, name);
1627c478bd9Sstevel@tonic-gate 	(void) dlpi_close(fd);
1637c478bd9Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate 
166*f4b3ec61Sdh /*
167*f4b3ec61Sdh  * Hold a data-link.
168*f4b3ec61Sdh  */
169*f4b3ec61Sdh static int
170*f4b3ec61Sdh i_dladm_hold_link(const char *name, zoneid_t zoneid, boolean_t docheck)
171*f4b3ec61Sdh {
172*f4b3ec61Sdh 	int		fd;
173*f4b3ec61Sdh 	dld_hold_vlan_t	dhv;
174*f4b3ec61Sdh 
175*f4b3ec61Sdh 	if (strlen(name) >= IFNAMSIZ) {
176*f4b3ec61Sdh 		errno = EINVAL;
177*f4b3ec61Sdh 		return (-1);
178*f4b3ec61Sdh 	}
179*f4b3ec61Sdh 
180*f4b3ec61Sdh 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
181*f4b3ec61Sdh 		return (-1);
182*f4b3ec61Sdh 
183*f4b3ec61Sdh 	bzero(&dhv, sizeof (dld_hold_vlan_t));
184*f4b3ec61Sdh 	(void) strlcpy(dhv.dhv_name, name, IFNAMSIZ);
185*f4b3ec61Sdh 	dhv.dhv_zid = zoneid;
186*f4b3ec61Sdh 	dhv.dhv_docheck = docheck;
187*f4b3ec61Sdh 
188*f4b3ec61Sdh 	if (i_dladm_ioctl(fd, DLDIOCHOLDVLAN, &dhv, sizeof (dhv)) < 0) {
189*f4b3ec61Sdh 		int olderrno = errno;
190*f4b3ec61Sdh 
191*f4b3ec61Sdh 		(void) close(fd);
192*f4b3ec61Sdh 		errno = olderrno;
193*f4b3ec61Sdh 		return (-1);
194*f4b3ec61Sdh 	}
195*f4b3ec61Sdh 
196*f4b3ec61Sdh 	(void) close(fd);
197*f4b3ec61Sdh 	return (0);
198*f4b3ec61Sdh }
199*f4b3ec61Sdh 
200*f4b3ec61Sdh /*
201*f4b3ec61Sdh  * Release a data-link.
202*f4b3ec61Sdh  */
203*f4b3ec61Sdh static int
204*f4b3ec61Sdh i_dladm_rele_link(const char *name, zoneid_t zoneid, boolean_t docheck)
205*f4b3ec61Sdh {
206*f4b3ec61Sdh 	int		fd;
207*f4b3ec61Sdh 	dld_hold_vlan_t	dhv;
208*f4b3ec61Sdh 
209*f4b3ec61Sdh 	if (strlen(name) >= IFNAMSIZ) {
210*f4b3ec61Sdh 		errno = EINVAL;
211*f4b3ec61Sdh 		return (-1);
212*f4b3ec61Sdh 	}
213*f4b3ec61Sdh 
214*f4b3ec61Sdh 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
215*f4b3ec61Sdh 		return (-1);
216*f4b3ec61Sdh 
217*f4b3ec61Sdh 	bzero(&dhv, sizeof (dld_hold_vlan_t));
218*f4b3ec61Sdh 	(void) strlcpy(dhv.dhv_name, name, IFNAMSIZ);
219*f4b3ec61Sdh 	dhv.dhv_zid = zoneid;
220*f4b3ec61Sdh 	dhv.dhv_docheck = docheck;
221*f4b3ec61Sdh 
222*f4b3ec61Sdh 	if (i_dladm_ioctl(fd, DLDIOCRELEVLAN, &dhv, sizeof (dhv)) < 0) {
223*f4b3ec61Sdh 		int olderrno = errno;
224*f4b3ec61Sdh 
225*f4b3ec61Sdh 		(void) close(fd);
226*f4b3ec61Sdh 		errno = olderrno;
227*f4b3ec61Sdh 		return (-1);
228*f4b3ec61Sdh 	}
229*f4b3ec61Sdh 
230*f4b3ec61Sdh 	(void) close(fd);
231*f4b3ec61Sdh 	return (0);
232*f4b3ec61Sdh }
233*f4b3ec61Sdh 
2347c478bd9Sstevel@tonic-gate /*
2357c478bd9Sstevel@tonic-gate  * Invoke the specified callback function for each active DDI_NT_NET
2367c478bd9Sstevel@tonic-gate  * node.
2377c478bd9Sstevel@tonic-gate  */
2387c478bd9Sstevel@tonic-gate int
2397c478bd9Sstevel@tonic-gate dladm_walk(void (*fn)(void *, const char *), void *arg)
2407c478bd9Sstevel@tonic-gate {
2417c478bd9Sstevel@tonic-gate 	di_node_t	root;
242aae21359Skrgopi 	dladm_walk_t	dw;
243aae21359Skrgopi 	dladm_dev_t	*ddp, *last_ddp;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
2467c478bd9Sstevel@tonic-gate 		errno = EFAULT;
2477c478bd9Sstevel@tonic-gate 		return (-1);
2487c478bd9Sstevel@tonic-gate 	}
249aae21359Skrgopi 	dw.dw_dev_list = NULL;
2507c478bd9Sstevel@tonic-gate 
251aae21359Skrgopi 	(void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dw,
2527c478bd9Sstevel@tonic-gate 	    i_dladm_nt_net_walk);
253aae21359Skrgopi 
2547c478bd9Sstevel@tonic-gate 	di_fini(root);
2557c478bd9Sstevel@tonic-gate 
256aae21359Skrgopi 	ddp = dw.dw_dev_list;
257aae21359Skrgopi 	while (ddp) {
258aae21359Skrgopi 		fn(arg, ddp->dd_name);
259aae21359Skrgopi 		last_ddp = ddp;
260aae21359Skrgopi 		ddp = ddp->dd_next;
261aae21359Skrgopi 		free(last_ddp);
2627c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 
264aae21359Skrgopi 	return (0);
2657c478bd9Sstevel@tonic-gate }
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate /*
268210db224Sericheng  * Invoke the specified callback function for each vlan managed by dld
2697c478bd9Sstevel@tonic-gate  */
2707c478bd9Sstevel@tonic-gate int
271aae21359Skrgopi dladm_walk_vlan(void (*fn)(void *, const char *), void *arg, const char *name)
2727c478bd9Sstevel@tonic-gate {
273aae21359Skrgopi 	int		fd, bufsize, i;
274aae21359Skrgopi 	int		nvlan = 4094;
275210db224Sericheng 	dld_ioc_vlan_t	*iocp = NULL;
276210db224Sericheng 	dld_vlan_info_t	*dvip;
2777c478bd9Sstevel@tonic-gate 
278210db224Sericheng 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
2797c478bd9Sstevel@tonic-gate 		return (-1);
2807c478bd9Sstevel@tonic-gate 
281aae21359Skrgopi 	bufsize = sizeof (dld_ioc_vlan_t) + nvlan * sizeof (dld_vlan_info_t);
2827c478bd9Sstevel@tonic-gate 
283aae21359Skrgopi 	if ((iocp = (dld_ioc_vlan_t *)calloc(1, bufsize)) == NULL)
284aae21359Skrgopi 		return (-1);
2857c478bd9Sstevel@tonic-gate 
286ba2e4443Sseb 	(void) strlcpy((char *)iocp->div_name, name, IFNAMSIZ);
287aae21359Skrgopi 	if (i_dladm_ioctl(fd, DLDIOCVLAN, iocp, bufsize) == 0) {
288aae21359Skrgopi 		dvip = (dld_vlan_info_t *)(iocp + 1);
289aae21359Skrgopi 		for (i = 0; i < iocp->div_count; i++)
290210db224Sericheng 			(*fn)(arg, dvip[i].dvi_name);
2917c478bd9Sstevel@tonic-gate 	}
292aae21359Skrgopi 	/*
293aae21359Skrgopi 	 * Note: Callers of dladm_walk_vlan() ignore the return
294aae21359Skrgopi 	 * value of this routine. So ignoring ioctl failure case
295aae21359Skrgopi 	 * and just returning 0.
296aae21359Skrgopi 	 */
297210db224Sericheng 	free(iocp);
2987c478bd9Sstevel@tonic-gate 	(void) close(fd);
2997c478bd9Sstevel@tonic-gate 	return (0);
3007c478bd9Sstevel@tonic-gate }
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate /*
3047c478bd9Sstevel@tonic-gate  * Returns the current attributes of the specified datalink.
3057c478bd9Sstevel@tonic-gate  */
3067c478bd9Sstevel@tonic-gate int
3077c478bd9Sstevel@tonic-gate dladm_info(const char *name, dladm_attr_t *dap)
3087c478bd9Sstevel@tonic-gate {
3097c478bd9Sstevel@tonic-gate 	int		fd;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
3127c478bd9Sstevel@tonic-gate 		return (-1);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	if (i_dladm_info(fd, name, dap) < 0)
3157c478bd9Sstevel@tonic-gate 		goto failed;
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	(void) close(fd);
3187c478bd9Sstevel@tonic-gate 	return (0);
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate failed:
3217c478bd9Sstevel@tonic-gate 	(void) close(fd);
3227c478bd9Sstevel@tonic-gate 	return (-1);
3237c478bd9Sstevel@tonic-gate }
3240ba2cbe9Sxc 
3250ba2cbe9Sxc const char *
3260ba2cbe9Sxc dladm_status2str(dladm_status_t status, char *buf)
3270ba2cbe9Sxc {
3280ba2cbe9Sxc 	const char	*s;
3290ba2cbe9Sxc 
3300ba2cbe9Sxc 	switch (status) {
3310ba2cbe9Sxc 	case DLADM_STATUS_OK:
3320ba2cbe9Sxc 		s = "ok";
3330ba2cbe9Sxc 		break;
3340ba2cbe9Sxc 	case DLADM_STATUS_BADARG:
3350ba2cbe9Sxc 		s = "invalid argument";
3360ba2cbe9Sxc 		break;
3370ba2cbe9Sxc 	case DLADM_STATUS_FAILED:
3380ba2cbe9Sxc 		s = "operation failed";
3390ba2cbe9Sxc 		break;
3400ba2cbe9Sxc 	case DLADM_STATUS_TOOSMALL:
3410ba2cbe9Sxc 		s = "buffer size too small";
3420ba2cbe9Sxc 		break;
3430ba2cbe9Sxc 	case DLADM_STATUS_NOTSUP:
3440ba2cbe9Sxc 		s = "operation not supported";
3450ba2cbe9Sxc 		break;
3460ba2cbe9Sxc 	case DLADM_STATUS_NOTFOUND:
3470ba2cbe9Sxc 		s = "object not found";
3480ba2cbe9Sxc 		break;
3490ba2cbe9Sxc 	case DLADM_STATUS_BADVAL:
3500ba2cbe9Sxc 		s = "invalid value";
3510ba2cbe9Sxc 		break;
3520ba2cbe9Sxc 	case DLADM_STATUS_NOMEM:
3530ba2cbe9Sxc 		s = "insufficient memory";
3540ba2cbe9Sxc 		break;
3550ba2cbe9Sxc 	case DLADM_STATUS_EXIST:
3560ba2cbe9Sxc 		s = "object already exists";
3570ba2cbe9Sxc 		break;
3580ba2cbe9Sxc 	case DLADM_STATUS_LINKINVAL:
3590ba2cbe9Sxc 		s = "invalid link";
3600ba2cbe9Sxc 		break;
3610ba2cbe9Sxc 	case DLADM_STATUS_PROPRDONLY:
3620ba2cbe9Sxc 		s = "read-only property";
3630ba2cbe9Sxc 		break;
3640ba2cbe9Sxc 	case DLADM_STATUS_BADVALCNT:
3650ba2cbe9Sxc 		s = "invalid number of values";
3660ba2cbe9Sxc 		break;
3670ba2cbe9Sxc 	case DLADM_STATUS_DBNOTFOUND:
3680ba2cbe9Sxc 		s = "database not found";
3690ba2cbe9Sxc 		break;
3700ba2cbe9Sxc 	case DLADM_STATUS_DENIED:
3710ba2cbe9Sxc 		s = "permission denied";
3720ba2cbe9Sxc 		break;
3730ba2cbe9Sxc 	case DLADM_STATUS_IOERR:
3740ba2cbe9Sxc 		s = "I/O error";
3750ba2cbe9Sxc 		break;
376*f4b3ec61Sdh 	case DLADM_STATUS_TEMPONLY:
377*f4b3ec61Sdh 		s = "change cannot be persistent, specify -t please";
378*f4b3ec61Sdh 		break;
3790ba2cbe9Sxc 	default:
38033343a97Smeem 		s = "<unknown error>";
38133343a97Smeem 		break;
3820ba2cbe9Sxc 	}
38333343a97Smeem 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
3840ba2cbe9Sxc 	return (buf);
3850ba2cbe9Sxc }
3860ba2cbe9Sxc 
3870ba2cbe9Sxc /*
3880ba2cbe9Sxc  * Convert a unix errno to a dladm_status_t.
3890ba2cbe9Sxc  * We only convert errnos that are likely to be encountered. All others
3900ba2cbe9Sxc  * are mapped to DLADM_STATUS_FAILED.
3910ba2cbe9Sxc  */
3920ba2cbe9Sxc dladm_status_t
3930ba2cbe9Sxc dladm_errno2status(int err)
3940ba2cbe9Sxc {
3950ba2cbe9Sxc 	switch (err) {
3960ba2cbe9Sxc 	case EINVAL:
3970ba2cbe9Sxc 		return (DLADM_STATUS_BADARG);
3980ba2cbe9Sxc 	case EEXIST:
3990ba2cbe9Sxc 		return (DLADM_STATUS_EXIST);
4000ba2cbe9Sxc 	case ENOENT:
4010ba2cbe9Sxc 		return (DLADM_STATUS_NOTFOUND);
4020ba2cbe9Sxc 	case ENOSPC:
4030ba2cbe9Sxc 		return (DLADM_STATUS_TOOSMALL);
4040ba2cbe9Sxc 	case ENOMEM:
4050ba2cbe9Sxc 		return (DLADM_STATUS_NOMEM);
4060ba2cbe9Sxc 	case ENOTSUP:
4070ba2cbe9Sxc 		return (DLADM_STATUS_NOTSUP);
4080ba2cbe9Sxc 	case EACCES:
4090ba2cbe9Sxc 		return (DLADM_STATUS_DENIED);
4100ba2cbe9Sxc 	case EIO:
4110ba2cbe9Sxc 		return (DLADM_STATUS_IOERR);
4120ba2cbe9Sxc 	default:
4130ba2cbe9Sxc 		return (DLADM_STATUS_FAILED);
4140ba2cbe9Sxc 	}
4150ba2cbe9Sxc }
4160ba2cbe9Sxc 
4170ba2cbe9Sxc /*
4180ba2cbe9Sxc  * These are the uid and gid of the user 'dladm'.
4190ba2cbe9Sxc  * The directory /etc/dladm and all files under it are owned by this user.
4200ba2cbe9Sxc  */
4210ba2cbe9Sxc #define	DLADM_DB_OWNER		15
4220ba2cbe9Sxc #define	DLADM_DB_GROUP		3
4230ba2cbe9Sxc #define	LOCK_DB_PERMS		S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
4240ba2cbe9Sxc 
4250ba2cbe9Sxc static int
4260ba2cbe9Sxc i_dladm_lock_db(const char *lock_file, short type)
4270ba2cbe9Sxc {
4280ba2cbe9Sxc 	int	lock_fd;
4290ba2cbe9Sxc 	struct  flock lock;
4300ba2cbe9Sxc 
4310ba2cbe9Sxc 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
4320ba2cbe9Sxc 	    LOCK_DB_PERMS)) < 0)
4330ba2cbe9Sxc 		return (-1);
4340ba2cbe9Sxc 
4350ba2cbe9Sxc 	lock.l_type = type;
4360ba2cbe9Sxc 	lock.l_whence = SEEK_SET;
4370ba2cbe9Sxc 	lock.l_start = 0;
4380ba2cbe9Sxc 	lock.l_len = 0;
4390ba2cbe9Sxc 
4400ba2cbe9Sxc 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
4410ba2cbe9Sxc 		int err = errno;
4420ba2cbe9Sxc 
4430ba2cbe9Sxc 		(void) close(lock_fd);
4440ba2cbe9Sxc 		(void) unlink(lock_file);
4450ba2cbe9Sxc 		errno = err;
4460ba2cbe9Sxc 		return (-1);
4470ba2cbe9Sxc 	}
4480ba2cbe9Sxc 	return (lock_fd);
4490ba2cbe9Sxc }
4500ba2cbe9Sxc 
4510ba2cbe9Sxc static void
4520ba2cbe9Sxc i_dladm_unlock_db(const char *lock_file, int fd)
4530ba2cbe9Sxc {
4540ba2cbe9Sxc 	struct flock lock;
4550ba2cbe9Sxc 
4560ba2cbe9Sxc 	if (fd < 0)
4570ba2cbe9Sxc 		return;
4580ba2cbe9Sxc 
4590ba2cbe9Sxc 	lock.l_type = F_UNLCK;
4600ba2cbe9Sxc 	lock.l_whence = SEEK_SET;
4610ba2cbe9Sxc 	lock.l_start = 0;
4620ba2cbe9Sxc 	lock.l_len = 0;
4630ba2cbe9Sxc 
4640ba2cbe9Sxc 	(void) fcntl(fd, F_SETLKW, &lock);
4650ba2cbe9Sxc 	(void) close(fd);
4660ba2cbe9Sxc 	(void) unlink(lock_file);
4670ba2cbe9Sxc }
4680ba2cbe9Sxc 
4690ba2cbe9Sxc dladm_status_t
4700ba2cbe9Sxc i_dladm_rw_db(const char *db_file, mode_t db_perms,
4710ba2cbe9Sxc     dladm_status_t (*process_db)(void *, FILE *, FILE *),
4720ba2cbe9Sxc     void *arg, boolean_t writeop)
4730ba2cbe9Sxc {
4740ba2cbe9Sxc 	dladm_status_t	status = DLADM_STATUS_OK;
4750ba2cbe9Sxc 	FILE		*fp, *nfp = NULL;
4760ba2cbe9Sxc 	char		lock[MAXPATHLEN];
4770ba2cbe9Sxc 	char		file[MAXPATHLEN];
4780ba2cbe9Sxc 	char		newfile[MAXPATHLEN];
4790ba2cbe9Sxc 	char		*db_basename;
4800ba2cbe9Sxc 	int		nfd, lock_fd;
4810ba2cbe9Sxc 
4820ba2cbe9Sxc 	/*
4830ba2cbe9Sxc 	 * If we are called from a boot script such as net-physical,
4840ba2cbe9Sxc 	 * it's quite likely that the root fs is still not writable.
4850ba2cbe9Sxc 	 * For this case, it's ok for the lock creation to fail since
4860ba2cbe9Sxc 	 * no one else could be accessing our configuration file.
4870ba2cbe9Sxc 	 */
4880ba2cbe9Sxc 	db_basename = strrchr(db_file, '/');
4890ba2cbe9Sxc 	if (db_basename == NULL || db_basename[1] == '\0')
4900ba2cbe9Sxc 		return (dladm_errno2status(EINVAL));
4910ba2cbe9Sxc 	db_basename++;
4920ba2cbe9Sxc 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
4930ba2cbe9Sxc 	if ((lock_fd = i_dladm_lock_db
4940ba2cbe9Sxc 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
4950ba2cbe9Sxc 		return (dladm_errno2status(errno));
4960ba2cbe9Sxc 
4970ba2cbe9Sxc 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
4980ba2cbe9Sxc 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
4990ba2cbe9Sxc 		int	err = errno;
5000ba2cbe9Sxc 
5010ba2cbe9Sxc 		i_dladm_unlock_db(lock, lock_fd);
5020ba2cbe9Sxc 		if (err == ENOENT)
5030ba2cbe9Sxc 			return (DLADM_STATUS_DBNOTFOUND);
5040ba2cbe9Sxc 
5050ba2cbe9Sxc 		return (dladm_errno2status(err));
5060ba2cbe9Sxc 	}
5070ba2cbe9Sxc 
5080ba2cbe9Sxc 	if (writeop) {
5090ba2cbe9Sxc 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
5100ba2cbe9Sxc 		    dladm_rootdir, db_file);
5110ba2cbe9Sxc 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
5120ba2cbe9Sxc 		    db_perms)) < 0) {
5130ba2cbe9Sxc 			(void) fclose(fp);
5140ba2cbe9Sxc 			i_dladm_unlock_db(lock, lock_fd);
5150ba2cbe9Sxc 			return (dladm_errno2status(errno));
5160ba2cbe9Sxc 		}
5170ba2cbe9Sxc 
5180ba2cbe9Sxc 		if ((nfp = fdopen(nfd, "w")) == NULL) {
5190ba2cbe9Sxc 			(void) close(nfd);
5200ba2cbe9Sxc 			(void) fclose(fp);
5210ba2cbe9Sxc 			(void) unlink(newfile);
5220ba2cbe9Sxc 			i_dladm_unlock_db(lock, lock_fd);
5230ba2cbe9Sxc 			return (dladm_errno2status(errno));
5240ba2cbe9Sxc 		}
5250ba2cbe9Sxc 	}
5260ba2cbe9Sxc 	status = (*process_db)(arg, fp, nfp);
5270ba2cbe9Sxc 	if (!writeop || status != DLADM_STATUS_OK)
5280ba2cbe9Sxc 		goto done;
5290ba2cbe9Sxc 
5300ba2cbe9Sxc 	/*
5310ba2cbe9Sxc 	 * Configuration files need to be owned by the 'dladm' user.
5320ba2cbe9Sxc 	 * If we are invoked by root, the file ownership needs to be fixed.
5330ba2cbe9Sxc 	 */
5340ba2cbe9Sxc 	if (getuid() == 0 || geteuid() == 0) {
5350ba2cbe9Sxc 		if (fchown(nfd, DLADM_DB_OWNER, DLADM_DB_GROUP) < 0) {
5360ba2cbe9Sxc 			status = dladm_errno2status(errno);
5370ba2cbe9Sxc 			goto done;
5380ba2cbe9Sxc 		}
5390ba2cbe9Sxc 	}
5400ba2cbe9Sxc 
5410ba2cbe9Sxc 	if (fflush(nfp) == EOF) {
5420ba2cbe9Sxc 		status = dladm_errno2status(errno);
5430ba2cbe9Sxc 		goto done;
5440ba2cbe9Sxc 	}
5450ba2cbe9Sxc 	(void) fclose(fp);
5460ba2cbe9Sxc 	(void) fclose(nfp);
5470ba2cbe9Sxc 
5480ba2cbe9Sxc 	if (rename(newfile, file) < 0) {
5490ba2cbe9Sxc 		(void) unlink(newfile);
5500ba2cbe9Sxc 		i_dladm_unlock_db(lock, lock_fd);
5510ba2cbe9Sxc 		return (dladm_errno2status(errno));
5520ba2cbe9Sxc 	}
5530ba2cbe9Sxc 
5540ba2cbe9Sxc 	i_dladm_unlock_db(lock, lock_fd);
5550ba2cbe9Sxc 	return (DLADM_STATUS_OK);
5560ba2cbe9Sxc 
5570ba2cbe9Sxc done:
5580ba2cbe9Sxc 	if (nfp != NULL) {
5590ba2cbe9Sxc 		(void) fclose(nfp);
5600ba2cbe9Sxc 		if (status != DLADM_STATUS_OK)
5610ba2cbe9Sxc 			(void) unlink(newfile);
5620ba2cbe9Sxc 	}
5630ba2cbe9Sxc 	(void) fclose(fp);
5640ba2cbe9Sxc 	i_dladm_unlock_db(lock, lock_fd);
5650ba2cbe9Sxc 	return (status);
5660ba2cbe9Sxc }
5670ba2cbe9Sxc 
5680ba2cbe9Sxc dladm_status_t
5690ba2cbe9Sxc dladm_set_rootdir(const char *rootdir)
5700ba2cbe9Sxc {
5710ba2cbe9Sxc 	DIR	*dp;
5720ba2cbe9Sxc 
5730ba2cbe9Sxc 	if (rootdir == NULL || *rootdir != '/' ||
5740ba2cbe9Sxc 	    (dp = opendir(rootdir)) == NULL)
5750ba2cbe9Sxc 		return (DLADM_STATUS_BADARG);
5760ba2cbe9Sxc 
5770ba2cbe9Sxc 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
5780ba2cbe9Sxc 	(void) closedir(dp);
5790ba2cbe9Sxc 	return (DLADM_STATUS_OK);
5800ba2cbe9Sxc }
581*f4b3ec61Sdh 
582*f4b3ec61Sdh /*
583*f4b3ec61Sdh  * Do a "hold" operation to a link.
584*f4b3ec61Sdh  */
585*f4b3ec61Sdh int
586*f4b3ec61Sdh dladm_hold_link(const char *name, zoneid_t zoneid, boolean_t docheck)
587*f4b3ec61Sdh {
588*f4b3ec61Sdh 	return (i_dladm_hold_link(name, zoneid, docheck));
589*f4b3ec61Sdh }
590*f4b3ec61Sdh 
591*f4b3ec61Sdh /*
592*f4b3ec61Sdh  * Do a "release" operation to a link.
593*f4b3ec61Sdh  */
594*f4b3ec61Sdh int
595*f4b3ec61Sdh dladm_rele_link(const char *name, zoneid_t zoneid, boolean_t docheck)
596*f4b3ec61Sdh {
597*f4b3ec61Sdh 	return (i_dladm_rele_link(name, zoneid, docheck));
598*f4b3ec61Sdh }
599