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