[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