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 *
i_ipadm_find_conf_type(const char * type)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
i_ipadm_ip_addhostname2dbline(nvlist_t * nvl,char * buf,size_t buflen)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
i_ipadm_ip4_nvp2dbline(nvpair_t * nvp,char * buf,size_t buflen)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
i_ipadm_ip6_nvp2dbline(nvpair_t * nvp,char * buf,size_t buflen)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
i_ipadm_intfid_nvp2dbline(nvpair_t * nvp,char * buf,size_t buflen)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
i_ipadm_dhcp_nvp2dbline(nvpair_t * nvp,char * buf,size_t buflen)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
i_ipadm_str_nvp2dbline(nvpair_t * nvp,char * buf,size_t buflen)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
ipadm_nvlist2str(nvlist_t * nvl,char * buf,size_t buflen)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
i_ipadm_add_nvpair(nvlist_t * nvl,char * name,char * value)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
i_ipadm_add_ipaddr2nvl(nvlist_t * nvl,ipadm_addrobj_t ipaddr)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
i_ipadm_add_intfid2nvl(nvlist_t * nvl,ipadm_addrobj_t addr)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
i_ipadm_add_dhcp2nvl(nvlist_t * nvl,boolean_t primary,int32_t wait)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
i_ipadm_str_dbline2nvl(nvlist_t * nvl,char * name,char * value)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
i_ipadm_ip4_dbline2nvl(nvlist_t * nvl,char * name,char * value)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
i_ipadm_ip6_dbline2nvl(nvlist_t * nvl,char * name,char * value)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
i_ipadm_intfid_dbline2nvl(nvlist_t * nvl,char * name,char * value)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
i_ipadm_dhcp_dbline2nvl(nvlist_t * nvl,char * name,char * value)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
ipadm_str2nvlist(const char * inbuf,nvlist_t ** ipnvl,uint_t flags)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
ipadm_rw_db(db_wfunc_t * db_walk_func,void * arg,const char * db_file,mode_t db_perms,ipadm_db_op_t db_op)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
ipadm_process_db_line(db_wfunc_t * db_walk_func,void * arg,FILE * fp,FILE * nfp,ipadm_db_op_t db_op)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 }
834