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