netdev-linux: Support 'send' for netdevs opened with NETDEV_ETH_TYPE_NONE.
authorBen Pfaff <blp@nicira.com>
Fri, 1 Apr 2011 16:22:39 +0000 (09:22 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 1 Apr 2011 22:52:19 +0000 (15:52 -0700)
The new implementation of the bonding code expects to be able to
send packets using netdev_send().  This makes it possible for
Linux netdevs.

lib/netdev-linux.c
lib/netdev-provider.h

index 6b0d0149f58ea80b1a7926b4995ff2fb8d8a15fc..eecaaa591a2a1884cf23f2c87f6e5b11505478ba 100644 (file)
@@ -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. */
@@ -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
index c6ebd2a5e1f12d5bd2407954686cb33c4536a10c..b095779cdac33b1f5e60b11d81f604b12b2d0038 100644 (file)
@@ -210,7 +210,8 @@ struct netdev_class {
      * transmission through this interface.  This function may be set to null
      * if it would always return EOPNOTSUPP anyhow.  (This will prevent the
      * network device from being usefully used by the netdev-based "userspace
-     * datapath".) */
+     * datapath".  It will also prevent the OVS implementation of bonding from
+     * working properly over 'netdev'.) */
     int (*send)(struct netdev *netdev, const void *buffer, size_t size);
 
     /* Registers with the poll loop to wake up from the next call to