Merge commit '259e0b1ad1bfea762a76f0098deb8f8d8db1dfa3'
[sliver-openvswitch.git] / lib / netdev-bsd.c
index 903659f..b799cb5 100644 (file)
@@ -83,6 +83,7 @@ struct netdev_bsd {
     int ifindex;
     uint8_t etheraddr[ETH_ADDR_LEN];
     struct in_addr in4;
+    struct in_addr netmask;
     struct in6_addr in6;
     int mtu;
     int carrier;
@@ -106,9 +107,6 @@ enum {
     VALID_CARRIER = 1 << 5
 };
 
-/* An AF_INET socket (used for ioctl operations). */
-static int af_inet_sock = -1;
-
 #if defined(__NetBSD__)
 /* AF_LINK socket used for netdev_bsd_get_stats and set_etheraddr */
 static int af_link_sock = -1;
@@ -132,8 +130,6 @@ static int cache_notifier_refcount;
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
-static int netdev_bsd_do_ioctl(const char *, struct ifreq *, unsigned long cmd,
-                               const char *cmd_name);
 static void destroy_tap(int fd, const char *name);
 static int get_flags(const struct netdev *, int *flagsp);
 static int set_flags(const char *, int flags);
@@ -180,30 +176,23 @@ netdev_get_kernel_name(const struct netdev *netdev)
 static int
 netdev_bsd_init(void)
 {
+#if defined(__NetBSD__)
     static int status = -1;
 
     if (status >= 0) {  /* already initialized */
         return status;
     }
 
-    af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
-    status = af_inet_sock >= 0 ? 0 : errno;
-    if (status) {
-        VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status));
-        return status;
-    }
-
-#if defined(__NetBSD__)
     af_link_sock = socket(AF_LINK, SOCK_DGRAM, 0);
     status = af_link_sock >= 0 ? 0 : errno;
     if (status) {
         VLOG_ERR("failed to create link socket: %s", ovs_strerror(status));
-        close(af_inet_sock);
-        af_inet_sock = -1;
     }
-#endif /* defined(__NetBSD__) */
 
     return status;
+#else
+    return 0;
+#endif /* defined(__NetBSD__) */
 }
 
 /*
@@ -254,6 +243,7 @@ netdev_bsd_cache_cb(const struct rtbsd_change *change,
                 dev->cache_valid = 0;
                 netdev_bsd_changed(dev);
             }
+            netdev_close(base_dev);
         }
     } else {
         /*
@@ -266,9 +256,11 @@ netdev_bsd_cache_cb(const struct rtbsd_change *change,
         shash_init(&device_shash);
         netdev_get_devices(&netdev_bsd_class, &device_shash);
         SHASH_FOR_EACH (node, &device_shash) {
-            dev = node->data;
+            struct netdev *netdev = node->data;
+            dev = netdev_bsd_cast(netdev);
             dev->cache_valid = 0;
             netdev_bsd_changed(dev);
+            netdev_close(netdev);
         }
         shash_destroy(&device_shash);
     }
@@ -370,14 +362,15 @@ netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
     if (ioctl(netdev->tap_fd, TAPGIFNAME, &ifr) == -1) {
         /* XXX Need to destroy the device? */
         error = errno;
+        close(netdev->tap_fd);
         goto error_unref_notifier;
     }
 
     /* Change the name of the tap device */
 #if defined(SIOCSIFNAME)
     ifr.ifr_data = (void *)name;
-    if (ioctl(af_inet_sock, SIOCSIFNAME, &ifr) == -1) {
-        error = errno;
+    error = af_inet_ioctl(SIOCSIFNAME, &ifr);
+    if (error) {
         destroy_tap(netdev->tap_fd, ifr.ifr_name);
         goto error_unref_notifier;
     }
@@ -400,8 +393,8 @@ netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
     /* Turn device UP */
     ifr_set_flags(&ifr, IFF_UP);
     strncpy(ifr.ifr_name, kernel_name, sizeof ifr.ifr_name);
-    if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
-        error = errno;
+    error = af_inet_ioctl(SIOCSIFFLAGS, &ifr);
+    if (error) {
         destroy_tap(netdev->tap_fd, kernel_name);
         goto error_unref_notifier;
     }
@@ -810,7 +803,7 @@ netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup)
         struct ifreq ifr;
         int error;
 
