gre: Add userspace GRE support.
authorJesse Gross <jesse@nicira.com>
Sun, 6 Dec 2009 00:01:06 +0000 (16:01 -0800)
committerJesse Gross <jesse@nicira.com>
Mon, 7 Dec 2009 20:48:08 +0000 (12:48 -0800)
This implements the userspace portion of GRE on Linux. It communicates
with the kernel module to setup tunnels using either Netlink or ioctls
as appropriate based on the kernel version.

Significant portions of this commit were actually written by
Justin Pettit.

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

index 47d89ef..0dff508 100644 (file)
@@ -21,6 +21,7 @@
 #include <arpa/inet.h>
 #include <inttypes.h>
 #include <linux/if_tun.h>
+#include <linux/ip.h>
 #include <linux/types.h>
 #include <linux/ethtool.h>
 #include <linux/rtnetlink.h>
@@ -32,6 +33,7 @@
 #include <netpacket/packet.h>
 #include <net/ethernet.h>
 #include <net/if.h>
+#include <linux/if_tunnel.h>
 #include <net/if_arp.h>
 #include <net/if_packet.h>
 #include <net/route.h>
@@ -48,6 +50,7 @@
 #include "netlink.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
+#include "openvswitch/gre.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "rtnetlink.h"
 #include "shash.h"
 #include "svec.h"
 
+#ifndef GRE_IOCTL_ONLY
+#include <linux/if_link.h>
+#endif
+
 #define THIS_MODULE VLM_netdev_linux
 #include "vlog.h"
 \f
 #define ADVERTISED_Asym_Pause           (1 << 14)
 #endif
 
