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