-        error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+        error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
                                     SIOCGIFMTU, "SIOCGIFMTU");
         if (error) {
             return error;
@@ -839,15 +832,17 @@ netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
 
     if (!(netdev->cache_valid & VALID_CARRIER)) {
         struct ifmediareq ifmr;
+        int error;
 
         memset(&ifmr, 0, sizeof(ifmr));
         strncpy(ifmr.ifm_name, netdev_get_kernel_name(netdev_),
                 sizeof ifmr.ifm_name);
 
-        if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+        error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+        if (error) {
             VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
-                        netdev_get_name(netdev_), ovs_strerror(errno));
-            return errno;
+                        netdev_get_name(netdev_), ovs_strerror(error));
+            return error;
         }
 
         netdev->carrier = (ifmr.ifm_status & IFM_ACTIVE) == IFM_ACTIVE;
@@ -1052,10 +1047,11 @@ netdev_bsd_get_features(const struct netdev *netdev,
     /* We make two SIOCGIFMEDIA ioctl calls.  The first to determine the
      * number of supported modes, and a second with a buffer to retrieve
      * them. */
-    if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+    error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+    if (error) {
         VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
-                    netdev_get_name(netdev), ovs_strerror(errno));
-        return errno;
+                    netdev_get_name(netdev), ovs_strerror(error));
+        return error;
     }
 
     media_list = xcalloc(ifmr.ifm_count, sizeof(int));
@@ -1068,10 +1064,10 @@ netdev_bsd_get_features(const struct netdev *netdev,
         goto cleanup;
     }
 
-    if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+    error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr);
+    if (error) {
         VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
-                    netdev_get_name(netdev), ovs_strerror(errno));
-        error = errno;
+                    netdev_get_name(netdev), ovs_strerror(error));
         goto cleanup;
     }
 
@@ -1097,8 +1093,8 @@ cleanup:
 }
 
 /*
- * If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if
- * 'in4' is non-null) and returns true.  Otherwise, returns false.
+ * If 'netdev' has an assigned IPv4 address, sets '*in4' to that address and
+ * '*netmask' to its netmask and returns true.  Otherwise, returns false.
  */
 static int
 netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4,
@@ -1112,7 +1108,7 @@ netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4,
         int error;
 
         ifr.ifr_addr.sa_family = AF_INET;
-        error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+        error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
                                     SIOCGIFADDR, "SIOCGIFADDR");
         if (error) {
             return error;
@@ -1120,15 +1116,16 @@ netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4,
 
         sin = (struct sockaddr_in *) &ifr.ifr_addr;
         netdev->in4 = sin->sin_addr;
-        netdev->cache_valid |= VALID_IN4;
-        error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev_), &ifr,
+        error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr,
                                     SIOCGIFNETMASK, "SIOCGIFNETMASK");
         if (error) {
             return error;
         }
-        *netmask = ((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr;
+        netdev->netmask = sin->sin_addr;
+        netdev->cache_valid |= VALID_IN4;
     }
     *in4 = netdev->in4;
+    *netmask = netdev->netmask;
 
     return in4->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0;
 }
@@ -1147,11 +1144,14 @@ netdev_bsd_set_in4(struct netdev *netdev_, struct in_addr addr,
 
     error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", addr);
     if (!error) {
-        netdev->cache_valid |= VALID_IN4;
-        netdev->in4 = addr;
         if (addr.s_addr != INADDR_ANY) {
             error = do_set_addr(netdev_, SIOCSIFNETMASK,
                                 "SIOCSIFNETMASK", mask);
+            if (!error) {
+                netdev->cache_valid |= VALID_IN4;
+                netdev->in4 = addr;
+                netdev->netmask = mask;
+            }
         }
         netdev_bsd_changed(netdev);
     }
@@ -1193,36 +1193,28 @@ netdev_bsd_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
 }
 
 #if defined(__NetBSD__)
-static struct netdev *
-find_netdev_by_kernel_name(const char *kernel_name)
+static char *
+netdev_bsd_kernel_name_to_ovs_name(const char *kernel_name)
 {
+    char *ovs_name = NULL;
     struct shash device_shash;
     struct shash_node *node;
 
     shash_init(&device_shash);
     netdev_get_devices(&netdev_tap_class, &device_shash);
     SHASH_FOR_EACH(node, &device_shash) {
-        struct netdev_bsd * const dev = node->data;
+        struct netdev *netdev = node->data;
+        struct netdev_bsd * const dev = netdev_bsd_cast(netdev);
 
         if (!strcmp(dev->kernel_name, kernel_name)) {
-            shash_destroy(&device_shash);
-            return &dev->up;
+            free(ovs_name);
+            ovs_name = xstrdup(netdev_get_name(&dev->up));
         }
+        netdev_close(netdev);
     }
     shash_destroy(&device_shash);
-    return NULL;
-}
 
