[ovs-dev] [RFC flow tunnels 7/8] lib: Switch to flow based tunneling.

Ethan Jackson ethan at nicira.com
Wed Jan 9 23:43:47 UTC 2013


With this patch, ovs-vswitchd uses flow based tunneling
exclusively.  I.E. each kind of tunnel shares a single tunnel
backer in the datapath.  Tunnel headers are set by userspace using
the ipv4_tunnel datapath action.  There are still some significant
pieces of work to do, but the basic building blocks are there to
begin testing.

Signed-off-by: Jesse Gross <jesse at nicira.com>
Signed-off-by: Ethan Jackson <ethan at nicira.com>
---
 NEWS                   |    2 +
 lib/dpif-linux.c       |   42 ++++-
 lib/netdev-vport.c     |  405 +++++++++++++++---------------------------------
 lib/netdev-vport.h     |    1 -
 lib/netdev.c           |    7 +
 lib/netdev.h           |    1 +
 lib/odp-util.c         |   17 +-
 lib/odp-util.h         |    4 +-
 ofproto/ofproto-dpif.c |  248 +++++++++++++++++++++--------
 tests/ofproto-dpif.at  |   19 ---
 10 files changed, 375 insertions(+), 371 deletions(-)

diff --git a/NEWS b/NEWS
index d30c47d..4f7ffe8 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,8 @@ post-v1.9.0
         retire that meaning of ANY in favor of the OpenFlow 1.1 meaning.
     - Inheritance of the Don't Fragment bit in IP tunnels (df_inherit) is
       no longer supported.
+    - Tunneling requires the version of the kernel module paired Open vSwitch
+      1.9.0 or later.
 
 
 v1.9.0 - xx xxx xxxx
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 19ae565..0968340 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -387,6 +387,44 @@ dpif_linux_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
     return error;
 }
 
+static const char *
+get_vport_type(const struct dpif_linux_vport *vport)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+    switch (vport->type) {
+    case OVS_VPORT_TYPE_NETDEV:
+        return "system";
+
+    case OVS_VPORT_TYPE_INTERNAL:
+        return "internal";
+
+    case OVS_VPORT_TYPE_PATCH:
+        return "patch";
+
+    case OVS_VPORT_TYPE_GRE:
+        return "gre";
+
+    case OVS_VPORT_TYPE_GRE64:
+        return "gre64";
+
+    case OVS_VPORT_TYPE_CAPWAP:
+        return "capwap";
+
+    case OVS_VPORT_TYPE_VXLAN:
+        return "vxlan";
+
+    case OVS_VPORT_TYPE_UNSPEC:
+    case OVS_VPORT_TYPE_FT_GRE:
+    case __OVS_VPORT_TYPE_MAX:
+        break;
+    }
+
+    VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
+                 vport->dp_ifindex, vport->name, (unsigned int) vport->type);
+    return "unknown";
+}
+
 static int
 dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
                     uint32_t *port_nop)
@@ -481,7 +519,7 @@ dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
             error = ENODEV;
         } else if (dpif_port) {
             dpif_port->name = xstrdup(reply.name);
-            dpif_port->type = xstrdup(netdev_vport_get_netdev_type(&reply));
+            dpif_port->type = xstrdup(get_vport_type(&reply));
             dpif_port->port_no = reply.port_no;
         }
         ofpbuf_delete(buf);
@@ -583,7 +621,7 @@ dpif_linux_port_dump_next(const struct dpif *dpif OVS_UNUSED, void *state_,
     }
 
     dpif_port->name = CONST_CAST(char *, vport.name);
-    dpif_port->type = CONST_CAST(char *, netdev_vport_get_netdev_type(&vport));
+    dpif_port->type = CONST_CAST(char *, get_vport_type(&vport));
     dpif_port->port_no = vport.port_no;
     return 0;
 }
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 48637d1..bf2bf6a 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -69,22 +69,13 @@ struct netdev_vport {
 struct vport_class {
     enum ovs_vport_type type;
     struct netdev_class netdev_class;
-    int (*parse_config)(const char *name, const char *type,
-                        const struct smap *args, struct ofpbuf *options,
-                        struct netdev_tunnel_config *tnl_cfg);
-    int (*unparse_config)(const char *name, const char *type,
-                          const struct nlattr *options, size_t options_len,
-                          struct smap *args);
 };
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
 static int netdev_vport_create(const struct netdev_class *, const char *,
                                struct netdev_dev **);
-static void netdev_vport_poll_notify(const struct netdev *);
-static int tnl_port_config_from_nlattr(const struct nlattr *options,
-                                       size_t options_len,
-                                       struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]);
+static void netdev_vport_poll_notify(struct netdev_dev_vport *);
 
 static bool
 is_vport_class(const struct netdev_class *class)
@@ -152,62 +143,6 @@ netdev_vport_get_vport_type(const struct netdev *netdev)
             : OVS_VPORT_TYPE_UNSPEC);
 }
 
-static uint32_t
-get_u32_or_zero(const struct nlattr *a)
-{
-    return a ? nl_attr_get_u32(a) : 0;
-}
-
-const char *
-netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
-{
-    struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
-
-    switch (vport->type) {
-    case OVS_VPORT_TYPE_UNSPEC:
-        break;
-
-    case OVS_VPORT_TYPE_NETDEV:
-        return "system";
-
-    case OVS_VPORT_TYPE_INTERNAL:
-        return "internal";
-
-    case OVS_VPORT_TYPE_PATCH:
-        return "patch";
-
-    case OVS_VPORT_TYPE_GRE:
-        if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
-                                        a)) {
-            break;
-        }
-        return (get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
-                ? "ipsec_gre" : "gre");
-
-    case OVS_VPORT_TYPE_GRE64:
-        if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
-                                        a)) {
-            break;
-        }
-        return (get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
-                ? "ipsec_gre64" : "gre64");
-
-    case OVS_VPORT_TYPE_CAPWAP:
-        return "capwap";
-
-    case OVS_VPORT_TYPE_VXLAN:
-        return "vxlan";
-
-    case OVS_VPORT_TYPE_FT_GRE:
-    case __OVS_VPORT_TYPE_MAX:
-        break;
-    }
-
-    VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
-                 vport->dp_ifindex, vport->name, (unsigned int) vport->type);
-    return "unknown";
-}
-
 static int
 netdev_vport_create(const struct netdev_class *netdev_class, const char *name,
                     struct netdev_dev **netdev_devp)
