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 0:
167 		return (DLADM_STATUS_OK);
168 	case EINVAL:
169 		return (DLADM_STATUS_BADARG);
170 	case EEXIST:
171 		return (DLADM_STATUS_EXIST);
172 	case ENOENT:
173 		return (DLADM_STATUS_NOTFOUND);
174 	case ENOSPC:
175 		return (DLADM_STATUS_TOOSMALL);
176 	case ENOMEM:
177 		return (DLADM_STATUS_NOMEM);
178 	case ENOTSUP:
179 		return (DLADM_STATUS_NOTSUP);
180 	case ENETDOWN:
181 		return (DLADM_STATUS_NONOTIF);
182 	case EACCES:
183 		return (DLADM_STATUS_DENIED);
184 	case EIO:
185 		return (DLADM_STATUS_IOERR);
186 	case EBUSY:
187 		return (DLADM_STATUS_LINKBUSY);
188 	case EAGAIN:
189 		return (DLADM_STATUS_TRYAGAIN);
190 	default:
191 		return (DLADM_STATUS_FAILED);
192 	}
193 }
194 
195 /*
196  * These are the uid and gid of the user 'dladm'.
197  * The directory /etc/dladm and all files under it are owned by this user.
198  */
199 #define	DLADM_DB_OWNER	15
200 #define	DLADM_DB_GROUP	3
201 #define	LOCK_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
202 
203 static int
204 i_dladm_lock_db(const char *lock_file, short type)
205 {
206 	int	lock_fd;
207 	struct	flock lock;
208 
209 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
210 	    LOCK_DB_PERMS)) < 0)
211 		return (-1);
212 
213 	lock.l_type = type;
214 	lock.l_whence = SEEK_SET;
215 	lock.l_start = 0;
216 	lock.l_len = 0;
217 
218 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
219 		int err = errno;
220 
221 		(void) close(lock_fd);
222 		(void) unlink(lock_file);
223 		errno = err;
224 		return (-1);
225 	}
226 	return (lock_fd);
227 }
228 
229 static void
230 i_dladm_unlock_db(const char *lock_file, int fd)
231 {
232 	struct flock lock;
233 
234 	if (fd < 0)
235 		return;
236 
237 	lock.l_type = F_UNLCK;
238 	lock.l_whence = SEEK_SET;
239 	lock.l_start = 0;
240 	lock.l_len = 0;
241 
242 	(void) fcntl(fd, F_SETLKW, &lock);
243 	(void) close(fd);
244 	(void) unlink(lock_file);
245 }
246 
247 /*
248  * Given a link class, returns its class string.
249  */
250 const char *
251 dladm_class2str(datalink_class_t class, char *buf)
252 {
253 	const char *s;
254 
255 	switch (class) {
256 	case DATALINK_CLASS_PHYS:
257 		s = "phys";
258 		break;
259 	case DATALINK_CLASS_VLAN:
260 		s = "vlan";
261 		break;
262 	case DATALINK_CLASS_AGGR:
263 		s = "aggr";
264 		break;
265 	case DATALINK_CLASS_VNIC:
266 		s = "vnic";
267 		break;
268 	default:
269 		s = "unknown";
270 		break;
271 	}
272 
273 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
274 	return (buf);
275 }
276 
277 /*
278  * Given a physical link media type, returns its media type string.
279  */
280 const char *
281 dladm_media2str(uint32_t media, char *buf)
282 {
283 	const char *s;
284 
285 	switch (media) {
286 	case DL_ETHER:
287 		s = "Ethernet";
288 		break;
289 	case DL_WIFI:
290 		s = "WiFi";
291 		break;
292 	case DL_IB:
293 		s = "Infiniband";
294 		break;
295 	case DL_IPV4:
296 		s = "IPv4Tunnel";
297 		break;
298 	case DL_IPV6:
299 		s = "IPv6Tunnel";
300 		break;
301 	case DL_CSMACD:
302 		s = "CSMA/CD";
303 		break;
304 	case DL_TPB:
305 		s = "TokenBus";
306 		break;
307 	case DL_TPR:
308 		s = "TokenRing";
309 		break;
310 	case DL_METRO:
311 		s = "MetroNet";
312 		break;
313 	case DL_HDLC:
314 		s = "HDLC";
315 		break;
316 	case DL_CHAR:
317 		s = "SyncCharacter";
318 		break;
319 	case DL_CTCA:
320 		s = "CTCA";
321 		break;
322 	case DL_FDDI:
323 		s = "FDDI";
324 		break;
325 	case DL_FC:
326 		s = "FiberChannel";
327 		break;
328 	case DL_ATM:
329 		s = "ATM";
330 		break;
331 	case DL_IPATM:
332 		s = "ATM(ClassicIP)";
333 		break;
334 	case DL_X25:
335 		s = "X.25";
336 		break;
337 	case DL_IPX25:
338 		s = "X.25(ClassicIP)";
339 		break;
340 	case DL_ISDN:
341 		s = "ISDN";
342 		break;
343 	case DL_HIPPI:
344 		s = "HIPPI";
345 		break;
346 	case DL_100VG:
347 		s = "100BaseVGEthernet";
348 		break;
349 	case DL_100VGTPR:
350 		s = "100BaseVGTokenRing";
351 		break;
352 	case DL_ETH_CSMA:
353 		s = "IEEE802.3";
354 		break;
355 	case DL_100BT:
356 		s = "100BaseT";
357 		break;
358 	case DL_FRAME:
359 		s = "FrameRelay";
360 		break;
361 	case DL_MPFRAME:
362 		s = "MPFrameRelay";
363 		break;
364 	case DL_ASYNC:
365 		s = "AsyncCharacter";
366 		break;
367 	default:
368 		s = "--";
369 		break;
370 	}
371 
372 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
373 	return (buf);
374 }
375 
376 dladm_status_t
377 i_dladm_rw_db(const char *db_file, mode_t db_perms,
378     dladm_status_t (*process_db)(void *, FILE *, FILE *),
379     void *arg, boolean_t writeop)
380 {
381 	dladm_status_t	status = DLADM_STATUS_OK;
382 	FILE		*fp, *nfp = NULL;
383 	char		lock[MAXPATHLEN];
384 	char		file[MAXPATHLEN];
385 	char		newfile[MAXPATHLEN];
386 	char		*db_basename;
387 	int		nfd, lock_fd;
388 
389 	/*
390 	 * If we are called from a boot script such as net-physical,
391 	 * it's quite likely that the root fs is still not writable.
392 	 * For this case, it's ok for the lock creation to fail since
393 	 * no one else could be accessing our configuration file.
394 	 */
395 	db_basename = strrchr(db_file, '/');
396 	if (db_basename == NULL || db_basename[1] == '\0')
397 		return (dladm_errno2status(EINVAL));
398 	db_basename++;
399 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
400 	if ((lock_fd = i_dladm_lock_db
401 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
402 		return (dladm_errno2status(errno));
403 
404 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
405 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
406 		int	err = errno;
407 
408 		i_dladm_unlock_db(lock, lock_fd);
409 		if (err == ENOENT)
410 			return (DLADM_STATUS_DBNOTFOUND);
411 
412 		return (dladm_errno2status(err));
413 	}
414 
415 	if (writeop) {
416 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
417 		    dladm_rootdir, db_file);
418 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
419 		    db_perms)) < 0) {
420 			(void) fclose(fp);
421 			i_dladm_unlock_db(lock, lock_fd);
422 			return (dladm_errno2status(errno));
423 		}
424 
425 		if ((nfp = fdopen(nfd, "w")) == NULL) {
426 			(void) close(nfd);
427 			(void) fclose(fp);
428 			(void) unlink(newfile);
429 			i_dladm_unlock_db(lock, lock_fd);
430 			return (dladm_errno2status(errno));
431 		}
432 	}
433 	status = (*process_db)(arg, fp, nfp);
434 	if (!writeop || status != DLADM_STATUS_OK)
435 		goto done;
436 
437 	/*
438 	 * Configuration files need to be owned by the 'dladm' user.
439 	 * If we are invoked by root, the file ownership needs to be fixed.
440 	 */
441 	if (getuid() == 0 || geteuid() == 0) {
442 		if (fchown(nfd, DLADM_DB_OWNER, DLADM_DB_GROUP) < 0) {
443 			status = dladm_errno2status(errno);
444 			goto done;
445 		}
446 	}
447 
448 	if (fflush(nfp) == EOF) {
449 		status = dladm_errno2status(errno);
450 		goto done;
451 	}
452 	(void) fclose(fp);
453 	(void) fclose(nfp);
454 
455 	if (rename(newfile, file) < 0) {
456 		(void) unlink(newfile);
457 		i_dladm_unlock_db(lock, lock_fd);
458 		return (dladm_errno2status(errno));
459 	}
460 
461 	i_dladm_unlock_db(lock, lock_fd);
462 	return (DLADM_STATUS_OK);
463 
464 done:
465 	if (nfp != NULL) {
466 		(void) fclose(nfp);
467 		if (status != DLADM_STATUS_OK)
468 			(void) unlink(newfile);
469 	}
470 	(void) fclose(fp);
471 	i_dladm_unlock_db(lock, lock_fd);
472 	return (status);
473 }
474 
475 dladm_status_t
476 dladm_set_rootdir(const char *rootdir)
477 {
478 	DIR	*dp;
479 
480 	if (rootdir == NULL || *rootdir != '/' ||
481 	    (dp = opendir(rootdir)) == NULL)
482 		return (DLADM_STATUS_BADARG);
483 
484 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
485 	(void) closedir(dp);
486 	return (DLADM_STATUS_OK);
487 }
488 
489 boolean_t
490 dladm_valid_linkname(const char *link)
491 {
492 	size_t		len = strlen(link);
493 	const char	*cp;
494 
495 	if (len + 1 >= MAXLINKNAMELEN)
496 		return (B_FALSE);
497 
498 	/*
499 	 * The link name cannot start with a digit and must end with a digit.
500 	 */
501 	if ((isdigit(link[0]) != 0) || (isdigit(link[len - 1]) == 0))
502 		return (B_FALSE);
503 
504 	/*
505 	 * The legal characters in a link name are:
506 	 * alphanumeric (a-z,  A-Z,  0-9), and the underscore ('_').
507 	 */
508 	for (cp = link; *cp != '\0'; cp++) {
509 		if ((isalnum(*cp) == 0) && (*cp != '_'))
510 			return (B_FALSE);
511 	}
512 
513 	return (B_TRUE);
514 }
515