support for tap interfaces on PlanetLab
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Mon, 30 Apr 2012 18:43:23 +0000 (20:43 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Mon, 30 Apr 2012 18:43:23 +0000 (20:43 +0200)
 - Add new interface type "tap_pl", which is created using PlanetLab
   VSYS and is otherwise (almost) equivalent to "tap".
 - Add new datatype_path "planetlab", which uses "tap_pl" for the
   internal interface and is otherwise equivalent to "netdev".

lib/automake.mk
lib/dpif-netdev.c
lib/dpif-provider.h
lib/dpif.c
lib/netdev-linux.c
lib/netdev-provider.h
lib/netdev.c
lib/tunalloc.c [new file with mode: 0644]
lib/tunalloc.h [new file with mode: 0644]

index 31e07f3..11e9738 100644 (file)
@@ -168,6 +168,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/timer.h \
        lib/timeval.c \
        lib/timeval.h \
+       lib/tunalloc.c \
+       lib/tunalloc.h \
        lib/type-props.h \
        lib/unaligned.h \
        lib/unicode.c \
index c71bd6e..9e8d5e4 100644 (file)
@@ -306,6 +306,8 @@ static const char* internal_port_type(const struct dp_netdev* dp)
 {
        if (dp->class == &dpif_netdev_class)
                return "tap";
+       if (dp->class == &dpif_planetlab_class)
+               return "tap_pl";
        return "dummy";
 }
 
@@ -367,7 +369,9 @@ choose_port(struct dpif *dpif, struct netdev *netdev)
     struct dp_netdev *dp = get_dp_netdev(dpif);
     int port_no;
 
-    if (dpif->dpif_class != &dpif_netdev_class) {
+    if (dpif->dpif_class != &dpif_netdev_class &&
+        dpif->dpif_class != &dpif_planetlab_class)
+    {
         /* If the port name contains a number, try to assign that port number.
          * This can make writing unit tests easier because port numbers are
          * predictable. */
@@ -1283,6 +1287,11 @@ const struct dpif_class dpif_netdev_class = {
     DPIF_NETDEV_CLASS_FUNCTIONS
 };
 
+const struct dpif_class dpif_planetlab_class = {
+    "planetlab",
+    DPIF_NETDEV_CLASS_FUNCTIONS
+};
+
 static void
 dpif_dummy_register__(const char *type)
 {
index 6338f50..e069e1e 100644 (file)
@@ -332,6 +332,7 @@ struct dpif_class {
 
 extern const struct dpif_class dpif_linux_class;
 extern const struct dpif_class dpif_netdev_class;
+extern const struct dpif_class dpif_planetlab_class;
 
 #ifdef  __cplusplus
 }
index 7cf5fba..30e9b55 100644 (file)
@@ -62,6 +62,7 @@ static const struct dpif_class *base_dpif_classes[] = {
     &dpif_linux_class,
 #endif
     &dpif_netdev_class,
+    &dpif_planetlab_class,
 };
 
 struct registered_dpif_class {
index d2a5c7a..be9e0a3 100644 (file)
@@ -69,6 +69,7 @@
 #include "sset.h"
 #include "timer.h"
 #include "vlog.h"
+#include "tunalloc.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_linux);
 
@@ -766,7 +767,7 @@ netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
         }
     }
 
-    if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap") &&
+    if (!strncmp(netdev_dev_get_type(netdev_dev_), "tap", 3) &&
         !netdev_dev->state.tap.opened) {
 
         /* We assume that the first user of the tap device is the primary user
@@ -791,7 +792,7 @@ netdev_linux_close(struct netdev *netdev_)
 {
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 
-    if (netdev->fd > 0 && strcmp(netdev_get_type(netdev_), "tap")) {
+    if (netdev->fd > 0 && strncmp(netdev_get_type(netdev_), "tap", 3)) {
         close(netdev->fd);
     }
     free(netdev);
@@ -898,7 +899,7 @@ netdev_linux_drain(struct netdev *netdev_)
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
     if (netdev->fd < 0) {
         return 0;
-    } else if (!strcmp(netdev_get_type(netdev_), "tap")) {
+    } else if (!strncmp(netdev_get_type(netdev_), "tap", 3)) {
         struct ifreq ifr;
         int error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
                                           SIOCGIFTXQLEN, "SIOCGIFTXQLEN");
@@ -1009,7 +1010,7 @@ netdev_linux_send_wait(struct netdev *netdev_)
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
     if (netdev->fd < 0) {
         /* Nothing to do. */
-    } else if (strcmp(netdev_get_type(netdev_), "tap")) {
+    } else if (strncmp(netdev_get_type(netdev_), "tap", 3)) {
         poll_fd_wait(netdev->fd, POLLOUT);
     } else {
         /* TAP device always accepts packets.*/
@@ -1780,6 +1781,51 @@ netdev_linux_get_qos_types(const struct netdev *netdev OVS_UNUSED,
     return 0;
 }
 
+static int
+netdev_linux_create_tap_pl(const struct netdev_class *class OVS_UNUSED,
+                        const char *name, struct netdev_dev **netdev_devp)
+{
+    struct netdev_dev_linux *netdev_dev;
+    struct tap_state *state;
+    char real_name[IFNAMSIZ];
+    int error;
+
+    netdev_dev = xzalloc(sizeof *netdev_dev);
+    state = &netdev_dev->state.tap;
+
+    error = cache_notifier_ref();
+    if (error) {
+        goto error;
+    }
+
+    /* Open tap device. */
+    state->fd = tun_alloc(IFF_TAP, real_name);
+    if (state->fd < 0) {
+        error = errno;
+        VLOG_WARN("tun_alloc(IFF_TAP, %s) failed: %s", name, strerror(error));
+        goto error_unref_notifier;
+    }
+    if (strcmp(name, real_name)) {
+        VLOG_WARN("tap_pl: requested %s, created %s", name, real_name);
+    }
+
+    /* Make non-blocking. */
+    error = set_nonblocking(state->fd);
+    if (error) {
+        goto error_unref_notifier;
+    }
+
+    netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_class);
+    *netdev_devp = &netdev_dev->netdev_dev;
+    return 0;
+
+error_unref_notifier:
+    cache_notifier_unref();
+error:
+    free(netdev_dev);
+    return error;
+}
+
 static const struct tc_ops *
 tc_lookup_ovs_name(const char *name)
 {
@@ -2466,6 +2512,15 @@ const struct netdev_class netdev_internal_class =
         netdev_vport_set_stats,
         NULL,                  /* get_features */
         netdev_internal_get_drv_info);
+
+const struct netdev_class netdev_tap_pl_class =
+    NETDEV_LINUX_CLASS(
+        "tap_pl",
+        netdev_linux_create_tap_pl,
+        netdev_tap_get_stats,
+        NULL,                   /* set_stats */
+        netdev_linux_get_features,
+        netdev_linux_get_drv_info);
 \f
 /* HTB traffic control class. */
 
index e486ee6..9a3217e 100644 (file)
@@ -593,6 +593,7 @@ const struct netdev_class *netdev_lookup_provider(const char *type);
 extern const struct netdev_class netdev_linux_class;
 extern const struct netdev_class netdev_internal_class;
 extern const struct netdev_class netdev_tap_class;
+extern const struct netdev_class netdev_tap_pl_class;
 
 extern const struct netdev_class netdev_tunnel_class;
 
index 165009a..9445bdf 100644 (file)
@@ -78,6 +78,7 @@ netdev_initialize(void)
         netdev_register_provider(&netdev_linux_class);
         netdev_register_provider(&netdev_internal_class);
         netdev_register_provider(&netdev_tap_class);
+       netdev_register_provider(&netdev_tap_pl_class);
         netdev_vport_register();
 #endif
        netdev_register_provider(&netdev_tunnel_class);
diff --git a/lib/tunalloc.c b/lib/tunalloc.c
new file mode 100644 (file)
index 0000000..b5e953c
--- /dev/null
@@ -0,0 +1,95 @@
+/* Slice-side code to allocate tuntap interface in root slice
+ * Based on bmsocket.c
+ *  Thom Haddow - 08/10/09
+ *
+ * Call tun_alloc() with IFFTUN or IFFTAP as an argument to get back fd to
+ * new tuntap interface. Interface name can be acquired via TUNGETIFF ioctl.
+ */
+
+#include <sys/un.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#include "tunalloc.h"
+
+#define VSYS_TUNTAP "/var/run/pl-ovs.control"
+
+/* Reads vif FD from "fd", writes interface name to vif_name, and returns vif FD.
+ * vif_name should be IFNAMSIZ chars long. */
+static int receive_vif_fd(int fd, char *vif_name)
+{
+       struct msghdr msg;
+       struct iovec iov;
+       int rv;
+       size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
+       struct cmsghdr *cmsg;
+
+    /* Use IOV to read interface name */
+       iov.iov_base = vif_name;
+       iov.iov_len = IFNAMSIZ;
+
+       msg.msg_name = 0;
+       msg.msg_namelen = 0;
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       /* old BSD implementations should use msg_accrights instead of
+        * msg_control; the interface is different. */
+       msg.msg_control = ccmsg;
+       msg.msg_controllen = sizeof(ccmsg);
+
+       while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
+       if (rv == -1) {
+               return -1;
+       }
+       if(!rv) {
+               /* EOF */
+               return -1;
+       }
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       if (!cmsg->cmsg_type == SCM_RIGHTS) {
+               return -1;
+       }
+       return *(int*)CMSG_DATA(cmsg);
+}
+
+
+int tun_alloc(int iftype, char *if_name)
+{
+    int control_fd;
+    struct sockaddr_un addr;
+    int remotefd;
+
+    control_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (control_fd == -1) {
+        return -1;
+    }
+
+    memset(&addr, 0, sizeof(struct sockaddr_un));
+    /* Clear structure */
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, VSYS_TUNTAP,
+            sizeof(addr.sun_path) - 1);
+
+    if (connect(control_fd, (struct sockaddr *) &addr,
+                sizeof(struct sockaddr_un)) == -1) {
+        return -1;
+    }
+
+    /* passing type param */
+    if (send(control_fd, &iftype, sizeof(iftype), 0) != sizeof(iftype)) {
+        return -1;
+    }
+
+    remotefd = receive_vif_fd(control_fd, if_name);
+
+    close(control_fd);
+
+    return remotefd;
+}
diff --git a/lib/tunalloc.h b/lib/tunalloc.h
new file mode 100644 (file)
index 0000000..3e5caae
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _TUNALLOC_H
+#define _TUNALLOC_H
+
+int tun_alloc(int iftype, char *if_name);
+
+#endif