@@ -255,88 +190,12 @@ netdev_vport_close(struct netdev *netdev_)
 }
 
 static int
-netdev_vport_get_config(struct netdev_dev *dev_, struct smap *args)
-{
-    const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
-    const struct vport_class *vport_class = vport_class_cast(netdev_class);
-    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
-    const char *name = netdev_dev_get_name(dev_);
-    int error;
-
-    if (!dev->options) {
-        struct dpif_linux_vport reply;
-        struct ofpbuf *buf;
-
-        error = dpif_linux_vport_get(name, &reply, &buf);
-        if (error) {
-            VLOG_ERR_RL(&rl, "%s: vport query failed (%s)",
-                        name, strerror(error));
-            return error;
-        }
-
-        dev->options = ofpbuf_clone_data(reply.options, reply.options_len);
-        ofpbuf_delete(buf);
-    }
-
-    error = vport_class->unparse_config(name, netdev_class->type,
-                                        dev->options->data,
-                                        dev->options->size,
-                                        args);
-    if (error) {
-        VLOG_ERR_RL(&rl, "%s: failed to parse kernel config (%s)",
-                    name, strerror(error));
-    }
-    return error;
-}
-
-static int
-netdev_vport_set_config(struct netdev_dev *dev_, const struct smap *args)
-{
-    const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
-    const struct vport_class *vport_class = vport_class_cast(netdev_class);
-    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
-    const char *name = netdev_dev_get_name(dev_);
-    struct netdev_tunnel_config tnl_cfg;
-    struct ofpbuf *options;
-    int error;
-
-    options = ofpbuf_new(64);
-    error = vport_class->parse_config(name, netdev_dev_get_type(dev_),
-                                      args, options, &tnl_cfg);
-    if (!error
-        && (!dev->options
-            || options->size != dev->options->size
-            || memcmp(options->data, dev->options->data, options->size))) {
-        struct dpif_linux_vport vport;
-
-        dpif_linux_vport_init(&vport);
-        vport.cmd = OVS_VPORT_CMD_SET;
-        vport.name = name;
-        vport.options = options->data;
-        vport.options_len = options->size;
-        error = dpif_linux_vport_transact(&vport, NULL, NULL);
-        if (!error || error == ENODEV) {
-            /* Either reconfiguration succeeded or this vport is not installed
-             * in the kernel (e.g. it hasn't been added to a dpif yet with
-             * dpif_port_add()). */
-            ofpbuf_delete(dev->options);
-            dev->options = options;
-            dev->tnl_cfg = tnl_cfg;
-            options = NULL;
-            error = 0;
-        }
-    }
-    ofpbuf_delete(options);
-
-    return error;
-}
-
-static int
 netdev_vport_set_etheraddr(struct netdev *netdev,
                            const uint8_t mac[ETH_ADDR_LEN])
 {
-    memcpy(netdev_vport_get_dev(netdev)->etheraddr, mac, ETH_ADDR_LEN);
-    netdev_vport_poll_notify(netdev);
+    struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+    memcpy(dev->etheraddr, mac, ETH_ADDR_LEN);
+    netdev_vport_poll_notify(dev);
     return 0;
 }
 
@@ -456,10 +315,8 @@ netdev_vport_wait(void)
 /* Helper functions. */
 
 static void
