This is the Open vSwitch user space portion of the 802.1AD patch. Signed-off-by: Thomas F Herbert <thomasfherb...@entpnt.com>
--- NEWS | 2 + lib/flow.c | 22 +++-- lib/flow.h | 10 +- lib/match.c | 2 +- lib/nx-match.c | 2 +- lib/odp-execute.c | 3 +- lib/odp-util.c | 217 +++++++++++++++++++++++++++++++++++++------ lib/odp-util.h | 2 +- lib/ofp-actions.c | 32 ++++--- lib/ofp-actions.h | 13 ++- lib/ofp-util.c | 2 +- lib/packets.c | 2 +- lib/packets.h | 7 ++ ofproto/ofproto-dpif-xlate.c | 13 ++- tests/ofproto-dpif.at | 40 ++++++++ utilities/ovs-ofctl.8.in | 3 +- 16 files changed, 307 insertions(+), 65 deletions(-) diff --git a/NEWS b/NEWS index f2fceb5..6655561 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,7 @@ Post-v2.3.0 --------------------- + - Add support for 8021.AD as specified in OpenFlow 1.1+. Adds push and pop + of 802.1AD Ethertype and handling of QinQ or double stacked Vlans. - Add bash command-line completion support for ovs-appctl/ovs-dpctl/ ovs-ofctl/ovsdb-tool commands. Please check utilities/ovs-command-compgen.INSTALL.md for how to use. diff --git a/lib/flow.c b/lib/flow.c index eb7fdf1..da6599f 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -121,7 +121,7 @@ struct mf_ctx { * away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are * defined as macros. */ -#if (FLOW_WC_SEQ != 28) +#if (FLOW_WC_SEQ != 29) #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 " @@ -228,7 +228,7 @@ parse_vlan(void **datap, size_t *sizep) data_pull(datap, sizep, ETH_ADDR_LEN * 2); - if (eth->eth_type == htons(ETH_TYPE_VLAN)) { + if (eth_type_vlan(eth->eth_type)) { if (OVS_LIKELY(*sizep >= sizeof(struct qtag_prefix) + sizeof(ovs_be16))) { const struct qtag_prefix *qp = data_pull(datap, sizep, sizeof *qp); @@ -689,7 +689,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc) void flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); fmd->dp_hash = flow->dp_hash; fmd->recirc_id = flow->recirc_id; @@ -836,7 +836,7 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, memset(&wc->masks, 0x0, sizeof wc->masks); /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); if (flow->tunnel.ip_dst) { if (flow->tunnel.flags & FLOW_TNL_F_KEY) { @@ -933,7 +933,7 @@ uint64_t flow_wc_map(const struct flow *flow) { /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); uint64_t map = (flow->tunnel.ip_dst) ? MINIFLOW_MAP(tunnel) : 0; @@ -985,7 +985,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 == 28); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata); memset(&wc->masks.regs, 0, sizeof wc->masks.regs); @@ -1546,7 +1546,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label)); /* Clear all L3 and L4 fields. */ - BUILD_ASSERT(FLOW_WC_SEQ == 28); + BUILD_ASSERT(FLOW_WC_SEQ == 29); memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0, sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT); } @@ -1732,8 +1732,12 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) return; } - if (flow->vlan_tci & htons(VLAN_CFI)) { - eth_push_vlan(b, htons(ETH_TYPE_VLAN), flow->vlan_tci); + if (flow->vlan_tci & htons(VLAN_CFI) && + (flow->vlan_ctci & htons(VLAN_CFI))) { + eth_push_vlan(b, htons(ETH_TYPE_VLAN_8021Q), flow->vlan_ctci); + eth_push_vlan(b, htons(ETH_TYPE_VLAN_8021AD), flow->vlan_tci); + } else if (flow->vlan_tci & htons(VLAN_CFI)) { + eth_push_vlan(b, htons(ETH_TYPE_VLAN_8021Q), flow->vlan_tci); } if (flow->dl_type == htons(ETH_TYPE_IP)) { diff --git a/lib/flow.h b/lib/flow.h index 8e56d05..270cfd0 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -38,7 +38,7 @@ struct pkt_metadata; /* 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 28 +#define FLOW_WC_SEQ 29 /* Number of Open vSwitch extension 32-bit registers. */ #define FLOW_N_REGS 8 @@ -109,7 +109,9 @@ struct flow { uint8_t dl_dst[ETH_ADDR_LEN]; /* Ethernet destination address. */ uint8_t dl_src[ETH_ADDR_LEN]; /* Ethernet source address. */ ovs_be16 dl_type; /* Ethernet frame type. */ - ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */ + ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; If 802.1ad, outer tag */ + ovs_be16 vlan_ctci; /* If 802.1ad, Customer TCI | VLAN_CFI, inner tag */ + ovs_be16 vlan_tpid; /* Vlan protocol type, either 802.1ad or 802.1q. */ ovs_be32 mpls_lse[FLOW_MAX_MPLS_LABELS]; /* MPLS label stack entry. */ /* L3 */ @@ -156,8 +158,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0); /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ BUILD_ASSERT_DECL(offsetof(struct flow, dp_hash) + sizeof(uint32_t) - == sizeof(struct flow_tnl) + 180 - && FLOW_WC_SEQ == 28); + == sizeof(struct flow_tnl) + 184 + && FLOW_WC_SEQ == 29); /* Incremental points at which flow classification may be performed in * segments. diff --git a/lib/match.c b/lib/match.c index 480b972..c41073b 100644 --- a/lib/match.c +++ b/lib/match.c @@ -870,7 +870,7 @@ match_format(const struct match *match, struct ds *s, int priority) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "priority=%d,", priority); diff --git a/lib/nx-match.c b/lib/nx-match.c index 2ad3cf2..1f72a84 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -817,7 +817,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 == 28); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); /* Metadata. */ if (match->wc.masks.dp_hash) { diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 720d24e..521d3b9 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -473,11 +473,10 @@ odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt, bool steal, case OVS_ACTION_ATTR_PUSH_VLAN: { const struct ovs_action_push_vlan *vlan = nl_attr_get(a); - for (i = 0; i < cnt; i++) { struct ofpbuf *buf = &packets[i]->ofpbuf; - eth_push_vlan(buf, htons(ETH_TYPE_VLAN), vlan->vlan_tci); + eth_push_vlan(buf, vlan->vlan_tpid, vlan->vlan_tci); } break; } diff --git a/lib/odp-util.c b/lib/odp-util.c index d52c172..06ddfaf 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -2861,7 +2861,7 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow, size_t max_mpls_depth, bool recirc, bool export_mask) { struct ovs_key_ethernet *eth_key; - size_t encap; + size_t encap = 0; const struct flow *data = export_mask ? mask : flow; nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority); @@ -2887,11 +2887,19 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow, sizeof *eth_key); get_ethernet_key(data, eth_key); - if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) { - if (export_mask) { - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); + if (flow->vlan_tci != htons(0) || eth_type_vlan(flow->dl_type)) { + if (flow->vlan_ctci != htons(0)) { + if (export_mask) { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); + } else { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN_8021AD)); + } } else { - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN)); + 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_8021Q)); + } } nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci); encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP); @@ -3624,6 +3632,81 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], return MAX(fitness, encap_fitness); } +/* Parse 802.1ADQ header then parse encapsulated Customer Vlan. */ +static enum odp_key_fitness +parse_8021ad_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], + uint64_t present_attrs, int out_of_range_attr, + uint64_t expected_attrs, struct flow *flow, + const struct nlattr *key, size_t key_len, + const struct flow *src_flow) +{ + 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); + enum odp_key_fitness encap_fitness; + enum odp_key_fitness fitness; + + /* Calculate fitness of outer attributes. */ + if (!is_mask) { + expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) | + (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)); + } else { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) { + expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN); + } + 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 outertag, 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, "Outer OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero " + "but CFI bit is not set", ntohs(flow->vlan_tci)); + return ODP_FIT_ERROR; + } + } else { + if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) { + return fitness; + } + } + + /* 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; + } + expected_attrs = 0; + + if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) { + return ODP_FIT_ERROR; + } + encap_fitness = parse_8021q_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len, src_flow); + + /* The overall fitness is the worse of the outer and inner attributes. */ + return MAX(fitness, encap_fitness); +} + static enum odp_key_fitness odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, struct flow *flow, const struct flow *src_flow) @@ -3699,7 +3782,7 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; } - /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */ + /* Get Ethertype or VLAN TPID or FLOW_DL_TYPE_NONE. */ if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) { return ODP_FIT_ERROR; @@ -3707,9 +3790,14 @@ 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)) { - return parse_8021q_onward(attrs, present_attrs, out_of_range_attr, - expected_attrs, flow, key, key_len, src_flow); + : eth_type_vlan(src_flow->dl_type)) { + if (src_flow->dl_type == ETH_TYPE_VLAN_8021AD) { + return parse_8021ad_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len, src_flow); + } else { + return parse_8021q_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len, src_flow); + } } if (is_mask) { flow->vlan_tci = htons(0xffff); @@ -3947,37 +4035,114 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow, put_ethernet_key(&mask, &wc->masks); } } - +static int +flow_get_vlan_depth(const struct flow *flow) +{ + int n = 0; + if (flow->vlan_tci & htons(VLAN_CFI)) + n++; + if (flow->vlan_ctci & htons(VLAN_CFI)) + n++; + return n; +} static void -pop_vlan(struct flow *base, - struct ofpbuf *odp_actions, struct flow_wildcards *wc) +pop_vlan(struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { + int base_n = flow_get_vlan_depth(base); + + if (base_n == 0) + return; + memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); - if (base->vlan_tci & htons(VLAN_CFI)) { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); + if (base_n == 1) { + base->vlan_tci = 0; + base->vlan_tpid = 0; + base->vlan_ctci = 0; + + } else if (base_n ==2) { + + base->vlan_tci = base->vlan_ctci; + base->vlan_ctci = 0; + base->vlan_tpid = htons(ETH_TYPE_VLAN_8021Q); } + nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); } static void -commit_vlan_action(ovs_be16 vlan_tci, struct flow *base, - struct ofpbuf *odp_actions, struct flow_wildcards *wc) +push_vlan(struct flow *base, ovs_be16 vlan_tci, ovs_be16 vlan_tpid, + struct ofpbuf *odp_actions, struct flow_wildcards *wc) { - if (base->vlan_tci == vlan_tci) { + struct ovs_action_push_vlan vlan; + int base_n; + ovs_be16 tpid = htons(ETH_TYPE_VLAN_8021Q); + + if (!(vlan_tci & htons(VLAN_CFI))) return; - } - pop_vlan(base, odp_actions, wc); - if (vlan_tci & htons(VLAN_CFI)) { - struct ovs_action_push_vlan vlan; + memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); + - vlan.vlan_tpid = htons(ETH_TYPE_VLAN); - vlan.vlan_tci = vlan_tci; - nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, - &vlan, sizeof vlan); + base_n = flow_get_vlan_depth(base); + + if (base_n == 2) { + return; + } else if (base_n == 1) { + if (!vlan_tpid) { + tpid = htons(ETH_TYPE_VLAN_8021AD); + } else { + tpid = vlan_tpid; + } + base->vlan_ctci = base->vlan_tci; + } else if (base_n == 0) { + if (!vlan_tpid) { + tpid = htons(ETH_TYPE_VLAN_8021Q); + } else { + tpid = vlan_tpid; + } + base->vlan_ctci = 0; } base->vlan_tci = vlan_tci; + base->vlan_tpid = tpid; + + vlan.vlan_tpid = tpid; + vlan.vlan_tci = vlan_tci; + nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, + &vlan, sizeof vlan); +} + +static void +commit_vlan_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions, struct flow_wildcards *wc) +{ + ovs_be16 vlan_tci = flow->vlan_tci; + ovs_be16 vlan_tpid = flow->vlan_tpid; + int base_n; + int flow_n; + + + if ((base->vlan_tci == vlan_tci) && (base->vlan_ctci == vlan_tci)) + return; + + base_n = flow_get_vlan_depth(base); + flow_n = flow_get_vlan_depth(flow); + + + if (flow_n == base_n) { + if (vlan_tci == base->vlan_tci) { + return; + } else { + pop_vlan(base, odp_actions, wc); + push_vlan(base, vlan_tci, vlan_tpid, odp_actions, wc); + } + } + else if (flow_n > base_n) { + push_vlan(base, vlan_tci, vlan_tpid, odp_actions, wc); + } + else if (flow_n < base_n) { + pop_vlan(base, odp_actions, wc); + } } /* Wildcarding already done at action translation time. */ @@ -4327,7 +4492,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base, slow = commit_set_nw_action(flow, base, odp_actions, wc, use_masked); commit_set_port_action(flow, base, odp_actions, wc, use_masked); 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 9c990cd..b361795 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -133,7 +133,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); * add another field and forget to adjust this value. */ #define ODPUTIL_FLOW_KEY_BYTES 512 -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); /* 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 diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 4680d81..7414a78 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -1266,16 +1266,20 @@ format_STRIP_VLAN(const struct ofpact_null *a, struct ds *s) static enum ofperr decode_OFPAT_RAW11_PUSH_VLAN(ovs_be16 eth_type, 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 *oam; + + if (!eth_type_vlan(eth_type)) { + /* XXX Only 802.1q and 802.1AD(QinQ) is supported. */ return OFPERR_OFPBAC_BAD_ARGUMENT; } - ofpact_put_PUSH_VLAN(out); + oam = ofpact_put_PUSH_VLAN(out); + oam->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) { @@ -1283,7 +1287,7 @@ encode_PUSH_VLAN(const struct ofpact_null *null OVS_UNUSED, * 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); } } @@ -1295,25 +1299,25 @@ parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts, char *error; *usable_protocols &= OFPUTIL_P_OF11_UP; - error = str_to_u16(arg, "ethertype", ðertype); + error = str_to_u16(arg, "push_vlan", ðertype); if (error) { return error; } - if (ethertype != ETH_TYPE_VLAN_8021Q) { - /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */ + if (!eth_type_vlan(htons(ethertype))) { + /* Check for valid VLAN ethertypes */ return xasprintf("%s: not a valid VLAN ethertype", arg); } - ofpact_put_PUSH_VLAN(ofpacts); + ofpact_put_PUSH_VLAN(ofpacts)->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 *a OVS_UNUSED, struct ds *s) { /* XXX 802.1AD case*/ - ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q); + ds_put_format(s, "push_vlan:%#"PRIx16, ntohs(a->ethertype)); } /* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */ @@ -5357,8 +5361,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, return 0; case OFPACT_PUSH_VLAN: - if (flow->vlan_tci & htons(VLAN_CFI)) { - /* Multiple VLAN headers not supported. */ + if (flow->vlan_tci & htons(VLAN_CFI) && + flow->vlan_ctci & htons(VLAN_CFI)) { + /* More then 2 levels of VLAN headers not supported. */ return OFPERR_OFPBAC_BAD_TAG; } /* Temporary mark that we have a vlan tag. */ @@ -5971,6 +5976,7 @@ ofpacts_get_meter(const struct ofpact ofpacts[], size_t ofpacts_len) /* Formatting ofpacts. */ + static void ofpact_format(const struct ofpact *a, struct ds *s) { diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index 8362aa8..215bbd4 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -66,7 +66,7 @@ OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact, "set_vlan_vid") \ OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact, "set_vlan_pcp") \ OFPACT(STRIP_VLAN, ofpact_null, ofpact, "strip_vlan") \ - OFPACT(PUSH_VLAN, ofpact_null, ofpact, "push_vlan") \ + OFPACT(PUSH_VLAN, ofpact_push_vlan, ofpact, "push_vlan") \ OFPACT(SET_ETH_SRC, ofpact_mac, ofpact, "mod_dl_src") \ OFPACT(SET_ETH_DST, ofpact_mac, ofpact, "mod_dl_dst") \ OFPACT(SET_IPV4_SRC, ofpact_ipv4, ofpact, "mod_nw_src") \ @@ -393,7 +393,7 @@ struct ofpact_set_field { union mf_value mask; }; -/* OFPACT_PUSH_VLAN/MPLS/PBB +/* OFPACT_PUSH_MPLS * * Used for NXAST_PUSH_MPLS, OFPAT11_PUSH_MPLS. */ struct ofpact_push_mpls { @@ -401,9 +401,16 @@ struct ofpact_push_mpls { ovs_be16 ethertype; }; +/* OFPACT_PUSH_VLAN + * + * Used for OFPAT11_PUSH_VLAN. */ +struct ofpact_push_vlan { + struct ofpact ofpact; + ovs_be16 ethertype; +}; /* OFPACT_POP_MPLS * - * Used for NXAST_POP_MPLS, OFPAT11_POP_MPLS.. */ + * Used for NXAST_POP_MPLS, OFPAT11_POP_MPLS. */ struct ofpact_pop_mpls { struct ofpact ofpact; ovs_be16 ethertype; diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 3f588c2..53982d5 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -186,7 +186,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 == 28); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); diff --git a/lib/packets.c b/lib/packets.c index af99e3b..f5b4526 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -217,7 +217,7 @@ set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type) return; } - if (eh->eth_type == htons(ETH_TYPE_VLAN)) { + if (eth_type_vlan(eh->eth_type)) { ovs_be16 *p; char *l2_5 = ofpbuf_l2_5(packet); diff --git a/lib/packets.h b/lib/packets.h index 65d2274..b6596bd 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -252,6 +252,13 @@ static inline bool eth_type_mpls(ovs_be16 eth_type) eth_type == htons(ETH_TYPE_MPLS_MCAST); } +static inline bool eth_type_vlan(ovs_be16 eth_type) +{ + return eth_type == htons(ETH_TYPE_VLAN_8021Q) || + eth_type == htons(ETH_TYPE_VLAN_8021AD); +} + + /* Minimum value for an Ethernet type. Values below this are IEEE 802.2 frame * lengths. */ #define ETH_TYPE_MIN 0x600 diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index ba0c0d8..97237db 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -2627,6 +2627,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, struct flow *flow = &ctx->xin->flow; struct flow_tnl flow_tnl; ovs_be16 flow_vlan_tci; + ovs_be16 flow_vlan_ctci; uint32_t flow_pkt_mark; uint8_t flow_nw_tos; odp_port_t out_port, odp_port; @@ -2635,7 +2636,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 == 28); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29); memset(&flow_tnl, 0, sizeof flow_tnl); if (!xport) { @@ -2732,6 +2733,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, } flow_vlan_tci = flow->vlan_tci; + flow_vlan_ctci = flow->vlan_ctci; flow_pkt_mark = flow->pkt_mark; flow_nw_tos = flow->nw_tos; @@ -2851,6 +2853,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, out: /* Restore flow */ flow->vlan_tci = flow_vlan_tci; + flow->vlan_ctci = flow_vlan_ctci; flow->pkt_mark = flow_pkt_mark; flow->nw_tos = flow_nw_tos; } @@ -3877,6 +3880,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, /* XXX 802.1AD(QinQ) */ memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); flow->vlan_tci = htons(VLAN_CFI); + if (ofpact_get_PUSH_VLAN(a)->ethertype == htons(ETH_TYPE_VLAN_8021AD)) { + memset(&wc->masks.vlan_ctci, 0xff, sizeof wc->masks.vlan_ctci); + flow->vlan_ctci |= htons(VLAN_CFI); + memset(&wc->masks.vlan_tpid, 0xff, sizeof wc->masks.vlan_tpid); + flow->vlan_tpid = htons(ETH_TYPE_VLAN_8021AD); + } else if (ofpact_get_PUSH_VLAN(a)->ethertype == htons(ETH_TYPE_VLAN_8021Q)) { + flow->vlan_tpid = htons(ETH_TYPE_VLAN_8021Q); + } break; case OFPACT_SET_ETH_SRC: diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 64cfcd4..7c21485 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -1475,6 +1475,46 @@ NXST_FLOW reply: OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ofproto-dpif - VLAN with AD Ethertype]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2], [3], [4]) + +AT_DATA([flows1.txt], [dnl +table=0 in_port=1 dl_type=0x8100 actions=pop_vlan,output:2 +table=0 in_port=1 dl_type=0x88a8 actions=pop_vlan,output:3 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows1.txt]) +AT_DATA([flows2.txt], [dnl +table=0 in_port=2 dl_type=0x0800 actions=push_vlan:0x8100,mod_vlan_vid:9,output:1 +table=0 in_port=3 dl_type=0x0800 actions=push_vlan:0x88a8,set_field:0x1006->vlan_tci,output:1 +table=0 in_port=4 dl_type=0x0800 actions=push_vlan:0x88a8,mod_vlan_vid:11,output:1 +]) +AT_CHECK([ovs-ofctl -O OpenFlow11 add-flows br0 flows2.txt]) + +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x8100,dl_vlan=9'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: pop_vlan,2 +]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x88a8,dl_vlan=9'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: pop_vlan,3 +]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0800'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: push_vlan(vid=9,pcp=0),1 +]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=3,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0800'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: push_vlan(tpid=0x88a8,vid=6,pcp=0),1 +]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=4,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0800'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: push_vlan(tpid=0x88a8,vid=11,pcp=0),1 +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ofproto-dpif - MPLS handling]) OVS_VSWITCHD_START([dnl add-port br0 p1 -- set Interface p1 type=dummy diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 7ffbeaa..ea8ae2e 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1276,8 +1276,7 @@ Strips the VLAN tag from a packet if it is present. . .IP \fBpush_vlan\fR:\fIethertype\fR Push a new VLAN tag onto the packet. Ethertype is used as the the Ethertype -for the tag. Only ethertype 0x8100 should be used. (0x88a8 which the spec -allows isn't supported at the moment.) +for the tag. Ethertypes 0x8100, 0x88a8, or 0x9100 may be used. A priority of zero and the tag of zero are used for the new tag. . .IP \fBpush_mpls\fR:\fIethertype\fR -- 1.8.3.2 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev