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