-netdev_vport_poll_notify(const struct netdev *netdev)
+netdev_vport_poll_notify(struct netdev_dev_vport *ndv)
 {
-    struct netdev_dev_vport *ndv = netdev_vport_get_dev(netdev);
-
     ndv->change_seq++;
     if (!ndv->change_seq) {
         ndv->change_seq++;
@@ -496,16 +353,16 @@ parse_key(const struct smap *args, const char *name,
 }
 
 static int
-parse_tunnel_config(const char *name, const char *type,
-                    const struct smap *args, struct ofpbuf *options,
-                    struct netdev_tunnel_config *tnl_cfg_)
+set_tunnel_config(struct netdev_dev *dev_, const struct smap *args)
 {
+    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+    const char *name = netdev_dev_get_name(dev_);
+    const char *type = netdev_dev_get_type(dev_);
+
     bool ipsec_mech_set, needs_dst_port, has_csum;
     struct netdev_tunnel_config tnl_cfg;
     struct smap_node *node;
-    uint8_t flags;
 
-    flags = TNL_F_DF_DEFAULT;
     has_csum = strstr(type, "gre");
     ipsec_mech_set = false;
     memset(&tnl_cfg, 0, sizeof tnl_cfg);
@@ -516,9 +373,6 @@ parse_tunnel_config(const char *name, const char *type,
 
     needs_dst_port = !strcmp(type, "vxlan");
     tnl_cfg.ipsec = strstr(type, "ipsec");
-    if (tnl_cfg.ipsec) {
-        flags |= TNL_F_IPSEC;
-    }
     tnl_cfg.dont_fragment = true;
 
     SMAP_FOR_EACH (node, args) {
@@ -538,14 +392,12 @@ parse_tunnel_config(const char *name, const char *type,
             }
         } else if (!strcmp(node->key, "tos")) {
             if (!strcmp(node->value, "inherit")) {
-                flags |= TNL_F_TOS_INHERIT;
                 tnl_cfg.tos_inherit = true;
             } else {
                 char *endptr;
                 int tos;
                 tos = strtol(node->value, &endptr, 0);
                 if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
-                    nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, tos);
                     tnl_cfg.tos = tos;
                 } else {
                     VLOG_WARN("%s: invalid TOS %s", name, node->value);
@@ -553,24 +405,18 @@ parse_tunnel_config(const char *name, const char *type,
             }
         } else if (!strcmp(node->key, "ttl")) {
             if (!strcmp(node->value, "inherit")) {
-                flags |= TNL_F_TTL_INHERIT;
                 tnl_cfg.ttl_inherit = true;
             } else {
-                nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->value));
                 tnl_cfg.ttl = atoi(node->value);
             }
         } else if (!strcmp(node->key, "dst_port") && needs_dst_port) {
             tnl_cfg.dst_port = htons(atoi(node->value));
-            nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT,
-                           atoi(node->value));
         } else if (!strcmp(node->key, "csum") && has_csum) {
             if (!strcmp(node->value, "true")) {
-                flags |= TNL_F_CSUM;
                 tnl_cfg.csum = true;
             }
         } else if (!strcmp(node->key, "df_default")) {
             if (!strcmp(node->value, "false")) {
-                flags &= ~TNL_F_DF_DEFAULT;
                 tnl_cfg.dont_fragment = false;
             }
         } else if (!strcmp(node->key, "peer_cert") && tnl_cfg.ipsec) {
@@ -611,7 +457,6 @@ parse_tunnel_config(const char *name, const char *type,
 
     /* Add a default destination port for VXLAN if none specified. */
     if (needs_dst_port && !tnl_cfg.dst_port) {
-        nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT, VXLAN_DST_PORT);
         tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
     }
 
@@ -647,14 +492,11 @@ parse_tunnel_config(const char *name, const char *type,
                  name, type);
         return EINVAL;
     }
-    nl_msg_put_be32(options, OVS_TUNNEL_ATTR_DST_IPV4, tnl_cfg.ip_dst);
 
     if (tnl_cfg.ip_src) {
         if (ip_is_multicast(tnl_cfg.ip_dst)) {
             VLOG_WARN("%s: remote_ip is multicast, ignoring local_ip", name);
             tnl_cfg.ip_src = 0;
-        } else {
-            nl_msg_put_be32(options, OVS_TUNNEL_ATTR_SRC_IPV4, tnl_cfg.ip_src);
         }
     }
 
@@ -665,147 +507,104 @@ parse_tunnel_config(const char *name, const char *type,
     tnl_cfg.in_key = parse_key(args, "in_key",
                                &tnl_cfg.in_key_present,
                                &tnl_cfg.in_key_flow);
-    if (tnl_cfg.in_key_present && !tnl_cfg.in_key_flow) {
-        nl_msg_put_be64(options, OVS_TUNNEL_ATTR_IN_KEY, tnl_cfg.in_key);
-    }
 
     tnl_cfg.out_key = parse_key(args, "out_key",
                                &tnl_cfg.out_key_present,
                                &tnl_cfg.out_key_flow);
-    if (!tnl_cfg.out_key_flow) {
-        nl_msg_put_be64(options, OVS_TUNNEL_ATTR_OUT_KEY, tnl_cfg.out_key);
-    }
-    nl_msg_put_u32(options, OVS_TUNNEL_ATTR_FLAGS, flags);
-
-    *tnl_cfg_ = tnl_cfg;
 
-    return 0;
-}
-
-static int
-tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len,
-                            struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1])
-{
-    static const struct nl_policy ovs_tunnel_policy[] = {
-        [OVS_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32, .optional = true },
-        [OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32, .optional = true },
-        [OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true },
-        [OVS_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true },
-        [OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
-        [OVS_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
-        [OVS_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true },
-        [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NL_A_U16, .optional = true },
-    };
-    struct ofpbuf buf;
+    dev->tnl_cfg = tnl_cfg;
+    netdev_vport_poll_notify(dev);
 
-    ofpbuf_use_const(&buf, options, options_len);
-    if (!nl_policy_parse(&buf, 0, ovs_tunnel_policy,
-                         a, ARRAY_SIZE(ovs_tunnel_policy))) {
-        return EINVAL;
-    }
     return 0;
 }
 
-static uint64_t
-get_be64_or_zero(const struct nlattr *a)
-{
-    return a ? ntohll(nl_attr_get_be64(a)) : 0;
-}
-
 static int
-unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
-                      const struct nlattr *options, size_t options_len,
-                      struct smap *args)
+get_tunnel_config(struct netdev_dev *dev, struct smap *args)
 {
-    struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
-    uint32_t flags;
-    int error;
-
-    error = tnl_port_config_from_nlattr(options, options_len, a);
-    if (error) {
-        return error;
-    }
+    const struct netdev_tunnel_config *tnl_cfg =
+        &netdev_dev_vport_cast(dev)->tnl_cfg;
 
-    if (a[OVS_TUNNEL_ATTR_DST_IPV4]) {
-        ovs_be32 daddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
-        smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(daddr));
+    if (tnl_cfg->ip_dst) {
+        smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_dst));
     }
 
-    if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
-        ovs_be32 saddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]);
-        smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(saddr));
+    if (tnl_cfg->ip_src) {
+        smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_src));
     }
 
