summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--include/openvswitch/flow.h19
-rw-r--r--include/openvswitch/ofp-actions.h10
-rw-r--r--include/openvswitch/packets.h8
-rw-r--r--lib/dpctl.c29
-rw-r--r--lib/dpif-netdev.c79
-rw-r--r--lib/flow.c205
-rw-r--r--lib/flow.h36
-rw-r--r--lib/match.c73
-rw-r--r--lib/meta-flow.c23
-rw-r--r--lib/nx-match.c14
-rw-r--r--lib/odp-util.c190
-rw-r--r--lib/odp-util.h8
-rw-r--r--lib/ofp-actions.c62
-rw-r--r--lib/ofp-util.c58
-rw-r--r--lib/tnl-ports.c2
-rw-r--r--ofproto/bond.c2
-rw-r--r--ofproto/ofproto-dpif-ipfix.c6
-rw-r--r--ofproto/ofproto-dpif-rid.h2
-rw-r--r--ofproto/ofproto-dpif-sflow.c4
-rw-r--r--ofproto/ofproto-dpif-xlate.c378
-rw-r--r--ofproto/ofproto-dpif-xlate.h6
-rw-r--r--ofproto/ofproto-dpif.c43
-rw-r--r--ofproto/ofproto.c6
-rw-r--r--ofproto/ofproto.h1
-rw-r--r--ovn/controller/pinctrl.c5
-rw-r--r--tests/ofp-print.at6
-rw-r--r--tests/ofproto-dpif.at340
-rw-r--r--tests/test-classifier.c17
-rw-r--r--tests/test-odp.c1
-rw-r--r--utilities/ovs-ofctl.c29
-rw-r--r--vswitchd/bridge.c2
-rw-r--r--vswitchd/vswitch.xml23
33 files changed, 1110 insertions, 578 deletions
diff --git a/NEWS b/NEWS
index 78c676b3e..18aa60e8d 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,7 @@ Post-v2.7.0
'ovs-appctl vlog' commands for 'dpdk' module. Lower bound
still can be configured via extra arguments for DPDK EAL.
- The "learn" action now supports a "limit" option (see ovs-ofctl(8)).
+ - New support for multiple VLANs (802.1ad or "QinQ").
v2.7.0 - 21 Feb 2017
---------------------
diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
index 6606d3c55..188467dc4 100644
--- a/include/openvswitch/flow.h
+++ b/include/openvswitch/flow.h
@@ -23,7 +23,7 @@
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 37
+#define FLOW_WC_SEQ 38
/* Number of Open vSwitch extension 32-bit registers. */
#define FLOW_N_REGS 16
@@ -64,6 +64,16 @@ const char *flow_tun_flag_to_string(uint32_t flags);
/* Maximum number of supported SAMPLE action nesting. */
#define FLOW_MAX_SAMPLE_NESTING 10
+/* Maximum number of supported VLAN headers.
+ *
+ * We require this to be a multiple of 2 so that vlans[] in struct flow is a
+ * multiple of 64 bits. */
+#define FLOW_MAX_VLAN_HEADERS 2
+BUILD_ASSERT_DECL(FLOW_MAX_VLAN_HEADERS % 2 == 0);
+
+/* Legacy maximum VLAN headers */
+#define LEGACY_MAX_VLAN_HEADERS 1
+
/*
* A flow in the network.
*
@@ -107,7 +117,8 @@ struct flow {
struct eth_addr dl_dst; /* Ethernet destination address. */
struct eth_addr dl_src; /* Ethernet source address. */
ovs_be16 dl_type; /* Ethernet frame type. */
- ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
+ uint8_t pad2[2]; /* Pad to 64 bits. */
+ union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS]; /* VLANs */
ovs_be32 mpls_lse[ROUND_UP(FLOW_MAX_MPLS_LABELS, 2)]; /* MPLS label stack
(with padding). */
/* L3 (64-bit aligned) */
@@ -146,8 +157,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0);
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
- == sizeof(struct flow_tnl) + 292
- && FLOW_WC_SEQ == 37);
+ == sizeof(struct flow_tnl) + 300
+ && FLOW_WC_SEQ == 38);
/* Incremental points at which flow classification may be performed in
* segments.
diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index fc2f1def6..e7529bfd6 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -68,7 +68,7 @@ struct vl_mff_map;
OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact, "set_vlan_vid") \
OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact, "set_vlan_pcp") \
OFPACT(STRIP_VLAN, ofpact_null, ofpact, "strip_vlan") \
- OFPACT(PUSH_VLAN, ofpact_null, ofpact, "push_vlan") \
+ OFPACT(PUSH_VLAN, ofpact_push_vlan, ofpact, "push_vlan") \
OFPACT(SET_ETH_SRC, ofpact_mac, ofpact, "mod_dl_src") \
OFPACT(SET_ETH_DST, ofpact_mac, ofpact, "mod_dl_dst") \
OFPACT(SET_IPV4_SRC, ofpact_ipv4, ofpact, "mod_nw_src") \
@@ -390,6 +390,14 @@ struct ofpact_vlan_pcp {
bool flow_has_vlan; /* VLAN present at action validation time? */
};
+/* OFPACT_PUSH_VLAN.
+ *
+ * Used for OFPAT11_PUSH_VLAN. */
+struct ofpact_push_vlan {
+ struct ofpact ofpact;
+ ovs_be16 ethertype;
+};
+
/* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST.
*
* Used for OFPAT10_SET_DL_SRC, OFPAT10_SET_DL_DST. */
diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
index 5d97309a9..f13d634ea 100644
--- a/include/openvswitch/packets.h
+++ b/include/openvswitch/packets.h
@@ -61,4 +61,12 @@ union flow_in_port {
ofp_port_t ofp_port;
};
+union flow_vlan_hdr {
+ ovs_be32 qtag;
+ struct {
+ ovs_be16 tpid; /* ETH_TYPE_VLAN_DOT1Q or ETH_TYPE_DOT1AD */
+ ovs_be16 tci;
+ };
+};
+
#endif /* packets.h */
diff --git a/lib/dpctl.c b/lib/dpctl.c
index 23837ce74..11be85706 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -1408,6 +1408,7 @@ dpctl_normalize_actions(int argc, const char *argv[],
struct ds s;
int left;
int i, error;
+ int encaps = 0;
ds_init(&s);
@@ -1464,12 +1465,14 @@ dpctl_normalize_actions(int argc, const char *argv[],
const struct ovs_action_push_vlan *push;
switch(nl_attr_type(a)) {
case OVS_ACTION_ATTR_POP_VLAN:
- flow.vlan_tci = htons(0);
+ flow_pop_vlan(&flow, NULL);
continue;
case OVS_ACTION_ATTR_PUSH_VLAN:
+ flow_push_vlan_uninit(&flow, NULL);
push = nl_attr_get_unspec(a, sizeof *push);
- flow.vlan_tci = push->vlan_tci;
+ flow.vlans[0].tpid = push->vlan_tpid;
+ flow.vlans[0].tci = push->vlan_tci;
continue;
}
@@ -1495,12 +1498,22 @@ dpctl_normalize_actions(int argc, const char *argv[],
sort_output_actions(af->actions.data, af->actions.size);
- if (af->flow.vlan_tci != htons(0)) {
- dpctl_print(dpctl_p, "vlan(vid=%"PRIu16",pcp=%d): ",
- vlan_tci_to_vid(af->flow.vlan_tci),
- vlan_tci_to_pcp(af->flow.vlan_tci));
- } else {
- dpctl_print(dpctl_p, "no vlan: ");
+ for (encaps = 0; encaps < FLOW_MAX_VLAN_HEADERS; encaps ++) {
+ union flow_vlan_hdr *vlan = &af->flow.vlans[encaps];
+ if (vlan->tci != htons(0)) {
+ dpctl_print(dpctl_p, "vlan(");
+ if (vlan->tpid != htons(ETH_TYPE_VLAN)) {
+ dpctl_print(dpctl_p, "tpid=0x%04"PRIx16",", vlan->tpid);
+ }
+ dpctl_print(dpctl_p, "vid=%"PRIu16",pcp=%d): ",
+ vlan_tci_to_vid(vlan->tci),
+ vlan_tci_to_pcp(vlan->tci));
+ } else {
+ if (encaps == 0) {
+ dpctl_print(dpctl_p, "no vlan: ");
+ }
+ break;
+ }
}
if (eth_type_mpls(af->flow.dl_type)) {
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 7d53a8def..a14a2ebb5 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -104,6 +104,7 @@ static struct vlog_rate_limit upcall_rl = VLOG_RATE_LIMIT_INIT(600, 600);
#define DP_NETDEV_CS_UNSUPPORTED_MASK (~(uint32_t)DP_NETDEV_CS_SUPPORTED_MASK)
static struct odp_support dp_netdev_support = {
+ .max_vlan_headers = SIZE_MAX,
.max_mpls_depth = SIZE_MAX,
.recirc = true,
.ct_state = true,
@@ -441,7 +442,7 @@ struct dp_netdev_flow {
static void dp_netdev_flow_unref(struct dp_netdev_flow *);
static bool dp_netdev_flow_ref(struct dp_netdev_flow *);
static int dpif_netdev_flow_from_nlattrs(const struct nlattr *, uint32_t,
- struct flow *);
+ struct flow *, bool);
/* A set of datapath actions within a "struct dp_netdev_flow".
*
@@ -2093,7 +2094,7 @@ dp_netdev_pmd_find_flow(const struct dp_netdev_pmd_thread *pmd,
/* If a UFID is not provided, determine one based on the key. */
if (!ufidp && key && key_len
- && !dpif_netdev_flow_from_nlattrs(key, key_len, &flow)) {
+ && !dpif_netdev_flow_from_nlattrs(key, key_len, &flow, false)) {
dpif_flow_hash(pmd->dp->dpif, &flow, sizeof flow, &ufid);
ufidp = &ufid;
}
@@ -2186,27 +2187,29 @@ static int
dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
const struct nlattr *mask_key,
uint32_t mask_key_len, const struct flow *flow,
- struct flow_wildcards *wc)
+ struct flow_wildcards *wc, bool probe)
{
enum odp_key_fitness fitness;
fitness = odp_flow_key_to_mask(mask_key, mask_key_len, wc, flow);
if (fitness) {
- /* This should not happen: it indicates that
- * odp_flow_key_from_mask() and odp_flow_key_to_mask()
- * disagree on the acceptable form of a mask. Log the problem
- * as an error, with enough details to enable debugging. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- if (!VLOG_DROP_ERR(&rl)) {
- struct ds s;
-
- ds_init(&s);
- odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
- true);
- VLOG_ERR("internal error parsing flow mask %s (%s)",
- ds_cstr(&s), odp_key_fitness_to_string(fitness));
- ds_destroy(&s);
+ if (!probe) {
+ /* This should not happen: it indicates that
+ * odp_flow_key_from_mask() and odp_flow_key_to_mask()
+ * disagree on the acceptable form of a mask. Log the problem
+ * as an error, with enough details to enable debugging. */
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+ if (!VLOG_DROP_ERR(&rl)) {
+ struct ds s;
+
+ ds_init(&s);
+ odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
+ true);
+ VLOG_ERR("internal error parsing flow mask %s (%s)",
+ ds_cstr(&s), odp_key_fitness_to_string(fitness));
+ ds_destroy(&s);
+ }
}
return EINVAL;
@@ -2217,24 +2220,26 @@ dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
static int
dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
- struct flow *flow)
+ struct flow *flow, bool probe)
{
odp_port_t in_port;
if (odp_flow_key_to_flow(key, key_len, flow)) {
- /* This should not happen: it indicates that odp_flow_key_from_flow()
- * and odp_flow_key_to_flow() disagree on the acceptable form of a
- * flow. Log the problem as an error, with enough details to enable
- * debugging. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- if (!VLOG_DROP_ERR(&rl)) {
- struct ds s;
-
- ds_init(&s);
- odp_flow_format(key, key_len, NULL, 0, NULL, &s, true);
- VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s));
- ds_destroy(&s);
+ if (!probe) {
+ /* This should not happen: it indicates that
+ * odp_flow_key_from_flow() and odp_flow_key_to_flow() disagree on
+ * the acceptable form of a flow. Log the problem as an error,
+ * with enough details to enable debugging. */
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+ if (!VLOG_DROP_ERR(&rl)) {
+ struct ds s;
+
+ ds_init(&s);
+ odp_flow_format(key, key_len, NULL, 0, NULL, &s, true);
+ VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s));
+ ds_destroy(&s);
+ }
}
return EINVAL;
@@ -2466,17 +2471,19 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
struct match match;
ovs_u128 ufid;
int error;
+ bool probe = put->flags & DPIF_FP_PROBE;
if (put->stats) {
memset(put->stats, 0, sizeof *put->stats);
}
- error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &match.flow);
+ error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &match.flow,
+ probe);
if (error) {
return error;
}
error = dpif_netdev_mask_from_nlattrs(put->key, put->key_len,
put->mask, put->mask_len,
- &match.flow, &match.wc);
+ &match.flow, &match.wc, probe);
if (error) {
return error;
}
@@ -4596,8 +4603,8 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet,
* VLAN. Unless we refactor a lot of code that translates between
* Netlink and struct flow representations, we have to do the same
* here. */
- if (!match.wc.masks.vlan_tci) {
- match.wc.masks.vlan_tci = htons(0xffff);
+ if (!match.wc.masks.vlans[0].tci) {
+ match.wc.masks.vlans[0].tci = htons(0xffff);
}
/* We can't allow the packet batching in the next loop to execute
diff --git a/lib/flow.c b/lib/flow.c
index b476fce49..f62852665 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -52,6 +52,8 @@ const uint8_t flow_segment_u64s[4] = {
FLOW_U64S
};
+int flow_vlan_limit = FLOW_MAX_VLAN_HEADERS;
+
/* Asserts that field 'f1' follows immediately after 'f0' in struct flow,
* without any intervening padding. */
#define ASSERT_SEQUENTIAL(f0, f1) \
@@ -73,8 +75,6 @@ const uint8_t flow_segment_u64s[4] = {
/* miniflow_extract() assumes the following to be true to optimize the
* extraction process. */
-ASSERT_SEQUENTIAL_SAME_WORD(dl_type, vlan_tci);
-
ASSERT_SEQUENTIAL_SAME_WORD(nw_frag, nw_tos);
ASSERT_SEQUENTIAL_SAME_WORD(nw_tos, nw_ttl);
ASSERT_SEQUENTIAL_SAME_WORD(nw_ttl, nw_proto);
@@ -125,7 +125,7 @@ struct mf_ctx {
* away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are
* defined as macros. */
-#if (FLOW_WC_SEQ != 37)
+#if (FLOW_WC_SEQ != 38)
#define MINIFLOW_ASSERT(X) ovs_assert(X)
BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime "
"assertions enabled. Consider updating FLOW_WC_SEQ after "
@@ -333,26 +333,29 @@ parse_mpls(const void **datap, size_t *sizep)
return MIN(count, FLOW_MAX_MPLS_LABELS);
}
-static inline ALWAYS_INLINE ovs_be16
-parse_vlan(const void **datap, size_t *sizep)
+/* passed vlan_hdrs arg must be at least size FLOW_MAX_VLAN_HEADERS. */
+static inline ALWAYS_INLINE size_t
+parse_vlan(const void **datap, size_t *sizep, union flow_vlan_hdr *vlan_hdrs)
{
- const struct eth_header *eth = *datap;
-
- struct qtag_prefix {
- ovs_be16 eth_type; /* ETH_TYPE_VLAN */
- ovs_be16 tci;
- };
+ const ovs_be16 *eth_type;
+ memset(vlan_hdrs, 0, sizeof(union flow_vlan_hdr) * FLOW_MAX_VLAN_HEADERS);
data_pull(datap, sizep, ETH_ADDR_LEN * 2);
- if (eth->eth_type == htons(ETH_TYPE_VLAN)) {
- if (OVS_LIKELY(*sizep
- >= sizeof(struct qtag_prefix) + sizeof(ovs_be16))) {
- const struct qtag_prefix *qp = data_pull(datap, sizep, sizeof *qp);
- return qp->tci | htons(VLAN_CFI);
+ eth_type = *datap;
+
+ size_t n;
+ for (n = 0; eth_type_vlan(*eth_type) && n < flow_vlan_limit; n++) {
+ if (OVS_UNLIKELY(*sizep < sizeof(ovs_be32) + sizeof(ovs_be16))) {
+ break;
}
+
+ const ovs_16aligned_be32 *qp = data_pull(datap, sizep, sizeof *qp);
+ vlan_hdrs[n].qtag = get_16aligned_be32(qp);
+ vlan_hdrs[n].tci |= htons(VLAN_CFI);
+ eth_type = *datap;
}
- return 0;
+ return n;
}
static inline ALWAYS_INLINE ovs_be16
@@ -630,16 +633,21 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
if (OVS_UNLIKELY(size < sizeof(struct eth_header))) {
goto out;
} else {
- ovs_be16 vlan_tci;
-
/* Link layer. */
ASSERT_SEQUENTIAL(dl_dst, dl_src);
miniflow_push_macs(mf, dl_dst, data);
- /* dl_type, vlan_tci. */
- vlan_tci = parse_vlan(&data, &size);
+
+ /* VLAN */
+ union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS];
+ size_t num_vlans = parse_vlan(&data, &size, vlans);
+
+ /* dl_type */
dl_type = parse_ethertype(&data, &size);
miniflow_push_be16(mf, dl_type, dl_type);
- miniflow_push_be16(mf, vlan_tci, vlan_tci);
+ miniflow_pad_to_64(mf, dl_type);
+ if (num_vlans > 0) {
+ miniflow_push_words_32(mf, vlans, vlans, num_vlans);
+ }
}
/* Parse mpls. */
@@ -880,8 +888,9 @@ ovs_be16
parse_dl_type(const struct eth_header *data_, size_t size)
{
const void *data = data_;
+ union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS];
- parse_vlan(&data, &size);
+ parse_vlan(&data, &size, vlans);
return parse_ethertype(&data, &size);
}
@@ -918,7 +927,7 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata)
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
match_init_catchall(flow_metadata);
if (flow->tunnel.tun_id != htonll(0)) {
@@ -1345,13 +1354,14 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
* the packet headers extracted to 'flow'. It will not set the mask for fields
* that do not make sense for the packet type. OpenFlow-only metadata is
* wildcarded, but other metadata is unconditionally exact-matched. */
-void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
- const struct flow *flow)
+void
+flow_wildcards_init_for_packet(struct flow_wildcards *wc,
+ const struct flow *flow)
{
memset(&wc->masks, 0x0, sizeof wc->masks);
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
if (flow_tnl_dst_is_set(&flow->tunnel)) {
if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
@@ -1402,7 +1412,15 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
WC_MASK_FIELD(wc, dl_dst);
WC_MASK_FIELD(wc, dl_src);
WC_MASK_FIELD(wc, dl_type);
- WC_MASK_FIELD(wc, vlan_tci);
+
+ /* No need to set mask of inner VLANs that don't exist. */
+ for (int i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ /* Always show the first zero VLAN. */
+ WC_MASK_FIELD(wc, vlans[i]);
+ if (flow->vlans[i].tci == htons(0)) {
+ break;
+ }
+ }
if (flow->dl_type == htons(ETH_TYPE_IP)) {
WC_MASK_FIELD(wc, nw_src);
@@ -1478,7 +1496,7 @@ void
flow_wc_map(const struct flow *flow, struct flowmap *map)
{
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
flowmap_init(map);
@@ -1504,7 +1522,7 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
FLOWMAP_SET(map, dl_dst);
FLOWMAP_SET(map, dl_src);
FLOWMAP_SET(map, dl_type);
- FLOWMAP_SET(map, vlan_tci);
+ FLOWMAP_SET(map, vlans);
FLOWMAP_SET(map, ct_state);
FLOWMAP_SET(map, ct_zone);
FLOWMAP_SET(map, ct_mark);
@@ -1572,7 +1590,7 @@ void
flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
{
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
@@ -1716,7 +1734,7 @@ flow_wildcards_set_xxreg_mask(struct flow_wildcards *wc, int idx,
uint32_t
miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
uint32_t hash = basis;
if (flow) {
@@ -1763,7 +1781,7 @@ ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst);
uint32_t
flow_hash_5tuple(const struct flow *flow, uint32_t basis)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
uint32_t hash = basis;
if (flow) {
@@ -1822,7 +1840,9 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
for (i = 0; i < ARRAY_SIZE(fields.eth_addr.be16); i++) {
fields.eth_addr.be16[i] = flow->dl_src.be16[i] ^ flow->dl_dst.be16[i];
}
- fields.vlan_tci = flow->vlan_tci & htons(VLAN_VID_MASK);
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ fields.vlan_tci ^= flow->vlans[i].tci & htons(VLAN_VID_MASK);
+ }
fields.eth_type = flow->dl_type;
/* UDP source and destination port are not taken into account because they
@@ -1888,6 +1908,7 @@ void
flow_random_hash_fields(struct flow *flow)
{
uint16_t rnd = random_uint16();
+ int i;
/* Initialize to all zeros. */
memset(flow, 0, sizeof *flow);
@@ -1895,7 +1916,11 @@ flow_random_hash_fields(struct flow *flow)
eth_addr_random(&flow->dl_src);
eth_addr_random(&flow->dl_dst);
- flow->vlan_tci = (OVS_FORCE ovs_be16) (random_uint16() & VLAN_VID_MASK);
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ uint16_t vlan = random_uint16() & VLAN_VID_MASK;
+ flow->vlans[i].tpid = htons(ETH_TYPE_VLAN_8021Q);
+ flow->vlans[i].tci = htons(vlan | VLAN_CFI);
+ }
/* Make most of the random flows IPv4, some IPv6, and rest random. */
flow->dl_type = rnd < 0x8000 ? htons(ETH_TYPE_IP) :
@@ -1928,6 +1953,7 @@ void
flow_mask_hash_fields(const struct flow *flow, struct flow_wildcards *wc,
enum nx_hash_fields fields)
{
+ int i;
switch (fields) {
case NX_HASH_FIELDS_ETH_SRC:
memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
@@ -1947,7 +1973,9 @@ flow_mask_hash_fields(const struct flow *flow, struct flow_wildcards *wc,
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
flow_unwildcard_tp_ports(flow, wc);
}
- wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ wc->masks.vlans[i].tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+ }
break;
case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP:
@@ -2047,7 +2075,7 @@ flow_hash_in_wildcards(const struct flow *flow,
/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
* OpenFlow 1.0 "dl_vlan" value:
*
- * - If it is in the range 0...4095, 'flow->vlan_tci' is set to match
+ * - If it is in the range 0...4095, 'flow->vlans[0].tci' is set to match
* that VLAN. Any existing PCP match is unchanged (it becomes 0 if
* 'flow' previously matched packets without a VLAN header).
*
@@ -2059,11 +2087,21 @@ void
flow_set_dl_vlan(struct flow *flow, ovs_be16 vid)
{
if (vid == htons(OFP10_VLAN_NONE)) {
- flow->vlan_tci = htons(0);
+ flow->vlans[0].tci = htons(0);
} else {
vid &= htons(VLAN_VID_MASK);
- flow->vlan_tci &= ~htons(VLAN_VID_MASK);
- flow->vlan_tci |= htons(VLAN_CFI) | vid;
+ flow->vlans[0].tci &= ~htons(VLAN_VID_MASK);
+ flow->vlans[0].tci |= htons(VLAN_CFI) | vid;
+ }
+}
+
+/* Sets the VLAN header TPID, which must be either ETH_TYPE_VLAN_8021Q or
+ * ETH_TYPE_VLAN_8021AD. */
+void
+flow_fix_vlan_tpid(struct flow *flow)
+{
+ if (flow->vlans[0].tpid == htons(0) && flow->vlans[0].tci != 0) {
+ flow->vlans[0].tpid = htons(ETH_TYPE_VLAN_8021Q);
}
}
@@ -2074,8 +2112,8 @@ void
flow_set_vlan_vid(struct flow *flow, ovs_be16 vid)
{
ovs_be16 mask = htons(VLAN_VID_MASK | VLAN_CFI);
- flow->vlan_tci &= ~mask;
- flow->vlan_tci |= vid & mask;
+ flow->vlans[0].tci &= ~mask;
+ flow->vlans[0].tci |= vid & mask;
}
/* Sets the VLAN PCP that 'flow' matches to 'pcp', which should be in the
@@ -2089,8 +2127,68 @@ void
flow_set_vlan_pcp(struct flow *flow, uint8_t pcp)
{
pcp &= 0x07;
- flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
- flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
+ flow->vlans[0].tci &= ~htons(VLAN_PCP_MASK);
+ flow->vlans[0].tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
+}
+
+/* Counts the number of VLAN headers. */
+int
+flow_count_vlan_headers(const struct flow *flow)
+{
+ int i;
+
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ if (!(flow->vlans[i].tci & htons(VLAN_CFI))) {
+ break;
+ }
+ }
+ return i;
+}
+
+/* Given '*p_an' and '*p_bn' pointing to one past the last VLAN header of
+ * 'a' and 'b' respectively, skip common VLANs so that they point to the
+ * first different VLAN counting from bottom. */
+void
+flow_skip_common_vlan_headers(const struct flow *a, int *p_an,
+ const struct flow *b, int *p_bn)
+{
+ int an = *p_an, bn = *p_bn;
+
+ for (an--, bn--; an >= 0 && bn >= 0; an--, bn--) {
+ if (a->vlans[an].qtag != b->vlans[bn].qtag) {
+ break;
+ }
+ }
+ *p_an = an;
+ *p_bn = bn;
+}
+
+void
+flow_pop_vlan(struct flow *flow, struct flow_wildcards *wc)
+{
+ int n = flow_count_vlan_headers(flow);
+ if (n == 0) {
+ return;
+ }
+ if (wc) {
+ memset(&wc->masks.vlans[1], 0xff,
+ sizeof(union flow_vlan_hdr) * (n - 1));
+ }
+ memmove(&flow->vlans[0], &flow->vlans[1],
+ sizeof(union flow_vlan_hdr) * (n - 1));
+ memset(&flow->vlans[n - 1], 0, sizeof(union flow_vlan_hdr));
+}
+
+void
+flow_push_vlan_uninit(struct flow *flow, struct flow_wildcards *wc)
+{
+ if (wc) {
+ int n = flow_count_vlan_headers(flow);
+ memset(wc->masks.vlans, 0xff, sizeof(union flow_vlan_hdr) * n);
+ }
+ memmove(&flow->vlans[1], &flow->vlans[0],
+ sizeof(union flow_vlan_hdr) * (FLOW_MAX_VLAN_HEADERS - 1));
+ memset(&flow->vlans[0], 0, sizeof(union flow_vlan_hdr));
}
/* Returns the number of MPLS LSEs present in 'flow'
@@ -2231,7 +2329,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
if (clear_flow_L3) {
/* Clear all L3 and L4 fields and dp_hash. */
- BUILD_ASSERT(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT(FLOW_WC_SEQ == 38);
memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
flow->dp_hash = 0;
@@ -2461,8 +2559,11 @@ flow_compose(struct dp_packet *p, const struct flow *flow)
return;
}
- if (flow->vlan_tci & htons(VLAN_CFI)) {
- eth_push_vlan(p, htons(ETH_TYPE_VLAN), flow->vlan_tci);
+ for (int encaps = FLOW_MAX_VLAN_HEADERS - 1; encaps >= 0; encaps--) {
+ if (flow->vlans[encaps].tci & htons(VLAN_CFI)) {
+ eth_push_vlan(p, flow->vlans[encaps].tpid,
+ flow->vlans[encaps].tci);
+ }
}
if (flow->dl_type == htons(ETH_TYPE_IP)) {
@@ -2783,3 +2884,13 @@ minimask_has_extra(const struct minimask *a, const struct minimask *b)
return false;
}
+
+void
+flow_limit_vlans(int vlan_limit)
+{
+ if (vlan_limit <= 0) {
+ flow_vlan_limit = FLOW_MAX_VLAN_HEADERS;
+ } else {
+ flow_vlan_limit = MIN(vlan_limit, FLOW_MAX_VLAN_HEADERS);
+ }
+}
diff --git a/lib/flow.h b/lib/flow.h
index 14a30041e..86b52584b 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -53,6 +53,9 @@ struct match;
extern const uint8_t flow_segment_u64s[];
+/* Configured maximum VLAN headers. */
+extern int flow_vlan_limit;
+
#define FLOW_U64_OFFSET(FIELD) \
(offsetof(struct flow, FIELD) / sizeof(uint64_t))
#define FLOW_U64_OFFREM(FIELD) \
@@ -87,9 +90,17 @@ static inline bool flow_equal(const struct flow *, const struct flow *);
static inline size_t flow_hash(const struct flow *, uint32_t basis);
void flow_set_dl_vlan(struct flow *, ovs_be16 vid);
+void flow_fix_vlan_tpid(struct flow *);
void flow_set_vlan_vid(struct flow *, ovs_be16 vid);
void flow_set_vlan_pcp(struct flow *, uint8_t pcp);
+void flow_limit_vlans(int vlan_limit);
+int flow_count_vlan_headers(const struct flow *);
+void flow_skip_common_vlan_headers(const struct flow *a, int *p_an,
+ const struct flow *b, int *p_bn);
+void flow_pop_vlan(struct flow*, struct flow_wildcards*);
+void flow_push_vlan_uninit(struct flow*, struct flow_wildcards*);
+
int flow_count_mpls_labels(const struct flow *, struct flow_wildcards *);
int flow_count_common_mpls_labels(const struct flow *a, int an,
const struct flow *b, int bn,
@@ -694,7 +705,7 @@ static inline uint32_t miniflow_get_u32(const struct miniflow *,
unsigned int u32_ofs);
static inline ovs_be32 miniflow_get_be32(const struct miniflow *,
unsigned int be32_ofs);
-static inline uint16_t miniflow_get_vid(const struct miniflow *);
+static inline uint16_t miniflow_get_vid(const struct miniflow *, size_t);
static inline uint16_t miniflow_get_tcp_flags(const struct miniflow *);
static inline ovs_be64 miniflow_get_metadata(const struct miniflow *);
@@ -732,7 +743,7 @@ static inline uint32_t minimask_get_u32(const struct minimask *,
unsigned int u32_ofs);
static inline ovs_be32 minimask_get_be32(const struct minimask *,
unsigned int be32_ofs);
-static inline uint16_t minimask_get_vid_mask(const struct minimask *);
+static inline uint16_t minimask_get_vid_mask(const struct minimask *, size_t);
static inline ovs_be64 minimask_get_metadata_mask(const struct minimask *);
bool minimask_equal(const struct minimask *a, const struct minimask *b);
@@ -779,10 +790,15 @@ static inline ovs_be32 miniflow_get_be32(const struct miniflow *flow,
/* Returns the VID within the vlan_tci member of the "struct flow" represented
* by 'flow'. */
static inline uint16_t
-miniflow_get_vid(const struct miniflow *flow)
+miniflow_get_vid(const struct miniflow *flow, size_t n)
{
- ovs_be16 tci = MINIFLOW_GET_BE16(flow, vlan_tci);
- return vlan_tci_to_vid(tci);
+ if (n < FLOW_MAX_VLAN_HEADERS) {
+ union flow_vlan_hdr hdr = {
+ .qtag = MINIFLOW_GET_BE32(flow, vlans[n])
+ };
+ return vlan_tci_to_vid(hdr.tci);
+ }
+ return 0;
}
/* Returns the uint32_t that would be at byte offset '4 * u32_ofs' if 'mask'
@@ -802,9 +818,9 @@ minimask_get_be32(const struct minimask *mask, unsigned int be32_ofs)
/* Returns the VID mask within the vlan_tci member of the "struct
* flow_wildcards" represented by 'mask'. */
static inline uint16_t
-minimask_get_vid_mask(const struct minimask *mask)
+minimask_get_vid_mask(const struct minimask *mask, size_t n)
{
- return miniflow_get_vid(&mask->masks);
+ return miniflow_get_vid(&mask->masks, n);
}
/* Returns the value of the "tcp_flags" field in 'flow'. */
@@ -889,7 +905,7 @@ static inline void
pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow)
{
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
md->recirc_id = flow->recirc_id;
md->dp_hash = flow->dp_hash;
@@ -938,9 +954,9 @@ static inline bool is_vlan(const struct flow *flow,
struct flow_wildcards *wc)
{
if (wc) {
- WC_MASK_FIELD_MASK(wc, vlan_tci, htons(VLAN_CFI));
+ WC_MASK_FIELD_MASK(wc, vlans[0].tci, htons(VLAN_CFI));
}
- return (flow->vlan_tci & htons(VLAN_CFI)) != 0;
+ return (flow->vlans[0].tci & htons(VLAN_CFI)) != 0;
}
static inline bool is_ip_any(const struct flow *flow)
diff --git a/lib/match.c b/lib/match.c
index 1a5b4bae7..4a8db187b 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -558,8 +558,8 @@ match_set_dl_tci(struct match *match, ovs_be16 tci)
void
match_set_dl_tci_masked(struct match *match, ovs_be16 tci, ovs_be16 mask)
{
- match->flow.vlan_tci = tci & mask;
- match->wc.masks.vlan_tci = mask;
+ match->flow.vlans[0].tci = tci & mask;
+ match->wc.masks.vlans[0].tci = mask;
}
/* Modifies 'match' so that the VLAN VID is wildcarded. If the PCP is already
@@ -568,9 +568,9 @@ match_set_dl_tci_masked(struct match *match, ovs_be16 tci, ovs_be16 mask)
void
match_set_any_vid(struct match *match)
{
- if (match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK)) {
- match->wc.masks.vlan_tci &= ~htons(VLAN_VID_MASK);
- match->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+ if (match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK)) {
+ match->wc.masks.vlans[0].tci &= ~htons(VLAN_VID_MASK);
+ match->flow.vlans[0].tci &= ~htons(VLAN_VID_MASK);
} else {
match_set_dl_tci_masked(match, htons(0), htons(0));
}
@@ -589,9 +589,9 @@ match_set_dl_vlan(struct match *match, ovs_be16 dl_vlan)
{
flow_set_dl_vlan(&match->flow, dl_vlan);
if (dl_vlan == htons(OFP10_VLAN_NONE)) {
- match->wc.masks.vlan_tci = OVS_BE16_MAX;
+ match->wc.masks.vlans[0].tci = OVS_BE16_MAX;
} else {
- match->wc.masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+ match->wc.masks.vlans[0].tci |= htons(VLAN_VID_MASK | VLAN_CFI);
}
}
@@ -616,7 +616,8 @@ match_set_vlan_vid_masked(struct match *match, ovs_be16 vid, ovs_be16 mask)
mask &= vid_mask;
flow_set_vlan_vid(&match->flow, vid & mask);
- match->wc.masks.vlan_tci = mask | (match->wc.masks.vlan_tci & pcp_mask);
+ match->wc.masks.vlans[0].tci =
+ mask | (match->wc.masks.vlans[0].tci & pcp_mask);
}
/* Modifies 'match' so that the VLAN PCP is wildcarded. If the VID is already
@@ -625,9 +626,9 @@ match_set_vlan_vid_masked(struct match *match, ovs_be16 vid, ovs_be16 mask)
void
match_set_any_pcp(struct match *match)
{
- if (match->wc.masks.vlan_tci & htons(VLAN_VID_MASK)) {
- match->wc.masks.vlan_tci &= ~htons(VLAN_PCP_MASK);
- match->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+ if (match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK)) {
+ match->wc.masks.vlans[0].tci &= ~htons(VLAN_PCP_MASK);
+ match->flow.vlans[0].tci &= ~htons(VLAN_PCP_MASK);
} else {
match_set_dl_tci_masked(match, htons(0), htons(0));
}
@@ -639,7 +640,7 @@ void
match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp)
{
flow_set_vlan_pcp(&match->flow, dl_vlan_pcp);
- match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
+ match->wc.masks.vlans[0].tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
}
/* Modifies 'match' so that the MPLS label 'idx' matches 'lse' exactly. */
@@ -1168,7 +1169,7 @@ match_format(const struct match *match, struct ds *s, int priority)
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "%spriority=%s%d,",
@@ -1315,30 +1316,46 @@ match_format(const struct match *match, struct ds *s, int priority)
ofputil_format_port(f->in_port.ofp_port, s);
ds_put_char(s, ',');
}
- if (wc->masks.vlan_tci) {
- ovs_be16 vid_mask = wc->masks.vlan_tci & htons(VLAN_VID_MASK);
- ovs_be16 pcp_mask = wc->masks.vlan_tci & htons(VLAN_PCP_MASK);
- ovs_be16 cfi = wc->masks.vlan_tci & htons(VLAN_CFI);
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ char str_i[8];
- if (cfi && f->vlan_tci & htons(VLAN_CFI)
+ if (!wc->masks.vlans[i].tci) {
+ break;
+ }
+
+ /* Print VLAN tags as dl_vlan, dl_vlan1, dl_vlan2 ... */
+ if (i == 0) {
+ str_i[0] = '\0';
+ } else {
+ snprintf(str_i, sizeof(str_i), "%d", i);
+ }
+ ovs_be16 vid_mask = wc->masks.vlans[i].tci & htons(VLAN_VID_MASK);
+ ovs_be16 pcp_mask = wc->masks.vlans[i].tci & htons(VLAN_PCP_MASK);
+ ovs_be16 cfi = wc->masks.vlans[i].tci & htons(VLAN_CFI);
+
+ if (cfi && f->vlans[i].tci & htons(VLAN_CFI)
&& (!vid_mask || vid_mask == htons(VLAN_VID_MASK))
&& (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK))
&& (vid_mask || pcp_mask)) {
if (vid_mask) {
- ds_put_format(s, "%sdl_vlan=%s%"PRIu16",", colors.param,
- colors.end, vlan_tci_to_vid(f->vlan_tci));
+ ds_put_format(s, "%sdl_vlan%s=%s%"PRIu16",",
+ colors.param, str_i, colors.end,
+ vlan_tci_to_vid(f->vlans[i].tci));
}
if (pcp_mask) {
- ds_put_format(s, "%sdl_vlan_pcp=%s%d,", colors.param,
- colors.end, vlan_tci_to_pcp(f->vlan_tci));
+ ds_put_format(s, "%sdl_vlan_pcp%s=%s%d,",
+ colors.param, str_i, colors.end,
+ vlan_tci_to_pcp(f->vlans[i].tci));
}
- } else if (wc->masks.vlan_tci == htons(0xffff)) {
- ds_put_format(s, "%svlan_tci=%s0x%04"PRIx16",", colors.param,
- colors.end, ntohs(f->vlan_tci));
+ } else if (wc->masks.vlans[i].tci == htons(0xffff)) {
+ ds_put_format(s, "%svlan_tci%s=%s0x%04"PRIx16",",
+ colors.param, str_i, colors.end,
+ ntohs(f->vlans[i].tci));
} else {
- ds_put_format(s, "%svlan_tci=%s0x%04"PRIx16"/0x%04"PRIx16",",
- colors.param, colors.end,
- ntohs(f->vlan_tci), ntohs(wc->masks.vlan_tci));
+ ds_put_format(s, "%svlan_tci%s=%s0x%04"PRIx16"/0x%04"PRIx16",",
+ colors.param, str_i, colors.end,
+ ntohs(f->vlans[i].tci),
+ ntohs(wc->masks.vlans[i].tci));
}
}
format_eth_masked(s, "dl_src", f->dl_src, wc->masks.dl_src);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index a500744ee..93fbc5b37 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -288,14 +288,14 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
return eth_addr_is_zero(wc->masks.arp_tha);
case MFF_VLAN_TCI:
- return !wc->masks.vlan_tci;
+ return !wc->masks.vlans[0].tci;
case MFF_DL_VLAN:
- return !(wc->masks.vlan_tci & htons(VLAN_VID_MASK));
+ return !(wc->masks.vlans[0].tci & htons(VLAN_VID_MASK));
case MFF_VLAN_VID:
- return !(wc->masks.vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI));
+ return !(wc->masks.vlans[0].tci & htons(VLAN_VID_MASK | VLAN_CFI));
case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
- return !(wc->masks.vlan_tci & htons(VLAN_PCP_MASK));
+ return !(wc->masks.vlans[0].tci & htons(VLAN_PCP_MASK));
case MFF_MPLS_LABEL:
return !(wc->masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK));
@@ -732,19 +732,19 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
break;
case MFF_VLAN_TCI:
- value->be16 = flow->vlan_tci;
+ value->be16 = flow->vlans[0].tci;
break;
case MFF_DL_VLAN:
- value->be16 = flow->vlan_tci & htons(VLAN_VID_MASK);
+ value->be16 = flow->vlans[0].tci & htons(VLAN_VID_MASK);
break;
case MFF_VLAN_VID:
- value->be16 = flow->vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI);
+ value->be16 = flow->vlans[0].tci & htons(VLAN_VID_MASK | VLAN_CFI);
break;
case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
- value->u8 = vlan_tci_to_pcp(flow->vlan_tci);
+ value->u8 = vlan_tci_to_pcp(flow->vlans[0].tci);
break;
case MFF_MPLS_LABEL:
@@ -1381,19 +1381,24 @@ mf_set_flow_value(const struct mf_field *mf,
break;
case MFF_VLAN_TCI:
- flow->vlan_tci = value->be16;
+ flow->vlans[0].tci = value->be16;
+ flow_fix_vlan_tpid(flow);
break;
case MFF_DL_VLAN:
flow_set_dl_vlan(flow, value->be16);
+ flow_fix_vlan_tpid(flow);
break;
+
case MFF_VLAN_VID:
flow_set_vlan_vid(flow, value->be16);
+ flow_fix_vlan_tpid(flow);
break;
case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
flow_set_vlan_pcp(flow, value->u8);
+ flow_fix_vlan_tpid(flow);
break;
case MFF_MPLS_LABEL:
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 888591990..8fcae977c 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -986,7 +986,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
/* Metadata. */
if (match->wc.masks.dp_hash) {
@@ -1029,8 +1029,8 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
/* 802.1Q. */
if (oxm) {
ovs_be16 VID_CFI_MASK = htons(VLAN_VID_MASK | VLAN_CFI);
- ovs_be16 vid = flow->vlan_tci & VID_CFI_MASK;
- ovs_be16 mask = match->wc.masks.vlan_tci & VID_CFI_MASK;
+ ovs_be16 vid = flow->vlans[0].tci & VID_CFI_MASK;
+ ovs_be16 mask = match->wc.masks.vlans[0].tci & VID_CFI_MASK;
if (mask == htons(VLAN_VID_MASK | VLAN_CFI)) {
nxm_put_16(b, MFF_VLAN_VID, oxm, vid);
@@ -1038,14 +1038,14 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
nxm_put_16m(b, MFF_VLAN_VID, oxm, vid, mask);
}
- if (vid && vlan_tci_to_pcp(match->wc.masks.vlan_tci)) {
+ if (vid && vlan_tci_to_pcp(match->wc.masks.vlans[0].tci)) {
nxm_put_8(b, MFF_VLAN_PCP, oxm,
- vlan_tci_to_pcp(flow->vlan_tci));
+ vlan_tci_to_pcp(flow->vlans[0].tci));
}
} else {
- nxm_put_16m(b, MFF_VLAN_TCI, oxm, flow->vlan_tci,
- match->wc.masks.vlan_tci);
+ nxm_put_16m(b, MFF_VLAN_TCI, oxm, flow->vlans[0].tci,
+ match->wc.masks.vlans[0].tci);
}
/* MPLS. */
diff --git a/lib/odp-util.c b/lib/odp-util.c
index fcdb2808f..a7b14ed72 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -4423,7 +4423,8 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
bool export_mask, struct ofpbuf *buf)
{
struct ovs_key_ethernet *eth_key;
- size_t encap;
+ size_t encap[FLOW_MAX_VLAN_HEADERS] = {0};
+ size_t max_vlans;
const struct flow *flow = parms->flow;
const struct flow *data = export_mask ? parms->mask : parms->flow;
@@ -4488,19 +4489,33 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
sizeof *eth_key);
get_ethernet_key(data, eth_key);
- if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
+ if (OVS_UNLIKELY(parms->probe)) {
+ max_vlans = FLOW_MAX_VLAN_HEADERS;
+ } else {
+ max_vlans = MIN(parms->support.max_vlan_headers, flow_vlan_limit);
+ }
+ for (int encaps = 0; encaps < max_vlans; encaps++) {
+ ovs_be16 tpid = flow->vlans[encaps].tpid;
+
+ if (flow->vlans[encaps].tci == htons(0)) {
+ if (eth_type_vlan(flow->dl_type)) {
+ /* If VLAN was truncated the tpid is in dl_type */
+ tpid = flow->dl_type;
+ } else {
+ break;
+ }
+ }
+
if (export_mask) {
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
} else {
- nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, tpid);
}
- nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci);
- encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
- if (flow->vlan_tci == htons(0)) {
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlans[encaps].tci);
+ encap[encaps] = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
+ if (flow->vlans[encaps].tci == htons(0)) {
goto unencap;
}
- } else {
- encap = 0;
}
if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
@@ -4523,6 +4538,10 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type);
+ if (eth_type_vlan(flow->dl_type)) {
+ goto unencap;
+ }
+
if (flow->dl_type == htons(ETH_TYPE_IP)) {
struct ovs_key_ipv4 *ipv4_key;
@@ -4616,8 +4635,10 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
}
unencap:
- if (encap) {
- nl_msg_end_nested(buf, encap);
+ for (int encaps = max_vlans - 1; encaps >= 0; encaps--) {
+ if (encap[encaps]) {
+ nl_msg_end_nested(buf, encap[encaps]);
+ }
}
}
@@ -5211,63 +5232,78 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
bool is_mask = src_flow != flow;
- const struct nlattr *encap
- = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)
- ? attrs[OVS_KEY_ATTR_ENCAP] : NULL);
+ const struct nlattr *encap;
enum odp_key_fitness encap_fitness;
- enum odp_key_fitness fitness;
+ enum odp_key_fitness fitness = ODP_FIT_ERROR;
+ int encaps = 0;
- /* Calculate fitness of outer attributes. */
- if (!is_mask) {
- expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
- (UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
- } else {
- if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
- expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
+ while (encaps < flow_vlan_limit &&
+ (is_mask
+ ? (src_flow->vlans[encaps].tci & htons(VLAN_CFI)) != 0
+ : eth_type_vlan(flow->dl_type))) {
+
+ encap = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)
+ ? attrs[OVS_KEY_ATTR_ENCAP] : NULL);
+
+ /* Calculate fitness of outer attributes. */
+ if (!is_mask) {
+ expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
+ (UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
+ } else {
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) {
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP);
+ }
}
- if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) {
- expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP);
+ fitness = check_expectations(present_attrs, out_of_range_attr,
+ expected_attrs, key, key_len);
+
+ /* Set vlan_tci.
+ * Remove the TPID from dl_type since it's not the real Ethertype. */
+ flow->vlans[encaps].tpid = flow->dl_type;
+ flow->dl_type = htons(0);
+ flow->vlans[encaps].tci =
+ (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)
+ ? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN])
+ : htons(0));
+ if (!is_mask) {
+ if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
+ return ODP_FIT_TOO_LITTLE;
+ } else if (flow->vlans[encaps].tci == htons(0)) {
+ /* Corner case for a truncated 802.1Q header. */
+ if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
+ return ODP_FIT_TOO_MUCH;
+ }
+ return fitness;
+ } else if (!(flow->vlans[encaps].tci & htons(VLAN_CFI))) {
+ VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
+ "but CFI bit is not set",
+ ntohs(flow->vlans[encaps].tci));
+ return ODP_FIT_ERROR;
+ }
+ } else {
+ if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) {
+ return fitness;
+ }
}
- }
- fitness = check_expectations(present_attrs, out_of_range_attr,
- expected_attrs, key, key_len);
- /* Set vlan_tci.
- * Remove the TPID from dl_type since it's not the real Ethertype. */
- flow->dl_type = htons(0);
- flow->vlan_tci = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)
- ? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN])
- : htons(0));
- if (!is_mask) {
- if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
- return ODP_FIT_TOO_LITTLE;
- } else if (flow->vlan_tci == htons(0)) {
- /* Corner case for a truncated 802.1Q header. */
- if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
- return ODP_FIT_TOO_MUCH;
- }
- return fitness;
- } else if (!(flow->vlan_tci & htons(VLAN_CFI))) {
- VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
- "but CFI bit is not set", ntohs(flow->vlan_tci));
+ /* Now parse the encapsulated attributes. */
+ if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
+ attrs, &present_attrs, &out_of_range_attr)) {
return ODP_FIT_ERROR;
}
- } else {
- if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) {
- return fitness;
+ expected_attrs = 0;
+
+ if (!parse_ethertype(attrs, present_attrs, &expected_attrs,
+ flow, src_flow)) {
+ return ODP_FIT_ERROR;
}
- }
- /* Now parse the encapsulated attributes. */
- if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
- attrs, &present_attrs, &out_of_range_attr)) {
- return ODP_FIT_ERROR;
+ encaps++;
}
- expected_attrs = 0;
- if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) {
- return ODP_FIT_ERROR;
- }
encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
expected_attrs, flow, key, key_len,
src_flow);
@@ -5399,16 +5435,17 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
}
if (is_mask
- ? (src_flow->vlan_tci & htons(VLAN_CFI)) != 0
- : src_flow->dl_type == htons(ETH_TYPE_VLAN)) {
+ ? (src_flow->vlans[0].tci & htons(VLAN_CFI)) != 0
+ : eth_type_vlan(src_flow->dl_type)) {
return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
expected_attrs, flow, key, key_len, src_flow);
}
if (is_mask) {
/* A missing VLAN mask means exact match on vlan_tci 0 (== no VLAN). */
- flow->vlan_tci = htons(0xffff);
+ flow->vlans[0].tpid = htons(0xffff);
+ flow->vlans[0].tci = htons(0xffff);
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
- flow->vlan_tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
+ flow->vlans[0].tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
}
}
@@ -5659,35 +5696,28 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
}
static void
-pop_vlan(struct flow *base,
- struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+commit_vlan_action(const struct flow* flow, struct flow *base,
+ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
{
- memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+ int base_n = flow_count_vlan_headers(base);
+ int flow_n = flow_count_vlan_headers(flow);
+ flow_skip_common_vlan_headers(base, &base_n, flow, &flow_n);
- if (base->vlan_tci & htons(VLAN_CFI)) {
+ /* Pop all mismatching vlan of base, push those of flow */
+ for (; base_n >= 0; base_n--) {
nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
- base->vlan_tci = 0;
- }
-}
-
-static void
-commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
- struct ofpbuf *odp_actions, struct flow_wildcards *wc)
-{
- if (base->vlan_tci == vlan_tci) {
- return;
+ wc->masks.vlans[base_n].qtag = OVS_BE32_MAX;
}
- pop_vlan(base, odp_actions, wc);
- if (vlan_tci & htons(VLAN_CFI)) {
+ for (; flow_n >= 0; flow_n--) {
struct ovs_action_push_vlan vlan;
- vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
- vlan.vlan_tci = vlan_tci;
+ vlan.vlan_tpid = flow->vlans[flow_n].tpid;
+ vlan.vlan_tci = flow->vlans[flow_n].tci;
nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
&vlan, sizeof vlan);
}
- base->vlan_tci = vlan_tci;
+ memcpy(base->vlans, flow->vlans, sizeof(base->vlans));
}
/* Wildcarding already done at action translation time. */
@@ -6132,7 +6162,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
if (!mpls_done) {
commit_mpls_action(flow, base, odp_actions);
}
- commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
+ commit_vlan_action(flow, base, odp_actions, wc);
commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 2d0081575..50fa1d133 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -143,7 +143,7 @@ void odp_portno_names_destroy(struct hmap *portno_names);
* add another field and forget to adjust this value.
*/
#define ODPUTIL_FLOW_KEY_BYTES 640
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
/* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
* key. An array of "struct nlattr" might not, in theory, be sufficiently
@@ -170,6 +170,8 @@ int odp_flow_from_string(const char *s,
/* Indicates support for various fields. This defines how flows will be
* serialised. */
struct odp_support {
+ /* Maximum number of 802.1q VLAN headers to serialize in a mask. */
+ size_t max_vlan_headers;
/* Maximum number of MPLS label stack entries to serialise in a mask. */
size_t max_mpls_depth;
@@ -204,6 +206,10 @@ struct odp_flow_key_parms {
* then it will always be serialised. */
struct odp_support support;
+ /* Indicates if we are probing datapath capability. If true, ignore the
+ * configured flow limits. */
+ bool probe;
+
/* The netlink formatted version of the flow. It is used in cases where
* the mask cannot be constructed from the OVS internal representation
* and needs to see the original form. */
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index a7ae6f303..4e5cf8561 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -1679,24 +1679,24 @@ decode_OFPAT_RAW11_PUSH_VLAN(ovs_be16 eth_type,
enum ofp_version ofp_version OVS_UNUSED,
struct ofpbuf *out)
{
- if (eth_type != htons(ETH_TYPE_VLAN_8021Q)) {
- /* XXX 802.1AD(QinQ) isn't supported at the moment */
+ struct ofpact_push_vlan *push_vlan;
+ if (!eth_type_vlan(eth_type)) {
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- ofpact_put_PUSH_VLAN(out);
+ push_vlan = ofpact_put_PUSH_VLAN(out);
+ push_vlan->ethertype = eth_type;
return 0;
}
static void
-encode_PUSH_VLAN(const struct ofpact_null *null OVS_UNUSED,
+encode_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan,
enum ofp_version ofp_version, struct ofpbuf *out)
{
if (ofp_version == OFP10_VERSION) {
/* PUSH is a side effect of a SET_VLAN_VID/PCP, which should
* follow this action. */
} else {
- /* XXX ETH_TYPE_VLAN_8021AD case */
- put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q));
+ put_OFPAT11_PUSH_VLAN(out, push_vlan->ethertype);
}
}
@@ -1704,6 +1704,7 @@ static char * OVS_WARN_UNUSED_RESULT
parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
+ struct ofpact_push_vlan *push_vlan;
uint16_t ethertype;
char *error;
@@ -1713,21 +1714,19 @@ parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts,
return error;
}
- if (ethertype != ETH_TYPE_VLAN_8021Q) {
- /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */
+ if (!eth_type_vlan(htons(ethertype))) {
return xasprintf("%s: not a valid VLAN ethertype", arg);
}
-
- ofpact_put_PUSH_VLAN(ofpacts);
+ push_vlan = ofpact_put_PUSH_VLAN(ofpacts);
+ push_vlan->ethertype = htons(ethertype);
return NULL;
}
static void
-format_PUSH_VLAN(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+format_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan, struct ds *s)
{
- /* XXX 802.1AD case*/
ds_put_format(s, "%spush_vlan:%s%#"PRIx16,
- colors.param, colors.end, ETH_TYPE_VLAN_8021Q);
+ colors.param, colors.end, ntohs(push_vlan->ethertype));
}
/* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */
@@ -7322,43 +7321,43 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
/* Remember if we saw a vlan tag in the flow to aid translating to
* OpenFlow 1.1+ if need be. */
ofpact_get_SET_VLAN_VID(a)->flow_has_vlan =
- (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
- if (!(flow->vlan_tci & htons(VLAN_CFI)) &&
+ (flow->vlans[0].tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
+ if (!(flow->vlans[0].tci & htons(VLAN_CFI)) &&
!ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
inconsistent_match(usable_protocols);
}
/* Temporary mark that we have a vlan tag. */
- flow->vlan_tci |= htons(VLAN_CFI);
+ flow->vlans[0].tci |= htons(VLAN_CFI);
return 0;
case OFPACT_SET_VLAN_PCP:
/* Remember if we saw a vlan tag in the flow to aid translating to
* OpenFlow 1.1+ if need be. */
ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan =
- (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
- if (!(flow->vlan_tci & htons(VLAN_CFI)) &&
+ (flow->vlans[0].tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
+ if (!(flow->vlans[0].tci & htons(VLAN_CFI)) &&
!ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
inconsistent_match(usable_protocols);
}
/* Temporary mark that we have a vlan tag. */
- flow->vlan_tci |= htons(VLAN_CFI);
+ flow->vlans[0].tci |= htons(VLAN_CFI);
return 0;
case OFPACT_STRIP_VLAN:
- if (!(flow->vlan_tci & htons(VLAN_CFI))) {
+ if (!(flow->vlans[0].tci & htons(VLAN_CFI))) {
inconsistent_match(usable_protocols);
}
- /* Temporary mark that we have no vlan tag. */
- flow->vlan_tci = htons(0);
+ flow_pop_vlan(flow, NULL);
return 0;
case OFPACT_PUSH_VLAN:
- if (flow->vlan_tci & htons(VLAN_CFI)) {
- /* Multiple VLAN headers not supported. */
+ if (flow->vlans[FLOW_MAX_VLAN_HEADERS - 1].tci & htons(VLAN_CFI)) {
+ /* Support maximum (FLOW_MAX_VLAN_HEADERS) VLAN headers. */
return OFPERR_OFPBAC_BAD_TAG;
}
/* Temporary mark that we have a vlan tag. */
- flow->vlan_tci |= htons(VLAN_CFI);
+ flow_push_vlan_uninit(flow, NULL);
+ flow->vlans[0].tci |= htons(VLAN_CFI);
return 0;
case OFPACT_SET_ETH_SRC:
@@ -7405,7 +7404,8 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
mf = ofpact_get_SET_FIELD(a)->field;
/* Require OXM_OF_VLAN_VID to have an existing VLAN header. */
if (!mf_are_prereqs_ok(mf, flow, NULL) ||
- (mf->id == MFF_VLAN_VID && !(flow->vlan_tci & htons(VLAN_CFI)))) {
+ (mf->id == MFF_VLAN_VID &&
+ !(flow->vlans[0].tci & htons(VLAN_CFI)))) {
VLOG_WARN_RL(&rl, "set_field %s lacks correct prerequisities",
mf->name);
return OFPERR_OFPBAC_MATCH_INCONSISTENT;
@@ -7413,11 +7413,11 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
/* Remember if we saw a vlan tag in the flow to aid translating to
* OpenFlow 1.1 if need be. */
ofpact_get_SET_FIELD(a)->flow_has_vlan =
- (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
+ (flow->vlans[0].tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
if (mf->id == MFF_VLAN_TCI) {
/* The set field may add or remove the vlan tag,
* Mark the status temporarily. */
- flow->vlan_tci = ofpact_get_SET_FIELD(a)->value->be16;
+ flow->vlans[0].tci = ofpact_get_SET_FIELD(a)->value->be16;
}
return 0;
@@ -7599,9 +7599,11 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
{
struct ofpact *a;
ovs_be16 dl_type = match->flow.dl_type;
- ovs_be16 vlan_tci = match->flow.vlan_tci;
uint8_t nw_proto = match->flow.nw_proto;
enum ofperr error = 0;
+ union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS];
+
+ memcpy(&vlans, &match->flow.vlans, sizeof(vlans));
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
error = ofpact_check__(usable_protocols, a, match,
@@ -7612,7 +7614,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
}
/* Restore fields that may have been modified. */
match->flow.dl_type = dl_type;
- match->flow.vlan_tci = vlan_tci;
+ memcpy(&match->flow.vlans, &vlans, sizeof(vlans));
match->flow.nw_proto = nw_proto;
return error;
}
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index b19e975da..02ee95fad 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -101,7 +101,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
@@ -141,10 +141,10 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
/* VLAN TCI mask. */
if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) {
- wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
+ wc->masks.vlans[0].tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
}
if (!(ofpfw & OFPFW10_DL_VLAN)) {
- wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+ wc->masks.vlans[0].tci |= htons(VLAN_VID_MASK | VLAN_CFI);
}
}
@@ -182,8 +182,8 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
* because we can't have a specific PCP without an 802.1Q header.
* However, older versions of OVS treated this as matching packets
* withut an 802.1Q header, so we do here too. */
- match->flow.vlan_tci = htons(0);
- match->wc.masks.vlan_tci = htons(0xffff);
+ match->flow.vlans[0].tci = htons(0);
+ match->wc.masks.vlans[0].tci = htons(0xffff);
} else {
ovs_be16 vid, pcp, tci;
uint16_t hpcp;
@@ -192,7 +192,7 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
hpcp = (ofmatch->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK;
pcp = htons(hpcp);
tci = vid | pcp | htons(VLAN_CFI);
- match->flow.vlan_tci = tci & match->wc.masks.vlan_tci;
+ match->flow.vlans[0].tci = tci & match->wc.masks.vlans[0].tci;
}
/* Clean up. */
@@ -241,22 +241,23 @@ ofputil_match_to_ofp10_match(const struct match *match,
/* Translate VLANs. */
ofmatch->dl_vlan = htons(0);
ofmatch->dl_vlan_pcp = 0;
- if (match->wc.masks.vlan_tci == htons(0)) {
+ if (match->wc.masks.vlans[0].tci == htons(0)) {
ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP;
- } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
- && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
+ } else if (match->wc.masks.vlans[0].tci & htons(VLAN_CFI)
+ && !(match->flow.vlans[0].tci & htons(VLAN_CFI))) {
ofmatch->dl_vlan = htons(OFP10_VLAN_NONE);
} else {
- if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
+ if (!(match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK))) {
ofpfw |= OFPFW10_DL_VLAN;
} else {
- ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci));
+ ofmatch->dl_vlan =
+ htons(vlan_tci_to_vid(match->flow.vlans[0].tci));
}
- if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) {
+ if (!(match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK))) {
ofpfw |= OFPFW10_DL_VLAN_PCP;
} else {
- ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci);
+ ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlans[0].tci);
}
}
@@ -345,17 +346,17 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
if (!(wc & OFPFW11_DL_VLAN)) {
if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) {
/* Match only packets without a VLAN tag. */
- match->flow.vlan_tci = htons(0);
- match->wc.masks.vlan_tci = OVS_BE16_MAX;
+ match->flow.vlans[0].tci = htons(0);
+ match->wc.masks.vlans[0].tci = OVS_BE16_MAX;
} else {
if (ofmatch->dl_vlan == htons(OFPVID11_ANY)) {
/* Match any packet with a VLAN tag regardless of VID. */
- match->flow.vlan_tci = htons(VLAN_CFI);
- match->wc.masks.vlan_tci = htons(VLAN_CFI);
+ match->flow.vlans[0].tci = htons(VLAN_CFI);
+ match->wc.masks.vlans[0].tci = htons(VLAN_CFI);
} else if (ntohs(ofmatch->dl_vlan) < 4096) {
/* Match only packets with the specified VLAN VID. */
- match->flow.vlan_tci = htons(VLAN_CFI) | ofmatch->dl_vlan;
- match->wc.masks.vlan_tci = htons(VLAN_CFI | VLAN_VID_MASK);
+ match->flow.vlans[0].tci = htons(VLAN_CFI) | ofmatch->dl_vlan;
+ match->wc.masks.vlans[0].tci = htons(VLAN_CFI | VLAN_VID_MASK);
} else {
/* Invalid VID. */
return OFPERR_OFPBMC_BAD_VALUE;
@@ -363,9 +364,9 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
if (!(wc & OFPFW11_DL_VLAN_PCP)) {
if (ofmatch->dl_vlan_pcp <= 7) {
- match->flow.vlan_tci |= htons(ofmatch->dl_vlan_pcp
+ match->flow.vlans[0].tci |= htons(ofmatch->dl_vlan_pcp
<< VLAN_PCP_SHIFT);
- match->wc.masks.vlan_tci |= htons(VLAN_PCP_MASK);
+ match->wc.masks.vlans[0].tci |= htons(VLAN_PCP_MASK);
} else {
/* Invalid PCP. */
return OFPERR_OFPBMC_BAD_VALUE;
@@ -483,23 +484,24 @@ ofputil_match_to_ofp11_match(const struct match *match,
ofmatch->dl_dst = match->flow.dl_dst;
ofmatch->dl_dst_mask = eth_addr_invert(match->wc.masks.dl_dst);
- if (match->wc.masks.vlan_tci == htons(0)) {
+ if (match->wc.masks.vlans[0].tci == htons(0)) {
wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP;
- } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
- && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
+ } else if (match->wc.masks.vlans[0].tci & htons(VLAN_CFI)
+ && !(match->flow.vlans[0].tci & htons(VLAN_CFI))) {
ofmatch->dl_vlan = htons(OFPVID11_NONE);
wc |= OFPFW11_DL_VLAN_PCP;
} else {
- if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
+ if (!(match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK))) {
ofmatch->dl_vlan = htons(OFPVID11_ANY);
} else {
- ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci));
+ ofmatch->dl_vlan =
+ htons(vlan_tci_to_vid(match->flow.vlans[0].tci));
}
- if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) {
+ if (!(match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK))) {
wc |= OFPFW11_DL_VLAN_PCP;
} else {
- ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci);
+ ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlans[0].tci);
}
}
diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
index ffa13899a..5f6dc5096 100644
--- a/lib/tnl-ports.c
+++ b/lib/tnl-ports.c
@@ -136,7 +136,7 @@ map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
} else {
match.wc.masks.ipv6_dst = in6addr_exact;
}
- match.wc.masks.vlan_tci = OVS_BE16_MAX;
+ match.wc.masks.vlans[0].tci = OVS_BE16_MAX;
memset(&match.wc.masks.dl_dst, 0xff, sizeof (struct eth_addr));
cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
diff --git a/ofproto/bond.c b/ofproto/bond.c
index ef65a2a3c..17bd3b816 100644
--- a/ofproto/bond.c
+++ b/ofproto/bond.c
@@ -1735,7 +1735,7 @@ static unsigned int
bond_hash_tcp(const struct flow *flow, uint16_t vlan, uint32_t basis)
{
struct flow hash_flow = *flow;
- hash_flow.vlan_tci = htons(vlan);
+ hash_flow.vlans[0].tci = htons(vlan);
/* The symmetric quality of this hash function is not required, but
* flow_hash_symmetric_l4 already exists, and is sufficient for our
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 23a04ce6c..3f90f21c7 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -1581,7 +1581,7 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
/* Choose the right template ID matching the protocols in the
* sampled packet. */
- l2 = (flow->vlan_tci == 0) ? IPFIX_PROTO_L2_ETH : IPFIX_PROTO_L2_VLAN;
+ l2 = (flow->vlans[0].tci == 0) ? IPFIX_PROTO_L2_ETH : IPFIX_PROTO_L2_VLAN;
switch(ntohs(flow->dl_type)) {
case ETH_TYPE_IP:
@@ -1659,8 +1659,8 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
if (l2 == IPFIX_PROTO_L2_VLAN) {
struct ipfix_data_record_flow_key_vlan *data_vlan;
- uint16_t vlan_id = vlan_tci_to_vid(flow->vlan_tci);
- uint8_t priority = vlan_tci_to_pcp(flow->vlan_tci);
+ uint16_t vlan_id = vlan_tci_to_vid(flow->vlans[0].tci);
+ uint8_t priority = vlan_tci_to_pcp(flow->vlans[0].tci);
data_vlan = dp_packet_put_zeros(&msg, sizeof *data_vlan);
data_vlan->vlan_id = htons(vlan_id);
diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h
index dfe54ff32..2c05c3607 100644
--- a/ofproto/ofproto-dpif-rid.h
+++ b/ofproto/ofproto-dpif-rid.h
@@ -99,7 +99,7 @@ struct rule;
/* Metadata for restoring pipeline context after recirculation. Helpers
* are inlined below to keep them together with the definition for easier
* updates. */
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
struct frozen_metadata {
/* Metadata in struct flow. */
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 107397390..59fafa14c 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1301,8 +1301,8 @@ dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet,
/* Add extended switch element. */
memset(&switchElem, 0, sizeof(switchElem));
switchElem.tag = SFLFLOW_EX_SWITCH;
- switchElem.flowType.sw.src_vlan = vlan_tci_to_vid(flow->vlan_tci);
- switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow->vlan_tci);
+ switchElem.flowType.sw.src_vlan = vlan_tci_to_vid(flow->vlans[0].tci);
+ switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow->vlans[0].tci);
/* Retrieve data from user_action_cookie. */
vlan_tci = cookie->sflow.vlan_tci;
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 8f61e43b9..940ed269a 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -379,6 +379,17 @@ struct xlate_ctx {
enum xlate_error error; /* Translation failed. */
};
+/* Structure to track VLAN manipulation */
+struct xvlan_single {
+ uint16_t tpid;
+ uint16_t vid;
+ uint16_t pcp;
+};
+
+struct xvlan {
+ struct xvlan_single v[FLOW_MAX_VLAN_HEADERS];
+};
+
const char *xlate_strerror(enum xlate_error error)
{
switch (error) {
@@ -486,9 +497,18 @@ static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
bool honor_table_miss, bool with_ct_orig);
static bool input_vid_is_valid(const struct xlate_ctx *,
uint16_t vid, struct xbundle *);
-static uint16_t input_vid_to_vlan(const struct xbundle *, uint16_t vid);
+static void xvlan_copy(struct xvlan *dst, const struct xvlan *src);
+static void xvlan_pop(struct xvlan *src);
+static void xvlan_extract(const struct flow *, struct xvlan *);
+static void xvlan_put(struct flow *, const struct xvlan *);
+static void xvlan_input_translate(const struct xbundle *,
+ const struct xvlan *in,
+ struct xvlan *xvlan);
+static void xvlan_output_translate(const struct xbundle *,
+ const struct xvlan *xvlan,
+ struct xvlan *out);
static void output_normal(struct xlate_ctx *, const struct xbundle *,
- uint16_t vlan);
+ const struct xvlan *);
/* Optional bond recirculation parameter to compose_output_action(). */
struct xlate_bond_recirc {
@@ -529,8 +549,10 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
bool forward_bpdu, bool has_in_band,
const struct dpif_backer_support *);
static void xlate_xbundle_set(struct xbundle *xbundle,
- enum port_vlan_mode vlan_mode, int vlan,
- unsigned long *trunks, bool use_priority_tags,
+ enum port_vlan_mode vlan_mode,
+ int vlan,
+ unsigned long *trunks,
+ bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable, bool protected);
static void xlate_xport_set(struct xport *xport, odp_port_t odp_port,
@@ -835,8 +857,9 @@ xlate_xbridge_set(struct xbridge *xbridge,
static void
xlate_xbundle_set(struct xbundle *xbundle,
- enum port_vlan_mode vlan_mode, int vlan,
- unsigned long *trunks, bool use_priority_tags,
+ enum port_vlan_mode vlan_mode,
+ int vlan, unsigned long *trunks,
+ bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable, bool protected)
{
@@ -1133,8 +1156,10 @@ xlate_remove_ofproto(struct ofproto_dpif *ofproto)
void
xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
- const char *name, enum port_vlan_mode vlan_mode, int vlan,
- unsigned long *trunks, bool use_priority_tags,
+ const char *name, enum port_vlan_mode vlan_mode,
+ int vlan,
+ unsigned long *trunks,
+ bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable, bool protected)
{
@@ -1676,9 +1701,20 @@ xbundle_trunks_vlan(const struct xbundle *bundle, uint16_t vlan)
}
static bool
-xbundle_includes_vlan(const struct xbundle *xbundle, uint16_t vlan)
+xbundle_includes_vlan(const struct xbundle *xbundle, const struct xvlan *xvlan)
{
- return vlan == xbundle->vlan || xbundle_trunks_vlan(xbundle, vlan);
+ switch (xbundle->vlan_mode) {
+ case PORT_VLAN_ACCESS:
+ return xvlan->v[0].vid == xbundle->vlan && xvlan->v[1].vid == 0;
+
+ case PORT_VLAN_TRUNK:
+ case PORT_VLAN_NATIVE_UNTAGGED:
+ case PORT_VLAN_NATIVE_TAGGED:
+ return xbundle_trunks_vlan(xbundle, xvlan->v[0].vid);
+
+ default:
+ OVS_NOT_REACHED();
+ }
}
static mirror_mask_t
@@ -1762,13 +1798,16 @@ static void
mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
mirror_mask_t mirrors)
{
+ struct xvlan in_xvlan;
+ struct xvlan xvlan;
+
/* Figure out what VLAN the packet is in (because mirrors can select
* packets on basis of VLAN). */
- uint16_t vid = vlan_tci_to_vid(ctx->xin->flow.vlan_tci);
- if (!input_vid_is_valid(ctx, vid, xbundle)) {
+ xvlan_extract(&ctx->xin->flow, &in_xvlan);
+ if (!input_vid_is_valid(ctx, in_xvlan.v[0].vid, xbundle)) {
return;
}
- uint16_t vlan = input_vid_to_vlan(xbundle, vid);
+ xvlan_input_translate(xbundle, &in_xvlan, &xvlan);
const struct xbridge *xbridge = ctx->xbridge;
@@ -1810,9 +1849,9 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
/* If this mirror selects on the basis of VLAN, and it does not select
* 'vlan', then discard this mirror and go on to the next one. */
if (vlans) {
- ctx->wc->masks.vlan_tci |= htons(VLAN_CFI | VLAN_VID_MASK);
+ ctx->wc->masks.vlans[0].tci |= htons(VLAN_CFI | VLAN_VID_MASK);
}
- if (vlans && !bitmap_is_set(vlans, vlan)) {
+ if (vlans && !bitmap_is_set(vlans, xvlan.v[0].vid)) {
mirrors = zero_rightmost_1bit(mirrors);
continue;
}
@@ -1829,18 +1868,21 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
struct xbundle *out_xbundle = xbundle_lookup(xcfg, out);
if (out_xbundle) {
- output_normal(ctx, out_xbundle, vlan);
+ output_normal(ctx, out_xbundle, &xvlan);
}
- } else if (vlan != out_vlan
+ } else if (xvlan.v[0].vid != out_vlan
&& !eth_addr_is_reserved(ctx->xin->flow.dl_dst)) {
struct xbundle *xbundle;
+ uint16_t old_vid = xvlan.v[0].vid;
+ xvlan.v[0].vid = out_vlan;
LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
- if (xbundle_includes_vlan(xbundle, out_vlan)
+ if (xbundle_includes_vlan(xbundle, &xvlan)
&& !xbundle_mirror_out(xbridge, xbundle)) {
- output_normal(ctx, xbundle, out_vlan);
+ output_normal(ctx, xbundle, &xvlan);
}
}
+ xvlan.v[0].vid = old_vid;
}
/* output_normal() could have recursively output (to different
@@ -1863,32 +1905,6 @@ mirror_ingress_packet(struct xlate_ctx *ctx)
}
}
-/* Given 'vid', the VID obtained from the 802.1Q header that was received as
- * part of a packet (specify 0 if there was no 802.1Q header), and 'in_xbundle',
- * the bundle on which the packet was received, returns the VLAN to which the
- * packet belongs.
- *
- * Both 'vid' and the return value are in the range 0...4095. */
-static uint16_t
-input_vid_to_vlan(const struct xbundle *in_xbundle, uint16_t vid)
-{
- switch (in_xbundle->vlan_mode) {
- case PORT_VLAN_ACCESS:
- return in_xbundle->vlan;
- break;
-
- case PORT_VLAN_TRUNK:
- return vid;
-
- case PORT_VLAN_NATIVE_UNTAGGED:
- case PORT_VLAN_NATIVE_TAGGED:
- return vid ? vid : in_xbundle->vlan;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
/* Checks whether a packet with the given 'vid' may ingress on 'in_xbundle'.
* If so, returns true. Otherwise, returns false.
*
@@ -1923,7 +1939,7 @@ input_vid_is_valid(const struct xlate_ctx *ctx,
}
/* Fall through. */
case PORT_VLAN_TRUNK:
- if (!xbundle_includes_vlan(in_xbundle, vid)) {
+ if (!xbundle_trunks_vlan(in_xbundle, vid)) {
xlate_report_error(ctx, "dropping VLAN %"PRIu16" packet "
"received on port %s not configured for "
"trunking VLAN %"PRIu16,
@@ -1938,26 +1954,115 @@ input_vid_is_valid(const struct xlate_ctx *ctx,
}
-/* Given 'vlan', the VLAN that a packet belongs to, and
- * 'out_xbundle', a bundle on which the packet is to be output, returns the VID
- * that should be included in the 802.1Q header. (If the return value is 0,
- * then the 802.1Q header should only be included in the packet if there is a
- * nonzero PCP.)
- *
- * Both 'vlan' and the return value are in the range 0...4095. */
-static uint16_t
-output_vlan_to_vid(const struct xbundle *out_xbundle, uint16_t vlan)
+static void
+xvlan_copy(struct xvlan *dst, const struct xvlan *src)
+{
+ *dst = *src;
+}
+
+static void
+xvlan_pop(struct xvlan *src)
+{
+ memmove(&src->v[0], &src->v[1], sizeof(src->v) - sizeof(src->v[0]));
+ memset(&src->v[FLOW_MAX_VLAN_HEADERS - 1], 0,
+ sizeof(src->v[FLOW_MAX_VLAN_HEADERS - 1]));
+}
+
+/* Extract VLAN information (headers) from flow */
+static void
+xvlan_extract(const struct flow *flow, struct xvlan *xvlan)
+{
+ int i;
+ memset(xvlan, 0, sizeof(*xvlan));
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ if (!eth_type_vlan(flow->vlans[i].tpid) ||
+ !(flow->vlans[i].tci & htons(VLAN_CFI))) {
+ break;
+ }
+ xvlan->v[i].tpid = ntohs(flow->vlans[i].tpid);
+ xvlan->v[i].vid = vlan_tci_to_vid(flow->vlans[i].tci);
+ xvlan->v[i].pcp = ntohs(flow->vlans[i].tci) & VLAN_PCP_MASK;
+ }
+}
+
+/* Put VLAN information (headers) to flow */
+static void
+xvlan_put(struct flow *flow, const struct xvlan *xvlan)
+{
+ ovs_be16 tci;
+ int i;
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ tci = htons(xvlan->v[i].vid | (xvlan->v[i].pcp & VLAN_PCP_MASK));
+ if (tci) {
+ tci |= htons(VLAN_CFI);
+ flow->vlans[i].tpid = xvlan->v[i].tpid ?
+ htons(xvlan->v[i].tpid) :
+ htons(ETH_TYPE_VLAN_8021Q);
+ }
+ flow->vlans[i].tci = tci;
+ }
+}
+
+/* Given 'in_xvlan', extracted from the input 802.1Q headers received as part
+ * of a packet, and 'in_xbundle', the bundle on which the packet was received,
+ * returns the VLANs of the packet during bridge internal processing. */
+static void
+xvlan_input_translate(const struct xbundle *in_xbundle,
+ const struct xvlan *in_xvlan, struct xvlan *xvlan)
+{
+
+ switch (in_xbundle->vlan_mode) {
+ case PORT_VLAN_ACCESS:
+ memset(xvlan, 0, sizeof(*xvlan));
+ xvlan->v[0].tpid = in_xvlan->v[0].tpid ? in_xvlan->v[0].tpid :
+ ETH_TYPE_VLAN_8021Q;
+ xvlan->v[0].vid = in_xbundle->vlan;
+ xvlan->v[0].pcp = in_xvlan->v[0].pcp;
+ break;
+
+ case PORT_VLAN_TRUNK:
+ xvlan_copy(xvlan, in_xvlan);
+ break;
+
+ case PORT_VLAN_NATIVE_UNTAGGED:
+ case PORT_VLAN_NATIVE_TAGGED:
+ xvlan_copy(xvlan, in_xvlan);
+ if (!in_xvlan->v[0].vid) {
+ xvlan->v[0].tpid = in_xvlan->v[0].tpid ? in_xvlan->v[0].tpid :
+ ETH_TYPE_VLAN_8021Q;
+ xvlan->v[0].vid = in_xbundle->vlan;
+ xvlan->v[0].pcp = in_xvlan->v[0].pcp;
+ }
+ break;
+
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+/* Given 'xvlan', the VLANs of a packet during internal processing, and
+ * 'out_xbundle', a bundle on which the packet is to be output, returns the
+ * VLANs that should be included in output packet. */
+static void
+xvlan_output_translate(const struct xbundle *out_xbundle,
+ const struct xvlan *xvlan, struct xvlan *out_xvlan)
{
switch (out_xbundle->vlan_mode) {
case PORT_VLAN_ACCESS:
- return 0;
+ memset(out_xvlan, 0, sizeof(*out_xvlan));
+ break;
case PORT_VLAN_TRUNK:
case PORT_VLAN_NATIVE_TAGGED:
- return vlan;
+ xvlan_copy(out_xvlan, xvlan);
+ break;
case PORT_VLAN_NATIVE_UNTAGGED:
- return vlan == out_xbundle->vlan ? 0 : vlan;
+ xvlan_copy(out_xvlan, xvlan);
+ if (xvlan->v[0].vid == out_xbundle->vlan) {
+ xvlan_pop(out_xvlan);
+ }
+ break;
default:
OVS_NOT_REACHED();
@@ -1966,16 +2071,21 @@ output_vlan_to_vid(const struct xbundle *out_xbundle, uint16_t vlan)
static void
output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
- uint16_t vlan)
+ const struct xvlan *xvlan)
{
- ovs_be16 *flow_tci = &ctx->xin->flow.vlan_tci;
uint16_t vid;
- ovs_be16 tci, old_tci;
+ union flow_vlan_hdr old_vlans[FLOW_MAX_VLAN_HEADERS];
struct xport *xport;
struct xlate_bond_recirc xr;
bool use_recirc = false;
+ struct xvlan out_xvlan;
- vid = output_vlan_to_vid(out_xbundle, vlan);
+ xvlan_output_translate(out_xbundle, xvlan, &out_xvlan);
+ if (out_xbundle->use_priority_tags) {
+ out_xvlan.v[0].pcp = ntohs(ctx->xin->flow.vlans[0].tci) &
+ VLAN_PCP_MASK;
+ }
+ vid = out_xvlan.v[0].vid;
if (ovs_list_is_empty(&out_xbundle->xports)) {
/* Partially configured bundle with no slaves. Drop the packet. */
return;
@@ -2038,18 +2148,11 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
}
}
- old_tci = *flow_tci;
- tci = htons(vid);
- if (tci || out_xbundle->use_priority_tags) {
- tci |= *flow_tci & htons(VLAN_PCP_MASK);
- if (tci) {
- tci |= htons(VLAN_CFI);
- }
- }
- *flow_tci = tci;
+ memcpy(&old_vlans, &ctx->xin->flow.vlans, sizeof(old_vlans));
+ xvlan_put(&ctx->xin->flow, &out_xvlan);
compose_output_action(ctx, xport->ofp_port, use_recirc ? &xr : NULL);
- *flow_tci = old_tci;
+ memcpy(&ctx->xin->flow.vlans, &old_vlans, sizeof(old_vlans));
}
/* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
@@ -2323,7 +2426,8 @@ static void
xlate_normal_mcast_send_group(struct xlate_ctx *ctx,
struct mcast_snooping *ms OVS_UNUSED,
struct mcast_group *grp,
- struct xbundle *in_xbundle, uint16_t vlan)
+ struct xbundle *in_xbundle,
+ const struct xvlan *xvlan)
OVS_REQ_RDLOCK(ms->rwlock)
{
struct xlate_cfg *xcfg;
@@ -2335,7 +2439,7 @@ xlate_normal_mcast_send_group(struct xlate_ctx *ctx,
mcast_xbundle = xbundle_lookup(xcfg, b->port);
if (mcast_xbundle && mcast_xbundle != in_xbundle) {
xlate_report(ctx, OFT_DETAIL, "forwarding to mcast group port");
- output_normal(ctx, mcast_xbundle, vlan);
+ output_normal(ctx, mcast_xbundle, xvlan);
} else if (!mcast_xbundle) {
xlate_report(ctx, OFT_WARN,
"mcast group port is unknown, dropping");
@@ -2350,7 +2454,8 @@ xlate_normal_mcast_send_group(struct xlate_ctx *ctx,
static void
xlate_normal_mcast_send_mrouters(struct xlate_ctx *ctx,
struct mcast_snooping *ms,
- struct xbundle *in_xbundle, uint16_t vlan)
+ struct xbundle *in_xbundle,
+ const struct xvlan *xvlan)
OVS_REQ_RDLOCK(ms->rwlock)
{
struct xlate_cfg *xcfg;
@@ -2361,13 +2466,13 @@ xlate_normal_mcast_send_mrouters(struct xlate_ctx *ctx,
LIST_FOR_EACH(mrouter, mrouter_node, &ms->mrouter_lru) {
mcast_xbundle = xbundle_lookup(xcfg, mrouter->port);
if (mcast_xbundle && mcast_xbundle != in_xbundle
- && mrouter->vlan == vlan) {
+ && mrouter->vlan == xvlan->v[0].vid) {
xlate_report(ctx, OFT_DETAIL, "forwarding to mcast router port");
- output_normal(ctx, mcast_xbundle, vlan);
+ output_normal(ctx, mcast_xbundle, xvlan);
} else if (!mcast_xbundle) {
xlate_report(ctx, OFT_WARN,
"mcast router port is unknown, dropping");
- } else if (mrouter->vlan != vlan) {
+ } else if (mrouter->vlan != xvlan->v[0].vid) {
xlate_report(ctx, OFT_DETAIL,
"mcast router is on another vlan, dropping");
} else {
@@ -2381,7 +2486,8 @@ xlate_normal_mcast_send_mrouters(struct xlate_ctx *ctx,
static void
xlate_normal_mcast_send_fports(struct xlate_ctx *ctx,
struct mcast_snooping *ms,
- struct xbundle *in_xbundle, uint16_t vlan)
+ struct xbundle *in_xbundle,
+ const struct xvlan *xvlan)
OVS_REQ_RDLOCK(ms->rwlock)
{
struct xlate_cfg *xcfg;
@@ -2393,7 +2499,7 @@ xlate_normal_mcast_send_fports(struct xlate_ctx *ctx,
mcast_xbundle = xbundle_lookup(xcfg, fport->port);
if (mcast_xbundle && mcast_xbundle != in_xbundle) {
xlate_report(ctx, OFT_DETAIL, "forwarding to mcast flood port");
- output_normal(ctx, mcast_xbundle, vlan);
+ output_normal(ctx, mcast_xbundle, xvlan);
} else if (!mcast_xbundle) {
xlate_report(ctx, OFT_WARN,
"mcast flood port is unknown, dropping");
@@ -2408,7 +2514,8 @@ xlate_normal_mcast_send_fports(struct xlate_ctx *ctx,
static void
xlate_normal_mcast_send_rports(struct xlate_ctx *ctx,
struct mcast_snooping *ms,
- struct xbundle *in_xbundle, uint16_t vlan)
+ struct xbundle *in_xbundle,
+ const struct xvlan *xvlan)
OVS_REQ_RDLOCK(ms->rwlock)
{
struct xlate_cfg *xcfg;
@@ -2421,7 +2528,7 @@ xlate_normal_mcast_send_rports(struct xlate_ctx *ctx,
if (mcast_xbundle && mcast_xbundle != in_xbundle) {
xlate_report(ctx, OFT_DETAIL,
"forwarding report to mcast flagged port");
- output_normal(ctx, mcast_xbundle, vlan);
+ output_normal(ctx, mcast_xbundle, xvlan);
} else if (!mcast_xbundle) {
xlate_report(ctx, OFT_WARN,
"mcast port is unknown, dropping the report");
@@ -2434,16 +2541,16 @@ xlate_normal_mcast_send_rports(struct xlate_ctx *ctx,
static void
xlate_normal_flood(struct xlate_ctx *ctx, struct xbundle *in_xbundle,
- uint16_t vlan)
+ struct xvlan *xvlan)
{
struct xbundle *xbundle;
LIST_FOR_EACH (xbundle, list_node, &ctx->xbridge->xbundles) {
if (xbundle != in_xbundle
- && xbundle_includes_vlan(xbundle, vlan)
+ && xbundle_includes_vlan(xbundle, xvlan)
&& xbundle->floodable
&& !xbundle_mirror_out(ctx->xbridge, xbundle)) {
- output_normal(ctx, xbundle, vlan);
+ output_normal(ctx, xbundle, xvlan);
}
}
ctx->nf_output_iface = NF_OUT_FLOOD;
@@ -2472,12 +2579,13 @@ xlate_normal(struct xlate_ctx *ctx)
struct xport *in_port;
struct mac_entry *mac;
void *mac_port;
+ struct xvlan in_xvlan;
+ struct xvlan xvlan;
uint16_t vlan;
- uint16_t vid;
memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
- wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+ wc->masks.vlans[0].tci |= htons(VLAN_VID_MASK | VLAN_CFI);
in_xbundle = lookup_input_bundle(ctx, flow->in_port.ofp_port, &in_port);
if (!in_xbundle) {
@@ -2486,8 +2594,8 @@ xlate_normal(struct xlate_ctx *ctx)
}
/* Drop malformed frames. */
- if (flow->dl_type == htons(ETH_TYPE_VLAN) &&
- !(flow->vlan_tci & htons(VLAN_CFI))) {
+ if (eth_type_vlan(flow->dl_type) &&
+ !(flow->vlans[0].tci & htons(VLAN_CFI))) {
if (ctx->xin->packet != NULL) {
xlate_report_error(ctx, "dropping packet with partial "
"VLAN tag received on port %s",
@@ -2510,13 +2618,14 @@ xlate_normal(struct xlate_ctx *ctx)
}
/* Check VLAN. */
- vid = vlan_tci_to_vid(flow->vlan_tci);
- if (!input_vid_is_valid(ctx, vid, in_xbundle)) {
+ xvlan_extract(flow, &in_xvlan);
+ if (!input_vid_is_valid(ctx, in_xvlan.v[0].vid, in_xbundle)) {
xlate_report(ctx, OFT_WARN,
"disallowed VLAN VID for this input port, dropping");
return;
}
- vlan = input_vid_to_vlan(in_xbundle, vid);
+ xvlan_input_translate(in_xbundle, &in_xvlan, &xvlan);
+ vlan = xvlan.v[0].vid;
/* Check other admissibility requirements. */
if (in_port && !is_admissible(ctx, in_port, vlan)) {
@@ -2567,7 +2676,7 @@ xlate_normal(struct xlate_ctx *ctx)
if (mcast_snooping_is_membership(flow->tp_src)) {
ovs_rwlock_rdlock(&ms->rwlock);
- xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan);
+ xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, &xvlan);
/* RFC4541: section 2.1.1, item 1: A snooping switch should
* forward IGMP Membership Reports only to those ports where
* multicast routers are attached. Alternatively stated: a
@@ -2576,11 +2685,11 @@ xlate_normal(struct xlate_ctx *ctx)
* An administrative control may be provided to override this
* restriction, allowing the report messages to be flooded to
* other ports. */
- xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, vlan);
+ xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, &xvlan);
ovs_rwlock_unlock(&ms->rwlock);
} else {
xlate_report(ctx, OFT_DETAIL, "multicast traffic, flooding");
- xlate_normal_flood(ctx, in_xbundle, vlan);
+ xlate_normal_flood(ctx, in_xbundle, &xvlan);
}
return;
} else if (is_mld(flow, wc)) {
@@ -2591,12 +2700,12 @@ xlate_normal(struct xlate_ctx *ctx)
}
if (is_mld_report(flow, wc)) {
ovs_rwlock_rdlock(&ms->rwlock);
- xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan);
- xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, vlan);
+ xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, &xvlan);
+ xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, &xvlan);
ovs_rwlock_unlock(&ms->rwlock);
} else {
xlate_report(ctx, OFT_DETAIL, "MLD query, flooding");
- xlate_normal_flood(ctx, in_xbundle, vlan);
+ xlate_normal_flood(ctx, in_xbundle, &xvlan);
}
} else {
if (is_ip_local_multicast(flow, wc)) {
@@ -2605,7 +2714,7 @@ xlate_normal(struct xlate_ctx *ctx)
* be forwarded on all ports */
xlate_report(ctx, OFT_DETAIL,
"RFC4541: section 2.1.2, item 2, flooding");
- xlate_normal_flood(ctx, in_xbundle, vlan);
+ xlate_normal_flood(ctx, in_xbundle, &xvlan);
return;
}
}
@@ -2618,17 +2727,17 @@ xlate_normal(struct xlate_ctx *ctx)
grp = mcast_snooping_lookup(ms, &flow->ipv6_dst, vlan);
}
if (grp) {
- xlate_normal_mcast_send_group(ctx, ms, grp, in_xbundle, vlan);
- xlate_normal_mcast_send_fports(ctx, ms, in_xbundle, vlan);
- xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan);
+ xlate_normal_mcast_send_group(ctx, ms, grp, in_xbundle, &xvlan);
+ xlate_normal_mcast_send_fports(ctx, ms, in_xbundle, &xvlan);
+ xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, &xvlan);
} else {
if (mcast_snooping_flood_unreg(ms)) {
xlate_report(ctx, OFT_DETAIL,
"unregistered multicast, flooding");
- xlate_normal_flood(ctx, in_xbundle, vlan);
+ xlate_normal_flood(ctx, in_xbundle, &xvlan);
} else {
- xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan);
- xlate_normal_mcast_send_fports(ctx, ms, in_xbundle, vlan);
+ xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, &xvlan);
+ xlate_normal_mcast_send_fports(ctx, ms, in_xbundle, &xvlan);
}
}
ovs_rwlock_unlock(&ms->rwlock);
@@ -2643,7 +2752,7 @@ xlate_normal(struct xlate_ctx *ctx)
struct xbundle *mac_xbundle = xbundle_lookup(xcfg, mac_port);
if (mac_xbundle && mac_xbundle != in_xbundle) {
xlate_report(ctx, OFT_DETAIL, "forwarding to learned port");
- output_normal(ctx, mac_xbundle, vlan);
+ output_normal(ctx, mac_xbundle, &xvlan);
} else if (!mac_xbundle) {
xlate_report(ctx, OFT_WARN,
"learned port is unknown, dropping");
@@ -2654,7 +2763,7 @@ xlate_normal(struct xlate_ctx *ctx)
} else {
xlate_report(ctx, OFT_DETAIL,
"no learned MAC for destination, flooding");
- xlate_normal_flood(ctx, in_xbundle, vlan);
+ xlate_normal_flood(ctx, in_xbundle, &xvlan);
}
}
}
@@ -2794,7 +2903,7 @@ fix_sflow_action(struct xlate_ctx *ctx, unsigned int user_cookie_offset)
ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
cookie->type = USER_ACTION_COOKIE_SFLOW;
- cookie->sflow.vlan_tci = base->vlan_tci;
+ cookie->sflow.vlan_tci = base->vlans[0].tci;
/* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output
* port information") for the interpretation of cookie->output. */
@@ -3097,7 +3206,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
struct flow_wildcards *wc = ctx->wc;
struct flow *flow = &ctx->xin->flow;
struct flow_tnl flow_tnl;
- ovs_be16 flow_vlan_tci;
+ union flow_vlan_hdr flow_vlans[FLOW_MAX_VLAN_HEADERS];
uint32_t flow_pkt_mark;
uint8_t flow_nw_tos;
odp_port_t out_port, odp_port;
@@ -3106,7 +3215,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
memset(&flow_tnl, 0, sizeof flow_tnl);
if (!xport) {
@@ -3292,7 +3401,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
return;
}
- flow_vlan_tci = flow->vlan_tci;
+ memcpy(flow_vlans, flow->vlans, sizeof flow_vlans);
flow_pkt_mark = flow->pkt_mark;
flow_nw_tos = flow->nw_tos;
@@ -3420,7 +3529,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
out:
/* Restore flow */
- flow->vlan_tci = flow_vlan_tci;
+ memcpy(flow->vlans, flow_vlans, sizeof flow->vlans);
flow->pkt_mark = flow_pkt_mark;
flow->nw_tos = flow_nw_tos;
}
@@ -5426,34 +5535,41 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
break;
case OFPACT_SET_VLAN_VID:
- wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
- if (flow->vlan_tci & htons(VLAN_CFI) ||
+ wc->masks.vlans[0].tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+ if (flow->vlans[0].tci & htons(VLAN_CFI) ||
ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
- flow->vlan_tci &= ~htons(VLAN_VID_MASK);
- flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
- | htons(VLAN_CFI));
+ if (!flow->vlans[0].tpid) {
+ flow->vlans[0].tpid = htons(ETH_TYPE_VLAN);
+ }
+ flow->vlans[0].tci &= ~htons(VLAN_VID_MASK);
+ flow->vlans[0].tci |=
+ (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid) |
+ htons(VLAN_CFI));
}
break;
case OFPACT_SET_VLAN_PCP:
- wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
- if (flow->vlan_tci & htons(VLAN_CFI) ||
+ wc->masks.vlans[0].tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
+ if (flow->vlans[0].tci & htons(VLAN_CFI) ||
ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
- flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
- flow->vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
- << VLAN_PCP_SHIFT) | VLAN_CFI);
+ if (!flow->vlans[0].tpid) {
+ flow->vlans[0].tpid = htons(ETH_TYPE_VLAN);
+ }
+ flow->vlans[0].tci &= ~htons(VLAN_PCP_MASK);
+ flow->vlans[0].tci |=
+ htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
+ << VLAN_PCP_SHIFT) | VLAN_CFI);
}
break;
case OFPACT_STRIP_VLAN:
- memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
- flow->vlan_tci = htons(0);
+ flow_pop_vlan(flow, wc);
break;
case OFPACT_PUSH_VLAN:
- /* XXX 802.1AD(QinQ) */
- memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
- flow->vlan_tci = htons(VLAN_CFI);
+ flow_push_vlan_uninit(flow, wc);
+ flow->vlans[0].tpid = ofpact_get_PUSH_VLAN(a)->ethertype;
+ flow->vlans[0].tci = htons(VLAN_CFI);
break;
case OFPACT_SET_ETH_SRC:
@@ -5933,6 +6049,8 @@ xlate_wc_init(struct xlate_ctx *ctx)
static void
xlate_wc_finish(struct xlate_ctx *ctx)
{
+ int i;
+
/* Clear the metadata and register wildcard masks, because we won't
* use non-header fields as part of the cache. */
flow_wildcards_clear_non_packet_fields(ctx->wc);
@@ -5952,8 +6070,10 @@ xlate_wc_finish(struct xlate_ctx *ctx)
ctx->wc->masks.tp_dst &= htons(UINT8_MAX);
}
/* VLAN_TCI CFI bit must be matched if any of the TCI is matched. */
- if (ctx->wc->masks.vlan_tci) {
- ctx->wc->masks.vlan_tci |= htons(VLAN_CFI);
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ if (ctx->wc->masks.vlans[i].tci) {
+ ctx->wc->masks.vlans[i].tci |= htons(VLAN_CFI);
+ }
}
/* The classifier might return masks that match on tp_src and tp_dst even
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 6ad63c947..c722b43ef 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -167,8 +167,10 @@ void xlate_ofproto_set(struct ofproto_dpif *, const char *name, struct dpif *,
void xlate_remove_ofproto(struct ofproto_dpif *);
void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *,
- const char *name, enum port_vlan_mode, int vlan,
- unsigned long *trunks, bool use_priority_tags,
+ const char *name, enum port_vlan_mode,
+ int vlan,
+ unsigned long *trunks,
+ bool use_priority_tags,
const struct bond *, const struct lacp *,
bool floodable, bool protected);
void xlate_bundle_remove(struct ofbundle *);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 513ab7101..1c4c80469 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -449,8 +449,9 @@ type_run(const char *type)
HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
xlate_bundle_set(ofproto, bundle, bundle->name,
- bundle->vlan_mode, bundle->vlan,
- bundle->trunks, bundle->use_priority_tags,
+ bundle->vlan_mode,
+ bundle->vlan, bundle->trunks,
+ bundle->use_priority_tags,
bundle->bond, bundle->lacp,
bundle->floodable, bundle->protected);
}
@@ -947,6 +948,41 @@ check_variable_length_userdata(struct dpif_backer *backer)
}
}
+/* Tests number of 802.1q VLAN headers supported by 'backer''s datapath.
+ *
+ * Returns the number of elements in a struct flow's vlan
+ * if the datapath supports at least that many VLAN headers. */
+static size_t
+check_max_vlan_headers(struct dpif_backer *backer)
+{
+ struct flow flow;
+ struct odp_flow_key_parms odp_parms = {
+ .flow = &flow,
+ .probe = true,
+ };
+ int n;
+
+ memset(&flow, 0, sizeof flow);
+ flow.dl_type = htons(ETH_TYPE_IP);
+ for (n = 0; n < FLOW_MAX_VLAN_HEADERS; n++) {
+ struct odputil_keybuf keybuf;
+ struct ofpbuf key;
+
+ flow_push_vlan_uninit(&flow, NULL);
+ flow.vlans[0].tpid = htons(ETH_TYPE_VLAN);
+ flow.vlans[0].tci = htons(1) | htons(VLAN_CFI);
+
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&odp_parms, &key);
+ if (!dpif_probe_feature(backer->dpif, "VLAN", &key, NULL, NULL)) {
+ break;
+ }
+ }
+
+ VLOG_INFO("%s: VLAN header stack length probed as %d",
+ dpif_name(backer->dpif), n);
+ return n;
+}
/* Tests the MPLS label stack depth supported by 'backer''s datapath.
*
* Returns the number of elements in a struct flow's mpls_lse field
@@ -1254,6 +1290,7 @@ check_support(struct dpif_backer *backer)
/* Actions. */
backer->support.odp.recirc = check_recirc(backer);
+ backer->support.odp.max_vlan_headers = check_max_vlan_headers(backer);
backer->support.odp.max_mpls_depth = check_max_mpls_depth(backer);
backer->support.masked_set_action = check_masked_set_action(backer);
backer->support.trunc = check_trunc_action(backer);
@@ -2833,7 +2870,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
bool need_flush = false;
struct ofport_dpif *port;
struct ofbundle *bundle;
- unsigned long *trunks;
+ unsigned long *trunks = NULL;
int vlan;
size_t i;
bool ok;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 0f4a0d11a..09594f101 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -8610,3 +8610,9 @@ ofproto_unixctl_init(void)
unixctl_command_register("ofproto/list", "", 0, 0,
ofproto_unixctl_list, NULL);
}
+
+void
+ofproto_set_vlan_limit(int vlan_limit)
+{
+ flow_limit_vlans(vlan_limit);
+}
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 97e63e60e..2b36eaf41 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -340,6 +340,7 @@ int ofproto_get_stp_status(struct ofproto *, struct ofproto_stp_status *);
int ofproto_set_rstp(struct ofproto *, const struct ofproto_rstp_settings *);
int ofproto_get_rstp_status(struct ofproto *, struct ofproto_rstp_status *);
+void ofproto_set_vlan_limit(int vlan_limit);
/* Configuration of ports. */
void ofproto_port_unregister(struct ofproto *, ofp_port_t ofp_port);
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 890b47fe0..b34218977 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -153,8 +153,9 @@ pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
arp->ar_tha = eth_addr_zero;
put_16aligned_be32(&arp->ar_tpa, ip_flow->nw_dst);
- if (ip_flow->vlan_tci & htons(VLAN_CFI)) {
- eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q), ip_flow->vlan_tci);
+ if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
+ eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q),
+ ip_flow->vlans[0].tci);
}
/* Compose actions.
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 61a71044d..5738a9657 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -2841,7 +2841,7 @@ ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \
31 6d 00 00 00 00 00 00 00 00 \
"], [0], [dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,metadata=0x5a5a5a5a5a5a5a5a,in_port=1 (via action) data_len=64 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86,tcp_flags=syn tcp_csum:316d
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86,tcp_flags=syn tcp_csum:316d
])
AT_CLEANUP
@@ -2862,7 +2862,7 @@ ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \
31 6d 00 00 00 00 00 00 00 00 \
" 3], [0], [dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,metadata=0x5a5a5a5a5a5a5a5a,in_port=1 (via action) data_len=64 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:316d
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:316d
00000000 82 82 82 82 82 82 80 81-81 81 81 81 81 00 00 50
00000010 08 00 45 00 00 28 00 00-00 00 00 06 32 05 53 53
00000020 53 53 54 54 54 54 00 55-00 56 00 00 00 00 00 00
@@ -2889,7 +2889,7 @@ AT_CHECK([ovs-ofctl ofp-print "
], [0], [dnl
NXT_PACKET_IN2 (xid=0x0): table_id=7 cookie=0xfedcba9876543210 total_len=64 metadata=0x5a5a5a5a5a5a5a5a (via action) data_len=48 buffer=0x00000114
userdata=01.02.03.04.05
-ip,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=0.0.0.0,nw_dst=0.0.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+ip,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=0.0.0.0,nw_dst=0.0.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
])
AT_CLEANUP
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index b626f40c4..691f63777 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -1432,13 +1432,13 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e
+tcp,dl_vlan=15,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e
dnl
OFPT_PACKET_IN (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e
+tcp,dl_vlan=15,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e
dnl
OFPT_PACKET_IN (xid=0x0): total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e
+tcp,dl_vlan=15,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=fin tcp_csum:2e7e
])
dnl Modified VLAN controller action.
@@ -1452,13 +1452,13 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered)
-ip,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64
+ip,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64
dnl
NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered)
-ip,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64
+ip,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64
dnl
NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=38 in_port=1 (via action) data_len=38 (unbuffered)
-ip,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64
+ip,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=16,nw_tos=0,nw_ecn=0,nw_ttl=64
])
dnl Checksum TCP.
@@ -1475,28 +1475,28 @@ NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=54 in_port=1 (via action) data_len
tcp,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d
dnl
NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=58 reg0=0x1,in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d
dnl
NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=58 reg0=0x1,reg1=0x2,in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d
dnl
NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:2e7d
dnl
NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:4880
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:4880
dnl
NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:6082
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=fin tcp_csum:6082
dnl
NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=11,tcp_flags=fin tcp_csum:6035
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=11,tcp_flags=fin tcp_csum:6035
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:5fea
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:5fea
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=58 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=58 (unbuffered)
-tcp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:5fea
+tcp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86,tcp_flags=fin tcp_csum:5fea
])
dnl Checksum UDP.
@@ -1513,28 +1513,28 @@ NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len
udp,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
dnl
NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 reg0=0x1,in_port=1 (via action) data_len=64 (unbuffered)
-udp,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
+udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
dnl
NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 reg0=0x1,reg1=0x2,in_port=1 (via action) data_len=64 (unbuffered)
-udp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
+udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
dnl
NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,in_port=1 (via action) data_len=64 (unbuffered)
-udp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
+udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:1234
dnl
NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,in_port=1 (via action) data_len=64 (unbuffered)
-udp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:2c37
+udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:2c37
dnl
NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=64 (unbuffered)
-udp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:4439
+udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=8,tp_dst=11 udp_csum:4439
dnl
NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=64 (unbuffered)
-udp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=11 udp_csum:43ec
+udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=11 udp_csum:43ec
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=64 (unbuffered)
-udp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
+udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=64 (unbuffered)
-udp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
+udp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
])
dnl Modified ARP controller action.
@@ -1584,28 +1584,28 @@ NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=98 in_port=1 (via action) data_len
sctp,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
dnl
NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=102 reg0=0x1,in_port=1 (via action) data_len=102 (unbuffered)
-sctp,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
dnl
NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=102 reg0=0x1,reg1=0x2,in_port=1 (via action) data_len=102 (unbuffered)
-sctp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
dnl
NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,in_port=1 (via action) data_len=102 (unbuffered)
-sctp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
dnl
NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,in_port=1 (via action) data_len=102 (unbuffered)
-sctp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
dnl
NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=102 (unbuffered)
-sctp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
dnl
NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=102 (unbuffered)
-sctp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=2223 sctp_csum:dd778f5f
+sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=2223 sctp_csum:dd778f5f
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=102 (unbuffered)
-sctp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 sctp_csum:62051f56
+sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 sctp_csum:62051f56
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=102 reg0=0x1,reg1=0x2,reg2=0x3,reg3=0x4,reg4=0x5,tun_id=0x6,in_port=1 (via action) data_len=102 (unbuffered)
-sctp,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 sctp_csum:62051f56
+sctp,dl_vlan=80,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 sctp_csum:62051f56
])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -1793,13 +1793,13 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=42 in_port=1 (via action) data_len=42 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
dnl
NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=42 in_port=1 (via action) data_len=42 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
dnl
NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=42 in_port=1 (via action) data_len=42 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
])
dnl Modified MPLS controller action.
@@ -3303,6 +3303,92 @@ done
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - VLAN depth limit])
+OVS_VSWITCHD_START([dnl
+ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
+ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \
+ add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3
+])
+
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,eth_type=0x8100,vlan_tci=0x0010/0x01ff actions=output:2
+table=0 in_port=1,eth_type=0xabcd,vlan_tci=0x0010/0x01ff actions=output:3
+])
+AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt])
+flow="in_port(1),eth(src=00:11:22:33:44:55,dst=00:22:44:66:88:00),eth_type(0x8100),vlan(vid=16,pcp=0), \
+ encap(eth_type(0x8100),vlan(vid=17,pcp=0),encap(eth_type(0xabcd)))"
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+
+AT_CHECK([ovs-vsctl set Open_vswitch `ovs-vsctl show | head -n1` other_config:vlan-limit=0])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 3
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - Multi-VLAN actions])
+OVS_VSWITCHD_START([dnl
+ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
+ add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2
+])
+AT_CHECK([ovs-vsctl set Open_vswitch `ovs-vsctl show | head -n1` other_config:vlan-limit=0])
+
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,vlan_tci=0x1100/0x1fff actions=pop_vlan,output:2
+table=0 in_port=1,vlan_tci=0x1101/0x1fff actions=push_vlan:0x8100,set_field:0x1201/0x1fff->vlan_tci,output:2
+table=0 in_port=1,vlan_tci=0x1102/0x1fff actions=push_vlan:0x88a8,set_field:0x1202/0x1fff->vlan_tci,output:2
+table=0 in_port=1,vlan_tci=0x1103/0x1fff actions=set_field:0x1203/0x1fff->vlan_tci,output:2
+table=0 in_port=1,vlan_tci=0x1104/0x1fff actions=pop_vlan,goto_table:1
+table=1 vlan_tci=0 actions=output:2
+table=1 vlan_tci=0x1300/0x1fff actions=pop_vlan,output:2
+table=1 vlan_tci=0x1301/0x1fff actions=push_vlan:0x88a8,set_field:0x1401/0x1fff->vlan_tci,output:2
+table=1 vlan_tci=0x1302/0x1fff actions=set_field:0x1402/0x1fff->vlan_tci,output:2
+])
+
+AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt])
+
+check_act() {
+ psd_hdr="in_port(1),eth(src=00:11:22:33:44:55,dst=00:22:44:66:88:00),"
+ AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$psd_hdr$1"], [0], [stdout])
+ AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $2
+])
+}
+
+check_act "eth_type(0x8100),vlan(vid=0x0100,pcp=0),encap(eth_type(0xabcd))" \
+ "pop_vlan,2"
+
+check_act "eth_type(0x8100),vlan(vid=0x0101,pcp=0),encap(eth_type(0xabcd))" \
+ "push_vlan(vid=513,pcp=0),2"
+
+check_act "eth_type(0x8100),vlan(vid=0x0102,pcp=0),encap(eth_type(0xabcd))" \
+ "push_vlan(tpid=0x88a8,vid=514,pcp=0),2"
+
+check_act "eth_type(0x8100),vlan(vid=0x0103,pcp=0),encap(eth_type(0xabcd))" \
+ "pop_vlan,push_vlan(vid=515,pcp=0),2"
+
+check_act "eth_type(0x8100),vlan(vid=0x0104,pcp=0),encap(eth_type(0xabcd))" \
+ "pop_vlan,2"
+
+check_act "eth_type(0x88a8),vlan(vid=0x0104,pcp=0),encap(eth_type(0x8100),\
+vlan(vid=0x0300,pcp=0),encap(eth_type(0xabcd)))" "pop_vlan,pop_vlan,2"
+
+check_act "eth_type(0x88a8),vlan(vid=0x0104,pcp=0),encap(eth_type(0x8100),\
+vlan(vid=0x0301,pcp=0),encap(eth_type(0xabcd)))" \
+ "pop_vlan,push_vlan(tpid=0x88a8,vid=1025,pcp=0),2"
+
+check_act "eth_type(0x88a8),vlan(vid=0x0104,pcp=0),encap(eth_type(0x8100),\
+vlan(vid=0x0302,pcp=0),encap(eth_type(0xabcd)))" \
+ "pop_vlan,pop_vlan,push_vlan(vid=1026,pcp=0),2"
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - MPLS handling])
OVS_VSWITCHD_START([dnl
add-port br0 p1 -- set Interface p1 type=dummy
@@ -3442,21 +3528,21 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
@@ -3464,8 +3550,8 @@ mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,
])
dnl Modified MPLS controller action.
-dnl In this test, the input packet is vlan-tagged, which should be stripped
-dnl before we push the MPLS and VLAN tags.
+dnl In this test, the input packet is vlan-tagged, which should be kept as
+dnl inner vlan.
AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --pidfile 2> ofctl_monitor.log])
for i in 1 2 3; do
@@ -3475,26 +3561,29 @@ OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3])
OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
])
dnl Modified MPLS controller action.
@@ -3510,21 +3599,21 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
@@ -3532,8 +3621,8 @@ mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,
])
dnl Modified MPLS controller action.
-dnl In this test, the input packet is vlan-tagged, which should be stripped
-dnl before we push the MPLS and VLAN tags.
+dnl In this test, the input packet is vlan-tagged, which should be kept as
+dnl inner vlan.
AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --pidfile 2> ofctl_monitor.log])
for i in 1 2 3; do
@@ -3543,26 +3632,29 @@ OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3])
OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
])
dnl Modified MPLS controller action.
@@ -3578,21 +3670,21 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
@@ -3600,8 +3692,8 @@ mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,
])
dnl Modified MPLS controller action.
-dnl In this test, the input packet is vlan-tagged, which should be stripped
-dnl before we push the MPLS and VLAN tags.
+dnl In this test, the input packet is vlan-tagged, which should be kept as
+dnl inner vlan.
AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --pidfile 2> ofctl_monitor.log])
for i in 1 2 3; do
@@ -3611,26 +3703,29 @@ OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3])
OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
])
dnl Modified MPLS controller action.
@@ -3646,21 +3741,21 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
@@ -3668,8 +3763,8 @@ mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,
])
dnl Modified MPLS controller action.
-dnl In this test, the input packet is vlan-tagged, which should be stripped
-dnl before we push the MPLS and VLAN tags.
+dnl In this test, the input packet is vlan-tagged, which should be kept as
+dnl inner vlan.
AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 -m 65534 -P standard --detach --pidfile 2> ofctl_monitor.log])
for i in 1 2 3; do
@@ -3679,31 +3774,34 @@ OVS_WAIT_UNTIL([test `grep OFPT_PACKET_IN ofctl_monitor.log | wc -l` -ge 3])
OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
dnl
-OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+OFPT_PACKET_IN (OF1.2): total_len=66 in_port=1 (via action) data_len=66 (unbuffered)
+mpls,dl_vlan=99,dl_vlan_pcp=1,dl_vlan1=88,dl_vlan_pcp1=7,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63
-00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
-00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
-00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
+00000010 81 00 e0 58 88 47 00 00-a1 40 45 00 00 28 00 00
+00000020 00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030 00 00 00 00 00 00 00 00-00 00 50 00 00 00 2e 91
+00000040 00 00
])
dnl Modified MPLS controller action.
-dnl In this test, the input packet is vlan-tagged, which should be stripped
-dnl before we push the MPLS and VLAN tags.
+dnl In this test, the input packet is vlan-tagged, which should be kept as
+dnl inner vlan.
AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P standard --detach --pidfile 2> ofctl_monitor.log])
for i in 1 2 3; do
@@ -3714,21 +3812,21 @@ OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
@@ -3748,21 +3846,21 @@ OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
AT_CHECK([ofctl_strip < ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
00000030 00 00 00 00 00 00 50 00-00 00 2e 91 00 00
dnl
OFPT_PACKET_IN (OF1.2): total_len=62 in_port=1 (via action) data_len=62 (unbuffered)
-mpls,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+mpls,dl_vlan=99,dl_vlan_pcp=1,vlan_tci1=0x0000,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
00000000 50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63
00000010 88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
00000020 f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
diff --git a/tests/test-classifier.c b/tests/test-classifier.c
index f85ea4f24..37baad2a4 100644
--- a/tests/test-classifier.c
+++ b/tests/test-classifier.c
@@ -58,7 +58,7 @@ static bool versioned = false;
CLS_FIELD(nw_src, NW_SRC) \
CLS_FIELD(nw_dst, NW_DST) \
CLS_FIELD(in_port.ofp_port, IN_PORT) \
- CLS_FIELD(vlan_tci, VLAN_TCI) \
+ CLS_FIELD(vlans[0].tci, VLAN_TCI) \
CLS_FIELD(dl_type, DL_TYPE) \
CLS_FIELD(tp_src, TP_SRC) \
CLS_FIELD(tp_dst, TP_DST) \
@@ -231,8 +231,8 @@ match(const struct cls_rule *wild_, const struct flow *fixed)
eq = eth_addr_equal_except(fixed->dl_dst, wild.flow.dl_dst,
wild.wc.masks.dl_dst);
} else if (f_idx == CLS_F_IDX_VLAN_TCI) {
- eq = !((fixed->vlan_tci ^ wild.flow.vlan_tci)
- & wild.wc.masks.vlan_tci);
+ eq = !((fixed->vlans[0].tci ^ wild.flow.vlans[0].tci)
+ & wild.wc.masks.vlans[0].tci);
} else if (f_idx == CLS_F_IDX_TUN_ID) {
eq = !((fixed->tunnel.tun_id ^ wild.flow.tunnel.tun_id)
& wild.wc.masks.tunnel.tun_id);
@@ -427,7 +427,7 @@ compare_classifiers(struct classifier *cls, size_t n_invisible_rules,
flow.metadata = metadata_values[get_value(&x, N_METADATA_VALUES)];
flow.in_port.ofp_port = in_port_values[get_value(&x,
N_IN_PORT_VALUES)];
- flow.vlan_tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)];
+ flow.vlans[0].tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)];
flow.dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)];
flow.tp_src = tp_src_values[get_value(&x, N_TP_SRC_VALUES)];
flow.tp_dst = tp_dst_values[get_value(&x, N_TP_DST_VALUES)];
@@ -688,7 +688,7 @@ make_rule(int wc_fields, int priority, int value_pat)
} else if (f_idx == CLS_F_IDX_DL_DST) {
WC_MASK_FIELD(&match.wc, dl_dst);
} else if (f_idx == CLS_F_IDX_VLAN_TCI) {
- match.wc.masks.vlan_tci = OVS_BE16_MAX;
+ match.wc.masks.vlans[0].tci = OVS_BE16_MAX;
} else if (f_idx == CLS_F_IDX_TUN_ID) {
match.wc.masks.tunnel.tun_id = OVS_BE64_MAX;
} else if (f_idx == CLS_F_IDX_METADATA) {
@@ -1431,7 +1431,7 @@ benchmark(bool use_wc)
flow->metadata = metadata_values[get_value(&x, N_METADATA_VALUES)];
flow->in_port.ofp_port = in_port_values[get_value(&x,
N_IN_PORT_VALUES)];
- flow->vlan_tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)];
+ flow->vlans[0].tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)];
flow->dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)];
flow->tp_src = tp_src_values[get_value(&x, N_TP_SRC_VALUES)];
flow->tp_dst = tp_dst_values[get_value(&x, N_TP_DST_VALUES)];
@@ -1702,7 +1702,10 @@ test_miniflow(struct ovs_cmdl_context *ctx OVS_UNUSED)
miniflow = miniflow_create(&flow);
/* Check that the flow equals its miniflow. */
- assert(miniflow_get_vid(miniflow) == vlan_tci_to_vid(flow.vlan_tci));
+ for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ assert(miniflow_get_vid(miniflow, i) ==
+ vlan_tci_to_vid(flow.vlans[i].tci));
+ }
for (i = 0; i < FLOW_U64S; i++) {
assert(miniflow_get(miniflow, i) == flow_u64[i]);
}
diff --git a/tests/test-odp.c b/tests/test-odp.c
index 31699a590..db95cfbae 100644
--- a/tests/test-odp.c
+++ b/tests/test-odp.c
@@ -62,6 +62,7 @@ parse_keys(bool wc_keys)
.ct_zone = true,
.ct_mark = true,
.ct_label = true,
+ .max_vlan_headers = SIZE_MAX,
},
};
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index e8dcbd680..f154c5202 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -4141,8 +4141,8 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
enum ofputil_protocol usable_protocols; /* Unused for now. */
match_init_catchall(&match);
- match.flow.vlan_tci = htons(strtoul(ctx->argv[1], NULL, 16));
- match.wc.masks.vlan_tci = htons(strtoul(ctx->argv[2], NULL, 16));
+ match.flow.vlans[0].tci = htons(strtoul(ctx->argv[1], NULL, 16));
+ match.wc.masks.vlans[0].tci = htons(strtoul(ctx->argv[2], NULL, 16));
/* Convert to and from string. */
string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
@@ -4153,8 +4153,8 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
ovs_fatal(0, "%s", error_s);
}
printf("%04"PRIx16"/%04"PRIx16"\n",
- ntohs(fm.match.flow.vlan_tci),
- ntohs(fm.match.wc.masks.vlan_tci));
+ ntohs(fm.match.flow.vlans[0].tci),
+ ntohs(fm.match.wc.masks.vlans[0].tci));
free(string_s);
/* Convert to and from NXM. */
@@ -4168,8 +4168,8 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
printf("%s\n", ofperr_to_string(error));
} else {
printf("%04"PRIx16"/%04"PRIx16"\n",
- ntohs(nxm_match.flow.vlan_tci),
- ntohs(nxm_match.wc.masks.vlan_tci));
+ ntohs(nxm_match.flow.vlans[0].tci),
+ ntohs(nxm_match.wc.masks.vlans[0].tci));
}
free(nxm_s);
ofpbuf_uninit(&nxm);
@@ -4183,14 +4183,15 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
if (error) {
printf("%s\n", ofperr_to_string(error));
} else {
- uint16_t vid = ntohs(nxm_match.flow.vlan_tci) &
+ uint16_t vid = ntohs(nxm_match.flow.vlans[0].tci) &
(VLAN_VID_MASK | VLAN_CFI);
- uint16_t mask = ntohs(nxm_match.wc.masks.vlan_tci) &
+ uint16_t mask = ntohs(nxm_match.wc.masks.vlans[0].tci) &
(VLAN_VID_MASK | VLAN_CFI);
printf("%04"PRIx16"/%04"PRIx16",", vid, mask);
- if (vid && vlan_tci_to_pcp(nxm_match.wc.masks.vlan_tci)) {
- printf("%02"PRIx8"\n", vlan_tci_to_pcp(nxm_match.flow.vlan_tci));
+ if (vid && vlan_tci_to_pcp(nxm_match.wc.masks.vlans[0].tci)) {
+ printf("%02"PRIx8"\n",
+ vlan_tci_to_pcp(nxm_match.flow.vlans[0].tci));
} else {
printf("--\n");
}
@@ -4206,8 +4207,8 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
(of10_raw.wildcards & htonl(OFPFW10_DL_VLAN)) != 0,
of10_raw.dl_vlan_pcp,
(of10_raw.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0,
- ntohs(of10_match.flow.vlan_tci),
- ntohs(of10_match.wc.masks.vlan_tci));
+ ntohs(of10_match.flow.vlans[0].tci),
+ ntohs(of10_match.wc.masks.vlans[0].tci));
/* Convert to and from OpenFlow 1.1. */
ofputil_match_to_ofp11_match(&match, &of11_raw);
@@ -4217,8 +4218,8 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
(of11_raw.wildcards & htonl(OFPFW11_DL_VLAN)) != 0,
of11_raw.dl_vlan_pcp,
(of11_raw.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0,
- ntohs(of11_match.flow.vlan_tci),
- ntohs(of11_match.wc.masks.vlan_tci));
+ ntohs(of11_match.flow.vlans[0].tci),
+ ntohs(of11_match.wc.masks.vlans[0].tci));
}
/* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 2e1001350..f1483112b 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -598,6 +598,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
OFPROTO_FLOW_LIMIT_DEFAULT));
ofproto_set_max_idle(smap_get_int(&ovs_cfg->other_config, "max-idle",
OFPROTO_MAX_IDLE_DEFAULT));
+ ofproto_set_vlan_limit(smap_get_int(&ovs_cfg->other_config, "vlan-limit",
+ LEGACY_MAX_VLAN_HEADERS));
ofproto_set_threads(
smap_get_int(&ovs_cfg->other_config, "n-handler-threads", 0),
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index a91be59b0..464a211ad 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -350,6 +350,29 @@
Defaults to 100 ie. there is (1/100 =) 1% chance of EMC insertion.
</p>
</column>
+
+ <column name="other_config" key="vlan-limit"
+ type='{"type": "integer", "minInteger": 0}'>
+ <p>
+ Limits the number of VLAN headers that can be matched to the
+ specified number. Further VLAN headers will be treated as payload,
+ e.g. a packet with more 802.1q headers will match Ethernet type
+ 0x8100.
+ </p>
+ <p>
+ Value <code>0</code> means unlimited. The actual number of supported
+ VLAN headers is the smallest of <code>vlan-limit</code>, the number
+ of VLANs supported by Open vSwitch userspace (currently 2), and the
+ number supported by the datapath.
+ </p>
+
+ <p>
+ If this value is absent, the default is currently 1. This maintains
+ backward compatibility with controllers that were designed for use
+ with Open vSwitch versions earlier than 2.8, which only supported one
+ VLAN.
+ </p>
+ </column>
</group>
<group title="Status">