-static const char *
-netdev_bsd_convert_kernel_name_to_ovs_name(const char *kernel_name)
-{
-    const struct netdev * const netdev =
-      find_netdev_by_kernel_name(kernel_name);
-
-    if (netdev == NULL) {
-        return NULL;
-    }
-    return netdev_get_name(netdev);
+    return ovs_name ? ovs_name : xstrdup(kernel_name);
 }
 #endif
 
@@ -1304,18 +1296,10 @@ netdev_bsd_get_next_hop(const struct in_addr *host OVS_UNUSED,
             if ((i == RTA_IFP) && sa->sa_family == AF_LINK) {
                 const struct sockaddr_dl * const sdl =
                   (const struct sockaddr_dl *)sa;
-                const size_t nlen = sdl->sdl_nlen;
-                char * const kernel_name = xmalloc(nlen + 1);
-                const char *name;
-
-                memcpy(kernel_name, sdl->sdl_data, nlen);
-                kernel_name[nlen] = 0;
-                name = netdev_bsd_convert_kernel_name_to_ovs_name(kernel_name);
-                if (name == NULL) {
-                    ifname = xstrdup(kernel_name);
-                } else {
-                    ifname = xstrdup(name);
-                }
+                char *kernel_name;
+
+                kernel_name = xmemdup0(sdl->sdl_data, sdl->sdl_nlen);
+                ifname = netdev_bsd_kernel_name_to_ovs_name(kernel_name);
                 free(kernel_name);
             }
             RT_ADVANCE(cp, sa);
@@ -1355,7 +1339,7 @@ do_set_addr(struct netdev *netdev,
 {
     struct ifreq ifr;
     make_in4_sockaddr(&ifr.ifr_addr, addr);
-    return netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr,
+    return af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr,
                                ioctl_name);
 }
 
@@ -1541,7 +1525,7 @@ destroy_tap(int fd, const char *name)
     close(fd);
     strcpy(ifr.ifr_name, name);
     /* XXX What to do if this call fails? */
-    ioctl(af_inet_sock, SIOCIFDESTROY, &ifr);
+    af_inet_ioctl(SIOCIFDESTROY, &ifr);
 }
 
 static int
@@ -1550,7 +1534,7 @@ get_flags(const struct netdev *netdev, int *flags)
     struct ifreq ifr;
     int error;
 
-    error = netdev_bsd_do_ioctl(netdev_get_kernel_name(netdev), &ifr,
+    error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr,
                                 SIOCGIFFLAGS, "SIOCGIFFLAGS");
 
     *flags = ifr_get_flags(&ifr);
@@ -1565,7 +1549,7 @@ set_flags(const char *name, int flags)
 
     ifr_set_flags(&ifr, flags);
 
-    return netdev_bsd_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
+    return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
 }
 
 static int
@@ -1623,16 +1607,18 @@ set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
 {
 #if defined(__FreeBSD__)
     struct ifreq ifr;
+    int error;
 
     memset(&ifr, 0, sizeof ifr);
     strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
     ifr.ifr_addr.sa_family = hwaddr_family;
     ifr.ifr_addr.sa_len = hwaddr_len;
     memcpy(ifr.ifr_addr.sa_data, mac, hwaddr_len);
-    if (ioctl(af_inet_sock, SIOCSIFLLADDR, &ifr) < 0) {
+    error = af_inet_ioctl(SIOCSIFLLADDR, &ifr);
+    if (error) {
         VLOG_ERR("ioctl(SIOCSIFLLADDR) on %s device failed: %s",
-                 netdev_name, ovs_strerror(errno));
-        return errno;
+                 netdev_name, ovs_strerror(error));
+        return error;
     }
     return 0;
 #elif defined(__NetBSD__)
@@ -1690,19 +1676,6 @@ set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
 #endif
 }
 
-static int
-netdev_bsd_do_ioctl(const char *name, struct ifreq *ifr, unsigned long cmd,
-                    const char *cmd_name)
-{
-    strncpy(ifr->ifr_name, name, sizeof ifr->ifr_name);
-    if (ioctl(af_inet_sock, cmd, ifr) == -1) {
-        VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name,
-                    ovs_strerror(errno));
-        return errno;
-    }
-    return 0;
-}
-
 static int
 ifr_get_flags(const struct ifreq *ifr)
 {