[ovs-dev] [RFC PATCH v5] Add support for 802.1ad (QinQ tunneling)
Xiao Liang
shaw.leon at gmail.com
Wed Sep 14 04:34:36 UTC 2016
Flow key handleing changes:
- Add VLAN header array in struct flow, to record multiple 802.1q VLAN
headers.
- Add dpif multi-VLAN capability probing. If datapath supports multi-VLAN,
increase the maximum depth of nested OVS_KEY_ATTR_ENCAP.
Refacter VLAN handling in dpif-xlate:
- Introduce 'xvlan' to track VLAN stack during flow processing.
- Input and output VLAN translation according to the xbundle type.
Push VLAN action support:
- Allow ethertype 0x88a8 in VLAN headers and push_vlan action.
- Support push_vlan on dot1q packets.
Add new port VLAN mode "dot1q-tunnel":
- Example:
ovs-vsctl set Port p1 vlan_mode=dot1q-tunnel tag=100
Pushes another VLAN 100 header on packets (tagged and untagged) on ingress,
and pops it on egress.
- Customer VLAN check:
ovs-vsctl set Port p1 vlan_mode=dot1q-tunnel tag=100 cvlans=10,20
Only customer VLAN of 10 and 20 are allowed.
Use other_config:vlan-limit in table Open_vSwitch to limit maxium VLANs
that can be matched
Add test cases for VLAN depth limit, Multi-VLAN actions and QinQ VLAN handling
Signed-off-by: Xiao Liang <shaw.leon at gmail.com>
---
v2: Add VLAN handling test cases for dot1q-tunnel vlan_mode.
Adjust VLAN+MPLS test cases for multi VLAN.
v3: Set default max_vlan_headers of netdev and test-odp (fix test failure found by Tom).
Fix loop condition when parsing mask in odp-util.c.
Clean up comments.
v4: Rename vlan_hdr to flow_vlan_hdr.
Correct tpid/dl_type handling in odp-util.c
Add parameter to miniflow_get_vid to get inner vid, and add inner VLAN check in test-classifier.c
v5: Squash the commits.
Add global max_vlan_headers and option 'other_config:vlan-limit' to keep compatibility with legacy applications.
Make 'vlans' in 'struct flow' unions.
In match_format, print inner VLAN tags in as dl_vlan1, etc.
Fix some issues with wildcard.
Add tests for VLAN depth limit and multi-VLAN actions.
Some changes and refactors according to Ben and Eric's comments (flow_push/pop_vlan, commit_vlan_action, etc.)
---
include/openvswitch/flow.h | 16 +-
include/openvswitch/ofp-actions.h | 10 +-
include/openvswitch/packets.h | 8 +
lib/dpctl.c | 29 ++-
lib/dpif-netdev.c | 5 +-
lib/flow.c | 200 ++++++++++++++----
lib/flow.h | 34 ++-
lib/match.c | 73 ++++---
lib/meta-flow.c | 23 +-
lib/nx-match.c | 14 +-
lib/odp-util.c | 232 +++++++++++++--------
lib/odp-util.h | 8 +-
lib/ofp-actions.c | 62 +++---
lib/ofp-util.c | 58 +++---
lib/tnl-ports.c | 2 +-
ofproto/bond.c | 2 +-
ofproto/ofproto-dpif-ipfix.c | 6 +-
ofproto/ofproto-dpif-rid.h | 2 +-
ofproto/ofproto-dpif-sflow.c | 4 +-
ofproto/ofproto-dpif-xlate.c | 429 ++++++++++++++++++++++++++------------
ofproto/ofproto-dpif-xlate.h | 6 +-
ofproto/ofproto-dpif.c | 75 ++++++-
ofproto/ofproto.c | 7 +
ofproto/ofproto.h | 9 +-
ovn/controller/pinctrl.c | 5 +-
tests/ofproto-dpif.at | 348 ++++++++++++++++++++-----------
tests/test-classifier.c | 17 +-
tests/test-odp.c | 1 +
utilities/ovs-ofctl.c | 29 +--
vswitchd/bridge.c | 29 ++-
vswitchd/vswitch.ovsschema | 16 +-
vswitchd/vswitch.xml | 48 +++++
32 files changed, 1264 insertions(+), 543 deletions(-)
diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
index df80dfe..fc9e604 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 36
+#define FLOW_WC_SEQ 37
/* Number of Open vSwitch extension 32-bit registers. */
#define FLOW_N_REGS 16
@@ -61,6 +61,13 @@ const char *flow_tun_flag_to_string(uint32_t flags);
/* Maximum number of supported MPLS labels. */
#define FLOW_MAX_MPLS_LABELS 3
+/* Maximum number of supported VLAN headers.
+ * Multiple of 2 to satisfy 64-bit alignment */
+#define FLOW_MAX_VLAN_HEADERS 2
+
+/* Lagacy maximum VLAN headers */
+#define LEGACY_MAX_VLAN_HEADERS 1
+
/*
* A flow in the network.
*
@@ -103,7 +110,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]; /* VLAN headers */
ovs_be32 mpls_lse[ROUND_UP(FLOW_MAX_MPLS_LABELS, 2)]; /* MPLS label stack
(with padding). */
/* L3 (64-bit aligned) */
@@ -135,8 +143,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) + 248
- && FLOW_WC_SEQ == 36);
+ == sizeof(struct flow_tnl) + 256
+ && FLOW_WC_SEQ == 37);
/* 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 6759201..47abc34 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -66,7 +66,7 @@
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") \
@@ -409,6 +409,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 5d97309..f13d634 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 28f2f83..24a92c7 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -1490,6 +1490,7 @@ dpctl_normalize_actions(int argc, const char *argv[],
struct ds s;
int left;
int i, error;
+ int encaps = 0;
ds_init(&s);
@@ -1546,12 +1547,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;
}
@@ -1577,12 +1580,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 ecc7cea..ad4cfb4 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -98,6 +98,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,
@@ -4060,8 +4061,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 ba4f8c7..2e6a1eaee 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -52,6 +52,8 @@ const uint8_t flow_segment_u64s[4] = {
FLOW_U64S
};
+int max_vlan_headers = 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 != 36)
+#if (FLOW_WC_SEQ != 37)
#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 "
@@ -328,26 +328,30 @@ 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)
+static inline ALWAYS_INLINE bool
+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;
- };
+ int encaps;
+ 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;
+
+ for (encaps = 0;
+ eth_type_vlan(*eth_type) && encaps < max_vlan_headers;
+ encaps++) {
+ if (OVS_LIKELY(*sizep >= sizeof(ovs_be32) + sizeof(ovs_be16))) {
+ const ovs_16aligned_be32 *qp = data_pull(datap, sizep, sizeof *qp);
+ vlan_hdrs[encaps].qtag = get_16aligned_be32(qp);
+ vlan_hdrs[encaps].tci |= htons(VLAN_CFI);
+ eth_type = *datap;
+ } else {
+ return false;
}
}
- return 0;
+ return true;
}
static inline ALWAYS_INLINE ovs_be16
@@ -615,16 +619,20 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
if (OVS_UNLIKELY(size < sizeof(struct eth_header))) {
goto out;
} else {
- ovs_be16 vlan_tci;
+ union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS];
/* 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 */
+ if (OVS_UNLIKELY(!parse_vlan(&data, &size, vlans))) {
+ goto out;
+ }
+ /* 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);
+ miniflow_push_words_32(mf, vlans, vlans, FLOW_MAX_VLAN_HEADERS);
}
/* Parse mpls. */
@@ -831,8 +839,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);
}
@@ -869,7 +878,7 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata)
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
match_init_catchall(flow_metadata);
if (flow->tunnel.tun_id != htonll(0)) {
@@ -1275,7 +1284,7 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
memset(&wc->masks, 0x0, sizeof wc->masks);
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
if (flow_tnl_dst_is_set(&flow->tunnel)) {
if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
@@ -1325,7 +1334,16 @@ 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 doesn't exist.
+ * FIXME: set mask of the first zero VLAN? */
+ WC_MASK_FIELD(wc, vlans[0]);
+ for (int i = 1; i < FLOW_MAX_VLAN_HEADERS; i++) {
+ if (flow->vlans[i].tci == htons(0)) {
+ break;
+ }
+ WC_MASK_FIELD(wc, vlans[i]);
+ }
if (flow->dl_type == htons(ETH_TYPE_IP)) {
WC_MASK_FIELD(wc, nw_src);
@@ -1392,7 +1410,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 == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
flowmap_init(map);
@@ -1418,7 +1436,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);
@@ -1476,7 +1494,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 == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
@@ -1620,7 +1638,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 == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
uint32_t hash = basis;
if (flow) {
@@ -1667,7 +1685,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 == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
uint32_t hash = basis;
if (flow) {
@@ -1726,7 +1744,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
@@ -1792,6 +1812,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);
@@ -1799,7 +1820,10 @@ 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++) {
+ flow->vlans[i].tci =
+ (OVS_FORCE ovs_be16) (random_uint16() & VLAN_VID_MASK);
+ }
/* Make most of the random flows IPv4, some IPv6, and rest random. */
flow->dl_type = rnd < 0x8000 ? htons(ETH_TYPE_IP) :
@@ -1832,6 +1856,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);
@@ -1851,7 +1876,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:
@@ -1951,7 +1978,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).
*
@@ -1963,11 +1990,22 @@ 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. If 'force' is false, then set TPID only when it's not
+ * set yet and vlan_tci is not 0. */
+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);
}
}
@@ -1978,8 +2016,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
@@ -1993,8 +2031,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)
+{
+ int n = flow_count_vlan_headers(flow);
+ if (wc) {
+ 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'
@@ -2134,7 +2232,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
/* Clear all L3 and L4 fields and dp_hash. */
- BUILD_ASSERT(FLOW_WC_SEQ == 36);
+ BUILD_ASSERT(FLOW_WC_SEQ == 37);
memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
flow->dp_hash = 0;
@@ -2354,6 +2452,7 @@ flow_compose(struct dp_packet *p, const struct flow *flow)
{
uint32_t pseudo_hdr_csum;
size_t l4_len;
+ int encaps;
/* eth_compose() sets l3 pointer and makes sure it is 32-bit aligned. */
eth_compose(p, flow->dl_dst, flow->dl_src, ntohs(flow->dl_type), 0);
@@ -2363,8 +2462,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 (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)) {
@@ -2685,3 +2787,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) {
+ max_vlan_headers = FLOW_MAX_VLAN_HEADERS;
+ } else if (vlan_limit > 0) {
+ max_vlan_headers = MIN(vlan_limit, FLOW_MAX_VLAN_HEADERS);
+ }
+}
diff --git a/lib/flow.h b/lib/flow.h
index 5b83695..8cd6b47 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 max_vlan_headers;
+
#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,
@@ -671,7 +682,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 *);
@@ -709,7 +720,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);
@@ -756,10 +767,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'
@@ -779,9 +795,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'. */
@@ -858,9 +874,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 d78e6a1..18b6835 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -465,8 +465,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
@@ -475,9 +475,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));
}
@@ -496,9 +496,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);
}
}
@@ -523,7 +523,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
@@ -532,9 +533,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));
}
@@ -546,7 +547,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. */
@@ -1075,7 +1076,7 @@ match_format(const struct match *match, struct ds *s, int priority)
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "%spriority=%s%d,",
@@ -1207,30 +1208,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 d07f927..3ba1573 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -260,14 +260,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));
@@ -641,19 +641,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:
@@ -1234,19 +1234,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 b03ccf2..79099ef 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -917,7 +917,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 == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
/* Metadata. */
if (match->wc.masks.dp_hash) {
@@ -960,8 +960,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);
@@ -969,14 +969,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 6d29b67..a95d1f3 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -4278,7 +4278,9 @@ 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];
+ int encaps = 0;
+ size_t max_vlans;
const struct flow *flow = parms->flow;
const struct flow *data = export_mask ? parms->mask : parms->flow;
@@ -4320,19 +4322,43 @@ 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 = MIN(parms->support.max_vlan_headers,
+ FLOW_MAX_VLAN_HEADERS);
+ } else {
+ max_vlans = MIN(parms->support.max_vlan_headers,
+ max_vlan_headers);
+ }
+ for (encaps = 0; encaps < max_vlans; encaps++) {
+ if (flow->vlans[encaps].tpid != htons(0)) {
+ 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,
+ data->vlans[encaps].tpid);
+ }
+ 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)) {
+ encaps++;
+ goto unencap;
+ }
+ } else {
+ break;
+ }
+ }
+
+ /* If dpif supports less VLAN headers than that in flow, put ETHERTYPE and
+ * stop. */
+ if (encaps < FLOW_MAX_VLAN_HEADERS &&
+ flow->vlans[encaps].tpid != htons(0)) {
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,
+ data->vlans[encaps].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)) {
- goto unencap;
- }
- } else {
- encap = 0;
+ goto unencap;
}
if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
@@ -4355,6 +4381,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;
@@ -4449,8 +4479,8 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
}
unencap:
- if (encap) {
- nl_msg_end_nested(buf, encap);
+ for (encaps--; encaps >= 0; encaps--) {
+ nl_msg_end_nested(buf, encap[encaps]);
}
}
@@ -5017,69 +5047,103 @@ 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;
-
- /* 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);
+ enum odp_key_fitness fitness[FLOW_MAX_VLAN_HEADERS];
+ enum odp_key_fitness max_fitness;
+ int encaps = 0;
+
+ while (encaps < max_vlan_headers &&
+ (is_mask ?
+ (src_flow->vlans[encaps].tci & htons(VLAN_CFI)) :
+ eth_type_vlan(flow->dl_type))) {
+ /* Calculate fitness of outer attributes. */
+ encap = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)
+ ? attrs[OVS_KEY_ATTR_ENCAP] : NULL);
+ /* In case dpif support less VLAN headers, nested VLAN and ENCAP are
+ * not necessary. */
+ if (!is_mask && encaps == 0) {
+ 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[encaps] = 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[encaps] == ODP_FIT_PERFECT &&
+ nl_attr_get_size(encap)) {
+ return ODP_FIT_TOO_MUCH;
+ }
+ return fitness[encaps];
+ } 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[encaps];
+ }
}
- }
- 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;
+ if (is_mask ?
+ eth_type_vlan(src_flow->dl_type) :
+ eth_type_vlan(flow->dl_type)) {
+ 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);
+ }
+ encap_fitness = check_expectations(present_attrs, out_of_range_attr,
+ expected_attrs, key, key_len);
+ } else {
+ encap_fitness = parse_l2_5_onward(attrs, present_attrs,
+ out_of_range_attr, expected_attrs,
+ flow, key, key_len, src_flow);
}
- encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
- expected_attrs, flow, key, key_len,
- src_flow);
/* The overall fitness is the worse of the outer and inner attributes. */
- return MAX(fitness, encap_fitness);
+ max_fitness = encap_fitness;
+ for (encaps--; encaps >= 0; encaps--) {
+ max_fitness = MAX(max_fitness, fitness[encaps]);
+ }
+ return max_fitness;
}
static enum odp_key_fitness
@@ -5190,16 +5254,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);
}
}
@@ -5490,35 +5555,30 @@ 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 flow_n, base_n;
- if (base->vlan_tci & htons(VLAN_CFI)) {
- nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
- base->vlan_tci = 0;
- }
-}
+ base_n = flow_count_vlan_headers(base);
+ flow_n = flow_count_vlan_headers(flow);
+ flow_skip_common_vlan_headers(base, &base_n, flow, &flow_n);
-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;
+ /* Pop all mismatching vlan of base, push thoses of flow */
+ for (; base_n >= 0; base_n--) {
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+ 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. */
@@ -5951,7 +6011,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
commit_set_port_action(flow, base, odp_actions, wc, use_masked);
slow2 = commit_set_icmp_action(flow, base, odp_actions, wc);
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 a41bc76..a0f2361 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -141,7 +141,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 == 36);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
/* 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
@@ -167,6 +167,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;
@@ -198,6 +200,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 22c7b16..a91bb8e 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -1623,24 +1623,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);
}
}
@@ -1648,6 +1648,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;
@@ -1657,21 +1658,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. */
@@ -6870,43 +6869,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:
@@ -6953,7 +6952,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;
@@ -6961,11 +6961,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;
@@ -7126,9 +7126,11 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
{
struct ofpact *a;
ovs_be16 dl_type = flow->dl_type;
- ovs_be16 vlan_tci = flow->vlan_tci;
uint8_t nw_proto = flow->nw_proto;
enum ofperr error = 0;
+ union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS];
+
+ memcpy(&vlans, &flow->vlans, sizeof(vlans));
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
error = ofpact_check__(usable_protocols, a, flow,
@@ -7139,8 +7141,8 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
}
/* Restore fields that may have been modified. */
flow->dl_type = dl_type;
- flow->vlan_tci = vlan_tci;
flow->nw_proto = nw_proto;
+ memcpy(&flow->vlans, &vlans, sizeof(vlans));
return error;
}
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 1fa4998..ad8243f 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 == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
/* 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);
}
}
@@ -344,17 +345,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;
@@ -362,9 +363,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;
@@ -482,23 +483,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 ffa1389..5f6dc50 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 1d0c3ce..d5d1a49 100644
--- a/ofproto/bond.c
+++ b/ofproto/bond.c
@@ -1708,7 +1708,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 abea492..fbb6ada 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -1590,7 +1590,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:
@@ -1666,8 +1666,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 3bca817..f622278 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 == 36);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
struct frozen_metadata {
/* Metadata in struct flow. */
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index c3234ee..0a70719 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1273,8 +1273,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 74e3387..14934d4 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -124,9 +124,13 @@ struct xbundle {
struct lacp *lacp; /* LACP handle or null. */
enum port_vlan_mode vlan_mode; /* VLAN mode. */
+ uint16_t qinq_ethtype; /* Ethertype of dot1q-tunnel interface
+ * either 0x8100 or 0x88a8. */
int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */
unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1.
* NULL if all VLANs are trunked. */
+ unsigned long *cvlans; /* Bitmap of allowed customer vlans,
+ * NULL if all VLANs are allowed */
bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */
bool floodable; /* No port has OFPUTIL_PC_NO_FLOOD set? */
};
@@ -377,6 +381,13 @@ struct xlate_ctx {
enum xlate_error error; /* Translation failed. */
};
+/* Structure to track VLAN manipulation */
+struct xvlan {
+ uint16_t tpid;
+ uint16_t vid;
+ ovs_be16 pcp;
+};
+
const char *xlate_strerror(enum xlate_error error)
{
switch (error) {
@@ -548,9 +559,19 @@ static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
uint8_t table_id, bool may_packet_in,
bool honor_table_miss);
static bool input_vid_is_valid(uint16_t vid, struct xbundle *, bool warn);
-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_copy_pop(struct xvlan *dst, const struct xvlan *src);
+static void xvlan_copy_push_uninit(struct xvlan *dst, const 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 {
@@ -593,8 +614,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,
+ uint16_t qinq_ethtype, int vlan,
+ unsigned long *trunks, unsigned long *cvlans,
+ bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable);
static void xlate_xport_set(struct xport *xport, odp_port_t odp_port,
@@ -742,16 +765,19 @@ 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, uint16_t qinq_ethtype,
+ int vlan, unsigned long *trunks, unsigned long *cvlans,
+ bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable)
{
ovs_assert(xbundle->xbridge);
xbundle->vlan_mode = vlan_mode;
+ xbundle->qinq_ethtype = qinq_ethtype;
xbundle->vlan = vlan;
xbundle->trunks = trunks;
+ xbundle->cvlans = cvlans;
xbundle->use_priority_tags = use_priority_tags;
xbundle->floodable = floodable;
@@ -845,8 +871,8 @@ xlate_xbundle_copy(struct xbridge *xbridge, struct xbundle *xbundle)
new_xbundle->name = xstrdup(xbundle->name);
xlate_xbundle_init(new_xcfg, new_xbundle);
- xlate_xbundle_set(new_xbundle, xbundle->vlan_mode,
- xbundle->vlan, xbundle->trunks,
+ xlate_xbundle_set(new_xbundle, xbundle->vlan_mode, xbundle->qinq_ethtype,
+ xbundle->vlan, xbundle->trunks, xbundle->cvlans,
xbundle->use_priority_tags, xbundle->bond, xbundle->lacp,
xbundle->floodable);
LIST_FOR_EACH (xport, bundle_node, &xbundle->xports) {
@@ -1039,8 +1065,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,
+ uint16_t qinq_ethtype, int vlan,
+ unsigned long *trunks, unsigned long *cvlans,
+ bool use_priority_tags,
const struct bond *bond, const struct lacp *lacp,
bool floodable)
{
@@ -1060,7 +1088,7 @@ xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
free(xbundle->name);
xbundle->name = xstrdup(name);
- xlate_xbundle_set(xbundle, vlan_mode, vlan, trunks,
+ xlate_xbundle_set(xbundle, vlan_mode, qinq_ethtype, vlan, trunks, cvlans,
use_priority_tags, bond, lacp, floodable);
}
@@ -1590,9 +1618,30 @@ xbundle_trunks_vlan(const struct xbundle *bundle, uint16_t vlan)
}
static bool
-xbundle_includes_vlan(const struct xbundle *xbundle, uint16_t vlan)
+xbundle_allows_cvlan(const struct xbundle *bundle, uint16_t vlan)
+{
+ return (!bundle->cvlans || bitmap_is_set(bundle->cvlans, vlan));
+}
+
+static bool
+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[0].vid == xbundle->vlan && xvlan[1].vid == 0;
+
+ case PORT_VLAN_TRUNK:
+ case PORT_VLAN_NATIVE_UNTAGGED:
+ case PORT_VLAN_NATIVE_TAGGED:
+ return xbundle_trunks_vlan(xbundle, xvlan[0].vid);
+
+ case PORT_VLAN_DOT1Q_TUNNEL:
+ return xvlan[0].vid == xbundle->vlan &&
+ xbundle_allows_cvlan(xbundle, xvlan[1].vid);
+
+ default:
+ OVS_NOT_REACHED();
+ }
}
static mirror_mask_t
@@ -1673,11 +1722,13 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
/* Figure out what VLAN the packet is in (because mirrors can select
* packets on basis of VLAN). */
bool warn = ctx->xin->packet != NULL;
- uint16_t vid = vlan_tci_to_vid(ctx->xin->flow.vlan_tci);
- if (!input_vid_is_valid(vid, xbundle, warn)) {
+ struct xvlan in_xvlan[FLOW_MAX_VLAN_HEADERS];
+ struct xvlan xvlan[FLOW_MAX_VLAN_HEADERS];
+ xvlan_extract(&ctx->xin->flow, in_xvlan);
+ if (!input_vid_is_valid(in_xvlan[0].vid, xbundle, warn)) {
return;
}
- uint16_t vlan = input_vid_to_vlan(xbundle, vid);
+ xvlan_input_translate(xbundle, in_xvlan, xvlan);
const struct xbridge *xbridge = ctx->xbridge;
@@ -1719,9 +1770,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[0].vid)) {
mirrors = zero_rightmost_1bit(mirrors);
continue;
}
@@ -1738,18 +1789,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[0].vid != out_vlan
&& !eth_addr_is_reserved(ctx->xin->flow.dl_dst)) {
struct xbundle *xbundle;
+ uint16_t old_vid = xvlan[0].vid;
+ xvlan[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[0].vid = old_vid;
}
/* output_normal() could have recursively output (to different
@@ -1773,32 +1827,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 and, if 'warn' is true, logs
* a warning.
@@ -1836,7 +1864,7 @@ input_vid_is_valid(uint16_t vid, struct xbundle *in_xbundle, bool warn)
}
/* Fall through. */
case PORT_VLAN_TRUNK:
- if (!xbundle_includes_vlan(in_xbundle, vid)) {
+ if (!xbundle_trunks_vlan(in_xbundle, vid)) {
if (warn) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "dropping VLAN %"PRIu16" packet "
@@ -1847,50 +1875,182 @@ input_vid_is_valid(uint16_t vid, struct xbundle *in_xbundle, bool warn)
}
return true;
+ case PORT_VLAN_DOT1Q_TUNNEL:
+ if (!xbundle_allows_cvlan(in_xbundle, vid)) {
+ if (warn) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "dropping VLAN %"PRIu16" packet received "
+ "on port %s not configured for dot1q tunneling"
+ "VLAN %"PRIu16, vid, in_xbundle->name, vid);
+ }
+ return false;
+ }
+ return true;
+
default:
OVS_NOT_REACHED();
}
}
-/* 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)
+{
+ memcpy(dst, src, sizeof(*dst) * FLOW_MAX_VLAN_HEADERS);
+}
+
+static void
+xvlan_copy_pop(struct xvlan *dst, const struct xvlan *src)
+{
+ memcpy(dst, src + 1, sizeof(*dst) * (FLOW_MAX_VLAN_HEADERS - 1));
+ memset(&dst[FLOW_MAX_VLAN_HEADERS - 1], 0, sizeof(*dst));
+}
+
+static void
+xvlan_copy_push_uninit(struct xvlan *dst, const struct xvlan *src)
+{
+ memcpy(dst + 1, src, sizeof(*dst) * (FLOW_MAX_VLAN_HEADERS - 1));
+ memset(&dst[0], 0, sizeof(*dst));
+}
+
+/* Extract VLAN information (headers) from flow */
+static void
+xvlan_extract(const struct flow *flow, struct xvlan *xvlan)
+{
+ int i;
+ memset(xvlan, 0, sizeof(struct xvlan) * FLOW_MAX_VLAN_HEADERS);
+ 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[i].tpid = ntohs(flow->vlans[i].tpid);
+ xvlan[i].vid = vlan_tci_to_vid(flow->vlans[i].tci);
+ xvlan[i].pcp = flow->vlans[i].tci & htons(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[i].vid) | (xvlan[i].pcp & htons(VLAN_PCP_MASK));
+ if (tci) {
+ tci |= htons(VLAN_CFI);
+ flow->vlans[i].tpid = xvlan[i].tpid ?
+ htons(xvlan[i].tpid) : htons(ETH_TYPE_VLAN);
+ }
+ 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) * FLOW_MAX_VLAN_HEADERS);
+ xvlan->tpid = in_xvlan->tpid? in_xvlan->tpid: ETH_TYPE_VLAN;
+ xvlan->vid = in_xbundle->vlan;
+ xvlan->pcp = in_xvlan->pcp;
+ return;
+
+ case PORT_VLAN_TRUNK:
+ xvlan_copy(xvlan, in_xvlan);
+ return;
+
+ case PORT_VLAN_NATIVE_UNTAGGED:
+ case PORT_VLAN_NATIVE_TAGGED:
+ xvlan_copy(xvlan, in_xvlan);
+ if (!in_xvlan->vid) {
+ xvlan->tpid = in_xvlan->tpid? in_xvlan->tpid: ETH_TYPE_VLAN;
+ xvlan->vid = in_xbundle->vlan;
+ xvlan->pcp = in_xvlan->pcp;
+ }
+ return;
+
+ case PORT_VLAN_DOT1Q_TUNNEL:
+ xvlan_copy_push_uninit(xvlan, in_xvlan);
+ xvlan->tpid = in_xbundle->qinq_ethtype;
+ xvlan->vid = in_xbundle->vlan;
+ xvlan->pcp = htons(0);
+ return;
+
+ 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) * FLOW_MAX_VLAN_HEADERS);
+ return;
case PORT_VLAN_TRUNK:
case PORT_VLAN_NATIVE_TAGGED:
- return vlan;
+ xvlan_copy(out_xvlan, xvlan);
+ return;
case PORT_VLAN_NATIVE_UNTAGGED:
- return vlan == out_xbundle->vlan ? 0 : vlan;
+ if (xvlan->vid == out_xbundle->vlan) {
+ xvlan_copy_pop(out_xvlan, xvlan);
+ } else {
+ xvlan_copy(out_xvlan, xvlan);
+ }
+ return;
+
+ case PORT_VLAN_DOT1Q_TUNNEL:
+ xvlan_copy_pop(out_xvlan, xvlan);
+ return;
default:
OVS_NOT_REACHED();
}
}
+/* If output xbundle is dot1q-tunnel, set mask bits of cvlan */
+static void
+check_and_set_cvlan_mask(struct flow_wildcards *wc,
+ const struct xbundle *xbundle)
+{
+ if (xbundle->vlan_mode == PORT_VLAN_DOT1Q_TUNNEL && xbundle->cvlans) {
+ wc->masks.vlans[1].tci = htons(0xffff);
+ }
+}
+
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[FLOW_MAX_VLAN_HEADERS];
+
+ check_and_set_cvlan_mask(ctx->wc, out_xbundle);
- vid = output_vlan_to_vid(out_xbundle, vlan);
+ xvlan_output_translate(out_xbundle, xvlan, out_xvlan);
+ if (out_xbundle->use_priority_tags) {
+ out_xvlan[0].pcp = ctx->xin->flow.vlans[0].tci & htons(VLAN_PCP_MASK);
+ }
+ vid = out_xvlan[0].vid;
if (ovs_list_is_empty(&out_xbundle->xports)) {
/* Partially configured bundle with no slaves. Drop the packet. */
return;
@@ -1945,18 +2105,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
@@ -2296,7 +2449,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;
@@ -2308,7 +2462,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, "forwarding to mcast group port");
- output_normal(ctx, mcast_xbundle, vlan);
+ output_normal(ctx, mcast_xbundle, xvlan);
} else if (!mcast_xbundle) {
xlate_report(ctx, "mcast group port is unknown, dropping");
} else {
@@ -2321,7 +2475,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;
@@ -2333,7 +2488,7 @@ xlate_normal_mcast_send_mrouters(struct xlate_ctx *ctx,
mcast_xbundle = xbundle_lookup(xcfg, mrouter->port);
if (mcast_xbundle && mcast_xbundle != in_xbundle) {
xlate_report(ctx, "forwarding to mcast router port");
- output_normal(ctx, mcast_xbundle, vlan);
+ output_normal(ctx, mcast_xbundle, xvlan);
} else if (!mcast_xbundle) {
xlate_report(ctx, "mcast router port is unknown, dropping");
} else {
@@ -2346,7 +2501,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;
@@ -2358,7 +2514,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, "forwarding to mcast flood port");
- output_normal(ctx, mcast_xbundle, vlan);
+ output_normal(ctx, mcast_xbundle, xvlan);
} else if (!mcast_xbundle) {
xlate_report(ctx, "mcast flood port is unknown, dropping");
} else {
@@ -2371,7 +2527,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;
@@ -2383,7 +2540,7 @@ xlate_normal_mcast_send_rports(struct xlate_ctx *ctx,
mcast_xbundle = xbundle_lookup(xcfg, rport->port);
if (mcast_xbundle && mcast_xbundle != in_xbundle) {
xlate_report(ctx, "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, "mcast port is unknown, dropping the Report");
} else {
@@ -2394,16 +2551,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;
@@ -2432,12 +2589,13 @@ xlate_normal(struct xlate_ctx *ctx)
struct xport *in_port;
struct mac_entry *mac;
void *mac_port;
+ struct xvlan in_xvlan[FLOW_MAX_VLAN_HEADERS];
+ struct xvlan xvlan[FLOW_MAX_VLAN_HEADERS];
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->xbridge, flow->in_port.ofp_port,
ctx->xin->packet != NULL, &in_port);
@@ -2447,8 +2605,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) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "bridge %s: dropping packet with partial "
@@ -2472,12 +2630,14 @@ xlate_normal(struct xlate_ctx *ctx)
}
/* Check VLAN. */
- vid = vlan_tci_to_vid(flow->vlan_tci);
- if (!input_vid_is_valid(vid, in_xbundle, ctx->xin->packet != NULL)) {
+ xvlan_extract(flow, in_xvlan);
+ if (!input_vid_is_valid(in_xvlan[0].vid, in_xbundle,
+ ctx->xin->packet != NULL)) {
xlate_report(ctx, "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[0].vid;
/* Check other admissibility requirements. */
if (in_port && !is_admissible(ctx, in_port, vlan)) {
@@ -2524,7 +2684,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
@@ -2533,11 +2693,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, "multicast traffic, flooding");
- xlate_normal_flood(ctx, in_xbundle, vlan);
+ xlate_normal_flood(ctx, in_xbundle, xvlan);
}
return;
} else if (is_mld(flow, wc)) {
@@ -2548,12 +2708,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, "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)) {
@@ -2561,7 +2721,7 @@ xlate_normal(struct xlate_ctx *ctx)
* address in the 224.0.0.x range which are not IGMP must
* be forwarded on all ports */
xlate_report(ctx, "RFC4541: section 2.1.2, item 2, flooding");
- xlate_normal_flood(ctx, in_xbundle, vlan);
+ xlate_normal_flood(ctx, in_xbundle, xvlan);
return;
}
}
@@ -2574,16 +2734,16 @@ 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, "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);
@@ -2598,7 +2758,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, "forwarding to learned port");
- output_normal(ctx, mac_xbundle, vlan);
+ output_normal(ctx, mac_xbundle, xvlan);
} else if (!mac_xbundle) {
xlate_report(ctx, "learned port is unknown, dropping");
} else {
@@ -2606,7 +2766,7 @@ xlate_normal(struct xlate_ctx *ctx)
}
} else {
xlate_report(ctx, "no learned MAC for destination, flooding");
- xlate_normal_flood(ctx, in_xbundle, vlan);
+ xlate_normal_flood(ctx, in_xbundle, xvlan);
}
}
}
@@ -2735,7 +2895,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. */
@@ -3016,7 +3176,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 == 36);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
memset(&flow_tnl, 0, sizeof flow_tnl);
if (!xport) {
@@ -3151,7 +3311,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
return;
}
- flow_vlan_tci = flow->vlan_tci;
+ flow_vlan_tci = flow->vlans[0].tci;
flow_pkt_mark = flow->pkt_mark;
flow_nw_tos = flow->nw_tos;
@@ -3279,7 +3439,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
out:
/* Restore flow */
- flow->vlan_tci = flow_vlan_tci;
+ flow->vlans[0].tci = flow_vlan_tci;
flow->pkt_mark = flow_pkt_mark;
flow->nw_tos = flow_nw_tos;
}
@@ -4798,34 +4958,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:
@@ -5311,6 +5478,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);
@@ -5330,8 +5499,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);
+ }
}
}
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 7808a60..dac0da5 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -160,8 +160,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,
+ uint16_t qinq_ethtype, int vlan,
+ unsigned long *trunks, unsigned long *cvlans,
+ bool use_priority_tags,
const struct bond *, const struct lacp *,
bool floodable);
void xlate_bundle_remove(struct ofbundle *);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 289e7d6..3a7651c 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -135,9 +135,11 @@ struct ofbundle {
/* Configuration. */
struct ovs_list ports; /* Contains "struct ofport"s. */
enum port_vlan_mode vlan_mode; /* VLAN mode */
+ uint16_t qinq_ethtype;
int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */
unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1.
* NULL if all VLANs are trunked. */
+ unsigned long *cvlans;
struct lacp *lacp; /* LACP if LACP is enabled, otherwise NULL. */
struct bond *bond; /* Nonnull iff more than one port. */
bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */
@@ -618,8 +620,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->qinq_ethtype,
+ bundle->vlan, bundle->trunks, bundle->cvlans,
+ bundle->use_priority_tags,
bundle->bond, bundle->lacp,
bundle->floodable);
}
@@ -1116,6 +1119,44 @@ 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,
+ .support = {
+ .max_vlan_headers = SIZE_MAX
+ },
+ .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)) {
+ break;
+ }
+ }
+
+ VLOG_INFO("%s: VLAN label 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
@@ -1312,6 +1353,7 @@ check_support(struct dpif_backer *backer)
backer->support.variable_length_userdata = false;
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);
@@ -2865,6 +2907,7 @@ bundle_destroy(struct ofbundle *bundle)
hmap_remove(&ofproto->bundles, &bundle->hmap_node);
free(bundle->name);
free(bundle->trunks);
+ free(bundle->cvlans);
lacp_unref(bundle->lacp);
bond_unref(bundle->bond);
free(bundle);
@@ -2878,7 +2921,8 @@ 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;
+ unsigned long *cvlans = NULL;
int vlan;
size_t i;
bool ok;
@@ -2903,8 +2947,10 @@ bundle_set(struct ofproto *ofproto_, void *aux,
ovs_list_init(&bundle->ports);
bundle->vlan_mode = PORT_VLAN_TRUNK;
+ bundle->qinq_ethtype = ETH_TYPE_VLAN_8021AD;
bundle->vlan = -1;
bundle->trunks = NULL;
+ bundle->cvlans = NULL;
bundle->use_priority_tags = s->use_priority_tags;
bundle->lacp = NULL;
bundle->bond = NULL;
@@ -2968,6 +3014,11 @@ bundle_set(struct ofproto *ofproto_, void *aux,
need_flush = true;
}
+ if (s->qinq_ethtype != bundle->qinq_ethtype) {
+ bundle->qinq_ethtype= s->qinq_ethtype;
+ need_flush = true;
+ }
+
/* Set VLAN tag. */
vlan = (s->vlan_mode == PORT_VLAN_TRUNK ? -1
: s->vlan >= 0 && s->vlan <= 4095 ? s->vlan
@@ -3005,6 +3056,10 @@ bundle_set(struct ofproto *ofproto_, void *aux,
}
break;
+ case PORT_VLAN_DOT1Q_TUNNEL:
+ cvlans = CONST_CAST(unsigned long *, s->cvlans);
+ break;
+
default:
OVS_NOT_REACHED();
}
@@ -3022,6 +3077,20 @@ bundle_set(struct ofproto *ofproto_, void *aux,
free(trunks);
}
+ if (!vlan_bitmap_equal(cvlans, bundle->cvlans)) {
+ free(bundle->cvlans);
+ if (cvlans== s->cvlans) {
+ bundle->cvlans= vlan_bitmap_clone(cvlans);
+ } else {
+ bundle->cvlans = cvlans;
+ cvlans = NULL;
+ }
+ need_flush = true;
+ }
+ if (cvlans != s->cvlans) {
+ free(cvlans);
+ }
+
/* Bonding. */
if (!ovs_list_is_short(&bundle->ports)) {
bundle->ofproto->has_bonded_bundles = true;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index f813c0b..eced240 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -8194,3 +8194,10 @@ 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 22d94c7..eef66fc 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -338,6 +338,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);
@@ -385,7 +386,11 @@ enum port_vlan_mode {
/* Untagged incoming packets are part of 'vlan', as are incoming packets
* tagged with 'vlan'. Outgoing packets tagged with 'vlan' are untagged.
* Other VLANs in 'trunks' are trunked. */
- PORT_VLAN_NATIVE_UNTAGGED
+ PORT_VLAN_NATIVE_UNTAGGED,
+
+ /* 802.1q tunnel port. Incomming packets are added an outer vlan tag
+ * 'vlan'. If 'cvlans' is set, only allows VLANs in 'cvlans'. */
+ PORT_VLAN_DOT1Q_TUNNEL
};
/* Configuration of bundles. */
@@ -396,8 +401,10 @@ struct ofproto_bundle_settings {
size_t n_slaves;
enum port_vlan_mode vlan_mode; /* Selects mode for vlan and trunks */
+ uint16_t qinq_ethtype;
int vlan; /* VLAN VID, except for PORT_VLAN_TRUNK. */
unsigned long *trunks; /* vlan_bitmap, except for PORT_VLAN_ACCESS. */
+ unsigned long *cvlans;
bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */
struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index d65e213..6f29b6b 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -152,8 +152,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/ofproto-dpif.at b/tests/ofproto-dpif.at
index 557c8be..2c7a323 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -3096,6 +3096,11 @@ OVS_VSWITCHD_START(
add-port br0 p7 vlan_mode=native-untagged tag=12 -- \
add-port br0 p8 vlan_mode=native-untagged tag=12 trunks=10,12 \
other-config:priority-tags=true -- \
+ add-port br0 p9 vlan_mode=dot1q-tunnel tag=10 qinq_ethtype=802.1q -- \
+ add-port br0 p10 vlan_mode=dot1q-tunnel tag=10 cvlans=10,12 qinq_ethtype=802.1q -- \
+ add-port br0 p11 vlan_mode=dot1q-tunnel tag=12 qinq_ethtype=802.1q -- \
+ add-port br0 p12 vlan_mode=dot1q-tunnel tag=12 qinq_ethtype=802.1q \
+ other-config:priority-tags=true -- \
set Interface p1 type=dummy -- \
set Interface p2 type=dummy -- \
set Interface p3 type=dummy -- \
@@ -3103,7 +3108,11 @@ OVS_VSWITCHD_START(
set Interface p5 type=dummy -- \
set Interface p6 type=dummy -- \
set Interface p7 type=dummy -- \
- set Interface p8 type=dummy --])
+ set Interface p8 type=dummy -- \
+ set Interface p9 type=dummy -- \
+ set Interface p10 type=dummy -- \
+ set Interface p11 type=dummy -- \
+ set Interface p12 type=dummy --])
dnl Each of these specifies an in_port by number, a VLAN VID (or "none"),
dnl a VLAN PCP (used if the VID isn't "none") and the expected set of datapath
@@ -3112,84 +3121,93 @@ for tuple in \
"100 none 0 drop" \
"100 0 0 drop" \
"100 0 1 drop" \
- "100 10 0 1,5,6,7,8,pop_vlan,2" \
- "100 10 1 1,5,6,7,8,pop_vlan,2" \
+ "100 10 0 1,5,6,7,8,pop_vlan,2,9" \
+ "100 10 1 1,5,6,7,8,pop_vlan,2,9" \
"100 11 0 5,7" \
"100 11 1 5,7" \
- "100 12 0 1,5,6,pop_vlan,3,4,7,8" \
- "100 12 1 1,5,6,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
+ "100 12 0 1,5,6,pop_vlan,3,4,7,8,11,12" \
+ "100 12 1 1,5,6,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
"1 none 0 drop" \
"1 0 0 drop" \
"1 0 1 drop" \
- "1 10 0 5,6,7,8,100,pop_vlan,2" \
- "1 10 1 5,6,7,8,100,pop_vlan,2" \
+ "1 10 0 5,6,7,8,100,pop_vlan,2,9" \
+ "1 10 1 5,6,7,8,100,pop_vlan,2,9" \
"1 11 0 drop" \
"1 11 1 drop" \
- "1 12 0 5,6,100,pop_vlan,3,4,7,8" \
- "1 12 1 5,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
- "2 none 0 push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
- "2 0 0 pop_vlan,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
- "2 0 1 pop_vlan,push_vlan(vid=10,pcp=1),1,5,6,7,8,100" \
+ "1 12 0 5,6,100,pop_vlan,3,4,7,8,11,12" \
+ "1 12 1 5,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
+ "2 none 0 9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+ "2 0 0 pop_vlan,9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+ "2 0 1 pop_vlan,9,push_vlan(vid=10,pcp=1),1,5,6,7,8,100" \
"2 10 0 drop" \
"2 10 1 drop" \
"2 11 0 drop" \
"2 11 1 drop" \
"2 12 0 drop" \
"2 12 1 drop" \
- "3 none 0 4,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
- "3 0 0 pop_vlan,4,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
- "3 0 1 8,pop_vlan,4,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
+ "3 none 0 4,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "3 0 0 pop_vlan,4,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "3 0 1 8,12,pop_vlan,4,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
"3 10 0 drop" \
"3 10 1 drop" \
"3 11 0 drop" \
"3 11 1 drop" \
"3 12 0 drop" \
"3 12 1 drop" \
- "4 none 0 3,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
- "4 0 0 pop_vlan,3,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
- "4 0 1 3,8,pop_vlan,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
+ "4 none 0 3,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "4 0 0 pop_vlan,3,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "4 0 1 3,8,12,pop_vlan,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
"4 10 0 drop" \
"4 10 1 drop" \
"4 11 0 drop" \
"4 11 1 drop" \
"4 12 0 drop" \
"4 12 1 drop" \
- "5 none 0 2,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
- "5 0 0 pop_vlan,2,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
- "5 0 1 pop_vlan,2,push_vlan(vid=10,pcp=1),1,6,7,8,100" \
- "5 10 0 1,6,7,8,100,pop_vlan,2" \
- "5 10 1 1,6,7,8,100,pop_vlan,2" \
+ "5 none 0 2,9,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
+ "5 0 0 pop_vlan,2,9,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
+ "5 0 1 pop_vlan,2,9,push_vlan(vid=10,pcp=1),1,6,7,8,100" \
+ "5 10 0 1,6,7,8,100,pop_vlan,2,9" \
+ "5 10 1 1,6,7,8,100,pop_vlan,2,9" \
"5 11 0 7,100" \
"5 11 1 7,100" \
- "5 12 0 1,6,100,pop_vlan,3,4,7,8" \
- "5 12 1 1,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
- "6 none 0 2,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
- "6 0 0 pop_vlan,2,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
- "6 0 1 pop_vlan,2,push_vlan(vid=10,pcp=1),1,5,7,8,100" \
- "6 10 0 1,5,7,8,100,pop_vlan,2" \
- "6 10 1 1,5,7,8,100,pop_vlan,2" \
+ "5 12 0 1,6,100,pop_vlan,3,4,7,8,11,12" \
+ "5 12 1 1,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
+ "6 none 0 2,9,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
+ "6 0 0 pop_vlan,2,9,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
+ "6 0 1 pop_vlan,2,9,push_vlan(vid=10,pcp=1),1,5,7,8,100" \
+ "6 10 0 1,5,7,8,100,pop_vlan,2,9" \
+ "6 10 1 1,5,7,8,100,pop_vlan,2,9" \
"6 11 0 drop" \
"6 11 1 drop" \
- "6 12 0 1,5,100,pop_vlan,3,4,7,8" \
- "6 12 1 1,5,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
- "7 none 0 3,4,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
- "7 0 0 pop_vlan,3,4,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
- "7 0 1 3,8,pop_vlan,4,push_vlan(vid=12,pcp=1),1,5,6,100" \
- "7 10 0 1,5,6,8,100,pop_vlan,2" \
- "7 10 1 1,5,6,8,100,pop_vlan,2" \
+ "6 12 0 1,5,100,pop_vlan,3,4,7,8,11,12" \
+ "6 12 1 1,5,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
+ "7 none 0 3,4,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "7 0 0 pop_vlan,3,4,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "7 0 1 3,8,12,pop_vlan,4,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
+ "7 10 0 1,5,6,8,100,pop_vlan,2,9" \
+ "7 10 1 1,5,6,8,100,pop_vlan,2,9" \
"7 11 0 5,100" \
"7 11 1 5,100" \
- "7 12 0 1,5,6,100,pop_vlan,3,4,8" \
- "7 12 1 1,5,6,100,pop_vlan,4,push_vlan(vid=0,pcp=1),3,8" \
- "8 none 0 3,4,7,push_vlan(vid=12,pcp=0),1,5,6,100" \
- "8 0 0 pop_vlan,3,4,7,push_vlan(vid=12,pcp=0),1,5,6,100" \
- "8 0 1 3,pop_vlan,4,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
- "8 10 0 1,5,6,7,100,pop_vlan,2" \
- "8 10 1 1,5,6,7,100,pop_vlan,2" \
+ "7 12 0 1,5,6,100,pop_vlan,3,4,8,11,12" \
+ "7 12 1 1,5,6,100,pop_vlan,4,11,push_vlan(vid=0,pcp=1),3,8,12" \
+ "8 none 0 3,4,7,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "8 0 0 pop_vlan,3,4,7,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "8 0 1 3,12,pop_vlan,4,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
+ "8 10 0 1,5,6,7,100,pop_vlan,2,9" \
+ "8 10 1 1,5,6,7,100,pop_vlan,2,9" \
"8 11 0 drop" \
"8 11 1 drop" \
- "8 12 0 1,5,6,100,pop_vlan,3,4,7" \
- "8 12 1 1,5,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3"
+ "8 12 0 1,5,6,100,pop_vlan,3,4,7,11,12" \
+ "8 12 1 1,5,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,12" \
+ "9 none 0 2,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+ "9 10 0 10,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+ "9 11 0 push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+ "10 none 0 drop" \
+ "10 0 0 drop" \
+ "10 11 0 drop" \
+ "10 12 0 9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+ "11 10 0 7,8,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+ "11 10 1 7,8,12,push_vlan(vid=12,pcp=0),1,5,6,100"
do
set $tuple
in_port=$1
@@ -3217,6 +3235,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
@@ -3378,8 +3482,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
@@ -3389,26 +3493,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.
@@ -3446,8 +3553,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
@@ -3457,26 +3564,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.
@@ -3514,8 +3624,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
@@ -3525,26 +3635,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.
@@ -3582,8 +3695,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
@@ -3593,31 +3706,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
diff --git a/tests/test-classifier.c b/tests/test-classifier.c
index f85ea4f..774a9be 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 dabfb85..7631dd6 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 306bb78..cc711df 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -4107,8 +4107,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);
@@ -4119,8 +4119,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. */
@@ -4133,8 +4133,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);
@@ -4148,14 +4148,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");
}
@@ -4171,8 +4172,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);
@@ -4182,8 +4183,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 61cb966..ae0c036 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -566,6 +566,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
ofproto_set_max_idle(smap_get_int(&ovs_cfg->other_config, "max-idle",
OFPROTO_MAX_IDLE_DEFAULT));
ofproto_set_cpu_mask(smap_get(&ovs_cfg->other_config, "pmd-cpu-mask"));
+ 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),
@@ -941,6 +943,11 @@ port_configure(struct port *port)
s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks);
}
+ s.cvlans = NULL;
+ if (cfg->n_cvlans) {
+ s.cvlans = vlan_bitmap_from_array(cfg->cvlans, cfg->n_cvlans);
+ }
+
/* Get VLAN mode. */
if (cfg->vlan_mode) {
if (!strcmp(cfg->vlan_mode, "access")) {
@@ -951,6 +958,8 @@ port_configure(struct port *port)
s.vlan_mode = PORT_VLAN_NATIVE_TAGGED;
} else if (!strcmp(cfg->vlan_mode, "native-untagged")) {
s.vlan_mode = PORT_VLAN_NATIVE_UNTAGGED;
+ } else if (!strcmp(cfg->vlan_mode, "dot1q-tunnel")) {
+ s.vlan_mode = PORT_VLAN_DOT1Q_TUNNEL;
} else {
/* This "can't happen" because ovsdb-server should prevent it. */
VLOG_WARN("port %s: unknown VLAN mode %s, falling "
@@ -960,7 +969,7 @@ port_configure(struct port *port)
} else {
if (s.vlan >= 0) {
s.vlan_mode = PORT_VLAN_ACCESS;
- if (cfg->n_trunks) {
+ if (cfg->n_trunks || cfg->n_cvlans) {
VLOG_WARN("port %s: ignoring trunks in favor of implicit vlan",
port->name);
}
@@ -968,6 +977,24 @@ port_configure(struct port *port)
s.vlan_mode = PORT_VLAN_TRUNK;
}
}
+
+ if (cfg->qinq_ethtype) {
+ if (!strcmp(cfg->qinq_ethtype, "802.1q") ||
+ !strcmp(cfg->qinq_ethtype, "0x8100")) {
+ s.qinq_ethtype = ETH_TYPE_VLAN_8021Q;
+ } else if (!strcmp(cfg->qinq_ethtype, "802.1ad") ||
+ !strcmp(cfg->qinq_ethtype, "0x88a8")) {
+ s.qinq_ethtype = ETH_TYPE_VLAN_8021AD;
+ } else {
+ /* This "can't happen" because ovsdb-server should prevent it. */
+ VLOG_WARN("port %s: invalid QinQ ethertype %s, falling "
+ "back to 802.1ad", port->name, cfg->qinq_ethtype);
+ s.qinq_ethtype = ETH_TYPE_VLAN_8021AD;
+ }
+ } else {
+ s.qinq_ethtype = ETH_TYPE_VLAN_8021AD;
+ }
+
s.use_priority_tags = smap_get_bool(&cfg->other_config, "priority-tags",
false);
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index 8966803..ac86c8a 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,6 +1,6 @@
{"name": "Open_vSwitch",
- "version": "7.14.0",
- "cksum": "3974332717 22936",
+ "version": "7.15.0",
+ "cksum": "916215147 23349",
"tables": {
"Open_vSwitch": {
"columns": {
@@ -145,6 +145,11 @@
"minInteger": 0,
"maxInteger": 4095},
"min": 0, "max": 4096}},
+ "cvlans": {
+ "type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 4095},
+ "min": 0, "max": 4096}},
"tag": {
"type": {"key": {"type": "integer",
"minInteger": 0,
@@ -152,7 +157,12 @@
"min": 0, "max": 1}},
"vlan_mode": {
"type": {"key": {"type": "string",
- "enum": ["set", ["trunk", "access", "native-tagged", "native-untagged"]]},
+ "enum": ["set", ["trunk", "access", "native-tagged",
+ "native-untagged", "dot1q-tunnel"]]},
+ "min": 0, "max": 1}},
+ "qinq_ethtype": {
+ "type": {"key": {"type": "string",
+ "enum": ["set", ["802.1q", "802.1ad", "0x88a8", "0x8100"]]},
"min": 0, "max": 1}},
"qos": {
"type": {"key": {"type": "uuid",
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index f64c18a..fca89d3 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -334,6 +334,23 @@
datapaths.
</p>
</column>
+ <column name="other_config" key="vlan-limit"
+ type='{"type": "integer", "minInteger": 0}'>
+ <p>
+ Limit the maxmium of VLAN headers that can be matched. Further VLAN
+ headers will be treated as payload, e.g. a packet with more 802.1q
+ headers will match eth_type 0x8100.
+ </p>
+ <p>
+ Value <code>0</code> means unlimited. The actual value of max VLAN
+ headers is the minumium of <code>vlan-limit</code>, the max VLANs
+ supported by Open vSwitch userspace (currently 2), and that supported
+ by datapath.
+ </p>
+ <p>
+ The default value is 1, to keep backward compatibility.
+ </p>
+ </column>
</group>
<group title="Status">
@@ -1297,6 +1314,22 @@
exception that a packet that egresses on a native-untagged port in
the native VLAN will not have an 802.1Q header.
</dd>
+
+ <dt>dot1q-tunnel</dt>
+ <dd>
+ <p>
+ A dot1q-tunnel port tunnels packets with VLAN specified in
+ <ref column="tag"/> column. That is it adds 802.1Q header, with
+ ethertype and VLAN ID specified in <ref column="qinq-ethtype"/>
+ and <ref column="tag"/>, to both tagged and untagged packets on
+ ingress, and pops dot1q header of this VLAN on egress.
+ </p>
+
+ <p>
+ If <ref column="cvlans"/> is set, only allows packets of these
+ VLANs.
+ </p>
+ </dd>
</dl>
<p>
A packet will only egress through bridge ports that carry the VLAN of
@@ -1320,6 +1353,13 @@
</ul>
</column>
+ <column name="qinq_ethtype">
+ <p>
+ Ethertype of dot1q-tunnel port, could be either "802.1q"(0x8100) or
+ "802.1ad"(0x88a8). Defaults to "802.1ad".
+ </p>
+ </column>
+
<column name="tag">
<p>
For an access port, the port's implicitly tagged VLAN. For a
@@ -1341,6 +1381,14 @@
</p>
</column>
+ <column name="cvlans">
+ <p>
+ For a trunk-qinq port if specific cvlans are specified only those
+ cvlans are 1ad tunneled, others are dropped. If no cvlans specified
+ explicitly then all cvlans are 1ad tunneled.
+ </p>
+ </column>
+
<column name="other_config" key="priority-tags"
type='{"type": "boolean"}'>
<p>
--
2.9.3
More information about the dev
mailing list