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