netdev-bsd: Correct pointer use after refactoring.
[sliver-openvswitch.git] / lib / netdev-bsd.c
index a5b7c93..87ac79f 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2011 Gaetano Catalli.
+ * Copyright (c) 2013 YAMAMOTO Takashi.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,6 +40,9 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/sysctl.h>
+#if defined(__NetBSD__)
+#include <net/route.h>
+#endif
 
 #include "rtbsd.h"
 #include "coverage.h"
@@ -105,6 +109,11 @@ enum {
 /* 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;
+#endif /* defined(__NetBSD__) */
+
 #define PCAP_SNAPLEN 2048
 
 
@@ -179,10 +188,20 @@ netdev_bsd_init(void)
 
     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", 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", strerror(status));
+        close(af_inet_sock);
+        af_inet_sock = -1;
     }
+#endif /* defined(__NetBSD__) */
 
     return status;
 }
@@ -844,10 +863,38 @@ netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
     return 0;
 }
 
+static void
+convert_stats(struct netdev_stats *stats, const struct if_data *ifd)
+{
+    /*
+     * note: UINT64_MAX means unsupported
+     */
+    stats->rx_packets = ifd->ifi_ipackets;
+    stats->tx_packets = ifd->ifi_opackets;
+    stats->rx_bytes = ifd->ifi_obytes;
+    stats->tx_bytes = ifd->ifi_ibytes;
+    stats->rx_errors = ifd->ifi_ierrors;
+    stats->tx_errors = ifd->ifi_oerrors;
+    stats->rx_dropped = ifd->ifi_iqdrops;
+    stats->tx_dropped = UINT64_MAX;
+    stats->multicast = ifd->ifi_imcasts;
+    stats->collisions = ifd->ifi_collisions;
+    stats->rx_length_errors = UINT64_MAX;
+    stats->rx_over_errors = UINT64_MAX;
+    stats->rx_crc_errors = UINT64_MAX;
+    stats->rx_frame_errors = UINT64_MAX;
+    stats->rx_fifo_errors = UINT64_MAX;
+    stats->rx_missed_errors = UINT64_MAX;
+    stats->tx_aborted_errors = UINT64_MAX;
+    stats->tx_carrier_errors = UINT64_MAX;
+    stats->tx_fifo_errors = UINT64_MAX;
+    stats->tx_heartbeat_errors = UINT64_MAX;
+    stats->tx_window_errors = UINT64_MAX;
+}
+
 /* Retrieves current device stats for 'netdev'. */
 static int
-netdev_bsd_get_stats(const struct netdev *netdev_ OVS_UNUSED,
-                     struct netdev_stats *stats)
+netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
 {
 #if defined(__FreeBSD__)
     int if_count, i;
@@ -880,38 +927,29 @@ netdev_bsd_get_stats(const struct netdev *netdev_ OVS_UNUSED,
                         netdev_get_name(netdev_), strerror(errno));
             return errno;
         } else if (!strcmp(ifmd.ifmd_name, netdev_get_name(netdev_))) {
-            stats->rx_packets = ifmd.ifmd_data.ifi_ipackets;
-            stats->tx_packets = ifmd.ifmd_data.ifi_opackets;
-            stats->rx_bytes = ifmd.ifmd_data.ifi_ibytes;
-            stats->tx_bytes = ifmd.ifmd_data.ifi_obytes;
-            stats->rx_errors = ifmd.ifmd_data.ifi_ierrors;
-            stats->tx_errors = ifmd.ifmd_data.ifi_oerrors;
-            stats->rx_dropped = ifmd.ifmd_data.ifi_iqdrops;
-            stats->tx_dropped = UINT64_MAX;
-            stats->multicast = ifmd.ifmd_data.ifi_imcasts;
-            stats->collisions = ifmd.ifmd_data.ifi_collisions;
-
-            stats->rx_length_errors = UINT64_MAX;
-            stats->rx_over_errors = UINT64_MAX;
-            stats->rx_crc_errors = UINT64_MAX;
-            stats->rx_frame_errors = UINT64_MAX;
-            stats->rx_fifo_errors = UINT64_MAX;
-            stats->rx_missed_errors = UINT64_MAX;
-
-            stats->tx_aborted_errors = UINT64_MAX;
-            stats->tx_carrier_errors = UINT64_MAX;
-            stats->tx_fifo_errors = UINT64_MAX;
-            stats->tx_heartbeat_errors = UINT64_MAX;
-            stats->tx_window_errors = UINT64_MAX;
+            convert_stats(stats, &ifmd.ifmd_data);
             break;
         }
     }
 
     return 0;
