- Inheritance of the Don't Fragment bit in IP tunnels (df_inherit) is
no longer supported.
- Patch ports are implemented in userspace.
+ - Tunneling requires the version of the kernel module paired with Open
+ vSwitch 1.9.0 or later.
v1.9.0 - xx xxx xxxx
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_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_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)
const char *name = netdev_vport_get_dpif_port(netdev);
const char *type = netdev_get_type(netdev);
struct dpif_linux_vport request, reply;
- const struct ofpbuf *options;
struct nl_sock *sock = NULL;
uint32_t upcall_pid;
struct ofpbuf *buf;
}
request.name = name;
- options = netdev_vport_get_options(netdev);
- if (options && options->size) {
- request.options = options->data;
- request.options_len = options->size;
- }
-
if (request.type == OVS_VPORT_TYPE_NETDEV) {
netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false);
}
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);
}
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;
}
struct netdev_dev netdev_dev;
unsigned int change_seq;
uint8_t etheraddr[ETH_ADDR_LEN];
+ struct netdev_stats stats;
/* Tunnels. */
- struct ofpbuf *options;
struct netdev_tunnel_config tnl_cfg;
/* Patch Ports. */
- struct netdev_stats stats;
char *peer;
};
struct vport_class {
enum ovs_vport_type type;
+ const char *dpif_port;
struct netdev_class netdev_class;
};
-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 int get_patch_config(struct netdev_dev *, struct smap *args);
-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)
return &netdev_dev_vport_cast(netdev_dev)->tnl_cfg;
}
-/* If 'netdev' is a vport netdev, returns an ofpbuf that contains Netlink
- * options to include in OVS_VPORT_ATTR_OPTIONS for configuring that vport.
- * Otherwise returns NULL. */
-const struct ofpbuf *
-netdev_vport_get_options(const struct netdev *netdev)
-{
- const struct netdev_dev *dev = netdev_get_dev(netdev);
-
- return (is_vport_class(netdev_dev_get_class(dev))
- ? netdev_dev_vport_cast(dev)->options
- : NULL);
-}
-
enum ovs_vport_type
netdev_vport_get_vport_type(const struct netdev *netdev)
{
const char *
netdev_vport_get_dpif_port(const struct netdev *netdev)
{
- return netdev_get_name(netdev);
-}
-
-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_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_MAX:
- break;
- }
+ const struct netdev_dev *dev = netdev_get_dev(netdev);
+ const struct netdev_class *class = netdev_dev_get_class(dev);
+ const char *dpif_port;
- VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
- vport->dp_ifindex, vport->name, (unsigned int) vport->type);
- return "unknown";
+ dpif_port = (is_vport_class(class)
+ ? vport_class_cast(class)->dpif_port
+ : NULL);
+ return dpif_port ? dpif_port : netdev_get_name(netdev);
}
static int
{
struct netdev_dev_vport *netdev_dev = netdev_dev_vport_cast(netdev_dev_);
- ofpbuf_delete(netdev_dev->options);
route_table_unregister();
free(netdev_dev->peer);
free(netdev_dev);
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;
}
/* 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++;
bool ipsec_mech_set, needs_dst_port, has_csum;
struct netdev_tunnel_config tnl_cfg;
struct smap_node *node;
- struct ofpbuf *options;
- int error = EINVAL;
- uint8_t flags;
- flags = TNL_F_DF_DEFAULT;
has_csum = strstr(type, "gre");
ipsec_mech_set = false;
memset(&tnl_cfg, 0, sizeof tnl_cfg);
- options = ofpbuf_new(64);
-
if (!strcmp(type, "capwap")) {
VLOG_WARN_ONCE("CAPWAP tunnel support is deprecated.");
}
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) {
}
} 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);
}
} 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) {
if (!use_ssl_cert || strcmp(use_ssl_cert, "true")) {
VLOG_ERR("%s: 'peer_cert' requires 'certificate' argument",
name);
- goto exit;
+ return EINVAL;
}
ipsec_mech_set = true;
}
/* 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);
}
if (pid < 0) {
VLOG_ERR("%s: IPsec requires the ovs-monitor-ipsec daemon",
name);
- goto exit;
+ return EINVAL;
}
if (smap_get(args, "peer_cert") && smap_get(args, "psk")) {
VLOG_ERR("%s: cannot define both 'peer_cert' and 'psk'", name);
- goto exit;
+ return EINVAL;
}
if (!ipsec_mech_set) {
VLOG_ERR("%s: IPsec requires an 'peer_cert' or psk' argument",
name);
- goto exit;
+ return EINVAL;
}
}
if (!tnl_cfg.ip_dst) {
VLOG_ERR("%s: %s type requires valid 'remote_ip' argument",
name, type);
- goto exit;
+ 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);
}
}
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_present && !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);
dev->tnl_cfg = tnl_cfg;
+ netdev_vport_poll_notify(dev);
- error = 0;
- if (!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;
- options = NULL;
- error = 0;
- }
- }
-
-exit:
- ofpbuf_delete(options);
- return error;
-}
-
-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;
-
- 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
-get_tunnel_config(struct netdev_dev *dev_, struct smap *args)
+get_tunnel_config(struct netdev_dev *dev, struct smap *args)
{
- struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
- const char *name = netdev_dev_get_name(dev_);
- struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
- uint32_t flags;
- int error;
+ const struct netdev_tunnel_config *tnl_cfg =
+ &netdev_dev_vport_cast(dev)->tnl_cfg;
- 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);
+ if (tnl_cfg->ip_dst) {
+ smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_dst));
}
- error = tnl_port_config_from_nlattr(dev->options->data, dev->options->size,
- a);
- if (error) {
- VLOG_ERR_RL(&rl, "%s: failed to parse kernel config (%s)",
- name, strerror(error));
- return error;
- }
-
- 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_src) {
+ smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_src));
}
- 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 (!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_DEFAULT)) {
+ if (!tnl_cfg->dont_fragment) {
smap_add(args, "df_default", "false");
}
}
void
-netdev_vport_patch_inc_rx(const struct netdev *netdev,
+netdev_vport_inc_rx(const struct netdev *netdev,
const struct dpif_flow_stats *stats)
{
- if (netdev_vport_is_patch(netdev)) {
+ if (is_vport_class(netdev_dev_get_class(netdev_get_dev(netdev)))) {
struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
dev->stats.rx_packets += stats->n_packets;
dev->stats.rx_bytes += stats->n_bytes;
}
void
-netdev_vport_patch_inc_tx(const struct netdev *netdev,
- const struct dpif_flow_stats *stats)
+netdev_vport_inc_tx(const struct netdev *netdev,
+ const struct dpif_flow_stats *stats)
{
- if (netdev_vport_is_patch(netdev)) {
+ if (is_vport_class(netdev_dev_get_class(netdev_get_dev(netdev)))) {
struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
dev->stats.tx_packets += stats->n_packets;
dev->stats.tx_bytes += stats->n_bytes;
}
static int
-patch_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
+get_stats(const struct netdev *netdev, struct netdev_stats *stats)
{
struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
memcpy(stats, &dev->stats, sizeof *stats);
}
\f
#define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG, \
- GET_TUNNEL_CONFIG, GET_STATS, \
- GET_STATUS) \
+ GET_TUNNEL_CONFIG, GET_STATUS) \
NULL, \
netdev_vport_run, \
netdev_vport_wait, \
NULL, /* get_carrier */ \
NULL, /* get_carrier_resets */ \
NULL, /* get_miimon */ \
- GET_STATS, \
+ get_stats, \
NULL, /* set_stats */ \
\
NULL, /* get_features */ \
\
netdev_vport_change_seq
-#define TUNNEL_CLASS(NAME, VPORT_TYPE) \
- { VPORT_TYPE, \
+#define TUNNEL_CLASS(NAME, VPORT_TYPE, DPIF_PORT) \
+ { VPORT_TYPE, DPIF_PORT, \
{ NAME, VPORT_FUNCTIONS(get_tunnel_config, \
set_tunnel_config, \
get_netdev_tunnel_config, \
- netdev_vport_get_stats, \
tunnel_get_status) }}
void
netdev_vport_register(void)
{
static const struct vport_class vport_classes[] = {
- TUNNEL_CLASS("gre", OVS_VPORT_TYPE_GRE),
- TUNNEL_CLASS("ipsec_gre", OVS_VPORT_TYPE_GRE),
- TUNNEL_CLASS("gre64", OVS_VPORT_TYPE_GRE64),
- TUNNEL_CLASS("ipsec_gre64", OVS_VPORT_TYPE_GRE64),
- TUNNEL_CLASS("capwap", OVS_VPORT_TYPE_CAPWAP),
- TUNNEL_CLASS("vxlan", OVS_VPORT_TYPE_VXLAN),
-
- { OVS_VPORT_TYPE_UNSPEC,
+ TUNNEL_CLASS("gre", OVS_VPORT_TYPE_GRE, "gre_system"),
+ TUNNEL_CLASS("ipsec_gre", OVS_VPORT_TYPE_GRE, "gre_system"),
+ TUNNEL_CLASS("gre64", OVS_VPORT_TYPE_GRE64, "gre64_system"),
+ TUNNEL_CLASS("ipsec_gre64", OVS_VPORT_TYPE_GRE64, "gre64_system"),
+ TUNNEL_CLASS("capwap", OVS_VPORT_TYPE_CAPWAP, "capwap_system"),
+ TUNNEL_CLASS("vxlan", OVS_VPORT_TYPE_VXLAN, "vxlan_system"),
+
+ { OVS_VPORT_TYPE_UNSPEC, NULL,
{ "patch", VPORT_FUNCTIONS(get_patch_config,
set_patch_config,
NULL,
- patch_get_stats,
NULL) }},
};
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 *);
bool netdev_vport_is_patch(const struct netdev *);
int netdev_vport_get_stats(const struct netdev *, struct netdev_stats *);
const char *netdev_vport_patch_peer(const struct netdev *netdev);
-void netdev_vport_patch_inc_rx(const struct netdev *,
- const struct dpif_flow_stats *);
-void netdev_vport_patch_inc_tx(const struct netdev *,
- const struct dpif_flow_stats *);
+void netdev_vport_inc_rx(const struct netdev *,
+ const struct dpif_flow_stats *);
+void netdev_vport_inc_tx(const struct netdev *,
+ const struct dpif_flow_stats *);
const char *netdev_vport_get_dpif_port(const struct netdev *);
return cookie ? odp_actions->size - NLA_ALIGN(sizeof *cookie) : 0;
}
+
+void
+odp_put_tunnel_action(const struct flow_tnl *tunnel,
+ struct ofpbuf *odp_actions)
+{
+ size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
+ tun_key_to_attr(odp_actions, tunnel);
+ nl_msg_end_nested(odp_actions, offset);
+}
\f
/* The commit_odp_actions() function and its helpers. */
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)) {
/* A valid IPV4_TUNNEL must have non-zero ip_dst. */
if (flow->tunnel.ip_dst) {
- size_t offset;
-
- offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
- tun_key_to_attr(odp_actions, &base->tunnel);
- nl_msg_end_nested(odp_actions, offset);
+ odp_put_tunnel_action(&base->tunnel, odp_actions);
} else {
commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
&base->tunnel.tun_id, sizeof base->tunnel.tun_id);
}
/* 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);
/*
- * 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.
struct ds;
struct flow;
+struct flow_tnl;
struct nlattr;
struct ofpbuf;
struct simap;
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);
\f
size_t odp_put_userspace_action(uint32_t pid,
const union user_action_cookie *,
struct ofpbuf *odp_actions);
+void odp_put_tunnel_action(const struct flow_tnl *tunnel,
+ struct ofpbuf *odp_actions);
/* Reasons why a subfacet might not be fast-pathable. */
enum slow_path_reason {
#include "simap.h"
#include "smap.h"
#include "timer.h"
+#include "tunnel.h"
#include "unaligned.h"
#include "unixctl.h"
#include "vlan-bitmap.h"
uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */
bool may_enable; /* May be enabled in bonds. */
long long int carrier_seq; /* Carrier status changes. */
+ struct tnl_port *tnl_port; /* Tunnel handle, or null. */
/* Spanning tree. */
struct stp_port *stp_port; /* Spanning Tree Protocol, if any. */
struct timer next_expiration;
struct hmap odp_to_ofport_map; /* ODP port to ofport mapping. */
+ struct sset tnl_backers; /* Set of dpif ports backing tunnels. */
+
/* Facet revalidation flags applying to facets which use this backer. */
enum revalidate_reason need_revalidate; /* Revalidate every facet. */
struct tag_set revalidate_set; /* Revalidate only matching facets. */
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 ofport_dpif *);
/* Packet processing. */
static void update_learning_table(struct ofproto_dpif *,
goto next;
}
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
+ &all_ofproto_dpifs) {
+ if (sset_contains(&ofproto->backer->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,
drop_key_clear(backer);
hmap_destroy(&backer->drop_keys);
+ sset_destroy(&backer->tnl_backers);
hmap_destroy(&backer->odp_to_ofport_map);
node = shash_find(&all_dpif_backers, backer->type);
free(backer->type);
hmap_init(&backer->drop_keys);
timer_set_duration(&backer->next_expiration, 1000);
backer->need_revalidate = 0;
+ sset_init(&backer->tnl_backers);
tag_set_init(&backer->revalidate_set);
*backerp = backer;
{
struct ofport_dpif *port = ofport_dpif_cast(port_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ const struct netdev *netdev = port->up.netdev;
struct dpif_port dpif_port;
int error;
port->may_enable = true;
port->stp_port = NULL;
port->stp_state = STP_DISABLED;
+ port->tnl_port = NULL;
hmap_init(&port->priorities);
port->realdev_ofp_port = 0;
port->vlandev_vid = 0;
- port->carrier_seq = netdev_get_carrier_resets(port->up.netdev);
+ port->carrier_seq = netdev_get_carrier_resets(netdev);
- if (netdev_vport_is_patch(port->up.netdev)) {
+ if (netdev_vport_is_patch(netdev)) {
/* XXX By bailing out here, we don't do required sFlow work. */
port->odp_port = OVSP_NONE;
return 0;
}
error = dpif_port_query_by_name(ofproto->backer->dpif,
- netdev_get_name(port->up.netdev),
+ netdev_vport_get_dpif_port(netdev),
&dpif_port);
if (error) {
return error;
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(netdev)) {
+ port->tnl_port = tnl_port_add(&port->up, port->odp_port);
+ } else {
+ /* 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",
+ dpif_port.name);
+ return EBUSY;
+ }
- hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
- hash_int(port->odp_port, 0));
+ hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
+ hash_int(port->odp_port, 0));
+ }
if (ofproto->sflow) {
dpif_sflow_add_port(ofproto->sflow, port_, port->odp_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_vport_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(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->backer->tnl_backers, dp_port_name);
}
- if (port->odp_port != OVSP_NONE) {
+ if (port->odp_port != OVSP_NONE && !port->tnl_port) {
hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
}
+ tnl_port_del(port->tnl_port);
sset_find_and_delete(&ofproto->ports, devname);
sset_find_and_delete(&ofproto->ghost_ports, devname);
ofproto->backer->need_revalidate = REV_RECONFIGURE;
ofport->carrier_seq = carrier_seq;
port_run_fast(ofport);
+
+ if (ofport->tnl_port
+ && tnl_port_reconfigure(&ofport->up, ofport->odp_port,
+ &ofport->tnl_port)) {
+ ofproto_dpif_cast(ofport->up.ofproto)->backer->need_revalidate = true;
+ }
+
if (ofport->cfm) {
int cfm_opup = cfm_get_opup(ofport->cfm);
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_vport_get_dpif_port(netdev);
+ const char *devname = netdev_get_name(netdev);
if (netdev_vport_is_patch(netdev)) {
sset_add(&ofproto->ghost_ports, netdev_get_name(netdev));
return 0;
}
- 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->ghost_ports, devname);
+ sset_add(&ofproto->backer->tnl_backers, dp_port_name);
+ } else {
+ sset_add(&ofproto->ports, devname);
+ }
+ return 0;
+}
+
+/* Returns true if the odp_port backing 'ofport' may be deleted from the
+ * datapath. In most cases, this function simply returns true. However, for
+ * tunnels it's possible that multiple ofports use the same odp_port, in which
+ * case we need to keep the odp_port backer around until the last ofport is
+ * deleted. */
+static bool
+may_dpif_port_del(struct ofport_dpif *ofport)
+{
+ struct dpif_backer *backer = ofproto_dpif_cast(ofport->up.ofproto)->backer;
+ struct ofproto_dpif *ofproto_iter;
+
+ if (!ofport->tnl_port) {
+ return true;
+ }
+
+ HMAP_FOR_EACH (ofproto_iter, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct ofport_dpif *iter;
+
+ if (backer != ofproto_iter->backer) {
+ continue;
+ }
+
+ HMAP_FOR_EACH (iter, up.hmap_node, &ofproto_iter->up.ports) {
+ if (ofport == iter) {
+ continue;
+ }
+
+ if (!strcmp(netdev_vport_get_dpif_port(ofport->up.netdev),
+ netdev_vport_get_dpif_port(iter->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->ghost_ports,
+ netdev_get_name(ofport->up.netdev));
+ if (may_dpif_port_del(ofport)) {
+ error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port);
+ if (!error) {
+ const char *dpif_port;
+
/* 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. */
+ dpif_port = netdev_vport_get_dpif_port(ofport->up.netdev);
+ sset_find_and_delete(&ofproto->backer->tnl_backers, dpif_port);
bundle_remove(&ofport->up);
}
}
}
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_);
* 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
{
const struct ofport_dpif *port;
enum odp_key_fitness fitness;
- int error;
+ int error = ENODEV;
fitness = odp_flow_key_to_flow(key, key_len, flow);
if (fitness == ODP_FIT_ERROR) {
*odp_in_port = flow->in_port;
}
- port = odp_port_to_ofport(backer, flow->in_port);
- if (!port) {
- flow->in_port = OFPP_NONE;
- error = ENODEV;
- goto exit;
- }
+ if (tnl_port_should_receive(flow)) {
+ const struct ofport *ofport = tnl_port_receive(flow);
+ if (!ofport) {
+ flow->in_port = OFPP_NONE;
+ goto exit;
+ }
+ port = ofport_dpif_cast(ofport);
- if (ofproto) {
- *ofproto = ofproto_dpif_cast(port->up.ofproto);
- }
+ /* We can't reproduce 'key' from 'flow'. */
+ fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
- 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;
+ /* XXX: Since the tunnel module is not scoped per backer, it's
+ * theoretically possible that we'll receive an ofport belonging to an
+ * entirely different datapath. In practice, this can't happen because
+ * no platforms has two separate datapaths which each support
+ * tunneling. */
+ ovs_assert(ofproto_dpif_cast(port->up.ofproto)->backer == backer);
+ } else {
+ port = odp_port_to_ofport(backer, flow->in_port);
+ if (!port) {
+ flow->in_port = OFPP_NONE;
+ 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);
+ }
+ /* We can't reproduce 'key' from 'flow'. */
+ fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
}
}
error = 0;
+ if (ofproto) {
+ *ofproto = ofproto_dpif_cast(port->up.ofproto);
+ }
+
exit:
if (fitnessp) {
*fitnessp = fitness;
struct flow flow;
struct subfacet *subfacet;
struct ofproto_dpif *ofproto;
+ struct ofport_dpif *ofport;
uint32_t key_hash;
if (ofproto_receive(backer, NULL, key, key_len, &flow, NULL, &ofproto,
continue;
}
+ ofport = get_ofp_port(ofproto, flow.in_port);
+ if (ofport && ofport->tnl_port) {
+ netdev_vport_inc_rx(ofport->up.netdev, stats);
+ }
+
key_hash = odp_flow_key_hash(key, key_len);
subfacet = subfacet_find(ofproto, key, key_len, key_hash, &flow);
switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
{
const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf key, odp_actions;
struct odputil_keybuf keybuf;
uint32_t odp_port;
}
dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
- netdev_vport_patch_inc_tx(ofport->up.netdev, &stats);
- netdev_vport_patch_inc_rx(peer->up.netdev, &stats);
+ netdev_vport_inc_tx(ofport->up.netdev, &stats);
+ netdev_vport_inc_rx(peer->up.netdev, &stats);
flow.in_port = peer->up.ofp_port;
peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
return 0;
}
- odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
- flow.vlan_tci);
- if (odp_port != ofport->odp_port) {
- eth_pop_vlan(packet);
- flow.vlan_tci = htons(0);
+ ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
+
+ if (ofport->tnl_port) {
+ struct dpif_flow_stats stats;
+
+ odp_port = tnl_port_send(ofport->tnl_port, &flow);
+ if (odp_port == OVSP_NONE) {
+ return ENODEV;
+ }
+
+ dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
+ netdev_vport_inc_tx(ofport->up.netdev, &stats);
+ odp_put_tunnel_action(&flow.tunnel, &odp_actions);
+ } else {
+ odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
+ flow.vlan_tci);
+ if (odp_port != ofport->odp_port) {
+ eth_pop_vlan(packet);
+ flow.vlan_tci = htons(0);
+ }
}
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
odp_flow_key_from_flow(&key, &flow,
ofp_port_to_odp_port(ofproto, flow.in_port));
- ofpbuf_init(&odp_actions, 32);
compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
{
const struct ofport_dpif *ofport = get_ofp_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, odp_port;
ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
if (ctx->resubmit_stats) {
- netdev_vport_patch_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
- netdev_vport_patch_inc_rx(peer->up.netdev, ctx->resubmit_stats);
+ netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
+ netdev_vport_inc_rx(peer->up.netdev, ctx->resubmit_stats);
}
return;
}
odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_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);
+ if (ofport->tnl_port) {
+ odp_port = tnl_port_send(ofport->tnl_port, &ctx->flow);
+ if (odp_port == OVSP_NONE) {
+ xlate_report(ctx, "Tunneling decided against output");
+ return;
+ }
+
+ if (ctx->resubmit_stats) {
+ netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
+ }
+ out_port = odp_port;
+ commit_odp_tunnel_action(&ctx->flow, &ctx->base_flow,
+ ctx->odp_actions);
+ } else {
+ 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);
+ }
}
commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_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;
}
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