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