-#else
-    /* XXXnotyet */
-    memset(stats, 0, sizeof(*stats));
+#elif defined(__NetBSD__)
+    struct ifdatareq ifdr;
+    int saved_errno;
+    int ret;
+
+    memset(&ifdr, 0, sizeof(ifdr));
+    strncpy(ifdr.ifdr_name, netdev_get_kernel_name(netdev_),
+            sizeof(ifdr.ifdr_name));
+    ret = ioctl(af_link_sock, SIOCGIFDATA, &ifdr);
+    saved_errno = errno;
+    if (ret == -1) {
+        return saved_errno;
+    }
+    convert_stats(stats, &ifdr.ifdr_data);
     return 0;
+#else
+#error not implemented
 #endif
 }
 
@@ -1153,6 +1191,149 @@ netdev_bsd_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
     return 0;
 }
 
+#if defined(__NetBSD__)
+static struct netdev *
+find_netdev_by_kernel_name(const char *kernel_name)
+{
+    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;
+
+        if (!strcmp(dev->kernel_name, kernel_name)) {
+            shash_destroy(&device_shash);
+            return &dev->up;
+        }
+    }
+    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);
+}
+#endif
+
+static int
+netdev_bsd_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
+                        char **netdev_name)
+{
+#if defined(__NetBSD__)
+    static int seq = 0;
+    struct sockaddr_in sin;
+    struct sockaddr_dl sdl;
+    int s;
+    int i;
+    struct {
+        struct rt_msghdr h;
+        char space[512];
+    } buf;
+    struct rt_msghdr *rtm = &buf.h;
+    const pid_t pid = getpid();
+    char *cp;
+    ssize_t ssz;
+    bool gateway = false;
+    char *ifname = NULL;
+    int saved_errno;
+
+    memset(next_hop, 0, sizeof(*next_hop));
+    *netdev_name = NULL;
+
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_len = sizeof(sin);
+    sin.sin_family = AF_INET;
+    sin.sin_port = 0;
+    sin.sin_addr = *host;
+
+    memset(&sdl, 0, sizeof(sdl));
+    sdl.sdl_len = sizeof(sdl);
+    sdl.sdl_family = AF_LINK;
+
+    s = socket(PF_ROUTE, SOCK_RAW, 0);
+    memset(&buf, 0, sizeof(buf));
+    rtm->rtm_flags = RTF_HOST|RTF_UP;
+    rtm->rtm_version = RTM_VERSION;
+    rtm->rtm_addrs = RTA_DST|RTA_IFP;
+    cp = (void *)&buf.space;
+    memcpy(cp, &sin, sizeof(sin));
+    RT_ADVANCE(cp, (struct sockaddr *)(void *)&sin);
+    memcpy(cp, &sdl, sizeof(sdl));
+    RT_ADVANCE(cp, (struct sockaddr *)(void *)&sdl);
+    rtm->rtm_msglen = cp - (char *)(void *)rtm;
+    rtm->rtm_seq = ++seq;
+    rtm->rtm_type = RTM_GET;
+    rtm->rtm_pid = pid;
+    write(s, rtm, rtm->rtm_msglen);
+    memset(&buf, 0, sizeof(buf));
+    do {
+        ssz = read(s, &buf, sizeof(buf));
+    } while (ssz > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
+    saved_errno = errno;
+    close(s);
+    if (ssz <= 0) {
+        if (ssz < 0) {
+            return saved_errno;
+        }
+        return EPIPE; /* XXX */
+    }
+    cp = (void *)&buf.space;
+    for (i = 1; i; i <<= 1) {
+        if ((rtm->rtm_addrs & i) != 0) {
+            const struct sockaddr *sa = (const void *)cp;
+
+            if ((i == RTA_GATEWAY) && sa->sa_family == AF_INET) {
+                const struct sockaddr_in * const sin =
+                  (const struct sockaddr_in *)sa;
+
+                *next_hop = sin->sin_addr;
+                gateway = true;
+            }
+            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);
+                }
+                free(kernel_name);
+            }
+            RT_ADVANCE(cp, sa);
+        }
+    }
+    if (ifname == NULL) {
+        return ENXIO;
+    }
+    if (!gateway) {
+        *next_hop = *host;
+    }
+    *netdev_name = ifname;
+    VLOG_DBG("host " IP_FMT " next-hop " IP_FMT " if %s",
+      IP_ARGS(host->s_addr), IP_ARGS(next_hop->s_addr), *netdev_name);
+    return 0;
+#else
+    return EOPNOTSUPP;
+#endif
+}
+
 static void
 make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr)
 {
@@ -1278,7 +1459,7 @@ const struct netdev_class netdev_bsd_class = {
     netdev_bsd_set_in4,
     netdev_bsd_get_in6,
     NULL, /* add_router */
-    NULL, /* get_next_hop */
+    netdev_bsd_get_next_hop,
     NULL, /* get_status */
     NULL, /* arp_lookup */
 
@@ -1333,7 +1514,7 @@ const struct netdev_class netdev_tap_class = {
     netdev_bsd_set_in4,
     netdev_bsd_get_in6,
     NULL, /* add_router */
-    NULL, /* get_next_hop */
+    netdev_bsd_get_next_hop,
     NULL, /* get_status */
     NULL, /* arp_lookup */
 
@@ -1438,9 +1619,7 @@ set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
               int hwaddr_len OVS_UNUSED,
               const uint8_t mac[ETH_ADDR_LEN] OVS_UNUSED)
 {
-#if defined(__NetBSD__)
-    return ENOTSUP; /* XXX */
-#else
+#if defined(__FreeBSD__)
     struct ifreq ifr;
 
     memset(&ifr, 0, sizeof ifr);
@@ -1454,6 +1633,58 @@ set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
         return errno;
     }
     return 0;
+#elif defined(__NetBSD__)
+    struct if_laddrreq req;
+    struct sockaddr_dl *sdl;
+    struct sockaddr_storage oldaddr;
+    int ret;
+
+    /*
+     * get the old address, add new one, and then remove old one.
+     */
+
+    if (hwaddr_len != ETH_ADDR_LEN) {
+        /* just to be safe about sockaddr storage size */
+        return EOPNOTSUPP;
+    }
+    memset(&req, 0, sizeof(req));
+    strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
+    req.addr.ss_len = sizeof(req.addr);
+    req.addr.ss_family = hwaddr_family;
+    sdl = (struct sockaddr_dl *)&req.addr;
+    sdl->sdl_alen = hwaddr_len;
+    ret = ioctl(af_link_sock, SIOCGLIFADDR, &req);
+    if (ret == -1) {
+        return errno;
+    }
+    if (!memcmp(&sdl->sdl_data[sdl->sdl_nlen], mac, hwaddr_len)) {
+        return 0;
+    }
+    oldaddr = req.addr;
+
+    memset(&req, 0, sizeof(req));
+    strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
+    req.flags = IFLR_ACTIVE;
+    sdl = (struct sockaddr_dl *)&req.addr;
+    sdl->sdl_len = offsetof(struct sockaddr_dl, sdl_data) + hwaddr_len;
+    sdl->sdl_alen = hwaddr_len;
+    sdl->sdl_family = hwaddr_family;
+    memcpy(sdl->sdl_data, mac, hwaddr_len);
+    ret = ioctl(af_link_sock, SIOCALIFADDR, &req);
+    if (ret == -1) {
+        return errno;
+    }
+
+    memset(&req, 0, sizeof(req));
+    strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
+    req.addr = oldaddr;
+    ret = ioctl(af_link_sock, SIOCDLIFADDR, &req);
+    if (ret == -1) {
+        return errno;
+    }
+    return 0;
+#else
+#error not implemented
 #endif
 }
 
@@ -1474,9 +1705,9 @@ static int
 ifr_get_flags(const struct ifreq *ifr)
 {
 #ifdef HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH
-    return (ifr.ifr_flagshigh << 16) | ifr.ifr_flags;
+    return (ifr->ifr_flagshigh << 16) | ifr->ifr_flags;
 #else
-    return ifr.ifr_flags;
+    return ifr->ifr_flags;
 #endif
 }