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 2006 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 <fcntl.h> 32 #include <strings.h> 33 #include <dirent.h> 34 #include <net/if.h> 35 #include <sys/stat.h> 36 #include <sys/dld.h> 37 #include <libdlpi.h> 38 #include <libdevinfo.h> 39 #include <libdladm_impl.h> 40 41 typedef struct dladm_dev { 42 char dd_name[IFNAMSIZ]; 43 struct dladm_dev *dd_next; 44 } dladm_dev_t; 45 46 typedef struct dladm_walk { 47 dladm_dev_t *dw_dev_list; 48 } dladm_walk_t; 49 50 static char dladm_rootdir[MAXPATHLEN] = "/"; 51 52 /* 53 * Issue an ioctl to the specified file descriptor attached to the 54 * DLD control driver interface. 55 */ 56 int 57 i_dladm_ioctl(int fd, int ic_cmd, void *ic_dp, int ic_len) 58 { 59 struct strioctl iocb; 60 61 iocb.ic_cmd = ic_cmd; 62 iocb.ic_timout = 0; 63 iocb.ic_len = ic_len; 64 iocb.ic_dp = (char *)ic_dp; 65 66 return (ioctl(fd, I_STR, &iocb)); 67 } 68 69 /* 70 * Return the attributes of the specified datalink from the DLD driver. 71 */ 72 static int 73 i_dladm_info(int fd, const char *name, dladm_attr_t *dap) 74 { 75 dld_ioc_attr_t dia; 76 77 if (strlen(name) >= IFNAMSIZ) { 78 errno = EINVAL; 79 return (-1); 80 } 81 82 (void) strlcpy(dia.dia_name, name, IFNAMSIZ); 83 84 if (i_dladm_ioctl(fd, DLDIOCATTR, &dia, sizeof (dia)) < 0) 85 return (-1); 86 87 (void) strlcpy(dap->da_dev, dia.dia_dev, MAXNAMELEN); 88 dap->da_max_sdu = dia.dia_max_sdu; 89 dap->da_vid = dia.dia_vid; 90 91 return (0); 92 } 93 94 /* 95 * Adds a datalink to the array corresponding to arg. 96 */ 97 static void 98 i_dladm_nt_net_add(void *arg, char *name) 99 { 100 dladm_walk_t *dwp = arg; 101 dladm_dev_t *ddp = dwp->dw_dev_list; 102 dladm_dev_t **lastp = &dwp->dw_dev_list; 103 104 while (ddp) { 105 /* 106 * Skip duplicates. 107 */ 108 if (strcmp(ddp->dd_name, name) == 0) 109 return; 110 111 lastp = &ddp->dd_next; 112 ddp = ddp->dd_next; 113 } 114 115 if ((ddp = malloc(sizeof (*ddp))) == NULL) 116 return; 117 118 (void) strlcpy(ddp->dd_name, name, IFNAMSIZ); 119 ddp->dd_next = NULL; 120 *lastp = ddp; 121 } 122 123 /* 124 * Walker callback invoked for each DDI_NT_NET node. 125 */ 126 static int 127 i_dladm_nt_net_walk(di_node_t node, di_minor_t minor, void *arg) 128 { 129 dl_info_ack_t dlia; 130 char name[IFNAMSIZ]; 131 int fd; 132 char *provider; 133 uint_t ppa; 134 135 provider = di_minor_name(minor); 136 137 if ((fd = dlpi_open(provider)) < 0) 138 return (DI_WALK_CONTINUE); 139 140 if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) < 0) { 141 (void) dlpi_close(fd); 142 return (DI_WALK_CONTINUE); 143 } 144 145 if (dlia.dl_provider_style == DL_STYLE1) { 146 i_dladm_nt_net_add(arg, provider); 147 (void) dlpi_close(fd); 148 return (DI_WALK_CONTINUE); 149 } 150 151 ppa = di_instance(node); 152 153 if (dlpi_attach(fd, -1, ppa) < 0) { 154 (void) dlpi_close(fd); 155 return (DI_WALK_CONTINUE); 156 } 157 (void) snprintf(name, IFNAMSIZ - 1, "%s%d", provider, ppa); 158 i_dladm_nt_net_add(arg, name); 159 (void) dlpi_close(fd); 160 return (DI_WALK_CONTINUE); 161 } 162 163 /* 164 * Invoke the specified callback function for each active DDI_NT_NET 165 * node. 166 */ 167 int 168 dladm_walk(void (*fn)(void *, const char *), void *arg) 169 { 170 di_node_t root; 171 dladm_walk_t dw; 172 dladm_dev_t *ddp, *last_ddp; 173 174 if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { 175 errno = EFAULT; 176 return (-1); 177 } 178 dw.dw_dev_list = NULL; 179 180 (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dw, 181 i_dladm_nt_net_walk); 182 183 di_fini(root); 184 185 ddp = dw.dw_dev_list; 186 while (ddp) { 187 fn(arg, ddp->dd_name); 188 (void) dladm_walk_vlan(fn, arg, ddp->dd_name); 189 last_ddp = ddp; 190 ddp = ddp->dd_next; 191 free(last_ddp); 192 } 193 194 return (0); 195 } 196 197 /* 198 * Invoke the specified callback function for each vlan managed by dld 199 */ 200 int 201 dladm_walk_vlan(void (*fn)(void *, const char *), void *arg, const char *name) 202 { 203 int fd, bufsize, i; 204 int nvlan = 4094; 205 dld_ioc_vlan_t *iocp = NULL; 206 dld_vlan_info_t *dvip; 207 208 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 209 return (-1); 210 211 bufsize = sizeof (dld_ioc_vlan_t) + nvlan * sizeof (dld_vlan_info_t); 212 213 if ((iocp = (dld_ioc_vlan_t *)calloc(1, bufsize)) == NULL) 214 return (-1); 215 216 (void) strlcpy((char *)iocp->div_name, name, IFNAMSIZ); 217 if (i_dladm_ioctl(fd, DLDIOCVLAN, iocp, bufsize) == 0) { 218 dvip = (dld_vlan_info_t *)(iocp + 1); 219 for (i = 0; i < iocp->div_count; i++) 220 (*fn)(arg, dvip[i].dvi_name); 221 } 222 /* 223 * Note: Callers of dladm_walk_vlan() ignore the return 224 * value of this routine. So ignoring ioctl failure case 225 * and just returning 0. 226 */ 227 free(iocp); 228 (void) close(fd); 229 return (0); 230 } 231 232 233 /* 234 * Returns the current attributes of the specified datalink. 235 */ 236 int 237 dladm_info(const char *name, dladm_attr_t *dap) 238 { 239 int fd; 240 241 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 242 return (-1); 243 244 if (i_dladm_info(fd, name, dap) < 0) 245 goto failed; 246 247 (void) close(fd); 248 return (0); 249 250 failed: 251 (void) close(fd); 252 return (-1); 253 } 254 255 const char * 256 dladm_status2str(dladm_status_t status, char *buf) 257 { 258 const char *s; 259 260 switch (status) { 261 case DLADM_STATUS_OK: 262 s = "ok"; 263 break; 264 case DLADM_STATUS_BADARG: 265 s = "invalid argument"; 266 break; 267 case DLADM_STATUS_FAILED: 268 s = "operation failed"; 269 break; 270 case DLADM_STATUS_TOOSMALL: 271 s = "buffer size too small"; 272 break; 273 case DLADM_STATUS_NOTSUP: 274 s = "operation not supported"; 275 break; 276 case DLADM_STATUS_NOTFOUND: 277 s = "object not found"; 278 break; 279 case DLADM_STATUS_BADVAL: 280 s = "invalid value"; 281 break; 282 case DLADM_STATUS_NOMEM: 283 s = "insufficient memory"; 284 break; 285 case DLADM_STATUS_EXIST: 286 s = "object already exists"; 287 break; 288 case DLADM_STATUS_LINKINVAL: 289 s = "invalid link"; 290 break; 291 case DLADM_STATUS_PROPRDONLY: 292 s = "read-only property"; 293 break; 294 case DLADM_STATUS_BADVALCNT: 295 s = "invalid number of values"; 296 break; 297 case DLADM_STATUS_DBNOTFOUND: 298 s = "database not found"; 299 break; 300 case DLADM_STATUS_DENIED: 301 s = "permission denied"; 302 break; 303 case DLADM_STATUS_IOERR: 304 s = "I/O error"; 305 break; 306 default: 307 s = ""; 308 } 309 (void) snprintf(buf, DLADM_STRSIZE, "%s", s); 310 return (buf); 311 } 312 313 /* 314 * Convert a unix errno to a dladm_status_t. 315 * We only convert errnos that are likely to be encountered. All others 316 * are mapped to DLADM_STATUS_FAILED. 317 */ 318 dladm_status_t 319 dladm_errno2status(int err) 320 { 321 switch (err) { 322 case EINVAL: 323 return (DLADM_STATUS_BADARG); 324 case EEXIST: 325 return (DLADM_STATUS_EXIST); 326 case ENOENT: 327 return (DLADM_STATUS_NOTFOUND); 328 case ENOSPC: 329 return (DLADM_STATUS_TOOSMALL); 330 case ENOMEM: 331 return (DLADM_STATUS_NOMEM); 332 case ENOTSUP: 333 return (DLADM_STATUS_NOTSUP); 334 case EACCES: 335 return (DLADM_STATUS_DENIED); 336 case EIO: 337 return (DLADM_STATUS_IOERR); 338 default: 339 return (DLADM_STATUS_FAILED); 340 } 341 } 342 343 /* 344 * These are the uid and gid of the user 'dladm'. 345 * The directory /etc/dladm and all files under it are owned by this user. 346 */ 347 #define DLADM_DB_OWNER 15 348 #define DLADM_DB_GROUP 3 349 #define LOCK_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 350 351 static int 352 i_dladm_lock_db(const char *lock_file, short type) 353 { 354 int lock_fd; 355 struct flock lock; 356 357 if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC, 358 LOCK_DB_PERMS)) < 0) 359 return (-1); 360 361 lock.l_type = type; 362 lock.l_whence = SEEK_SET; 363 lock.l_start = 0; 364 lock.l_len = 0; 365 366 if (fcntl(lock_fd, F_SETLKW, &lock) < 0) { 367 int err = errno; 368 369 (void) close(lock_fd); 370 (void) unlink(lock_file); 371 errno = err; 372 return (-1); 373 } 374 return (lock_fd); 375 } 376 377 static void 378 i_dladm_unlock_db(const char *lock_file, int fd) 379 { 380 struct flock lock; 381 382 if (fd < 0) 383 return; 384 385 lock.l_type = F_UNLCK; 386 lock.l_whence = SEEK_SET; 387 lock.l_start = 0; 388 lock.l_len = 0; 389 390 (void) fcntl(fd, F_SETLKW, &lock); 391 (void) close(fd); 392 (void) unlink(lock_file); 393 } 394 395 dladm_status_t 396 i_dladm_rw_db(const char *db_file, mode_t db_perms, 397 dladm_status_t (*process_db)(void *, FILE *, FILE *), 398 void *arg, boolean_t writeop) 399 { 400 dladm_status_t status = DLADM_STATUS_OK; 401 FILE *fp, *nfp = NULL; 402 char lock[MAXPATHLEN]; 403 char file[MAXPATHLEN]; 404 char newfile[MAXPATHLEN]; 405 char *db_basename; 406 int nfd, lock_fd; 407 408 /* 409 * If we are called from a boot script such as net-physical, 410 * it's quite likely that the root fs is still not writable. 411 * For this case, it's ok for the lock creation to fail since 412 * no one else could be accessing our configuration file. 413 */ 414 db_basename = strrchr(db_file, '/'); 415 if (db_basename == NULL || db_basename[1] == '\0') 416 return (dladm_errno2status(EINVAL)); 417 db_basename++; 418 (void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename); 419 if ((lock_fd = i_dladm_lock_db 420 (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS) 421 return (dladm_errno2status(errno)); 422 423 (void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file); 424 if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) { 425 int err = errno; 426 427 i_dladm_unlock_db(lock, lock_fd); 428 if (err == ENOENT) 429 return (DLADM_STATUS_DBNOTFOUND); 430 431 return (dladm_errno2status(err)); 432 } 433 434 if (writeop) { 435 (void) snprintf(newfile, MAXPATHLEN, "%s/%s.new", 436 dladm_rootdir, db_file); 437 if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, 438 db_perms)) < 0) { 439 (void) fclose(fp); 440 i_dladm_unlock_db(lock, lock_fd); 441 return (dladm_errno2status(errno)); 442 } 443 444 if ((nfp = fdopen(nfd, "w")) == NULL) { 445 (void) close(nfd); 446 (void) fclose(fp); 447 (void) unlink(newfile); 448 i_dladm_unlock_db(lock, lock_fd); 449 return (dladm_errno2status(errno)); 450 } 451 } 452 status = (*process_db)(arg, fp, nfp); 453 if (!writeop || status != DLADM_STATUS_OK) 454 goto done; 455 456 /* 457 * Configuration files need to be owned by the 'dladm' user. 458 * If we are invoked by root, the file ownership needs to be fixed. 459 */ 460 if (getuid() == 0 || geteuid() == 0) { 461 if (fchown(nfd, DLADM_DB_OWNER, DLADM_DB_GROUP) < 0) { 462 status = dladm_errno2status(errno); 463 goto done; 464 } 465 } 466 467 if (fflush(nfp) == EOF) { 468 status = dladm_errno2status(errno); 469 goto done; 470 } 471 (void) fclose(fp); 472 (void) fclose(nfp); 473 474 if (rename(newfile, file) < 0) { 475 (void) unlink(newfile); 476 i_dladm_unlock_db(lock, lock_fd); 477 return (dladm_errno2status(errno)); 478 } 479 480 i_dladm_unlock_db(lock, lock_fd); 481 return (DLADM_STATUS_OK); 482 483 done: 484 if (nfp != NULL) { 485 (void) fclose(nfp); 486 if (status != DLADM_STATUS_OK) 487 (void) unlink(newfile); 488 } 489 (void) fclose(fp); 490 i_dladm_unlock_db(lock, lock_fd); 491 return (status); 492 } 493 494 dladm_status_t 495 dladm_set_rootdir(const char *rootdir) 496 { 497 DIR *dp; 498 499 if (rootdir == NULL || *rootdir != '/' || 500 (dp = opendir(rootdir)) == NULL) 501 return (DLADM_STATUS_BADARG); 502 503 (void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN); 504 (void) closedir(dp); 505 return (DLADM_STATUS_OK); 506 } 507