dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / lib / ipmgmtd / ipmgmt_persist.c
blob09fe54e5acf3a0a06d39200e561c2ad273b1294d
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 2016 Argo Technologie SA.
28 * Contains DB walker functions, which are of type `db_wfunc_t';
30 * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
31 * size_t bufsize, int *errp);
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.
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.
46 * All the 'read' callback functions, retrieve the information from the DB, by
47 * reading `db_nvl' and then populate the `cbarg'.
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"
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"
67 #define ATYPE "_atype" /* name of the address type nvpair */
68 #define FLAGS "_flags" /* name of the flags nvpair */
71 * flag used by ipmgmt_persist_aobjmap() to indicate address type is
72 * IPADM_ADDR_IPV6_ADDRCONF.
74 #define IPMGMT_ATYPE_V6ACONF 0x1
76 extern pthread_rwlock_t ipmgmt_dbconf_lock;
78 /* signifies whether volatile copy of data store is in use */
79 static boolean_t ipmgmt_rdonly_root = B_FALSE;
82 * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
83 * in private nvpairs `proto', `ifname' & `aobjname'.
85 static boolean_t
86 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
87 const char *aobjname)
89 char *db_proto = NULL, *db_ifname = NULL;
90 char *db_aobjname = NULL;
91 nvpair_t *nvp;
92 char *name;
94 /* walk through db_nvl and retrieve all its private nvpairs */
95 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
96 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
97 name = nvpair_name(nvp);
98 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
99 (void) nvpair_value_string(nvp, &db_proto);
100 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
101 (void) nvpair_value_string(nvp, &db_ifname);
102 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
103 (void) nvpair_value_string(nvp, &db_aobjname);
106 if (proto != NULL && proto[0] == '\0')
107 proto = NULL;
108 if (ifname != NULL && ifname[0] == '\0')
109 ifname = NULL;
110 if (aobjname != NULL && aobjname[0] == '\0')
111 aobjname = NULL;
113 if ((proto == NULL && db_proto != NULL) ||
114 (proto != NULL && db_proto == NULL) ||
115 (proto != NULL && db_proto != NULL &&
116 strcmp(proto, db_proto) != 0)) {
117 /* no intersection - different protocols. */
118 return (B_FALSE);
120 if ((ifname == NULL && db_ifname != NULL) ||
121 (ifname != NULL && db_ifname == NULL) ||
122 (ifname != NULL && db_ifname != NULL &&
123 strcmp(ifname, db_ifname) != 0)) {
124 /* no intersection - different interfaces. */
125 return (B_FALSE);
127 if ((aobjname == NULL && db_aobjname != NULL) ||
128 (aobjname != NULL && db_aobjname == NULL) ||
129 (aobjname != NULL && db_aobjname != NULL &&
130 strcmp(aobjname, db_aobjname) != 0)) {
131 /* no intersection - different address objects */
132 return (B_FALSE);
135 return (B_TRUE);
139 * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
141 static boolean_t
142 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
144 nvpair_t *nvp;
145 char *name;
146 char *proto = NULL, *ifname = NULL, *aobjname = NULL;
148 /* walk through in_nvl and retrieve all its private nvpairs */
149 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
150 nvp = nvlist_next_nvpair(in_nvl, nvp)) {
151 name = nvpair_name(nvp);
152 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
153 (void) nvpair_value_string(nvp, &proto);
154 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
155 (void) nvpair_value_string(nvp, &ifname);
156 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
157 (void) nvpair_value_string(nvp, &aobjname);
160 return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
164 * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
165 * in private nvpairs `proto', `ifname' & `aobjname'.
167 static boolean_t
168 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
169 const char *ifname, char *aobjname)
171 char *db_ifname = NULL, *db_proto = NULL;
172 char *db_aobjname = NULL;
173 nvpair_t *nvp;
174 char *name;
176 /* walk through db_nvl and retrieve all private nvpairs */
177 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
178 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
179 name = nvpair_name(nvp);
180 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
181 (void) nvpair_value_string(nvp, &db_proto);
182 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
183 (void) nvpair_value_string(nvp, &db_ifname);
184 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
185 (void) nvpair_value_string(nvp, &db_aobjname);
188 if (proto != NULL && proto[0] != '\0') {
189 if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
190 return (B_FALSE);
192 if (ifname != NULL && ifname[0] != '\0') {
193 if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
194 return (B_FALSE);
196 if (aobjname != NULL && aobjname[0] != '\0') {
197 if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
198 return (B_FALSE);
201 return (B_TRUE);
205 * Retrieves the property value from the DB. The property whose value is to be
206 * retrieved is in `pargp->ia_pname'.
208 /* ARGSUSED */
209 boolean_t
210 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
211 int *errp)
213 ipmgmt_prop_arg_t *pargp = arg;
214 boolean_t cont = B_TRUE;
215 char *pval;
216 int err = 0;
218 *errp = 0;
220 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
221 pargp->ia_ifname, pargp->ia_aobjname))
222 return (B_TRUE);
224 if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
225 &pval)) == 0) {
226 (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
228 * We have retrieved what we are looking for.
229 * Stop the walker.
231 cont = B_FALSE;
232 } else {
233 if (err == ENOENT)
234 err = 0;
235 *errp = err;
238 return (cont);
242 * Removes the property value from the DB. The property whose value is to be
243 * removed is in `pargp->ia_pname'.
245 /* ARGSUSED */
246 boolean_t
247 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
248 int *errp)
250 ipmgmt_prop_arg_t *pargp = arg;
252 *errp = 0;
253 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
254 pargp->ia_ifname, pargp->ia_aobjname))
255 return (B_TRUE);
257 if (!nvlist_exists(db_nvl, pargp->ia_pname))
258 return (B_TRUE);
261 * We found the property in the DB. If IPMGMT_REMOVE is not set then
262 * delete the entry from the db. If it is set, then the property is a
263 * multi-valued property so just remove the specified values from DB.
265 if (pargp->ia_flags & IPMGMT_REMOVE) {
266 char *dbpval = NULL;
267 char *inpval = pargp->ia_pval;
268 char pval[MAXPROPVALLEN];
269 char *val, *lasts;
271 *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
272 if (*errp != 0)
273 return (B_FALSE);
276 * multi-valued properties are represented as comma separated
277 * values. Use string tokenizer functions to split them and
278 * search for the value to be removed.
280 bzero(pval, sizeof (pval));
281 if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
282 if (strcmp(val, inpval) != 0)
283 (void) strlcat(pval, val, MAXPROPVALLEN);
284 while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
285 if (strcmp(val, inpval) != 0) {
286 if (pval[0] != '\0')
287 (void) strlcat(pval, ",",
288 MAXPROPVALLEN);
289 (void) strlcat(pval, val,
290 MAXPROPVALLEN);
293 } else {
294 if (strcmp(dbpval, inpval) != 0)
295 *errp = ENOENT;
296 else
297 buf[0] = '\0';
298 return (B_FALSE);
300 *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
301 if (*errp != 0)
302 return (B_FALSE);
304 (void) memset(buf, 0, buflen);
305 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
306 /* buffer overflow */
307 *errp = ENOBUFS;
309 } else {
310 buf[0] = '\0';
313 /* stop the search */
314 return (B_FALSE);
318 * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
319 * found, when one of the following occurs first.
320 * - the input aobjname matches the db aobjname. Return the db address.
321 * - the input interface matches the db interface. Return all the
322 * matching db lines with addresses.
324 /* ARGSUSED */
325 boolean_t
326 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
327 int *errp)
329 ipmgmt_getaddr_cbarg_t *cbarg = arg;
330 char *db_aobjname = NULL;
331 char *db_ifname = NULL;
332 nvlist_t *db_addr = NULL;
333 char name[IPMGMT_STRSIZE];
334 nvpair_t *nvp;
335 boolean_t add_nvl = B_FALSE;
337 /* Parse db nvlist */
338 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
339 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
340 if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
341 (void) nvpair_value_nvlist(nvp, &db_addr);
342 else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
343 (void) nvpair_value_string(nvp, &db_ifname);
344 else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
345 (void) nvpair_value_string(nvp, &db_aobjname);
348 if (db_aobjname == NULL) /* Not an address */
349 return (B_TRUE);
351 /* Check for a match between the aobjnames or the interface name */
352 if (cbarg->cb_aobjname[0] != '\0') {
353 if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
354 add_nvl = B_TRUE;
355 } else if (cbarg->cb_ifname[0] != '\0') {
356 if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
357 add_nvl = B_TRUE;
358 } else {
359 add_nvl = B_TRUE;
362 if (add_nvl) {
363 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
364 cbarg->cb_ocnt);
365 *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
366 if (*errp == 0)
367 cbarg->cb_ocnt++;
369 return (B_TRUE);
373 * This function only gets called if a volatile filesystem version
374 * of the configuration file has been created. This only happens in the
375 * extremely rare case that a request has been made to update the configuration
376 * file at boottime while the root filesystem was read-only. This is
377 * really a rare occurrence now that we don't support UFS root filesystems
378 * any longer. This function will periodically attempt to write the
379 * configuration back to its location on the root filesystem. Success
380 * will indicate that the filesystem is no longer read-only.
382 /* ARGSUSED */
383 static void *
384 ipmgmt_db_restore_thread(void *arg)
386 int err;
388 for (;;) {
389 (void) sleep(5);
390 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
391 if (!ipmgmt_rdonly_root)
392 break;
393 err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE);
394 if (err == 0) {
395 ipmgmt_rdonly_root = B_FALSE;
396 break;
398 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
400 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
401 return (NULL);
405 * This function takes the appropriate lock, read or write, based on the
406 * `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated
407 * by the fact that we are not always guaranteed to have a writable root
408 * filesystem since it is possible that we are reading or writing during
409 * bootime while the root filesystem is still read-only. This is, by far,
410 * the exception case. Normally, this function will be called when the
411 * root filesystem is writable. In the unusual case where this is not
412 * true, the configuration file is copied to the volatile file system
413 * and is updated there until the root filesystem becomes writable. At
414 * that time the file will be moved back to its proper location by
415 * ipmgmt_db_restore_thread().
417 extern int
418 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
420 int err;
421 boolean_t writeop;
422 mode_t mode;
423 pthread_t tid;
424 pthread_attr_t attr;
426 writeop = (db_op != IPADM_DB_READ);
427 if (writeop) {
428 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
429 mode = IPADM_FILE_MODE;
430 } else {
431 (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
432 mode = 0;
436 * Did a previous write attempt fail? If so, don't even try to
437 * read/write to IPADM_DB_FILE.
439 if (!ipmgmt_rdonly_root) {
440 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE,
441 mode, db_op);
442 if (err != EROFS)
443 goto done;
447 * If we haven't already copied the file to the volatile
448 * file system, do so. This should only happen on a failed
449 * writeop(i.e., we have acquired the write lock above).
451 if (access(IPADM_VOL_DB_FILE, F_OK) != 0) {
452 assert(writeop);
453 err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE);
454 if (err != 0)
455 goto done;
456 (void) pthread_attr_init(&attr);
457 (void) pthread_attr_setdetachstate(&attr,
458 PTHREAD_CREATE_DETACHED);
459 err = pthread_create(&tid, &attr, ipmgmt_db_restore_thread,
460 NULL);
461 (void) pthread_attr_destroy(&attr);
462 if (err != 0) {
463 (void) unlink(IPADM_VOL_DB_FILE);
464 goto done;
466 ipmgmt_rdonly_root = B_TRUE;
470 * Read/write from the volatile copy.
472 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE,
473 mode, db_op);
474 done:
475 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
476 return (err);
480 * Used to add an entry towards the end of DB. It just returns B_TRUE for
481 * every line of the DB. When we reach the end, ipadm_rw_db() adds the
482 * line at the end.
484 /* ARGSUSED */
485 boolean_t
486 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
488 return (B_TRUE);
492 * This function is used to update or create an entry in DB. The nvlist_t,
493 * `in_nvl', represents the line we are looking for. Once we ensure the right
494 * line from DB, we update that entry.
496 boolean_t
497 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
498 int *errp)
500 ipadm_dbwrite_cbarg_t *cb = arg;
501 uint_t flags = cb->dbw_flags;
502 nvlist_t *in_nvl = cb->dbw_nvl;
503 nvpair_t *nvp;
504 char *name, *instrval = NULL, *dbstrval = NULL;
505 char pval[MAXPROPVALLEN];
507 *errp = 0;
508 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
509 return (B_TRUE);
511 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
512 nvp = nvlist_next_nvpair(in_nvl, nvp)) {
513 name = nvpair_name(nvp);
514 if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
515 break;
518 if (nvp == NULL)
519 return (B_TRUE);
521 assert(nvpair_type(nvp) == DATA_TYPE_STRING);
523 if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
524 return (B_FALSE);
527 * If IPMGMT_APPEND is set then we are dealing with multi-valued
528 * properties. We append to the entry from the db, with the new value.
530 if (flags & IPMGMT_APPEND) {
531 if ((*errp = nvlist_lookup_string(db_nvl, name,
532 &dbstrval)) != 0)
533 return (B_FALSE);
534 (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
535 instrval);
536 if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
537 return (B_FALSE);
538 } else {
539 /* case of in-line update of a db entry */
540 if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
541 return (B_FALSE);
544 (void) memset(buf, 0, buflen);
545 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
546 /* buffer overflow */
547 *errp = ENOBUFS;
550 /* we updated the DB entry, so do not continue */
551 return (B_FALSE);
555 * For the given `cbarg->cb_ifname' interface, retrieves any persistent
556 * interface information (used in 'ipadm show-if')
558 /* ARGSUSED */
559 boolean_t
560 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
561 int *errp)
563 ipmgmt_getif_cbarg_t *cbarg = arg;
564 char *ifname = cbarg->cb_ifname;
565 char *intf = NULL;
566 ipadm_if_info_t *ifp = NULL;
567 sa_family_t af;
568 char *afstr;
570 *errp = 0;
571 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 ||
572 nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 ||
573 (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) {
574 return (B_TRUE);
576 af = atoi(afstr);
577 for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) {
578 if (strcmp(ifp->ifi_name, intf) == 0)
579 break;
581 if (ifp == NULL) {
582 ipadm_if_info_t *new;
584 if ((new = calloc(1, sizeof (*new))) == NULL) {
585 *errp = ENOMEM;
586 return (B_FALSE); /* don't continue the walk */
588 new->ifi_next = cbarg->cb_ifinfo;
589 cbarg->cb_ifinfo = new;
590 ifp = new;
591 (void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name));
594 if (af == AF_INET) {
595 ifp->ifi_pflags |= IFIF_IPV4;
596 } else {
597 assert(af == AF_INET6);
598 ifp->ifi_pflags |= IFIF_IPV6;
601 /* Terminate the walk if we found both v4 and v6 interfaces. */
602 if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) &&
603 (ifp->ifi_pflags & IFIF_IPV6))
604 return (B_FALSE);
606 return (B_TRUE);
610 * Deletes those entries from the database for which interface name
611 * matches with the given `cbarg->cb_ifname'
613 /* ARGSUSED */
614 boolean_t
615 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
616 int *errp)
618 ipmgmt_if_cbarg_t *cbarg = arg;
619 boolean_t isv6 = (cbarg->cb_family == AF_INET6);
620 char *ifname = cbarg->cb_ifname;
621 char *modstr = NULL;
622 char *afstr;
623 char *aobjname;
624 uint_t proto;
625 ipmgmt_aobjmap_t *head;
626 boolean_t aobjfound = B_FALSE;
628 *errp = 0;
630 if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
631 return (B_TRUE);
633 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
634 if (atoi(afstr) == cbarg->cb_family)
635 goto delete;
636 return (B_TRUE);
639 /* Reset all the interface configurations for 'ifname' */
640 if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
641 nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
642 goto delete;
644 if (!isv6 &&
645 (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
646 nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
647 goto delete;
650 if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
652 * This must be an address property. Delete this
653 * line if there is a match in the address family.
655 head = aobjmap.aobjmap_head;
656 while (head != NULL) {
657 if (strcmp(head->am_aobjname, aobjname) == 0) {
658 aobjfound = B_TRUE;
659 if (head->am_family == cbarg->cb_family)
660 goto delete;
662 head = head->am_next;
665 * If aobjfound = B_FALSE, then this address is not
666 * available in active configuration. We should go ahead
667 * and delete it.
669 if (!aobjfound)
670 goto delete;
674 * If we are removing both v4 and v6 interface, then we get rid of
675 * all the properties for that interface. On the other hand, if we
676 * are deleting only v4 instance of an interface, then we delete v4
677 * properties only.
679 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
680 proto = ipadm_str2proto(modstr);
681 switch (proto) {
682 case MOD_PROTO_IPV6:
683 if (isv6)
684 goto delete;
685 break;
686 case MOD_PROTO_IPV4:
687 if (!isv6)
688 goto delete;
689 break;
690 case MOD_PROTO_IP:
691 /* this should never be the case, today */
692 assert(0);
693 break;
696 /* Not found a match yet. Continue processing the db */
697 return (B_TRUE);
698 delete:
699 /* delete the line from the db */
700 buf[0] = '\0';
701 return (B_TRUE);
705 * Deletes those entries from the database for which address object name
706 * matches with the given `cbarg->cb_aobjname'
708 /* ARGSUSED */
709 boolean_t
710 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
711 int *errp)
713 ipmgmt_resetaddr_cbarg_t *cbarg = arg;
714 char *aobjname = cbarg->cb_aobjname;
716 *errp = 0;
717 if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
718 return (B_TRUE);
720 /* delete the line from the db */
721 buf[0] = '\0';
722 return (B_TRUE);
726 * Retrieves all interface props, including addresses, for given interface(s).
727 * `invl' contains the list of interfaces, for which information need to be
728 * retrieved.
730 /* ARGSUSED */
731 boolean_t
732 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
733 int *errp)
735 ipmgmt_initif_cbarg_t *cbarg = arg;
736 nvlist_t *onvl = cbarg->cb_onvl;
737 nvlist_t *invl = cbarg->cb_invl;
738 sa_family_t in_af = cbarg->cb_family;
739 char *db_ifname;
741 *errp = 0;
742 if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
743 nvlist_exists(invl, db_ifname)) {
744 char name[IPMGMT_STRSIZE];
745 sa_family_t db_af = in_af;
746 uint_t proto;
747 char *pstr;
749 if (in_af != AF_UNSPEC) {
750 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
751 &pstr) == 0) {
752 proto = ipadm_str2proto(pstr);
753 if (proto == MOD_PROTO_IPV4)
754 db_af = AF_INET;
755 else if (proto == MOD_PROTO_IPV6)
756 db_af = AF_INET6;
757 else
758 db_af = in_af;
759 } else {
760 if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
761 nvlist_exists(db_nvl, IPADM_NVP_DHCP))
762 db_af = AF_INET;
763 else
764 db_af = AF_INET6;
767 if (in_af == db_af) {
768 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
769 cbarg->cb_ocnt);
770 *errp = nvlist_add_nvlist(onvl, name, db_nvl);
771 if (*errp == 0)
772 cbarg->cb_ocnt++;
775 return (B_TRUE);
779 * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
780 * into `aobjmap' structure.
782 static int
783 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
785 ipmgmt_aobjmap_t *new, *head;
787 head = aobjmap.aobjmap_head;
788 if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
789 return (ENOMEM);
790 *new = *nodep;
791 new->am_next = NULL;
793 /* Add the node at the beginning of the list */
794 if (head == NULL) {
795 aobjmap.aobjmap_head = new;
796 } else {
797 new->am_next = aobjmap.aobjmap_head;
798 aobjmap.aobjmap_head = new;
800 return (0);
804 * A recursive function to generate alphabetized number given a decimal number.
805 * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
806 * 'ab', 'ac', et al.
808 static void
809 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
811 if (num >= 26)
812 i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
813 if (*cp != endp) {
814 *cp[0] = 'a' + (num % 26);
815 (*cp)++;
820 * This function generates an `aobjname', when required, and then does
821 * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
822 * through the `aobjmap' to check if an address object with the same
823 * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
824 * `aobjname's are not allowed.
826 * If `nodep->am_aobjname' is an empty string then the daemon generates an
827 * `aobjname' using the `am_nextnum', which contains the next number to be
828 * used to generate `aobjname'. `am_nextnum' is converted to base26 using
829 * `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
831 * `am_nextnum' will be 0 to begin with. Every time an address object that
832 * needs `aobjname' is added it's incremented by 1. So for the first address
833 * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
834 * For the second address object on that interface `am_aobjname' will be net0/_b
835 * and `am_nextnum' will incremented to 2.
837 static int
838 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
840 ipmgmt_aobjmap_t *head;
841 uint32_t nextnum;
843 for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
844 if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
845 break;
846 nextnum = (head == NULL ? 0 : head->am_nextnum);
849 * if `aobjname' is empty, then the daemon has to generate the
850 * next `aobjname' for the given interface and family.
852 if (nodep->am_aobjname[0] == '\0') {
853 char tmpstr[IPADM_AOBJ_USTRSIZ - 1]; /* 1 for leading '_' */
854 char *cp = tmpstr;
855 char *endp = tmpstr + sizeof (tmpstr);
857 i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
859 if (cp == endp)
860 return (EINVAL);
861 cp[0] = '\0';
863 if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
864 nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
865 return (EINVAL);
867 nodep->am_nextnum = ++nextnum;
868 } else {
869 for (head = aobjmap.aobjmap_head; head != NULL;
870 head = head->am_next) {
871 if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
872 return (EEXIST);
874 nodep->am_nextnum = nextnum;
876 return (i_ipmgmt_add_amnode(nodep));
880 * Performs following operations on the global `aobjmap' linked list.
881 * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
882 * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
883 * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
884 * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
887 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
889 ipmgmt_aobjmap_t *head, *prev, *matched = NULL;
890 boolean_t update = B_TRUE;
891 int err = 0;
892 ipadm_db_op_t db_op;
894 (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
896 head = aobjmap.aobjmap_head;
897 switch (op) {
898 case ADDROBJ_ADD:
900 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
901 * update, else add the new node.
903 for (; head != NULL; head = head->am_next) {
905 * For IPv6, we need to distinguish between the
906 * linklocal and non-linklocal nodes
908 if (strcmp(head->am_aobjname,
909 nodep->am_aobjname) == 0 &&
910 (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
911 head->am_linklocal == nodep->am_linklocal))
912 break;
915 if (head != NULL) {
916 /* update the node */
917 (void) strlcpy(head->am_ifname, nodep->am_ifname,
918 sizeof (head->am_ifname));
919 head->am_lnum = nodep->am_lnum;
920 head->am_family = nodep->am_family;
921 head->am_flags = nodep->am_flags;
922 head->am_atype = nodep->am_atype;
923 if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
924 head->am_ifid = nodep->am_ifid;
925 head->am_linklocal = nodep->am_linklocal;
927 } else {
928 for (head = aobjmap.aobjmap_head; head != NULL;
929 head = head->am_next) {
930 if (strcmp(head->am_ifname,
931 nodep->am_ifname) == 0)
932 break;
934 nodep->am_nextnum = (head == NULL ? 0 :
935 head->am_nextnum);
936 err = i_ipmgmt_add_amnode(nodep);
938 db_op = IPADM_DB_WRITE;
939 break;
940 case ADDROBJ_DELETE:
941 prev = head;
942 while (head != NULL) {
943 if (strcmp(head->am_aobjname,
944 nodep->am_aobjname) == 0) {
945 nodep->am_atype = head->am_atype;
947 * There could be multiple IPV6_ADDRCONF nodes,
948 * with same address object name, so check for
949 * logical number also.
951 if (head->am_atype !=
952 IPADM_ADDR_IPV6_ADDRCONF ||
953 nodep->am_lnum == head->am_lnum)
954 break;
956 prev = head;
957 head = head->am_next;
959 if (head != NULL) {
961 * If the address object is in both active and
962 * persistent configuration and the user is deleting it
963 * only from active configuration then mark this node
964 * for deletion by reseting IPMGMT_ACTIVE bit.
965 * With this the same address object name cannot
966 * be reused until it is permanently removed.
968 if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
969 nodep->am_flags == IPMGMT_ACTIVE) {
970 /* Update flags in the in-memory map. */
971 head->am_flags &= ~IPMGMT_ACTIVE;
972 head->am_lnum = -1;
974 /* Update info in file. */
975 db_op = IPADM_DB_WRITE;
976 *nodep = *head;
977 } else {
978 (void) strlcpy(nodep->am_ifname,
979 head->am_ifname,
980 sizeof (nodep->am_ifname));
981 /* otherwise delete the node */
982 if (head == aobjmap.aobjmap_head)
983 aobjmap.aobjmap_head = head->am_next;
984 else
985 prev->am_next = head->am_next;
986 free(head);
987 db_op = IPADM_DB_DELETE;
989 } else {
990 err = ENOENT;
992 break;
993 case ADDROBJ_LOOKUPADD:
994 err = i_ipmgmt_lookupadd_amnode(nodep);
995 update = B_FALSE;
996 break;
997 case ADDROBJ_SETLIFNUM:
998 update = B_FALSE;
999 for (; head != NULL; head = head->am_next) {
1000 if (strcmp(head->am_ifname,
1001 nodep->am_ifname) == 0 &&
1002 head->am_family == nodep->am_family &&
1003 head->am_lnum == nodep->am_lnum) {
1004 err = EEXIST;
1005 break;
1007 if (strcmp(head->am_aobjname,
1008 nodep->am_aobjname) == 0) {
1009 matched = head;
1012 if (err == EEXIST)
1013 break;
1014 if (matched != NULL) {
1015 /* update the lifnum */
1016 matched->am_lnum = nodep->am_lnum;
1017 } else {
1018 err = ENOENT;
1020 break;
1021 default:
1022 assert(0);
1025 if (err == 0 && update)
1026 err = ipmgmt_persist_aobjmap(nodep, db_op);
1028 (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
1030 return (err);
1034 * Given a node in `aobjmap', this function converts it into nvlist_t structure.
1035 * The content to be written to DB must be represented as nvlist_t.
1037 static int
1038 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
1040 int err;
1041 char strval[IPMGMT_STRSIZE];
1043 *nvl = NULL;
1044 if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
1045 goto fail;
1047 if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
1048 np->am_aobjname)) != 0)
1049 goto fail;
1051 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
1052 np->am_ifname)) != 0)
1053 goto fail;
1055 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
1056 if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
1057 goto fail;
1059 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
1060 if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
1061 goto fail;
1063 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
1064 if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
1065 goto fail;
1067 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
1068 if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
1069 goto fail;
1071 if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1072 struct sockaddr_in6 *in6;
1074 in6 = (struct sockaddr_in6 *)&np->am_ifid;
1075 if (np->am_linklocal &&
1076 IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
1077 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1078 "default")) != 0)
1079 goto fail;
1080 } else {
1081 if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
1082 IPMGMT_STRSIZE) == NULL) {
1083 err = errno;
1084 goto fail;
1086 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1087 strval)) != 0)
1088 goto fail;
1090 } else {
1091 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1092 "")) != 0)
1093 goto fail;
1095 return (err);
1096 fail:
1097 nvlist_free(*nvl);
1098 return (err);
1102 * Read the aobjmap data store and build the in-memory representation
1103 * of the aobjmap. We don't need to hold any locks while building this as
1104 * we do this in very early stage of daemon coming up, even before the door
1105 * is opened.
1107 /* ARGSUSED */
1108 extern boolean_t
1109 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1110 int *errp)
1112 nvpair_t *nvp = NULL;
1113 char *name, *strval = NULL;
1114 ipmgmt_aobjmap_t node;
1115 struct sockaddr_in6 *in6;
1117 *errp = 0;
1118 node.am_next = NULL;
1119 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1120 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1121 name = nvpair_name(nvp);
1123 if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
1124 return (B_TRUE);
1125 if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
1126 (void) strlcpy(node.am_aobjname, strval,
1127 sizeof (node.am_aobjname));
1128 } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
1129 (void) strlcpy(node.am_ifname, strval,
1130 sizeof (node.am_ifname));
1131 } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
1132 node.am_lnum = atoi(strval);
1133 } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
1134 node.am_family = (sa_family_t)atoi(strval);
1135 } else if (strcmp(FLAGS, name) == 0) {
1136 node.am_flags = atoi(strval);
1137 } else if (strcmp(ATYPE, name) == 0) {
1138 node.am_atype = (ipadm_addr_type_t)atoi(strval);
1139 } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
1140 if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1141 in6 = (struct sockaddr_in6 *)&node.am_ifid;
1142 if (strcmp(strval, "default") == 0) {
1143 bzero(in6, sizeof (node.am_ifid));
1144 node.am_linklocal = B_TRUE;
1145 } else {
1146 (void) inet_pton(AF_INET6, strval,
1147 &in6->sin6_addr);
1148 if (IN6_IS_ADDR_UNSPECIFIED(
1149 &in6->sin6_addr))
1150 node.am_linklocal = B_TRUE;
1156 /* we have all the information we need, add the node */
1157 *errp = i_ipmgmt_add_amnode(&node);
1159 return (B_TRUE);
1163 * Updates an entry from the temporary cache file, which matches the given
1164 * address object name.
1166 /* ARGSUSED */
1167 static boolean_t
1168 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1169 size_t buflen, int *errp)
1171 ipadm_dbwrite_cbarg_t *cb = arg;
1172 nvlist_t *in_nvl = cb->dbw_nvl;
1173 uint32_t flags = cb->dbw_flags;
1174 char *db_lifnumstr = NULL, *in_lifnumstr = NULL;
1176 *errp = 0;
1177 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
1178 return (B_TRUE);
1180 if (flags & IPMGMT_ATYPE_V6ACONF) {
1181 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1182 &db_lifnumstr) != 0 ||
1183 nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
1184 &in_lifnumstr) != 0 ||
1185 (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
1186 strcmp(db_lifnumstr, in_lifnumstr) != 0))
1187 return (B_TRUE);
1190 /* we found the match */
1191 (void) memset(buf, 0, buflen);
1192 if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
1193 /* buffer overflow */
1194 *errp = ENOBUFS;
1197 /* stop the walker */
1198 return (B_FALSE);
1202 * Deletes an entry from the temporary cache file, which matches the given
1203 * address object name.
1205 /* ARGSUSED */
1206 static boolean_t
1207 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1208 size_t buflen, int *errp)
1210 ipmgmt_aobjmap_t *nodep = arg;
1211 char *db_lifnumstr = NULL;
1213 *errp = 0;
1214 if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
1215 nodep->am_aobjname))
1216 return (B_TRUE);
1218 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1219 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1220 &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
1221 return (B_TRUE);
1224 /* we found the match, delete the line from the db */
1225 buf[0] = '\0';
1227 /* stop the walker */
1228 return (B_FALSE);
1232 * Adds or deletes aobjmap node information into a temporary cache file.
1234 extern int
1235 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
1237 int err;
1238 ipadm_dbwrite_cbarg_t cb;
1239 nvlist_t *nvl = NULL;
1241 if (op == IPADM_DB_WRITE) {
1242 if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
1243 return (err);
1244 cb.dbw_nvl = nvl;
1245 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
1246 cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
1247 else
1248 cb.dbw_flags = 0;
1250 err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
1251 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
1252 nvlist_free(nvl);
1253 } else {
1254 assert(op == IPADM_DB_DELETE);
1256 err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
1257 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
1259 return (err);
1263 * upgrades the ipadm data-store. It renames all the old private protocol
1264 * property names which start with leading protocol names to begin with
1265 * IPADM_PRIV_PROP_PREFIX.
1267 /* ARGSUSED */
1268 boolean_t
1269 ipmgmt_db_upgrade(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1270 int *errp)
1272 nvpair_t *nvp;
1273 char *name, *pname = NULL, *protostr = NULL, *pval = NULL;
1274 uint_t proto, nproto;
1275 char nname[IPMGMT_STRSIZE], tmpstr[IPMGMT_STRSIZE];
1277 *errp = 0;
1279 * We are interested in lines which contain protocol properties. We
1280 * walk through other lines in the DB.
1282 if (nvlist_exists(db_nvl, IPADM_NVP_IFNAME) ||
1283 nvlist_exists(db_nvl, IPADM_NVP_AOBJNAME)) {
1284 return (B_TRUE);
1286 assert(nvlist_exists(db_nvl, IPADM_NVP_PROTONAME));
1289 * extract the propname from the `db_nvl' and also extract the
1290 * protocol from the `db_nvl'.
1292 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1293 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1294 name = nvpair_name(nvp);
1295 if (strcmp(name, IPADM_NVP_PROTONAME) == 0) {
1296 if (nvpair_value_string(nvp, &protostr) != 0)
1297 return (B_TRUE);
1298 } else {
1299 assert(!IPADM_PRIV_NVP(name));
1300 pname = name;
1301 if (nvpair_value_string(nvp, &pval) != 0)
1302 return (B_TRUE);
1306 /* if the private property is in the right format return */
1307 if (strncmp(pname, IPADM_PERSIST_PRIVPROP_PREFIX,
1308 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1309 return (B_TRUE);
1311 /* if it's a public property move onto the next property */
1312 nproto = proto = ipadm_str2proto(protostr);
1313 if (ipadm_legacy2new_propname(pname, nname, sizeof (nname),
1314 &nproto) != 0) {
1315 return (B_TRUE);
1318 /* replace the old protocol with new protocol, if required */
1319 if (nproto != proto) {
1320 protostr = ipadm_proto2str(nproto);
1321 if (nvlist_add_string(db_nvl, IPADM_NVP_PROTONAME,
1322 protostr) != 0) {
1323 return (B_TRUE);
1327 /* replace the old property name with new property name, if required */
1328 /* add the prefix to property name */
1329 (void) snprintf(tmpstr, sizeof (tmpstr), "_%s", nname);
1330 if (nvlist_add_string(db_nvl, tmpstr, pval) != 0 ||
1331 nvlist_remove(db_nvl, pname, DATA_TYPE_STRING) != 0) {
1332 return (B_TRUE);
1334 (void) memset(buf, 0, buflen);
1335 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
1336 /* buffer overflow */
1337 *errp = ENOBUFS;
1339 return (B_TRUE);
1343 * Called during boot.
1345 * Walk through the DB and apply all the global module properties. We plow
1346 * through the DB even if we fail to apply property.
1348 /* ARGSUSED */
1349 static boolean_t
1350 ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1351 int *errp)
1353 ipadm_handle_t iph = cbarg;
1354 nvpair_t *nvp, *pnvp;
1355 char *strval = NULL, *name, *mod = NULL, *pname;
1356 char tmpstr[IPMGMT_STRSIZE];
1357 uint_t proto;
1360 * We could have used nvl_exists() directly, however we need several
1361 * calls to it and each call traverses the list. Since this codepath
1362 * is exercised during boot, let's traverse the list ourselves and do
1363 * the necessary checks.
1365 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1366 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1367 name = nvpair_name(nvp);
1368 if (IPADM_PRIV_NVP(name)) {
1369 if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1370 strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1371 return (B_TRUE);
1372 else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1373 nvpair_value_string(nvp, &mod) != 0)
1374 return (B_TRUE);
1375 } else {
1376 /* possible a property */
1377 pnvp = nvp;
1381 /* if we are here than we found a global property */
1382 assert(mod != NULL);
1383 assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1385 proto = ipadm_str2proto(mod);
1386 name = nvpair_name(pnvp);
1387 if (nvpair_value_string(pnvp, &strval) == 0) {
1388 if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
1389 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1390 /* private protocol property */
1391 pname = &name[1];
1392 } else if (ipadm_legacy2new_propname(name, tmpstr,
1393 sizeof (tmpstr), &proto) == 0) {
1394 pname = tmpstr;
1395 } else {
1396 pname = name;
1398 if (ipadm_set_prop(iph, pname, strval, proto,
1399 IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
1400 ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
1401 pname);
1405 return (B_TRUE);
1408 /* initialize global module properties */
1409 void
1410 ipmgmt_init_prop()
1412 ipadm_handle_t iph = NULL;
1414 if (ipadm_open(&iph, IPH_INIT) != IPADM_SUCCESS) {
1415 ipmgmt_log(LOG_WARNING, "Could not reapply any of the "
1416 "persisted protocol properties");
1417 return;
1419 /* ipmgmt_db_init() logs warnings if there are any issues */
1420 (void) ipmgmt_db_walk(ipmgmt_db_init, iph, IPADM_DB_READ);
1421 ipadm_close(iph);
1424 void
1425 ipmgmt_release_scf_resources(scf_resources_t *res)
1427 scf_entry_destroy(res->sr_ent);
1428 scf_transaction_destroy(res->sr_tx);
1429 scf_value_destroy(res->sr_val);
1430 scf_property_destroy(res->sr_prop);
1431 scf_pg_destroy(res->sr_pg);
1432 scf_instance_destroy(res->sr_inst);
1433 (void) scf_handle_unbind(res->sr_handle);
1434 scf_handle_destroy(res->sr_handle);
1438 * It creates the necessary SCF handles and binds the given `fmri' to an
1439 * instance. These resources are required for retrieving property value,
1440 * creating property groups and modifying property values.
1443 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
1445 res->sr_tx = NULL;
1446 res->sr_ent = NULL;
1447 res->sr_inst = NULL;
1448 res->sr_pg = NULL;
1449 res->sr_prop = NULL;
1450 res->sr_val = NULL;
1452 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL)
1453 return (-1);
1455 if (scf_handle_bind(res->sr_handle) != 0) {
1456 scf_handle_destroy(res->sr_handle);
1457 return (-1);
1459 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL)
1460 goto failure;
1461 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
1462 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1463 goto failure;
1465 /* we will create the rest of the resources on demand */
1466 return (0);
1468 failure:
1469 ipmgmt_log(LOG_WARNING, "failed to create scf resources: %s",
1470 scf_strerror(scf_error()));
1471 ipmgmt_release_scf_resources(res);
1472 return (-1);
1476 * persists the `pval' for a given property `pname' in SCF. The only supported
1477 * SCF property types are INTEGER and ASTRING.
1479 static int
1480 ipmgmt_set_scfprop_value(scf_resources_t *res, const char *pname, void *pval,
1481 scf_type_t ptype)
1483 int result = -1;
1484 boolean_t new;
1486 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL)
1487 goto failure;
1488 switch (ptype) {
1489 case SCF_TYPE_INTEGER:
1490 scf_value_set_integer(res->sr_val, *(int64_t *)pval);
1491 break;
1492 case SCF_TYPE_ASTRING:
1493 if (scf_value_set_astring(res->sr_val, (char *)pval) != 0) {
1494 ipmgmt_log(LOG_WARNING, "Error setting string value %s "
1495 "for property %s: %s", pval, pname,
1496 scf_strerror(scf_error()));
1497 goto failure;
1499 break;
1500 default:
1501 goto failure;
1504 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL)
1505 goto failure;
1506 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL)
1507 goto failure;
1508 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL)
1509 goto failure;
1511 retry:
1512 new = (scf_pg_get_property(res->sr_pg, pname, res->sr_prop) != 0);
1513 if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1)
1514 goto failure;
1515 if (new) {
1516 if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
1517 pname, ptype) == -1) {
1518 goto failure;
1520 } else {
1521 if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
1522 pname, ptype) == -1) {
1523 goto failure;
1527 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0)
1528 goto failure;
1530 result = scf_transaction_commit(res->sr_tx);
1531 if (result == 0) {
1532 scf_transaction_reset(res->sr_tx);
1533 if (scf_pg_update(res->sr_pg) == -1) {
1534 goto failure;
1536 goto retry;
1538 if (result == -1)
1539 goto failure;
1540 return (0);
1542 failure:
1543 ipmgmt_log(LOG_WARNING, "failed to save the data in SCF: %s",
1544 scf_strerror(scf_error()));
1545 return (-1);
1549 * Given a `pgname'/`pname', it retrieves the value based on `ptype' and
1550 * places it in `pval'.
1552 static int
1553 ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1554 void *pval, scf_type_t ptype)
1556 ssize_t numvals;
1557 scf_simple_prop_t *prop;
1559 prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
1560 numvals = scf_simple_prop_numvalues(prop);
1561 if (numvals <= 0)
1562 goto ret;
1563 switch (ptype) {
1564 case SCF_TYPE_INTEGER:
1565 *(int64_t **)pval = scf_simple_prop_next_integer(prop);
1566 break;
1567 case SCF_TYPE_ASTRING:
1568 *(char **)pval = scf_simple_prop_next_astring(prop);
1569 break;
1571 ret:
1572 scf_simple_prop_free(prop);
1573 return (numvals);
1577 * It stores the `pval' for given `pgname'/`pname' property group in SCF.
1579 static int
1580 ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1581 void *pval, scf_type_t ptype)
1583 scf_error_t err;
1585 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1586 ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
1587 scf_strerror(scf_error()));
1588 return (-1);
1591 if (scf_instance_add_pg(res->sr_inst, pgname, SCF_GROUP_APPLICATION,
1592 0, res->sr_pg) != 0) {
1593 if ((err = scf_error()) != SCF_ERROR_EXISTS) {
1594 ipmgmt_log(LOG_WARNING,
1595 "Error adding property group '%s/%s': %s",
1596 pgname, pname, scf_strerror(err));
1597 return (-1);
1600 * if the property group already exists, then we get the
1601 * composed view of the property group for the given instance.
1603 if (scf_instance_get_pg_composed(res->sr_inst, NULL, pgname,
1604 res->sr_pg) != 0) {
1605 ipmgmt_log(LOG_WARNING, "Error getting composed view "
1606 "of the property group '%s/%s': %s", pgname, pname,
1607 scf_strerror(scf_error()));
1608 return (-1);
1612 return (ipmgmt_set_scfprop_value(res, pname, pval, ptype));
1616 * Returns B_TRUE, if the non-global zone is being booted for the first time
1617 * after being installed. This is required to setup the ipadm data-store for
1618 * the first boot of the non-global zone. Please see, PSARC 2010/166,
1619 * for more info.
1621 * Note that, this API cannot be used to determine first boot post image-update.
1622 * 'pkg image-update' clones the current BE and the existing value of
1623 * ipmgmtd/first_boot_done will be carried forward and obviously it will be set
1624 * to B_TRUE.
1626 boolean_t
1627 ipmgmt_ngz_firstboot_postinstall()
1629 scf_resources_t res;
1630 boolean_t bval = B_TRUE;
1631 char *strval;
1633 /* we always err on the side of caution */
1634 if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
1635 return (bval);
1637 if (ipmgmt_get_scfprop(&res, IPMGMTD_APP_PG, IPMGMTD_PROP_FBD, &strval,
1638 SCF_TYPE_ASTRING) > 0) {
1639 bval = (strcmp(strval, IPMGMTD_TRUESTR) == 0 ?
1640 B_FALSE : B_TRUE);
1641 } else {
1643 * IPMGMTD_PROP_FBD does not exist in the SCF. Lets create it.
1644 * Since we err on the side of caution, we ignore the return
1645 * error and return B_TRUE.
1647 (void) ipmgmt_set_scfprop(&res, IPMGMTD_APP_PG,
1648 IPMGMTD_PROP_FBD, IPMGMTD_TRUESTR, SCF_TYPE_ASTRING);
1650 ipmgmt_release_scf_resources(&res);
1651 return (bval);
1655 * Returns B_TRUE, if the data-store needs upgrade otherwise returns B_FALSE.
1656 * Today we have to take care of, one case of, upgrading from version 0 to
1657 * version 1, so we will use boolean_t as means to decide if upgrade is needed
1658 * or not. Further, the upcoming projects might completely move the flatfile
1659 * data-store into SCF and hence we shall keep this interface simple.
1661 boolean_t
1662 ipmgmt_needs_upgrade(scf_resources_t *res)
1664 boolean_t bval = B_TRUE;
1665 int64_t *verp;
1667 if (ipmgmt_get_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER,
1668 &verp, SCF_TYPE_INTEGER) > 0) {
1669 if (*verp == IPADM_DB_VERSION)
1670 bval = B_FALSE;
1673 * 'datastore_version' doesn't exist. Which means that we need to
1674 * upgrade the datastore. We will create 'datastore_version' and set
1675 * the version value to IPADM_DB_VERSION, after we upgrade the file.
1677 return (bval);
1681 * This is called after the successful upgrade of the local data-store. With
1682 * the data-store upgraded to recent version we don't have to do anything on
1683 * subsequent reboots.
1685 void
1686 ipmgmt_update_dbver(scf_resources_t *res)
1688 int64_t version = IPADM_DB_VERSION;
1690 (void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
1691 IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);