-    if (!a[OVS_TUNNEL_ATTR_IN_KEY] && !a[OVS_TUNNEL_ATTR_OUT_KEY]) {
+    if (tnl_cfg->in_key_flow && tnl_cfg->out_key_flow) {
         smap_add(args, "key", "flow");
+    } else if (tnl_cfg->in_key_present && tnl_cfg->out_key_present
+               && tnl_cfg->in_key == tnl_cfg->out_key) {
+        smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg->in_key));
     } else {
-        uint64_t in_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_IN_KEY]);
-        uint64_t out_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_OUT_KEY]);
-
-        if (in_key && in_key == out_key) {
-            smap_add_format(args, "key", "%"PRIu64, in_key);
-        } else {
-            if (!a[OVS_TUNNEL_ATTR_IN_KEY]) {
-                smap_add(args, "in_key", "flow");
-            } else if (in_key) {
-                smap_add_format(args, "in_key", "%"PRIu64, in_key);
-            }
+        if (tnl_cfg->in_key_flow) {
+            smap_add(args, "in_key", "flow");
+        } else if (tnl_cfg->in_key_present) {
+            smap_add_format(args, "in_key", "%"PRIu64,
+                            ntohll(tnl_cfg->in_key));
+        }
 
-            if (!a[OVS_TUNNEL_ATTR_OUT_KEY]) {
-                smap_add(args, "out_key", "flow");
-            } else if (out_key) {
-                smap_add_format(args, "out_key", "%"PRIu64, out_key);
-            }
+        if (tnl_cfg->out_key_flow) {
+            smap_add(args, "out_key", "flow");
+        } else if (tnl_cfg->out_key_present) {
+            smap_add_format(args, "out_key", "%"PRIu64,
+                            ntohll(tnl_cfg->out_key));
         }
     }
 
-    flags = get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]);
-
-    if (flags & TNL_F_TTL_INHERIT) {
+    if (tnl_cfg->ttl_inherit) {
         smap_add(args, "ttl", "inherit");
-    } else if (a[OVS_TUNNEL_ATTR_TTL]) {
-        int ttl = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TTL]);
-        smap_add_format(args, "ttl", "%d", ttl);
+    } else if (tnl_cfg->ttl != DEFAULT_TTL) {
+        smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg->ttl);
     }
 
-    if (flags & TNL_F_TOS_INHERIT) {
+    if (tnl_cfg->tos_inherit) {
         smap_add(args, "tos", "inherit");
-    } else if (a[OVS_TUNNEL_ATTR_TOS]) {
-        int tos = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TOS]);
-        smap_add_format(args, "tos", "0x%x", tos);
+    } else if (tnl_cfg->tos) {
+        smap_add_format(args, "tos", "0x%x", tnl_cfg->tos);
     }
 
-    if (a[OVS_TUNNEL_ATTR_DST_PORT]) {
-        uint16_t dst_port = nl_attr_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]);
+    if (tnl_cfg->dst_port) {
+        uint16_t dst_port = ntohs(tnl_cfg->dst_port);
         if (dst_port != VXLAN_DST_PORT) {
             smap_add_format(args, "dst_port", "%d", dst_port);
         }
     }
 
-    if (flags & TNL_F_CSUM) {
+    if (tnl_cfg->csum) {
         smap_add(args, "csum", "true");
     }
-    if (flags & TNL_F_DF_INHERIT) {
-        /* Shouldn't happen as "df_inherit" is no longer supported.  However,
-         * for completeness we report it if it's there. */
-        smap_add(args, "df_inherit", "true");
-    }
-    if (!(flags & TNL_F_DF_DEFAULT)) {
+
+    if (!tnl_cfg->dont_fragment) {
         smap_add(args, "df_default", "false");
     }
 
     return 0;
 }
 
+static const char *
+get_tunnel_dpif_port(struct netdev_dev *dev)
+{
+    const char *type = netdev_dev_get_type(dev);
+
+    if (strstr(type, "gre64")) {
+        return "gre64";
+    } else if (strstr(type, "gre")) {
+        return "gre";
+    } else {
+        return type;
+    }
+}
+
 static int
-parse_patch_config(const char *name, const char *type OVS_UNUSED,
-                   const struct smap *args, struct ofpbuf *options,
-                   struct netdev_tunnel_config *tnl_cfg)
+set_patch_config(struct netdev_dev *dev_, const struct smap *args)
 {
+    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+    const char *name = netdev_dev_get_name(dev_);
+    struct ofpbuf *options;
     const char *peer;
 
-    memset(tnl_cfg, 0, sizeof *tnl_cfg);
-
     peer = smap_get(args, "peer");
     if (!peer) {
         VLOG_ERR("%s: patch type requires valid 'peer' argument", name);
@@ -827,27 +626,68 @@ parse_patch_config(const char *name, const char *type OVS_UNUSED,
         return EINVAL;
     }
 
+    options = ofpbuf_new(0);
     nl_msg_put_string(options, OVS_PATCH_ATTR_PEER, peer);
 
+    if (!dev->options
+        || options->size != dev->options->size
+        || memcmp(options->data, dev->options->data, options->size)) {
+        struct dpif_linux_vport vport;
+        int error;
+
+        dpif_linux_vport_init(&vport);
+        vport.cmd = OVS_VPORT_CMD_SET;
+        vport.name = name;
+        vport.options = options->data;
+        vport.options_len = options->size;
+
+        error = dpif_linux_vport_transact(&vport, NULL, NULL);
+        if (error && error != ENODEV) {
+            return error;
+        }
+
+        /* Either reconfiguration succeeded or this vport is not installed in
+         * the kernel (e.g. it hasn't been added to a dpif yet with
+         * dpif_port_add()). */
+        ofpbuf_delete(dev->options);
+        dev->options = options;
+        options = NULL;
+    }
+    ofpbuf_delete(options);
+
     return 0;
 }
 
 static int
-unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
-                     const struct nlattr *options, size_t options_len,
-                     struct smap *args)
+get_patch_config(struct netdev_dev *dev_, struct smap *args)
 {
     static const struct nl_policy ovs_patch_policy[] = {
         [OVS_PATCH_ATTR_PEER] = { .type = NL_A_STRING,
                                .max_len = IFNAMSIZ,
                                .optional = false }
     };
+    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+    const char *name = netdev_dev_get_name(dev_);
 
     struct nlattr *a[ARRAY_SIZE(ovs_patch_policy)];
-    struct ofpbuf buf;
 
-    ofpbuf_use_const(&buf, options, options_len);
-    if (!nl_policy_parse(&buf, 0, ovs_patch_policy,
+    if (!dev->options) {
+        struct dpif_linux_vport reply;
+        struct ofpbuf *buf;
+        int error;
+
+        error = dpif_linux_vport_get(name, &reply, &buf);
+        if (error) {
+            VLOG_ERR_RL(&rl, "%s: vport query failed (%s)",
+                        name, strerror(error));
+            return error;
+        }
+
+        dev->options = ofpbuf_clone_data(reply.options, reply.options_len);
+        ofpbuf_delete(buf);
+    }
+
+    if (!nl_policy_parse(dev->options, 0, ovs_patch_policy,
                          a, ARRAY_SIZE(ovs_patch_policy))) {
         return EINVAL;
     }
@@ -856,16 +696,18 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
     return 0;
 }
 
-#define VPORT_FUNCTIONS(GET_TUNNEL_CONFIG, GET_STATUS)      \
+#define VPORT_FUNCTIONS(GET_DPIF_PORT, GET_CONFIG,          \
+                        SET_CONFIG, GET_TUNNEL_CONFIG,      \
+                        GET_STATUS)                         \
     NULL,                                                   \
     netdev_vport_run,                                       \
     netdev_vport_wait,                                      \
                                                             \
     netdev_vport_create,                                    \
     netdev_vport_destroy,                                   \
-    NULL,                       /* get_dpif_port */         \
-    netdev_vport_get_config,                                \
-    netdev_vport_set_config,                                \
+    GET_DPIF_PORT,                                          \
+    GET_CONFIG,                                             \
+    SET_CONFIG,                                             \
     GET_TUNNEL_CONFIG,                                      \
                                                             \
     netdev_vport_open,                                      \
@@ -919,9 +761,11 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
 
 #define TUNNEL_CLASS(NAME, VPORT_TYPE)                      \
     { VPORT_TYPE,                                           \
-        { NAME, VPORT_FUNCTIONS(get_netdev_tunnel_config,   \
-                                tunnel_get_status) },       \
-            parse_tunnel_config, unparse_tunnel_config }
+        { NAME, VPORT_FUNCTIONS(get_tunnel_dpif_port,       \
+                                get_tunnel_config,          \
+                                set_tunnel_config,          \
+                                get_netdev_tunnel_config,   \
+                                tunnel_get_status) }}
 
 void
 netdev_vport_register(void)
@@ -935,8 +779,11 @@ netdev_vport_register(void)
         TUNNEL_CLASS("vxlan", OVS_VPORT_TYPE_VXLAN),
 
         { OVS_VPORT_TYPE_PATCH,
-          { "patch", VPORT_FUNCTIONS(NULL, NULL) },
-          parse_patch_config, unparse_patch_config }
+          { "patch", VPORT_FUNCTIONS(NULL,
+                                     get_patch_config,
+                                     set_patch_config,
+                                     NULL,
+                                     NULL) }}
     };
 
     int i;
