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