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-2017, 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'.
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)
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);
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;
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 {
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))
1349 (void) memset(buf, 0, buflen);
1350 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
1351 /* buffer overflow */
1352 *errp = ENOBUFS;
1353 }
1354 return (B_TRUE);
1355 }
1356
1357 /*
1358 * Called during boot.
1359 *
1360 * Walk through the DB and apply all the global module properties. We plow
1361 * through the DB even if we fail to apply property.
1362 */
1363 /* ARGSUSED */
1364 static boolean_t
1365 ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1366 int *errp)
1367 {
1368 ipadm_handle_t iph = cbarg;
1369 nvpair_t *nvp, *pnvp;
1370 char *strval = NULL, *name, *mod = NULL, *pname;
1371 char tmpstr[IPMGMT_STRSIZE];
1372 uint_t proto;
1373
1374 /*
1375 * We could have used nvl_exists() directly, however we need several
1376 * calls to it and each call traverses the list. Since this codepath
1377 * is exercised during boot, let's traverse the list ourselves and do
1378 * the necessary checks.
1379 */
1380 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1381 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1382 name = nvpair_name(nvp);
1383 if (IPADM_PRIV_NVP(name)) {
1384 if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1385 strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1386 return (B_TRUE);
1387 else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1388 nvpair_value_string(nvp, &mod) != 0)
1389 return (B_TRUE);
1390 } else {
1391 /* possible a property */
1392 pnvp = nvp;
1393 }
1394 }
1395
1396 /* if we are here than we found a global property */
1397 assert(mod != NULL);
1398 assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1399
1400 proto = ipadm_str2proto(mod);
1401 name = nvpair_name(pnvp);
1402 if (nvpair_value_string(pnvp, &strval) == 0) {
1403 if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
1404 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1405 /* private protocol property */
1406 pname = &name[1];
1407 } else if (ipadm_legacy2new_propname(name, tmpstr,
1408 sizeof (tmpstr), &proto) == 0) {
1409 pname = tmpstr;
1410 } else {
1411 pname = name;
1412 }
1413 if (ipadm_set_prop(iph, pname, strval, proto,
1414 IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
1415 ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
1416 pname);
1565 * places it in `pval'.
1566 */
1567 static int
1568 ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1569 void *pval, scf_type_t ptype)
1570 {
1571 ssize_t numvals;
1572 scf_simple_prop_t *prop;
1573
1574 prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
1575 numvals = scf_simple_prop_numvalues(prop);
1576 if (numvals <= 0)
1577 goto ret;
1578 switch (ptype) {
1579 case SCF_TYPE_INTEGER:
1580 *(int64_t **)pval = scf_simple_prop_next_integer(prop);
1581 break;
1582 case SCF_TYPE_ASTRING:
1583 *(char **)pval = scf_simple_prop_next_astring(prop);
1584 break;
1585 }
1586 ret:
1587 scf_simple_prop_free(prop);
1588 return (numvals);
1589 }
1590
1591 /*
1592 * It stores the `pval' for given `pgname'/`pname' property group in SCF.
1593 */
1594 static int
1595 ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1596 void *pval, scf_type_t ptype)
1597 {
1598 scf_error_t err;
1599
1600 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1601 ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
1602 scf_strerror(scf_error()));
1603 return (-1);
1604 }
1687 /*
1688 * 'datastore_version' doesn't exist. Which means that we need to
1689 * upgrade the datastore. We will create 'datastore_version' and set
1690 * the version value to IPADM_DB_VERSION, after we upgrade the file.
1691 */
1692 return (bval);
1693 }
1694
1695 /*
1696 * This is called after the successful upgrade of the local data-store. With
1697 * the data-store upgraded to recent version we don't have to do anything on
1698 * subsequent reboots.
1699 */
1700 void
1701 ipmgmt_update_dbver(scf_resources_t *res)
1702 {
1703 int64_t version = IPADM_DB_VERSION;
1704
1705 (void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
1706 IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);
1707 }
|
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 Nexenta Systems, Inc.
25 * Copyright 2016 Argo Technologie SA.
26 * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
27 */
28
29 /*
30 * Contains DB walker functions, which are of type `db_wfunc_t';
31 *
32 * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
33 * size_t bufsize, int *errp);
34 *
35 * ipadm_rw_db() walks through the data store, one line at a time and calls
36 * these call back functions with:
37 * `cbarg' - callback argument
38 * `db_nvl' - representing a line from DB in nvlist_t form
39 * `buf' - character buffer to hold modified line
40 * `bufsize'- size of the buffer
41 * `errp' - captures any error inside the walker function.
42 *
43 * All the 'write' callback functions modify `db_nvl' based on `cbarg' and
44 * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'.
63 /* SCF related property group names and property names */
64 #define IPMGMTD_APP_PG "ipmgmtd"
65 #define IPMGMTD_PROP_FBD "first_boot_done"
66 #define IPMGMTD_PROP_DBVER "datastore_version"
67 #define IPMGMTD_TRUESTR "true"
68
69 #define ATYPE "_atype" /* name of the address type nvpair */
70 #define FLAGS "_flags" /* name of the flags nvpair */
71
72 /*
73 * flag used by ipmgmt_persist_aobjmap() to indicate address type is
74 * IPADM_ADDR_IPV6_ADDRCONF.
75 */
76 #define IPMGMT_ATYPE_V6ACONF 0x1
77
78 extern pthread_rwlock_t ipmgmt_dbconf_lock;
79
80 /* signifies whether volatile copy of data store is in use */
81 static boolean_t ipmgmt_rdonly_root = B_FALSE;
82
83 typedef int ipmgmt_if_updater_func_t(nvlist_t *, nvpair_t *, uint_t);
84
85 static ipmgmt_if_updater_func_t ipmgmt_if_family_updater;
86 static ipmgmt_if_updater_func_t ipmgmt_if_groupmembers_updater;
87
88 static int ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl);
89
90 typedef struct {
91 const char *name;
92 ipmgmt_if_updater_func_t *func;
93 } ipmgmt_if_updater_ent_t;
94
95 static ipmgmt_if_updater_ent_t ipmgmt_if_updater_ent[] = {
96 {IPADM_NVP_FAMILIES, ipmgmt_if_family_updater},
97 {IPADM_NVP_MIFNAMES, ipmgmt_if_groupmembers_updater},
98 {NULL, NULL}
99 };
100
101 static ipmgmt_if_updater_ent_t *
102 ipmgmt_find_if_field_updater(const char *field_name)
103 {
104 int i;
105
106 for (i = 0; ipmgmt_if_updater_ent[i].name != NULL; i++) {
107 if (strcmp(field_name, ipmgmt_if_updater_ent[i].name) == 0) {
108 break;
109 }
110 }
111
112 return (&ipmgmt_if_updater_ent[i]);
113 }
114
115 static int
116 ipmgmt_if_groupmembers_updater(nvlist_t *db_nvl, nvpair_t *member_nvp,
117 uint_t flags)
118 {
119 char **members;
120 char *member;
121 char *out_members[256];
122 uint_t nelem = 0, cnt = 0;
123 int err;
124
125 if ((err = nvpair_value_string(member_nvp, &member)) != 0)
126 return (err);
127
128 err = nvlist_lookup_string_array(db_nvl, IPADM_NVP_MIFNAMES,
129 &members, &nelem);
130
131 if (err != 0 && (flags & IPMGMT_REMOVE))
132 return (ENOENT);
133
134 while (nelem-- > 0) {
135 if ((flags & IPMGMT_REMOVE) &&
136 (strcmp(member, members[nelem]) == 0))
137 continue;
138
139 if ((out_members[cnt] = strdup(members[nelem])) == NULL) {
140 err = ENOMEM;
141 goto fail;
142 }
143
144 cnt++;
145 }
146
147 if (flags & IPMGMT_APPEND) {
148 if ((out_members[cnt] = strdup(member)) == NULL) {
149 err = ENOMEM;
150 goto fail;
151 }
152 cnt++;
153 }
154
155 if (cnt == 0) {
156 err = nvlist_remove(db_nvl, IPADM_NVP_MIFNAMES,
157 DATA_TYPE_STRING_ARRAY);
158 } else {
159 err = nvlist_add_string_array(db_nvl, IPADM_NVP_MIFNAMES,
160 out_members, cnt);
161 }
162
163 fail:
164 while (cnt--)
165 free(out_members[cnt]);
166
167 return (err);
168 }
169
170 static int
171 ipmgmt_if_family_updater(nvlist_t *db_nvl, nvpair_t *families_nvp, uint_t flags)
172 {
173 uint16_t *families;
174 uint_t nelem = 0;
175 int err;
176
177 if ((err = nvpair_value_uint16_array(families_nvp, &families,
178 &nelem)) != 0)
179 return (err);
180
181 return (ipmgmt_update_family_nvp(db_nvl, families[0], flags));
182 }
183
184 int
185 ipmgmt_update_family_nvp(nvlist_t *nvl, sa_family_t af, uint_t flags)
186 {
187 uint16_t *families = NULL;
188 uint16_t out_families[2];
189 uint_t nelem = 0, cnt;
190 int err;
191
192 err = nvlist_lookup_uint16_array(nvl, IPADM_NVP_FAMILIES,
193 &families, &nelem);
194 if (err != 0 && (flags & IPMGMT_REMOVE)) {
195 return (ENOENT);
196 }
197
198 if (flags & IPMGMT_APPEND) {
199 if (families != NULL) {
200 if (nelem == 2 || families[0] == af) {
201 return (EEXIST);
202 }
203 out_families[0] = families[0];
204 out_families[1] = af;
205 cnt = 2;
206 } else {
207 out_families[0] = af;
208 cnt = 1;
209 }
210 } else {
211 assert(nelem == 1 || nelem == 2);
212 cnt = 0;
213 while (nelem-- > 0) {
214 if (families[nelem] != af) {
215 out_families[cnt] = families[nelem];
216 cnt++;
217 }
218 }
219 }
220
221 if (cnt != 0) {
222 return (nvlist_add_uint16_array(nvl, IPADM_NVP_FAMILIES,
223 out_families, cnt));
224 }
225 return (nvlist_remove(nvl, IPADM_NVP_FAMILIES, DATA_TYPE_UINT16_ARRAY));
226 }
227
228 /*
229 * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
230 * in private nvpairs `proto', `ifname' & `aobjname'.
231 */
232 static boolean_t
233 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
234 const char *aobjname)
235 {
236 char *db_proto = NULL, *db_ifname = NULL;
237 char *db_aobjname = NULL;
238 nvpair_t *nvp;
239 char *name;
240
241 /* walk through db_nvl and retrieve all its private nvpairs */
242 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
243 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
244 name = nvpair_name(nvp);
245 if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
246 (void) nvpair_value_string(nvp, &db_proto);
247 else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
456 } else {
457 buf[0] = '\0';
458 }
459
460 /* stop the search */
461 return (B_FALSE);
462 }
463
464 /*
465 * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
466 * found, when one of the following occurs first.
467 * - the input aobjname matches the db aobjname. Return the db address.
468 * - the input interface matches the db interface. Return all the
469 * matching db lines with addresses.
470 */
471 /* ARGSUSED */
472 boolean_t
473 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
474 int *errp)
475 {
476 ipmgmt_get_cbarg_t *cbarg = arg;
477 char *db_aobjname = NULL;
478 char *db_ifname = NULL;
479 nvlist_t *db_addr = NULL;
480 char name[IPMGMT_STRSIZE];
481 nvpair_t *nvp;
482 boolean_t add_nvl = B_FALSE;
483
484 /* Parse db nvlist */
485 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
486 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
487 if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
488 (void) nvpair_value_nvlist(nvp, &db_addr);
489 else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
490 (void) nvpair_value_string(nvp, &db_ifname);
491 else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
492 (void) nvpair_value_string(nvp, &db_aobjname);
493 }
494
495 if (db_aobjname == NULL) /* Not an address */
496 return (B_TRUE);
682 instrval);
683 if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
684 return (B_FALSE);
685 } else {
686 /* case of in-line update of a db entry */
687 if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
688 return (B_FALSE);
689 }
690
691 (void) memset(buf, 0, buflen);
692 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
693 /* buffer overflow */
694 *errp = ENOBUFS;
695 }
696
697 /* we updated the DB entry, so do not continue */
698 return (B_FALSE);
699 }
700
701 /*
702 * This function is used to update a DB line that describes
703 * an interface, its family and group interface
704 *
705 */
706 boolean_t
707 ipmgmt_db_update_if(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
708 int *errp)
709 {
710 ipadm_dbwrite_cbarg_t *cb = arg;
711 ipmgmt_if_updater_ent_t *updater;
712 nvlist_t *in_nvl = cb->dbw_nvl;
713 uint_t flags = cb->dbw_flags;
714 nvpair_t *nvp;
715 char *name;
716 char *db_ifname;
717 char *gifname = NULL;
718 char *mifname = NULL;
719
720 *errp = 0;
721
722 /* Only one flag */
723 if ((flags & (IPMGMT_APPEND | IPMGMT_REMOVE)) == 0 ||
724 ((flags & IPMGMT_APPEND) && (flags & IPMGMT_REMOVE))) {
725 *errp = EINVAL;
726 return (B_FALSE);
727 }
728
729 if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES))
730 return (B_TRUE);
731
732 if (nvlist_exists(db_nvl, IPADM_NVP_IFCLASS) &&
733 nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
734 nvlist_lookup_string(in_nvl, IPADM_NVP_GIFNAME, &gifname) == 0 &&
735 nvlist_lookup_string(in_nvl, IPADM_NVP_MIFNAMES, &mifname) == 0 &&
736 strcmp(db_ifname, mifname) == 0) {
737 if (flags & IPMGMT_APPEND) {
738 if ((*errp = nvlist_add_string(db_nvl,
739 IPADM_NVP_GIFNAME, gifname)) != 0)
740 return (B_FALSE);
741 } else {
742 if ((*errp = nvlist_remove(db_nvl, IPADM_NVP_GIFNAME,
743 DATA_TYPE_STRING)) != 0)
744 return (B_FALSE);
745 }
746 cb->dbw_flags &= ~IPMGMT_UPDATE_IPMP;
747 goto done;
748 }
749
750 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
751 return (B_TRUE);
752
753 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
754 nvp = nvlist_next_nvpair(in_nvl, nvp)) {
755 name = nvpair_name(nvp);
756 if (strcmp(name, IPADM_NVP_FAMILIES) != 0 &&
757 strcmp(name, IPADM_NVP_MIFNAMES) != 0)
758 continue;
759
760 updater = ipmgmt_find_if_field_updater(name);
761 assert(updater != NULL);
762 *errp = (*updater->func)(db_nvl, nvp, flags);
763 if (*errp != 0)
764 return (B_FALSE);
765 }
766
767 cb->dbw_flags &= ~IPMGMT_UPDATE_IF;
768
769 done:
770 (void) memset(buf, 0, buflen);
771 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
772 *errp = ENOBUFS;
773 return (B_FALSE);
774 }
775
776 /* we finished all operations, so do not continue */
777 if ((cb->dbw_flags & (IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP)) == 0)
778 return (B_FALSE);
779
780 return (B_TRUE);
781 }
782
783 /*
784 * For the given `cbarg->cb_ifname' interface retrieves
785 * the nvlist that represents the persistent interface information
786 * The nvlist contains:
787 * IPADM_NVP_IFNAME
788 * IPADM_NVP_FAMILIES
789 * IPADM_NVP_IF_CLASS
790 *
791 * (used in 'ipadm show-if')
792 */
793 /* ARGSUSED */
794 boolean_t
795 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
796 int *errp)
797 {
798 ipmgmt_get_cbarg_t *cbarg = arg;
799 char *ifname = cbarg->cb_ifname;
800 nvpair_t *nvp;
801 char *db_ifname = NULL;
802 uint16_t *db_families = NULL;
803 uint_t nelem = 0;
804
805 /* Parse db nvlist */
806 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
807 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
808 if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) {
809 (void) nvpair_value_string(nvp, &db_ifname);
810 } else if (strcmp(nvpair_name(nvp), IPADM_NVP_FAMILIES) == 0) {
811 (void) nvpair_value_uint16_array(nvp,
812 &db_families, &nelem);
813 }
814 }
815
816 if (db_ifname == NULL || db_families == NULL)
817 return (B_TRUE);
818
819 if (ifname != NULL && ifname[0] != '\0' &&
820 strcmp(ifname, db_ifname) != 0)
821 return (B_TRUE);
822
823 *errp = nvlist_add_nvlist(cbarg->cb_onvl, db_ifname, db_nvl);
824 if (*errp == 0)
825 cbarg->cb_ocnt++;
826
827 if (ifname != NULL && ifname[0] != '\0')
828 return (B_FALSE);
829
830 return (B_TRUE);
831 }
832
833 /*
834 * Deletes those entries from the database for which interface name
835 * matches with the given `cbarg->cb_ifname'
836 */
837 /* ARGSUSED */
838 boolean_t
839 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
840 int *errp)
841 {
842 ipmgmt_if_cbarg_t *cbarg = arg;
843 boolean_t isv6 = (cbarg->cb_family == AF_INET6);
844 char *ifname = cbarg->cb_ifname;
845 char *modstr = NULL;
846 char *aobjname;
847 uint_t proto;
848 ipmgmt_aobjmap_t *head;
849 boolean_t aobjfound = B_FALSE;
850
851 *errp = 0;
852
853 if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
854 return (B_TRUE);
855
856 if (nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
857
858 if ((*errp = ipmgmt_update_family_nvp(db_nvl, cbarg->cb_family,
859 IPMGMT_REMOVE)) != 0) {
860 return (B_FALSE);
861 }
862
863 if (cbarg->cb_family == AF_INET) {
864 cbarg->cb_ipv4exists = B_FALSE;
865 } else {
866 assert(cbarg->cb_family == AF_INET6);
867 cbarg->cb_ipv6exists = B_FALSE;
868 }
869 if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) {
870 cbarg->cb_ipv4exists = B_FALSE;
871 cbarg->cb_ipv6exists = B_FALSE;
872 goto delete;
873 }
874 /* Otherwise need to reconstruct this string */
875 (void) memset(buf, 0, buflen);
876 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
877 /* buffer overflow */
878 *errp = ENOBUFS;
879 return (B_FALSE);
880 }
881 return (B_TRUE);
882 }
883
884 /* Reset all the interface configurations for 'ifname' */
885 if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
886 nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
887 goto delete;
888 }
889 if (!isv6 &&
890 (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
891 nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
892 goto delete;
893 }
894
895 if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
896 /*
897 * This must be an address property. Delete this
898 * line if there is a match in the address family.
899 */
900 head = aobjmap.aobjmap_head;
916 }
917
918 /*
919 * If we are removing both v4 and v6 interface, then we get rid of
920 * all the properties for that interface. On the other hand, if we
921 * are deleting only v4 instance of an interface, then we delete v4
922 * properties only.
923 */
924 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
925 proto = ipadm_str2proto(modstr);
926 switch (proto) {
927 case MOD_PROTO_IPV6:
928 if (isv6)
929 goto delete;
930 break;
931 case MOD_PROTO_IPV4:
932 if (!isv6)
933 goto delete;
934 break;
935 case MOD_PROTO_IP:
936 if (!cbarg->cb_ipv4exists && !cbarg->cb_ipv6exists)
937 goto delete;
938 break;
939 }
940 }
941 /* Not found a match yet. Continue processing the db */
942 return (B_TRUE);
943 delete:
944 /* delete the line from the db */
945 buf[0] = '\0';
946 return (B_TRUE);
947 }
948
949 /*
950 * Deletes those entries from the database for which address object name
951 * matches with the given `cbarg->cb_aobjname'
952 */
953 /* ARGSUSED */
954 boolean_t
955 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
956 int *errp)
957 {
1117 return (EEXIST);
1118 }
1119 nodep->am_nextnum = nextnum;
1120 }
1121 return (i_ipmgmt_add_amnode(nodep));
1122 }
1123
1124 /*
1125 * Performs following operations on the global `aobjmap' linked list.
1126 * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
1127 * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
1128 * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
1129 * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
1130 */
1131 int
1132 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
1133 {
1134 ipmgmt_aobjmap_t *head, *prev, *matched = NULL;
1135 boolean_t update = B_TRUE;
1136 int err = 0;
1137 ipadm_db_op_t db_op = IPADM_DB_READ;
1138
1139 (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
1140
1141 head = aobjmap.aobjmap_head;
1142 switch (op) {
1143 case ADDROBJ_ADD:
1144 /*
1145 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
1146 * update, else add the new node.
1147 */
1148 for (; head != NULL; head = head->am_next) {
1149 /*
1150 * For IPv6, we need to distinguish between the
1151 * linklocal and non-linklocal nodes
1152 */
1153 if (strcmp(head->am_aobjname,
1154 nodep->am_aobjname) == 0 &&
1155 (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
1156 head->ipmgmt_am_linklocal ==
1157 nodep->ipmgmt_am_linklocal))
1593 (void) memset(buf, 0, buflen);
1594 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
1595 /* buffer overflow */
1596 *errp = ENOBUFS;
1597 }
1598 return (B_TRUE);
1599 }
1600
1601 /*
1602 * Called during boot.
1603 *
1604 * Walk through the DB and apply all the global module properties. We plow
1605 * through the DB even if we fail to apply property.
1606 */
1607 /* ARGSUSED */
1608 static boolean_t
1609 ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1610 int *errp)
1611 {
1612 ipadm_handle_t iph = cbarg;
1613 nvpair_t *nvp, *pnvp = NULL;
1614 char *strval = NULL, *name, *mod = NULL, *pname;
1615 char tmpstr[IPMGMT_STRSIZE];
1616 uint_t proto;
1617
1618 /*
1619 * We could have used nvl_exists() directly, however we need several
1620 * calls to it and each call traverses the list. Since this codepath
1621 * is exercised during boot, let's traverse the list ourselves and do
1622 * the necessary checks.
1623 */
1624 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1625 nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1626 name = nvpair_name(nvp);
1627 if (IPADM_PRIV_NVP(name)) {
1628 if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1629 strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1630 return (B_TRUE);
1631 else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1632 nvpair_value_string(nvp, &mod) != 0)
1633 return (B_TRUE);
1634 } else {
1635 /* possible a property */
1636 pnvp = nvp;
1637 }
1638 }
1639
1640 /* If we are here then we have found a global property */
1641 assert(mod != NULL);
1642 assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1643
1644 proto = ipadm_str2proto(mod);
1645 name = nvpair_name(pnvp);
1646 if (nvpair_value_string(pnvp, &strval) == 0) {
1647 if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
1648 strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1649 /* private protocol property */
1650 pname = &name[1];
1651 } else if (ipadm_legacy2new_propname(name, tmpstr,
1652 sizeof (tmpstr), &proto) == 0) {
1653 pname = tmpstr;
1654 } else {
1655 pname = name;
1656 }
1657 if (ipadm_set_prop(iph, pname, strval, proto,
1658 IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
1659 ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
1660 pname);
1809 * places it in `pval'.
1810 */
1811 static int
1812 ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1813 void *pval, scf_type_t ptype)
1814 {
1815 ssize_t numvals;
1816 scf_simple_prop_t *prop;
1817
1818 prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
1819 numvals = scf_simple_prop_numvalues(prop);
1820 if (numvals <= 0)
1821 goto ret;
1822 switch (ptype) {
1823 case SCF_TYPE_INTEGER:
1824 *(int64_t **)pval = scf_simple_prop_next_integer(prop);
1825 break;
1826 case SCF_TYPE_ASTRING:
1827 *(char **)pval = scf_simple_prop_next_astring(prop);
1828 break;
1829 default:
1830 break;
1831 }
1832 ret:
1833 scf_simple_prop_free(prop);
1834 return (numvals);
1835 }
1836
1837 /*
1838 * It stores the `pval' for given `pgname'/`pname' property group in SCF.
1839 */
1840 static int
1841 ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1842 void *pval, scf_type_t ptype)
1843 {
1844 scf_error_t err;
1845
1846 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1847 ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
1848 scf_strerror(scf_error()));
1849 return (-1);
1850 }
1933 /*
1934 * 'datastore_version' doesn't exist. Which means that we need to
1935 * upgrade the datastore. We will create 'datastore_version' and set
1936 * the version value to IPADM_DB_VERSION, after we upgrade the file.
1937 */
1938 return (bval);
1939 }
1940
1941 /*
1942 * This is called after the successful upgrade of the local data-store. With
1943 * the data-store upgraded to recent version we don't have to do anything on
1944 * subsequent reboots.
1945 */
1946 void
1947 ipmgmt_update_dbver(scf_resources_t *res)
1948 {
1949 int64_t version = IPADM_DB_VERSION;
1950
1951 (void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
1952 IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);
1953 }
1954
1955 /*
1956 * Return TRUE if `ifname' has persistent configuration for the `af' address
1957 * family in the datastore.
1958 * It is possible to call the function with af == AF_UNSPEC, so in this case
1959 * the function returns TRUE if either AF_INET or AF_INET6 interface exists
1960 */
1961 boolean_t
1962 ipmgmt_persist_if_exists(const char *ifname, sa_family_t af)
1963 {
1964 boolean_t exists = B_FALSE;
1965 nvlist_t *if_info_nvl;
1966 uint16_t *families = NULL;
1967 sa_family_t af_db;
1968 uint_t nelem = 0;
1969
1970 if (ipmgmt_get_ifinfo_nvl(ifname, &if_info_nvl) != 0)
1971 goto done;
1972
1973 if (nvlist_lookup_uint16_array(if_info_nvl, IPADM_NVP_FAMILIES,
1974 &families, &nelem) != 0)
1975 goto done;
1976
1977 while (nelem-- > 0) {
1978 af_db = families[nelem];
1979 if (af_db == af || (af == AF_UNSPEC &&
1980 (af_db == AF_INET || af_db == AF_INET6))) {
1981 exists = B_TRUE;
1982 break;
1983 }
1984 }
1985
1986 done:
1987 if (if_info_nvl != NULL)
1988 nvlist_free(if_info_nvl);
1989
1990 return (exists);
1991 }
1992
1993 /*
1994 * Retrieves the membership information for the requested mif_name
1995 * if mif_name is a memeber of a IPMP group, then gif_name will contain
1996 * the name of IPMP group interface, otherwise the variable will be empty
1997 */
1998 void
1999 ipmgmt_get_group_interface(const char *mif_name, char *gif_name, size_t size)
2000 {
2001 char *gif_name_from_nvl;
2002 nvlist_t *if_info_nvl;
2003
2004 gif_name[0] = '\0';
2005
2006 if (ipmgmt_get_ifinfo_nvl(mif_name, &if_info_nvl) != 0)
2007 goto done;
2008
2009 if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_GIFNAME,
2010 &gif_name_from_nvl) != 0)
2011 goto done;
2012
2013 (void) strlcpy(gif_name, gif_name_from_nvl, size);
2014
2015 done:
2016 if (if_info_nvl != NULL)
2017 nvlist_free(if_info_nvl);
2018 }
2019
2020 static int
2021 ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl)
2022 {
2023 ipmgmt_get_cbarg_t cbarg;
2024 nvpair_t *nvp;
2025 nvlist_t *nvl;
2026 int err;
2027
2028 cbarg.cb_ifname = NULL;
2029 cbarg.cb_aobjname = NULL;
2030 cbarg.cb_ocnt = 0;
2031
2032 if ((err = nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0)) != 0)
2033 goto done;
2034
2035 err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ);
2036 if (err == ENOENT && cbarg.cb_ocnt > 0)
2037 err = 0;
2038
2039 if (err != 0)
2040 goto done;
2041
2042 for (nvp = nvlist_next_nvpair(cbarg.cb_onvl, NULL); nvp != NULL;
2043 nvp = nvlist_next_nvpair(cbarg.cb_onvl, nvp)) {
2044
2045 if (strcmp(nvpair_name(nvp), ifname) != 0)
2046 continue;
2047
2048 if ((err = nvpair_value_nvlist(nvp, &nvl)) != 0 ||
2049 (err = nvlist_dup(nvl, if_info_nvl, NV_UNIQUE_NAME)) != 0)
2050 *if_info_nvl = NULL;
2051
2052 break;
2053 }
2054
2055 done:
2056 nvlist_free(cbarg.cb_onvl);
2057
2058 return (err);
2059 }
|