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 #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> 37 38 static char dladm_rootdir[MAXPATHLEN] = "/"; 39 40 const char * 41 dladm_status2str(dladm_status_t status, char *buf) 42 { 43 const char *s; 44 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; 133 } 134 (void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s)); 135 return (buf); 136 } 137 138 /* 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. 142 */ 143 dladm_status_t 144 dladm_errno2status(int err) 145 { 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); 174 } 175 } 176 177 #define LOCK_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 178 179 static int 180 i_dladm_lock_db(const char *lock_file, short type) 181 { 182 int lock_fd; 183 struct flock lock; 184 185 if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC, 186 LOCK_DB_PERMS)) < 0) 187 return (-1); 188 189 lock.l_type = type; 190 lock.l_whence = SEEK_SET; 191 lock.l_start = 0; 192 lock.l_len = 0; 193 194 if (fcntl(lock_fd, F_SETLKW, &lock) < 0) { 195 int err = errno; 196 197 (void) close(lock_fd); 198 (void) unlink(lock_file); 199 errno = err; 200 return (-1); 201 } 202 return (lock_fd); 203 } 204 205 static void 206 i_dladm_unlock_db(const char *lock_file, int fd) 207 { 208 struct flock lock; 209 210 if (fd < 0) 211 return; 212 213 lock.l_type = F_UNLCK; 214 lock.l_whence = SEEK_SET; 215 lock.l_start = 0; 216 lock.l_len = 0; 217 218 (void) fcntl(fd, F_SETLKW, &lock); 219 (void) close(fd); 220 (void) unlink(lock_file); 221 } 222 223 /* 224 * Given a link class, returns its class string. 225 */ 226 const char * 227 dladm_class2str(datalink_class_t class, char *buf) 228 { 229 const char *s; 230 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; 247 } 248 249 (void) snprintf(buf, DLADM_STRSIZE, "%s", s); 250 return (buf); 251 } 252 253 /* 254 * Given a physical link media type, returns its media type string. 255 */ 256 const char * 257 dladm_media2str(uint32_t media, char *buf) 258 { 259 const char *s; 260 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 case DL_IPNET: 344 s = "IPNET"; 345 break; 346 default: 347 s = "--"; 348 break; 349 } 350 351 (void) snprintf(buf, DLADM_STRSIZE, "%s", s); 352 return (buf); 353 } 354 355 dladm_status_t 356 i_dladm_rw_db(const char *db_file, mode_t db_perms, 357 dladm_status_t (*process_db)(void *, FILE *, FILE *), 358 void *arg, boolean_t writeop) 359 { 360 dladm_status_t status = DLADM_STATUS_OK; 361 FILE *fp, *nfp = NULL; 362 char lock[MAXPATHLEN]; 363 char file[MAXPATHLEN]; 364 char newfile[MAXPATHLEN]; 365 char *db_basename; 366 int nfd, lock_fd; 367 368 /* 369 * If we are called from a boot script such as net-physical, 370 * it's quite likely that the root fs is still not writable. 371 * For this case, it's ok for the lock creation to fail since 372 * no one else could be accessing our configuration file. 373 */ 374 db_basename = strrchr(db_file, '/'); 375 if (db_basename == NULL || db_basename[1] == '\0') 376 return (dladm_errno2status(EINVAL)); 377 db_basename++; 378 (void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename); 379 if ((lock_fd = i_dladm_lock_db 380 (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS) 381 return (dladm_errno2status(errno)); 382 383 (void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file); 384 if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) { 385 int err = errno; 386 387 i_dladm_unlock_db(lock, lock_fd); 388 if (err == ENOENT) 389 return (DLADM_STATUS_DBNOTFOUND); 390 391 return (dladm_errno2status(err)); 392 } 393 394 if (writeop) { 395 (void) snprintf(newfile, MAXPATHLEN, "%s/%s.new", 396 dladm_rootdir, db_file); 397 if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, 398 db_perms)) < 0) { 399 (void) fclose(fp); 400 i_dladm_unlock_db(lock, lock_fd); 401 return (dladm_errno2status(errno)); 402 } 403 404 if ((nfp = fdopen(nfd, "w")) == NULL) { 405 (void) close(nfd); 406 (void) fclose(fp); 407 (void) unlink(newfile); 408 i_dladm_unlock_db(lock, lock_fd); 409 return (dladm_errno2status(errno)); 410 } 411 } 412 status = (*process_db)(arg, fp, nfp); 413 if (!writeop || status != DLADM_STATUS_OK) 414 goto done; 415 416 /* 417 * Configuration files need to be owned by the 'dladm' user. 418 * If we are invoked by root, the file ownership needs to be fixed. 419 */ 420 if (getuid() == 0 || geteuid() == 0) { 421 if (fchown(nfd, UID_DLADM, GID_SYS) < 0) { 422 status = dladm_errno2status(errno); 423 goto done; 424 } 425 } 426 427 if (fflush(nfp) == EOF) { 428 status = dladm_errno2status(errno); 429 goto done; 430 } 431 (void) fclose(fp); 432 (void) fclose(nfp); 433 434 if (rename(newfile, file) < 0) { 435 (void) unlink(newfile); 436 i_dladm_unlock_db(lock, lock_fd); 437 return (dladm_errno2status(errno)); 438 } 439 440 i_dladm_unlock_db(lock, lock_fd); 441 return (DLADM_STATUS_OK); 442 443 done: 444 if (nfp != NULL) { 445 (void) fclose(nfp); 446 if (status != DLADM_STATUS_OK) 447 (void) unlink(newfile); 448 } 449 (void) fclose(fp); 450 i_dladm_unlock_db(lock, lock_fd); 451 return (status); 452 } 453 454 dladm_status_t 455 dladm_set_rootdir(const char *rootdir) 456 { 457 DIR *dp; 458 459 if (rootdir == NULL || *rootdir != '/' || 460 (dp = opendir(rootdir)) == NULL) 461 return (DLADM_STATUS_BADARG); 462 463 (void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN); 464 (void) closedir(dp); 465 return (DLADM_STATUS_OK); 466 } 467 468 boolean_t 469 dladm_valid_linkname(const char *link) 470 { 471 size_t len = strlen(link); 472 const char *cp; 473 474 if (len + 1 >= MAXLINKNAMELEN) 475 return (B_FALSE); 476 477 /* 478 * The link name cannot start with a digit and must end with a digit. 479 */ 480 if ((isdigit(link[0]) != 0) || (isdigit(link[len - 1]) == 0)) 481 return (B_FALSE); 482 483 /* 484 * The legal characters in a link name are: 485 * alphanumeric (a-z, A-Z, 0-9), and the underscore ('_'). 486 */ 487 for (cp = link; *cp != '\0'; cp++) { 488 if ((isalnum(*cp) == 0) && (*cp != '_')) 489 return (B_FALSE); 490 } 491 492 return (B_TRUE); 493 } 494