netdev-linux: Support 'send' for netdevs opened with NETDEV_ETH_TYPE_NONE.
[sliver-openvswitch.git] / lib / netdev-linux.c
index dce7592..eecaaa5 100644 (file)
@@ -64,7 +64,7 @@
 #include "rtnetlink-link.h"
 #include "socket-util.h"
 #include "shash.h"
-#include "svec.h"
+#include "sset.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_linux);
@@ -368,8 +368,9 @@ struct netdev_linux {
     int fd;
 };
 
-/* An AF_INET socket (used for ioctl operations). */
-static int af_inet_sock = -1;
+/* Sockets used for ioctl operations. */
+static int af_inet_sock = -1;   /* AF_INET, SOCK_DGRAM. */
+static int af_packet_sock = -1; /* AF_PACKET, SOCK_RAW. */
 
 /* A Netlink routing socket that is not subscribed to any multicast groups. */
 static struct nl_sock *rtnl_sock;
@@ -443,6 +444,14 @@ netdev_linux_init(void)
         status = af_inet_sock >= 0 ? 0 : errno;
         if (status) {
             VLOG_ERR("failed to create inet socket: %s", strerror(status));
+        } else {
+            /* Create AF_PACKET socket. */
+            af_packet_sock = socket(AF_PACKET, SOCK_RAW, 0);
+            status = af_packet_sock >= 0 ? 0 : errno;
+            if (status) {
+                VLOG_ERR("failed to create packet socket: %s",
+                         strerror(status));
+            }
         }
 
         /* Create rtnetlink socket. */
@@ -729,9 +738,9 @@ netdev_linux_close(struct netdev *netdev_)
     free(netdev);
 }
 
-/* Initializes 'svec' with a list of the names of all known network devices. */
+/* Initializes 'sset' with a list of the names of all known network devices. */
 static int
-netdev_linux_enumerate(struct svec *svec)
+netdev_linux_enumerate(struct sset *sset)
 {
     struct if_nameindex *names;
 
@@ -740,7 +749,7 @@ netdev_linux_enumerate(struct svec *svec)
         size_t i;
 
         for (i = 0; names[i].if_name != NULL; i++) {
-            svec_add(svec, names[i].if_name);
+            sset_add(sset, names[i].if_name);
         }
         if_freenameindex(names);
         return 0;
@@ -819,16 +828,36 @@ netdev_linux_drain(struct netdev *netdev_)
 static int
 netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
 {
-    struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+    struct sockaddr_ll sll;
+    struct msghdr msg;
+    struct iovec iov;
+    int ifindex;
+    int error;
 
-    /* XXX should support sending even if 'ethertype' was NETDEV_ETH_TYPE_NONE.
-     */
-    if (netdev->fd < 0) {
-        return EPIPE;
+    error = get_ifindex(netdev_, &ifindex);
+    if (error) {
+        return error;
     }
 
+    /* We don't bother setting most fields in sockaddr_ll because the kernel
+     * ignores them for SOCK_RAW. */
+    memset(&sll, 0, sizeof sll);
+    sll.sll_family = AF_PACKET;
+    sll.sll_ifindex = ifindex;
+
+    iov.iov_base = (void *) data;
+    iov.iov_len = size;
+
+    msg.msg_name = &sll;
+    msg.msg_namelen = sizeof sll;
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = NULL;
+    msg.msg_controllen = 0;
+    msg.msg_flags = 0;
+
     for (;;) {
-        ssize_t retval = write(netdev->fd, data, size);
+        ssize_t retval = sendmsg(af_packet_sock, &msg, 0);
         if (retval < 0) {
             /* The Linux AF_PACKET implementation never blocks waiting for room
              * for packets, instead returning ENOBUFS.  Translate this into
@@ -1510,14 +1539,14 @@ netdev_linux_set_policing(struct netdev *netdev,
 
 static int
 netdev_linux_get_qos_types(const struct netdev *netdev OVS_UNUSED,
-                           struct svec *types)
+                           struct sset *types)
 {
     const struct tc_ops **opsp;
 
     for (opsp = tcs; *opsp != NULL; opsp++) {
         const struct tc_ops *ops = *opsp;
         if (ops->tc_install && ops->ovs_name[0] != '\0') {
-            svec_add(types, ops->ovs_name);
+            sset_add(types, ops->ovs_name);
         }
     }
     return 0;
@@ -2301,7 +2330,7 @@ htb_get__(const struct netdev *netdev)
     return CONTAINER_OF(netdev_dev->tc, struct htb, tc);
 }
 
-static struct htb *
+static void
 htb_install__(struct netdev *netdev, uint64_t max_rate)
 {
     struct netdev_dev_linux *netdev_dev =
@@ -2313,8 +2342,6 @@ htb_install__(struct netdev *netdev, uint64_t max_rate)
     htb->max_rate = max_rate;
 
     netdev_dev->tc = &htb->tc;
-
-    return htb;
 }
 
 /* Create an HTB qdisc.
@@ -2497,8 +2524,10 @@ htb_parse_class_details__(struct netdev *netdev,
         return EINVAL;
     }
 
+    /* HTB requires at least an mtu sized min-rate to send any traffic even
+     * on uncongested links. */
     hc->min_rate = min_rate_s ? strtoull(min_rate_s, NULL, 10) / 8 : 0;
-    hc->min_rate = MAX(hc->min_rate, 1500);
+    hc->min_rate = MAX(hc->min_rate, mtu);
     hc->min_rate = MIN(hc->min_rate, htb->max_rate);
 
     /* max-rate */
@@ -2598,12 +2627,11 @@ htb_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
     struct ofpbuf msg;
     struct nl_dump dump;
     struct htb_class hc;
-    struct htb *htb;
 
     /* Get qdisc options. */
     hc.max_rate = 0;
     htb_query_class__(netdev, tc_make_handle(1, 0xfffe), 0, &hc, NULL);
-    htb = htb_install__(netdev, hc.max_rate);
+    htb_install__(netdev, hc.max_rate);
 
     /* Get queues. */
     if (!start_queue_dump(netdev, &dump)) {
@@ -2787,7 +2815,7 @@ hfsc_class_cast__(const struct tc_queue *queue)
     return CONTAINER_OF(queue, struct hfsc_class, tc_queue);
 }
 
-static struct hfsc *
+static void
 hfsc_install__(struct netdev *netdev, uint32_t max_rate)
 {
     struct netdev_dev_linux * netdev_dev;
@@ -2798,8 +2826,6 @@ hfsc_install__(struct netdev *netdev, uint32_t max_rate)
     tc_init(&hfsc->tc, &tc_ops_hfsc);
     hfsc->max_rate = max_rate;
     netdev_dev->tc = &hfsc->tc;
-
-    return hfsc;
 }
 
 static void
@@ -3098,13 +3124,12 @@ static int
 hfsc_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
 {
     struct ofpbuf msg;
-    struct hfsc *hfsc;
     struct nl_dump dump;
     struct hfsc_class hc;
 
     hc.max_rate = 0;
     hfsc_query_class__(netdev, tc_make_handle(1, 0xfffe), 0, &hc, NULL);
-    hfsc = hfsc_install__(netdev, hc.max_rate);
+    hfsc_install__(netdev, hc.max_rate);
 
     if (!start_queue_dump(netdev, &dump)) {
         return ENODEV;