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 (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * This file contains routines to read/write formatted entries from/to 27 * libipadm data store /etc/ipadm/ipadm.conf. Each entry in the DB is a 28 * series of IPADM_NVPAIR_SEP separated (name, value) pairs, as shown 29 * below: 30 * name=value[;...] 31 * 32 * The 'name' determines how to interpret 'value'. The supported names are: 33 * 34 * IPADM_NVP_IPV6ADDR - value holds local and remote IPv6 addresses and when 35 * converted to nvlist, will contain nvpairs for local and remote 36 * addresses. These nvpairs are of type DATA_TYPE_STRING 37 * 38 * IPADM_NVP_IPV4ADDR - value holds local and remote IPv4 addresses and when 39 * converted to nvlist, will contain nvpairs for local and remote 40 * addresses. These nvpairs are of type DATA_TYPE_STRING 41 * 42 * IPADM_NVP_INTFID - value holds token, prefixlen, stateless and stateful 43 * info and when converted to nvlist, will contain following nvpairs 44 * interface_id: DATA_TYPE_UINT8_ARRAY 45 * prefixlen: DATA_TYPE_UINT32 46 * stateless: DATA_TYPE_STRING 47 * stateful: DATA_TYPE_STRING 48 * 49 * IPADM_NVP_DHCP - value holds wait time and primary info and when converted 50 * to nvlist, will contain following nvpairs 51 * wait: DATA_TYPE_INT32 52 * primary: DATA_TYPE_BOOLEAN 53 * 54 * default - value is a single entity and when converted to nvlist, will 55 * contain nvpair of type DATA_TYPE_STRING. nvpairs private to 56 * ipadm are of this type. Further the property name and property 57 * values are stored as nvpairs of this type. 58 * 59 * The syntax for each line is described above the respective functions below. 60 */ 61 62 #include <stdlib.h> 63 #include <strings.h> 64 #include <errno.h> 65 #include <ctype.h> 66 #include <sys/types.h> 67 #include <sys/stat.h> 68 #include <sys/dld.h> 69 #include <fcntl.h> 70 #include <dirent.h> 71 #include <unistd.h> 72 #include <assert.h> 73 #include <sys/socket.h> 74 #include <netinet/in.h> 75 #include <arpa/inet.h> 76 #include <sys/sockio.h> 77 #include "libipadm_impl.h" 78 79 #define MAXLINELEN 1024 80 #define IPADM_NVPAIR_SEP ";" 81 #define IPADM_NAME_SEP "," 82 83 static char ipadm_rootdir[MAXPATHLEN] = "/"; 84 85 static int ipadm_process_db_line(db_wfunc_t *, void *, FILE *fp, FILE *nfp, 86 ipadm_db_op_t); 87 88 /* 89 * convert nvpair to a "name=value" string for writing to the DB. 90 */ 91 typedef size_t ipadm_wfunc_t(nvpair_t *, char *, size_t); 92 93 /* 94 * ipadm_rfunc_t takes (`name', `value') and adds the appropriately typed 95 * nvpair to the nvlist. 96 */ 97 typedef void ipadm_rfunc_t(nvlist_t *, char *name, char *value); 98 99 static ipadm_rfunc_t i_ipadm_str_dbline2nvl, i_ipadm_ip4_dbline2nvl, 100 i_ipadm_ip6_dbline2nvl, i_ipadm_intfid_dbline2nvl, 101 i_ipadm_dhcp_dbline2nvl; 102 103 static ipadm_wfunc_t i_ipadm_str_nvp2dbline, i_ipadm_ip4_nvp2dbline, 104 i_ipadm_ip6_nvp2dbline, i_ipadm_intfid_nvp2dbline, 105 i_ipadm_dhcp_nvp2dbline; 106 107 /* 108 * table of function pointers to read/write formatted entries from/to 109 * ipadm.conf. 110 */ 111 typedef struct ipadm_conf_ent_s { 112 const char *ipent_type_name; 113 ipadm_wfunc_t *ipent_wfunc; 114 ipadm_rfunc_t *ipent_rfunc; 115 } ipadm_conf_ent_t; 116 117 static ipadm_conf_ent_t ipadm_conf_ent[] = { 118 { IPADM_NVP_IPV6ADDR, i_ipadm_ip6_nvp2dbline, i_ipadm_ip6_dbline2nvl }, 119 { IPADM_NVP_IPV4ADDR, i_ipadm_ip4_nvp2dbline, i_ipadm_ip4_dbline2nvl }, 120 { IPADM_NVP_INTFID, i_ipadm_intfid_nvp2dbline, 121 i_ipadm_intfid_dbline2nvl }, 122 { IPADM_NVP_DHCP, i_ipadm_dhcp_nvp2dbline, i_ipadm_dhcp_dbline2nvl }, 123 { NULL, i_ipadm_str_nvp2dbline, i_ipadm_str_dbline2nvl } 124 }; 125 126 static ipadm_conf_ent_t * 127 i_ipadm_find_conf_type(const char *type) 128 { 129 int i; 130 131 for (i = 0; ipadm_conf_ent[i].ipent_type_name != NULL; i++) 132 if (strcmp(type, ipadm_conf_ent[i].ipent_type_name) == 0) 133 break; 134 return (&ipadm_conf_ent[i]); 135 } 136 137 /* 138 * Extracts the hostnames IPADM_NVP_IPADDRHNAME and IPADM_NVP_IPDADDRHNAME from 139 * the given nvlist `nvl' and adds the strings to `buf'. 140 */ 141 size_t 142 i_ipadm_ip_addhostname2dbline(nvlist_t *nvl, char *buf, size_t buflen) 143 { 144 char *cp; 145 char tmpbuf[IPADM_STRSIZE]; 146 147 /* Add the local hostname */ 148 if (nvlist_lookup_string(nvl, IPADM_NVP_IPADDRHNAME, &cp) != 0) 149 return (0); 150 (void) strlcat(buf, cp, buflen); /* local hostname */ 151 152 /* Add the dst hostname */ 153 if (nvlist_lookup_string(nvl, IPADM_NVP_IPDADDRHNAME, &cp) != 0) { 154 /* no dst addr. just add a NULL character */ 155 (void) snprintf(tmpbuf, sizeof (tmpbuf), ","); 156 } else { 157 (void) snprintf(tmpbuf, sizeof (tmpbuf), ",%s", cp); 158 } 159 return (strlcat(buf, tmpbuf, buflen)); 160 } 161 162 /* 163 * Converts IPADM_NVP_IPV4ADDR nvpair to a string representation for writing to 164 * the DB. The converted string format: 165 * ipv4addr=<local numeric IP string or hostname,remote numeric IP 166 * string or hostname> 167 */ 168 static size_t 169 i_ipadm_ip4_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen) 170 { 171 nvlist_t *v; 172 int nbytes; 173 174 assert(nvpair_type(nvp) == DATA_TYPE_NVLIST && 175 strcmp(nvpair_name(nvp), IPADM_NVP_IPV4ADDR) == 0); 176 177 (void) snprintf(buf, buflen, "%s=", IPADM_NVP_IPV4ADDR); 178 if (nvpair_value_nvlist(nvp, &v) != 0) 179 goto fail; 180 nbytes = i_ipadm_ip_addhostname2dbline(v, buf, buflen); 181 if (nbytes != 0) 182 return (nbytes); 183 fail: 184 buf[0] = '\0'; 185 return (0); 186 } 187 188 /* 189 * Converts IPADM_NVP_IPV6ADDR nvpair to a string representation for writing to 190 * the DB. The converted string format: 191 * ipv6addr=<local numeric IP string or hostname,remote numeric IP 192 * string or hostname> 193 */ 194 static size_t 195 i_ipadm_ip6_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen) 196 { 197 nvlist_t *v; 198 int nbytes; 199 200 assert(nvpair_type(nvp) == DATA_TYPE_NVLIST && 201 strcmp(nvpair_name(nvp), IPADM_NVP_IPV6ADDR) == 0); 202 203 (void) snprintf(buf, buflen, "%s=", IPADM_NVP_IPV6ADDR); 204 if (nvpair_value_nvlist(nvp, &v) != 0) 205 goto fail; 206 nbytes = i_ipadm_ip_addhostname2dbline(v, buf, buflen); 207 if (nbytes != 0) 208 return (nbytes); 209 fail: 210 buf[0] = '\0'; 211 return (0); 212 } 213 214 /* 215 * Converts IPADM_NVP_INTFID nvpair to a string representation for writing to 216 * the DB. The converted string format: 217 * IPADM_NVP_INTFID=<intfid/prefixlen>,{yes|no},{yes|no} 218 */ 219 static size_t 220 i_ipadm_intfid_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen) 221 { 222 char addrbuf[IPADM_STRSIZE]; 223 nvlist_t *v; 224 uint32_t prefixlen; 225 struct in6_addr in6addr; 226 char *stateless; 227 char *stateful; 228 229 assert(nvpair_type(nvp) == DATA_TYPE_NVLIST && 230 strcmp(nvpair_name(nvp), IPADM_NVP_INTFID) == 0); 231 232 (void) snprintf(buf, buflen, "%s=", IPADM_NVP_INTFID); 233 if (nvpair_value_nvlist(nvp, &v) != 0) 234 goto fail; 235 if (i_ipadm_nvl2in6_addr(v, IPADM_NVP_IPNUMADDR, &in6addr) != 236 IPADM_SUCCESS) 237 goto fail; 238 (void) inet_ntop(AF_INET6, &in6addr, addrbuf, 239 sizeof (addrbuf)); 240 (void) strlcat(buf, addrbuf, buflen); 241 if (nvlist_lookup_uint32(v, IPADM_NVP_PREFIXLEN, &prefixlen) != 0 || 242 nvlist_lookup_string(v, IPADM_NVP_STATELESS, &stateless) != 0 || 243 nvlist_lookup_string(v, IPADM_NVP_STATEFUL, &stateful) != 0) 244 goto fail; 245 (void) snprintf(addrbuf, sizeof (addrbuf), "/%d,%s,%s", 246 prefixlen, stateless, stateful); 247 return (strlcat(buf, addrbuf, buflen)); 248 fail: 249 buf[0] = '\0'; 250 return (0); 251 } 252 253 /* 254 * Converts IPADM_NVP_DHCP nvpair to a string representation for writing to the 255 * DB. The converted string format: 256 * IPADM_NVP_DHCP=<wait_time>,{yes|no} 257 */ 258 static size_t 259 i_ipadm_dhcp_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen) 260 { 261 char addrbuf[IPADM_STRSIZE]; 262 int32_t wait; 263 boolean_t primary; 264 nvlist_t *v; 265 266 assert(nvpair_type(nvp) == DATA_TYPE_NVLIST && 267 strcmp(nvpair_name(nvp), IPADM_NVP_DHCP) == 0); 268 269 if (nvpair_value_nvlist(nvp, &v) != 0 || 270 nvlist_lookup_int32(v, IPADM_NVP_WAIT, &wait) != 0 || 271 nvlist_lookup_boolean_value(v, IPADM_NVP_PRIMARY, &primary) != 0) { 272 return (0); 273 } 274 (void) snprintf(buf, buflen, "%s=", IPADM_NVP_DHCP); 275 (void) snprintf(addrbuf, sizeof (addrbuf), "%d,%s", wait, 276 (primary ? "yes" : "no")); 277 return (strlcat(buf, addrbuf, buflen)); 278 } 279 280 /* 281 * Constructs a "<name>=<value>" string from the nvpair, whose type must 282 * be STRING. 283 */ 284 static size_t 285 i_ipadm_str_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen) 286 { 287 char *str = NULL; 288 289 assert(nvpair_type(nvp) == DATA_TYPE_STRING); 290 if (nvpair_value_string(nvp, &str) != 0) 291 return (0); 292 return (snprintf(buf, buflen, "%s=%s", nvpair_name(nvp), str)); 293 } 294 295 /* 296 * Converts a nvlist to string of the form: 297 * <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>; 298 */ 299 size_t 300 ipadm_nvlist2str(nvlist_t *nvl, char *buf, size_t buflen) 301 { 302 nvpair_t *nvp = NULL; 303 uint_t nbytes = 0, tbytes = 0; 304 ipadm_conf_ent_t *ipent; 305 size_t bufsize = buflen; 306 307 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL; 308 nvp = nvlist_next_nvpair(nvl, nvp)) { 309 ipent = i_ipadm_find_conf_type(nvpair_name(nvp)); 310 nbytes = (*ipent->ipent_wfunc)(nvp, buf, buflen); 311 /* add nvpair separator */ 312 nbytes += snprintf(buf + nbytes, buflen - nbytes, "%s", 313 IPADM_NVPAIR_SEP); 314 buflen -= nbytes; 315 buf += nbytes; 316 tbytes += nbytes; 317 if (tbytes >= bufsize) /* buffer overflow */ 318 return (0); 319 } 320 nbytes = snprintf(buf, buflen, "%c%c", '\n', '\0'); 321 tbytes += nbytes; 322 if (tbytes >= bufsize) 323 return (0); 324 return (tbytes); 325 } 326 327 /* 328 * Adds a nvpair, using the `name' and `value', to the nvlist in `nvl'. 329 * The value will be interpreted as explained at the top of this file. 330 */ 331 static void 332 i_ipadm_add_nvpair(nvlist_t *nvl, char *name, char *value) 333 { 334 ipadm_conf_ent_t *ipent; 335 336 ipent = i_ipadm_find_conf_type(name); 337 (*ipent->ipent_rfunc)(nvl, name, value); 338 } 339 340 /* 341 * Adds an nvpair for IPv4 addr to the nvlist. The "name" is the string in 342 * IPADM_NVP_IPV4ADDR. The "value" for IPADM_NVP_IPV4ADDR is another nvlist. 343 * Allocate the value nvlist for IPADM_NVP_IPV4ADDR if necessary, and add 344 * the address and hostnames from the address object `ipaddr' to it. 345 * Then add the allocated nvlist to `nvl'. 346 */ 347 ipadm_status_t 348 i_ipadm_add_ipaddr2nvl(nvlist_t *nvl, ipadm_addrobj_t ipaddr) 349 { 350 nvlist_t *nvl_addr = NULL; 351 int err; 352 char *name; 353 sa_family_t af = ipaddr->ipadm_af; 354 355 if (af == AF_INET) { 356 name = IPADM_NVP_IPV4ADDR; 357 } else { 358 assert(af == AF_INET6); 359 name = IPADM_NVP_IPV6ADDR; 360 } 361 362 if (!nvlist_exists(nvl, name)) { 363 if ((err = nvlist_alloc(&nvl_addr, NV_UNIQUE_NAME, 0)) != 0) 364 return (ipadm_errno2status(err)); 365 if ((err = nvlist_add_nvlist(nvl, name, nvl_addr)) != 0) { 366 nvlist_free(nvl_addr); 367 return (ipadm_errno2status(err)); 368 } 369 nvlist_free(nvl_addr); 370 } 371 if ((err = nvlist_lookup_nvlist(nvl, name, &nvl_addr)) != 0 || 372 (err = nvlist_add_string(nvl_addr, IPADM_NVP_IPADDRHNAME, 373 ipaddr->ipadm_static_aname)) != 0) 374 return (ipadm_errno2status(err)); 375 if (ipaddr->ipadm_static_dname[0] != '\0') { 376 if ((err = nvlist_add_string(nvl_addr, IPADM_NVP_IPDADDRHNAME, 377 ipaddr->ipadm_static_dname)) != 0) 378 return (ipadm_errno2status(err)); 379 } 380 381 return (IPADM_SUCCESS); 382 } 383 384 /* 385 * Adds an nvpair for IPv6 interface id to the nvlist. The "name" is 386 * the string in IPADM_NVP_INTFID. The "value" for IPADM_NVP_INTFID is another 387 * nvlist. Allocate the value nvlist for IPADM_NVP_INTFID if necessary, and add 388 * the interface id and its prefixlen from the address object `ipaddr' to it. 389 * Then add the allocated nvlist to `nvl'. 390 */ 391 ipadm_status_t 392 i_ipadm_add_intfid2nvl(nvlist_t *nvl, ipadm_addrobj_t addr) 393 { 394 nvlist_t *nvl_addr = NULL; 395 struct in6_addr addr6; 396 int err; 397 398 if (!nvlist_exists(nvl, IPADM_NVP_INTFID)) { 399 if ((err = nvlist_alloc(&nvl_addr, NV_UNIQUE_NAME, 0)) != 0) 400 return (ipadm_errno2status(err)); 401 if ((err = nvlist_add_nvlist(nvl, IPADM_NVP_INTFID, 402 nvl_addr)) != 0) { 403 nvlist_free(nvl_addr); 404 return (ipadm_errno2status(err)); 405 } 406 nvlist_free(nvl_addr); 407 } 408 if ((err = nvlist_lookup_nvlist(nvl, IPADM_NVP_INTFID, 409 &nvl_addr)) != 0 || (err = nvlist_add_uint32(nvl_addr, 410 IPADM_NVP_PREFIXLEN, addr->ipadm_intfidlen)) != 0) { 411 return (ipadm_errno2status(err)); 412 } 413 addr6 = addr->ipadm_intfid.sin6_addr; 414 if ((err = nvlist_add_uint8_array(nvl_addr, IPADM_NVP_IPNUMADDR, 415 addr6.s6_addr, 16)) != 0) { 416 return (ipadm_errno2status(err)); 417 } 418 if (addr->ipadm_stateless) 419 err = nvlist_add_string(nvl_addr, IPADM_NVP_STATELESS, "yes"); 420 else 421 err = nvlist_add_string(nvl_addr, IPADM_NVP_STATELESS, "no"); 422 if (err != 0) 423 return (ipadm_errno2status(err)); 424 if (addr->ipadm_stateful) 425 err = nvlist_add_string(nvl_addr, IPADM_NVP_STATEFUL, "yes"); 426 else 427 err = nvlist_add_string(nvl_addr, IPADM_NVP_STATEFUL, "no"); 428 if (err != 0) 429 return (ipadm_errno2status(err)); 430 431 return (IPADM_SUCCESS); 432 } 433 434 /* 435 * Adds an nvpair for a dhcp address object to the nvlist. The "name" is 436 * the string in IPADM_NVP_DHCP. The "value" for IPADM_NVP_DHCP is another 437 * nvlist. Allocate the value nvlist for IPADM_NVP_DHCP if necessary, and add 438 * the parameters from the arguments `primary' and `wait'. 439 * Then add the allocated nvlist to `nvl'. 440 */ 441 ipadm_status_t 442 i_ipadm_add_dhcp2nvl(nvlist_t *nvl, boolean_t primary, int32_t wait) 443 { 444 nvlist_t *nvl_dhcp = NULL; 445 int err; 446 447 if (!nvlist_exists(nvl, IPADM_NVP_DHCP)) { 448 if ((err = nvlist_alloc(&nvl_dhcp, NV_UNIQUE_NAME, 0)) != 0) 449 return (ipadm_errno2status(err)); 450 if ((err = nvlist_add_nvlist(nvl, IPADM_NVP_DHCP, 451 nvl_dhcp)) != 0) { 452 nvlist_free(nvl_dhcp); 453 return (ipadm_errno2status(err)); 454 } 455 nvlist_free(nvl_dhcp); 456 } 457 if ((err = nvlist_lookup_nvlist(nvl, IPADM_NVP_DHCP, &nvl_dhcp)) != 0 || 458 (err = nvlist_add_int32(nvl_dhcp, IPADM_NVP_WAIT, wait)) != 0 || 459 (err = nvlist_add_boolean_value(nvl_dhcp, IPADM_NVP_PRIMARY, 460 primary)) != 0) { 461 return (ipadm_errno2status(err)); 462 } 463 464 return (IPADM_SUCCESS); 465 } 466 467 /* 468 * Add (name, value) as an nvpair of type DATA_TYPE_STRING to nvlist. 469 */ 470 static void 471 i_ipadm_str_dbline2nvl(nvlist_t *nvl, char *name, char *value) 472 { 473 /* if value is NULL create an empty node */ 474 if (value == NULL) 475 (void) nvlist_add_string(nvl, name, ""); 476 else 477 (void) nvlist_add_string(nvl, name, value); 478 } 479 480 /* 481 * `name' = IPADM_NVP_IPV4ADDR and 482 * `value' = <local numeric IP string or hostname,remote numeric IP string or 483 * hostname> 484 * This function will add an nvlist with the hostname information in 485 * nvpairs to the nvlist in `nvl'. 486 */ 487 static void 488 i_ipadm_ip4_dbline2nvl(nvlist_t *nvl, char *name, char *value) 489 { 490 char *cp, *hname; 491 struct ipadm_addrobj_s ipaddr; 492 493 assert(strcmp(name, IPADM_NVP_IPV4ADDR) == 0 && value != NULL); 494 495 bzero(&ipaddr, sizeof (ipaddr)); 496 ipaddr.ipadm_af = AF_INET; 497 498 hname = value; /* local hostname */ 499 cp = strchr(hname, ','); 500 assert(cp != NULL); 501 *cp++ = '\0'; 502 (void) strlcpy(ipaddr.ipadm_static_aname, hname, 503 sizeof (ipaddr.ipadm_static_aname)); 504 505 if (*cp != '\0') { 506 /* we have a dst hostname */ 507 (void) strlcpy(ipaddr.ipadm_static_dname, cp, 508 sizeof (ipaddr.ipadm_static_dname)); 509 } 510 (void) i_ipadm_add_ipaddr2nvl(nvl, &ipaddr); 511 } 512 513 /* 514 * `name' = IPADM_NVP_IPV6ADDR and 515 * `value' = <local numeric IP string or hostname,remote numeric IP string or 516 * hostname> 517 * This function will add an nvlist with the hostname information in 518 * nvpairs to the nvlist in `nvl'. 519 */ 520 static void 521 i_ipadm_ip6_dbline2nvl(nvlist_t *nvl, char *name, char *value) 522 { 523 char *cp, *hname; 524 struct ipadm_addrobj_s ipaddr; 525 526 assert(strcmp(name, IPADM_NVP_IPV6ADDR) == 0 && value != NULL); 527 528 bzero(&ipaddr, sizeof (ipaddr)); 529 ipaddr.ipadm_af = AF_INET6; 530 531 hname = value; /* local hostname */ 532 cp = strchr(hname, ','); 533 assert(cp != NULL); 534 *cp++ = '\0'; 535 (void) strlcpy(ipaddr.ipadm_static_aname, hname, 536 sizeof (ipaddr.ipadm_static_aname)); 537 538 if (*cp != '\0') { 539 /* we have a dst hostname */ 540 (void) strlcpy(ipaddr.ipadm_static_dname, cp, 541 sizeof (ipaddr.ipadm_static_dname)); 542 } 543 (void) i_ipadm_add_ipaddr2nvl(nvl, &ipaddr); 544 } 545 546 /* 547 * `name' = IPADM_NVP_INTFID and `value' = <intfid/prefixlen>,{yes,no},{yes|no} 548 * This function will add an nvlist with the address object information in 549 * nvpairs to the nvlist in `nvl'. 550 */ 551 static void 552 i_ipadm_intfid_dbline2nvl(nvlist_t *nvl, char *name, char *value) 553 { 554 char *cp; 555 struct ipadm_addrobj_s ipaddr; 556 char *endp; 557 char *prefixlen; 558 char *stateless; 559 char *stateful; 560 561 assert(strcmp(name, IPADM_NVP_INTFID) == 0 && value != NULL); 562 563 bzero(&ipaddr, sizeof (ipaddr)); 564 565 cp = strchr(value, '/'); 566 assert(cp != NULL); 567 568 *cp++ = '\0'; 569 ipaddr.ipadm_intfid.sin6_family = AF_INET6; 570 (void) inet_pton(AF_INET6, value, &ipaddr.ipadm_intfid.sin6_addr); 571 572 prefixlen = cp; 573 cp = strchr(cp, ','); 574 assert(cp != NULL); 575 *cp++ = '\0'; 576 577 errno = 0; 578 ipaddr.ipadm_intfidlen = (uint32_t)strtoul(prefixlen, &endp, 10); 579 if (*endp != '\0' || errno != 0) 580 return; 581 582 stateless = cp; 583 stateful = strchr(stateless, ','); 584 assert(stateful != NULL); 585 *stateful++ = '\0'; 586 ipaddr.ipadm_stateless = (strcmp(stateless, "yes") == 0); 587 ipaddr.ipadm_stateful = (strcmp(stateful, "yes") == 0); 588 589 /* Add all of it to the given nvlist */ 590 (void) i_ipadm_add_intfid2nvl(nvl, &ipaddr); 591 } 592 593 /* 594 * `name' = IPADM_NVP_DHCP and `value' = <wait_time>,{yes|no} 595 * This function will add an nvlist with the dhcp address object information in 596 * nvpairs to the nvlist in `nvl'. 597 */ 598 static void 599 i_ipadm_dhcp_dbline2nvl(nvlist_t *nvl, char *name, char *value) 600 { 601 char *cp; 602 char *endp; 603 long wait_time; 604 boolean_t primary; 605 606 assert(strcmp(name, IPADM_NVP_DHCP) == 0 && value != NULL); 607 cp = strchr(value, ','); 608 assert(cp != NULL); 609 *cp++ = '\0'; 610 errno = 0; 611 wait_time = strtol(value, &endp, 10); 612 if (*endp != '\0' || errno != 0) 613 return; 614 primary = (strcmp(cp, "yes") == 0); 615 (void) i_ipadm_add_dhcp2nvl(nvl, primary, (int32_t)wait_time); 616 } 617 618 /* 619 * Parses the buffer, for name-value pairs and creates nvlist. The value 620 * is always considered to be a string. 621 */ 622 int 623 ipadm_str2nvlist(const char *inbuf, nvlist_t **ipnvl, uint_t flags) 624 { 625 char *nv, *name, *val, *buf, *cp, *sep; 626 int err; 627 628 if (inbuf == NULL || inbuf[0] == '\0' || ipnvl == NULL) 629 return (EINVAL); 630 *ipnvl = NULL; 631 632 /* 633 * If IPADM_NORVAL is set, then `inbuf' should be comma delimited values 634 */ 635 if ((flags & IPADM_NORVAL) && strchr(inbuf, '=') != NULL) 636 return (EINVAL); 637 638 if ((cp = buf = strdup(inbuf)) == NULL) 639 return (errno); 640 641 while (isspace(*buf)) 642 buf++; 643 644 if (*buf == '\0') { 645 err = EINVAL; 646 goto fail; 647 } 648 649 nv = buf; 650 /* 651 * work on one nvpair at a time and extract the name and value 652 */ 653 sep = ((flags & IPADM_NORVAL) ? IPADM_NAME_SEP : IPADM_NVPAIR_SEP); 654 while ((nv = strsep(&buf, sep)) != NULL) { 655 if (*nv == '\n') 656 continue; 657 name = nv; 658 if ((val = strchr(nv, '=')) != NULL) 659 *val++ = '\0'; 660 if (*ipnvl == NULL && 661 (err = nvlist_alloc(ipnvl, NV_UNIQUE_NAME, 0)) != 0) 662 goto fail; 663 if (nvlist_exists(*ipnvl, name)) { 664 err = EEXIST; 665 goto fail; 666 } 667 /* Add the extracted nvpair to the nvlist `ipnvl'. */ 668 (void) i_ipadm_add_nvpair(*ipnvl, name, val); 669 } 670 free(cp); 671 return (0); 672 fail: 673 free(cp); 674 nvlist_free(*ipnvl); 675 *ipnvl = NULL; 676 return (err); 677 } 678 679 /* 680 * Opens the data store for read/write operation. For write operation we open 681 * another file and scribble the changes to it and copy the new file back to 682 * old file. 683 */ 684 int 685 ipadm_rw_db(db_wfunc_t *db_walk_func, void *arg, const char *db_file, 686 mode_t db_perms, ipadm_db_op_t db_op) 687 { 688 FILE *fp, *nfp = NULL; 689 char file[MAXPATHLEN]; 690 char newfile[MAXPATHLEN]; 691 int nfd; 692 boolean_t writeop; 693 int err = 0; 694 695 writeop = (db_op != IPADM_DB_READ); 696 697 (void) snprintf(file, MAXPATHLEN, "%s/%s", ipadm_rootdir, db_file); 698 699 /* open the data store */ 700 if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) 701 return (errno); 702 703 if (writeop) { 704 (void) snprintf(newfile, MAXPATHLEN, "%s/%s.new", 705 ipadm_rootdir, db_file); 706 if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, 707 db_perms)) < 0) { 708 err = errno; 709 (void) fclose(fp); 710 return (err); 711 } 712 713 if ((nfp = fdopen(nfd, "w")) == NULL) { 714 err = errno; 715 (void) close(nfd); 716 (void) fclose(fp); 717 (void) unlink(newfile); 718 return (err); 719 } 720 } 721 err = ipadm_process_db_line(db_walk_func, arg, fp, nfp, db_op); 722 if (!writeop) 723 goto done; 724 if (err != 0 && err != ENOENT) 725 goto done; 726 727 if (fflush(nfp) == EOF) { 728 err = errno; 729 goto done; 730 } 731 (void) fclose(fp); 732 (void) fclose(nfp); 733 734 if (rename(newfile, file) < 0) { 735 err = errno; 736 (void) unlink(newfile); 737 } 738 return (err); 739 done: 740 if (nfp != NULL) { 741 (void) fclose(nfp); 742 if (err != 0) 743 (void) unlink(newfile); 744 } 745 (void) fclose(fp); 746 return (err); 747 } 748 749 /* 750 * Processes each line of the configuration file, skipping lines with 751 * leading spaces, blank lines and comments. The line form the DB 752 * is converted to nvlist and the callback function is called to process 753 * the list. The buf could be modified by the callback function and 754 * if this is a write operation and buf is not truncated, buf will 755 * be written to disk. 756 * 757 * Further if cont is set to B_FALSE, the remainder of the file will 758 * continue to be read (however callback function will not be called) and, 759 * if necessary, written to disk as well. 760 */ 761 static int 762 ipadm_process_db_line(db_wfunc_t *db_walk_func, void *arg, FILE *fp, FILE *nfp, 763 ipadm_db_op_t db_op) 764 { 765 int err = 0; 766 char buf[MAXLINELEN]; 767 boolean_t cont = B_TRUE; 768 int i, len; 769 nvlist_t *db_nvl = NULL; 770 boolean_t line_deleted = B_FALSE; 771 772 while (fgets(buf, MAXLINELEN, fp) != NULL) { 773 /* 774 * Skip leading spaces, blank lines, and comments. 775 */ 776 len = strnlen(buf, MAXLINELEN); 777 for (i = 0; i < len; i++) { 778 if (!isspace(buf[i])) 779 break; 780 } 781 782 if (i != len && buf[i] != '#' && cont) { 783 if (ipadm_str2nvlist(buf, &db_nvl, 0) == 0) { 784 cont = db_walk_func(arg, db_nvl, buf, 785 MAXLINELEN, &err); 786 } else { 787 /* Delete corrupted line. */ 788 buf[0] = '\0'; 789 } 790 nvlist_free(db_nvl); 791 db_nvl = NULL; 792 } 793 if (err != 0) 794 break; 795 if (nfp != NULL && buf[0] == '\0') 796 line_deleted = B_TRUE; 797 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) { 798 err = errno; 799 break; 800 } 801 } 802 803 if (err != 0 || !cont) 804 return (err); 805 806 if (db_op == IPADM_DB_WRITE) { 807 nvlist_t *nvl; 808 809 /* 810 * `arg' will be NULL when we are doing in-line update of 811 * entries. 812 */ 813 if (arg != NULL) { 814 nvl = ((ipadm_dbwrite_cbarg_t *)arg)->dbw_nvl; 815 /* 816 * If the specified entry is not found above, we add 817 * the entry to the configuration file, here. 818 */ 819 (void) memset(buf, 0, MAXLINELEN); 820 if (ipadm_nvlist2str(nvl, buf, MAXLINELEN) == 0) 821 err = ENOBUFS; 822 else if (fputs(buf, nfp) == EOF) 823 err = errno; 824 } 825 return (err); 826 } 827 828 if (db_op == IPADM_DB_DELETE && line_deleted) 829 return (0); 830 831 /* if we have come this far, then we didn't find any match */ 832 return (ENOENT); 833 }