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

+ + +

+ Limits the number of VLAN headers that can be matched to the + specified number. Further VLAN headers will be treated as payload, + e.g. a packet with more 802.1q headers will match Ethernet type + 0x8100. +

+

+ Value 0 means unlimited. The actual number of supported + VLAN headers is the smallest of vlan-limit, the number + of VLANs supported by Open vSwitch userspace (currently 2), and the + number supported by the datapath. +

+ +

+ If this value is absent, the default is currently 1. This maintains + backward compatibility with controllers that were designed for use + with Open vSwitch versions earlier than 2.8, which only supported one + VLAN. +

+