tap_pl: allocation of a tap using vsys
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Mon, 7 May 2012 14:26:40 +0000 (16:26 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Mon, 7 May 2012 14:26:40 +0000 (16:26 +0200)
Planetlab tap interfaces are allocated by a vsys script (fd_tuntap).  The
script chooses the name of the interface and sends the corresponding fd
to user context through a Unix domain socket. The allocated tap interface
is not persistent. Moreover, the tap interface is not visibile in user
context until an IP address is assigned to it (by using vsys/vif_up).

The create_bridge script first creates a Planetlab tap with an IP
address, then creates a bridge with the same name of the tap.

Makefile.am
planetlab/automake.mk [new file with mode: 0644]
planetlab/pltap-ovs/pltap-ovs.c [new file with mode: 0644]
planetlab/pltap-ovs/tunalloc.c [new file with mode: 0644]
planetlab/pltap-ovs/tunalloc.h [new file with mode: 0644]
planetlab/scripts/create_bridge [new file with mode: 0755]
planetlab/scripts/create_port [new file with mode: 0755]
planetlab/scripts/del_bridge [new file with mode: 0755]

index e138ba4..5ab0f55 100644 (file)
@@ -204,3 +204,4 @@ include rhel/automake.mk
 include xenserver/automake.mk
 include python/automake.mk
 include python/compat/automake.mk
+include planetlab/automake.mk
diff --git a/planetlab/automake.mk b/planetlab/automake.mk
new file mode 100644 (file)
index 0000000..a08dd8e
--- /dev/null
@@ -0,0 +1,12 @@
+sbin_PROGRAMS += \
+       planetlab/pltap-ovs/pltap-ovs
+
+dist_sbin_SCRIPTS += \
+       planetlab/scripts/create_bridge \
+       planetlab/scripts/create_port \
+       planetlab/scripts/del_bridge
+
+planetlab_pltap_ovs_pltap_ovs_SOURCES = \
+       planetlab/pltap-ovs/pltap-ovs.c \
+       planetlab/pltap-ovs/tunalloc.c \
+       planetlab/pltap-ovs/tunalloc.h
diff --git a/planetlab/pltap-ovs/pltap-ovs.c b/planetlab/pltap-ovs/pltap-ovs.c
new file mode 100644 (file)
index 0000000..2e9833e
--- /dev/null
@@ -0,0 +1,136 @@
+#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#include "tunalloc.h"
+
+#define OVS_SOCK "/var/tun/pl-ovs.control"
+
+char *appname;
+
+#define ERROR(msg)                                                             \
+        do {                                                                   \
+                fprintf(stderr, "%s: %s: %s", appname, msg, strerror(errno));  \
+                exit(1);                                                       \
+        } while (0)
+
+
+int send_vif_fd(int sock_fd, int vif_fd, char *vif_name)
+{
+        int retval;
+        struct msghdr msg;
+        struct cmsghdr *p_cmsg;
+        struct iovec vec;
+        size_t cmsgbuf[CMSG_SPACE(sizeof(vif_fd)) / sizeof(size_t)];
+        int *p_fds;
+
+
+        msg.msg_control = cmsgbuf;
+        msg.msg_controllen = sizeof(cmsgbuf);
+        p_cmsg = CMSG_FIRSTHDR(&msg);
+        p_cmsg->cmsg_level = SOL_SOCKET;
+        p_cmsg->cmsg_type = SCM_RIGHTS;
+        p_cmsg->cmsg_len = CMSG_LEN(sizeof(vif_fd));
+        p_fds = (int *) CMSG_DATA(p_cmsg);
+        *p_fds = vif_fd;
+        msg.msg_controllen = p_cmsg->cmsg_len;
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &vec;
+        msg.msg_iovlen = 1;
+        msg.msg_flags = 0;
+
+        /* Send the interface name as the iov */
+        vec.iov_base = vif_name;
+        vec.iov_len = strlen(vif_name)+1;
+
+        while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
+        if (retval == -1) {
+                ERROR("sending file descriptor");
+        }
+        return 0;
+}
+
+void send_fd(int p, int fd, char* vif_name)
+{
+        int control_fd;
+        int accept_fd;
+        struct sockaddr_un addr, accept_addr;
+        socklen_t addr_len = sizeof(accept_addr);
+        int i;
+
+        control_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (control_fd == -1 && errno != ENOENT) {
+                ERROR("Could not create UNIX socket");
+        }
+
+        memset(&addr, 0, sizeof(struct sockaddr_un));
+        /* Clear structure */
+        addr.sun_family = AF_UNIX;
+        strncpy(addr.sun_path, OVS_SOCK,
+                        sizeof(addr.sun_path) - 1);
+
+        if (unlink(OVS_SOCK) == -1 && errno != ENOENT) {
+                ERROR("Could not unlink " OVS_SOCK " control socket");
+        }
+
+        if (bind(control_fd, (struct sockaddr *) &addr,
+                                sizeof(struct sockaddr_un)) == -1) {
+                ERROR("Could not bind to " OVS_SOCK " control socket");
+        }
+
+        if (listen(control_fd, 5) == -1) {
+                ERROR("listen on " OVS_SOCK " failed");
+        }
+        if (write(p, "1", 1) != 1) {
+                ERROR("writing on the synch pipe");
+        }
+        if ((accept_fd = accept(control_fd, (struct sockaddr*) &accept_addr,
+                                                &addr_len)) == -1) {
+                ERROR("accept on " OVS_SOCK " failed");
+        }
+        send_vif_fd(accept_fd, fd, vif_name);
+}
+
+int main(int argc, char* argv[])
+{
+        char if_name[IFNAMSIZ];
+        int p[2]; // synchronization pipe
+        char dummy;
+
+        if (pipe(p) < 0) {
+                ERROR("pipe");
+        }
+
+        int tun_fd = tun_alloc(IFF_TAP, if_name);
+
+        appname = argv[0];
+
+        switch(fork()) {
+        case -1:
+                ERROR("fork");
+                exit(1);
+        case 0:
+                close(1);
+                open("/dev/null", O_WRONLY);
+                close(p[0]);
+                send_fd(p[1], tun_fd, if_name);
+                exit(0);
+        default:
+                close(p[1]);
+                if (read(p[0], &dummy, 1) != 1) {
+                        ERROR("reading from the synch pipe");
+                }
+                printf("%s\n", if_name);
+        }
+        return 0;
+}
diff --git a/planetlab/pltap-ovs/tunalloc.c b/planetlab/pltap-ovs/tunalloc.c
new file mode 100644 (file)
index 0000000..6adcf24
--- /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 <sys/socket.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#define VSYS_TUNTAP "/vsys/fd_tuntap.control"
+
+/* Reads vif FD from "fd", writes interface name to vif_name, and returns vif FD.
+ * vif_name should be IFNAMSIZ chars long. */
+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) {
+               perror("recvmsg");
+               return -1;
+       }
+       if(!rv) {
+               /* EOF */
+               return -1;
+       }
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       if (!cmsg->cmsg_type == SCM_RIGHTS) {
+               fprintf(stderr, "got control message of unknown type %d\n",
+                       cmsg->cmsg_type);
+               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) {
+        perror("Could not create UNIX socket\n");
+        exit(-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) {
+        perror("Could not connect to Vsys control socket");
+        exit(-1);
+    }
+
+    /* passing type param */
+    if (send(control_fd, &iftype, sizeof(iftype), 0) != sizeof(iftype)) {
+        perror("Could not send paramater to Vsys control socket");
+        exit(-1);
+    }
+
+    remotefd = receive_vif_fd(control_fd, if_name);
+    return remotefd;
+}
diff --git a/planetlab/pltap-ovs/tunalloc.h b/planetlab/pltap-ovs/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
diff --git a/planetlab/scripts/create_bridge b/planetlab/scripts/create_bridge
new file mode 100755 (executable)
index 0000000..72acfd7
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+function error 
+{
+       echo $1 >&2
+       killall pltap-ovs 2>/dev/null || true
+       exit 1
+}
+
+function is_switch_running
+{
+       ovs-appctl version >/dev/null 2>&1
+}
+
+if [ -z "$1" ]; then
+       error "Usage: ${0##*/} <IP/PREFIX>"
+fi
+
+# TODO: check paramether validity
+
+IP=${1%/*}
+PREFIX=${1#*/}
+
+set -e
+
+# ensure ovs-vswitchd is running
+if ! is_switch_running; then
+       ovs-vswitchd --pidfile --detach --log-file >/dev/null 2>&1
+fi
+while ! is_switch_running; do 
+       sleep 1
+done
+       
+
+# check whether the address is already assigned
+set -e
+TAPNAME=$(ip addr show to "$IP/32" | perl -ne '/^\s*\d+:\s*([\w-]+):/ && print $1')
+if [ ! -z "$TAPNAME" ]; then
+    if ovs-vsctl br-exists "$TAPNAME"; then
+               echo $TAPNAME
+               exit 0
+    fi
+    error "$IP already assigned to $TAPNAME"
+fi
+
+TAPNAME=$(pltap-ovs)
+cat < /vsys/vif_up.out&
+cat >/vsys/vif_up.in << EOF
+       $TAPNAME
+       $IP
+       $PREFIX
+EOF
+while ! ip link show up | egrep -q "^[0-9]+: +$TAPNAME:"; do
+       echo "Waiting for $TAPNAME to come UP..." >&2
+       sleep 1
+done
+ovs-vsctl add-br $TAPNAME -- set bridge $TAPNAME datapath_type=planetlab
+echo $TAPNAME
diff --git a/planetlab/scripts/create_port b/planetlab/scripts/create_port
new file mode 100755 (executable)
index 0000000..6fa8ea5
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+function error 
+{
+       echo $1 >&2
+       exit 1
+}
+
+if [ -z "$2" ]; then
+       error "Usage ${0##*/} <bridge> <port>"
+fi
+
+if ovs-vsctl list-ports "$1" | grep -q "^$2$"; then
+       exit 0
+fi
+ovs-vsctl add-port "$1" "$2" -- set interface "$2" type=tunnel
diff --git a/planetlab/scripts/del_bridge b/planetlab/scripts/del_bridge
new file mode 100755 (executable)
index 0000000..9f62afc
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+function error 
+{
+       echo $1 >&2
+       exit 1
+}
+
+function is_switch_running
+{
+       ovs-appctl version >/dev/null 2>&1
+}
+
+if [ -z "$1" ]; then
+       error "Usage: ${0##*/} <bridge name>" 
+fi
+
+# ensure ovs-vswitchd is running
+if ! is_switch_running; then
+       exit 0;
+fi
+
+ovs-vsctl del-br $1 || true
+ovs-appctl exit