diff --git a/lib/netdev-vport.h b/lib/netdev-vport.h
index 31c1198..dbe7c81 100644
--- a/lib/netdev-vport.h
+++ b/lib/netdev-vport.h
@@ -29,7 +29,6 @@ void netdev_vport_register(void);
 const struct ofpbuf *netdev_vport_get_options(const struct netdev *);
 
 enum ovs_vport_type netdev_vport_get_vport_type(const struct netdev *);
-const char *netdev_vport_get_netdev_type(const struct dpif_linux_vport *);
 
 int netdev_vport_get_stats(const struct netdev *, struct netdev_stats *);
 
diff --git a/lib/netdev.c b/lib/netdev.c
index 94e3468..dafdeb6 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -1453,6 +1453,13 @@ netdev_get_dpif_port(const struct netdev *netdev)
     return port ? port : netdev_get_name(netdev);
 }
 
+const char *
+netdev_get_type_from_name(const char *name)
+{
+    const struct netdev_dev *dev = netdev_dev_from_name(name);
+    return dev ? netdev_dev_get_type(dev) : NULL;
+}
+
 struct netdev_dev *
 netdev_get_dev(const struct netdev *netdev)
 {
diff --git a/lib/netdev.h b/lib/netdev.h
index 942afc6..e9a1b48 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -128,6 +128,7 @@ const struct netdev_tunnel_config *
 const char *netdev_get_name(const struct netdev *);
 const char *netdev_get_type(const struct netdev *);
 const char *netdev_get_dpif_port(const struct netdev *);
+const char *netdev_get_type_from_name(const char *name);
 int netdev_get_mtu(const struct netdev *, int *mtup);
 int netdev_set_mtu(const struct netdev *, int mtu);
 int netdev_get_ifindex(const struct netdev *);
diff --git a/lib/odp-util.c b/lib/odp-util.c
index e2f21da..0d773bd 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -2006,8 +2006,14 @@ commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
     nl_msg_end_nested(odp_actions, offset);
 }
 
-static void
-commit_set_tunnel_action(const struct flow *flow, struct flow *base,
+/* If any of the flow key data that ODP actions can modify are different in
+ * 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
+ * 'odp_actions' that change the flow tunneling information in key from
+ * 'base->tunnel' into 'flow->tunnel', and then changes 'base->tunnel' in the
+ * same way.  In other words, operates the same as commit_odp_actions(), but
+ * only on tunneling information. */
+void
+commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
                          struct ofpbuf *odp_actions)
 {
     if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
@@ -2210,12 +2216,13 @@ commit_set_skb_mark_action(const struct flow *flow, struct flow *base,
 }
 /* If any of the flow key data that ODP actions can modify are different in
  * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
- * key from 'base' into 'flow', and then changes 'base' the same way. */
+ * key from 'base' into 'flow', and then changes 'base' the same way.  Does not
+ * commit set_tunnel actions.  Users should call commit_odp_tunnel_action()
+ * in addition to this function if needed. */
 void
 commit_odp_actions(const struct flow *flow, struct flow *base,
                    struct ofpbuf *odp_actions)
 {
-    commit_set_tunnel_action(flow, base, odp_actions);
     commit_set_ether_addr_action(flow, base, odp_actions);
     commit_vlan_action(flow, base, odp_actions);
     commit_set_nw_action(flow, base, odp_actions);
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 9d38f33..5ee9708 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -106,6 +106,8 @@ enum odp_key_fitness odp_flow_key_to_flow(const struct nlattr *, size_t,
                                           struct flow *);
 const char *odp_key_fitness_to_string(enum odp_key_fitness);
 
+void commit_odp_tunnel_action(const struct flow *, struct flow *base,
+                              struct ofpbuf *odp_actions);
 void commit_odp_actions(const struct flow *, struct flow *base,
                         struct ofpbuf *odp_actions);
 
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 5b86d46..7e0d1a1 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -51,6 +51,7 @@
 #include "simap.h"
 #include "smap.h"
 #include "timer.h"
+#include "tunnel.h"
 #include "unaligned.h"
 #include "unixctl.h"
 #include "vlan-bitmap.h"
@@ -673,8 +674,9 @@ struct ofproto_dpif {
     struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
     struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
 
-    /* Ports. */
-    struct sset ports;             /* Set of port names. */
+    struct sset ports;             /* Set of non-tunnel port names. */
+    struct sset tnls;              /* Set of tunnel port names. */
+    struct sset tnl_backers;       /* Set of dpif ports backing tunnels. */
     struct sset port_poll_set;     /* Queued names for port_poll() reply. */
     int port_poll_errno;           /* Last errno for port_poll() reply. */
 };
@@ -702,6 +704,7 @@ static struct ofport_dpif *get_odp_port(const struct ofproto_dpif *,
 static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
                           const struct ofpbuf *, ovs_be16 initial_tci,
                           struct ds *);
+static bool may_dpif_port_del(struct ofproto_dpif *, struct ofport_dpif *);
 
 /* Packet processing. */
 static void update_learning_table(struct ofproto_dpif *,
@@ -839,6 +842,14 @@ type_run(const char *type)
             goto next;
         }
 
+        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
+                       &all_ofproto_dpifs) {
+            if (sset_contains(&ofproto->ports, devname)
+                || sset_contains(&ofproto->tnl_backers, devname)) {
+                goto next;
+            }
+        }
+
         ofproto = lookup_ofproto_dpif_by_port_name(devname);
         if (dpif_port_query_by_name(backer->dpif, devname, &port)) {
             /* The port was removed.  If we know the datapath,
@@ -1120,6 +1131,8 @@ construct(struct ofproto *ofproto_)
     hmap_init(&ofproto->realdev_vid_map);
 
     sset_init(&ofproto->ports);
+    sset_init(&ofproto->tnls);
+    sset_init(&ofproto->tnl_backers);
     sset_init(&ofproto->port_poll_set);
     ofproto->port_poll_errno = 0;
 
@@ -1264,6 +1277,8 @@ destruct(struct ofproto *ofproto_)
     hmap_destroy(&ofproto->realdev_vid_map);
 
     sset_destroy(&ofproto->ports);
+    sset_destroy(&ofproto->tnls);
+    sset_destroy(&ofproto->tnl_backers);
     sset_destroy(&ofproto->port_poll_set);
 
     close_dpif_backer(ofproto->backer);
@@ -1290,6 +1305,10 @@ run(struct ofproto *ofproto_)
     struct ofbundle *bundle;
     int error;
 
+    if (tnl_run()) {
+        ofproto->need_revalidate = true;
+    }
+
     if (!clogged) {
         complete_operations(ofproto);
     }
@@ -1523,7 +1542,7 @@ port_construct(struct ofport *port_)
     port->carrier_seq = netdev_get_carrier_resets(port->up.netdev);
 
     error = dpif_port_query_by_name(ofproto->backer->dpif,
-                                    netdev_get_name(port->up.netdev),
+                                    netdev_get_dpif_port(port->up.netdev),
                                     &dpif_port);
     if (error) {
         return error;
@@ -1531,12 +1550,19 @@ port_construct(struct ofport *port_)
 
     port->odp_port = dpif_port.port_no;
 
-    /* Sanity-check that a mapping doesn't already exist.  This
-     * shouldn't happen. */
-    if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) {
-        VLOG_ERR("port %s already has an OpenFlow port number\n",
-                 dpif_port.name);
-        return EBUSY;
+    if (!netdev_get_tunnel_config(port->up.netdev)) {
+        /* Sanity-check that a mapping doesn't already exist.  This
+         * shouldn't happen for non-tunnel ports. */
+        if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) {
+            VLOG_ERR("port %s already has an OpenFlow port number\n",
+                     dpif_port.name);
+            return EBUSY;
+        }
+    }
+
+    error = tnl_port_add(&port->up, port->odp_port);
+    if (error) {
+        return error;
     }
 
     hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
@@ -1554,17 +1580,22 @@ port_destruct(struct ofport *port_)
 {
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+    const char *dp_port_name = netdev_get_dpif_port(port->up.netdev);
     const char *devname = netdev_get_name(port->up.netdev);
 
-    if (dpif_port_exists(ofproto->backer->dpif, devname)) {
+    if (dpif_port_exists(ofproto->backer->dpif, dp_port_name)
+        && may_dpif_port_del(ofproto, port)) {
         /* The underlying device is still there, so delete it.  This
          * happens when the ofproto is being destroyed, since the caller
          * assumes that removal of attached ports will happen as part of
          * destruction. */
         dpif_port_del(ofproto->backer->dpif, port->odp_port);
+        sset_find_and_delete(&ofproto->tnl_backers, dp_port_name);
     }
 
+    tnl_port_del(port_);
     sset_find_and_delete(&ofproto->ports, devname);
+    sset_find_and_delete(&ofproto->tnls, devname);
     hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
     ofproto->need_revalidate = REV_RECONFIGURE;
     bundle_remove(port_);
@@ -2874,6 +2905,26 @@ port_query_by_name(const struct ofproto *ofproto_, const char *devname,
     struct dpif_port dpif_port;
     int error;
 
+    if (sset_contains(&ofproto->tnls, devname)) {
+        const struct ofport *ofport;
+        const char *type;
+
+        /* We may be called before ofproto->up.port_by_name is populated with
+         * the appropriate ofport.  For this reason, we must get the name and
+         * type from the netdev layer directly. */
+        type = netdev_get_type_from_name(devname);
+        if (type) {
+            ofproto_port->name = xstrdup(devname);
+            ofproto_port->type = xstrdup(type);
+
+            ofport = shash_find_data(&ofproto->up.port_by_name, devname);
+            ofproto_port->ofp_port = ofport ? ofport->ofp_port : OFPP_NONE;
+            return 0;
+        } else {
+            return ENODEV;
+        }
+    }
+
     if (!sset_contains(&ofproto->ports, devname)) {
         return ENODEV;
     }
@@ -2889,34 +2940,77 @@ static int
 port_add(struct ofproto *ofproto_, struct netdev *netdev)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    uint32_t odp_port = UINT32_MAX;
-    int error;
+    const char *dp_port_name = netdev_get_dpif_port(netdev);
+    const char *devname = netdev_get_name(netdev);
 
-    error = dpif_port_add(ofproto->backer->dpif, netdev, &odp_port);
-    if (!error) {
-        sset_add(&ofproto->ports, netdev_get_name(netdev));
+    if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
+        int error = dpif_port_add(ofproto->backer->dpif, netdev, NULL);
+        if (error) {
+            return error;
+        }
     }
-    return error;
+
+    if (netdev_get_tunnel_config(netdev)) {
+        sset_add(&ofproto->tnls, devname);
+        sset_add(&ofproto->tnl_backers, dp_port_name);
+    } else {
+        sset_add(&ofproto->ports, devname);
+    }
+    return 0;
+}
+
+static bool
+may_dpif_port_del(struct ofproto_dpif *ofproto, struct ofport_dpif *ofport)
+{
+    struct ofproto_dpif *ofproto_iter;
+    struct ofport *ofport_up;
+
+    if (!netdev_get_tunnel_config(ofport->up.netdev)) {
+        return true;
+    }
+
+    HMAP_FOR_EACH (ofproto_iter, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        if (ofproto->backer != ofproto_iter->backer) {
+            continue;
+        }
+
+        HMAP_FOR_EACH (ofport_up, hmap_node, &ofproto_iter->up.ports) {
+            if (&ofport->up == ofport_up) {
+                continue;
+            }
+
+            if (!strcmp(netdev_get_dpif_port(ofport->up.netdev),
+                        netdev_get_dpif_port(ofport_up->netdev))) {
+                return false;
+            }
+        }
+    }
+
+    return true;
 }
 
 static int
 port_del(struct ofproto *ofproto_, uint16_t ofp_port)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    uint32_t odp_port = ofp_port_to_odp_port(ofproto, ofp_port);
+    struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
     int error = 0;
 
-    if (odp_port != OFPP_NONE) {
-        error = dpif_port_del(ofproto->backer->dpif, odp_port);
+    if (!ofport) {
+        return 0;
     }
-    if (!error) {
-        struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
-        if (ofport) {
+
+    sset_find_and_delete(&ofproto->tnls, netdev_get_name(ofport->up.netdev));
+    if (may_dpif_port_del(ofproto, ofport)) {
+        error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port);
+        if (!error) {
             /* The caller is going to close ofport->up.netdev.  If this is a
              * bonded port, then the bond is using that netdev, so remove it
              * from the bond.  The client will need to reconfigure everything
              * after deleting ports, so then the slave will get re-added. */
             bundle_remove(&ofport->up);
+            sset_find_and_delete(&ofproto->tnl_backers,
+                                 netdev_get_dpif_port(ofport->up.netdev));
         }
     }
     return error;
@@ -2982,6 +3076,7 @@ ofproto_update_local_port_stats(const struct ofproto *ofproto_,
 struct port_dump_state {
     uint32_t bucket;
     uint32_t offset;
+    bool tnls;
 };
 
 static int
@@ -2989,22 +3084,21 @@ port_dump_start(const struct ofproto *ofproto_ OVS_UNUSED, void **statep)
 {
     struct port_dump_state *state;
 
-    *statep = state = xmalloc(sizeof *state);
-    state->bucket = 0;
-    state->offset = 0;
+    *statep = state = xzalloc(sizeof *state);
     return 0;
 }
 
 static int
-port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
+port_dump_next(const struct ofproto *ofproto_, void *state_,
                struct ofproto_port *port)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct port_dump_state *state = state_;
+    const struct sset *sset;
     struct sset_node *node;
 
-    while ((node = sset_at_position(&ofproto->ports, &state->bucket,
-                               &state->offset))) {
+    sset = state->tnls ? &ofproto->tnls : &ofproto->ports;
+    while ((node = sset_at_position(sset, &state->bucket, &state->offset))) {
         int error;
 
         error = port_query_by_name(ofproto_, node->name, port);
@@ -3013,6 +3107,13 @@ port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
         }
     }
 
+    if (!state->tnls) {
+        state->tnls = true;
+        state->bucket = 0;
+        state->offset = 0;
+        return port_dump_next(ofproto_, state_, port);
+    }
+
     return EOF;
 }
 
@@ -3416,6 +3517,10 @@ handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops,
  * odp_flow_key_to_flow().  (This differs from the value returned in
  * flow->vlan_tci only for packets received on VLAN splinters.)
  *
+ * Similarly, this function also includes some logic to help with tunnels.  It
+ * may modify 'flow' as necessary to make the tunneling implementation
+ * transparent to the upcall processing logic.
+ *
  * Returns 0 if successful, ENODEV if the parsed flow has no associated ofport,
  * or some other positive errno if there are other problems. */
 static int
@@ -3443,44 +3548,51 @@ ofproto_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
         *odp_in_port = flow->in_port;
     }
 
-    port = odp_port_to_ofport(backer, flow->in_port);
-    if (!port) {
-        flow->in_port = OFPP_NONE;
-        error = ofproto ? ENODEV : 0;
-        goto exit;
+    if (tnl_port_should_receive(flow)) {
+        const struct ofport *ofport = tnl_port_receive(flow);
+        if (!ofport) {
+            error = ENODEV;
+            goto exit;
+        }
+        port = ofport_dpif_cast(ofport);
+        fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
+    } else {
+        port = odp_port_to_ofport(backer, flow->in_port);
+        if (!port) {
+            flow->in_port = OFPP_NONE;
+            error = ofproto ? ENODEV : 0;
+            goto exit;
+        }
+
+        flow->in_port = port->up.ofp_port;
+        if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
+            if (packet) {
+                /* Make the packet resemble the flow, so that it gets sent to
+                 * an OpenFlow controller properly, so that it looks correct
+                 * for sFlow, and so that flow_extract() will get the correct
+                 * vlan_tci if it is called on 'packet'.
+                 *
+                 * The allocated space inside 'packet' probably also contains
+                 * 'key', that is, both 'packet' and 'key' are probably part of
+                 * a struct dpif_upcall (see the large comment on that
+                 * structure definition), so pushing data on 'packet' is in
+                 * general not a good idea since it could overwrite 'key' or
+                 * free it as a side effect.  However, it's OK in this special
+                 * case because we know that 'packet' is inside a Netlink
+                 * attribute: pushing 4 bytes will just overwrite the 4-byte
+                 * "struct nlattr", which is fine since we don't need that
+                 * header anymore. */
+                eth_push_vlan(packet, flow->vlan_tci);
+            }
+            fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
+        }
     }
+    error = 0;
 
     if (ofproto) {
         *ofproto = ofproto_dpif_cast(port->up.ofproto);
     }
 
-    flow->in_port = port->up.ofp_port;
-    if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
-        if (packet) {
-            /* Make the packet resemble the flow, so that it gets sent to an
-             * OpenFlow controller properly, so that it looks correct for
-             * sFlow, and so that flow_extract() will get the correct vlan_tci
-             * if it is called on 'packet'.
-             *
-             * The allocated space inside 'packet' probably also contains
-             * 'key', that is, both 'packet' and 'key' are probably part of a
-             * struct dpif_upcall (see the large comment on that structure
-             * definition), so pushing data on 'packet' is in general not a
-             * good idea since it could overwrite 'key' or free it as a side
-             * effect.  However, it's OK in this special case because we know
-             * that 'packet' is inside a Netlink attribute: pushing 4 bytes
-             * will just overwrite the 4-byte "struct nlattr", which is fine
-             * since we don't need that header anymore. */
-            eth_push_vlan(packet, flow->vlan_tci);
-        }
-
-        /* Let the caller know that we can't reproduce 'key' from 'flow'. */
-        if (fitness == ODP_FIT_PERFECT) {
-            fitness = ODP_FIT_TOO_MUCH;
-        }
-    }
-    error = 0;
-
 exit:
     if (fitnessp) {
         *fitnessp = fitness;
@@ -5399,6 +5511,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
     const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
     uint32_t odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
     ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
+    ovs_be64 flow_tun_id = ctx->flow.tunnel.tun_id;
     uint8_t flow_nw_tos = ctx->flow.nw_tos;
     struct priority_to_dscp *pdscp;
     uint32_t out_port;
@@ -5420,10 +5533,16 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
         ctx->flow.nw_tos |= pdscp->dscp;
     }
 
-    out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
-                                      ctx->flow.vlan_tci);
-    if (out_port != odp_port) {
-        ctx->flow.vlan_tci = htons(0);
+    if (!tnl_port_send(&ofport->up, &ctx->flow, &odp_port)) {
+        out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
+                                          ctx->flow.vlan_tci);
+        if (out_port != odp_port) {
+            ctx->flow.vlan_tci = htons(0);
+        }
+    } else {
+        out_port = odp_port;
+        commit_odp_tunnel_action(&ctx->flow, &ctx->base_flow,
+                                 ctx->odp_actions);
     }
     commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
     nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
@@ -5431,6 +5550,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
     ctx->sflow_odp_port = odp_port;
     ctx->sflow_n_outputs++;
     ctx->nf_output_iface = ofp_port;
+    ctx->flow.tunnel.tun_id = flow_tun_id;
     ctx->flow.vlan_tci = flow_vlan_tci;
     ctx->flow.nw_tos = flow_nw_tos;
 }
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index fd66d24..eea7fac 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -212,25 +212,6 @@ AT_CHECK([tail -1 stdout], [0],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto-dpif - set_tunnel])
-OVS_VSWITCHD_START
-ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [90])
-AT_DATA([flows.txt], [dnl
-in_port=90 actions=resubmit:1,resubmit:2,resubmit:3,resubmit:4,resubmit:5
-in_port=1 actions=set_tunnel:1,output:1
-in_port=2 actions=set_tunnel:1,output:2
-in_port=3 actions=set_tunnel:2,set_tunnel:3,output:3
-in_port=4 actions=set_tunnel:4,set_tunnel:3,output:4
-in_port=5 actions=set_tunnel:5
-])
-AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0],
-  [Datapath actions: set(tun_id(0x1)),1,2,set(tun_id(0x3)),3,4
-])
-OVS_VSWITCHD_STOP
-AT_CLEANUP
-
 AT_SETUP([ofproto-dpif - controller])
 OVS_VSWITCHD_START([dnl
    add-port br0 p1 -- set Interface p1 type=dummy
-- 
1.7.9.5




More information about the dev mailing list