Merge commit '9dc63482bbeae23dd57b0f885a3fd26b44656844'
[sliver-openvswitch.git] / lib / netdev-linux.c
index 768b4e8..cf45905 100644 (file)
@@ -355,7 +355,6 @@ static int tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes);
 struct netdev_linux {
     struct netdev up;
 
-    struct shash_node *shash_node;
     unsigned int cache_valid;
     unsigned int change_seq;
 
@@ -385,7 +384,6 @@ struct netdev_linux {
     enum netdev_features current;    /* Cached from ETHTOOL_GSET. */
     enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */
     enum netdev_features supported;  /* Cached from ETHTOOL_GSET. */
-    enum netdev_features peer;       /* Cached from ETHTOOL_GSET. */
 
     struct ethtool_drvinfo drvinfo;  /* Cached from ETHTOOL_GDRVINFO. */
     struct tc *tc;
@@ -400,21 +398,14 @@ struct netdev_rx_linux {
     int fd;
 };
 
-static const struct netdev_rx_class netdev_rx_linux_class;
-
-/* Sockets used for ioctl operations. */
-static int af_inet_sock = -1;   /* AF_INET, SOCK_DGRAM. */
-
 /* This is set pretty low because we probably won't learn anything from the
  * additional log messages. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
-static int netdev_linux_init(void);
+static void netdev_linux_run(void);
 
 static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *,
                                    int cmd, const char *cmd_name);
-static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd,
-                                 const char *cmd_name);
 static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *,
                                  int cmd, const char *cmd_name);
 static int get_flags(const struct netdev *, unsigned int *flags);
@@ -435,7 +426,7 @@ static void netdev_linux_miimon_wait(void);
 static bool
 is_netdev_linux_class(const struct netdev_class *netdev_class)
 {
-    return netdev_class->init == netdev_linux_init;
+    return netdev_class->run == netdev_linux_run;
 }
 
 static bool
@@ -455,25 +446,10 @@ netdev_linux_cast(const struct netdev *netdev)
 static struct netdev_rx_linux *
 netdev_rx_linux_cast(const struct netdev_rx *rx)
 {
-    netdev_rx_assert_class(rx, &netdev_rx_linux_class);
+    ovs_assert(is_netdev_linux_class(netdev_get_class(rx->netdev)));
     return CONTAINER_OF(rx, struct netdev_rx_linux, up);
 }
 \f
-static int
-netdev_linux_init(void)
-{
-    static int status = -1;
-    if (status < 0) {
-        /* Create AF_INET socket. */
-        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;
-}
-
 static void
 netdev_linux_run(void)
 {
@@ -539,11 +515,11 @@ static void
 netdev_linux_cache_cb(const struct rtnetlink_link_change *change,
                       void *aux OVS_UNUSED)
 {
-    struct netdev_linux *dev;
     if (change) {
         struct netdev *base_dev = netdev_from_name(change->ifname);
         if (base_dev && is_netdev_linux_class(netdev_get_class(base_dev))) {
             netdev_linux_update(netdev_linux_cast(base_dev), change);
+            netdev_close(base_dev);
         }
     } else {
         struct shash device_shash;
@@ -553,12 +529,12 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change,
         netdev_get_devices(&netdev_linux_class, &device_shash);
         SHASH_FOR_EACH (node, &device_shash) {
             struct netdev *netdev = node->data;
+            struct netdev_linux *dev = netdev_linux_cast(netdev);
             unsigned int flags;
 
-            dev = netdev_linux_cast(netdev);
-
             get_flags(&dev->up, &flags);
             netdev_linux_changed(dev, flags, 0);
+            netdev_close(netdev);
         }
         shash_destroy(&device_shash);
     }
@@ -593,29 +569,38 @@ cache_notifier_unref(void)
     }
 }
 
+static struct netdev *
+netdev_linux_alloc(void)
+{
+    struct netdev_linux *netdev = xzalloc(sizeof *netdev);
+    return &netdev->up;
+}
+
+static int
+netdev_linux_common_construct(struct netdev_linux *netdev)
+{
+    netdev->change_seq = 1;
+
+    return cache_notifier_ref();
+}
+
 /* Creates system and internal devices. */
 static int
-netdev_linux_create(const struct netdev_class *class, const char *name,
-                    struct netdev **netdevp)
+netdev_linux_construct(struct netdev *netdev_)
 {
-    struct netdev_linux *netdev;
+    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
     int error;
 
-    error = cache_notifier_ref();
+    error = netdev_linux_common_construct(netdev);
     if (error) {
         return error;
     }
 
-    netdev = xzalloc(sizeof *netdev);
-    netdev->change_seq = 1;
-    netdev_init(&netdev->up, name, class);
     error = get_flags(&netdev->up, &netdev->ifi_flags);
     if (error == ENODEV) {
-        if (class != &netdev_internal_class) {
+        if (netdev->up.netdev_class != &netdev_internal_class) {
             /* The device does not exist, so don't allow it to be opened. */
-            netdev_uninit(&netdev->up, false);
             cache_notifier_unref();
-            free(netdev);
             return ENODEV;
         } else {
             /* "Internal" netdevs have to be created as netdev objects before
@@ -625,7 +610,6 @@ netdev_linux_create(const struct netdev_class *class, const char *name,
         }
     }
 
-    *netdevp = &netdev->up;
     return 0;
 }
 
@@ -636,18 +620,15 @@ netdev_linux_create(const struct netdev_class *class, const char *name,
  * buffers, across all readers.  Therefore once data is read it will
  * be unavailable to other reads for tap devices. */
 static int
-netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
-                        const char *name, struct netdev **netdevp)
+netdev_linux_construct_tap(struct netdev *netdev_)
 {
-    struct netdev_linux *netdev;
+    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
     static const char tap_dev[] = "/dev/net/tun";
+    const char *name = netdev_->name;
     struct ifreq ifr;
     int error;
 
-    netdev = xzalloc(sizeof *netdev);
-    netdev->change_seq = 1;
-
-    error = cache_notifier_ref();
+    error = netdev_linux_common_construct(netdev);
     if (error) {
         goto error;
     }
@@ -676,8 +657,6 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
         goto error_close;
     }
 
-    netdev_init(&netdev->up, name, &netdev_tap_class);
-    *netdevp = &netdev->up;
     return 0;
 
 error_close:
@@ -685,12 +664,11 @@ error_close:
 error_unref_notifier:
     cache_notifier_unref();
 error:
-    free(netdev);
     return error;
 }
 
 static void
-netdev_linux_destroy(struct netdev *netdev_)
+netdev_linux_destruct(struct netdev *netdev_)
 {
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 
@@ -703,44 +681,59 @@ netdev_linux_destroy(struct netdev *netdev_)
     {
         close(netdev->tap_fd);
     }
-    free(netdev);
 
     cache_notifier_unref();
 }
 
+static void
+netdev_linux_dealloc(struct netdev *netdev_)
+{
+    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+    free(netdev);
+}
+
+static struct netdev_rx *
+netdev_linux_rx_alloc(void)
+{
+    struct netdev_rx_linux *rx = xzalloc(sizeof *rx);
+    return &rx->up;
+}
+
 static int
-netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
+netdev_linux_rx_construct(struct netdev_rx *rx_)
 {
+    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+    struct netdev *netdev_ = rx->up.netdev;
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
-    bool is_tap = is_tap_netdev(netdev_);
-    struct netdev_rx_linux *rx;
     int error;
-    int fd;
 
-    if (is_tap) {
-        fd = netdev->tap_fd;
+    rx->is_tap = is_tap_netdev(netdev_);
+    if (rx->is_tap) {
+        rx->fd = netdev->tap_fd;
     } else {
         struct sockaddr_ll sll;
         int ifindex;
         /* Result of tcpdump -dd inbound */
-        static struct sock_filter filt[] = {
+        static const struct sock_filter filt[] = {
             { 0x28, 0, 0, 0xfffff004 }, /* ldh [0] */
             { 0x15, 0, 1, 0x00000004 }, /* jeq #4     jt 2  jf 3 */
             { 0x6, 0, 0, 0x00000000 },  /* ret #0 */
             { 0x6, 0, 0, 0x0000ffff }   /* ret #65535 */
         };
-        static struct sock_fprog fprog = { ARRAY_SIZE(filt), filt };
+        static const struct sock_fprog fprog = {
+            ARRAY_SIZE(filt), (struct sock_filter *) filt
+        };
 
         /* Create file descriptor. */
-        fd = socket(PF_PACKET, SOCK_RAW, 0);
-        if (fd < 0) {
+        rx->fd = socket(PF_PACKET, SOCK_RAW, 0);
+        if (rx->fd < 0) {
             error = errno;
             VLOG_ERR("failed to create raw socket (%s)", ovs_strerror(error));
             goto error;
         }
 
         /* Set non-blocking mode. */
-        error = set_nonblocking(fd);
+        error = set_nonblocking(rx->fd);
         if (error) {
             goto error;
         }
@@ -756,7 +749,7 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
         sll.sll_family = AF_PACKET;
         sll.sll_ifindex = ifindex;
         sll.sll_protocol = (OVS_FORCE unsigned short int) htons(ETH_P_ALL);
-        if (bind(fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
+        if (bind(rx->fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
             error = errno;
             VLOG_ERR("%s: failed to bind raw socket (%s)",
                      netdev_get_name(netdev_), ovs_strerror(error));
@@ -764,44 +757,45 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
         }
 
         /* Filter for only inbound packets. */
-        error = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog,
+        error = setsockopt(rx->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog,
                            sizeof fprog);
         if (error) {
             error = errno;
-            VLOG_ERR("%s: failed attach filter (%s)",
+            VLOG_ERR("%s: failed to attach filter (%s)",
                      netdev_get_name(netdev_), ovs_strerror(error));
             goto error;
         }
     }
 
-    rx = xmalloc(sizeof *rx);
-    netdev_rx_init(&rx->up, netdev_, &netdev_rx_linux_class);
-    rx->is_tap = is_tap;
-    rx->fd = fd;
-
-    *rxp = &rx->up;
     return 0;
 
 error:
-    if (fd >= 0) {
-        close(fd);
+    if (rx->fd >= 0) {
+        close(rx->fd);
     }
     return error;
 }
 
 static void
-netdev_rx_linux_destroy(struct netdev_rx *rx_)
+netdev_linux_rx_destruct(struct netdev_rx *rx_)
 {
     struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
 
     if (!rx->is_tap) {
         close(rx->fd);
     }
+}
+
+static void
+netdev_linux_rx_dealloc(struct netdev_rx *rx_)
+{
+    struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
+
     free(rx);
 }
 
 static int
-netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size)
+netdev_linux_rx_recv(struct netdev_rx *rx_, void *data, size_t size)
 {
     struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
     ssize_t retval;
@@ -824,20 +818,20 @@ netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size)
 }
 
 static void
-netdev_rx_linux_wait(struct netdev_rx *rx_)
+netdev_linux_rx_wait(struct netdev_rx *rx_)
 {
     struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
     poll_fd_wait(rx->fd, POLLIN);
 }
 
 static int
-netdev_rx_linux_drain(struct netdev_rx *rx_)
+netdev_linux_rx_drain(struct netdev_rx *rx_)
 {
     struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_);
     if (rx->is_tap) {
         struct ifreq ifr;
-        int error = netdev_linux_do_ioctl(netdev_rx_get_name(rx_), &ifr,
-                                          SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
+        int error = af_inet_ifreq_ioctl(netdev_rx_get_name(rx_), &ifr,
+                                        SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
         if (error) {
             return error;
         }
@@ -1021,8 +1015,8 @@ netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup)
         struct ifreq ifr;
         int error;
 
-        error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
-                                      SIOCGIFMTU, "SIOCGIFMTU");
+        error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr,
+                                    SIOCGIFMTU, "SIOCGIFMTU");
 
         netdev->netdev_mtu_error = error;
         netdev->mtu = ifr.ifr_mtu;
@@ -1055,8 +1049,8 @@ netdev_linux_set_mtu(const struct netdev *netdev_, int mtu)
         netdev->cache_valid &= ~VALID_MTU;
     }
     ifr.ifr_mtu = mtu;
-    error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
-                                  SIOCSIFMTU, "SIOCSIFMTU");
+    error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr,
+                                SIOCSIFMTU, "SIOCSIFMTU");
     if (!error || error == ENODEV) {
         netdev->netdev_mtu_error = error;
         netdev->mtu = ifr.ifr_mtu;
@@ -1105,7 +1099,7 @@ netdev_linux_do_miimon(const char *name, int cmd, const char *cmd_name,
 
     memset(&ifr, 0, sizeof ifr);
     memcpy(&ifr.ifr_data, data, sizeof *data);
-    error = netdev_linux_do_ioctl(name, &ifr, cmd, cmd_name);
+    error = af_inet_ifreq_ioctl(name, &ifr, cmd, cmd_name);
     memcpy(data, &ifr.ifr_data, sizeof *data);
 
     return error;
@@ -1184,6 +1178,7 @@ netdev_linux_miimon_run(void)
         bool miimon;
 
         if (dev->miimon_interval <= 0 || !timer_expired(&dev->miimon_timer)) {
+            netdev_close(netdev);
             continue;
         }
 
@@ -1194,6 +1189,7 @@ netdev_linux_miimon_run(void)
         }
 
         timer_set_duration(&dev->miimon_timer, dev->miimon_interval);
+        netdev_close(netdev);
     }
 
     shash_destroy(&device_shash);
@@ -1214,6 +1210,7 @@ netdev_linux_miimon_wait(void)
         if (dev->miimon_interval > 0) {
             timer_wait(&dev->miimon_timer);
         }
+        netdev_close(netdev);
     }
     shash_destroy(&device_shash);
 }
@@ -1633,18 +1630,14 @@ netdev_linux_read_features(struct netdev_linux *netdev)
         netdev->current |= NETDEV_F_AUTONEG;
     }
 
-    /* Peer advertisements. */
-    netdev->peer = 0;                  /* XXX */
-
 out:
     netdev->cache_valid |= VALID_FEATURES;
     netdev->get_features_error = error;
 }
 
-/* Stores the features supported by 'netdev' into each of '*current',
- * '*advertised', '*supported', and '*peer' that are non-null.  Each value is a
- * bitmap of NETDEV_* bits.  Returns 0 if successful, otherwise a positive
- * errno value. */
+/* Stores the features supported by 'netdev' into of '*current', '*advertised',
+ * '*supported', and '*peer'.  Each value is a bitmap of NETDEV_* bits.
+ * Returns 0 if successful, otherwise a positive errno value. */
 static int
 netdev_linux_get_features(const struct netdev *netdev_,
                           enum netdev_features *current,
@@ -1660,7 +1653,7 @@ netdev_linux_get_features(const struct netdev *netdev_,
         *current = netdev->current;
         *advertised = netdev->advertised;
         *supported = netdev->supported;
-        *peer = netdev->peer;
+        *peer = 0;              /* XXX */
     }
     return netdev->get_features_error;
 }
@@ -2194,11 +2187,10 @@ do_set_addr(struct netdev *netdev,
             int ioctl_nr, const char *ioctl_name, struct in_addr addr)
 {
     struct ifreq ifr;
-    ovs_strzcpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name);
-    make_in4_sockaddr(&ifr.ifr_addr, addr);
 
-    return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr,
-                                 ioctl_name);
+    make_in4_sockaddr(&ifr.ifr_addr, addr);
+    return af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr,
+                               ioctl_name);
 }
 
 /* Adds 'router' as a default IP gateway. */
