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