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