@@ -2214,7 +2206,7 @@ netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router)
     make_in4_sockaddr(&rt.rt_gateway, router);
     make_in4_sockaddr(&rt.rt_genmask, any);
     rt.rt_flags = RTF_UP | RTF_GATEWAY;
-    error = ioctl(af_inet_sock, SIOCADDRT, &rt) < 0 ? errno : 0;
+    error = af_inet_ioctl(SIOCADDRT, &rt);
     if (error) {
         VLOG_WARN("ioctl(SIOCADDRT): %s", ovs_strerror(error));
     }
@@ -2340,7 +2332,7 @@ netdev_linux_arp_lookup(const struct netdev *netdev,
     r.arp_flags = 0;
     ovs_strzcpy(r.arp_dev, netdev_get_name(netdev), sizeof r.arp_dev);
     COVERAGE_INC(netdev_arp_lookup);
-    retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0;
+    retval = af_inet_ioctl(SIOCGARP, &r);
     if (!retval) {
         memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN);
     } else if (retval != ENXIO) {
@@ -2401,23 +2393,23 @@ netdev_linux_change_seq(const struct netdev *netdev)
     return netdev_linux_cast(netdev)->change_seq;
 }
 
-#define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS,  \
+#define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS, SET_STATS,  \
                            GET_FEATURES, GET_STATUS)            \
 {                                                               \
     NAME,                                                       \
                                                                 \
-    netdev_linux_init,                                          \
+    NULL,                                                       \
     netdev_linux_run,                                           \
     netdev_linux_wait,                                          \
                                                                 \
-    CREATE,                                                     \
-    netdev_linux_destroy,                                       \
+    netdev_linux_alloc,                                         \
+    CONSTRUCT,                                                  \
+    netdev_linux_destruct,                                      \
+    netdev_linux_dealloc,                                       \
     NULL,                       /* get_config */                \
     NULL,                       /* set_config */                \
     NULL,                       /* get_tunnel_config */         \
                                                                 \
-    netdev_linux_rx_open,                                       \
-                                                                \
     netdev_linux_send,                                          \
     netdev_linux_send_wait,                                     \
                                                                 \
@@ -2457,13 +2449,21 @@ netdev_linux_change_seq(const struct netdev *netdev)
                                                                 \
     netdev_linux_update_flags,                                  \
                                                                 \
-    netdev_linux_change_seq                                     \
+    netdev_linux_change_seq,                                    \
+                                                                \
+    netdev_linux_rx_alloc,                                      \
+    netdev_linux_rx_construct,                                  \
+    netdev_linux_rx_destruct,                                   \
+    netdev_linux_rx_dealloc,                                    \
+    netdev_linux_rx_recv,                                       \
+    netdev_linux_rx_wait,                                       \
+    netdev_linux_rx_drain,                                      \
 }
 
 const struct netdev_class netdev_linux_class =
     NETDEV_LINUX_CLASS(
         "system",
-        netdev_linux_create,
+        netdev_linux_construct,
         netdev_linux_get_stats,
         NULL,                    /* set_stats */
         netdev_linux_get_features,
@@ -2472,7 +2472,7 @@ const struct netdev_class netdev_linux_class =
 const struct netdev_class netdev_tap_class =
     NETDEV_LINUX_CLASS(
         "tap",
-        netdev_linux_create_tap,
+        netdev_linux_construct_tap,
         netdev_tap_get_stats,
         NULL,                   /* set_stats */
         netdev_linux_get_features,
@@ -2481,18 +2481,11 @@ const struct netdev_class netdev_tap_class =
 const struct netdev_class netdev_internal_class =
     NETDEV_LINUX_CLASS(
         "internal",
-        netdev_linux_create,
+        netdev_linux_construct,
         netdev_internal_get_stats,
         netdev_internal_set_stats,
         NULL,                  /* get_features */
         netdev_internal_get_status);
-
-static const struct netdev_rx_class netdev_rx_linux_class = {
-    netdev_rx_linux_destroy,
-    netdev_rx_linux_recv,
-    netdev_rx_linux_wait,
-    netdev_rx_linux_drain,
-};
 \f
 /* HTB traffic control class. */
 
@@ -4196,14 +4189,6 @@ tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes)
 \f
 /* Linux-only functions declared in netdev-linux.h  */
 
-/* Returns a fd for an AF_INET socket or a negative errno value. */
-int
-netdev_linux_get_af_inet_sock(void)
-{
-    int error = netdev_linux_init();
-    return error ? -error : af_inet_sock;
-}
-
 /* Modifies the 'flag' bit in ethtool's flags field for 'netdev'.  If
  * 'enable' is true, the bit is set.  Otherwise, it is cleared. */
 int
@@ -4396,8 +4381,7 @@ get_flags(const struct netdev *dev, unsigned int *flags)
     int error;
 
     *flags = 0;
-    error = netdev_linux_do_ioctl(dev->name, &ifr, SIOCGIFFLAGS,
-                                  "SIOCGIFFLAGS");
+    error = af_inet_ifreq_ioctl(dev->name, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS");
     if (!error) {
         *flags = ifr.ifr_flags;
     }
@@ -4410,20 +4394,23 @@ set_flags(const char *name, unsigned int flags)
     struct ifreq ifr;
 
     ifr.ifr_flags = flags;
-    return netdev_linux_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
+    return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
 }
 
 static int
 do_get_ifindex(const char *netdev_name)
 {
     struct ifreq ifr;
+    int error;
 
     ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
     COVERAGE_INC(netdev_get_ifindex);
-    if (ioctl(af_inet_sock, SIOCGIFINDEX, &ifr) < 0) {
+
+    error = af_inet_ioctl(SIOCGIFINDEX, &ifr);
+    if (error) {
         VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s",
-                     netdev_name, ovs_strerror(errno));
-        return -errno;
+                     netdev_name, ovs_strerror(error));
+        return -error;
     }
     return ifr.ifr_ifindex;
 }
