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