Print this page
usr/src/cmd/dlmgmtd/dlmgmt_door.c

@@ -23,10 +23,11 @@
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2018, Joyent Inc.
  * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
  * Copyright 2020 RackTop Systems Inc.
+ * Copyright 2023 Oxide Computer Company
  */
 
 /*
  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */

@@ -84,10 +85,11 @@
 
 #include <libdlpi.h>
 #include <libdllink.h>
 #include <libdlvlan.h>
 #include <libdlvnic.h>
+#include <libdlaggr.h>
 
 #include <inet/tcp.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <net/route.h>

@@ -3026,17 +3028,79 @@
                 di_prof_fini(prof);
 
         return (0);
 }
 
+/*
+ * Retrieve the list of datalink IDs assigned to a zone.
+ *
+ * On return, *count will be updated with the total number of links and, if it
+ * is not NULL, **linksp will be updated to point to allocated memory
+ * containing the link IDs. This should be passed to free() when the caller is
+ * finished with it.
+ */
 static int
+fetch_zone_datalinks(zlog_t *zlogp, zoneid_t zoneid, int *countp,
+    datalink_id_t **linksp)
+{
+        datalink_id_t *links = NULL;
+        int links_size = 0;
+        int num_links;
+
+        if (linksp != NULL)
+                *linksp = NULL;
+        *countp = 0;
+
+        num_links = 0;
+        if (zone_list_datalink(zoneid, &num_links, NULL) != 0) {
+                zerror(zlogp, B_TRUE,
+                    "unable to determine number of network interfaces");
+                return (-1);
+        }
+
+        if (num_links == 0)
+                return (0);
+
+        /* If linkp is NULL, the caller only wants the count. */
+        if (linksp == NULL) {
+                *countp = num_links;
+                return (0);
+        }
+
+        do {
+                datalink_id_t *p;
+
+                links_size = num_links;
+                p = reallocarray(links, links_size, sizeof (datalink_id_t));
+
+                if (p == NULL) {
+                        zerror(zlogp, B_TRUE,
+                            "failed to allocate memory for zone links");
+                        free(links);
+                        return (-1);
+                }
+                links = p;
+
+                if (zone_list_datalink(zoneid, &num_links, links) != 0) {
+                        zerror(zlogp, B_TRUE, "failed to list zone links");
+                        free(links);
+                        return (-1);
+                }
+        } while (links_size < num_links);
+
+        *countp = num_links;
+        *linksp = links;
+
+        return (0);
+}
+
+static int
 remove_datalink_pool(zlog_t *zlogp, zoneid_t zoneid)
 {
         ushort_t flags;
         zone_iptype_t iptype;
-        int i, dlnum = 0;
-        datalink_id_t *dllink, *dllinks = NULL;
+        int i;
         dladm_status_t err;
 
         if (strlen(pool_name) == 0)
                 return (0);
 

@@ -3052,38 +3116,19 @@
                 else
                         iptype = ZS_SHARED;
         }
 
         if (iptype == ZS_EXCLUSIVE) {
-                /*
-                 * Get the datalink count and for each datalink,
-                 * attempt to clear the pool property and clear
-                 * the pool_name.
-                 */
-                if (zone_list_datalink(zoneid, &dlnum, NULL) != 0) {
-                        zerror(zlogp, B_TRUE, "unable to count network "
-                            "interfaces");
-                        return (-1);
-                }
+                datalink_id_t *dllinks = NULL;
+                int dlnum = 0;
 
-                if (dlnum == 0)
-                        return (0);
-
-                if ((dllinks = malloc(dlnum * sizeof (datalink_id_t)))
-                    == NULL) {
-                        zerror(zlogp, B_TRUE, "memory allocation failed");
+                if (fetch_zone_datalinks(zlogp, zoneid, &dlnum, &dllinks) != 0)
                         return (-1);
-                }
-                if (zone_list_datalink(zoneid, &dlnum, dllinks) != 0) {
-                        zerror(zlogp, B_TRUE, "unable to list network "
-                            "interfaces");
-                        return (-1);
-                }
 
                 bzero(pool_name, sizeof (pool_name));
-                for (i = 0, dllink = dllinks; i < dlnum; i++, dllink++) {
-                        err = dladm_set_linkprop(dld_handle, *dllink, "pool",
+                for (i = 0; i < dlnum; i++) {
+                        err = dladm_set_linkprop(dld_handle, dllinks[i], "pool",
                             NULL, 0, DLADM_OPT_ACTIVE);
                         if (err != DLADM_STATUS_OK) {
                                 zerror(zlogp, B_TRUE,
                                     "WARNING: unable to clear pool");
                         }

@@ -3098,11 +3143,11 @@
 {
         ushort_t flags;
         zone_iptype_t iptype;
         int i, dlnum = 0;
         dladm_status_t dlstatus;
-        datalink_id_t *dllink, *dllinks = NULL;
+        datalink_id_t *dllinks = NULL;
 
         if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
             sizeof (flags)) < 0) {
                 if (vplat_get_iptype(zlogp, &iptype) < 0) {
                         zerror(zlogp, B_FALSE, "unable to determine ip-type");

@@ -3117,58 +3162,118 @@
 
         if (iptype != ZS_EXCLUSIVE)
                 return (0);
 
         /*
-         * Get the datalink count and for each datalink,
-         * attempt to clear the pool property and clear
-         * the pool_name.
+         * Get the datalink count and for each datalink, attempt to clear the
+         * protection and allowed_ips properties.
          */
-        if (zone_list_datalink(zoneid, &dlnum, NULL) != 0) {
-                zerror(zlogp, B_TRUE, "unable to count network interfaces");
-                return (-1);
-        }
 
-        if (dlnum == 0)
-                return (0);
-
-        if ((dllinks = malloc(dlnum * sizeof (datalink_id_t))) == NULL) {
-                zerror(zlogp, B_TRUE, "memory allocation failed");
+        if (fetch_zone_datalinks(zlogp, zoneid, &dlnum, &dllinks) != 0)
                 return (-1);
-        }
-        if (zone_list_datalink(zoneid, &dlnum, dllinks) != 0) {
-                zerror(zlogp, B_TRUE, "unable to list network interfaces");
-                free(dllinks);
-                return (-1);
-        }
 
-        for (i = 0, dllink = dllinks; i < dlnum; i++, dllink++) {
+        for (i = 0; i < dlnum; i++) {
                 char dlerr[DLADM_STRSIZE];
 
-                dlstatus = dladm_set_linkprop(dld_handle, *dllink,
+                dlstatus = dladm_set_linkprop(dld_handle, dllinks[i],
                     "protection", NULL, 0, DLADM_OPT_ACTIVE);
                 if (dlstatus == DLADM_STATUS_NOTFOUND) {
                         /* datalink does not belong to the GZ */
                         continue;
                 }
-                if (dlstatus != DLADM_STATUS_OK)
+                if (dlstatus != DLADM_STATUS_OK) {
                         zerror(zlogp, B_FALSE,
-                            "clear 'protection' link property: %s",
-                            dladm_status2str(dlstatus, dlerr));
+                            "clear link %d 'protection' link property: %s",
+                            dllinks[i], dladm_status2str(dlstatus, dlerr));
+                }
 
-                dlstatus = dladm_set_linkprop(dld_handle, *dllink,
+                dlstatus = dladm_set_linkprop(dld_handle, dllinks[i],
                     "allowed-ips", NULL, 0, DLADM_OPT_ACTIVE);
-                if (dlstatus != DLADM_STATUS_OK)
+                if (dlstatus != DLADM_STATUS_OK) {
                         zerror(zlogp, B_FALSE,
-                            "clear 'allowed-ips' link property: %s",
-                            dladm_status2str(dlstatus, dlerr));
+                            "clear link %d 'allowed-ips' link property: %s",
+                            dllinks[i], dladm_status2str(dlstatus, dlerr));
         }
+        }
         free(dllinks);
         return (0);
 }
 
 static int
+unconfigure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid)
+{
+        datalink_id_t *dllinks;
+        int dlnum = 0;
+        uint_t i;
+
+        /*
+         * The kernel shutdown callback for the dls module should have removed
+         * all datalinks from this zone.  If any remain, then there's a
+         * problem.
+         */
+
+        if (fetch_zone_datalinks(zlogp, zoneid, &dlnum, &dllinks) != 0)
+                return (-1);
+
+        if (dlnum == 0)
+                return (0);
+
+        /*
+         * There are some datalinks left in the zone. The most likely cause of
+         * this is that the datalink-management daemon (dlmgmtd) was not
+         * running when the zone was shut down. That prevented the kernel from
+         * doing the required upcall to move the links back to the GZ. To
+         * attempt recovery, do that now.
+         */
+
+        for (i = 0; i < dlnum; i++) {
+                char dlerr[DLADM_STRSIZE];
+                dladm_status_t status;
+                uint32_t link_flags;
+                datalink_id_t link = dllinks[i];
+                char *prop_vals[] = { GLOBAL_ZONENAME };
+
+                status = dladm_datalink_id2info(dld_handle, link,
+                    &link_flags, NULL, NULL, NULL, 0);
+
+                if (status != DLADM_STATUS_OK) {
+                        zerror(zlogp, B_FALSE,
+                            "failed to get link info for %u: %s",
+                            link, dladm_status2str(status, dlerr));
+                        continue;
+                }
+
+                if (link_flags & DLADM_OPT_TRANSIENT)
+                        continue;
+
+                status = dladm_set_linkprop(dld_handle, link, "zone",
+                    prop_vals, 1, DLADM_OPT_ACTIVE);
+
+                if (status != DLADM_STATUS_OK) {
+                        zerror(zlogp, B_FALSE,
+                            "failed to move link %u to GZ: %s",
+                            link, dladm_status2str(status, dlerr));
+                }
+        }
+
+        free(dllinks);
+
+        /* Check again and log a message if links remain */
+
+        if (fetch_zone_datalinks(zlogp, zoneid, &dlnum, NULL) != 0)
+                return (-1);
+
+        if (dlnum == 0)
+                return (0);
+
+        zerror(zlogp, B_FALSE, "%d datalink(s) remain in zone after shutdown",
+            dlnum);
+
+        return (-1);
+}
+
+static int
 tcp_abort_conn(zlog_t *zlogp, zoneid_t zoneid,
     const struct sockaddr_storage *local, const struct sockaddr_storage *remote)
 {
         int fd;
         struct strioctl ioc;

@@ -5213,80 +5318,79 @@
                 return (0);
         }
 }
 
 /*
- * Delete all transient VNICs belonging to this zone. A transient VNIC
+ * Delete all transient links belonging to this zone. A transient link
  * is one that is created and destroyed along with the lifetime of the
- * zone. Non-transient VNICs, ones that are assigned from the GZ to a
+ * zone. Non-transient links, ones that are assigned from the GZ to a
  * NGZ, are reassigned to the GZ in zone_shutdown() via the
  * zone-specific data (zsd) callbacks.
  */
 static int
-delete_transient_vnics(zlog_t *zlogp, zoneid_t zoneid)
+delete_transient_links(zlog_t *zlogp, zoneid_t zoneid)
 {
-        dladm_status_t status;
-        int num_links = 0;
-        datalink_id_t *links, link;
-        uint32_t link_flags;
-        datalink_class_t link_class;
-        char link_name[MAXLINKNAMELEN];
+        datalink_id_t *dllinks = NULL;
+        int dlnum = 0;
+        uint_t i;
 
-        if (zone_list_datalink(zoneid, &num_links, NULL) != 0) {
-                zerror(zlogp, B_TRUE, "unable to determine "
-                    "number of network interfaces");
+        if (fetch_zone_datalinks(zlogp, zoneid, &dlnum, &dllinks) != 0)
                 return (-1);
-        }
 
-        if (num_links == 0)
+        if (dlnum == 0)
                 return (0);
 
-        links = malloc(num_links * sizeof (datalink_id_t));
-
-        if (links == NULL) {
-                zerror(zlogp, B_TRUE, "failed to delete "
-                    "network interfaces because of alloc fail");
-                return (-1);
-        }
-
-        if (zone_list_datalink(zoneid, &num_links, links) != 0) {
-                zerror(zlogp, B_TRUE, "failed to delete "
-                    "network interfaces because of failure "
-                    "to list them");
-                return (-1);
-        }
-
-        for (int i = 0; i < num_links; i++) {
+        for (i = 0; i < dlnum; i++) {
+                char link_name[MAXLINKNAMELEN];
                 char dlerr[DLADM_STRSIZE];
-                link = links[i];
+                datalink_id_t link = dllinks[i];
+                datalink_class_t link_class;
+                dladm_status_t status;
+                uint32_t link_flags;
 
                 status = dladm_datalink_id2info(dld_handle, link, &link_flags,
                     &link_class, NULL, link_name, sizeof (link_name));
 
                 if (status != DLADM_STATUS_OK) {
-                        zerror(zlogp, B_FALSE, "failed to "
-                            "delete network interface (%u)"
-                            "due to failure to get link info: %s",
-                            link,
-                            dladm_status2str(status, dlerr));
-                        return (-1);
+                        zerror(zlogp, B_FALSE,
+                            "failed to get link info for %u: %s",
+                            link, dladm_status2str(status, dlerr));
+                        continue;
                 }
 
-                if (link_flags & DLADM_OPT_TRANSIENT) {
-                        assert(link_class & DATALINK_CLASS_VNIC);
+                if (!(link_flags & DLADM_OPT_TRANSIENT))
+                        continue;
+
+                switch (link_class) {
+                case DATALINK_CLASS_VNIC:
+                case DATALINK_CLASS_ETHERSTUB:
                         status = dladm_vnic_delete(dld_handle, link,
                             DLADM_OPT_ACTIVE);
+                        break;
+                case DATALINK_CLASS_VLAN:
+                        status = dladm_vlan_delete(dld_handle, link,
+                            DLADM_OPT_ACTIVE);
+                        break;
+                case DATALINK_CLASS_AGGR:
+                        status = dladm_aggr_delete(dld_handle, link,
+                            DLADM_OPT_ACTIVE);
+                        break;
+                default:
+                        zerror(zlogp, B_FALSE,
+                            "unhandled class for transient link %s (%u)",
+                            link_name, link);
+                        continue;
+                }
 
                         if (status != DLADM_STATUS_OK) {
-                                zerror(zlogp, B_TRUE, "failed to delete link "
-                                    "with id %d: %s", link,
-                                    dladm_status2str(status, dlerr));
-                                return (-1);
+                        zerror(zlogp, B_TRUE,
+                            "failed to delete transient link %s (%u): %s",
+                            link_name, link, dladm_status2str(status, dlerr));
                         }
                 }
-        }
 
+        free(dllinks);
         return (0);
 }
 
 int
 vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting,

@@ -5326,16 +5430,19 @@
                 if (unmount_cmd)
                         (void) lu_root_teardown(zlogp);
                 goto error;
         }
 
-        if (remove_datalink_pool(zlogp, zoneid) != 0)
-                zerror(zlogp, B_FALSE, "unable clear datalink pool property");
+        if (remove_datalink_pool(zlogp, zoneid) != 0) {
+                zerror(zlogp, B_FALSE,
+                    "unable to clear datalink pool property");
+        }
 
-        if (remove_datalink_protect(zlogp, zoneid) != 0)
+        if (remove_datalink_protect(zlogp, zoneid) != 0) {
                 zerror(zlogp, B_FALSE,
-                    "unable clear datalink protect property");
+                    "unable to clear datalink protect property");
+        }
 
         /*
          * The datalinks assigned to the zone will be removed from the NGZ as
          * part of zone_shutdown() so that we need to remove protect/pool etc.
          * before zone_shutdown(). Even if the shutdown itself fails, the zone

@@ -5397,15 +5504,21 @@
                                     "network interfaces in zone");
                                 goto error;
                         }
                         break;
                 case ZS_EXCLUSIVE:
-                        if (delete_transient_vnics(zlogp, zoneid) != 0) {
+                        if (delete_transient_links(zlogp, zoneid) != 0) {
                                 zerror(zlogp, B_FALSE, "unable to delete "
-                                    "transient vnics in zone");
+                                    "transient links in zone");
                                 goto error;
                         }
+                        if (unconfigure_exclusive_network_interfaces(zlogp,
+                            zoneid) != 0) {
+                                zerror(zlogp, B_FALSE, "unable to unconfigure "
+                                    "network interfaces in zone");
+                                goto error;
+                        }
 
                         status = dladm_zone_halt(dld_handle, zoneid);
                         if (status != DLADM_STATUS_OK) {
                                 zerror(zlogp, B_FALSE, "unable to notify "
                                     "dlmgmtd of zone halt: %s",