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