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 /*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2016 Nexenta Systems, Inc.
25 * Copyright 2016 Argo Technologie SA.
26 * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
27 */
28
29 /*
30 * Contains DB walker functions, which are of type `db_wfunc_t';
31 *
32 * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
33 * size_t bufsize, int *errp);
34 *
35 * ipadm_rw_db() walks through the data store, one line at a time and calls
36 * these call back functions with:
37 * `cbarg' - callback argument
38 * `db_nvl' - representing a line from DB in nvlist_t form
39 * `buf' - character buffer to hold modified line
40 * `bufsize'- size of the buffer
41 * `errp' - captures any error inside the walker function.
42 *
43 * All the 'write' callback functions modify `db_nvl' based on `cbarg' and
44 * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'.
45 * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(),
46 * the modified `buf' is written back into DB.
47 *
48 * All the 'read' callback functions, retrieve the information from the DB, by
49 * reading `db_nvl' and then populate the `cbarg'.
50 */
51
52 #include <stdlib.h>
53 #include <strings.h>
54 #include <errno.h>
55 #include <assert.h>
56 #include <sys/types.h>
57 #include <sys/socket.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <unistd.h>
61 #include "ipmgmt_impl.h"
62
63 /* SCF related property group names and property names */
64 #define IPMGMTD_APP_PG "ipmgmtd"
65 #define IPMGMTD_PROP_FBD "first_boot_done"
66 #define IPMGMTD_PROP_DBVER "datastore_version"
67 #define IPMGMTD_TRUESTR "true"
68
69 #define ATYPE "_atype" /* name of the address type nvpair */
70 #define FLAGS "_flags" /* name of the flags nvpair */
71
72 /*
73 * flag used by ipmgmt_persist_aobjmap() to indicate address type is
74 * IPADM_ADDR_IPV6_ADDRCONF.
75 */
76 #define IPMGMT_ATYPE_V6ACONF 0x1
77
78 extern pthread_rwlock_t ipmgmt_dbconf_lock;
79
80 /* signifies whether volatile copy of data store is in use */
81 static boolean_t ipmgmt_rdonly_root = B_FALSE;
82
83 typedef int ipmgmt_if_updater_func_t(nvlist_t *, nvpair_t *, uint_t);
84
85 static ipmgmt_if_updater_func_t ipmgmt_if_family_updater;
86 static ipmgmt_if_updater_func_t ipmgmt_if_groupmembers_updater;
87
88 static int ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl);
89
90 typedef struct {
91 const char *name;
92 ipmgmt_if_updater_func_t *func;
93 } ipmgmt_if_updater_ent_t;
94
95 static ipmgmt_if_updater_ent_t ipmgmt_if_updater_ent[] = {
96 {IPADM_NVP_FAMILIES, ipmgmt_if_family_updater},
97 {IPADM_NVP_MIFNAMES, ipmgmt_if_groupmembers_updater},
98 {NULL, NULL}
99 };
100
101 static ipmgmt_if_updater_ent_t *
102 ipmgmt_find_if_field_updater(const char *field_name)
103 {
104 int i;
105
106 for (i = 0; ipmgmt_if_updater_ent[i].name != NULL; i++) {
107 if (strcmp(field_name, ipmgmt_if_updater_ent[i].name) == 0) {
108 break;
109 }
110 }
111
112 return (&ipmgmt_if_updater_ent[i]);
113 }
114
115 static int
116 ipmgmt_if_groupmembers_updater(nvlist_t *db_nvl, nvpair_t *member_nvp,
117 uint_t flags)
118 {
119 char **members;
120 char *member;
121 char *out_members[256];
122 uint_t nelem = 0, cnt = 0;
123 int err;
124
125 if ((err = nvpair_value_string(member_nvp, &member)) != 0)
126 return (err);
127
128 err = nvlist_lookup_string_array(db_nvl, IPADM_NVP_MIFNAMES,
129 &members, &nelem);
130
131 if (err != 0 && (flags & IPMGMT_REMOVE))
132 return (ENOENT);
133
134 while (nelem-- > 0) {
135 if ((flags & IPMGMT_REMOVE) &&
136 (strcmp(member, members[nelem]) == 0))
137 continue;
138
139 if ((out_members[cnt] = strdup(members[nelem])) == NULL) {
140 err = ENOMEM;
141 goto fail;
142 }
143
144 cnt++;
145 }
146
147 if (flags & IPMGMT_APPEND) {
148 if ((out_members[cnt] = strdup(member)) == NULL) {
149 err = ENOMEM;
150 goto fail;
151 }
152 cnt++;
153 }
154
155 if (cnt == 0) {
156 err = nvlist_remove(db_nvl, IPADM_NVP_MIFNAMES,
157 DATA_TYPE_STRING_ARRAY);
158 } else {
159 err = nvlist_add_string_array(db_nvl, IPADM_NVP_MIFNAMES,
160 out_members, cnt);
161 }
162
163 fail:
164 while (cnt--)
165 free(out_members[cnt]);
166
167 return (err);
168 }
169
170 static int
171 ipmgmt_if_family_updater(nvlist_t *db_nvl, nvpair_t *families_nvp, uint_t flags)
172 {
173 uint16_t *families;
174 uint_t nelem = 0;
175 int err;
176
177 if ((err = nvpair_value_uint16_array(families_nvp, &families,
178 &nelem)) != 0)
179 return (err);
180
181 return (ipmgmt_update_family_nvp(db_nvl, families[0], flags));
182 }
183
184 int
185 ipmgmt_update_family_nvp(nvlist_t *nvl, sa_family_t af, uint_t flags)
186 {
187 uint16_t *families = NULL;
188 uint16_t out_families[2];
189 uint_t nelem = 0, cnt;
190 int err;
191
192 err = nvlist_lookup_uint16_array(nvl, IPADM_NVP_FAMILIES,
193 &families, &nelem);
194 if (err != 0 && (flags & IPMGMT_REMOVE)) {
195 return (ENOENT);
196 }
197
198 if (flags & IPMGMT_APPEND) {
199 if (families != NULL) {
200 if (nelem == 2 || families[0] == af) {
201 return (EEXIST);
202 }
203 out_families[0] = families[0];
204 out_families[1] = af;
205 cnt = 2;
206 } else {
207 out_families[0] = af;
208 cnt = 1;
209 }
210 } else {
211 assert(nelem == 1 || nelem == 2);
212 cnt = 0;
213 while (nelem-- > 0) {
214 if (families[nelem] != af) {
215 out_families[cnt] = families[nelem];
216 cnt++;
217 }
218 }
219 }
220
221 if (cnt != 0) {
222 return (nvlist_add_uint16_array(nvl, IPADM_NVP_FAMILIES,
223 out_families, cnt));
224 }
225 return (nvlist_remove(nvl, IPADM_NVP_FAMILIES, DATA_TYPE_UINT16_ARRAY));
226 }
227
228 /*
229 * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
230 * in private nvpairs `proto', `ifname' & `aobjname'.
231 */
232 static boolean_t
233 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
234 const char *aobjname)
235 {
236 char *db_proto = NULL, *db_ifname = NULL;
237 char *db_aobjname = NULL;
238 nvpair_t *nvp;
239 char *name;
240
241 /* walk through db_nvl and retrieve all its private nvpairs */
242 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
243 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
244 name = nvpair_name(nvp);
245 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
246 (void) nvpair_value_string(nvp, &db_proto);
247 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
248 (void) nvpair_value_string(nvp, &db_ifname);
249 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
250 (void) nvpair_value_string(nvp, &db_aobjname);
251 }
252
253 if (proto != NULL && proto[0] == '\0')
254 proto = NULL;
255 if (ifname != NULL && ifname[0] == '\0')
256 ifname = NULL;
257 if (aobjname != NULL && aobjname[0] == '\0')
258 aobjname = NULL;
259
260 if ((proto == NULL && db_proto != NULL) ||
261 (proto != NULL && db_proto == NULL) ||
262 (proto != NULL && db_proto != NULL &&
263 strcmp(proto, db_proto) != 0)) {
264 /* no intersection - different protocols. */
265 return (B_FALSE);
266 }
267 if ((ifname == NULL && db_ifname != NULL) ||
268 (ifname != NULL && db_ifname == NULL) ||
269 (ifname != NULL && db_ifname != NULL &&
270 strcmp(ifname, db_ifname) != 0)) {
271 /* no intersection - different interfaces. */
272 return (B_FALSE);
273 }
274 if ((aobjname == NULL && db_aobjname != NULL) ||
275 (aobjname != NULL && db_aobjname == NULL) ||
276 (aobjname != NULL && db_aobjname != NULL &&
277 strcmp(aobjname, db_aobjname) != 0)) {
278 /* no intersection - different address objects */
279 return (B_FALSE);
280 }
281
282 return (B_TRUE);
283 }
284
285 /*
286 * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
287 */
288 static boolean_t
289 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
290 {
291 nvpair_t *nvp;
292 char *name;
293 char *proto = NULL, *ifname = NULL, *aobjname = NULL;
294
295 /* walk through in_nvl and retrieve all its private nvpairs */
296 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
297 nvp = nvlist_next_nvpair(in_nvl, nvp)) {
298 name = nvpair_name(nvp);
299 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
300 (void) nvpair_value_string(nvp, &proto);
301 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
302 (void) nvpair_value_string(nvp, &ifname);
303 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
304 (void) nvpair_value_string(nvp, &aobjname);
305 }
306
307 return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
308 }
309
310 /*
311 * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
312 * in private nvpairs `proto', `ifname' & `aobjname'.
313 */
314 static boolean_t
315 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
316 const char *ifname, char *aobjname)
317 {
318 char *db_ifname = NULL, *db_proto = NULL;
319 char *db_aobjname = NULL;
320 nvpair_t *nvp;
321 char *name;
322
323 /* walk through db_nvl and retrieve all private nvpairs */
324 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
325 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
326 name = nvpair_name(nvp);
327 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
328 (void) nvpair_value_string(nvp, &db_proto);
329 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
330 (void) nvpair_value_string(nvp, &db_ifname);
331 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
332 (void) nvpair_value_string(nvp, &db_aobjname);
333 }
334
335 if (proto != NULL && proto[0] != '\0') {
336 if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
337 return (B_FALSE);
338 }
339 if (ifname != NULL && ifname[0] != '\0') {
340 if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
341 return (B_FALSE);
342 }
343 if (aobjname != NULL && aobjname[0] != '\0') {
344 if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
345 return (B_FALSE);
346 }
347
348 return (B_TRUE);
349 }
350
351 /*
352 * Retrieves the property value from the DB. The property whose value is to be
353 * retrieved is in `pargp->ia_pname'.
354 */
355 /* ARGSUSED */
356 boolean_t
357 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
358 int *errp)
359 {
360 ipmgmt_prop_arg_t *pargp = arg;
361 boolean_t cont = B_TRUE;
362 char *pval;
363 int err = 0;
364
365 *errp = 0;
366
367 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
368 pargp->ia_ifname, pargp->ia_aobjname))
369 return (B_TRUE);
370
371 if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
372 &pval)) == 0) {
373 (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
374 /*
375 * We have retrieved what we are looking for.
376 * Stop the walker.
377 */
378 cont = B_FALSE;
379 } else {
380 if (err == ENOENT)
381 err = 0;
382 *errp = err;
383 }
384
385 return (cont);
386 }
387
388 /*
389 * Removes the property value from the DB. The property whose value is to be
390 * removed is in `pargp->ia_pname'.
391 */
392 /* ARGSUSED */
393 boolean_t
394 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
395 int *errp)
396 {
397 ipmgmt_prop_arg_t *pargp = arg;
398
399 *errp = 0;
400 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
401 pargp->ia_ifname, pargp->ia_aobjname))
402 return (B_TRUE);
403
404 if (!nvlist_exists(db_nvl, pargp->ia_pname))
405 return (B_TRUE);
406
407 /*
408 * We found the property in the DB. If IPMGMT_REMOVE is not set then
409 * delete the entry from the db. If it is set, then the property is a
410 * multi-valued property so just remove the specified values from DB.
411 */
412 if (pargp->ia_flags & IPMGMT_REMOVE) {
413 char *dbpval = NULL;
414 char *inpval = pargp->ia_pval;
415 char pval[MAXPROPVALLEN];
416 char *val, *lasts;
417
418 *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
419 if (*errp != 0)
420 return (B_FALSE);
421
422 /*
423 * multi-valued properties are represented as comma separated
424 * values. Use string tokenizer functions to split them and
425 * search for the value to be removed.
426 */
427 bzero(pval, sizeof (pval));
428 if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
429 if (strcmp(val, inpval) != 0)
430 (void) strlcat(pval, val, MAXPROPVALLEN);
431 while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
432 if (strcmp(val, inpval) != 0) {
433 if (pval[0] != '\0')
434 (void) strlcat(pval, ",",
435 MAXPROPVALLEN);
436 (void) strlcat(pval, val,
437 MAXPROPVALLEN);
438 }
439 }
440 } else {
441 if (strcmp(dbpval, inpval) != 0)
442 *errp = ENOENT;
443 else
444 buf[0] = '\0';
445 return (B_FALSE);
446 }
447 *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
448 if (*errp != 0)
449 return (B_FALSE);
450
451 (void) memset(buf, 0, buflen);
452 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
453 /* buffer overflow */
454 *errp = ENOBUFS;
455 }
456 } else {
457 buf[0] = '\0';
458 }
459
460 /* stop the search */
461 return (B_FALSE);
462 }
463
464 /*
465 * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
466 * found, when one of the following occurs first.
467 * - the input aobjname matches the db aobjname. Return the db address.
468 * - the input interface matches the db interface. Return all the
469 * matching db lines with addresses.
470 */
471 /* ARGSUSED */
472 boolean_t
473 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
474 int *errp)
475 {
476 ipmgmt_get_cbarg_t *cbarg = arg;
477 char *db_aobjname = NULL;
478 char *db_ifname = NULL;
479 nvlist_t *db_addr = NULL;
480 char name[IPMGMT_STRSIZE];
481 nvpair_t *nvp;
482 boolean_t add_nvl = B_FALSE;
483
484 /* Parse db nvlist */
485 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
486 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
487 if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
488 (void) nvpair_value_nvlist(nvp, &db_addr);
489 else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
490 (void) nvpair_value_string(nvp, &db_ifname);
491 else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
492 (void) nvpair_value_string(nvp, &db_aobjname);
493 }
494
495 if (db_aobjname == NULL) /* Not an address */
496 return (B_TRUE);
497
498 /* Check for a match between the aobjnames or the interface name */
499 if (cbarg->cb_aobjname[0] != '\0') {
500 if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
501 add_nvl = B_TRUE;
502 } else if (cbarg->cb_ifname[0] != '\0') {
503 if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
504 add_nvl = B_TRUE;
505 } else {
506 add_nvl = B_TRUE;
507 }
508
509 if (add_nvl) {
510 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
511 cbarg->cb_ocnt);
512 *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
513 if (*errp == 0)
514 cbarg->cb_ocnt++;
515 }
516 return (B_TRUE);
517 }
518
519 /*
520 * This function only gets called if a volatile filesystem version
521 * of the configuration file has been created. This only happens in the
522 * extremely rare case that a request has been made to update the configuration
523 * file at boottime while the root filesystem was read-only. This is
524 * really a rare occurrence now that we don't support UFS root filesystems
525 * any longer. This function will periodically attempt to write the
526 * configuration back to its location on the root filesystem. Success
527 * will indicate that the filesystem is no longer read-only.
528 */
529 /* ARGSUSED */
530 static void *
531 ipmgmt_db_restore_thread(void *arg)
532 {
533 int err;
534
535 for (;;) {
536 (void) sleep(5);
537 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
538 if (!ipmgmt_rdonly_root)
539 break;
540 err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE);
541 if (err == 0) {
542 ipmgmt_rdonly_root = B_FALSE;
543 break;
544 }
545 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
546 }
547 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
548 return (NULL);
549 }
550
551 /*
552 * This function takes the appropriate lock, read or write, based on the
553 * `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated
554 * by the fact that we are not always guaranteed to have a writable root
555 * filesystem since it is possible that we are reading or writing during
556 * bootime while the root filesystem is still read-only. This is, by far,
557 * the exception case. Normally, this function will be called when the
558 * root filesystem is writable. In the unusual case where this is not
559 * true, the configuration file is copied to the volatile file system
560 * and is updated there until the root filesystem becomes writable. At
561 * that time the file will be moved back to its proper location by
562 * ipmgmt_db_restore_thread().
563 */
564 extern int
565 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
566 {
567 int err;
568 boolean_t writeop;
569 mode_t mode;
570 pthread_t tid;
571 pthread_attr_t attr;
572
573 writeop = (db_op != IPADM_DB_READ);
574 if (writeop) {
575 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
576 mode = IPADM_FILE_MODE;
577 } else {
578 (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
579 mode = 0;
580 }
581
582 /*
583 * Did a previous write attempt fail? If so, don't even try to
584 * read/write to IPADM_DB_FILE.
585 */
586 if (!ipmgmt_rdonly_root) {
587 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE,
588 mode, db_op);
589 if (err != EROFS)
590 goto done;
591 }
592
593 /*
594 * If we haven't already copied the file to the volatile
595 * file system, do so. This should only happen on a failed
596 * writeop(i.e., we have acquired the write lock above).
597 */
598 if (access(IPADM_VOL_DB_FILE, F_OK) != 0) {
599 assert(writeop);
600 err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE);
601 if (err != 0)
602 goto done;
603 (void) pthread_attr_init(&attr);
604 (void) pthread_attr_setdetachstate(&attr,
605 PTHREAD_CREATE_DETACHED);
606 err = pthread_create(&tid, &attr, ipmgmt_db_restore_thread,
607 NULL);
608 (void) pthread_attr_destroy(&attr);
609 if (err != 0) {
610 (void) unlink(IPADM_VOL_DB_FILE);
611 goto done;
612 }
613 ipmgmt_rdonly_root = B_TRUE;
614 }
615
616 /*
617 * Read/write from the volatile copy.
618 */
619 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE,
620 mode, db_op);
621 done:
622 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
623 return (err);
624 }
625
626 /*
627 * Used to add an entry towards the end of DB. It just returns B_TRUE for
628 * every line of the DB. When we reach the end, ipadm_rw_db() adds the
629 * line at the end.
630 */
631 /* ARGSUSED */
632 boolean_t
633 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
634 {
635 return (B_TRUE);
636 }
637
638 /*
639 * This function is used to update or create an entry in DB. The nvlist_t,
640 * `in_nvl', represents the line we are looking for. Once we ensure the right
641 * line from DB, we update that entry.
642 */
643 boolean_t
644 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
645 int *errp)
646 {
647 ipadm_dbwrite_cbarg_t *cb = arg;
648 uint_t flags = cb->dbw_flags;
649 nvlist_t *in_nvl = cb->dbw_nvl;
650 nvpair_t *nvp;
651 char *name, *instrval = NULL, *dbstrval = NULL;
652 char pval[MAXPROPVALLEN];
653
654 *errp = 0;
655 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
656 return (B_TRUE);
657
658 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
659 nvp = nvlist_next_nvpair(in_nvl, nvp)) {
660 name = nvpair_name(nvp);
661 if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
662 break;
663 }
664
665 if (nvp == NULL)
666 return (B_TRUE);
667
668 assert(nvpair_type(nvp) == DATA_TYPE_STRING);
669
670 if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
671 return (B_FALSE);
672
673 /*
674 * If IPMGMT_APPEND is set then we are dealing with multi-valued
675 * properties. We append to the entry from the db, with the new value.
676 */
677 if (flags & IPMGMT_APPEND) {
678 if ((*errp = nvlist_lookup_string(db_nvl, name,
679 &dbstrval)) != 0)
680 return (B_FALSE);
681 (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
682 instrval);
683 if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
684 return (B_FALSE);
685 } else {
686 /* case of in-line update of a db entry */
687 if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
688 return (B_FALSE);
689 }
690
691 (void) memset(buf, 0, buflen);
692 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
693 /* buffer overflow */
694 *errp = ENOBUFS;
695 }
696
697 /* we updated the DB entry, so do not continue */
698 return (B_FALSE);
699 }
700
701 /*
702 * This function is used to update a DB line that describes
703 * an interface, its family and group interface
704 *
705 */
706 boolean_t
707 ipmgmt_db_update_if(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
708 int *errp)
709 {
710 ipadm_dbwrite_cbarg_t *cb = arg;
711 ipmgmt_if_updater_ent_t *updater;
712 nvlist_t *in_nvl = cb->dbw_nvl;
713 uint_t flags = cb->dbw_flags;
714 nvpair_t *nvp;
715 char *name;
716 char *db_ifname;
717 char *gifname = NULL;
718 char *mifname = NULL;
719
720 *errp = 0;
721
722 /* Only one flag */
723 if ((flags & (IPMGMT_APPEND | IPMGMT_REMOVE)) == 0 ||
724 ((flags & IPMGMT_APPEND) && (flags & IPMGMT_REMOVE))) {
725 *errp = EINVAL;
726 return (B_FALSE);
727 }
728
729 if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES))
730 return (B_TRUE);
731
732 if (nvlist_exists(db_nvl, IPADM_NVP_IFCLASS) &&
733 nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
734 nvlist_lookup_string(in_nvl, IPADM_NVP_GIFNAME, &gifname) == 0 &&
735 nvlist_lookup_string(in_nvl, IPADM_NVP_MIFNAMES, &mifname) == 0 &&
736 strcmp(db_ifname, mifname) == 0) {
737 if (flags & IPMGMT_APPEND) {
738 if ((*errp = nvlist_add_string(db_nvl,
739 IPADM_NVP_GIFNAME, gifname)) != 0)
740 return (B_FALSE);
741 } else {
742 if ((*errp = nvlist_remove(db_nvl, IPADM_NVP_GIFNAME,
743 DATA_TYPE_STRING)) != 0)
744 return (B_FALSE);
745 }
746 cb->dbw_flags &= ~IPMGMT_UPDATE_IPMP;
747 goto done;
748 }
749
750 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
751 return (B_TRUE);
752
753 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
754 nvp = nvlist_next_nvpair(in_nvl, nvp)) {
755 name = nvpair_name(nvp);
756 if (strcmp(name, IPADM_NVP_FAMILIES) != 0 &&
757 strcmp(name, IPADM_NVP_MIFNAMES) != 0)
758 continue;
759
760 updater = ipmgmt_find_if_field_updater(name);
761 assert(updater != NULL);
762 *errp = (*updater->func)(db_nvl, nvp, flags);
763 if (*errp != 0)
764 return (B_FALSE);
765 }
766
767 cb->dbw_flags &= ~IPMGMT_UPDATE_IF;
768
769 done:
770 (void) memset(buf, 0, buflen);
771 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
772 *errp = ENOBUFS;
773 return (B_FALSE);
774 }
775
776 /* we finished all operations, so do not continue */
777 if ((cb->dbw_flags & (IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP)) == 0)
778 return (B_FALSE);
779
780 return (B_TRUE);
781 }
782
783 /*
784 * For the given `cbarg->cb_ifname' interface retrieves
785 * the nvlist that represents the persistent interface information
786 * The nvlist contains:
787 * IPADM_NVP_IFNAME
788 * IPADM_NVP_FAMILIES
789 * IPADM_NVP_IF_CLASS
790 *
791 * (used in 'ipadm show-if')
792 */
793 /* ARGSUSED */
794 boolean_t
795 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
796 int *errp)
797 {
798 ipmgmt_get_cbarg_t *cbarg = arg;
799 char *ifname = cbarg->cb_ifname;
800 nvpair_t *nvp;
801 char *db_ifname = NULL;
802 uint16_t *db_families = NULL;
803 uint_t nelem = 0;
804
805 /* Parse db nvlist */
806 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
807 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
808 if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) {
809 (void) nvpair_value_string(nvp, &db_ifname);
810 } else if (strcmp(nvpair_name(nvp), IPADM_NVP_FAMILIES) == 0) {
811 (void) nvpair_value_uint16_array(nvp,
812 &db_families, &nelem);
813 }
814 }
815
816 if (db_ifname == NULL || db_families == NULL)
817 return (B_TRUE);
818
819 if (ifname != NULL && ifname[0] != '\0' &&
820 strcmp(ifname, db_ifname) != 0)
821 return (B_TRUE);
822
823 *errp = nvlist_add_nvlist(cbarg->cb_onvl, db_ifname, db_nvl);
824 if (*errp == 0)
825 cbarg->cb_ocnt++;
826
827 if (ifname != NULL && ifname[0] != '\0')
828 return (B_FALSE);
829
830 return (B_TRUE);
831 }
832
833 /*
834 * Deletes those entries from the database for which interface name
835 * matches with the given `cbarg->cb_ifname'
836 */
837 /* ARGSUSED */
838 boolean_t
839 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
840 int *errp)
841 {
842 ipmgmt_if_cbarg_t *cbarg = arg;
843 boolean_t isv6 = (cbarg->cb_family == AF_INET6);
844 char *ifname = cbarg->cb_ifname;
845 char *modstr = NULL;
846 char *aobjname;
847 uint_t proto;
848 ipmgmt_aobjmap_t *head;
849 boolean_t aobjfound = B_FALSE;
850
851 *errp = 0;
852
853 if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
854 return (B_TRUE);
855
856 if (nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
857
858 if ((*errp = ipmgmt_update_family_nvp(db_nvl, cbarg->cb_family,
859 IPMGMT_REMOVE)) != 0) {
860 return (B_FALSE);
861 }
862
863 if (cbarg->cb_family == AF_INET) {
864 cbarg->cb_ipv4exists = B_FALSE;
865 } else {
866 assert(cbarg->cb_family == AF_INET6);
867 cbarg->cb_ipv6exists = B_FALSE;
868 }
869 if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
870 cbarg->cb_ipv4exists = B_FALSE;
871 cbarg->cb_ipv6exists = B_FALSE;
872 goto delete;
873 }
874 /* Otherwise need to reconstruct this string */
875 (void) memset(buf, 0, buflen);
876 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
877 /* buffer overflow */
878 *errp = ENOBUFS;
879 return (B_FALSE);
880 }
881 return (B_TRUE);
882 }
883
884 /* Reset all the interface configurations for 'ifname' */
885 if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
886 nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
887 goto delete;
888 }
889 if (!isv6 &&
890 (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
891 nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
892 goto delete;
893 }
894
895 if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
896 /*
897 * This must be an address property. Delete this
898 * line if there is a match in the address family.
899 */
900 head = aobjmap.aobjmap_head;
901 while (head != NULL) {
902 if (strcmp(head->am_aobjname, aobjname) == 0) {
903 aobjfound = B_TRUE;
904 if (head->am_family == cbarg->cb_family)
905 goto delete;
906 }
907 head = head->am_next;
908 }
909 /*
910 * If aobjfound = B_FALSE, then this address is not
911 * available in active configuration. We should go ahead
912 * and delete it.
913 */
914 if (!aobjfound)
915 goto delete;
916 }
917
918 /*
919 * If we are removing both v4 and v6 interface, then we get rid of
920 * all the properties for that interface. On the other hand, if we
921 * are deleting only v4 instance of an interface, then we delete v4
922 * properties only.
923 */
924 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
925 proto = ipadm_str2proto(modstr);
926 switch (proto) {
927 case MOD_PROTO_IPV6:
928 if (isv6)
929 goto delete;
930 break;
931 case MOD_PROTO_IPV4:
932 if (!isv6)
933 goto delete;
934 break;
935 case MOD_PROTO_IP:
936 if (!cbarg->cb_ipv4exists && !cbarg->cb_ipv6exists)
937 goto delete;
938 break;
939 }
940 }
941 /* Not found a match yet. Continue processing the db */
942 return (B_TRUE);
943 delete:
944 /* delete the line from the db */
945 buf[0] = '\0';
946 return (B_TRUE);
947 }
948
949 /*
950 * Deletes those entries from the database for which address object name
951 * matches with the given `cbarg->cb_aobjname'
952 */
953 /* ARGSUSED */
954 boolean_t
955 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
956 int *errp)
957 {
958 ipmgmt_resetaddr_cbarg_t *cbarg = arg;
959 char *aobjname = cbarg->cb_aobjname;
960
961 *errp = 0;
962 if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
963 return (B_TRUE);
964
965 /* delete the line from the db */
966 buf[0] = '\0';
967 return (B_TRUE);
968 }
969
970 /*
971 * Retrieves all interface props, including addresses, for given interface(s).
972 * `invl' contains the list of interfaces, for which information need to be
973 * retrieved.
974 */
975 /* ARGSUSED */
976 boolean_t
977 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
978 int *errp)
979 {
980 ipmgmt_initif_cbarg_t *cbarg = arg;
981 nvlist_t *onvl = cbarg->cb_onvl;
982 nvlist_t *invl = cbarg->cb_invl;
983 sa_family_t in_af = cbarg->cb_family;
984 char *db_ifname;
985
986 *errp = 0;
987 if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
988 nvlist_exists(invl, db_ifname)) {
989 char name[IPMGMT_STRSIZE];
990 sa_family_t db_af = in_af;
991 uint_t proto;
992 char *pstr;
993
994 if (in_af != AF_UNSPEC) {
995 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
996 &pstr) == 0) {
997 proto = ipadm_str2proto(pstr);
998 if (proto == MOD_PROTO_IPV4)
999 db_af = AF_INET;
1000 else if (proto == MOD_PROTO_IPV6)
1001 db_af = AF_INET6;
1002 else
1003 db_af = in_af;
1004 } else {
1005 if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
1006 nvlist_exists(db_nvl, IPADM_NVP_DHCP))
1007 db_af = AF_INET;
1008 else
1009 db_af = AF_INET6;
1010 }
1011 }
1012 if (in_af == db_af) {
1013 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
1014 cbarg->cb_ocnt);
1015 *errp = nvlist_add_nvlist(onvl, name, db_nvl);
1016 if (*errp == 0)
1017 cbarg->cb_ocnt++;
1018 }
1019 }
1020 return (B_TRUE);
1021 }
1022
1023 /*
1024 * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
1025 * into `aobjmap' structure.
1026 */
1027 static int
1028 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
1029 {
1030 ipmgmt_aobjmap_t *new, *head;
1031
1032 head = aobjmap.aobjmap_head;
1033 if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
1034 return (ENOMEM);
1035 *new = *nodep;
1036 new->am_next = NULL;
1037
1038 /* Add the node at the beginning of the list */
1039 if (head == NULL) {
1040 aobjmap.aobjmap_head = new;
1041 } else {
1042 new->am_next = aobjmap.aobjmap_head;
1043 aobjmap.aobjmap_head = new;
1044 }
1045 return (0);
1046 }
1047
1048 /*
1049 * A recursive function to generate alphabetized number given a decimal number.
1050 * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
1051 * 'ab', 'ac', et al.
1052 */
1053 static void
1054 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
1055 {
1056 if (num >= 26)
1057 i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
1058 if (*cp != endp) {
1059 *cp[0] = 'a' + (num % 26);
1060 (*cp)++;
1061 }
1062 }
1063
1064 /*
1065 * This function generates an `aobjname', when required, and then does
1066 * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
1067 * through the `aobjmap' to check if an address object with the same
1068 * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
1069 * `aobjname's are not allowed.
1070 *
1071 * If `nodep->am_aobjname' is an empty string then the daemon generates an
1072 * `aobjname' using the `am_nextnum', which contains the next number to be
1073 * used to generate `aobjname'. `am_nextnum' is converted to base26 using
1074 * `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
1075 *
1076 * `am_nextnum' will be 0 to begin with. Every time an address object that
1077 * needs `aobjname' is added it's incremented by 1. So for the first address
1078 * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
1079 * For the second address object on that interface `am_aobjname' will be net0/_b
1080 * and `am_nextnum' will incremented to 2.
1081 */
1082 static int
1083 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
1084 {
1085 ipmgmt_aobjmap_t *head;
1086 uint32_t nextnum;
1087
1088 for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
1089 if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
1090 break;
1091 nextnum = (head == NULL ? 0 : head->am_nextnum);
1092
1093 /*
1094 * if `aobjname' is empty, then the daemon has to generate the
1095 * next `aobjname' for the given interface and family.
1096 */
1097 if (nodep->am_aobjname[0] == '\0') {
1098 char tmpstr[IPADM_AOBJ_USTRSIZ - 1]; /* 1 for leading '_' */
1099 char *cp = tmpstr;
1100 char *endp = tmpstr + sizeof (tmpstr);
1101
1102 i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
1103
1104 if (cp == endp)
1105 return (EINVAL);
1106 cp[0] = '\0';
1107
1108 if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
1109 nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
1110 return (EINVAL);
1111 }
1112 nodep->am_nextnum = ++nextnum;
1113 } else {
1114 for (head = aobjmap.aobjmap_head; head != NULL;
1115 head = head->am_next) {
1116 if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
1117 return (EEXIST);
1118 }
1119 nodep->am_nextnum = nextnum;
1120 }
1121 return (i_ipmgmt_add_amnode(nodep));
1122 }
1123
1124 /*
1125 * Performs following operations on the global `aobjmap' linked list.
1126 * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
1127 * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
1128 * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
1129 * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
1130 */
1131 int
1132 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
1133 {
1134 ipmgmt_aobjmap_t *head, *prev, *matched = NULL;
1135 boolean_t update = B_TRUE;
1136 int err = 0;
1137 ipadm_db_op_t db_op = IPADM_DB_READ;
1138
1139 (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
1140
1141 head = aobjmap.aobjmap_head;
1142 switch (op) {
1143 case ADDROBJ_ADD:
1144 /*
1145 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
1146 * update, else add the new node.
1147 */
1148 for (; head != NULL; head = head->am_next) {
1149 /*
1150 * For IPv6, we need to distinguish between the
1151 * linklocal and non-linklocal nodes
1152 */
1153 if (strcmp(head->am_aobjname,
1154 nodep->am_aobjname) == 0 &&
1155 (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
1156 head->ipmgmt_am_linklocal ==
1157 nodep->ipmgmt_am_linklocal))
1158 break;
1159 }
1160
1161 if (head != NULL) {
1162 /* update the node */
1163 (void) strlcpy(head->am_ifname, nodep->am_ifname,
1164 sizeof (head->am_ifname));
1165 head->am_lnum = nodep->am_lnum;
1166 head->am_family = nodep->am_family;
1167 head->am_flags = nodep->am_flags;
1168 head->am_atype = nodep->am_atype;
1169 head->am_atype_cache = nodep->am_atype_cache;
1170 } else {
1171 for (head = aobjmap.aobjmap_head; head != NULL;
1172 head = head->am_next) {
1173 if (strcmp(head->am_ifname,
1174 nodep->am_ifname) == 0)
1175 break;
1176 }
1177 nodep->am_nextnum = (head == NULL ? 0 :
1178 head->am_nextnum);
1179 err = i_ipmgmt_add_amnode(nodep);
1180 }
1181 db_op = IPADM_DB_WRITE;
1182 break;
1183 case ADDROBJ_DELETE:
1184 prev = head;
1185 while (head != NULL) {
1186 if (strcmp(head->am_aobjname,
1187 nodep->am_aobjname) == 0) {
1188 nodep->am_atype = head->am_atype;
1189 /*
1190 * There could be multiple IPV6_ADDRCONF nodes,
1191 * with same address object name, so check for
1192 * logical number also.
1193 */
1194 if (head->am_atype !=
1195 IPADM_ADDR_IPV6_ADDRCONF ||
1196 nodep->am_lnum == head->am_lnum)
1197 break;
1198 }
1199 prev = head;
1200 head = head->am_next;
1201 }
1202 if (head != NULL) {
1203 /*
1204 * If the address object is in both active and
1205 * persistent configuration and the user is deleting it
1206 * only from active configuration then mark this node
1207 * for deletion by reseting IPMGMT_ACTIVE bit.
1208 * With this the same address object name cannot
1209 * be reused until it is permanently removed.
1210 */
1211 if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
1212 nodep->am_flags == IPMGMT_ACTIVE) {
1213 /* Update flags in the in-memory map. */
1214 head->am_flags &= ~IPMGMT_ACTIVE;
1215 head->am_lnum = -1;
1216
1217 /* Update info in file. */
1218 db_op = IPADM_DB_WRITE;
1219 *nodep = *head;
1220 } else {
1221 (void) strlcpy(nodep->am_ifname,
1222 head->am_ifname,
1223 sizeof (nodep->am_ifname));
1224 /* otherwise delete the node */
1225 if (head == aobjmap.aobjmap_head)
1226 aobjmap.aobjmap_head = head->am_next;
1227 else
1228 prev->am_next = head->am_next;
1229 free(head);
1230 db_op = IPADM_DB_DELETE;
1231 }
1232 } else {
1233 err = ENOENT;
1234 }
1235 break;
1236 case ADDROBJ_LOOKUPADD:
1237 err = i_ipmgmt_lookupadd_amnode(nodep);
1238 update = B_FALSE;
1239 break;
1240 case ADDROBJ_SETLIFNUM:
1241 update = B_FALSE;
1242 for (; head != NULL; head = head->am_next) {
1243 if (strcmp(head->am_ifname,
1244 nodep->am_ifname) == 0 &&
1245 head->am_family == nodep->am_family &&
1246 head->am_lnum == nodep->am_lnum) {
1247 err = EEXIST;
1248 break;
1249 }
1250 if (strcmp(head->am_aobjname,
1251 nodep->am_aobjname) == 0) {
1252 matched = head;
1253 }
1254 }
1255 if (err == EEXIST)
1256 break;
1257 if (matched != NULL) {
1258 /* update the lifnum */
1259 matched->am_lnum = nodep->am_lnum;
1260 } else {
1261 err = ENOENT;
1262 }
1263 break;
1264 default:
1265 assert(0);
1266 }
1267
1268 if (err == 0 && update)
1269 err = ipmgmt_persist_aobjmap(nodep, db_op);
1270
1271 (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
1272
1273 return (err);
1274 }
1275
1276 /*
1277 * Given a node in `aobjmap', this function converts it into nvlist_t structure.
1278 * The content to be written to DB must be represented as nvlist_t.
1279 */
1280 static int
1281 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
1282 {
1283 int err;
1284 char strval[IPMGMT_STRSIZE];
1285
1286 *nvl = NULL;
1287 if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
1288 goto fail;
1289
1290 if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
1291 np->am_aobjname)) != 0)
1292 goto fail;
1293
1294 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
1295 np->am_ifname)) != 0)
1296 goto fail;
1297
1298 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
1299 if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
1300 goto fail;
1301
1302 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
1303 if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
1304 goto fail;
1305
1306 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
1307 if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
1308 goto fail;
1309
1310 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
1311 if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
1312 goto fail;
1313
1314 switch (np->am_atype) {
1315 case IPADM_ADDR_IPV6_ADDRCONF: {
1316 struct sockaddr_in6 *in6;
1317
1318 in6 = &np->ipmgmt_am_ifid;
1319 if (np->ipmgmt_am_linklocal &&
1320 IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
1321 if ((err = nvlist_add_string(*nvl,
1322 IPADM_NVP_IPNUMADDR, "default")) != 0) {
1323 goto fail;
1324 }
1325 } else {
1326 if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
1327 IPMGMT_STRSIZE) == NULL) {
1328 err = errno;
1329 goto fail;
1330 }
1331 if ((err = nvlist_add_string(*nvl,
1332 IPADM_NVP_IPNUMADDR, strval)) != 0) {
1333 goto fail;
1334 }
1335 }
1336 }
1337 break;
1338 case IPADM_ADDR_DHCP: {
1339 if (np->ipmgmt_am_reqhost &&
1340 *np->ipmgmt_am_reqhost != '\0' &&
1341 (err = nvlist_add_string(*nvl, IPADM_NVP_REQHOST,
1342 np->ipmgmt_am_reqhost)) != 0)
1343 goto fail;
1344 }
1345 /* FALLTHRU */
1346 default:
1347 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1348 "")) != 0)
1349 goto fail;
1350 break;
1351 }
1352 return (err);
1353 fail:
1354 nvlist_free(*nvl);
1355 return (err);
1356 }
1357
1358 /*
1359 * Read the aobjmap data store and build the in-memory representation
1360 * of the aobjmap. We don't need to hold any locks while building this as
1361 * we do this in very early stage of daemon coming up, even before the door
1362 * is opened.
1363 */
1364 /* ARGSUSED */
1365 extern boolean_t
1366 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1367 int *errp)
1368 {
1369 nvpair_t *nvp = NULL;
1370 char *name, *strval = NULL;
1371 ipmgmt_aobjmap_t node;
1372 struct sockaddr_in6 *in6;
1373
1374 *errp = 0;
1375 node.am_next = NULL;
1376 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1377 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1378 name = nvpair_name(nvp);
1379
1380 if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
1381 return (B_TRUE);
1382 if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
1383 (void) strlcpy(node.am_aobjname, strval,
1384 sizeof (node.am_aobjname));
1385 } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
1386 (void) strlcpy(node.am_ifname, strval,
1387 sizeof (node.am_ifname));
1388 } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
1389 node.am_lnum = atoi(strval);
1390 } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
1391 node.am_family = (sa_family_t)atoi(strval);
1392 } else if (strcmp(FLAGS, name) == 0) {
1393 node.am_flags = atoi(strval);
1394 } else if (strcmp(ATYPE, name) == 0) {
1395 node.am_atype = (ipadm_addr_type_t)atoi(strval);
1396 } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
1397 if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1398 in6 = &node.ipmgmt_am_ifid;
1399 if (strcmp(strval, "default") == 0) {
1400 bzero(in6,
1401 sizeof (node.ipmgmt_am_ifid));
1402 node.ipmgmt_am_linklocal = B_TRUE;
1403 } else {
1404 (void) inet_pton(AF_INET6, strval,
1405 &in6->sin6_addr);
1406 if (IN6_IS_ADDR_UNSPECIFIED(
1407 &in6->sin6_addr))
1408 node.ipmgmt_am_linklocal =
1409 B_TRUE;
1410 }
1411 }
1412 }
1413 }
1414
1415 /* we have all the information we need, add the node */
1416 *errp = i_ipmgmt_add_amnode(&node);
1417
1418 return (B_TRUE);
1419 }
1420
1421 /*
1422 * Updates an entry from the temporary cache file, which matches the given
1423 * address object name.
1424 */
1425 /* ARGSUSED */
1426 static boolean_t
1427 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1428 size_t buflen, int *errp)
1429 {
1430 ipadm_dbwrite_cbarg_t *cb = arg;
1431 nvlist_t *in_nvl = cb->dbw_nvl;
1432 uint32_t flags = cb->dbw_flags;
1433 char *db_lifnumstr = NULL, *in_lifnumstr = NULL;
1434
1435 *errp = 0;
1436 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
1437 return (B_TRUE);
1438
1439 if (flags & IPMGMT_ATYPE_V6ACONF) {
1440 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1441 &db_lifnumstr) != 0 ||
1442 nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
1443 &in_lifnumstr) != 0 ||
1444 (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
1445 strcmp(db_lifnumstr, in_lifnumstr) != 0))
1446 return (B_TRUE);
1447 }
1448
1449 /* we found the match */
1450 (void) memset(buf, 0, buflen);
1451 if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
1452 /* buffer overflow */
1453 *errp = ENOBUFS;
1454 }
1455
1456 /* stop the walker */
1457 return (B_FALSE);
1458 }
1459
1460 /*
1461 * Deletes an entry from the temporary cache file, which matches the given
1462 * address object name.
1463 */
1464 /* ARGSUSED */
1465 static boolean_t
1466 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1467 size_t buflen, int *errp)
1468 {
1469 ipmgmt_aobjmap_t *nodep = arg;
1470 char *db_lifnumstr = NULL;
1471
1472 *errp = 0;
1473 if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
1474 nodep->am_aobjname))
1475 return (B_TRUE);
1476
1477 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1478 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1479 &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
1480 return (B_TRUE);
1481 }
1482
1483 /* we found the match, delete the line from the db */
1484 buf[0] = '\0';
1485
1486 /* stop the walker */
1487 return (B_FALSE);
1488 }
1489
1490 /*
1491 * Adds or deletes aobjmap node information into a temporary cache file.
1492 */
1493 extern int
1494 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
1495 {
1496 int err;
1497 ipadm_dbwrite_cbarg_t cb;
1498 nvlist_t *nvl = NULL;
1499
1500 if (op == IPADM_DB_WRITE) {
1501 if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
1502 return (err);
1503 cb.dbw_nvl = nvl;
1504 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
1505 cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
1506 else
1507 cb.dbw_flags = 0;
1508
1509 err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
1510 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
1511 nvlist_free(nvl);
1512 } else {
1513 assert(op == IPADM_DB_DELETE);
1514
1515 err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
1516 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
1517 }
1518 return (err);
1519 }
1520
1521 /*
1522 * upgrades the ipadm data-store. It renames all the old private protocol
1523 * property names which start with leading protocol names to begin with
1524 * IPADM_PRIV_PROP_PREFIX.
1525 */
1526 /* ARGSUSED */
1527 boolean_t
1528 ipmgmt_db_upgrade(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1529 int *errp)
1530 {
1531 nvpair_t *nvp;
1532 char *name, *pname = NULL, *protostr = NULL, *pval = NULL;
1533 uint_t proto, nproto;
1534 char nname[IPMGMT_STRSIZE], tmpstr[IPMGMT_STRSIZE];
1535
1536 *errp = 0;
1537 /*
1538 * We are interested in lines which contain protocol properties. We
1539 * walk through other lines in the DB.
1540 */
1541 if (nvlist_exists(db_nvl, IPADM_NVP_IFNAME) ||
1542 nvlist_exists(db_nvl, IPADM_NVP_AOBJNAME)) {
1543 return (B_TRUE);
1544 }
1545 assert(nvlist_exists(db_nvl, IPADM_NVP_PROTONAME));
1546
1547 /*
1548 * extract the propname from the `db_nvl' and also extract the
1549 * protocol from the `db_nvl'.
1550 */
1551 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1552 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1553 name = nvpair_name(nvp);
1554 if (strcmp(name, IPADM_NVP_PROTONAME) == 0) {
1555 if (nvpair_value_string(nvp, &protostr) != 0)
1556 return (B_TRUE);
1557 } else {
1558 assert(!IPADM_PRIV_NVP(name));
1559 pname = name;
1560 if (nvpair_value_string(nvp, &pval) != 0)
1561 return (B_TRUE);
1562 }
1563 }
1564
1565 /* if the private property is in the right format return */
1566 if (strncmp(pname, IPADM_PERSIST_PRIVPROP_PREFIX,
1567 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1568 return (B_TRUE);
1569 }
1570 /* if it's a public property move onto the next property */
1571 nproto = proto = ipadm_str2proto(protostr);
1572 if (ipadm_legacy2new_propname(pname, nname, sizeof (nname),
1573 &nproto) != 0) {
1574 return (B_TRUE);
1575 }
1576
1577 /* replace the old protocol with new protocol, if required */
1578 if (nproto != proto) {
1579 protostr = ipadm_proto2str(nproto);
1580 if (nvlist_add_string(db_nvl, IPADM_NVP_PROTONAME,
1581 protostr) != 0) {
1582 return (B_TRUE);
1583 }
1584 }
1585
1586 /* replace the old property name with new property name, if required */
1587 /* add the prefix to property name */
1588 (void) snprintf(tmpstr, sizeof (tmpstr), "_%s", nname);
1589 if (nvlist_add_string(db_nvl, tmpstr, pval) != 0 ||
1590 nvlist_remove(db_nvl, pname, DATA_TYPE_STRING) != 0) {
1591 return (B_TRUE);
1592 }
1593 (void) memset(buf, 0, buflen);
1594 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
1595 /* buffer overflow */
1596 *errp = ENOBUFS;
1597 }
1598 return (B_TRUE);
1599 }
1600
1601 /*
1602 * Called during boot.
1603 *
1604 * Walk through the DB and apply all the global module properties. We plow
1605 * through the DB even if we fail to apply property.
1606 */
1607 /* ARGSUSED */
1608 static boolean_t
1609 ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1610 int *errp)
1611 {
1612 ipadm_handle_t iph = cbarg;
1613 nvpair_t *nvp, *pnvp = NULL;
1614 char *strval = NULL, *name, *mod = NULL, *pname;
1615 char tmpstr[IPMGMT_STRSIZE];
1616 uint_t proto;
1617
1618 /*
1619 * We could have used nvl_exists() directly, however we need several
1620 * calls to it and each call traverses the list. Since this codepath
1621 * is exercised during boot, let's traverse the list ourselves and do
1622 * the necessary checks.
1623 */
1624 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1625 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1626 name = nvpair_name(nvp);
1627 if (IPADM_PRIV_NVP(name)) {
1628 if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1629 strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1630 return (B_TRUE);
1631 else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1632 nvpair_value_string(nvp, &mod) != 0)
1633 return (B_TRUE);
1634 } else {
1635 /* possible a property */
1636 pnvp = nvp;
1637 }
1638 }
1639
1640 /* If we are here then we have found a global property */
1641 assert(mod != NULL);
1642 assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1643
1644 proto = ipadm_str2proto(mod);
1645 name = nvpair_name(pnvp);
1646 if (nvpair_value_string(pnvp, &strval) == 0) {
1647 if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
1648 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1649 /* private protocol property */
1650 pname = &name[1];
1651 } else if (ipadm_legacy2new_propname(name, tmpstr,
1652 sizeof (tmpstr), &proto) == 0) {
1653 pname = tmpstr;
1654 } else {
1655 pname = name;
1656 }
1657 if (ipadm_set_prop(iph, pname, strval, proto,
1658 IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
1659 ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
1660 pname);
1661 }
1662 }
1663
1664 return (B_TRUE);
1665 }
1666
1667 /* initialize global module properties */
1668 void
1669 ipmgmt_init_prop()
1670 {
1671 ipadm_handle_t iph = NULL;
1672
1673 if (ipadm_open(&iph, IPH_INIT) != IPADM_SUCCESS) {
1674 ipmgmt_log(LOG_WARNING, "Could not reapply any of the "
1675 "persisted protocol properties");
1676 return;
1677 }
1678 /* ipmgmt_db_init() logs warnings if there are any issues */
1679 (void) ipmgmt_db_walk(ipmgmt_db_init, iph, IPADM_DB_READ);
1680 ipadm_close(iph);
1681 }
1682
1683 void
1684 ipmgmt_release_scf_resources(scf_resources_t *res)
1685 {
1686 scf_entry_destroy(res->sr_ent);
1687 scf_transaction_destroy(res->sr_tx);
1688 scf_value_destroy(res->sr_val);
1689 scf_property_destroy(res->sr_prop);
1690 scf_pg_destroy(res->sr_pg);
1691 scf_instance_destroy(res->sr_inst);
1692 (void) scf_handle_unbind(res->sr_handle);
1693 scf_handle_destroy(res->sr_handle);
1694 }
1695
1696 /*
1697 * It creates the necessary SCF handles and binds the given `fmri' to an
1698 * instance. These resources are required for retrieving property value,
1699 * creating property groups and modifying property values.
1700 */
1701 int
1702 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
1703 {
1704 res->sr_tx = NULL;
1705 res->sr_ent = NULL;
1706 res->sr_inst = NULL;
1707 res->sr_pg = NULL;
1708 res->sr_prop = NULL;
1709 res->sr_val = NULL;
1710
1711 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL)
1712 return (-1);
1713
1714 if (scf_handle_bind(res->sr_handle) != 0) {
1715 scf_handle_destroy(res->sr_handle);
1716 return (-1);
1717 }
1718 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL)
1719 goto failure;
1720 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
1721 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1722 goto failure;
1723 }
1724 /* we will create the rest of the resources on demand */
1725 return (0);
1726
1727 failure:
1728 ipmgmt_log(LOG_WARNING, "failed to create scf resources: %s",
1729 scf_strerror(scf_error()));
1730 ipmgmt_release_scf_resources(res);
1731 return (-1);
1732 }
1733
1734 /*
1735 * persists the `pval' for a given property `pname' in SCF. The only supported
1736 * SCF property types are INTEGER and ASTRING.
1737 */
1738 static int
1739 ipmgmt_set_scfprop_value(scf_resources_t *res, const char *pname, void *pval,
1740 scf_type_t ptype)
1741 {
1742 int result = -1;
1743 boolean_t new;
1744
1745 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL)
1746 goto failure;
1747 switch (ptype) {
1748 case SCF_TYPE_INTEGER:
1749 scf_value_set_integer(res->sr_val, *(int64_t *)pval);
1750 break;
1751 case SCF_TYPE_ASTRING:
1752 if (scf_value_set_astring(res->sr_val, (char *)pval) != 0) {
1753 ipmgmt_log(LOG_WARNING, "Error setting string value %s "
1754 "for property %s: %s", pval, pname,
1755 scf_strerror(scf_error()));
1756 goto failure;
1757 }
1758 break;
1759 default:
1760 goto failure;
1761 }
1762
1763 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL)
1764 goto failure;
1765 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL)
1766 goto failure;
1767 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL)
1768 goto failure;
1769
1770 retry:
1771 new = (scf_pg_get_property(res->sr_pg, pname, res->sr_prop) != 0);
1772 if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1)
1773 goto failure;
1774 if (new) {
1775 if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
1776 pname, ptype) == -1) {
1777 goto failure;
1778 }
1779 } else {
1780 if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
1781 pname, ptype) == -1) {
1782 goto failure;
1783 }
1784 }
1785
1786 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0)
1787 goto failure;
1788
1789 result = scf_transaction_commit(res->sr_tx);
1790 if (result == 0) {
1791 scf_transaction_reset(res->sr_tx);
1792 if (scf_pg_update(res->sr_pg) == -1) {
1793 goto failure;
1794 }
1795 goto retry;
1796 }
1797 if (result == -1)
1798 goto failure;
1799 return (0);
1800
1801 failure:
1802 ipmgmt_log(LOG_WARNING, "failed to save the data in SCF: %s",
1803 scf_strerror(scf_error()));
1804 return (-1);
1805 }
1806
1807 /*
1808 * Given a `pgname'/`pname', it retrieves the value based on `ptype' and
1809 * places it in `pval'.
1810 */
1811 static int
1812 ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1813 void *pval, scf_type_t ptype)
1814 {
1815 ssize_t numvals;
1816 scf_simple_prop_t *prop;
1817
1818 prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
1819 numvals = scf_simple_prop_numvalues(prop);
1820 if (numvals <= 0)
1821 goto ret;
1822 switch (ptype) {
1823 case SCF_TYPE_INTEGER:
1824 *(int64_t **)pval = scf_simple_prop_next_integer(prop);
1825 break;
1826 case SCF_TYPE_ASTRING:
1827 *(char **)pval = scf_simple_prop_next_astring(prop);
1828 break;
1829 default:
1830 break;
1831 }
1832 ret:
1833 scf_simple_prop_free(prop);
1834 return (numvals);
1835 }
1836
1837 /*
1838 * It stores the `pval' for given `pgname'/`pname' property group in SCF.
1839 */
1840 static int
1841 ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1842 void *pval, scf_type_t ptype)
1843 {
1844 scf_error_t err;
1845
1846 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1847 ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
1848 scf_strerror(scf_error()));
1849 return (-1);
1850 }
1851
1852 if (scf_instance_add_pg(res->sr_inst, pgname, SCF_GROUP_APPLICATION,
1853 0, res->sr_pg) != 0) {
1854 if ((err = scf_error()) != SCF_ERROR_EXISTS) {
1855 ipmgmt_log(LOG_WARNING,
1856 "Error adding property group '%s/%s': %s",
1857 pgname, pname, scf_strerror(err));
1858 return (-1);
1859 }
1860 /*
1861 * if the property group already exists, then we get the
1862 * composed view of the property group for the given instance.
1863 */
1864 if (scf_instance_get_pg_composed(res->sr_inst, NULL, pgname,
1865 res->sr_pg) != 0) {
1866 ipmgmt_log(LOG_WARNING, "Error getting composed view "
1867 "of the property group '%s/%s': %s", pgname, pname,
1868 scf_strerror(scf_error()));
1869 return (-1);
1870 }
1871 }
1872
1873 return (ipmgmt_set_scfprop_value(res, pname, pval, ptype));
1874 }
1875
1876 /*
1877 * Returns B_TRUE, if the non-global zone is being booted for the first time
1878 * after being installed. This is required to setup the ipadm data-store for
1879 * the first boot of the non-global zone. Please see, PSARC 2010/166,
1880 * for more info.
1881 *
1882 * Note that, this API cannot be used to determine first boot post image-update.
1883 * 'pkg image-update' clones the current BE and the existing value of
1884 * ipmgmtd/first_boot_done will be carried forward and obviously it will be set
1885 * to B_TRUE.
1886 */
1887 boolean_t
1888 ipmgmt_ngz_firstboot_postinstall()
1889 {
1890 scf_resources_t res;
1891 boolean_t bval = B_TRUE;
1892 char *strval;
1893
1894 /* we always err on the side of caution */
1895 if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
1896 return (bval);
1897
1898 if (ipmgmt_get_scfprop(&res, IPMGMTD_APP_PG, IPMGMTD_PROP_FBD, &strval,
1899 SCF_TYPE_ASTRING) > 0) {
1900 bval = (strcmp(strval, IPMGMTD_TRUESTR) == 0 ?
1901 B_FALSE : B_TRUE);
1902 } else {
1903 /*
1904 * IPMGMTD_PROP_FBD does not exist in the SCF. Lets create it.
1905 * Since we err on the side of caution, we ignore the return
1906 * error and return B_TRUE.
1907 */
1908 (void) ipmgmt_set_scfprop(&res, IPMGMTD_APP_PG,
1909 IPMGMTD_PROP_FBD, IPMGMTD_TRUESTR, SCF_TYPE_ASTRING);
1910 }
1911 ipmgmt_release_scf_resources(&res);
1912 return (bval);
1913 }
1914
1915 /*
1916 * Returns B_TRUE, if the data-store needs upgrade otherwise returns B_FALSE.
1917 * Today we have to take care of, one case of, upgrading from version 0 to
1918 * version 1, so we will use boolean_t as means to decide if upgrade is needed
1919 * or not. Further, the upcoming projects might completely move the flatfile
1920 * data-store into SCF and hence we shall keep this interface simple.
1921 */
1922 boolean_t
1923 ipmgmt_needs_upgrade(scf_resources_t *res)
1924 {
1925 boolean_t bval = B_TRUE;
1926 int64_t *verp;
1927
1928 if (ipmgmt_get_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER,
1929 &verp, SCF_TYPE_INTEGER) > 0) {
1930 if (*verp == IPADM_DB_VERSION)
1931 bval = B_FALSE;
1932 }
1933 /*
1934 * 'datastore_version' doesn't exist. Which means that we need to
1935 * upgrade the datastore. We will create 'datastore_version' and set
1936 * the version value to IPADM_DB_VERSION, after we upgrade the file.
1937 */
1938 return (bval);
1939 }
1940
1941 /*
1942 * This is called after the successful upgrade of the local data-store. With
1943 * the data-store upgraded to recent version we don't have to do anything on
1944 * subsequent reboots.
1945 */
1946 void
1947 ipmgmt_update_dbver(scf_resources_t *res)
1948 {
1949 int64_t version = IPADM_DB_VERSION;
1950
1951 (void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
1952 IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);
1953 }
1954
1955 /*
1956 * Return TRUE if `ifname' has persistent configuration for the `af' address
1957 * family in the datastore.
1958 * It is possible to call the function with af == AF_UNSPEC, so in this case
1959 * the function returns TRUE if either AF_INET or AF_INET6 interface exists
1960 */
1961 boolean_t
1962 ipmgmt_persist_if_exists(const char *ifname, sa_family_t af)
1963 {
1964 boolean_t exists = B_FALSE;
1965 nvlist_t *if_info_nvl;
1966 uint16_t *families = NULL;
1967 sa_family_t af_db;
1968 uint_t nelem = 0;
1969
1970 if (ipmgmt_get_ifinfo_nvl(ifname, &if_info_nvl) != 0)
1971 goto done;
1972
1973 if (nvlist_lookup_uint16_array(if_info_nvl, IPADM_NVP_FAMILIES,
1974 &families, &nelem) != 0)
1975 goto done;
1976
1977 while (nelem-- > 0) {
1978 af_db = families[nelem];
1979 if (af_db == af || (af == AF_UNSPEC &&
1980 (af_db == AF_INET || af_db == AF_INET6))) {
1981 exists = B_TRUE;
1982 break;
1983 }
1984 }
1985
1986 done:
1987 if (if_info_nvl != NULL)
1988 nvlist_free(if_info_nvl);
1989
1990 return (exists);
1991 }
1992
1993 /*
1994 * Retrieves the membership information for the requested mif_name
1995 * if mif_name is a memeber of a IPMP group, then gif_name will contain
1996 * the name of IPMP group interface, otherwise the variable will be empty
1997 */
1998 void
1999 ipmgmt_get_group_interface(const char *mif_name, char *gif_name, size_t size)
2000 {
2001 char *gif_name_from_nvl;
2002 nvlist_t *if_info_nvl;
2003
2004 gif_name[0] = '\0';
2005
2006 if (ipmgmt_get_ifinfo_nvl(mif_name, &if_info_nvl) != 0)
2007 goto done;
2008
2009 if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_GIFNAME,
2010 &gif_name_from_nvl) != 0)
2011 goto done;
2012
2013 (void) strlcpy(gif_name, gif_name_from_nvl, size);
2014
2015 done:
2016 if (if_info_nvl != NULL)
2017 nvlist_free(if_info_nvl);
2018 }
2019
2020 static int
2021 ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl)
2022 {
2023 ipmgmt_get_cbarg_t cbarg;
2024 nvpair_t *nvp;
2025 nvlist_t *nvl;
2026 int err;
2027
2028 cbarg.cb_ifname = NULL;
2029 cbarg.cb_aobjname = NULL;
2030 cbarg.cb_ocnt = 0;
2031
2032 if ((err = nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0)) != 0)
2033 goto done;
2034
2035 err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
2036 if (err == ENOENT && cbarg.cb_ocnt > 0)
2037 err = 0;
2038
2039 if (err != 0)
2040 goto done;
2041
2042 for (nvp = nvlist_next_nvpair(cbarg.cb_onvl, NULL); nvp != NULL;
2043 nvp = nvlist_next_nvpair(cbarg.cb_onvl, nvp)) {
2044
2045 if (strcmp(nvpair_name(nvp), ifname) != 0)
2046 continue;
2047
2048 if ((err = nvpair_value_nvlist(nvp, &nvl)) != 0 ||
2049 (err = nvlist_dup(nvl, if_info_nvl, NV_UNIQUE_NAME)) != 0)
2050 *if_info_nvl = NULL;
2051
2052 break;
2053 }
2054
2055 done:
2056 nvlist_free(cbarg.cb_onvl);
2057
2058 return (err);
2059 }