+struct tap_state {
+    int tap_fd;                 /* File descriptor for TAP device. */
+};
+
 /* Provider-specific netdev object.  Netdev objects are devices that are
  * created by the netdev library through a netdev_create() call. */
 struct netdev_obj_linux {
     struct netdev_obj netdev_obj;
-
-    int tap_fd;                 /* File descriptor for TAP device. */
+    union {
+        struct tap_state tap;
+    } state;
 };
 
 struct netdev_linux {
@@ -118,6 +130,25 @@ static struct rtnetlink_notifier netdev_linux_cache_notifier;
 /* An AF_INET socket (used for ioctl operations). */
 static int af_inet_sock = -1;
 
+struct gre_config {
+    uint32_t local_ip;
+    uint32_t remote_ip;
+    uint32_t in_key;
+    uint32_t out_key;
+    bool have_in_key;
+    bool have_out_key;
+    bool in_csum;
+    bool out_csum;
+};
+
+static struct {
+    union {
+        struct nl_sock *nl_sock;
+        int ioctl_fd;
+    };
+    bool use_ioctl;
+} gre_descriptors;
+
 struct netdev_linux_notifier {
     struct netdev_notifier notifier;
     struct list node;
@@ -153,7 +184,10 @@ static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stat
 static struct netdev_obj_linux *
 netdev_obj_linux_cast(const struct netdev_obj *netdev_obj)
 {
+    /* xxx Gross hack!  Until we get the class 'type' issue resolved. */
+#if 0
     netdev_obj_assert_class(netdev_obj, &netdev_linux_class);
+#endif
     return CONTAINER_OF(netdev_obj, struct netdev_obj_linux, netdev_obj);
 }
 
@@ -209,33 +243,281 @@ netdev_linux_cache_cb(const struct rtnetlink_change *change,
     }
 }
 
+/* The arguments are marked as unused to prevent warnings on platforms where
+ * the Netlink interface isn't supported. */
+static int
+setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED,
+                  bool create UNUSED)
+{
+#ifdef GRE_IOCTL_ONLY
+    return EOPNOTSUPP;
+#else
+    int error;
+    struct ofpbuf request, *reply;
+    unsigned int nl_flags;
+    struct ifinfomsg ifinfomsg;
+    struct nlattr *linkinfo_hdr;
+    struct nlattr *info_data_hdr;
+    uint16_t iflags = 0;
+    uint16_t oflags = 0;
+    uint8_t pmtudisc = 0;
+
+    if (!gre_descriptors.nl_sock) {
+        error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0,
+                               &gre_descriptors.nl_sock);
+        if (error) {
+            VLOG_WARN("couldn't create netlink socket: %s\n", strerror(error));
+            gre_descriptors.nl_sock = NULL;
+            goto error;
+        }
+    }
+
+    ofpbuf_init(&request, 0);
+
+    nl_flags = NLM_F_REQUEST;
+    if (create) {
+        nl_flags |= NLM_F_CREATE|NLM_F_EXCL;
+    }
+
+    /* We over-reserve space, because we do some pointer arithmetic
+     * and don't want the buffer address shifting under us. */
+    nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 2048, RTM_NEWLINK,
+                        nl_flags);
+
+    memset(&ifinfomsg, 0, sizeof ifinfomsg);
+    ifinfomsg.ifi_family = AF_UNSPEC;
+    nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
+
+    linkinfo_hdr = ofpbuf_tail(&request);
+    nl_msg_put_unspec(&request, IFLA_LINKINFO, NULL, 0);
+
+    nl_msg_put_unspec(&request, IFLA_INFO_KIND, "gretap", 6);
+
+    info_data_hdr = ofpbuf_tail(&request);
+    nl_msg_put_unspec(&request, IFLA_INFO_DATA, NULL, 0);
+
+    /* Set flags */
+    if (config->have_in_key) {
+        iflags |= GRE_KEY;
+    }
+    if (config->have_out_key) {
+        oflags |= GRE_KEY;
+    }
+
+    if (config->in_csum) {
+        iflags |= GRE_CSUM;
+    }
+    if (config->out_csum) {
+        oflags |= GRE_CSUM;
+    }
+
+    /* Add options */
+    nl_msg_put_u32(&request, IFLA_GRE_IKEY, config->in_key);
+    nl_msg_put_u32(&request, IFLA_GRE_OKEY, config->out_key);
+    nl_msg_put_u16(&request, IFLA_GRE_IFLAGS, iflags);
+    nl_msg_put_u16(&request, IFLA_GRE_OFLAGS, oflags);
+    nl_msg_put_u32(&request, IFLA_GRE_LOCAL, config->local_ip);
+    nl_msg_put_u32(&request, IFLA_GRE_REMOTE, config->remote_ip);
+    nl_msg_put_u8(&request, IFLA_GRE_PMTUDISC, pmtudisc);
+    nl_msg_put_u8(&request, IFLA_GRE_TTL, 0);
+    nl_msg_put_u8(&request, IFLA_GRE_TOS, 0);
+
+    info_data_hdr->nla_len = (char *)ofpbuf_tail(&request)
+                                - (char *)info_data_hdr;
+    linkinfo_hdr->nla_len = (char *)ofpbuf_tail(&request)
+                                - (char *)linkinfo_hdr;
+
+    nl_msg_put_string(&request, IFLA_IFNAME, name);
+
+    error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
+    ofpbuf_uninit(&request);
+    if (error) {
+        VLOG_WARN("couldn't transact netlink socket: %s\n", strerror(error));
+        goto error;
+    }
+    ofpbuf_delete(reply);
+
+error:
+    return error;
+#endif
+}
+
+static int
+setup_gre_ioctl(const char *name, struct gre_config *config, bool create)
+{
+    struct ip_tunnel_parm p;
+    struct ifreq ifr;
+
+    memset(&p, 0, sizeof p);
+
+    strncpy(p.name, name, IFNAMSIZ);
+
+    p.iph.version = 4;
+    p.iph.ihl = 5;
+    p.iph.protocol = IPPROTO_GRE;
+    p.iph.saddr = config->local_ip;
+    p.iph.daddr = config->remote_ip;
+
+    if (config->have_in_key) {
+        p.i_flags |= GRE_KEY;
+        p.i_key = config->in_key;
+    }
+    if (config->have_out_key) {
+        p.o_flags |= GRE_KEY;
+        p.o_key = config->out_key;
+    }
+
+    if (config->in_csum) {
+        p.i_flags |= GRE_CSUM;
+    }
+    if (config->out_csum) {
+        p.o_flags |= GRE_CSUM;
+    }
+
+    strncpy(ifr.ifr_name, create ? GRE_IOCTL_DEVICE : name, IFNAMSIZ);
+    ifr.ifr_ifru.ifru_data = (void *)&p;
+
+    if (!gre_descriptors.ioctl_fd) {
+        gre_descriptors.ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0);
+        if (gre_descriptors.ioctl_fd < 0) {
+            VLOG_WARN("couldn't create gre ioctl socket: %s\n", strerror(errno));
+            gre_descriptors.ioctl_fd = 0;
+            return errno;
+        }
+    }
+
+    if (ioctl(gre_descriptors.ioctl_fd, create ? SIOCADDGRETAP : SIOCCHGGRETAP,
+              &ifr) < 0) {
+        VLOG_WARN("couldn't do gre ioctl: %s\n", strerror(errno));
+        return errno;
+    }
+
+    return 0;
+}
+
+static int
+setup_gre(const char *name, const struct shash *args, bool create)
+{
+    int error;
+    struct in_addr in_addr;
+    struct shash_node *node;
+    struct gre_config config;
+
+    memset(&config, 0, sizeof config);
+    config.in_csum = true;
+    config.out_csum = true;
+
+    SHASH_FOR_EACH (node, args) {
+        if (!strcmp(node->name, "remote_ip")) {
+            if (lookup_ip(node->data, &in_addr)) {
+                VLOG_WARN("bad 'remote_ip' for gre device %s ", name);
+            } else {
+                config.remote_ip = in_addr.s_addr;
+            }
+        } else if (!strcmp(node->name, "local_ip")) {
+            if (lookup_ip(node->data, &in_addr)) {
+                VLOG_WARN("bad 'local_ip' for gre device %s ", name);
+            } else {
+                config.local_ip = in_addr.s_addr;
+            }
+        } else if (!strcmp(node->name, "key")) {
+            config.have_in_key = true;
+            config.have_out_key = true;
+            config.in_key = htonl(atoi(node->data));
+            config.out_key = htonl(atoi(node->data));
+        } else if (!strcmp(node->name, "in_key")) {
+            config.have_in_key = true;
+            config.in_key = htonl(atoi(node->data));
+        } else if (!strcmp(node->name, "out_key")) {
+            config.have_out_key = true;
+            config.out_key = htonl(atoi(node->data));
+        } else if (!strcmp(node->name, "csum")) {
+            if (!strcmp(node->data, "false")) {
+                config.in_csum = false;
+                config.out_csum = false;
+            }
+        } else {
+            VLOG_WARN("unknown gre argument '%s'", node->name);
+        }
+    }
+
+    if (!config.remote_ip) {
+        VLOG_WARN("gre type requires valid 'remote_ip' argument");
+        error = EINVAL;
+        goto error;
+    }
+
+    if (!gre_descriptors.use_ioctl) {
+        error = setup_gre_netlink(name, &config, create);
+        if (error == EOPNOTSUPP) {
+            gre_descriptors.use_ioctl = true;
+        }
+    }
+    if (gre_descriptors.use_ioctl) {
+        error = setup_gre_ioctl(name, &config, create);
+    }
+
+error:
+    return error;
+}
+
+static int
+if_up(const char *name)
+{
+    struct ifreq ifr;
+
+    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+    ifr.ifr_flags = IFF_UP;
+
+    if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
+        VLOG_DBG_RL(&rl, "%s: failed to bring device up: %s",
+                    name, strerror(errno));
+        return errno;
+    }
+
+    return 0;
+}
+
 /* Creates the netdev object of 'type' with 'name'. */
 static int
