xref: /illumos-gate/usr/src/lib/libdladm/common/libdladm.c (revision d62bc4badc1c1f1549c961cfb8b420e650e1272b)
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 2008 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 <unistd.h>
29 #include <stropts.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <fcntl.h>
33 #include <strings.h>
34 #include <dirent.h>
35 #include <sys/stat.h>
36 #include <libdladm_impl.h>
37 #include <libintl.h>
38 #include <libdlpi.h>
39 
40 static char		dladm_rootdir[MAXPATHLEN] = "/";
41 
42 /*
43  * Issue an ioctl to the specified file descriptor attached to the
44  * DLD control driver interface.
45  */
46 int
47 i_dladm_ioctl(int fd, int ic_cmd, void *ic_dp, int ic_len)
48 {
49 	struct strioctl	iocb;
50 
51 	iocb.ic_cmd = ic_cmd;
52 	iocb.ic_timout = 0;
53 	iocb.ic_len = ic_len;
54 	iocb.ic_dp = (char *)ic_dp;
55 
56 	return (ioctl(fd, I_STR, &iocb));
57 }
58 
59 const char *
60 dladm_status2str(dladm_status_t status, char *buf)
61 {
62 	const char	*s;
63 
64 	switch (status) {
65 	case DLADM_STATUS_OK:
66 		s = "ok";
67 		break;
68 	case DLADM_STATUS_BADARG:
69 		s = "invalid argument";
70 		break;
71 	case DLADM_STATUS_FAILED:
72 		s = "operation failed";
73 		break;
74 	case DLADM_STATUS_TOOSMALL:
75 		s = "buffer size too small";
76 		break;
77 	case DLADM_STATUS_NOTSUP:
78 		s = "operation not supported";
79 		break;
80 	case DLADM_STATUS_NOTFOUND:
81 		s = "object not found";
82 		break;
83 	case DLADM_STATUS_BADVAL:
84 		s = "invalid value";
85 		break;
86 	case DLADM_STATUS_NOMEM:
87 		s = "insufficient memory";
88 		break;
89 	case DLADM_STATUS_EXIST:
90 		s = "object already exists";
91 		break;
92 	case DLADM_STATUS_LINKINVAL:
93 		s = "invalid link";
94 		break;
95 	case DLADM_STATUS_PROPRDONLY:
96 		s = "read-only property";
97 		break;
98 	case DLADM_STATUS_BADVALCNT:
99 		s = "invalid number of values";
100 		break;
101 	case DLADM_STATUS_DBNOTFOUND:
102 		s = "database not found";
103 		break;
104 	case DLADM_STATUS_DENIED:
105 		s = "permission denied";
106 		break;
107 	case DLADM_STATUS_IOERR:
108 		s = "I/O error";
109 		break;
110 	case DLADM_STATUS_TEMPONLY:
111 		s = "change cannot be persistent, specify -t please";
112 		break;
113 	case DLADM_STATUS_TIMEDOUT:
114 		s = "operation timed out";
115 		break;
116 	case DLADM_STATUS_ISCONN:
117 		s = "already connected";
118 		break;
119 	case DLADM_STATUS_NOTCONN:
120 		s = "not connected";
121 		break;
122 	case DLADM_STATUS_REPOSITORYINVAL:
123 		s = "invalid configuration repository";
124 		break;
125 	case DLADM_STATUS_MACADDRINVAL:
126 		s = "invalid MAC address";
127 		break;
128 	case DLADM_STATUS_KEYINVAL:
129 		s = "invalid key";
130 		break;
131 	case DLADM_STATUS_INVALIDMACADDRLEN:
132 		s = "invalid MAC address length";
133 		break;
134 	case DLADM_STATUS_INVALIDMACADDRTYPE:
135 		s = "invalid MAC address type";
136 		break;
137 	case DLADM_STATUS_LINKBUSY:
138 		s = "link busy";
139 		break;
140 	case DLADM_STATUS_VIDINVAL:
141 		s = "invalid VLAN identifier";
142 		break;
143 	case DLADM_STATUS_TRYAGAIN:
144 		s = "try again later";
145 		break;
146 	case DLADM_STATUS_NONOTIF:
147 		s = "link notification is not supported";
148 		break;
149 	default:
150 		s = "<unknown error>";
151 		break;
152 	}
153 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
154 	return (buf);
155 }
156 
157 /*
158  * Convert a unix errno to a dladm_status_t.
159  * We only convert errnos that are likely to be encountered. All others
160  * are mapped to DLADM_STATUS_FAILED.
161  */
162 dladm_status_t
163 dladm_errno2status(int err)
164 {
165 	switch (err) {
166 	case EINVAL:
167 		return (DLADM_STATUS_BADARG);
168 	case EEXIST:
169 		return (DLADM_STATUS_EXIST);
170 	case ENOENT:
171 		return (DLADM_STATUS_NOTFOUND);
172 	case ENOSPC:
173 		return (DLADM_STATUS_TOOSMALL);
174 	case ENOMEM:
175 		return (DLADM_STATUS_NOMEM);
176 	case ENOTSUP:
177 		return (DLADM_STATUS_NOTSUP);
178 	case ENETDOWN:
179 		return (DLADM_STATUS_NONOTIF);
180 	case EACCES:
181 		return (DLADM_STATUS_DENIED);
182 	case EIO:
183 		return (DLADM_STATUS_IOERR);
184 	case EBUSY:
185 		return (DLADM_STATUS_LINKBUSY);
186 	case EAGAIN:
187 		return (DLADM_STATUS_TRYAGAIN);
188 	default:
189 		return (DLADM_STATUS_FAILED);
190 	}
191 }
192 
193 /*
194  * These are the uid and gid of the user 'dladm'.
195  * The directory /etc/dladm and all files under it are owned by this user.
196  */
197 #define	DLADM_DB_OWNER	15
198 #define	DLADM_DB_GROUP	3
199 #define	LOCK_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
200 
201 static int
202 i_dladm_lock_db(const char *lock_file, short type)
203 {
204 	int	lock_fd;
205 	struct	flock lock;
206 
207 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
208 	    LOCK_DB_PERMS)) < 0)
209 		return (-1);
210 
211 	lock.l_type = type;
212 	lock.l_whence = SEEK_SET;
213 	lock.l_start = 0;
214 	lock.l_len = 0;
215 
216 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
217 		int err = errno;
218 
219 		(void) close(lock_fd);
220 		(void) unlink(lock_file);
221 		errno = err;
222 		return (-1);
223 	}
224 	return (lock_fd);
225 }
226 
227 static void
228 i_dladm_unlock_db(const char *lock_file, int fd)
229 {
230 	struct flock lock;
231 
232 	if (fd < 0)
233 		return;
234 
235 	lock.l_type = F_UNLCK;
236 	lock.l_whence = SEEK_SET;
237 	lock.l_start = 0;
238 	lock.l_len = 0;
239 
240 	(void) fcntl(fd, F_SETLKW, &lock);
241 	(void) close(fd);
242 	(void) unlink(lock_file);
243 }
244 
245 /*
246  * Given a link class, returns its class string.
247  */
248 const char *
249 dladm_class2str(datalink_class_t class, char *buf)
250 {
251 	const char *s;
252 
253 	switch (class) {
254 	case DATALINK_CLASS_PHYS:
255 		s = "phys";
256 		break;
257 	case DATALINK_CLASS_VLAN:
258 		s = "vlan";
259 		break;
260 	case DATALINK_CLASS_AGGR:
261 		s = "aggr";
262 		break;
263 	case DATALINK_CLASS_VNIC:
264 		s = "vnic";
265 		break;
266 	default:
267 		s = "unknown";
268 		break;
269 	}
270 
271 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
272 	return (buf);
273 }
274 
275 /*
276  * Given a physical link media type, returns its media type string.
277  */
278 const char *
279 dladm_media2str(uint32_t media, char *buf)
280 {
281 	const char *s;
282 
283 	switch (media) {
284 	case DL_ETHER:
285 		s = "Ethernet";
286 		break;
287 	case DL_WIFI:
288 		s = "WiFi";
289 		break;
290 	case DL_IB:
291 		s = "Infiniband";
292 		break;
293 	case DL_IPV4:
294 		s = "IPv4Tunnel";
295 		break;
296 	case DL_IPV6:
297 		s = "IPv6Tunnel";
298 		break;
299 	case DL_CSMACD:
300 		s = "CSMA/CD";
301 		break;
302 	case DL_TPB:
303 		s = "TokenBus";
304 		break;
305 	case DL_TPR:
306 		s = "TokenRing";
307 		break;
308 	case DL_METRO:
309 		s = "MetroNet";
310 		break;
311 	case DL_HDLC:
312 		s = "HDLC";
313 		break;
314 	case DL_CHAR:
315 		s = "SyncCharacter";
316 		break;
317 	case DL_CTCA:
318 		s = "CTCA";
319 		break;
320 	case DL_FDDI:
321 		s = "FDDI";
322 		break;
323 	case DL_FC:
324 		s = "FiberChannel";
325 		break;
326 	case DL_ATM:
327 		s = "ATM";
328 		break;
329 	case DL_IPATM:
330 		s = "ATM(ClassicIP)";
331 		break;
332 	case DL_X25:
333 		s = "X.25";
334 		break;
335 	case DL_IPX25:
336 		s = "X.25(ClassicIP)";
337 		break;
338 	case DL_ISDN:
339 		s = "ISDN";
340 		break;
341 	case DL_HIPPI:
342 		s = "HIPPI";
343 		break;
344 	case DL_100VG:
345 		s = "100BaseVGEthernet";
346 		break;
347 	case DL_100VGTPR:
348 		s = "100BaseVGTokenRing";
349 		break;
350 	case DL_ETH_CSMA:
351 		s = "IEEE802.3";
352 		break;
353 	case DL_100BT:
354 		s = "100BaseT";
355 		break;
356 	case DL_FRAME:
357 		s = "FrameRelay";
358 		break;
359 	case DL_MPFRAME:
360 		s = "MPFrameRelay";
361 		break;
362 	case DL_ASYNC:
363 		s = "AsyncCharacter";
364 		break;
365 	default:
366 		s = "--";
367 		break;
368 	}
369 
370 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
371 	return (buf);
372 }
373 
374 dladm_status_t
375 i_dladm_rw_db(const char *db_file, mode_t db_perms,
376     dladm_status_t (*process_db)(void *, FILE *, FILE *),
377     void *arg, boolean_t writeop)
378 {
379 	dladm_status_t	status = DLADM_STATUS_OK;
380 	FILE		*fp, *nfp = NULL;
381 	char		lock[MAXPATHLEN];
382 	char		file[MAXPATHLEN];
383 	char		newfile[MAXPATHLEN];
384 	char		*db_basename;
385 	int		nfd, lock_fd;
386 
387 	/*
388 	 * If we are called from a boot script such as net-physical,
389 	 * it's quite likely that the root fs is still not writable.
390 	 * For this case, it's ok for the lock creation to fail since
391 	 * no one else could be accessing our configuration file.
392 	 */
393 	db_basename = strrchr(db_file, '/');
394 	if (db_basename == NULL || db_basename[1] == '\0')
395 		return (dladm_errno2status(EINVAL));
396 	db_basename++;
397 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
398 	if ((lock_fd = i_dladm_lock_db
399 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
400 		return (dladm_errno2status(errno));
401 
402 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
403 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
404 		int	err = errno;
405 
406 		i_dladm_unlock_db(lock, lock_fd);
407 		if (err == ENOENT)
408 			return (DLADM_STATUS_DBNOTFOUND);
409 
410 		return (dladm_errno2status(err));
411 	}
412 
413 	if (writeop) {
414 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
415 		    dladm_rootdir, db_file);
416 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
417 		    db_perms)) < 0) {
418 			(void) fclose(fp);
419 			i_dladm_unlock_db(lock, lock_fd);
420 			return (dladm_errno2status(errno));
421 		}
422 
423 		if ((nfp = fdopen(nfd, "w")) == NULL) {
424 			(void) close(nfd);
425 			(void) fclose(fp);
426 			(void) unlink(newfile);
427 			i_dladm_unlock_db(lock, lock_fd);
428 			return (dladm_errno2status(errno));
429 		}
430 	}
431 	status = (*process_db)(arg, fp, nfp);
432 	if (!writeop || status != DLADM_STATUS_OK)
433 		goto done;
434 
435 	/*
436 	 * Configuration files need to be owned by the 'dladm' user.
437 	 * If we are invoked by root, the file ownership needs to be fixed.
438 	 */
439 	if (getuid() == 0 || geteuid() == 0) {
440 		if (fchown(nfd, DLADM_DB_OWNER, DLADM_DB_GROUP) < 0) {
441 			status = dladm_errno2status(errno);
442 			goto done;
443 		}
444 	}
445 
446 	if (fflush(nfp) == EOF) {
447 		status = dladm_errno2status(errno);
448 		goto done;
449 	}
450 	(void) fclose(fp);
451 	(void) fclose(nfp);
452 
453 	if (rename(newfile, file) < 0) {
454 		(void) unlink(newfile);
455 		i_dladm_unlock_db(lock, lock_fd);
456 		return (dladm_errno2status(errno));
457 	}
458 
459 	i_dladm_unlock_db(lock, lock_fd);
460 	return (DLADM_STATUS_OK);
461 
462 done:
463 	if (nfp != NULL) {
464 		(void) fclose(nfp);
465 		if (status != DLADM_STATUS_OK)
466 			(void) unlink(newfile);
467 	}
468 	(void) fclose(fp);
469 	i_dladm_unlock_db(lock, lock_fd);
470 	return (status);
471 }
472 
473 dladm_status_t
474 dladm_set_rootdir(const char *rootdir)
475 {
476 	DIR	*dp;
477 
478 	if (rootdir == NULL || *rootdir != '/' ||
479 	    (dp = opendir(rootdir)) == NULL)
480 		return (DLADM_STATUS_BADARG);
481 
482 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
483 	(void) closedir(dp);
484 	return (DLADM_STATUS_OK);
485 }
486 
487 boolean_t
488 dladm_valid_linkname(const char *link)
489 {
490 	size_t		len = strlen(link);
491 	const char	*cp;
492 
493 	if (len + 1 >= MAXLINKNAMELEN)
494 		return (B_FALSE);
495 
496 	/*
497 	 * The link name cannot start with a digit and must end with a digit.
498 	 */
499 	if ((isdigit(link[0]) != 0) || (isdigit(link[len - 1]) == 0))
500 		return (B_FALSE);
501 
502 	/*
503 	 * The legal characters in a link name are:
504 	 * alphanumeric (a-z,  A-Z,  0-9), and the underscore ('_').
505 	 */
506 	for (cp = link; *cp != '\0'; cp++) {
507 		if ((isalnum(*cp) == 0) && (*cp != '_'))
508 			return (B_FALSE);
509 	}
510 
511 	return (B_TRUE);
512 }
513