8158 Want named threads API
[unleashed.git] / usr / src / cmd / cmd-inet / lib / ipmgmtd / ipmgmt_persist.c
blobc1995611e9c5db2c37c4f747fbfe1329f4e46107
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2018 Joyent, Inc.
25 * Copyright 2016 Argo Technologie SA.
26 * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
30 * Contains DB walker functions, which are of type `db_wfunc_t';
32 * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
33 * size_t bufsize, int *errp);
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.
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.
48 * All the 'read' callback functions, retrieve the information from the DB, by
49 * reading `db_nvl' and then populate the `cbarg'.
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"
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"
69 #define ATYPE "_atype" /* name of the address type nvpair */
70 #define FLAGS "_flags" /* name of the flags nvpair */
73 * flag used by ipmgmt_persist_aobjmap() to indicate address type is
74 * IPADM_ADDR_IPV6_ADDRCONF.
76 #define IPMGMT_ATYPE_V6ACONF 0x1
78 extern pthread_rwlock_t ipmgmt_dbconf_lock;
80 /* signifies whether volatile copy of data store is in use */
81 static boolean_t ipmgmt_rdonly_root = B_FALSE;
84 * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
85 * in private nvpairs `proto', `ifname' & `aobjname'.
87 static boolean_t
88 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
89 const char *aobjname)
91 char *db_proto = NULL, *db_ifname = NULL;
92 char *db_aobjname = NULL;
93 nvpair_t *nvp;
94 char *name;
96 /* walk through db_nvl and retrieve all its private nvpairs */
97 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
98 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
99 name = nvpair_name(nvp);
100 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
101 (void) nvpair_value_string(nvp, &db_proto);
102 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
103 (void) nvpair_value_string(nvp, &db_ifname);
104 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
105 (void) nvpair_value_string(nvp, &db_aobjname);
108 if (proto != NULL && proto[0] == '\0')
109 proto = NULL;
110 if (ifname != NULL && ifname[0] == '\0')
111 ifname = NULL;
112 if (aobjname != NULL && aobjname[0] == '\0')
113 aobjname = NULL;
115 if ((proto == NULL && db_proto != NULL) ||
116 (proto != NULL && db_proto == NULL) ||
117 (proto != NULL && db_proto != NULL &&
118 strcmp(proto, db_proto) != 0)) {
119 /* no intersection - different protocols. */
120 return (B_FALSE);
122 if ((ifname == NULL && db_ifname != NULL) ||
123 (ifname != NULL && db_ifname == NULL) ||
124 (ifname != NULL && db_ifname != NULL &&
125 strcmp(ifname, db_ifname) != 0)) {
126 /* no intersection - different interfaces. */
127 return (B_FALSE);
129 if ((aobjname == NULL && db_aobjname != NULL) ||
130 (aobjname != NULL && db_aobjname == NULL) ||
131 (aobjname != NULL && db_aobjname != NULL &&
132 strcmp(aobjname, db_aobjname) != 0)) {
133 /* no intersection - different address objects */
134 return (B_FALSE);
137 return (B_TRUE);
141 * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
143 static boolean_t
144 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
146 nvpair_t *nvp;
147 char *name;
148 char *proto = NULL, *ifname = NULL, *aobjname = NULL;
150 /* walk through in_nvl and retrieve all its private nvpairs */
151 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
152 nvp = nvlist_next_nvpair(in_nvl, nvp)) {
153 name = nvpair_name(nvp);
154 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
155 (void) nvpair_value_string(nvp, &proto);
156 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
157 (void) nvpair_value_string(nvp, &ifname);
158 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
159 (void) nvpair_value_string(nvp, &aobjname);
162 return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
166 * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
167 * in private nvpairs `proto', `ifname' & `aobjname'.
169 static boolean_t
170 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
171 const char *ifname, char *aobjname)
173 char *db_ifname = NULL, *db_proto = NULL;
174 char *db_aobjname = NULL;
175 nvpair_t *nvp;
176 char *name;
178 /* walk through db_nvl and retrieve all private nvpairs */
179 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
180 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
181 name = nvpair_name(nvp);
182 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
183 (void) nvpair_value_string(nvp, &db_proto);
184 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
185 (void) nvpair_value_string(nvp, &db_ifname);
186 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
187 (void) nvpair_value_string(nvp, &db_aobjname);
190 if (proto != NULL && proto[0] != '\0') {
191 if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
192 return (B_FALSE);
194 if (ifname != NULL && ifname[0] != '\0') {
195 if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
196 return (B_FALSE);
198 if (aobjname != NULL && aobjname[0] != '\0') {
199 if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
200 return (B_FALSE);
203 return (B_TRUE);
207 * Retrieves the property value from the DB. The property whose value is to be
208 * retrieved is in `pargp->ia_pname'.
210 /* ARGSUSED */
211 boolean_t
212 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
213 int *errp)
215 ipmgmt_prop_arg_t *pargp = arg;
216 boolean_t cont = B_TRUE;
217 char *pval;
218 int err = 0;
220 *errp = 0;
222 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
223 pargp->ia_ifname, pargp->ia_aobjname))
224 return (B_TRUE);
226 if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
227 &pval)) == 0) {
228 (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
230 * We have retrieved what we are looking for.
231 * Stop the walker.
233 cont = B_FALSE;
234 } else {
235 if (err == ENOENT)
236 err = 0;
237 *errp = err;
240 return (cont);
244 * Removes the property value from the DB. The property whose value is to be
245 * removed is in `pargp->ia_pname'.
247 /* ARGSUSED */
248 boolean_t
249 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
250 int *errp)
252 ipmgmt_prop_arg_t *pargp = arg;
254 *errp = 0;
255 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
256 pargp->ia_ifname, pargp->ia_aobjname))
257 return (B_TRUE);
259 if (!nvlist_exists(db_nvl, pargp->ia_pname))
260 return (B_TRUE);
263 * We found the property in the DB. If IPMGMT_REMOVE is not set then
264 * delete the entry from the db. If it is set, then the property is a
265 * multi-valued property so just remove the specified values from DB.
267 if (pargp->ia_flags & IPMGMT_REMOVE) {
268 char *dbpval = NULL;
269 char *inpval = pargp->ia_pval;
270 char pval[MAXPROPVALLEN];
271 char *val, *lasts;
273 *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
274 if (*errp != 0)
275 return (B_FALSE);
278 * multi-valued properties are represented as comma separated
279 * values. Use string tokenizer functions to split them and
280 * search for the value to be removed.
282 bzero(pval, sizeof (pval));
283 if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
284 if (strcmp(val, inpval) != 0)
285 (void) strlcat(pval, val, MAXPROPVALLEN);
286 while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
287 if (strcmp(val, inpval) != 0) {
288 if (pval[0] != '\0')
289 (void) strlcat(pval, ",",
290 MAXPROPVALLEN);
291 (void) strlcat(pval, val,
292 MAXPROPVALLEN);
295 } else {
296 if (strcmp(dbpval, inpval) != 0)
297 *errp = ENOENT;
298 else
299 buf[0] = '\0';
300 return (B_FALSE);
302 *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
303 if (*errp != 0)
304 return (B_FALSE);
306 (void) memset(buf, 0, buflen);
307 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
308 /* buffer overflow */
309 *errp = ENOBUFS;
311 } else {
312 buf[0] = '\0';
315 /* stop the search */
316 return (B_FALSE);
320 * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
321 * found, when one of the following occurs first.
322 * - the input aobjname matches the db aobjname. Return the db address.
323 * - the input interface matches the db interface. Return all the
324 * matching db lines with addresses.
326 /* ARGSUSED */
327 boolean_t
328 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
329 int *errp)
331 ipmgmt_getaddr_cbarg_t *cbarg = arg;
332 char *db_aobjname = NULL;
333 char *db_ifname = NULL;
334 nvlist_t *db_addr = NULL;
335 char name[IPMGMT_STRSIZE];
336 nvpair_t *nvp;
337 boolean_t add_nvl = B_FALSE;
339 /* Parse db nvlist */
340 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
341 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
342 if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
343 (void) nvpair_value_nvlist(nvp, &db_addr);
344 else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
345 (void) nvpair_value_string(nvp, &db_ifname);
346 else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
347 (void) nvpair_value_string(nvp, &db_aobjname);
350 if (db_aobjname == NULL) /* Not an address */
351 return (B_TRUE);
353 /* Check for a match between the aobjnames or the interface name */
354 if (cbarg->cb_aobjname[0] != '\0') {
355 if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
356 add_nvl = B_TRUE;
357 } else if (cbarg->cb_ifname[0] != '\0') {
358 if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
359 add_nvl = B_TRUE;
360 } else {
361 add_nvl = B_TRUE;
364 if (add_nvl) {
365 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
366 cbarg->cb_ocnt);
367 *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
368 if (*errp == 0)
369 cbarg->cb_ocnt++;
371 return (B_TRUE);
375 * This function only gets called if a volatile filesystem version
376 * of the configuration file has been created. This only happens in the
377 * extremely rare case that a request has been made to update the configuration
378 * file at boottime while the root filesystem was read-only. This is
379 * really a rare occurrence now that we don't support UFS root filesystems
380 * any longer. This function will periodically attempt to write the
381 * configuration back to its location on the root filesystem. Success
382 * will indicate that the filesystem is no longer read-only.
384 /* ARGSUSED */
385 static void *
386 ipmgmt_db_restore_thread(void *arg)
388 int err;
390 for (;;) {
391 (void) sleep(5);
392 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
393 if (!ipmgmt_rdonly_root)
394 break;
395 err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE);
396 if (err == 0) {
397 ipmgmt_rdonly_root = B_FALSE;
398 break;
400 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
402 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
403 return (NULL);
407 * This function takes the appropriate lock, read or write, based on the
408 * `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated
409 * by the fact that we are not always guaranteed to have a writable root
410 * filesystem since it is possible that we are reading or writing during
411 * bootime while the root filesystem is still read-only. This is, by far,
412 * the exception case. Normally, this function will be called when the
413 * root filesystem is writable. In the unusual case where this is not
414 * true, the configuration file is copied to the volatile file system
415 * and is updated there until the root filesystem becomes writable. At
416 * that time the file will be moved back to its proper location by
417 * ipmgmt_db_restore_thread().
419 extern int
420 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
422 int err;
423 boolean_t writeop;
424 mode_t mode;
425 pthread_t tid;
426 pthread_attr_t attr;
428 writeop = (db_op != IPADM_DB_READ);
429 if (writeop) {
430 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
431 mode = IPADM_FILE_MODE;
432 } else {
433 (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
434 mode = 0;
438 * Did a previous write attempt fail? If so, don't even try to
439 * read/write to IPADM_DB_FILE.
441 if (!ipmgmt_rdonly_root) {
442 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE,
443 mode, db_op);
444 if (err != EROFS)
445 goto done;
449 * If we haven't already copied the file to the volatile
450 * file system, do so. This should only happen on a failed
451 * writeop(i.e., we have acquired the write lock above).
453 if (access(IPADM_VOL_DB_FILE, F_OK) != 0) {
454 assert(writeop);
455 err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE);
456 if (err != 0)
457 goto done;
458 (void) pthread_attr_init(&attr);
459 (void) pthread_attr_setdetachstate(&attr,
460 PTHREAD_CREATE_DETACHED);
461 (void) pthread_attr_setname_np(&attr, "db_restore");
462 err = pthread_create(&tid, &attr, ipmgmt_db_restore_thread,
463 NULL);
464 (void) pthread_attr_destroy(&attr);
465 if (err != 0) {
466 (void) unlink(IPADM_VOL_DB_FILE);
467 goto done;
469 ipmgmt_rdonly_root = B_TRUE;
473 * Read/write from the volatile copy.
475 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE,
476 mode, db_op);
477 done:
478 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
479 return (err);
483 * Used to add an entry towards the end of DB. It just returns B_TRUE for
484 * every line of the DB. When we reach the end, ipadm_rw_db() adds the
485 * line at the end.
487 /* ARGSUSED */
488 boolean_t
489 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
491 return (B_TRUE);
495 * This function is used to update or create an entry in DB. The nvlist_t,
496 * `in_nvl', represents the line we are looking for. Once we ensure the right
497 * line from DB, we update that entry.
499 boolean_t
500 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
501 int *errp)
503 ipadm_dbwrite_cbarg_t *cb = arg;
504 uint_t flags = cb->dbw_flags;
505 nvlist_t *in_nvl = cb->dbw_nvl;
506 nvpair_t *nvp;
507 char *name, *instrval = NULL, *dbstrval = NULL;
508 char pval[MAXPROPVALLEN];
510 *errp = 0;
511 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
512 return (B_TRUE);
514 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
515 nvp = nvlist_next_nvpair(in_nvl, nvp)) {
516 name = nvpair_name(nvp);
517 if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
518 break;
521 if (nvp == NULL)
522 return (B_TRUE);
524 assert(nvpair_type(nvp) == DATA_TYPE_STRING);
526 if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
527 return (B_FALSE);
530 * If IPMGMT_APPEND is set then we are dealing with multi-valued
531 * properties. We append to the entry from the db, with the new value.
533 if (flags & IPMGMT_APPEND) {
534 if ((*errp = nvlist_lookup_string(db_nvl, name,
535 &dbstrval)) != 0)
536 return (B_FALSE);
537 (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
538 instrval);
539 if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
540 return (B_FALSE);
541 } else {
542 /* case of in-line update of a db entry */
543 if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
544 return (B_FALSE);
547 (void) memset(buf, 0, buflen);
548 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
549 /* buffer overflow */
550 *errp = ENOBUFS;
553 /* we updated the DB entry, so do not continue */
554 return (B_FALSE);
558 * For the given `cbarg->cb_ifname' interface, retrieves any persistent
559 * interface information (used in 'ipadm show-if')
561 /* ARGSUSED */
562 boolean_t
563 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
564 int *errp)
566 ipmgmt_getif_cbarg_t *cbarg = arg;
567 char *ifname = cbarg->cb_ifname;
568 char *intf = NULL;
569 ipadm_if_info_t *ifp = NULL;
570 sa_family_t af;
571 char *afstr;
573 *errp = 0;
574 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 ||
575 nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 ||
576 (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) {
577 return (B_TRUE);
579 af = atoi(afstr);
580 for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) {
581 if (strcmp(ifp->ifi_name, intf) == 0)
582 break;
584 if (ifp == NULL) {
585 ipadm_if_info_t *new;
587 if ((new = calloc(1, sizeof (*new))) == NULL) {
588 *errp = ENOMEM;
589 return (B_FALSE); /* don't continue the walk */
591 new->ifi_next = cbarg->cb_ifinfo;
592 cbarg->cb_ifinfo = new;
593 ifp = new;
594 (void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name));
597 if (af == AF_INET) {
598 ifp->ifi_pflags |= IFIF_IPV4;
599 } else {
600 assert(af == AF_INET6);
601 ifp->ifi_pflags |= IFIF_IPV6;
604 /* Terminate the walk if we found both v4 and v6 interfaces. */
605 if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) &&
606 (ifp->ifi_pflags & IFIF_IPV6))
607 return (B_FALSE);
609 return (B_TRUE);
613 * Deletes those entries from the database for which interface name
614 * matches with the given `cbarg->cb_ifname'
616 /* ARGSUSED */
617 boolean_t
618 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
619 int *errp)
621 ipmgmt_if_cbarg_t *cbarg = arg;
622 boolean_t isv6 = (cbarg->cb_family == AF_INET6);
623 char *ifname = cbarg->cb_ifname;
624 char *modstr = NULL;
625 char *afstr;
626 char *aobjname;
627 uint_t proto;
628 ipmgmt_aobjmap_t *head;
629 boolean_t aobjfound = B_FALSE;
631 *errp = 0;
633 if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
634 return (B_TRUE);
636 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
637 if (atoi(afstr) == cbarg->cb_family)
638 goto delete;
639 return (B_TRUE);
642 /* Reset all the interface configurations for 'ifname' */
643 if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
644 nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
645 goto delete;
647 if (!isv6 &&
648 (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
649 nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
650 goto delete;
653 if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
655 * This must be an address property. Delete this
656 * line if there is a match in the address family.
658 head = aobjmap.aobjmap_head;
659 while (head != NULL) {
660 if (strcmp(head->am_aobjname, aobjname) == 0) {
661 aobjfound = B_TRUE;
662 if (head->am_family == cbarg->cb_family)
663 goto delete;
665 head = head->am_next;
668 * If aobjfound = B_FALSE, then this address is not
669 * available in active configuration. We should go ahead
670 * and delete it.
672 if (!aobjfound)
673 goto delete;
677 * If we are removing both v4 and v6 interface, then we get rid of
678 * all the properties for that interface. On the other hand, if we
679 * are deleting only v4 instance of an interface, then we delete v4
680 * properties only.
682 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
683 proto = ipadm_str2proto(modstr);
684 switch (proto) {
685 case MOD_PROTO_IPV6:
686 if (isv6)
687 goto delete;
688 break;
689 case MOD_PROTO_IPV4:
690 if (!isv6)
691 goto delete;
692 break;
693 case MOD_PROTO_IP:
694 /* this should never be the case, today */
695 assert(0);
696 break;
699 /* Not found a match yet. Continue processing the db */
700 return (B_TRUE);
701 delete:
702 /* delete the line from the db */
703 buf[0] = '\0';
704 return (B_TRUE);
708 * Deletes those entries from the database for which address object name
709 * matches with the given `cbarg->cb_aobjname'
711 /* ARGSUSED */
712 boolean_t
713 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
714 int *errp)
716 ipmgmt_resetaddr_cbarg_t *cbarg = arg;
717 char *aobjname = cbarg->cb_aobjname;
719 *errp = 0;
720 if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
721 return (B_TRUE);
723 /* delete the line from the db */
724 buf[0] = '\0';
725 return (B_TRUE);
729 * Retrieves all interface props, including addresses, for given interface(s).
730 * `invl' contains the list of interfaces, for which information need to be
731 * retrieved.
733 /* ARGSUSED */
734 boolean_t
735 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
736 int *errp)
738 ipmgmt_initif_cbarg_t *cbarg = arg;
739 nvlist_t *onvl = cbarg->cb_onvl;
740 nvlist_t *invl = cbarg->cb_invl;
741 sa_family_t in_af = cbarg->cb_family;
742 char *db_ifname;
744 *errp = 0;
745 if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
746 nvlist_exists(invl, db_ifname)) {
747 char name[IPMGMT_STRSIZE];
748 sa_family_t db_af = in_af;
749 uint_t proto;
750 char *pstr;
752 if (in_af != AF_UNSPEC) {
753 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
754 &pstr) == 0) {
755 proto = ipadm_str2proto(pstr);
756 if (proto == MOD_PROTO_IPV4)
757 db_af = AF_INET;
758 else if (proto == MOD_PROTO_IPV6)
759 db_af = AF_INET6;
760 else
761 db_af = in_af;
762 } else {
763 if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
764 nvlist_exists(db_nvl, IPADM_NVP_DHCP))
765 db_af = AF_INET;
766 else
767 db_af = AF_INET6;
770 if (in_af == db_af) {
771 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
772 cbarg->cb_ocnt);
773 *errp = nvlist_add_nvlist(onvl, name, db_nvl);
774 if (*errp == 0)
775 cbarg->cb_ocnt++;
778 return (B_TRUE);
782 * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
783 * into `aobjmap' structure.
785 static int
786 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
788 ipmgmt_aobjmap_t *new, *head;
790 head = aobjmap.aobjmap_head;
791 if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
792 return (ENOMEM);
793 *new = *nodep;
794 new->am_next = NULL;
796 /* Add the node at the beginning of the list */
797 if (head == NULL) {
798 aobjmap.aobjmap_head = new;
799 } else {
800 new->am_next = aobjmap.aobjmap_head;
801 aobjmap.aobjmap_head = new;
803 return (0);
807 * A recursive function to generate alphabetized number given a decimal number.
808 * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
809 * 'ab', 'ac', et al.
811 static void
812 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
814 if (num >= 26)
815 i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
816 if (*cp != endp) {
817 *cp[0] = 'a' + (num % 26);
818 (*cp)++;
823 * This function generates an `aobjname', when required, and then does
824 * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
825 * through the `aobjmap' to check if an address object with the same
826 * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
827 * `aobjname's are not allowed.
829 * If `nodep->am_aobjname' is an empty string then the daemon generates an
830 * `aobjname' using the `am_nextnum', which contains the next number to be
831 * used to generate `aobjname'. `am_nextnum' is converted to base26 using
832 * `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
834 * `am_nextnum' will be 0 to begin with. Every time an address object that
835 * needs `aobjname' is added it's incremented by 1. So for the first address
836 * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
837 * For the second address object on that interface `am_aobjname' will be net0/_b
838 * and `am_nextnum' will incremented to 2.
840 static int
841 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
843 ipmgmt_aobjmap_t *head;
844 uint32_t nextnum;
846 for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
847 if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
848 break;
849 nextnum = (head == NULL ? 0 : head->am_nextnum);
852 * if `aobjname' is empty, then the daemon has to generate the
853 * next `aobjname' for the given interface and family.
855 if (nodep->am_aobjname[0] == '\0') {
856 char tmpstr[IPADM_AOBJ_USTRSIZ - 1]; /* 1 for leading '_' */
857 char *cp = tmpstr;
858 char *endp = tmpstr + sizeof (tmpstr);
860 i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
862 if (cp == endp)
863 return (EINVAL);
864 cp[0] = '\0';
866 if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
867 nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
868 return (EINVAL);
870 nodep->am_nextnum = ++nextnum;
871 } else {
872 for (head = aobjmap.aobjmap_head; head != NULL;
873 head = head->am_next) {
874 if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
875 return (EEXIST);
877 nodep->am_nextnum = nextnum;
879 return (i_ipmgmt_add_amnode(nodep));
883 * Performs following operations on the global `aobjmap' linked list.
884 * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
885 * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
886 * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
887 * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
890 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
892 ipmgmt_aobjmap_t *head, *prev, *matched = NULL;
893 boolean_t update = B_TRUE;
894 int err = 0;
895 ipadm_db_op_t db_op;
897 (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
899 head = aobjmap.aobjmap_head;
900 switch (op) {
901 case ADDROBJ_ADD:
903 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
904 * update, else add the new node.
906 for (; head != NULL; head = head->am_next) {
908 * For IPv6, we need to distinguish between the
909 * linklocal and non-linklocal nodes
911 if (strcmp(head->am_aobjname,
912 nodep->am_aobjname) == 0 &&
913 (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
914 head->ipmgmt_am_linklocal ==
915 nodep->ipmgmt_am_linklocal))
916 break;
919 if (head != NULL) {
920 /* update the node */
921 (void) strlcpy(head->am_ifname, nodep->am_ifname,
922 sizeof (head->am_ifname));
923 head->am_lnum = nodep->am_lnum;
924 head->am_family = nodep->am_family;
925 head->am_flags = nodep->am_flags;
926 head->am_atype = nodep->am_atype;
927 head->am_atype_cache = nodep->am_atype_cache;
928 } else {
929 for (head = aobjmap.aobjmap_head; head != NULL;
930 head = head->am_next) {
931 if (strcmp(head->am_ifname,
932 nodep->am_ifname) == 0)
933 break;
935 nodep->am_nextnum = (head == NULL ? 0 :
936 head->am_nextnum);
937 err = i_ipmgmt_add_amnode(nodep);
939 db_op = IPADM_DB_WRITE;
940 break;
941 case ADDROBJ_DELETE:
942 prev = head;
943 while (head != NULL) {
944 if (strcmp(head->am_aobjname,
945 nodep->am_aobjname) == 0) {
946 nodep->am_atype = head->am_atype;
948 * There could be multiple IPV6_ADDRCONF nodes,
949 * with same address object name, so check for
950 * logical number also.
952 if (head->am_atype !=
953 IPADM_ADDR_IPV6_ADDRCONF ||
954 nodep->am_lnum == head->am_lnum)
955 break;
957 prev = head;
958 head = head->am_next;
960 if (head != NULL) {
962 * If the address object is in both active and
963 * persistent configuration and the user is deleting it
964 * only from active configuration then mark this node
965 * for deletion by reseting IPMGMT_ACTIVE bit.
966 * With this the same address object name cannot
967 * be reused until it is permanently removed.
969 if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
970 nodep->am_flags == IPMGMT_ACTIVE) {
971 /* Update flags in the in-memory map. */
972 head->am_flags &= ~IPMGMT_ACTIVE;
973 head->am_lnum = -1;
975 /* Update info in file. */
976 db_op = IPADM_DB_WRITE;
977 *nodep = *head;
978 } else {
979 (void) strlcpy(nodep->am_ifname,
980 head->am_ifname,
981 sizeof (nodep->am_ifname));
982 /* otherwise delete the node */
983 if (head == aobjmap.aobjmap_head)
984 aobjmap.aobjmap_head = head->am_next;
985 else
986 prev->am_next = head->am_next;
987 free(head);
988 db_op = IPADM_DB_DELETE;
990 } else {
991 err = ENOENT;
993 break;
994 case ADDROBJ_LOOKUPADD:
995 err = i_ipmgmt_lookupadd_amnode(nodep);
996 update = B_FALSE;
997 break;
998 case ADDROBJ_SETLIFNUM:
999 update = B_FALSE;
1000 for (; head != NULL; head = head->am_next) {
1001 if (strcmp(head->am_ifname,
1002 nodep->am_ifname) == 0 &&
1003 head->am_family == nodep->am_family &&
1004 head->am_lnum == nodep->am_lnum) {
1005 err = EEXIST;
1006 break;
1008 if (strcmp(head->am_aobjname,
1009 nodep->am_aobjname) == 0) {
1010 matched = head;
1013 if (err == EEXIST)
1014 break;
1015 if (matched != NULL) {
1016 /* update the lifnum */
1017 matched->am_lnum = nodep->am_lnum;
1018 } else {
1019 err = ENOENT;
1021 break;
1022 default:
1023 assert(0);
1026 if (err == 0 && update)
1027 err = ipmgmt_persist_aobjmap(nodep, db_op);
1029 (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
1031 return (err);
1035 * Given a node in `aobjmap', this function converts it into nvlist_t structure.
1036 * The content to be written to DB must be represented as nvlist_t.
1038 static int
1039 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
1041 int err;
1042 char strval[IPMGMT_STRSIZE];
1044 *nvl = NULL;
1045 if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
1046 goto fail;
1048 if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
1049 np->am_aobjname)) != 0)
1050 goto fail;
1052 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
1053 np->am_ifname)) != 0)
1054 goto fail;
1056 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
1057 if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
1058 goto fail;
1060 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
1061 if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
1062 goto fail;
1064 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
1065 if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
1066 goto fail;
1068 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
1069 if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
1070 goto fail;
1072 switch (np->am_atype) {
1073 case IPADM_ADDR_IPV6_ADDRCONF: {
1074 struct sockaddr_in6 *in6;
1076 in6 = &np->ipmgmt_am_ifid;
1077 if (np->ipmgmt_am_linklocal &&
1078 IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
1079 if ((err = nvlist_add_string(*nvl,
1080 IPADM_NVP_IPNUMADDR, "default")) != 0) {
1081 goto fail;
1083 } else {
1084 if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
1085 IPMGMT_STRSIZE) == NULL) {
1086 err = errno;
1087 goto fail;
1089 if ((err = nvlist_add_string(*nvl,
1090 IPADM_NVP_IPNUMADDR, strval)) != 0) {
1091 goto fail;
1095 break;
1096 case IPADM_ADDR_DHCP: {
1097 if (np->ipmgmt_am_reqhost &&
1098 *np->ipmgmt_am_reqhost != '\0' &&
1099 (err = nvlist_add_string(*nvl, IPADM_NVP_REQHOST,
1100 np->ipmgmt_am_reqhost)) != 0)
1101 goto fail;
1103 /* FALLTHRU */
1104 default:
1105 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1106 "")) != 0)
1107 goto fail;
1108 break;
1110 return (err);
1111 fail:
1112 nvlist_free(*nvl);
1113 return (err);
1117 * Read the aobjmap data store and build the in-memory representation
1118 * of the aobjmap. We don't need to hold any locks while building this as
1119 * we do this in very early stage of daemon coming up, even before the door
1120 * is opened.
1122 /* ARGSUSED */
1123 extern boolean_t
1124 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1125 int *errp)
1127 nvpair_t *nvp = NULL;
1128 char *name, *strval = NULL;
1129 ipmgmt_aobjmap_t node;
1130 struct sockaddr_in6 *in6;
1132 *errp = 0;
1133 node.am_next = NULL;
1134 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1135 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1136 name = nvpair_name(nvp);
1138 if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
1139 return (B_TRUE);
1140 if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
1141 (void) strlcpy(node.am_aobjname, strval,
1142 sizeof (node.am_aobjname));
1143 } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
1144 (void) strlcpy(node.am_ifname, strval,
1145 sizeof (node.am_ifname));
1146 } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
1147 node.am_lnum = atoi(strval);
1148 } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
1149 node.am_family = (sa_family_t)atoi(strval);
1150 } else if (strcmp(FLAGS, name) == 0) {
1151 node.am_flags = atoi(strval);
1152 } else if (strcmp(ATYPE, name) == 0) {
1153 node.am_atype = (ipadm_addr_type_t)atoi(strval);
1154 } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
1155 if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1156 in6 = &node.ipmgmt_am_ifid;
1157 if (strcmp(strval, "default") == 0) {
1158 bzero(in6,
1159 sizeof (node.ipmgmt_am_ifid));
1160 node.ipmgmt_am_linklocal = B_TRUE;
1161 } else {
1162 (void) inet_pton(AF_INET6, strval,
1163 &in6->sin6_addr);
1164 if (IN6_IS_ADDR_UNSPECIFIED(
1165 &in6->sin6_addr))
1166 node.ipmgmt_am_linklocal =
1167 B_TRUE;
1173 /* we have all the information we need, add the node */
1174 *errp = i_ipmgmt_add_amnode(&node);
1176 return (B_TRUE);
1180 * Updates an entry from the temporary cache file, which matches the given
1181 * address object name.
1183 /* ARGSUSED */
1184 static boolean_t
1185 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1186 size_t buflen, int *errp)
1188 ipadm_dbwrite_cbarg_t *cb = arg;
1189 nvlist_t *in_nvl = cb->dbw_nvl;
1190 uint32_t flags = cb->dbw_flags;
1191 char *db_lifnumstr = NULL, *in_lifnumstr = NULL;
1193 *errp = 0;
1194 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
1195 return (B_TRUE);
1197 if (flags & IPMGMT_ATYPE_V6ACONF) {
1198 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1199 &db_lifnumstr) != 0 ||
1200 nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
1201 &in_lifnumstr) != 0 ||
1202 (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
1203 strcmp(db_lifnumstr, in_lifnumstr) != 0))
1204 return (B_TRUE);
1207 /* we found the match */
1208 (void) memset(buf, 0, buflen);
1209 if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
1210 /* buffer overflow */
1211 *errp = ENOBUFS;
1214 /* stop the walker */
1215 return (B_FALSE);
1219 * Deletes an entry from the temporary cache file, which matches the given
1220 * address object name.
1222 /* ARGSUSED */
1223 static boolean_t
1224 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1225 size_t buflen, int *errp)
1227 ipmgmt_aobjmap_t *nodep = arg;
1228 char *db_lifnumstr = NULL;
1230 *errp = 0;
1231 if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
1232 nodep->am_aobjname))
1233 return (B_TRUE);
1235 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1236 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1237 &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
1238 return (B_TRUE);
1241 /* we found the match, delete the line from the db */
1242 buf[0] = '\0';
1244 /* stop the walker */
1245 return (B_FALSE);
1249 * Adds or deletes aobjmap node information into a temporary cache file.
1251 extern int
1252 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
1254 int err;
1255 ipadm_dbwrite_cbarg_t cb;
1256 nvlist_t *nvl = NULL;
1258 if (op == IPADM_DB_WRITE) {
1259 if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
1260 return (err);
1261 cb.dbw_nvl = nvl;
1262 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
1263 cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
1264 else
1265 cb.dbw_flags = 0;
1267 err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
1268 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
1269 nvlist_free(nvl);
1270 } else {
1271 assert(op == IPADM_DB_DELETE);
1273 err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
1274 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
1276 return (err);
1280 * upgrades the ipadm data-store. It renames all the old private protocol
1281 * property names which start with leading protocol names to begin with
1282 * IPADM_PRIV_PROP_PREFIX.
1284 /* ARGSUSED */
1285 boolean_t
1286 ipmgmt_db_upgrade(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1287 int *errp)
1289 nvpair_t *nvp;
1290 char *name, *pname = NULL, *protostr = NULL, *pval = NULL;
1291 uint_t proto, nproto;
1292 char nname[IPMGMT_STRSIZE], tmpstr[IPMGMT_STRSIZE];
1294 *errp = 0;
1296 * We are interested in lines which contain protocol properties. We
1297 * walk through other lines in the DB.
1299 if (nvlist_exists(db_nvl, IPADM_NVP_IFNAME) ||
1300 nvlist_exists(db_nvl, IPADM_NVP_AOBJNAME)) {
1301 return (B_TRUE);
1303 assert(nvlist_exists(db_nvl, IPADM_NVP_PROTONAME));
1306 * extract the propname from the `db_nvl' and also extract the
1307 * protocol from the `db_nvl'.
1309 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1310 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1311 name = nvpair_name(nvp);
1312 if (strcmp(name, IPADM_NVP_PROTONAME) == 0) {
1313 if (nvpair_value_string(nvp, &protostr) != 0)
1314 return (B_TRUE);
1315 } else {
1316 assert(!IPADM_PRIV_NVP(name));
1317 pname = name;
1318 if (nvpair_value_string(nvp, &pval) != 0)
1319 return (B_TRUE);
1323 /* if the private property is in the right format return */
1324 if (strncmp(pname, IPADM_PERSIST_PRIVPROP_PREFIX,
1325 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1326 return (B_TRUE);
1328 /* if it's a public property move onto the next property */
1329 nproto = proto = ipadm_str2proto(protostr);
1330 if (ipadm_legacy2new_propname(pname, nname, sizeof (nname),
1331 &nproto) != 0) {
1332 return (B_TRUE);
1335 /* replace the old protocol with new protocol, if required */
1336 if (nproto != proto) {
1337 protostr = ipadm_proto2str(nproto);
1338 if (nvlist_add_string(db_nvl, IPADM_NVP_PROTONAME,
1339 protostr) != 0) {
1340 return (B_TRUE);
1344 /* replace the old property name with new property name, if required */
1345 /* add the prefix to property name */
1346 (void) snprintf(tmpstr, sizeof (tmpstr), "_%s", nname);
1347 if (nvlist_add_string(db_nvl, tmpstr, pval) != 0 ||
1348 nvlist_remove(db_nvl, pname, DATA_TYPE_STRING) != 0) {
1349 return (B_TRUE);
1351 (void) memset(buf, 0, buflen);
1352 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
1353 /* buffer overflow */
1354 *errp = ENOBUFS;
1356 return (B_TRUE);
1360 * Called during boot.
1362 * Walk through the DB and apply all the global module properties. We plow
1363 * through the DB even if we fail to apply property.
1365 /* ARGSUSED */
1366 static boolean_t
1367 ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1368 int *errp)
1370 ipadm_handle_t iph = cbarg;
1371 nvpair_t *nvp, *pnvp;
1372 char *strval = NULL, *name, *mod = NULL, *pname;
1373 char tmpstr[IPMGMT_STRSIZE];
1374 uint_t proto;
1377 * We could have used nvl_exists() directly, however we need several
1378 * calls to it and each call traverses the list. Since this codepath
1379 * is exercised during boot, let's traverse the list ourselves and do
1380 * the necessary checks.
1382 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1383 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1384 name = nvpair_name(nvp);
1385 if (IPADM_PRIV_NVP(name)) {
1386 if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1387 strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1388 return (B_TRUE);
1389 else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1390 nvpair_value_string(nvp, &mod) != 0)
1391 return (B_TRUE);
1392 } else {
1393 /* possible a property */
1394 pnvp = nvp;
1398 /* if we are here than we found a global property */
1399 assert(mod != NULL);
1400 assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1402 proto = ipadm_str2proto(mod);
1403 name = nvpair_name(pnvp);
1404 if (nvpair_value_string(pnvp, &strval) == 0) {
1405 if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
1406 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1407 /* private protocol property */
1408 pname = &name[1];
1409 } else if (ipadm_legacy2new_propname(name, tmpstr,
1410 sizeof (tmpstr), &proto) == 0) {
1411 pname = tmpstr;
1412 } else {
1413 pname = name;
1415 if (ipadm_set_prop(iph, pname, strval, proto,
1416 IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
1417 ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
1418 pname);
1422 return (B_TRUE);
1425 /* initialize global module properties */
1426 void
1427 ipmgmt_init_prop()
1429 ipadm_handle_t iph = NULL;
1431 if (ipadm_open(&iph, IPH_INIT) != IPADM_SUCCESS) {
1432 ipmgmt_log(LOG_WARNING, "Could not reapply any of the "
1433 "persisted protocol properties");
1434 return;
1436 /* ipmgmt_db_init() logs warnings if there are any issues */
1437 (void) ipmgmt_db_walk(ipmgmt_db_init, iph, IPADM_DB_READ);
1438 ipadm_close(iph);
1441 void
1442 ipmgmt_release_scf_resources(scf_resources_t *res)
1444 scf_entry_destroy(res->sr_ent);
1445 scf_transaction_destroy(res->sr_tx);
1446 scf_value_destroy(res->sr_val);
1447 scf_property_destroy(res->sr_prop);
1448 scf_pg_destroy(res->sr_pg);
1449 scf_instance_destroy(res->sr_inst);
1450 (void) scf_handle_unbind(res->sr_handle);
1451 scf_handle_destroy(res->sr_handle);
1455 * It creates the necessary SCF handles and binds the given `fmri' to an
1456 * instance. These resources are required for retrieving property value,
1457 * creating property groups and modifying property values.
1460 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
1462 res->sr_tx = NULL;
1463 res->sr_ent = NULL;
1464 res->sr_inst = NULL;
1465 res->sr_pg = NULL;
1466 res->sr_prop = NULL;
1467 res->sr_val = NULL;
1469 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL)
1470 return (-1);
1472 if (scf_handle_bind(res->sr_handle) != 0) {
1473 scf_handle_destroy(res->sr_handle);
1474 return (-1);
1476 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL)
1477 goto failure;
1478 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
1479 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1480 goto failure;
1482 /* we will create the rest of the resources on demand */
1483 return (0);
1485 failure:
1486 ipmgmt_log(LOG_WARNING, "failed to create scf resources: %s",
1487 scf_strerror(scf_error()));
1488 ipmgmt_release_scf_resources(res);
1489 return (-1);
1493 * persists the `pval' for a given property `pname' in SCF. The only supported
1494 * SCF property types are INTEGER and ASTRING.
1496 static int
1497 ipmgmt_set_scfprop_value(scf_resources_t *res, const char *pname, void *pval,
1498 scf_type_t ptype)
1500 int result = -1;
1501 boolean_t new;
1503 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL)
1504 goto failure;
1505 switch (ptype) {
1506 case SCF_TYPE_INTEGER:
1507 scf_value_set_integer(res->sr_val, *(int64_t *)pval);
1508 break;
1509 case SCF_TYPE_ASTRING:
1510 if (scf_value_set_astring(res->sr_val, (char *)pval) != 0) {
1511 ipmgmt_log(LOG_WARNING, "Error setting string value %s "
1512 "for property %s: %s", pval, pname,
1513 scf_strerror(scf_error()));
1514 goto failure;
1516 break;
1517 default:
1518 goto failure;
1521 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL)
1522 goto failure;
1523 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL)
1524 goto failure;
1525 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL)
1526 goto failure;
1528 retry:
1529 new = (scf_pg_get_property(res->sr_pg, pname, res->sr_prop) != 0);
1530 if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1)
1531 goto failure;
1532 if (new) {
1533 if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
1534 pname, ptype) == -1) {
1535 goto failure;
1537 } else {
1538 if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
1539 pname, ptype) == -1) {
1540 goto failure;
1544 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0)
1545 goto failure;
1547 result = scf_transaction_commit(res->sr_tx);
1548 if (result == 0) {
1549 scf_transaction_reset(res->sr_tx);
1550 if (scf_pg_update(res->sr_pg) == -1) {
1551 goto failure;
1553 goto retry;
1555 if (result == -1)
1556 goto failure;
1557 return (0);
1559 failure:
1560 ipmgmt_log(LOG_WARNING, "failed to save the data in SCF: %s",
1561 scf_strerror(scf_error()));
1562 return (-1);
1566 * Given a `pgname'/`pname', it retrieves the value based on `ptype' and
1567 * places it in `pval'.
1569 static int
1570 ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1571 void *pval, scf_type_t ptype)
1573 ssize_t numvals;
1574 scf_simple_prop_t *prop;
1576 prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
1577 numvals = scf_simple_prop_numvalues(prop);
1578 if (numvals <= 0)
1579 goto ret;
1580 switch (ptype) {
1581 case SCF_TYPE_INTEGER:
1582 *(int64_t **)pval = scf_simple_prop_next_integer(prop);
1583 break;
1584 case SCF_TYPE_ASTRING:
1585 *(char **)pval = scf_simple_prop_next_astring(prop);
1586 break;
1588 ret:
1589 scf_simple_prop_free(prop);
1590 return (numvals);
1594 * It stores the `pval' for given `pgname'/`pname' property group in SCF.
1596 static int
1597 ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1598 void *pval, scf_type_t ptype)
1600 scf_error_t err;
1602 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1603 ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
1604 scf_strerror(scf_error()));
1605 return (-1);
1608 if (scf_instance_add_pg(res->sr_inst, pgname, SCF_GROUP_APPLICATION,
1609 0, res->sr_pg) != 0) {
1610 if ((err = scf_error()) != SCF_ERROR_EXISTS) {
1611 ipmgmt_log(LOG_WARNING,
1612 "Error adding property group '%s/%s': %s",
1613 pgname, pname, scf_strerror(err));
1614 return (-1);
1617 * if the property group already exists, then we get the
1618 * composed view of the property group for the given instance.
1620 if (scf_instance_get_pg_composed(res->sr_inst, NULL, pgname,
1621 res->sr_pg) != 0) {
1622 ipmgmt_log(LOG_WARNING, "Error getting composed view "
1623 "of the property group '%s/%s': %s", pgname, pname,
1624 scf_strerror(scf_error()));
1625 return (-1);
1629 return (ipmgmt_set_scfprop_value(res, pname, pval, ptype));
1633 * Returns B_TRUE, if the non-global zone is being booted for the first time
1634 * after being installed. This is required to setup the ipadm data-store for
1635 * the first boot of the non-global zone. Please see, PSARC 2010/166,
1636 * for more info.
1638 * Note that, this API cannot be used to determine first boot post image-update.
1639 * 'pkg image-update' clones the current BE and the existing value of
1640 * ipmgmtd/first_boot_done will be carried forward and obviously it will be set
1641 * to B_TRUE.
1643 boolean_t
1644 ipmgmt_ngz_firstboot_postinstall()
1646 scf_resources_t res;
1647 boolean_t bval = B_TRUE;
1648 char *strval;
1650 /* we always err on the side of caution */
1651 if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
1652 return (bval);
1654 if (ipmgmt_get_scfprop(&res, IPMGMTD_APP_PG, IPMGMTD_PROP_FBD, &strval,
1655 SCF_TYPE_ASTRING) > 0) {
1656 bval = (strcmp(strval, IPMGMTD_TRUESTR) == 0 ?
1657 B_FALSE : B_TRUE);
1658 } else {
1660 * IPMGMTD_PROP_FBD does not exist in the SCF. Lets create it.
1661 * Since we err on the side of caution, we ignore the return
1662 * error and return B_TRUE.
1664 (void) ipmgmt_set_scfprop(&res, IPMGMTD_APP_PG,
1665 IPMGMTD_PROP_FBD, IPMGMTD_TRUESTR, SCF_TYPE_ASTRING);
1667 ipmgmt_release_scf_resources(&res);
1668 return (bval);
1672 * Returns B_TRUE, if the data-store needs upgrade otherwise returns B_FALSE.
1673 * Today we have to take care of, one case of, upgrading from version 0 to
1674 * version 1, so we will use boolean_t as means to decide if upgrade is needed
1675 * or not. Further, the upcoming projects might completely move the flatfile
1676 * data-store into SCF and hence we shall keep this interface simple.
1678 boolean_t
1679 ipmgmt_needs_upgrade(scf_resources_t *res)
1681 boolean_t bval = B_TRUE;
1682 int64_t *verp;
1684 if (ipmgmt_get_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER,
1685 &verp, SCF_TYPE_INTEGER) > 0) {
1686 if (*verp == IPADM_DB_VERSION)
1687 bval = B_FALSE;
1690 * 'datastore_version' doesn't exist. Which means that we need to
1691 * upgrade the datastore. We will create 'datastore_version' and set
1692 * the version value to IPADM_DB_VERSION, after we upgrade the file.
1694 return (bval);
1698 * This is called after the successful upgrade of the local data-store. With
1699 * the data-store upgraded to recent version we don't have to do anything on
1700 * subsequent reboots.
1702 void
1703 ipmgmt_update_dbver(scf_resources_t *res)
1705 int64_t version = IPADM_DB_VERSION;
1707 (void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
1708 IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);