-netdev_linux_create(const char *name, const char *type, 
+netdev_linux_create_system(const char *name, const char *type UNUSED,
                     const struct shash *args, bool created)
 {
     struct netdev_obj_linux *netdev_obj;
-    static const char tap_dev[] = "/dev/net/tun";
-    struct ifreq ifr;
-    int error;
 
     if (!shash_is_empty(args)) {
-        VLOG_WARN("arguments for %s devices should be empty", type);
+        VLOG_WARN("arguments for system devices should be empty");
     }
 
-    /* Create the name binding in the netdev library for this object. */
     netdev_obj = xcalloc(1, sizeof *netdev_obj);
     netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_linux_class,
                     created);
-    netdev_obj->tap_fd = -1;
 
-    if (strcmp(type, "tap")) {
-        return 0;
+    return 0;
+}
+
+static int
+netdev_linux_create_tap(const char *name, const char *type UNUSED,
+                    const struct shash *args, bool created)
+{
+    struct netdev_obj_linux *netdev_obj;
+    struct tap_state *state;
+    static const char tap_dev[] = "/dev/net/tun";
+    struct ifreq ifr;
+    int error;
+
+    if (!shash_is_empty(args)) {
+        VLOG_WARN("arguments for TAP devices should be empty");
     }
 
+    netdev_obj = xcalloc(1, sizeof *netdev_obj);
+    netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_tap_class, created);
+
+    state = &netdev_obj->state.tap;
+
     /* Open tap device. */
-    netdev_obj->tap_fd = open(tap_dev, O_RDWR);
-    if (netdev_obj->tap_fd < 0) {
+    state->tap_fd = open(tap_dev, O_RDWR);
+    if (state->tap_fd < 0) {
         error = errno;
         VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
         goto error;
@@ -244,7 +526,7 @@ netdev_linux_create(const char *name, const char *type,
     /* Create tap device. */
     ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
     strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
-    if (ioctl(netdev_obj->tap_fd, TUNSETIFF, &ifr) == -1) {
+    if (ioctl(state->tap_fd, TUNSETIFF, &ifr) == -1) {
         VLOG_WARN("%s: creating tap device failed: %s", name,
                   strerror(errno));
         error = errno;
@@ -252,7 +534,39 @@ netdev_linux_create(const char *name, const char *type,
     }
 
     /* Make non-blocking. */
-    error = set_nonblocking(netdev_obj->tap_fd);
+    error = set_nonblocking(state->tap_fd);
+    if (error) {
+        goto error;
+    }
+
+    error = if_up(name);
+    if (error) {
+        goto error;
+    }
+
+    return 0;
+
+error:
+    netdev_destroy(name);
+    return error;
+}
+
+static int
+netdev_linux_create_gre(const char *name, const char *type UNUSED,
+                    const struct shash *args, bool created)
+{
+    struct netdev_obj_linux *netdev_obj;
+    int error;
+
+    netdev_obj = xcalloc(1, sizeof *netdev_obj);
+    netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_gre_class, created);
+
+    error = setup_gre(name, args, true);
+    if (error) {
+        goto error;
+    }
+
+    error = if_up(name);
     if (error) {
         goto error;
     }
@@ -264,18 +578,104 @@ error:
     return error;
 }
 
+static int
+netdev_linux_reconfigure_gre(struct netdev_obj *netdev_obj_,
+                             const struct shash *args)
+{
+    struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_);
+    const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj);
+
+    return setup_gre(name, args, false);
+}
+
+/* The arguments are marked as unused to prevent warnings on platforms where
+ * the Netlink interface isn't supported. */
+static int
+destroy_gre_netlink(struct netdev_obj_linux *netdev_obj UNUSED)
+{
+#ifdef GRE_IOCTL_ONLY
+    return EOPNOTSUPP;
+#else
+    const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj);
+    int error;
+    struct ofpbuf request, *reply;
+    struct ifinfomsg ifinfomsg;
+    int ifindex;
+
+    ofpbuf_init(&request, 0);
+    
+    nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 0, RTM_DELLINK,
+                        NLM_F_REQUEST);
+
+    memset(&ifinfomsg, 0, sizeof ifinfomsg);
+    ifinfomsg.ifi_family = AF_UNSPEC;
+    nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
+
+    ifindex = do_get_ifindex(name);
+    nl_msg_put_u32(&request, IFLA_LINK, ifindex);
+
+    nl_msg_put_string(&request, IFLA_IFNAME, name);
+
+    error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
+    ofpbuf_uninit(&request);
+    if (error) {
+        VLOG_WARN("couldn't transact netlink socket: %s\n", strerror(error));
+        goto error;
+    }
+    ofpbuf_delete(reply);
+
+error:
+    return 0;
+#endif
+}
+
+static int
+destroy_gre_ioctl(struct netdev_obj_linux *netdev_obj)
+{
+    const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj);
+    struct ip_tunnel_parm p;
+    struct ifreq ifr;
+
+    memset(&p, 0, sizeof p);
+    strncpy(p.name, name, IFNAMSIZ);
+
+    strncpy(ifr.ifr_name, name, IFNAMSIZ);
+    ifr.ifr_ifru.ifru_data = (void *)&p;
+
+    if (ioctl(gre_descriptors.ioctl_fd, SIOCDELGRETAP, &ifr) < 0) {
+        VLOG_WARN("couldn't do gre ioctl: %s\n", strerror(errno));
+        return errno;
+    }
+
+    return 0;
+}
+
+static void
+destroy_tap(struct netdev_obj_linux *netdev_obj)
+{
+    struct tap_state *state = &netdev_obj->state.tap;
+    if (state->tap_fd >= 0) {
+        close(state->tap_fd);
+    }
+}
+
 /* Destroys the netdev object 'netdev_obj_'. */
 static void
 netdev_linux_destroy(struct netdev_obj *netdev_obj_)
 {
     struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_);
