xref: /titanic_52/usr/src/lib/libdladm/common/libdladm.c (revision 1d842814faabbb6af3e2fe30a4bd61aa4a70eeb3)
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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <stropts.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <strings.h>
34 #include <dirent.h>
35 #include <net/if.h>
36 #include <sys/stat.h>
37 #include <sys/dld.h>
38 #include <libdlpi.h>
39 #include <libdevinfo.h>
40 #include <libdladm_impl.h>
41 #include <libintl.h>
42 #include <sys/vlan.h>
43 
44 typedef struct dladm_dev {
45 	char			dd_name[IFNAMSIZ];
46 	struct dladm_dev	*dd_next;
47 } dladm_dev_t;
48 
49 typedef struct dladm_walk {
50 	dladm_dev_t		*dw_dev_list;
51 } dladm_walk_t;
52 
53 static char		dladm_rootdir[MAXPATHLEN] = "/";
54 
55 /*
56  * Issue an ioctl to the specified file descriptor attached to the
57  * DLD control driver interface.
58  */
59 int
60 i_dladm_ioctl(int fd, int ic_cmd, void *ic_dp, int ic_len)
61 {
62 	struct strioctl	iocb;
63 
64 	iocb.ic_cmd = ic_cmd;
65 	iocb.ic_timout = 0;
66 	iocb.ic_len = ic_len;
67 	iocb.ic_dp = (char *)ic_dp;
68 
69 	return (ioctl(fd, I_STR, &iocb));
70 }
71 
72 /*
73  * Return the attributes of the specified datalink from the DLD driver.
74  */
75 static int
76 i_dladm_info(int fd, const char *name, dladm_attr_t *dap)
77 {
78 	dld_ioc_attr_t	dia;
79 
80 	if (strlen(name) >= IFNAMSIZ) {
81 		errno = EINVAL;
82 		return (-1);
83 	}
84 
85 	(void) strlcpy(dia.dia_name, name, IFNAMSIZ);
86 
87 	if (i_dladm_ioctl(fd, DLDIOCATTR, &dia, sizeof (dia)) < 0)
88 		return (-1);
89 
90 	(void) strlcpy(dap->da_dev, dia.dia_dev, MAXNAMELEN);
91 	dap->da_max_sdu = dia.dia_max_sdu;
92 	dap->da_vid = dia.dia_vid;
93 
94 	return (0);
95 }
96 
97 /*
98  * Adds a datalink to the array corresponding to arg.
99  */
100 static void
101 i_dladm_nt_net_add(void *arg, char *name)
102 {
103 	dladm_walk_t	*dwp = arg;
104 	dladm_dev_t	*ddp = dwp->dw_dev_list;
105 	dladm_dev_t	**lastp = &dwp->dw_dev_list;
106 
107 	while (ddp) {
108 		/*
109 		 * Skip duplicates.
110 		 */
111 		if (strcmp(ddp->dd_name, name) == 0)
112 			return;
113 
114 		lastp = &ddp->dd_next;
115 		ddp = ddp->dd_next;
116 	}
117 
118 	if ((ddp = malloc(sizeof (*ddp))) == NULL)
119 		return;
120 
121 	(void) strlcpy(ddp->dd_name, name, IFNAMSIZ);
122 	ddp->dd_next = NULL;
123 	*lastp = ddp;
124 }
125 
126 /*
127  * Walker callback invoked for each DDI_NT_NET node.
128  */
129 static int
130 i_dladm_nt_net_walk(di_node_t node, di_minor_t minor, void *arg)
131 {
132 	dl_info_ack_t	dlia;
133 	char		name[IFNAMSIZ];
134 	int		fd;
135 	char		*provider;
136 	uint_t		ppa;
137 
138 	provider = di_minor_name(minor);
139 
140 	if ((fd = dlpi_open(provider)) < 0)
141 		return (DI_WALK_CONTINUE);
142 
143 	if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) < 0) {
144 		(void) dlpi_close(fd);
145 		return (DI_WALK_CONTINUE);
146 	}
147 
148 	if (dlia.dl_provider_style == DL_STYLE1) {
149 		i_dladm_nt_net_add(arg, provider);
150 		(void) dlpi_close(fd);
151 		return (DI_WALK_CONTINUE);
152 	}
153 
154 	ppa = di_instance(node);
155 
156 	if (dlpi_attach(fd, -1, ppa) < 0) {
157 		(void) dlpi_close(fd);
158 		return (DI_WALK_CONTINUE);
159 	}
160 	(void) snprintf(name, IFNAMSIZ - 1, "%s%d", provider, ppa);
161 	i_dladm_nt_net_add(arg, name);
162 	(void) dlpi_close(fd);
163 	return (DI_WALK_CONTINUE);
164 }
165 
166 /*
167  * Hold a data-link.
168  */
169 static int
170 i_dladm_hold_link(const char *name, zoneid_t zoneid, boolean_t docheck)
171 {
172 	int		fd;
173 	dld_hold_vlan_t	dhv;
174 
175 	if (strlen(name) >= IFNAMSIZ) {
176 		errno = EINVAL;
177 		return (-1);
178 	}
179 
180 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
181 		return (-1);
182 
183 	bzero(&dhv, sizeof (dld_hold_vlan_t));
184 	(void) strlcpy(dhv.dhv_name, name, IFNAMSIZ);
185 	dhv.dhv_zid = zoneid;
186 	dhv.dhv_docheck = docheck;
187 
188 	if (i_dladm_ioctl(fd, DLDIOCHOLDVLAN, &dhv, sizeof (dhv)) < 0) {
189 		int olderrno = errno;
190 
191 		(void) close(fd);
192 		errno = olderrno;
193 		return (-1);
194 	}
195 
196 	(void) close(fd);
197 	return (0);
198 }
199 
200 /*
201  * Release a data-link.
202  */
203 static int
204 i_dladm_rele_link(const char *name, zoneid_t zoneid, boolean_t docheck)
205 {
206 	int		fd;
207 	dld_hold_vlan_t	dhv;
208 
209 	if (strlen(name) >= IFNAMSIZ) {
210 		errno = EINVAL;
211 		return (-1);
212 	}
213 
214 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
215 		return (-1);
216 
217 	bzero(&dhv, sizeof (dld_hold_vlan_t));
218 	(void) strlcpy(dhv.dhv_name, name, IFNAMSIZ);
219 	dhv.dhv_zid = zoneid;
220 	dhv.dhv_docheck = docheck;
221 
222 	if (i_dladm_ioctl(fd, DLDIOCRELEVLAN, &dhv, sizeof (dhv)) < 0) {
223 		int olderrno = errno;
224 
225 		(void) close(fd);
226 		errno = olderrno;
227 		return (-1);
228 	}
229 
230 	(void) close(fd);
231 	return (0);
232 }
233 
234 /*
235  * Invoke the specified callback function for each active DDI_NT_NET
236  * node.
237  */
238 int
239 dladm_walk(void (*fn)(void *, const char *), void *arg)
240 {
241 	di_node_t	root;
242 	dladm_walk_t	dw;
243 	dladm_dev_t	*ddp, *last_ddp;
244 
245 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
246 		errno = EFAULT;
247 		return (-1);
248 	}
249 	dw.dw_dev_list = NULL;
250 
251 	(void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dw,
252 	    i_dladm_nt_net_walk);
253 
254 	di_fini(root);
255 
256 	ddp = dw.dw_dev_list;
257 	while (ddp) {
258 		fn(arg, ddp->dd_name);
259 		last_ddp = ddp;
260 		ddp = ddp->dd_next;
261 		free(last_ddp);
262 	}
263 
264 	return (0);
265 }
266 
267 /*
268  * Invoke the specified callback function for each vlan managed by dld
269  */
270 int
271 dladm_walk_vlan(void (*fn)(void *, const char *), void *arg, const char *name)
272 {
273 	int		fd, bufsize, i;
274 	int		nvlan = 4094;
275 	dld_ioc_vlan_t	*iocp = NULL;
276 	dld_vlan_info_t	*dvip;
277 
278 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
279 		return (-1);
280 
281 	bufsize = sizeof (dld_ioc_vlan_t) + nvlan * sizeof (dld_vlan_info_t);
282 
283 	if ((iocp = (dld_ioc_vlan_t *)calloc(1, bufsize)) == NULL)
284 		return (-1);
285 
286 	(void) strlcpy((char *)iocp->div_name, name, IFNAMSIZ);
287 	if (i_dladm_ioctl(fd, DLDIOCVLAN, iocp, bufsize) == 0) {
288 		dvip = (dld_vlan_info_t *)(iocp + 1);
289 		for (i = 0; i < iocp->div_count; i++)
290 			(*fn)(arg, dvip[i].dvi_name);
291 	}
292 	/*
293 	 * Note: Callers of dladm_walk_vlan() ignore the return
294 	 * value of this routine. So ignoring ioctl failure case
295 	 * and just returning 0.
296 	 */
297 	free(iocp);
298 	(void) close(fd);
299 	return (0);
300 }
301 
302 
303 /*
304  * Returns the current attributes of the specified datalink.
305  */
306 int
307 dladm_info(const char *name, dladm_attr_t *dap)
308 {
309 	int		fd;
310 
311 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
312 		return (-1);
313 
314 	if (i_dladm_info(fd, name, dap) < 0)
315 		goto failed;
316 
317 	(void) close(fd);
318 	return (0);
319 
320 failed:
321 	(void) close(fd);
322 	return (-1);
323 }
324 
325 const char *
326 dladm_status2str(dladm_status_t status, char *buf)
327 {
328 	const char	*s;
329 
330 	switch (status) {
331 	case DLADM_STATUS_OK:
332 		s = "ok";
333 		break;
334 	case DLADM_STATUS_BADARG:
335 		s = "invalid argument";
336 		break;
337 	case DLADM_STATUS_FAILED:
338 		s = "operation failed";
339 		break;
340 	case DLADM_STATUS_TOOSMALL:
341 		s = "buffer size too small";
342 		break;
343 	case DLADM_STATUS_NOTSUP:
344 		s = "operation not supported";
345 		break;
346 	case DLADM_STATUS_NOTFOUND:
347 		s = "object not found";
348 		break;
349 	case DLADM_STATUS_BADVAL:
350 		s = "invalid value";
351 		break;
352 	case DLADM_STATUS_NOMEM:
353 		s = "insufficient memory";
354 		break;
355 	case DLADM_STATUS_EXIST:
356 		s = "object already exists";
357 		break;
358 	case DLADM_STATUS_LINKINVAL:
359 		s = "invalid link";
360 		break;
361 	case DLADM_STATUS_PROPRDONLY:
362 		s = "read-only property";
363 		break;
364 	case DLADM_STATUS_BADVALCNT:
365 		s = "invalid number of values";
366 		break;
367 	case DLADM_STATUS_DBNOTFOUND:
368 		s = "database not found";
369 		break;
370 	case DLADM_STATUS_DENIED:
371 		s = "permission denied";
372 		break;
373 	case DLADM_STATUS_IOERR:
374 		s = "I/O error";
375 		break;
376 	case DLADM_STATUS_TEMPONLY:
377 		s = "change cannot be persistent, specify -t please";
378 		break;
379 	default:
380 		s = "<unknown error>";
381 		break;
382 	}
383 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
384 	return (buf);
385 }
386 
387 /*
388  * Convert a unix errno to a dladm_status_t.
389  * We only convert errnos that are likely to be encountered. All others
390  * are mapped to DLADM_STATUS_FAILED.
391  */
392 dladm_status_t
393 dladm_errno2status(int err)
394 {
395 	switch (err) {
396 	case EINVAL:
397 		return (DLADM_STATUS_BADARG);
398 	case EEXIST:
399 		return (DLADM_STATUS_EXIST);
400 	case ENOENT:
401 		return (DLADM_STATUS_NOTFOUND);
402 	case ENOSPC:
403 		return (DLADM_STATUS_TOOSMALL);
404 	case ENOMEM:
405 		return (DLADM_STATUS_NOMEM);
406 	case ENOTSUP:
407 		return (DLADM_STATUS_NOTSUP);
408 	case EACCES:
409 		return (DLADM_STATUS_DENIED);
410 	case EIO:
411 		return (DLADM_STATUS_IOERR);
412 	default:
413 		return (DLADM_STATUS_FAILED);
414 	}
415 }
416 
417 /*
418  * These are the uid and gid of the user 'dladm'.
419  * The directory /etc/dladm and all files under it are owned by this user.
420  */
421 #define	DLADM_DB_OWNER		15
422 #define	DLADM_DB_GROUP		3
423 #define	LOCK_DB_PERMS		S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
424 
425 static int
426 i_dladm_lock_db(const char *lock_file, short type)
427 {
428 	int	lock_fd;
429 	struct  flock lock;
430 
431 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
432 	    LOCK_DB_PERMS)) < 0)
433 		return (-1);
434 
435 	lock.l_type = type;
436 	lock.l_whence = SEEK_SET;
437 	lock.l_start = 0;
438 	lock.l_len = 0;
439 
440 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
441 		int err = errno;
442 
443 		(void) close(lock_fd);
444 		(void) unlink(lock_file);
445 		errno = err;
446 		return (-1);
447 	}
448 	return (lock_fd);
449 }
450 
451 static void
452 i_dladm_unlock_db(const char *lock_file, int fd)
453 {
454 	struct flock lock;
455 
456 	if (fd < 0)
457 		return;
458 
459 	lock.l_type = F_UNLCK;
460 	lock.l_whence = SEEK_SET;
461 	lock.l_start = 0;
462 	lock.l_len = 0;
463 
464 	(void) fcntl(fd, F_SETLKW, &lock);
465 	(void) close(fd);
466 	(void) unlink(lock_file);
467 }
468 
469 dladm_status_t
470 i_dladm_rw_db(const char *db_file, mode_t db_perms,
471     dladm_status_t (*process_db)(void *, FILE *, FILE *),
472     void *arg, boolean_t writeop)
473 {
474 	dladm_status_t	status = DLADM_STATUS_OK;
475 	FILE		*fp, *nfp = NULL;
476 	char		lock[MAXPATHLEN];
477 	char		file[MAXPATHLEN];
478 	char		newfile[MAXPATHLEN];
479 	char		*db_basename;
480 	int		nfd, lock_fd;
481 
482 	/*
483 	 * If we are called from a boot script such as net-physical,
484 	 * it's quite likely that the root fs is still not writable.
485 	 * For this case, it's ok for the lock creation to fail since
486 	 * no one else could be accessing our configuration file.
487 	 */
488 	db_basename = strrchr(db_file, '/');
489 	if (db_basename == NULL || db_basename[1] == '\0')
490 		return (dladm_errno2status(EINVAL));
491 	db_basename++;
492 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
493 	if ((lock_fd = i_dladm_lock_db
494 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
495 		return (dladm_errno2status(errno));
496 
497 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
498 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
499 		int	err = errno;
500 
501 		i_dladm_unlock_db(lock, lock_fd);
502 		if (err == ENOENT)
503 			return (DLADM_STATUS_DBNOTFOUND);
504 
505 		return (dladm_errno2status(err));
506 	}
507 
508 	if (writeop) {
509 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
510 		    dladm_rootdir, db_file);
511 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
512 		    db_perms)) < 0) {
513 			(void) fclose(fp);
514 			i_dladm_unlock_db(lock, lock_fd);
515 			return (dladm_errno2status(errno));
516 		}
517 
518 		if ((nfp = fdopen(nfd, "w")) == NULL) {
519 			(void) close(nfd);
520 			(void) fclose(fp);
521 			(void) unlink(newfile);
522 			i_dladm_unlock_db(lock, lock_fd);
523 			return (dladm_errno2status(errno));
524 		}
525 	}
526 	status = (*process_db)(arg, fp, nfp);
527 	if (!writeop || status != DLADM_STATUS_OK)
528 		goto done;
529 
530 	/*
531 	 * Configuration files need to be owned by the 'dladm' user.
532 	 * If we are invoked by root, the file ownership needs to be fixed.
533 	 */
534 	if (getuid() == 0 || geteuid() == 0) {
535 		if (fchown(nfd, DLADM_DB_OWNER, DLADM_DB_GROUP) < 0) {
536 			status = dladm_errno2status(errno);
537 			goto done;
538 		}
539 	}
540 
541 	if (fflush(nfp) == EOF) {
542 		status = dladm_errno2status(errno);
543 		goto done;
544 	}
545 	(void) fclose(fp);
546 	(void) fclose(nfp);
547 
548 	if (rename(newfile, file) < 0) {
549 		(void) unlink(newfile);
550 		i_dladm_unlock_db(lock, lock_fd);
551 		return (dladm_errno2status(errno));
552 	}
553 
554 	i_dladm_unlock_db(lock, lock_fd);
555 	return (DLADM_STATUS_OK);
556 
557 done:
558 	if (nfp != NULL) {
559 		(void) fclose(nfp);
560 		if (status != DLADM_STATUS_OK)
561 			(void) unlink(newfile);
562 	}
563 	(void) fclose(fp);
564 	i_dladm_unlock_db(lock, lock_fd);
565 	return (status);
566 }
567 
568 dladm_status_t
569 dladm_set_rootdir(const char *rootdir)
570 {
571 	DIR	*dp;
572 
573 	if (rootdir == NULL || *rootdir != '/' ||
574 	    (dp = opendir(rootdir)) == NULL)
575 		return (DLADM_STATUS_BADARG);
576 
577 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
578 	(void) closedir(dp);
579 	return (DLADM_STATUS_OK);
580 }
581 
582 /*
583  * Do a "hold" operation to a link.
584  */
585 int
586 dladm_hold_link(const char *name, zoneid_t zoneid, boolean_t docheck)
587 {
588 	return (i_dladm_hold_link(name, zoneid, docheck));
589 }
590 
591 /*
592  * Do a "release" operation to a link.
593  */
594 int
595 dladm_rele_link(const char *name, zoneid_t zoneid, boolean_t docheck)
596 {
597 	return (i_dladm_rele_link(name, zoneid, docheck));
598 }
599