Print this page
usr/src/cmd/dlmgmtd/dlmgmt_door.c
        
*** 23,32 ****
--- 23,33 ----
   * 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,93 ****
--- 85,95 ----
  
  #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,3042 ****
                  di_prof_fini(prof);
  
          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;
          dladm_status_t err;
  
          if (strlen(pool_name) == 0)
                  return (0);
  
--- 3028,3106 ----
                  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;
          dladm_status_t err;
  
          if (strlen(pool_name) == 0)
                  return (0);
  
*** 3052,3089 ****
                  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);
!                 }
  
!                 if (dlnum == 0)
!                         return (0);
! 
!                 if ((dllinks = malloc(dlnum * sizeof (datalink_id_t)))
!                     == NULL) {
!                         zerror(zlogp, B_TRUE, "memory allocation failed");
                          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",
                              NULL, 0, DLADM_OPT_ACTIVE);
                          if (err != DLADM_STATUS_OK) {
                                  zerror(zlogp, B_TRUE,
                                      "WARNING: unable to clear pool");
                          }
--- 3116,3134 ----
                  else
                          iptype = ZS_SHARED;
          }
  
          if (iptype == ZS_EXCLUSIVE) {
!                 datalink_id_t *dllinks = NULL;
!                 int dlnum = 0;
  
!                 if (fetch_zone_datalinks(zlogp, zoneid, &dlnum, &dllinks) != 0)
                          return (-1);
  
                  bzero(pool_name, sizeof (pool_name));
!                 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,3108 ****
  {
          ushort_t flags;
          zone_iptype_t iptype;
          int i, dlnum = 0;
          dladm_status_t dlstatus;
!         datalink_id_t *dllink, *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");
--- 3143,3153 ----
  {
          ushort_t flags;
          zone_iptype_t iptype;
          int i, dlnum = 0;
          dladm_status_t dlstatus;
!         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,3174 ****
  
          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.
           */
-         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");
                  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++) {
                  char dlerr[DLADM_STRSIZE];
  
!                 dlstatus = dladm_set_linkprop(dld_handle, *dllink,
                      "protection", NULL, 0, DLADM_OPT_ACTIVE);
                  if (dlstatus == DLADM_STATUS_NOTFOUND) {
                          /* datalink does not belong to the GZ */
                          continue;
                  }
!                 if (dlstatus != DLADM_STATUS_OK)
                          zerror(zlogp, B_FALSE,
!                             "clear 'protection' link property: %s",
!                             dladm_status2str(dlstatus, dlerr));
  
!                 dlstatus = dladm_set_linkprop(dld_handle, *dllink,
                      "allowed-ips", NULL, 0, DLADM_OPT_ACTIVE);
!                 if (dlstatus != DLADM_STATUS_OK)
                          zerror(zlogp, B_FALSE,
!                             "clear 'allowed-ips' link property: %s",
!                             dladm_status2str(dlstatus, dlerr));
          }
          free(dllinks);
          return (0);
  }
  
  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;
--- 3162,3279 ----
  
          if (iptype != ZS_EXCLUSIVE)
                  return (0);
  
          /*
!          * Get the datalink count and for each datalink, attempt to clear the
!          * protection and allowed_ips properties.
           */
  
!         if (fetch_zone_datalinks(zlogp, zoneid, &dlnum, &dllinks) != 0)
                  return (-1);
  
!         for (i = 0; i < dlnum; i++) {
                  char dlerr[DLADM_STRSIZE];
  
!                 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) {
                          zerror(zlogp, B_FALSE,
!                             "clear link %d 'protection' link property: %s",
!                             dllinks[i], dladm_status2str(dlstatus, dlerr));
!                 }
  
!                 dlstatus = dladm_set_linkprop(dld_handle, dllinks[i],
                      "allowed-ips", NULL, 0, DLADM_OPT_ACTIVE);
!                 if (dlstatus != DLADM_STATUS_OK) {
                          zerror(zlogp, B_FALSE,
!                             "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,5292 ****
                  return (0);
          }
  }
  
  /*
!  * Delete all transient VNICs belonging to this zone. A transient VNIC
   * 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
   * 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)
  {
!         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];
  
!         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);
  
!         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++) {
                  char dlerr[DLADM_STRSIZE];
!                 link = links[i];
  
                  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);
                  }
  
!                 if (link_flags & DLADM_OPT_TRANSIENT) {
!                         assert(link_class & DATALINK_CLASS_VNIC);
                          status = dladm_vnic_delete(dld_handle, link,
                              DLADM_OPT_ACTIVE);
  
                          if (status != DLADM_STATUS_OK) {
!                                 zerror(zlogp, B_TRUE, "failed to delete link "
!                                     "with id %d: %s", link,
!                                     dladm_status2str(status, dlerr));
!                                 return (-1);
                          }
                  }
-         }
  
          return (0);
  }
  
  int
  vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting,
--- 5318,5396 ----
                  return (0);
          }
  }
  
  /*
!  * 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 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_links(zlog_t *zlogp, zoneid_t zoneid)
  {
!         datalink_id_t *dllinks = NULL;
!         int dlnum = 0;
!         uint_t i;
  
!         if (fetch_zone_datalinks(zlogp, zoneid, &dlnum, &dllinks) != 0)
                  return (-1);
  
!         if (dlnum == 0)
                  return (0);
  
!         for (i = 0; i < dlnum; i++) {
!                 char link_name[MAXLINKNAMELEN];
                  char dlerr[DLADM_STRSIZE];
!                 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 get link info for %u: %s",
!                             link, dladm_status2str(status, dlerr));
!                         continue;
                  }
  
!                 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 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,5341 ****
                  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_protect(zlogp, zoneid) != 0)
                  zerror(zlogp, B_FALSE,
!                     "unable 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
--- 5430,5448 ----
                  if (unmount_cmd)
                          (void) lu_root_teardown(zlogp);
                  goto error;
          }
  
!         if (remove_datalink_pool(zlogp, zoneid) != 0) {
!                 zerror(zlogp, B_FALSE,
!                     "unable to clear datalink pool property");
!         }
  
!         if (remove_datalink_protect(zlogp, zoneid) != 0) {
                  zerror(zlogp, B_FALSE,
!                     "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,5411 ****
                                      "network interfaces in zone");
                                  goto error;
                          }
                          break;
                  case ZS_EXCLUSIVE:
!                         if (delete_transient_vnics(zlogp, zoneid) != 0) {
                                  zerror(zlogp, B_FALSE, "unable to delete "
!                                     "transient vnics 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",
--- 5504,5524 ----
                                      "network interfaces in zone");
                                  goto error;
                          }
                          break;
                  case ZS_EXCLUSIVE:
!                         if (delete_transient_links(zlogp, zoneid) != 0) {
                                  zerror(zlogp, B_FALSE, "unable to delete "
!                                     "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",