+    const char *type = netdev_obj_get_type(netdev_obj_);
 
-    if (netdev_obj->tap_fd >= 0) {
-        close(netdev_obj->tap_fd);
+    if (!strcmp(type, "tap")) {
+        destroy_tap(netdev_obj);
+    } else if (!strcmp(type, "gre")) {
+        if (gre_descriptors.use_ioctl) {
+            destroy_gre_ioctl(netdev_obj);
+        } else {
+            destroy_gre_netlink(netdev_obj);
+        }
     }
     free(netdev_obj);
-
-    return;
 }
 
 static int
@@ -1466,7 +1866,7 @@ const struct netdev_class netdev_linux_class = {
     netdev_linux_run,
     netdev_linux_wait,
 
-    netdev_linux_create,
+    netdev_linux_create_system,
     netdev_linux_destroy,
     NULL,                       /* reconfigure */
 
@@ -1514,7 +1914,7 @@ const struct netdev_class netdev_tap_class = {
     NULL,                       /* run */
     NULL,                       /* wait */
 
-    netdev_linux_create,
+    netdev_linux_create_tap,
     netdev_linux_destroy,
     NULL,                       /* reconfigure */
 
@@ -1554,6 +1954,54 @@ const struct netdev_class netdev_tap_class = {
     netdev_linux_poll_add,
     netdev_linux_poll_remove,
 };
+
+const struct netdev_class netdev_gre_class = {
+    "gre",                      /* type */
+
+    netdev_linux_init,
+    NULL,                       /* run */
+    NULL,                       /* wait */
+
+    netdev_linux_create_gre,
+    netdev_linux_destroy,
+    netdev_linux_reconfigure_gre,
+
+    netdev_linux_open,
+    netdev_linux_close,
+
+    netdev_linux_enumerate,
+
+    netdev_linux_recv,
+    netdev_linux_recv_wait,
+    netdev_linux_drain,
+
+    netdev_linux_send,
+    netdev_linux_send_wait,
+
+    netdev_linux_set_etheraddr,
+    netdev_linux_get_etheraddr,
+    netdev_linux_get_mtu,
+    netdev_linux_get_ifindex,
+    netdev_linux_get_carrier,
+    netdev_linux_get_stats,
+
+    netdev_linux_get_features,
+    netdev_linux_set_advertisements,
+    netdev_linux_get_vlan_vid,
+    netdev_linux_set_policing,
+
+    netdev_linux_get_in4,
+    netdev_linux_set_in4,
+    netdev_linux_get_in6,
+    netdev_linux_add_router,
+    netdev_linux_get_next_hop,
+    netdev_linux_arp_lookup,
+
+    netdev_linux_update_flags,
+
+    netdev_linux_poll_add,
+    netdev_linux_poll_remove,
+};
 \f
 static int
 get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
index 64d227e..71f9599 100644 (file)
@@ -32,6 +32,7 @@
 struct netdev_obj {
     const struct netdev_class *class;
     int ref_cnt;
+    char *name;
     bool created;                    /* Was netdev_create() called? */
 };
 
@@ -42,6 +43,8 @@ static inline void netdev_obj_assert_class(const struct netdev_obj *netdev_obj,
 {
     assert(netdev_obj->class == class);
 }
+const char *netdev_obj_get_type(const struct netdev_obj *netdev_obj);
+const char *netdev_obj_get_name(const struct netdev_obj *netdev_obj);
 
 /* A network device (e.g. an Ethernet device).
  *
@@ -349,5 +352,6 @@ struct netdev_class {
 
 extern const struct netdev_class netdev_linux_class;
 extern const struct netdev_class netdev_tap_class;
+extern const struct netdev_class netdev_gre_class;
 
 #endif /* netdev.h */
index fb0f98e..6c87bd4 100644 (file)
@@ -42,6 +42,7 @@
 static const struct netdev_class *netdev_classes[] = {
     &netdev_linux_class,
     &netdev_tap_class,
+    &netdev_gre_class,
 };
 static int n_netdev_classes = ARRAY_SIZE(netdev_classes);
 
@@ -179,9 +180,11 @@ netdev_destroy(const char *name)
 
     netdev_obj = node->data;
     if (netdev_obj->ref_cnt != 0) {
-        VLOG_WARN("attempt to destroy open netdev object (%d): %s", 
+        VLOG_WARN("attempt to destroy netdev object with %d open handles: %s", 
                 netdev_obj->ref_cnt, name);
+#if 0  /* Temp hack */
         return EBUSY;
+#endif
     }
 
     shash_delete(&netdev_obj_shash, node);
@@ -273,7 +276,11 @@ netdev_close(struct netdev *netdev)
         int error;
 
         netdev_obj = shash_find_data(&netdev_obj_shash, name);
+#if 0
         assert(netdev_obj);
+#else
+        if (netdev_obj) {
+#endif
         if (netdev_obj->ref_cnt > 0) {
             netdev_obj->ref_cnt--;
         } else {
@@ -285,6 +292,9 @@ netdev_close(struct netdev *netdev)
         if (!netdev_obj->ref_cnt && !netdev_obj->created) {
             netdev_destroy(name);
         }
+#if 1
+        }
+#endif
 
         /* Restore flags that we changed, if any. */
         fatal_signal_block();
@@ -833,9 +843,26 @@ netdev_obj_init(struct netdev_obj *netdev_obj, const char *name,
     netdev_obj->class = class;
     netdev_obj->ref_cnt = 0;
     netdev_obj->created = created;
+    netdev_obj->name = xstrdup(name);
     shash_add(&netdev_obj_shash, name, netdev_obj);
 }
 
+/* Returns the class type of 'netdev_obj'.
+ *
+ * The caller must not free the returned value. */
+const char *netdev_obj_get_type(const struct netdev_obj *netdev_obj)
+{
+    return netdev_obj->class->type;
+}
+
+/* Returns the name of 'netdev_obj'.
+ *
+ * The caller must not free the returned value. */
+const char *netdev_obj_get_name(const struct netdev_obj *netdev_obj)
+{
+    return netdev_obj->name;
+}
+
 /* Initializes 'netdev' as a netdev named 'name' of the specified 'class'.
  *
  * This function adds 'netdev' to a netdev-owned linked list, so it is very
index d7f4d32..2aeb3f7 100644 (file)
@@ -247,7 +247,8 @@ static bool vlan_is_mirrored(const struct mirror *m UNUSED, int vlan UNUSED)
 }
 #endif
 
-static struct iface *iface_create(struct port *, const char *name);
+static struct iface *iface_create(struct port *port, 
+                                  const struct ovsrec_interface *if_cfg);
 static void iface_destroy(struct iface *);
 static struct iface *iface_lookup(const struct bridge *, const char *name);
 static struct iface *iface_from_dp_ifidx(const struct bridge *,
@@ -421,24 +422,12 @@ set_up_iface(const struct ovsrec_interface *iface_cfg, bool create)
     return error;
 }
 
-static int
-create_iface(const struct ovsrec_interface *iface_cfg)
-{
-    return set_up_iface(iface_cfg, true);
-}
-
 static int
 reconfigure_iface(const struct ovsrec_interface *iface_cfg)
 {
     return set_up_iface(iface_cfg, false);
 }
 
-static void
-destroy_iface(const char *iface_name)
-{
-    netdev_destroy(iface_name);
-}
-
 
 /* iterate_and_prune_ifaces() callback function that opens the network device
  * for 'iface', if it is not already open, and retrieves the interface's MAC
@@ -602,7 +591,6 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
                              p->devname, dpif_name(br->dpif),
                              strerror(retval));
                 }
-                destroy_iface(p->devname);
             }
         }
         shash_destroy(&want_ifaces);
@@ -642,17 +630,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
                 bool internal;
                 int error;
 
-                /* Attempt to create the network interface in case it
-                 * doesn't exist yet. */
-                error = iface ? create_iface(iface->cfg) : 0;
-                if (error) {
-                    VLOG_WARN("could not create iface %s: %s\n", if_name,
-                              strerror(error));
-                    continue;
-                }
-
                 /* Add to datapath. */
-                internal = !iface || iface_is_internal(br, if_name);
+                internal = iface_is_internal(br, if_name);
                 error = dpif_port_add(br->dpif, if_name,
                                       internal ? ODP_PORT_INTERNAL : 0, NULL);
                 if (error == EFBIG) {
@@ -3002,12 +2981,11 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
     }
     SHASH_FOR_EACH (node, &new_ifaces) {
         const struct ovsrec_interface *if_cfg = node->data;
-        const char *if_name = node->name;
         struct iface *iface;
 
-        iface = shash_find_data(&old_ifaces, if_name);
+        iface = shash_find_data(&old_ifaces, if_cfg->name);
         if (!iface) {
-            iface = iface_create(port, if_name);
+            iface = iface_create(port, if_cfg);
         }
         iface->cfg = if_cfg;
     }
@@ -3290,9 +3268,11 @@ port_update_vlan_compat(struct port *port)
 /* Interface functions. */
 
 static struct iface *
-iface_create(struct port *port, const char *name)
+iface_create(struct port *port, const struct ovsrec_interface *if_cfg)
 {
     struct iface *iface;
+    char *name = if_cfg->name;
+    int error;
 
     iface = xzalloc(sizeof *iface);
     iface->port = port;
@@ -3312,6 +3292,14 @@ iface_create(struct port *port, const char *name)
         port->bridge->has_bonded_ports = true;
     }
 
+    /* Attempt to create the network interface in case it
+     * doesn't exist yet. */
+    error = set_up_iface(if_cfg, true);
+    if (error) {
+        VLOG_WARN("could not create iface %s: %s\n", iface->name,
+                strerror(error));
+    }
+
     VLOG_DBG("attached network device %s to port %s", iface->name, port->name);
 
     bridge_flush(port->bridge);
@@ -3336,8 +3324,6 @@ iface_destroy(struct iface *iface)
         del->port_ifidx = iface->port_ifidx;
 
         netdev_close(iface->netdev);
-        free(iface->name);
-        free(iface);
 
         if (del_active) {
             ofproto_revalidate(port->bridge->ofproto, port->active_iface_tag);
@@ -3345,6 +3331,10 @@ iface_destroy(struct iface *iface)
             bond_send_learning_packets(port);
         }
 
+        netdev_destroy(iface->name);
+        free(iface->name);
+        free(iface);
+
         bridge_flush(port->bridge);
     }
 }