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 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Contains DB walker functions, which are of type `db_wfunc_t'; 28 * 29 * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf, 30 * size_t bufsize, int *errp); 31 * 32 * ipadm_rw_db() walks through the data store, one line at a time and calls 33 * these call back functions with: 34 * `cbarg' - callback argument 35 * `db_nvl' - representing a line from DB in nvlist_t form 36 * `buf' - character buffer to hold modified line 37 * `bufsize'- size of the buffer 38 * `errp' - captures any error inside the walker function. 39 * 40 * All the 'write' callback functions modify `db_nvl' based on `cbarg' and 41 * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'. 42 * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(), 43 * the modified `buf' is written back into DB. 44 * 45 * All the 'read' callback functions, retrieve the information from the DB, by 46 * reading `db_nvl' and then populate the `cbarg'. 47 */ 48 49 #include <stdlib.h> 50 #include <strings.h> 51 #include <errno.h> 52 #include <assert.h> 53 #include <sys/types.h> 54 #include <sys/socket.h> 55 #include <netinet/in.h> 56 #include <arpa/inet.h> 57 #include <unistd.h> 58 #include "ipmgmt_impl.h" 59 60 #define ATYPE "_atype" /* name of the address type nvpair */ 61 #define FLAGS "_flags" /* name of the flags nvpair */ 62 63 /* 64 * flag used by ipmgmt_persist_aobjmap() to indicate address type is 65 * IPADM_ADDR_IPV6_ADDRCONF. 66 */ 67 #define IPMGMT_ATYPE_V6ACONF 0x1 68 69 extern pthread_rwlock_t ipmgmt_dbconf_lock; 70 71 /* 72 * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed 73 * in private nvpairs `proto', `ifname' & `aobjname'. 74 */ 75 static boolean_t 76 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname, 77 const char *aobjname) 78 { 79 char *db_proto = NULL, *db_ifname = NULL; 80 char *db_aobjname = NULL; 81 nvpair_t *nvp; 82 char *name; 83 84 /* walk through db_nvl and retrieve all its private nvpairs */ 85 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 86 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 87 name = nvpair_name(nvp); 88 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 89 (void) nvpair_value_string(nvp, &db_proto); 90 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 91 (void) nvpair_value_string(nvp, &db_ifname); 92 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 93 (void) nvpair_value_string(nvp, &db_aobjname); 94 } 95 96 if (proto != NULL && proto[0] == '\0') 97 proto = NULL; 98 if (ifname != NULL && ifname[0] == '\0') 99 ifname = NULL; 100 if (aobjname != NULL && aobjname[0] == '\0') 101 aobjname = NULL; 102 103 if ((proto == NULL && db_proto != NULL) || 104 (proto != NULL && db_proto == NULL) || 105 strcmp(proto, db_proto) != 0) { 106 /* no intersection - different protocols. */ 107 return (B_FALSE); 108 } 109 if ((ifname == NULL && db_ifname != NULL) || 110 (ifname != NULL && db_ifname == NULL) || 111 strcmp(ifname, db_ifname) != 0) { 112 /* no intersection - different interfaces. */ 113 return (B_FALSE); 114 } 115 if ((aobjname == NULL && db_aobjname != NULL) || 116 (aobjname != NULL && db_aobjname == NULL) || 117 strcmp(aobjname, db_aobjname) != 0) { 118 /* no intersection - different address objects */ 119 return (B_FALSE); 120 } 121 122 return (B_TRUE); 123 } 124 125 /* 126 * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects. 127 */ 128 static boolean_t 129 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl) 130 { 131 nvpair_t *nvp; 132 char *name; 133 char *proto = NULL, *ifname = NULL, *aobjname = NULL; 134 135 /* walk through in_nvl and retrieve all its private nvpairs */ 136 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 137 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 138 name = nvpair_name(nvp); 139 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 140 (void) nvpair_value_string(nvp, &proto); 141 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 142 (void) nvpair_value_string(nvp, &ifname); 143 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 144 (void) nvpair_value_string(nvp, &aobjname); 145 } 146 147 return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname)); 148 } 149 150 /* 151 * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed 152 * in private nvpairs `proto', `ifname' & `aobjname'. 153 */ 154 static boolean_t 155 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto, 156 const char *ifname, char *aobjname) 157 { 158 char *db_ifname = NULL, *db_proto = NULL; 159 char *db_aobjname = NULL; 160 nvpair_t *nvp; 161 char *name; 162 163 /* walk through db_nvl and retrieve all private nvpairs */ 164 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 165 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 166 name = nvpair_name(nvp); 167 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 168 (void) nvpair_value_string(nvp, &db_proto); 169 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 170 (void) nvpair_value_string(nvp, &db_ifname); 171 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 172 (void) nvpair_value_string(nvp, &db_aobjname); 173 } 174 175 if (proto != NULL && proto[0] != '\0') { 176 if ((db_proto == NULL || strcmp(proto, db_proto) != 0)) 177 return (B_FALSE); 178 } 179 if (ifname != NULL && ifname[0] != '\0') { 180 if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0)) 181 return (B_FALSE); 182 } 183 if (aobjname != NULL && aobjname[0] != '\0') { 184 if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0)) 185 return (B_FALSE); 186 } 187 188 return (B_TRUE); 189 } 190 191 /* 192 * Retrieves the property value from the DB. The property whose value is to be 193 * retrieved is in `pargp->ia_pname'. 194 */ 195 /* ARGSUSED */ 196 boolean_t 197 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 198 int *errp) 199 { 200 ipmgmt_prop_arg_t *pargp = arg; 201 boolean_t cont = B_TRUE; 202 char *pval; 203 int err = 0; 204 205 *errp = 0; 206 207 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, 208 pargp->ia_ifname, pargp->ia_aobjname)) 209 return (B_TRUE); 210 211 if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname, 212 &pval)) == 0) { 213 (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval)); 214 /* 215 * We have retrieved what we are looking for. 216 * Stop the walker. 217 */ 218 cont = B_FALSE; 219 } else { 220 if (err == ENOENT) 221 err = 0; 222 *errp = err; 223 } 224 225 return (cont); 226 } 227 228 /* 229 * Removes the property value from the DB. The property whose value is to be 230 * removed is in `pargp->ia_pname'. 231 */ 232 /* ARGSUSED */ 233 boolean_t 234 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 235 int *errp) 236 { 237 ipmgmt_prop_arg_t *pargp = arg; 238 239 *errp = 0; 240 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, 241 pargp->ia_ifname, pargp->ia_aobjname)) 242 return (B_TRUE); 243 244 if (!nvlist_exists(db_nvl, pargp->ia_pname)) 245 return (B_TRUE); 246 247 /* 248 * We found the property in the DB. If IPMGMT_REMOVE is not set then 249 * delete the entry from the db. If it is set, then the property is a 250 * multi-valued property so just remove the specified values from DB. 251 */ 252 if (pargp->ia_flags & IPMGMT_REMOVE) { 253 char *dbpval = NULL; 254 char *inpval = pargp->ia_pval; 255 char pval[MAXPROPVALLEN]; 256 char *val, *lasts; 257 258 *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval); 259 if (*errp != 0) 260 return (B_FALSE); 261 262 /* 263 * multi-valued properties are represented as comma separated 264 * values. Use string tokenizer functions to split them and 265 * search for the value to be removed. 266 */ 267 bzero(pval, sizeof (pval)); 268 if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) { 269 if (strcmp(val, inpval) != 0) 270 (void) strlcat(pval, val, MAXPROPVALLEN); 271 while ((val = strtok_r(NULL, ",", &lasts)) != NULL) { 272 if (strcmp(val, inpval) != 0) { 273 if (pval[0] != '\0') 274 (void) strlcat(pval, ",", 275 MAXPROPVALLEN); 276 (void) strlcat(pval, val, 277 MAXPROPVALLEN); 278 } 279 } 280 } else { 281 if (strcmp(dbpval, inpval) != 0) 282 *errp = ENOENT; 283 else 284 buf[0] = '\0'; 285 return (B_FALSE); 286 } 287 *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval); 288 if (*errp != 0) 289 return (B_FALSE); 290 291 (void) memset(buf, 0, buflen); 292 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 293 /* buffer overflow */ 294 *errp = ENOBUFS; 295 } 296 } else { 297 buf[0] = '\0'; 298 } 299 300 /* stop the search */ 301 return (B_FALSE); 302 } 303 304 /* 305 * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is 306 * found, when one of the following occurs first. 307 * - the input aobjname matches the db aobjname. Return the db address. 308 * - the input interface matches the db interface. Return all the 309 * matching db lines with addresses. 310 */ 311 /* ARGSUSED */ 312 boolean_t 313 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 314 int *errp) 315 { 316 ipmgmt_getaddr_cbarg_t *cbarg = arg; 317 char *db_aobjname = NULL; 318 char *db_ifname = NULL; 319 nvlist_t *db_addr = NULL; 320 char name[IPMGMT_STRSIZE]; 321 nvpair_t *nvp; 322 boolean_t add_nvl = B_FALSE; 323 324 /* Parse db nvlist */ 325 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 326 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 327 if (nvpair_type(nvp) == DATA_TYPE_NVLIST) 328 (void) nvpair_value_nvlist(nvp, &db_addr); 329 else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) 330 (void) nvpair_value_string(nvp, &db_ifname); 331 else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0) 332 (void) nvpair_value_string(nvp, &db_aobjname); 333 } 334 335 if (db_aobjname == NULL) /* Not an address */ 336 return (B_TRUE); 337 338 /* Check for a match between the aobjnames or the interface name */ 339 if (cbarg->cb_aobjname[0] != '\0') { 340 if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0) 341 add_nvl = B_TRUE; 342 } else if (cbarg->cb_ifname[0] != '\0') { 343 if (strcmp(cbarg->cb_ifname, db_ifname) == 0) 344 add_nvl = B_TRUE; 345 } else { 346 add_nvl = B_TRUE; 347 } 348 349 if (add_nvl) { 350 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, 351 cbarg->cb_ocnt); 352 *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl); 353 if (*errp == 0) 354 cbarg->cb_ocnt++; 355 } 356 return (B_TRUE); 357 } 358 359 /* 360 * This function takes the appropriate lock, read or write, based on the 361 * `db_op' and then calls DB walker ipadm_rw_db(). 362 */ 363 extern int 364 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) 365 { 366 int err; 367 boolean_t writeop; 368 mode_t mode; 369 370 writeop = (db_op != IPADM_DB_READ); 371 372 if (writeop) { 373 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock); 374 mode = IPADM_FILE_MODE; 375 } else { 376 (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock); 377 mode = 0; 378 } 379 380 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE, mode, db_op); 381 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); 382 return (err); 383 } 384 385 /* 386 * Used to add an entry towards the end of DB. It just returns B_TRUE for 387 * every line of the DB. When we reach the end, ipadm_rw_db() adds the 388 * line at the end. 389 */ 390 /* ARGSUSED */ 391 boolean_t 392 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp) 393 { 394 return (B_TRUE); 395 } 396 397 /* 398 * This function is used to update or create an entry in DB. The nvlist_t, 399 * `in_nvl', represents the line we are looking for. Once we ensure the right 400 * line from DB, we update that entry. 401 */ 402 boolean_t 403 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 404 int *errp) 405 { 406 ipadm_dbwrite_cbarg_t *cb = arg; 407 uint_t flags = cb->dbw_flags; 408 nvlist_t *in_nvl = cb->dbw_nvl; 409 nvpair_t *nvp; 410 char *name, *instrval = NULL, *dbstrval = NULL; 411 char pval[MAXPROPVALLEN]; 412 413 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 414 return (B_TRUE); 415 416 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 417 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 418 name = nvpair_name(nvp); 419 if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name)) 420 break; 421 } 422 423 if (nvp == NULL) 424 return (B_TRUE); 425 426 assert(nvpair_type(nvp) == DATA_TYPE_STRING); 427 428 if ((*errp = nvpair_value_string(nvp, &instrval)) != 0) 429 return (B_FALSE); 430 431 /* 432 * If IPMGMT_APPEND is set then we are dealing with multi-valued 433 * properties. We append to the entry from the db, with the new value. 434 */ 435 if (flags & IPMGMT_APPEND) { 436 if ((*errp = nvlist_lookup_string(db_nvl, name, 437 &dbstrval)) != 0) 438 return (B_FALSE); 439 (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval, 440 instrval); 441 if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0) 442 return (B_FALSE); 443 } else { 444 /* case of in-line update of a db entry */ 445 if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0) 446 return (B_FALSE); 447 } 448 449 (void) memset(buf, 0, buflen); 450 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 451 /* buffer overflow */ 452 *errp = ENOBUFS; 453 } 454 *errp = 0; 455 456 /* we updated the DB entry, so do not continue */ 457 return (B_FALSE); 458 } 459 460 /* 461 * For the given `cbarg->cb_ifname' interface, retrieves any persistent 462 * interface information (used in 'ipadm show-if') 463 */ 464 /* ARGSUSED */ 465 boolean_t 466 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 467 int *errp) 468 { 469 ipmgmt_getif_cbarg_t *cbarg = arg; 470 char *ifname = cbarg->cb_ifname; 471 char *intf = NULL; 472 ipadm_if_info_t *ifp = NULL; 473 sa_family_t af; 474 char *afstr; 475 476 *errp = 0; 477 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 || 478 nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 || 479 (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) { 480 return (B_TRUE); 481 } 482 af = atoi(afstr); 483 for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) { 484 if (strcmp(ifp->ifi_name, intf) == 0) 485 break; 486 } 487 if (ifp == NULL) { 488 ipadm_if_info_t *new; 489 490 if ((new = calloc(1, sizeof (*new))) == NULL) { 491 *errp = ENOMEM; 492 return (B_FALSE); /* don't continue the walk */ 493 } 494 new->ifi_next = cbarg->cb_ifinfo; 495 cbarg->cb_ifinfo = new; 496 ifp = new; 497 (void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name)); 498 } 499 500 if (af == AF_INET) { 501 ifp->ifi_pflags |= IFIF_IPV4; 502 } else { 503 assert(af == AF_INET6); 504 ifp->ifi_pflags |= IFIF_IPV6; 505 } 506 507 /* Terminate the walk if we found both v4 and v6 interfaces. */ 508 if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) && 509 (ifp->ifi_pflags & IFIF_IPV6)) 510 return (B_FALSE); 511 512 return (B_TRUE); 513 } 514 515 /* 516 * Deletes those entries from the database for which interface name 517 * matches with the given `cbarg->cb_ifname' 518 */ 519 /* ARGSUSED */ 520 boolean_t 521 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 522 int *errp) 523 { 524 ipmgmt_if_cbarg_t *cbarg = arg; 525 boolean_t isv6 = (cbarg->cb_family == AF_INET6); 526 char *ifname = cbarg->cb_ifname; 527 char *modstr = NULL; 528 char *afstr; 529 char *aobjname; 530 uint_t proto; 531 ipmgmt_aobjmap_t *head; 532 boolean_t aobjfound = B_FALSE; 533 534 *errp = 0; 535 536 if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL)) 537 return (B_TRUE); 538 539 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) { 540 if (atoi(afstr) == cbarg->cb_family) 541 goto delete; 542 return (B_TRUE); 543 } 544 545 /* Reset all the interface configurations for 'ifname' */ 546 if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) || 547 nvlist_exists(db_nvl, IPADM_NVP_INTFID))) { 548 goto delete; 549 } 550 if (!isv6 && 551 (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || 552 nvlist_exists(db_nvl, IPADM_NVP_DHCP))) { 553 goto delete; 554 } 555 556 if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) { 557 /* 558 * This must be an address property. Delete this 559 * line if there is a match in the address family. 560 */ 561 head = aobjmap.aobjmap_head; 562 while (head != NULL) { 563 if (strcmp(head->am_aobjname, aobjname) == 0) { 564 aobjfound = B_TRUE; 565 if (head->am_family == cbarg->cb_family) 566 goto delete; 567 } 568 head = head->am_next; 569 } 570 /* 571 * If aobjfound = B_FALSE, then this address is not 572 * available in active configuration. We should go ahead 573 * and delete it. 574 */ 575 if (!aobjfound) 576 goto delete; 577 } 578 579 /* 580 * If we are removing both v4 and v6 interface, then we get rid of 581 * all the properties for that interface. On the other hand, if we 582 * are deleting only v4 instance of an interface, then we delete v4 583 * properties only. 584 */ 585 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) { 586 proto = ipadm_str2proto(modstr); 587 switch (proto) { 588 case MOD_PROTO_IPV6: 589 if (isv6) 590 goto delete; 591 break; 592 case MOD_PROTO_IPV4: 593 if (!isv6) 594 goto delete; 595 break; 596 case MOD_PROTO_IP: 597 /* this should never be the case, today */ 598 assert(0); 599 break; 600 } 601 } 602 /* Not found a match yet. Continue processing the db */ 603 return (B_TRUE); 604 delete: 605 /* delete the line from the db */ 606 buf[0] = '\0'; 607 return (B_TRUE); 608 } 609 610 /* 611 * Deletes those entries from the database for which address object name 612 * matches with the given `cbarg->cb_aobjname' 613 */ 614 /* ARGSUSED */ 615 boolean_t 616 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 617 int *errp) 618 { 619 ipmgmt_resetaddr_cbarg_t *cbarg = arg; 620 char *aobjname = cbarg->cb_aobjname; 621 622 *errp = 0; 623 if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname)) 624 return (B_TRUE); 625 626 /* delete the line from the db */ 627 buf[0] = '\0'; 628 return (B_TRUE); 629 } 630 631 /* 632 * Retrieves all interface props, including addresses, for given interface(s). 633 * `invl' contains the list of interfaces, for which information need to be 634 * retrieved. 635 */ 636 /* ARGSUSED */ 637 boolean_t 638 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 639 int *errp) 640 { 641 ipmgmt_initif_cbarg_t *cbarg = arg; 642 nvlist_t *onvl = cbarg->cb_onvl; 643 nvlist_t *invl = cbarg->cb_invl; 644 sa_family_t in_af = cbarg->cb_family; 645 char *db_ifname; 646 647 *errp = 0; 648 if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 && 649 nvlist_exists(invl, db_ifname)) { 650 char name[IPMGMT_STRSIZE]; 651 sa_family_t db_af = in_af; 652 uint_t proto; 653 char *pstr; 654 655 if (in_af != AF_UNSPEC) { 656 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, 657 &pstr) == 0) { 658 proto = ipadm_str2proto(pstr); 659 if (proto == MOD_PROTO_IPV4) 660 db_af = AF_INET; 661 else if (proto == MOD_PROTO_IPV6) 662 db_af = AF_INET6; 663 else 664 db_af = in_af; 665 } else { 666 if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || 667 nvlist_exists(db_nvl, IPADM_NVP_DHCP)) 668 db_af = AF_INET; 669 else 670 db_af = AF_INET6; 671 } 672 } 673 if (in_af == db_af) { 674 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, 675 cbarg->cb_ocnt); 676 *errp = nvlist_add_nvlist(onvl, name, db_nvl); 677 if (*errp == 0) 678 cbarg->cb_ocnt++; 679 } 680 } 681 return (B_TRUE); 682 } 683 684 /* 685 * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep' 686 * into `aobjmap' structure. 687 */ 688 static int 689 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep) 690 { 691 ipmgmt_aobjmap_t *new, *head; 692 693 head = aobjmap.aobjmap_head; 694 if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL) 695 return (ENOMEM); 696 *new = *nodep; 697 new->am_next = NULL; 698 699 /* Add the node at the beginning of the list */ 700 if (head == NULL) { 701 aobjmap.aobjmap_head = new; 702 } else { 703 new->am_next = aobjmap.aobjmap_head; 704 aobjmap.aobjmap_head = new; 705 } 706 return (0); 707 } 708 709 /* 710 * A recursive function to generate alphabetized number given a decimal number. 711 * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa', 712 * 'ab', 'ac', et al. 713 */ 714 static void 715 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp) 716 { 717 if (num >= 26) 718 i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp); 719 if (*cp != endp) { 720 *cp[0] = 'a' + (num % 26); 721 (*cp)++; 722 } 723 } 724 725 /* 726 * This function generates an `aobjname', when required, and then does 727 * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks 728 * through the `aobjmap' to check if an address object with the same 729 * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate 730 * `aobjname's are not allowed. 731 * 732 * If `nodep->am_aobjname' is an empty string then the daemon generates an 733 * `aobjname' using the `am_nextnum', which contains the next number to be 734 * used to generate `aobjname'. `am_nextnum' is converted to base26 using 735 * `a-z' alphabets in i_ipmgmt_num2priv_aobjname(). 736 * 737 * `am_nextnum' will be 0 to begin with. Every time an address object that 738 * needs `aobjname' is added it's incremented by 1. So for the first address 739 * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1. 740 * For the second address object on that interface `am_aobjname' will be net0/_b 741 * and `am_nextnum' will incremented to 2. 742 */ 743 static int 744 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep) 745 { 746 ipmgmt_aobjmap_t *head; 747 uint32_t nextnum; 748 749 for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next) 750 if (strcmp(head->am_ifname, nodep->am_ifname) == 0) 751 break; 752 nextnum = (head == NULL ? 0 : head->am_nextnum); 753 754 /* 755 * if `aobjname' is empty, then the daemon has to generate the 756 * next `aobjname' for the given interface and family. 757 */ 758 if (nodep->am_aobjname[0] == '\0') { 759 char tmpstr[IPADM_AOBJ_USTRSIZ - 1]; /* 1 for leading '_' */ 760 char *cp = tmpstr; 761 char *endp = tmpstr + sizeof (tmpstr); 762 763 i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp); 764 765 if (cp == endp) 766 return (EINVAL); 767 cp[0] = '\0'; 768 769 if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s", 770 nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) { 771 return (EINVAL); 772 } 773 nodep->am_nextnum = ++nextnum; 774 } else { 775 for (head = aobjmap.aobjmap_head; head != NULL; 776 head = head->am_next) { 777 if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0) 778 return (EEXIST); 779 } 780 nodep->am_nextnum = nextnum; 781 } 782 return (i_ipmgmt_add_amnode(nodep)); 783 } 784 785 /* 786 * Performs following operations on the global `aobjmap' linked list. 787 * (a) ADDROBJ_ADD: add or update address object in `aobjmap' 788 * (b) ADDROBJ_DELETE: delete address object from `aobjmap' 789 * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap' 790 * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap' 791 */ 792 int 793 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op) 794 { 795 ipmgmt_aobjmap_t *head, *prev, *matched = NULL; 796 boolean_t update = B_TRUE; 797 int err = 0; 798 ipadm_db_op_t db_op; 799 800 (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock); 801 802 head = aobjmap.aobjmap_head; 803 switch (op) { 804 case ADDROBJ_ADD: 805 /* 806 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and 807 * update, else add the new node. 808 */ 809 for (; head != NULL; head = head->am_next) { 810 /* 811 * For IPv6, we need to distinguish between the 812 * linklocal and non-linklocal nodes 813 */ 814 if (strcmp(head->am_aobjname, 815 nodep->am_aobjname) == 0 && 816 (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF || 817 head->am_linklocal == nodep->am_linklocal)) 818 break; 819 } 820 821 if (head != NULL) { 822 /* update the node */ 823 (void) strlcpy(head->am_ifname, nodep->am_ifname, 824 sizeof (head->am_ifname)); 825 head->am_lnum = nodep->am_lnum; 826 head->am_family = nodep->am_family; 827 head->am_flags = nodep->am_flags; 828 head->am_atype = nodep->am_atype; 829 if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 830 head->am_ifid = nodep->am_ifid; 831 head->am_linklocal = nodep->am_linklocal; 832 } 833 } else { 834 for (head = aobjmap.aobjmap_head; head != NULL; 835 head = head->am_next) { 836 if (strcmp(head->am_ifname, 837 nodep->am_ifname) == 0) 838 break; 839 } 840 nodep->am_nextnum = (head == NULL ? 0 : 841 head->am_nextnum); 842 err = i_ipmgmt_add_amnode(nodep); 843 } 844 db_op = IPADM_DB_WRITE; 845 break; 846 case ADDROBJ_DELETE: 847 prev = head; 848 while (head != NULL) { 849 if (strcmp(head->am_aobjname, 850 nodep->am_aobjname) == 0) { 851 nodep->am_atype = head->am_atype; 852 /* 853 * There could be multiple IPV6_ADDRCONF nodes, 854 * with same address object name, so check for 855 * logical number also. 856 */ 857 if (head->am_atype != 858 IPADM_ADDR_IPV6_ADDRCONF || 859 nodep->am_lnum == head->am_lnum) 860 break; 861 } 862 prev = head; 863 head = head->am_next; 864 } 865 if (head != NULL) { 866 /* 867 * If the address object is in both active and 868 * persistent configuration and the user is deleting it 869 * only from active configuration then mark this node 870 * for deletion by reseting IPMGMT_ACTIVE bit. 871 * With this the same address object name cannot 872 * be reused until it is permanently removed. 873 */ 874 if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) && 875 nodep->am_flags == IPMGMT_ACTIVE) { 876 /* Update flags in the in-memory map. */ 877 head->am_flags &= ~IPMGMT_ACTIVE; 878 head->am_lnum = -1; 879 880 /* Update info in file. */ 881 db_op = IPADM_DB_WRITE; 882 *nodep = *head; 883 } else { 884 (void) strlcpy(nodep->am_ifname, 885 head->am_ifname, 886 sizeof (nodep->am_ifname)); 887 /* otherwise delete the node */ 888 if (head == aobjmap.aobjmap_head) 889 aobjmap.aobjmap_head = head->am_next; 890 else 891 prev->am_next = head->am_next; 892 free(head); 893 db_op = IPADM_DB_DELETE; 894 } 895 } else { 896 err = ENOENT; 897 } 898 break; 899 case ADDROBJ_LOOKUPADD: 900 err = i_ipmgmt_lookupadd_amnode(nodep); 901 update = B_FALSE; 902 break; 903 case ADDROBJ_SETLIFNUM: 904 update = B_FALSE; 905 for (; head != NULL; head = head->am_next) { 906 if (strcmp(head->am_ifname, 907 nodep->am_ifname) == 0 && 908 head->am_family == nodep->am_family && 909 head->am_lnum == nodep->am_lnum) { 910 err = EEXIST; 911 break; 912 } 913 if (strcmp(head->am_aobjname, 914 nodep->am_aobjname) == 0) { 915 matched = head; 916 } 917 } 918 if (err == EEXIST) 919 break; 920 if (matched != NULL) { 921 /* update the lifnum */ 922 matched->am_lnum = nodep->am_lnum; 923 } else { 924 err = ENOENT; 925 } 926 break; 927 default: 928 assert(0); 929 } 930 931 if (err == 0 && update) 932 err = ipmgmt_persist_aobjmap(nodep, db_op); 933 934 (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); 935 936 return (err); 937 } 938 939 /* 940 * Given a node in `aobjmap', this function converts it into nvlist_t structure. 941 * The content to be written to DB must be represented as nvlist_t. 942 */ 943 static int 944 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np) 945 { 946 int err; 947 char strval[IPMGMT_STRSIZE]; 948 949 *nvl = NULL; 950 if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0) 951 goto fail; 952 953 if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME, 954 np->am_aobjname)) != 0) 955 goto fail; 956 957 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME, 958 np->am_ifname)) != 0) 959 goto fail; 960 961 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum); 962 if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0) 963 goto fail; 964 965 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family); 966 if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0) 967 goto fail; 968 969 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags); 970 if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0) 971 goto fail; 972 973 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype); 974 if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0) 975 goto fail; 976 977 if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 978 struct sockaddr_in6 *in6; 979 980 in6 = (struct sockaddr_in6 *)&np->am_ifid; 981 if (np->am_linklocal && 982 IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) { 983 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 984 "default")) != 0) 985 goto fail; 986 } else { 987 if (inet_ntop(AF_INET6, &in6->sin6_addr, strval, 988 IPMGMT_STRSIZE) == NULL) { 989 err = errno; 990 goto fail; 991 } 992 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 993 strval)) != 0) 994 goto fail; 995 } 996 } else { 997 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 998 "")) != 0) 999 goto fail; 1000 } 1001 return (err); 1002 fail: 1003 nvlist_free(*nvl); 1004 return (err); 1005 } 1006 1007 /* 1008 * Read the aobjmap data store and build the in-memory representation 1009 * of the aobjmap. We don't need to hold any locks while building this as 1010 * we do this in very early stage of daemon coming up, even before the door 1011 * is opened. 1012 */ 1013 /* ARGSUSED */ 1014 extern boolean_t 1015 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 1016 int *errp) 1017 { 1018 nvpair_t *nvp = NULL; 1019 char *name, *strval = NULL; 1020 ipmgmt_aobjmap_t node; 1021 struct sockaddr_in6 *in6; 1022 1023 *errp = 0; 1024 node.am_next = NULL; 1025 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 1026 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 1027 name = nvpair_name(nvp); 1028 1029 if ((*errp = nvpair_value_string(nvp, &strval)) != 0) 1030 return (B_TRUE); 1031 if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) { 1032 (void) strlcpy(node.am_aobjname, strval, 1033 sizeof (node.am_aobjname)); 1034 } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) { 1035 (void) strlcpy(node.am_ifname, strval, 1036 sizeof (node.am_ifname)); 1037 } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) { 1038 node.am_lnum = atoi(strval); 1039 } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) { 1040 node.am_family = (sa_family_t)atoi(strval); 1041 } else if (strcmp(FLAGS, name) == 0) { 1042 node.am_flags = atoi(strval); 1043 } else if (strcmp(ATYPE, name) == 0) { 1044 node.am_atype = (ipadm_addr_type_t)atoi(strval); 1045 } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) { 1046 if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1047 in6 = (struct sockaddr_in6 *)&node.am_ifid; 1048 if (strcmp(strval, "default") == 0) { 1049 bzero(in6, sizeof (node.am_ifid)); 1050 node.am_linklocal = B_TRUE; 1051 } else { 1052 (void) inet_pton(AF_INET6, strval, 1053 &in6->sin6_addr); 1054 if (IN6_IS_ADDR_UNSPECIFIED( 1055 &in6->sin6_addr)) 1056 node.am_linklocal = B_TRUE; 1057 } 1058 } 1059 } 1060 } 1061 1062 /* we have all the information we need, add the node */ 1063 *errp = i_ipmgmt_add_amnode(&node); 1064 1065 return (B_TRUE); 1066 } 1067 1068 /* 1069 * Updates an entry from the temporary cache file, which matches the given 1070 * address object name. 1071 */ 1072 /* ARGSUSED */ 1073 static boolean_t 1074 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, 1075 size_t buflen, int *errp) 1076 { 1077 ipadm_dbwrite_cbarg_t *cb = arg; 1078 nvlist_t *in_nvl = cb->dbw_nvl; 1079 uint32_t flags = cb->dbw_flags; 1080 char *db_lifnumstr = NULL, *in_lifnumstr = NULL; 1081 1082 *errp = 0; 1083 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 1084 return (B_TRUE); 1085 1086 if (flags & IPMGMT_ATYPE_V6ACONF) { 1087 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, 1088 &db_lifnumstr) != 0 || 1089 nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM, 1090 &in_lifnumstr) != 0 || 1091 (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 && 1092 strcmp(db_lifnumstr, in_lifnumstr) != 0)) 1093 return (B_TRUE); 1094 } 1095 1096 /* we found the match */ 1097 (void) memset(buf, 0, buflen); 1098 if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) { 1099 /* buffer overflow */ 1100 *errp = ENOBUFS; 1101 } 1102 1103 /* stop the walker */ 1104 return (B_FALSE); 1105 } 1106 1107 /* 1108 * Deletes an entry from the temporary cache file, which matches the given 1109 * address object name. 1110 */ 1111 /* ARGSUSED */ 1112 static boolean_t 1113 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, 1114 size_t buflen, int *errp) 1115 { 1116 ipmgmt_aobjmap_t *nodep = arg; 1117 char *db_lifnumstr = NULL; 1118 1119 *errp = 0; 1120 if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname, 1121 nodep->am_aobjname)) 1122 return (B_TRUE); 1123 1124 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1125 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, 1126 &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum) 1127 return (B_TRUE); 1128 } 1129 1130 /* we found the match, delete the line from the db */ 1131 buf[0] = '\0'; 1132 1133 /* stop the walker */ 1134 return (B_FALSE); 1135 } 1136 1137 /* 1138 * Adds or deletes aobjmap node information into a temporary cache file. 1139 */ 1140 extern int 1141 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op) 1142 { 1143 int err; 1144 ipadm_dbwrite_cbarg_t cb; 1145 nvlist_t *nvl = NULL; 1146 1147 if (op == IPADM_DB_WRITE) { 1148 if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0) 1149 return (err); 1150 cb.dbw_nvl = nvl; 1151 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) 1152 cb.dbw_flags = IPMGMT_ATYPE_V6ACONF; 1153 else 1154 cb.dbw_flags = 0; 1155 1156 err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, 1157 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE); 1158 nvlist_free(nvl); 1159 } else { 1160 assert(op == IPADM_DB_DELETE); 1161 1162 err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, 1163 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE); 1164 } 1165 return (err); 1166 } 1167