diff options
Diffstat (limited to 'usr/src/cmd/cmd-inet')
-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 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile | 17 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.lib/in.mpathd/net-ipmp | 31 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.lib/in.mpathd/network-ipmp.xml | 78 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile | 5 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c | 333 |
10 files changed, 998 insertions, 267 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); +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile index 3668af100b..d63134dae8 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/Makefile @@ -22,16 +22,21 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2021 Tintri by DDN, Inc. All rights reserved. +# PROG = in.mpathd ROOTFS_PROG = $(PROG) OBJS = mpd_tables.o mpd_main.o mpd_probe.o SRCS = $(OBJS:%.o=%.c) DEFAULTFILES = mpathd.dfl +SVCMETHOD = net-ipmp +MANIFEST = network-ipmp.xml include ../../../Makefile.cmd ROOTCMDDIR = $(ROOT)/lib/inet +ROOTMANIFESTDIR = $(ROOTSVCNETWORK) POFILE = $(PROG).po POFILES = $(SRCS:%.c=%.po) @@ -63,15 +68,9 @@ $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK) $(POST_PROCESS) -include ../Makefile.lib - -$(ROOTLIBINETPROG): - $(RM) $@; $(SYMLINK) ../../../lib/inet/$(PROG) $@ - -$(ROOTSBINPROG): - $(RM) $@; $(SYMLINK) ../lib/inet/$(PROG) $@ +check: $(CHKMANIFEST) -install: all $(ROOTLIBINETPROG) $(ROOTSBINPROG) $(ROOTCMD) \ +install: all $(ROOTCMD) $(ROOTMANIFEST) $(ROOTSVCMETHOD) \ $(ROOTETCDEFAULTFILES) clean: @@ -79,7 +78,7 @@ clean: lint: lint_SRCS -$(POFILE): $(POFILES) +$(POFILE): $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/net-ipmp b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/net-ipmp new file mode 100644 index 0000000000..be4263a74b --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/net-ipmp @@ -0,0 +1,31 @@ +#!/sbin/sh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2021 Tintri by DDN, Inc. All rights reserved. +# + +. /lib/svc/share/smf_include.sh + +if [ -z "$SMF_FMRI" ]; then + echo "this script can only be invoked by smf(5)" + exit $SMF_EXIT_ERR_NOSMF +fi + +smf_configure_ip || exit $SMF_EXIT_ERR_FATAL + +if /lib/inet/in.mpathd; then + exit $SMF_EXIT_OK +else + exit $SMF_EXIT_ERR_FATAL +fi diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/network-ipmp.xml b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/network-ipmp.xml new file mode 100644 index 0000000000..282bf5b4df --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.lib/in.mpathd/network-ipmp.xml @@ -0,0 +1,78 @@ +<?xml version='1.0'?> +<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> + +<!-- + Copyright 2021 Tintri by DDN, Inc. All rights reserved. + + This file and its contents are supplied under the terms of the + Common Development and Distribution License ("CDDL"), version 1.0. + You may only use this file in accordance with the terms of version + 1.0 of the CDDL. + + A full copy of the text of the CDDL should have accompanied this + source. A copy of the CDDL is also available via the Internet at + http://www.illumos.org/license/CDDL. + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='SUNWcsr:ipmp'> + +<service + name='network/ipmp' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <dependency name='loopback' grouping='require_all' restart_on='none' + type='service'> + <service_fmri value='svc:/network/loopback' /> + </dependency> + + <dependent name='network-physical' grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/network/physical:default' /> + </dependent> + + <exec_method type='method' name='start' exec='/lib/svc/method/net-ipmp' + timeout_seconds='60'> + </exec_method> + + <exec_method type='method' name='stop' exec=':kill' + timeout_seconds='60'> + </exec_method> + + <exec_method type='method' name='refresh' exec=':kill -HUP' + timeout_seconds='60'> + </exec_method> + + <property_group name='config' type='application'> + <propval name='action_authorization' type='astring' + value='solaris.smf.manage.ipmp' /> + </property_group> + + <property_group name='general' type='framework'> + <propval name='action_authorization' type='astring' + value='solaris.smf.manage.ipmp' /> + <propval name='value_authorization' type='astring' + value='solaris.smf.manage.ipmp' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + IP MultiPathing + </loctext> + </common_name> + <documentation> + <manpage title='in.mpathd' section='1M' /> + </documentation> + </template> +</service> +</service_bundle> diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile index 801fb40158..c9337fb75b 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile @@ -23,6 +23,9 @@ # Use is subject to license terms. # # Copyright 2020 Joyent, Inc. +# Copyright 2021 Tintri by DDN, Inc. All rights reserved. +# +# PROG = ipadm ROOTFS_PROG = $(PROG) @@ -39,7 +42,7 @@ COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:%.o=%.c) SRCS= $(LOCALSRCS) $(COMMONSRCS) CPPFLAGS += -I$(CMDINETCOMMONDIR) -LDLIBS += -lofmt -linetutil -lipadm -lnvpair +LDLIBS += -lofmt -linetutil -lipadm -lnvpair -lipmp -lcmdutils LINTFLAGS += -m ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%) diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c index e48b32733f..1a1aa5e5ac 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c @@ -21,11 +21,10 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2017 Nexenta Systems, Inc. * Copyright (c) 2018, Joyent, Inc. * Copyright 2017 Gary Mills * 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. * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. */ @@ -40,6 +39,8 @@ #include <libdllink.h> #include <libinetutil.h> #include <libipadm.h> +#include <ipmp.h> +#include <ipmp_admin.h> #include <locale.h> #include <netdb.h> #include <netinet/in.h> @@ -53,21 +54,25 @@ #include <sys/stat.h> #include <sys/types.h> #include <zone.h> +#include <sys/list.h> +#include <stddef.h> #define STR_UNKNOWN_VAL "?" #define LIFC_DEFAULT (LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES |\ LIFC_UNDER_IPMP) +static void do_create_ip_common(int, char **, const char *, uint32_t); + typedef void cmdfunc_t(int, char **, const char *); static cmdfunc_t do_help; -static cmdfunc_t do_create_if, do_delete_if, do_enable_if, do_disable_if; -static cmdfunc_t do_show_if; -static cmdfunc_t do_set_prop, do_show_prop, do_set_ifprop; -static cmdfunc_t do_show_ifprop, do_reset_ifprop, do_reset_prop; -static cmdfunc_t do_show_addrprop, do_set_addrprop, do_reset_addrprop; -static cmdfunc_t do_create_addr, do_delete_addr, do_show_addr; -static cmdfunc_t do_enable_addr, do_disable_addr; -static cmdfunc_t do_up_addr, do_down_addr, do_refresh_addr; +static cmdfunc_t do_create_ip, do_delete_ip; +static cmdfunc_t do_create_ipmp, do_add_ipmp, do_remove_ipmp; +static cmdfunc_t do_disable_if, do_enable_if, do_show_if; +static cmdfunc_t do_set_ifprop, do_reset_ifprop, do_show_ifprop; +static cmdfunc_t do_create_addr, do_delete_addr, do_show_addr, do_refresh_addr; +static cmdfunc_t do_disable_addr, do_enable_addr, do_down_addr, do_up_addr; +static cmdfunc_t do_set_addrprop, do_reset_addrprop, do_show_addrprop; +static cmdfunc_t do_set_prop, do_reset_prop, do_show_prop; typedef struct cmd { char *c_name; @@ -78,12 +83,28 @@ typedef struct cmd { static cmd_t cmds[] = { { "help", do_help, NULL }, /* interface management related sub-commands */ - { "create-if", do_create_if, "\tcreate-if\t[-t] <interface>" }, - { "disable-if", do_disable_if, "\tdisable-if\t-t <interface>" }, - { "enable-if", do_enable_if, "\tenable-if\t-t <interface>" }, - { "delete-if", do_delete_if, "\tdelete-if\t<interface>" }, - { "show-if", do_show_if, + { "create-if", do_create_ip, "\tcreate-if\t[-t] <interface>" }, + { "create-ip", do_create_ip, "\tcreate-ip\t[-t] <interface>" }, + { "delete-if", do_delete_ip, "\tdelete-if\t<interface>\n" }, + { "delete-ip", do_delete_ip, "\tdelete-ip\t<interface>\n" }, + + { "create-ipmp", do_create_ipmp, + "\tcreate-ipmp\t[-t] [-i <interface>[,<interface>]...] " + "<ipmp-interface>" }, + { "delete-ipmp", do_delete_ip, + "\tdelete-ipmp\t<ipmp-interface>" }, + { "add-ipmp", do_add_ipmp, + "\tadd-ipmp\t[-t] -i <interface>[,<interface>]... " + "<ipmp-interface>" }, + { "remove-ipmp", do_remove_ipmp, + "\tremove-ipmp\t[-t] -i <interface>[,<interface>]... " + "<ipmp-interface>\n" }, + + { "disable-if", do_disable_if, "\tdisable-if\t-t <interface>" }, + { "enable-if", do_enable_if, "\tenable-if\t-t <interface>" }, + { "show-if", do_show_if, "\tshow-if\t\t[[-p] -o <field>,...] [<interface>]\n" }, + { "set-ifprop", do_set_ifprop, "\tset-ifprop\t[-t] -p <prop>=<value[,...]> -m <protocol> " "<interface>" }, @@ -101,14 +122,15 @@ static cmd_t cmds[] = { "\t\t\t[-1] [-h <hostname>] <addrobj>\n" "\tcreate-addr\t[-t] -T addrconf [-i interface-id]\n" "\t\t\t[-p {stateful|stateless}={yes|no}] <addrobj>" }, - { "down-addr", do_down_addr, "\tdown-addr\t[-t] <addrobj>" }, - { "up-addr", do_up_addr, "\tup-addr\t\t[-t] <addrobj>" }, - { "disable-addr", do_disable_addr, "\tdisable-addr\t-t <addrobj>" }, - { "enable-addr", do_enable_addr, "\tenable-addr\t-t <addrobj>" }, - { "refresh-addr", do_refresh_addr, "\trefresh-addr\t[-i] <addrobj>" }, { "delete-addr", do_delete_addr, "\tdelete-addr\t[-r] <addrobj>" }, - { "show-addr", do_show_addr, - "\tshow-addr\t[[-p] -o <field>,...] [<addrobj>]\n" }, + { "show-addr", do_show_addr, + "\tshow-addr\t[[-p] -o <field>,...] [<addrobj>]" }, + { "refresh-addr", do_refresh_addr, "\trefresh-addr\t[-i] <addrobj>" }, + { "down-addr", do_down_addr, "\tdown-addr\t[-t] <addrobj>" }, + { "up-addr", do_up_addr, "\tup-addr\t\t[-t] <addrobj>" }, + { "disable-addr", do_disable_addr, "\tdisable-addr\t-t <addrobj>" }, + { "enable-addr", do_enable_addr, "\tenable-addr\t-t <addrobj>\n" }, + { "set-addrprop", do_set_addrprop, "\tset-addrprop\t[-t] -p <prop>=<value[,...]> <addrobj>" }, { "reset-addrprop", do_reset_addrprop, @@ -118,11 +140,11 @@ static cmd_t cmds[] = { "<addrobj>\n" }, /* protocol properties related sub-commands */ - { "set-prop", do_set_prop, + { "set-prop", do_set_prop, "\tset-prop\t[-t] -p <prop>[+|-]=<value[,...]> <protocol>" }, - { "reset-prop", do_reset_prop, + { "reset-prop", do_reset_prop, "\treset-prop\t[-t] -p <prop> <protocol>" }, - { "show-prop", do_show_prop, + { "show-prop", do_show_prop, "\tshow-prop\t[[-c] -o <field>,...] [-p <prop>,...]" " [protocol]" } }; @@ -282,7 +304,7 @@ typedef struct show_addr_args_s { typedef struct show_if_args_s { show_if_state_t *si_state; - ipadm_if_info_list_t *si_info; + ipadm_if_info_t *si_info; } show_if_args_t; typedef enum { @@ -296,6 +318,7 @@ typedef enum { typedef enum { SI_IFNAME, + SI_IFCLASS, SI_STATE, SI_CURRENT, SI_PERSISTENT @@ -315,6 +338,7 @@ static ofmt_field_t show_addr_fields[] = { static ofmt_field_t show_if_fields[] = { /* name, field width, id, callback */ { "IFNAME", 11, SI_IFNAME, print_si_cb}, +{ "CLASS", 10, SI_IFCLASS, print_si_cb}, { "STATE", 9, SI_STATE, print_si_cb}, { "CURRENT", 13, SI_CURRENT, print_si_cb}, { "PERSISTENT", 11, SI_PERSISTENT, print_si_cb}, @@ -328,6 +352,11 @@ typedef struct intf_mask { uint64_t mask; } fmask_t; +typedef enum { + IPMP_ADD_MEMBER, + IPMP_REMOVE_MEMBER +} ipmp_action_t; + /* * Handle to libipadm. Opened in main() before the sub-command specific * function is called and is closed before the program exits. @@ -349,6 +378,9 @@ static void warn_ipadmerr(ipadm_status_t, const char *, ...); static void ipadm_check_propstr(const char *, boolean_t, const char *); static void process_misc_addrargs(int, char **, const char *, int *, uint32_t *); +static void do_action_ipmp(char *, ipadm_ipmp_members_t *, ipmp_action_t, + uint32_t); + static void usage(int ret) @@ -419,19 +451,17 @@ main(int argc, char *argv[]) } /* - * Create an IP interface for which no saved configuration exists in the - * persistent store. + * Create regular IP interface or IPMP group interface */ static void -do_create_if(int argc, char *argv[], const char *use) +do_create_ip_common(int argc, char *argv[], const char *use, uint32_t flags) { ipadm_status_t status; int option; - uint32_t flags = IPADM_OPT_PERSIST|IPADM_OPT_ACTIVE; opterr = 0; - while ((option = getopt_long(argc, argv, ":t", if_longopts, - NULL)) != -1) { + while ((option = getopt_long(argc, argv, + ":t", if_longopts, NULL)) != -1) { switch (option) { case 't': /* @@ -444,8 +474,12 @@ do_create_if(int argc, char *argv[], const char *use) die_opterr(optopt, option, use); } } - if (optind != (argc - 1)) - die("Usage: %s", use); + if (optind != (argc - 1)) { + if (use != NULL) + die("usage: %s", use); + else + die(NULL); + } status = ipadm_create_if(iph, argv[optind], AF_UNSPEC, flags); if (status != IPADM_SUCCESS) { die("Could not create %s : %s", @@ -454,6 +488,185 @@ do_create_if(int argc, char *argv[], const char *use) } /* + * Helpers to parse options into ipadm_ipmp_members_t list, and to free it. + */ +static ipadm_ipmp_members_t * +get_ipmp_members(int argc, char *argv[], const char *use, uint32_t *flags) +{ + ipadm_ipmp_members_t *members = NULL; + ipadm_ipmp_member_t *member; + char *ifname; + int option; + + + opterr = 0; + while ((option = getopt_long(argc, argv, ":i:", if_longopts, NULL)) != + -1) { + switch (option) { + case 't': + *flags &= ~IPADM_OPT_PERSIST; + break; + case 'i': + if (members == NULL) { + members = calloc(1, + sizeof (ipadm_ipmp_members_t)); + if (members == NULL) + die("insufficient memory"); + list_create(members, + sizeof (ipadm_ipmp_member_t), + offsetof(ipadm_ipmp_member_t, node)); + } + + for (ifname = strtok(optarg, ","); + ifname != NULL; + ifname = strtok(NULL, ",")) { + if ((member = calloc(1, + sizeof (ipadm_ipmp_member_t))) == NULL) + die("insufficient memory"); + + if (strlcpy(member->if_name, ifname, + sizeof (member->if_name)) >= LIFNAMSIZ) + die("Incorrect length of interface " + "name: %s", ifname); + + list_insert_tail(members, member); + } + break; + default: + die_opterr(optopt, option, use); + } + } + + if (optind != (argc - 1)) + die("Usage: %s", use); + + if (members != NULL && list_is_empty(members)) { + free(members); + members = NULL; + } + + return (members); +} + +static void +free_ipmp_members(ipadm_ipmp_members_t *members) +{ + ipadm_ipmp_member_t *member; + + while ((member = list_remove_head(members)) != NULL) + free(member); + + list_destroy(members); + + free(members); +} + +/* + * Create an IPMP group interface for which no saved configuration + * exists in the persistent store. + */ +static void +do_create_ipmp(int argc, char *argv[], const char *use) +{ + uint32_t flags = IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE | IPADM_OPT_IPMP; + ipadm_ipmp_members_t *members = NULL; + ipmp_handle_t ipmp_handle; + int retval; + + retval = ipmp_open(&ipmp_handle); + if (retval != IPMP_SUCCESS) { + die("Could not create IPMP handle: %s", + ipadm_status2str(retval)); + } + + retval = ipmp_ping_daemon(ipmp_handle); + ipmp_close(ipmp_handle); + + if (retval != IPMP_SUCCESS) { + die("Cannot ping in.mpathd: %s", ipmp_errmsg(retval)); + } + + members = get_ipmp_members(argc, argv, use, &flags); + + do_create_ip_common(argc, argv, use, flags); + + if (members != NULL) { + do_action_ipmp(argv[optind], members, IPMP_ADD_MEMBER, flags); + free_ipmp_members(members); + } +} + +static void +do_add_ipmp(int argc, char *argv[], const char *use) +{ + uint32_t flags = IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE; + ipadm_ipmp_members_t *members; + + members = get_ipmp_members(argc, argv, use, &flags); + + if (members == NULL) + die_opterr(optopt, ':', use); + + do_action_ipmp(argv[optind], members, IPMP_ADD_MEMBER, flags); + free_ipmp_members(members); +} + +static void +do_remove_ipmp(int argc, char *argv[], const char *use) +{ + uint32_t flags = IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE; + ipadm_ipmp_members_t *members; + + members = get_ipmp_members(argc, argv, use, &flags); + + if (members == NULL) + die_opterr(optopt, ':', use); + + do_action_ipmp(argv[optind], members, IPMP_REMOVE_MEMBER, flags); + free_ipmp_members(members); +} + +static void +do_action_ipmp(char *ipmp, ipadm_ipmp_members_t *members, ipmp_action_t action, + uint32_t flags) +{ + ipadm_status_t (*func)(ipadm_handle_t, const char *, const char *, + uint32_t); + ipadm_status_t status; + ipadm_ipmp_member_t *member; + char *ifname; + const char *msg; + + if (action == IPMP_ADD_MEMBER) { + func = ipadm_add_ipmp_member; + msg = "Cannot add interface '%s' to IPMP interface '%s': %s"; + } else { + func = ipadm_remove_ipmp_member; + msg = "Cannot remove interface '%s' from IPMP interface '%s': " + "%s"; + } + + while ((member = list_remove_head(members)) != NULL) { + ifname = member->if_name; + + status = func(iph, ipmp, ifname, flags); + if (status != IPADM_SUCCESS) + die(msg, ifname, ipmp, ipadm_status2str(status)); + } +} + +/* + * Create an IP interface for which no saved configuration exists in the + * persistent store. + */ +static void +do_create_ip(int argc, char *argv[], const char *use) +{ + do_create_ip_common(argc, argv, use, + IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE); +} + +/* * Enable an IP interface based on the persistent configuration for * that interface. */ @@ -480,7 +693,7 @@ do_enable_if(int argc, char *argv[], const char *use) * Remove an IP interface from both active and persistent configuration. */ static void -do_delete_if(int argc, char *argv[], const char *use) +do_delete_ip(int argc, char *argv[], const char *use) { ipadm_status_t status; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; @@ -687,7 +900,7 @@ do_show_ifprop(int argc, char **argv, const char *use) uint_t proto; boolean_t m_arg = _B_FALSE; char *protostr; - ipadm_if_info_list_t *ifinfo, *ifl; + ipadm_if_info_t *ifinfo, *ifp; ipadm_status_t status; show_prop_state_t state; @@ -751,9 +964,8 @@ do_show_ifprop(int argc, char **argv, const char *use) if (status != IPADM_SUCCESS) die("Error retrieving interface(s): %s", ipadm_status2str(status)); - for (ifl = ifinfo; ifl != NULL; ifl = ifl->ifil_next) { - (void) strlcpy(state.sps_ifname, ifl->ifil_ifi.ifi_name, - LIFNAMSIZ); + for (ifp = ifinfo; ifp != NULL; ifp = ifp->ifi_next) { + (void) strlcpy(state.sps_ifname, ifp->ifi_name, LIFNAMSIZ); state.sps_proto = proto; show_properties(&state, IPADMPROP_CLASS_IF); } @@ -1057,14 +1269,16 @@ die(const char *format, ...) { va_list alist; - format = gettext(format); - (void) fprintf(stderr, "%s: ", progname); + if (format != NULL) { + format = gettext(format); + (void) fprintf(stderr, "%s: ", progname); - va_start(alist, format); - (void) vfprintf(stderr, format, alist); - va_end(alist); + va_start(alist, format); + (void) vfprintf(stderr, format, alist); + va_end(alist); - (void) putchar('\n'); + (void) putchar('\n'); + } ipadm_destroy_addrobj(ipaddr); ipadm_close(iph); @@ -1624,7 +1838,7 @@ flags2str(uint64_t flags, fmask_t *tbl, boolean_t is_bits, static boolean_t is_from_gz(const char *lifname) { - ipadm_if_info_list_t *if_info; + ipadm_if_info_t *if_info; char phyname[LIFNAMSIZ], *cp; boolean_t ret = _B_FALSE; ipadm_status_t status; @@ -1647,7 +1861,7 @@ is_from_gz(const char *lifname) if (status != IPADM_SUCCESS) return (ret); - if (if_info->ifil_ifi.ifi_cflags & IFIF_L3PROTECT) + if (if_info->ifi_cflags & IFIF_L3PROTECT) ret = _B_TRUE; ipadm_free_if_info(if_info); return (ret); @@ -1902,8 +2116,8 @@ static boolean_t print_si_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) { show_if_args_t *arg = ofarg->ofmt_cbarg; - ipadm_if_info_list_t *ifinfo = arg->si_info; - char *ifname = ifinfo->ifil_ifi.ifi_name; + ipadm_if_info_t *ifinfo = arg->si_info; + char *ifname = ifinfo->ifi_name; fmask_t intf_state[] = { { "ok", IFIS_OK, IPADM_ALL_BITS}, { "down", IFIS_DOWN, IPADM_ALL_BITS}, @@ -1933,22 +2147,33 @@ print_si_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) { "6", IFIF_IPV6, IFIF_IPV6 }, { NULL, 0, 0 } }; + fmask_t intf_class[] = { + { "IP", IPADM_IF_CLASS_REGULAR, IPADM_ALL_BITS}, + { "IPMP", IPADM_IF_CLASS_IPMP, IPADM_ALL_BITS}, + { "VIRTUAL", IPADM_IF_CLASS_VIRTUAL, IPADM_ALL_BITS}, + { "UNKNOWN", IPADM_IF_CLASS_UNKNOWN, IPADM_ALL_BITS}, + { NULL, 0, 0} + }; buf[0] = '\0'; switch (ofarg->ofmt_id) { case SI_IFNAME: (void) snprintf(buf, bufsize, "%s", ifname); break; + case SI_IFCLASS: + flags2str(ifinfo->ifi_class, intf_class, _B_FALSE, + buf, bufsize); + break; case SI_STATE: - flags2str(ifinfo->ifil_ifi.ifi_state, intf_state, _B_FALSE, + flags2str(ifinfo->ifi_state, intf_state, _B_FALSE, buf, bufsize); break; case SI_CURRENT: - flags2str(ifinfo->ifil_ifi.ifi_cflags, intf_cflags, _B_TRUE, + flags2str(ifinfo->ifi_cflags, intf_cflags, _B_TRUE, buf, bufsize); break; case SI_PERSISTENT: - flags2str(ifinfo->ifil_ifi.ifi_pflags, intf_pflags, _B_TRUE, + flags2str(ifinfo->ifi_pflags, intf_pflags, _B_TRUE, buf, bufsize); break; default: @@ -1969,7 +2194,7 @@ do_show_if(int argc, char *argv[], const char *use) ipadm_status_t status; show_if_state_t state; char *fields_str = NULL; - ipadm_if_info_list_t *if_info, *ptr; + ipadm_if_info_t *if_info, *ptr; show_if_args_t sargs; int option; ofmt_handle_t ofmt; @@ -2014,7 +2239,7 @@ do_show_if(int argc, char *argv[], const char *use) ipadm_status2str(status)); } - for (ptr = if_info; ptr != NULL; ptr = ptr->ifil_next) { + for (ptr = if_info; ptr != NULL; ptr = ptr->ifi_next) { sargs.si_info = ptr; ofmt_print(state.si_ofmt, &sargs); } |