diff options
author | Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> | 2021-08-04 15:22:53 +0200 |
---|---|---|
committer | Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> | 2021-11-22 12:30:03 +0100 |
commit | a73be61a80f7331c35adfa540bcf8f1546ff1e33 (patch) | |
tree | c0c72232c78479d3333c9ec6efdc60f0e75f6784 /usr/src/cmd/cmd-inet/lib | |
parent | f81209f5137586c57e31f7d74b929149299d9b3c (diff) | |
download | illumos-gate-a73be61a80f7331c35adfa540bcf8f1546ff1e33.tar.gz |
2554 ipadm needs IPMP configuration support
Contributed by: Ivan Krivonos <ivan.krivonos@nexenta.com>
Contributed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: C Fraire <cfraire@me.com>
Reviewed by: Yuri Pankov <ypankov@tintri.com>
Reviewed by: Andy Fiddaman <andyf@omnios.com>
Reviewed by: Ryan Goodfellow <ryan.goodfellow@oxide.computer>
Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src/cmd/cmd-inet/lib')
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile | 8 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c | 290 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h | 25 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c | 27 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c | 451 |
5 files changed, 598 insertions, 203 deletions
diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile index f9ae4eacdc..1c32ce2ff1 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile @@ -18,7 +18,10 @@ # # CDDL HEADER END # + +# # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2021 Tintri by DDN, Inc. All rights reserved. # # Needed for ROOTFS_LIBDIR definition @@ -38,9 +41,6 @@ ROOTCFGDIR= $(ROOTETC)/ipadm ROOTCFGFILES= $(CFGFILES:%=$(ROOTCFGDIR)/%) ROOTMANIFESTDIR= $(ROOTSVCNETWORK) -CERRWARN += -_gcc=-Wno-switch -CERRWARN += $(CNOWARN_UNINIT) - $(ROOTCFGFILES) := OWNER= ipadm $(ROOTCFGFILES) := GROUP= sys $(ROOTCFGFILES) := FILEMODE= 644 @@ -85,4 +85,4 @@ $(ROOTCFGDIR): $(ROOTCFGDIR)/%: $(ROOTCFGDIR) % $(INS.file) -include ../../../Makefile.targ +include ../../../Makefile.targ diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c index 92e3f73f55..b446b2ffa4 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c @@ -43,6 +43,9 @@ #include <libnvpair.h> #include "ipmgmt_impl.h" + +static void ipmgmt_common_handler(char *, char *, db_wfunc_t *); + /* Handler declaration for each door command */ typedef void ipmgmt_door_handler_t(void *argp); @@ -56,7 +59,8 @@ static ipmgmt_door_handler_t ipmgmt_getaddr_handler, ipmgmt_resetif_handler, ipmgmt_resetprop_handler, ipmgmt_setaddr_handler, - ipmgmt_setprop_handler; + ipmgmt_setprop_handler, + ipmgmt_ipmp_update_handler; typedef struct ipmgmt_door_info_s { uint_t idi_cmd; @@ -81,6 +85,7 @@ static ipmgmt_door_info_t i_ipmgmt_door_info_tbl[] = { { IPMGMT_CMD_ADDROBJ_ADD, B_TRUE, ipmgmt_aobjop_handler }, { IPMGMT_CMD_AOBJNAME2ADDROBJ, B_FALSE, ipmgmt_aobjop_handler }, { IPMGMT_CMD_LIF2ADDROBJ, B_FALSE, ipmgmt_aobjop_handler }, + { IPMGMT_CMD_IPMP_UPDATE, B_FALSE, ipmgmt_ipmp_update_handler}, { 0, 0, NULL }, }; @@ -593,6 +598,10 @@ ipmgmt_resetif_handler(void *argp) cbarg.cb_family = rargp->ia_family; cbarg.cb_ifname = rargp->ia_ifname; + + cbarg.cb_ipv4exists = B_TRUE; + cbarg.cb_ipv6exists = B_TRUE; + if (flags & IPMGMT_PERSIST) err = ipmgmt_db_walk(ipmgmt_db_resetif, &cbarg, IPADM_DB_DELETE); @@ -654,59 +663,10 @@ ipmgmt_resetaddr_handler(void *argp) static void ipmgmt_getaddr_handler(void *argp) { - size_t buflen, onvlsize; - char *buf, *onvlbuf; - ipmgmt_getaddr_arg_t *gargp = argp; - ipmgmt_getaddr_cbarg_t cbarg; - ipmgmt_get_rval_t rval, *rvalp = &rval; - int err = 0; - - cbarg.cb_ifname = gargp->ia_ifname; - cbarg.cb_aobjname = gargp->ia_aobjname; - cbarg.cb_ocnt = 0; - if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0) - goto fail; - err = ipmgmt_db_walk(ipmgmt_db_getaddr, &cbarg, IPADM_DB_READ); - if (err == ENOENT && cbarg.cb_ocnt > 0) { - /* - * If there is atleast one entry in the nvlist, - * do not return error. - */ - err = 0; - } - if (err != 0) - goto fail; - - if ((err = nvlist_size(cbarg.cb_onvl, &onvlsize, - NV_ENCODE_NATIVE)) != 0) { - goto fail; - } - - if (onvlsize > (UINT32_MAX - sizeof (ipmgmt_get_rval_t))) - goto fail; - - buflen = onvlsize + sizeof (ipmgmt_get_rval_t); - /* - * We cannot use malloc() here because door_return never returns, and - * memory allocated by malloc() would get leaked. Use alloca() instead. - */ - buf = alloca(buflen); - onvlbuf = buf + sizeof (ipmgmt_get_rval_t); - if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf, &onvlsize, - NV_ENCODE_NATIVE, 0)) != 0) { - goto fail; - } - nvlist_free(cbarg.cb_onvl); - rvalp = (ipmgmt_get_rval_t *)(void *)buf; - rvalp->ir_err = 0; - rvalp->ir_nvlsize = onvlsize; + ipmgmt_getaddr_arg_t *gargp = argp; - (void) door_return(buf, buflen, NULL, 0); - return; -fail: - nvlist_free(cbarg.cb_onvl); - rvalp->ir_err = err; - (void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0); + ipmgmt_common_handler(gargp->ia_ifname, gargp->ia_aobjname, + ipmgmt_db_getaddr); } /* @@ -727,65 +687,20 @@ ipmgmt_resetprop_handler(void *argp) } /* - * Handles the door command IPMGMT_CMD_GETIF. It retrieves the name of all the - * persisted interfaces and the IP protocols (IPv4 or IPv6) they support. + * Handles the door command IPMGMT_CMD_GETIF. It retrieves the names of all + * persisted interfaces and the IP protocol families (IPv4 or IPv6) they + * support. Returns the info as a nvlist using door_return() from + * ipmgmt_common_handler(). */ static void ipmgmt_getif_handler(void *argp) { - ipmgmt_getif_arg_t *getif = argp; - ipmgmt_getif_rval_t *rvalp; - ipmgmt_retval_t rval; - ipmgmt_getif_cbarg_t cbarg; - ipadm_if_info_list_t *ifl, *curifl; - ipadm_if_info_t *ifp, *rifp; - int i, err = 0, count = 0; - size_t rbufsize; + ipmgmt_getif_arg_t *getif = argp; assert(getif->ia_cmd == IPMGMT_CMD_GETIF); - bzero(&cbarg, sizeof (cbarg)); - cbarg.cb_ifname = getif->ia_ifname; - err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ); - if (err == ENOENT && cbarg.cb_ifinfo) { - /* - * If there is atleast one entry in the nvlist, - * do not return error. - */ - err = 0; - } - if (err != 0) { - rval.ir_err = err; - (void) door_return((char *)&rval, sizeof (rval), NULL, 0); - return; - } - - /* allocate sufficient buffer to return the interface info */ - for (ifl = cbarg.cb_ifinfo; ifl != NULL; ifl = ifl->ifil_next) - ++count; - rbufsize = sizeof (*rvalp) + count * sizeof (*ifp); - rvalp = alloca(rbufsize); - bzero(rvalp, rbufsize); - - rvalp->ir_ifcnt = count; - rifp = rvalp->ir_ifinfo; - ifl = cbarg.cb_ifinfo; - - /* - * copy the interface info to buffer allocated on stack. The reason - * we do this is to avoid memory leak, as door_return() would never - * return - */ - for (i = 0; i < count; i++) { - ifp = &ifl->ifil_ifi; - rifp = rvalp->ir_ifinfo + i; - (void) bcopy(ifp, rifp, sizeof (*rifp)); - curifl = ifl->ifil_next; - free(ifl); - ifl = curifl; - } - rvalp->ir_err = err; - (void) door_return((char *)rvalp, rbufsize, NULL, 0); + ipmgmt_common_handler(getif->ia_ifname, NULL, + ipmgmt_db_getif); } /* @@ -819,7 +734,7 @@ ipmgmt_initif_handler(void *argp) err = ipmgmt_db_walk(ipmgmt_db_initif, &cbarg, IPADM_DB_READ); if (err == ENOENT && cbarg.cb_ocnt > 0) { /* - * If there is atleast one entry in the nvlist, + * If there is at least one entry in the nvlist, * do not return error. */ err = 0; @@ -863,10 +778,10 @@ int ipmgmt_persist_if(ipmgmt_if_arg_t *sargp) { ipadm_dbwrite_cbarg_t cb; - uint32_t flags = sargp->ia_flags; - nvlist_t *nvl = NULL; - int err = 0; - char strval[IPMGMT_STRSIZE]; + uint32_t flags = sargp->ia_flags; + nvlist_t *nvl = NULL; + char strval[IPMGMT_STRSIZE]; + int err = 0; if (!(flags & IPMGMT_PERSIST) || sargp->ia_family == AF_UNSPEC || sargp->ia_ifname[0] == '\0') { @@ -875,16 +790,163 @@ ipmgmt_persist_if(ipmgmt_if_arg_t *sargp) } if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) goto ret; + if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME, sargp->ia_ifname)) != 0) goto ret; - (void) snprintf(strval, IPMGMT_STRSIZE, "%d", sargp->ia_family); - if ((err = nvlist_add_string(nvl, IPADM_NVP_FAMILY, strval)) != 0) + + if ((err = ipmgmt_update_family_nvp(nvl, sargp->ia_family, + IPMGMT_APPEND)) != 0) goto ret; + + (void) snprintf(strval, IPMGMT_STRSIZE, "%d", sargp->ia_ifclass); + if ((err = nvlist_add_string(nvl, IPADM_NVP_IFCLASS, strval)) != 0) + goto ret; + cb.dbw_nvl = nvl; - cb.dbw_flags = 0; - err = ipmgmt_db_walk(ipmgmt_db_add, &cb, IPADM_DB_WRITE); + cb.dbw_flags = IPMGMT_APPEND | IPMGMT_UPDATE_IF; + err = ipmgmt_db_walk(ipmgmt_db_update_if, &cb, IPADM_DB_WRITE); ret: nvlist_free(nvl); return (err); } + +/* + * The helper for ipmgmt_getif_handler and ipmgmt_getaddr_handler + */ +static void +ipmgmt_common_handler(char *if_name, char *aobj_name, db_wfunc_t worker) +{ + size_t buflen, onvlsize; + char *buf, *onvlbuf; + ipmgmt_get_cbarg_t cbarg; + ipmgmt_get_rval_t rval, *rvalp = &rval; + int err = 0; + + cbarg.cb_ifname = if_name; + cbarg.cb_aobjname = aobj_name; + cbarg.cb_ocnt = 0; + + if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0) + goto fail; + + err = ipmgmt_db_walk(worker, &cbarg, IPADM_DB_READ); + if (err == ENOENT && cbarg.cb_ocnt > 0) { + /* + * If there is at least one entry in the nvlist, + * do not return error. + */ + err = 0; + } + if (err != 0) + goto fail; + + if ((err = nvlist_size(cbarg.cb_onvl, &onvlsize, + NV_ENCODE_NATIVE)) != 0) + goto fail; + + if (onvlsize > (UINT32_MAX - sizeof (ipmgmt_get_rval_t))) + goto fail; + + buflen = onvlsize + sizeof (ipmgmt_get_rval_t); + /* + * We cannot use malloc() here because door_return never returns, and + * memory allocated by malloc() would get leaked. Use alloca() instead. + */ + buf = alloca(buflen); + onvlbuf = buf + sizeof (ipmgmt_get_rval_t); + if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf, + &onvlsize, NV_ENCODE_NATIVE, 0)) != 0) + goto fail; + + nvlist_free(cbarg.cb_onvl); + rvalp = (ipmgmt_get_rval_t *)(void *)buf; + rvalp->ir_err = 0; + rvalp->ir_nvlsize = onvlsize; + + (void) door_return(buf, buflen, NULL, 0); + +fail: + nvlist_free(cbarg.cb_onvl); + rvalp->ir_err = err; + (void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_IPMP_UPDATE + */ +static void +ipmgmt_ipmp_update_handler(void *argp) +{ + ipmgmt_ipmp_update_arg_t *uargp = argp; + ipmgmt_retval_t rval; + ipadm_dbwrite_cbarg_t cb; + + boolean_t gif_exists; + char gifname[LIFNAMSIZ]; + nvlist_t *nvl = NULL; + uint32_t flags = uargp->ia_flags; + int err = 0; + + assert(uargp->ia_cmd == IPMGMT_CMD_IPMP_UPDATE); + + gif_exists = ipmgmt_persist_if_exists(uargp->ia_gifname, + AF_UNSPEC); + + if (!ipmgmt_persist_if_exists(uargp->ia_mifname, AF_UNSPEC)) { + err = EINVAL; + goto ret; + } + + ipmgmt_get_group_interface(uargp->ia_mifname, gifname, LIFNAMSIZ); + + if (flags & IPMGMT_APPEND) { + /* Group interface should be available in the DB */ + if (!gif_exists) { + err = ENOENT; + goto ret; + } + + if (gifname[0] != '\0') { + err = EEXIST; + goto ret; + } + } + + if (flags & IPMGMT_REMOVE) { + /* We cannot remove something that does not exist */ + if (!gif_exists || gifname[0] == '\0') { + err = ENOENT; + goto ret; + } + if (strcmp(uargp->ia_gifname, gifname) != 0) { + err = EINVAL; + goto ret; + } + } + + if (flags & IPMGMT_PERSIST) { + if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) + goto ret; + + if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME, + uargp->ia_gifname)) != 0) + goto ret; + + if ((err = nvlist_add_string(nvl, IPADM_NVP_MIFNAMES, + uargp->ia_mifname)) != 0) + goto ret; + + if ((err = nvlist_add_string(nvl, IPADM_NVP_GIFNAME, + uargp->ia_gifname)) != 0) + goto ret; + + cb.dbw_nvl = nvl; + cb.dbw_flags = flags | IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP; + err = ipmgmt_db_walk(ipmgmt_db_update_if, &cb, IPADM_DB_WRITE); + } +ret: + nvlist_free(nvl); + rval.ir_err = err; + (void) door_return((char *)&rval, sizeof (rval), NULL, 0); +} diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h index 968fa77923..fe7e95a87e 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h @@ -22,7 +22,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, Chris Fraire <cfraire@me.com>. - * Copyright 2021, Tintri by DDN. All rights reserved. + * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ #ifndef _IPMGMT_IMPL_H @@ -51,6 +51,8 @@ extern void ipmgmt_log(int, const char *, ...); extern int ipmgmt_cpfile(const char *, const char *, boolean_t); /* ipmgmt_persist.c */ +extern boolean_t ipmgmt_persist_if_exists(const char *, sa_family_t); +extern void ipmgmt_get_group_interface(const char *, char *, size_t); /* * following are the list of DB walker callback functions and the callback @@ -60,25 +62,22 @@ extern int ipmgmt_cpfile(const char *, const char *, boolean_t); extern db_wfunc_t ipmgmt_db_getprop, ipmgmt_db_resetprop; /* following functions take ipadm_dbwrite_cbarg_t as callback argument */ -extern db_wfunc_t ipmgmt_db_add, ipmgmt_db_update; +extern db_wfunc_t ipmgmt_db_add, ipmgmt_db_update, ipmgmt_db_update_if; typedef struct { - char *cb_ifname; - ipadm_if_info_list_t *cb_ifinfo; -} ipmgmt_getif_cbarg_t; -extern db_wfunc_t ipmgmt_db_getif; - -typedef struct { - char *cb_aobjname; - char *cb_ifname; + char *cb_ifname; + char *cb_aobjname; nvlist_t *cb_onvl; - int cb_ocnt; -} ipmgmt_getaddr_cbarg_t; + int cb_ocnt; +} ipmgmt_get_cbarg_t; +extern db_wfunc_t ipmgmt_db_getif; extern db_wfunc_t ipmgmt_db_getaddr; typedef struct { sa_family_t cb_family; char *cb_ifname; + boolean_t cb_ipv4exists; + boolean_t cb_ipv6exists; } ipmgmt_if_cbarg_t; extern db_wfunc_t ipmgmt_db_setif, ipmgmt_db_resetif; @@ -182,6 +181,8 @@ typedef struct scf_resources { scf_transaction_entry_t *sr_ent; } scf_resources_t; +extern int ipmgmt_update_family_nvp(nvlist_t *, + sa_family_t, uint_t); extern int ipmgmt_db_walk(db_wfunc_t *, void *, ipadm_db_op_t); extern int ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *, uint32_t); extern boolean_t ipmgmt_aobjmap_init(void *, nvlist_t *, char *, diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c index e3a8d3e817..3573ca40ca 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2021, Tintri by DDN. All rights reserved. + * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ /* @@ -522,31 +522,6 @@ child_out: } /* - * Return TRUE if `ifname' has persistent configuration for the `af' address - * family in the datastore - */ -static boolean_t -ipmgmt_persist_if_exists(char *ifname, sa_family_t af) -{ - ipmgmt_getif_cbarg_t cbarg; - boolean_t exists = B_FALSE; - ipadm_if_info_t *ifp; - - bzero(&cbarg, sizeof (cbarg)); - cbarg.cb_ifname = ifname; - (void) ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ); - if (cbarg.cb_ifinfo != NULL) { - ifp = &cbarg.cb_ifinfo->ifil_ifi; - if ((af == AF_INET && (ifp->ifi_pflags & IFIF_IPV4)) || - (af == AF_INET6 && (ifp->ifi_pflags & IFIF_IPV6))) { - exists = B_TRUE; - } - } - free(cbarg.cb_ifinfo); - return (exists); -} - -/* * Persist any NGZ interfaces assigned to us from the global zone if they do * not already exist in the persistent db. We need to * do this before any calls to ipadm_enable_if() can succeed (i.e., diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c index 5f9d24df7a..cf9e30e819 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c @@ -24,7 +24,7 @@ * Copyright 2018 Joyent, Inc. * Copyright 2016 Argo Technologie SA. * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>. - * Copyright 2021, Tintri by DDN. All rights reserved. + * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ /* @@ -81,6 +81,161 @@ extern pthread_rwlock_t ipmgmt_dbconf_lock; /* signifies whether volatile copy of data store is in use */ static boolean_t ipmgmt_rdonly_root = B_FALSE; +typedef int ipmgmt_if_updater_func_t(nvlist_t *, nvpair_t *, uint_t); + +static ipmgmt_if_updater_func_t ipmgmt_if_family_updater; +static ipmgmt_if_updater_func_t ipmgmt_if_groupmembers_updater; + +static int ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl); + +typedef struct { + const char *name; + ipmgmt_if_updater_func_t *func; +} ipmgmt_if_updater_ent_t; + +static ipmgmt_if_updater_ent_t ipmgmt_if_updater_ent[] = { + {IPADM_NVP_FAMILIES, ipmgmt_if_family_updater}, + {IPADM_NVP_MIFNAMES, ipmgmt_if_groupmembers_updater}, + {NULL, NULL} +}; + +static ipmgmt_if_updater_ent_t * +ipmgmt_find_if_field_updater(const char *field_name) +{ + int i; + + for (i = 0; ipmgmt_if_updater_ent[i].name != NULL; i++) { + if (strcmp(field_name, ipmgmt_if_updater_ent[i].name) == 0) { + break; + } + } + + return (&ipmgmt_if_updater_ent[i]); +} + +static int +ipmgmt_if_groupmembers_updater(nvlist_t *db_nvl, nvpair_t *member_nvp, + uint_t flags) +{ + char **members; + char *member; + char **out_members; + uint_t nelem = 0, cnt = 0; + int err; + + if ((err = nvpair_value_string(member_nvp, &member)) != 0) + return (err); + + err = nvlist_lookup_string_array(db_nvl, IPADM_NVP_MIFNAMES, + &members, &nelem); + + if (err != 0 && (flags & IPMGMT_REMOVE)) + return (ENOENT); + + /* + * Reserve one extra slot for IPMGMT_APPEND. + * Probably not worth conditionalizing. + */ + out_members = calloc(nelem + 1, sizeof (char *)); + if (out_members == NULL) + return (ENOMEM); + + while (nelem-- > 0) { + if ((flags & IPMGMT_REMOVE) && + (strcmp(member, members[nelem]) == 0)) + continue; + + if ((out_members[cnt] = strdup(members[nelem])) == NULL) { + err = ENOMEM; + goto fail; + } + + cnt++; + } + + if (flags & IPMGMT_APPEND) { + if ((out_members[cnt] = strdup(member)) == NULL) { + err = ENOMEM; + goto fail; + } + cnt++; + } + + if (cnt == 0) { + err = nvlist_remove(db_nvl, IPADM_NVP_MIFNAMES, + DATA_TYPE_STRING_ARRAY); + } else { + err = nvlist_add_string_array(db_nvl, IPADM_NVP_MIFNAMES, + out_members, cnt); + } + +fail: + while (cnt--) + free(out_members[cnt]); + + free(out_members); + + return (err); +} + +static int +ipmgmt_if_family_updater(nvlist_t *db_nvl, nvpair_t *families_nvp, uint_t flags) +{ + uint16_t *families; + uint_t nelem = 0; + int err; + + if ((err = nvpair_value_uint16_array(families_nvp, &families, + &nelem)) != 0) + return (err); + + return (ipmgmt_update_family_nvp(db_nvl, families[0], flags)); +} + +int +ipmgmt_update_family_nvp(nvlist_t *nvl, sa_family_t af, uint_t flags) +{ + uint16_t *families = NULL; + uint16_t out_families[2]; + uint_t nelem = 0, cnt; + int err; + + err = nvlist_lookup_uint16_array(nvl, IPADM_NVP_FAMILIES, + &families, &nelem); + if (err != 0 && (flags & IPMGMT_REMOVE)) { + return (ENOENT); + } + + if (flags & IPMGMT_APPEND) { + if (families != NULL) { + if (nelem == 2 || families[0] == af) { + return (EEXIST); + } + out_families[0] = families[0]; + out_families[1] = af; + cnt = 2; + } else { + out_families[0] = af; + cnt = 1; + } + } else { + assert(nelem == 1 || nelem == 2); + cnt = 0; + while (nelem-- > 0) { + if (families[nelem] != af) { + out_families[cnt] = families[nelem]; + cnt++; + } + } + } + + if (cnt != 0) { + return (nvlist_add_uint16_array(nvl, IPADM_NVP_FAMILIES, + out_families, cnt)); + } + return (nvlist_remove(nvl, IPADM_NVP_FAMILIES, DATA_TYPE_UINT16_ARRAY)); +} + /* * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed * in private nvpairs `proto', `ifname' & `aobjname'. @@ -329,7 +484,7 @@ boolean_t ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp) { - ipmgmt_getaddr_cbarg_t *cbarg = arg; + ipmgmt_get_cbarg_t *cbarg = arg; char *db_aobjname = NULL; char *db_ifname = NULL; nvlist_t *db_addr = NULL; @@ -556,57 +711,130 @@ ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, } /* - * For the given `cbarg->cb_ifname' interface, retrieves any persistent - * interface information (used in 'ipadm show-if') + * This function is used to update a DB line that describes + * an interface, its family and group interface + * */ -/* ARGSUSED */ boolean_t -ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, +ipmgmt_db_update_if(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp) { - ipmgmt_getif_cbarg_t *cbarg = arg; - char *ifname = cbarg->cb_ifname; - char *intf = NULL; - ipadm_if_info_list_t *ifl = NULL; - ipadm_if_info_t *ifp; - sa_family_t af; - char *afstr; + ipadm_dbwrite_cbarg_t *cb = arg; + ipmgmt_if_updater_ent_t *updater; + nvlist_t *in_nvl = cb->dbw_nvl; + uint_t flags = cb->dbw_flags; + nvpair_t *nvp; + char *name; + char *db_ifname; + char *gifname = NULL; + char *mifname = NULL; *errp = 0; - if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 || - nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 || - (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) { + + /* Only one flag */ + if ((flags & (IPMGMT_APPEND | IPMGMT_REMOVE)) == 0 || + ((flags & IPMGMT_APPEND) && (flags & IPMGMT_REMOVE))) { + *errp = EINVAL; + return (B_FALSE); + } + + if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) return (B_TRUE); + + if (nvlist_exists(db_nvl, IPADM_NVP_IFCLASS) && + nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 && + nvlist_lookup_string(in_nvl, IPADM_NVP_GIFNAME, &gifname) == 0 && + nvlist_lookup_string(in_nvl, IPADM_NVP_MIFNAMES, &mifname) == 0 && + strcmp(db_ifname, mifname) == 0) { + if (flags & IPMGMT_APPEND) { + if ((*errp = nvlist_add_string(db_nvl, + IPADM_NVP_GIFNAME, gifname)) != 0) + return (B_FALSE); + } else { + if ((*errp = nvlist_remove(db_nvl, IPADM_NVP_GIFNAME, + DATA_TYPE_STRING)) != 0) + return (B_FALSE); + } + cb->dbw_flags &= ~IPMGMT_UPDATE_IPMP; + goto done; } - af = atoi(afstr); - for (ifl = cbarg->cb_ifinfo; ifl != NULL; ifl = ifl->ifil_next) { - ifp = &ifl->ifil_ifi; - if (strcmp(ifp->ifi_name, intf) == 0) - break; + + if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) + return (B_TRUE); + + for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(in_nvl, nvp)) { + name = nvpair_name(nvp); + if (strcmp(name, IPADM_NVP_FAMILIES) != 0 && + strcmp(name, IPADM_NVP_MIFNAMES) != 0) + continue; + + updater = ipmgmt_find_if_field_updater(name); + assert(updater != NULL); + *errp = (*updater->func)(db_nvl, nvp, flags); + if (*errp != 0) + return (B_FALSE); } - if (ifl == NULL) { - ipadm_if_info_list_t *new; - if ((new = calloc(1, sizeof (*new))) == NULL) { - *errp = ENOMEM; - return (B_FALSE); /* don't continue the walk */ - } - new->ifil_next = cbarg->cb_ifinfo; - cbarg->cb_ifinfo = new; - ifp = &new->ifil_ifi; - (void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name)); + cb->dbw_flags &= ~IPMGMT_UPDATE_IF; + +done: + (void) memset(buf, 0, buflen); + if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { + *errp = EOVERFLOW; + return (B_FALSE); } - if (af == AF_INET) { - ifp->ifi_pflags |= IFIF_IPV4; - } else { - assert(af == AF_INET6); - ifp->ifi_pflags |= IFIF_IPV6; + /* we finished all operations, so do not continue */ + if ((cb->dbw_flags & (IPMGMT_UPDATE_IF | IPMGMT_UPDATE_IPMP)) == 0) + return (B_FALSE); + + return (B_TRUE); +} + +/* + * For the given `cbarg->cb_ifname' interface, retrieve the nvlist that + * represents the persistent interface information. + * The nvlist contains: + * IPADM_NVP_IFNAME + * IPADM_NVP_FAMILIES + * IPADM_NVP_IF_CLASS + * + * (used in 'ipadm show-if') + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipmgmt_get_cbarg_t *cbarg = arg; + char *ifname = cbarg->cb_ifname; + nvpair_t *nvp; + char *db_ifname = NULL; + boolean_t families = B_FALSE; + + /* Parse db nvlist */ + for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(db_nvl, nvp)) { + if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) { + (void) nvpair_value_string(nvp, &db_ifname); + } else if (strcmp(nvpair_name(nvp), IPADM_NVP_FAMILIES) == 0) { + families = B_TRUE; + } } - /* Terminate the walk if we found both v4 and v6 interfaces. */ - if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) && - (ifp->ifi_pflags & IFIF_IPV6)) + if (db_ifname == NULL || !families) + return (B_TRUE); + + if (ifname != NULL && ifname[0] != '\0' && + strcmp(ifname, db_ifname) != 0) + return (B_TRUE); + + *errp = nvlist_add_nvlist(cbarg->cb_onvl, db_ifname, db_nvl); + if (*errp == 0) + cbarg->cb_ocnt++; + + if (ifname != NULL && ifname[0] != '\0') return (B_FALSE); return (B_TRUE); @@ -625,7 +853,6 @@ ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, boolean_t isv6 = (cbarg->cb_family == AF_INET6); char *ifname = cbarg->cb_ifname; char *modstr = NULL; - char *afstr; char *aobjname; uint_t proto; ipmgmt_aobjmap_t *head; @@ -636,9 +863,31 @@ ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL)) return (B_TRUE); - if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) { - if (atoi(afstr) == cbarg->cb_family) + if (nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) { + + if ((*errp = ipmgmt_update_family_nvp(db_nvl, cbarg->cb_family, + IPMGMT_REMOVE)) != 0) { + return (B_FALSE); + } + + if (cbarg->cb_family == AF_INET) { + cbarg->cb_ipv4exists = B_FALSE; + } else { + assert(cbarg->cb_family == AF_INET6); + cbarg->cb_ipv6exists = B_FALSE; + } + if (!nvlist_exists(db_nvl, IPADM_NVP_FAMILIES)) { + cbarg->cb_ipv4exists = B_FALSE; + cbarg->cb_ipv6exists = B_FALSE; goto delete; + } + /* Otherwise need to reconstruct this string */ + (void) memset(buf, 0, buflen); + if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { + /* buffer overflow */ + *errp = EOVERFLOW; + return (B_FALSE); + } return (B_TRUE); } @@ -694,8 +943,8 @@ ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, goto delete; break; case MOD_PROTO_IP: - /* this should never be the case, today */ - assert(0); + if (!cbarg->cb_ipv4exists && !cbarg->cb_ipv6exists) + goto delete; break; } } @@ -895,7 +1144,7 @@ ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op) ipmgmt_aobjmap_t *head, *prev, *matched = NULL; boolean_t update = B_TRUE; int err = 0; - ipadm_db_op_t db_op; + ipadm_db_op_t db_op = IPADM_DB_READ; (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock); @@ -1371,7 +1620,7 @@ ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp) { ipadm_handle_t iph = cbarg; - nvpair_t *nvp, *pnvp; + nvpair_t *nvp, *pnvp = NULL; char *strval = NULL, *name, *mod = NULL, *pname; char tmpstr[IPMGMT_STRSIZE]; uint_t proto; @@ -1398,7 +1647,7 @@ ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen, } } - /* if we are here than we found a global property */ + /* If we are here then we have found a global property */ assert(mod != NULL); assert(nvpair_type(pnvp) == DATA_TYPE_STRING); @@ -1587,6 +1836,8 @@ ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname, case SCF_TYPE_ASTRING: *(char **)pval = scf_simple_prop_next_astring(prop); break; + default: + break; } ret: scf_simple_prop_free(prop); @@ -1710,3 +1961,109 @@ ipmgmt_update_dbver(scf_resources_t *res) (void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER); } + +/* + * Return TRUE if `ifname' has persistent configuration for the `af' address + * family in the datastore. + * It is possible to call the function with af == AF_UNSPEC, so in this case + * the function returns TRUE if either AF_INET or AF_INET6 interface exists + */ +boolean_t +ipmgmt_persist_if_exists(const char *ifname, sa_family_t af) +{ + boolean_t exists = B_FALSE; + nvlist_t *if_info_nvl; + uint16_t *families = NULL; + sa_family_t af_db; + uint_t nelem = 0; + + if (ipmgmt_get_ifinfo_nvl(ifname, &if_info_nvl) != 0) + goto done; + + if (nvlist_lookup_uint16_array(if_info_nvl, IPADM_NVP_FAMILIES, + &families, &nelem) != 0) + goto done; + + while (nelem-- > 0) { + af_db = families[nelem]; + if (af_db == af || (af == AF_UNSPEC && + (af_db == AF_INET || af_db == AF_INET6))) { + exists = B_TRUE; + break; + } + } + +done: + if (if_info_nvl != NULL) + nvlist_free(if_info_nvl); + + return (exists); +} + +/* + * Retrieves the membership information for the requested mif_name + * if mif_name is a member of a IPMP group, then gif_name will contain + * the name of IPMP group interface, otherwise the variable will be empty + */ +void +ipmgmt_get_group_interface(const char *mif_name, char *gif_name, size_t size) +{ + char *gif_name_from_nvl; + nvlist_t *if_info_nvl; + + gif_name[0] = '\0'; + + if (ipmgmt_get_ifinfo_nvl(mif_name, &if_info_nvl) != 0) + goto done; + + if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_GIFNAME, + &gif_name_from_nvl) != 0) + goto done; + + (void) strlcpy(gif_name, gif_name_from_nvl, size); + +done: + if (if_info_nvl != NULL) + nvlist_free(if_info_nvl); +} + +static int +ipmgmt_get_ifinfo_nvl(const char *ifname, nvlist_t **if_info_nvl) +{ + ipmgmt_get_cbarg_t cbarg; + nvpair_t *nvp; + nvlist_t *nvl; + int err; + + cbarg.cb_ifname = NULL; + cbarg.cb_aobjname = NULL; + cbarg.cb_ocnt = 0; + + if ((err = nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0)) != 0) + goto done; + + err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ); + if (err == ENOENT && cbarg.cb_ocnt > 0) + err = 0; + + if (err != 0) + goto done; + + for (nvp = nvlist_next_nvpair(cbarg.cb_onvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(cbarg.cb_onvl, nvp)) { + + if (strcmp(nvpair_name(nvp), ifname) != 0) + continue; + + if ((err = nvpair_value_nvlist(nvp, &nvl)) != 0 || + (err = nvlist_dup(nvl, if_info_nvl, NV_UNIQUE_NAME)) != 0) + *if_info_nvl = NULL; + + break; + } + +done: + nvlist_free(cbarg.cb_onvl); + + return (err); +} |