@@ -4455,18 +4442,20 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
 {
     struct ifreq ifr;
     int hwaddr_family;
+    int error;
 
     memset(&ifr, 0, sizeof ifr);
     ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
     COVERAGE_INC(netdev_get_hwaddr);
-    if (ioctl(af_inet_sock, SIOCGIFHWADDR, &ifr) < 0) {
+    error = af_inet_ioctl(SIOCGIFHWADDR, &ifr);
+    if (error) {
         /* ENODEV probably means that a vif disappeared asynchronously and
          * hasn't been removed from the database yet, so reduce the log level
          * to INFO for that case. */
-        VLOG(errno == ENODEV ? VLL_INFO : VLL_ERR,
+        VLOG(error == ENODEV ? VLL_INFO : VLL_ERR,
              "ioctl(SIOCGIFHWADDR) on %s device failed: %s",
-             netdev_name, ovs_strerror(errno));
-        return errno;
+             netdev_name, ovs_strerror(error));
+        return error;
     }
     hwaddr_family = ifr.ifr_hwaddr.sa_family;
     if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
@@ -4482,18 +4471,19 @@ set_etheraddr(const char *netdev_name,
               const uint8_t mac[ETH_ADDR_LEN])
 {
     struct ifreq ifr;
+    int error;
 
     memset(&ifr, 0, sizeof ifr);
     ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
     ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
     memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN);
     COVERAGE_INC(netdev_set_hwaddr);
-    if (ioctl(af_inet_sock, SIOCSIFHWADDR, &ifr) < 0) {
+    error = af_inet_ioctl(SIOCSIFHWADDR, &ifr);
+    if (error) {
         VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s",
-                 netdev_name, ovs_strerror(errno));
-        return errno;
+                 netdev_name, ovs_strerror(error));
     }
-    return 0;
+    return error;
 }
 
 static int
