From 399c91552a13a5215bb1ed79917ee61ecd107f6b Mon Sep 17 00:00:00 2001 From: Giuseppe Lettieri Date: Mon, 30 Apr 2012 20:43:23 +0200 Subject: [PATCH] support for tap interfaces on PlanetLab - 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 | 2 + lib/dpif-netdev.c | 11 ++++- lib/dpif-provider.h | 1 + lib/dpif.c | 1 + lib/netdev-linux.c | 63 ++++++++++++++++++++++++++-- lib/netdev-provider.h | 1 + lib/netdev.c | 1 + lib/tunalloc.c | 95 +++++++++++++++++++++++++++++++++++++++++++ lib/tunalloc.h | 6 +++ 9 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 lib/tunalloc.c create mode 100644 lib/tunalloc.h diff --git a/lib/automake.mk b/lib/automake.mk index 31e07f322..11e9738a1 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -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 \ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index c71bd6eee..9e8d5e4b9 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -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) { diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h index 6338f50ac..e069e1e92 100644 --- a/lib/dpif-provider.h +++ b/lib/dpif-provider.h @@ -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 } diff --git a/lib/dpif.c b/lib/dpif.c index 7cf5fba4b..30e9b55bb 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -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 { diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index d2a5c7acc..be9e0a348 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -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); /* HTB traffic control class. */ diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h index e486ee691..9a3217ecc 100644 --- a/lib/netdev-provider.h +++ b/lib/netdev-provider.h @@ -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; diff --git a/lib/netdev.c b/lib/netdev.c index 165009a5b..9445bdf8b 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -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 index 000000000..b5e953c43 --- /dev/null +++ b/lib/tunalloc.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..3e5caae1d --- /dev/null +++ b/lib/tunalloc.h @@ -0,0 +1,6 @@ +#ifndef _TUNALLOC_H +#define _TUNALLOC_H + +int tun_alloc(int iftype, char *if_name); + +#endif -- 2.47.0