@@ -4501,37 +4491,24 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd,
                         int cmd, const char *cmd_name)
 {
     struct ifreq ifr;
+    int error;
 
     memset(&ifr, 0, sizeof ifr);
     ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
     ifr.ifr_data = (caddr_t) ecmd;
 
     ecmd->cmd = cmd;
-    if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) {
-        return 0;
-    } else {
-        if (errno != EOPNOTSUPP) {
+    error = af_inet_ioctl(SIOCETHTOOL, &ifr);
+    if (error) {
+        if (error != EOPNOTSUPP) {
             VLOG_WARN_RL(&rl, "ethtool command %s on network device %s "
-                         "failed: %s", cmd_name, name, ovs_strerror(errno));
+                         "failed: %s", cmd_name, name, ovs_strerror(error));
         } else {
             /* The device doesn't support this operation.  That's pretty
              * common, so there's no point in logging anything. */
         }
-        return errno;
-    }
-}
-
-static int
-netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd,
-                      const char *cmd_name)
-{
-    ovs_strzcpy(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;
+    return error;
 }
 
 static int
@@ -4542,7 +4519,7 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip,
     int error;
 
     ifr.ifr_addr.sa_family = AF_INET;
-    error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name);
+    error = af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name);
     if (!error) {
         const struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *,
                                                      &ifr.ifr_addr);