This commit contains the following changes: 1. New vlan variables (tpid and count) are added to flow structure to support provider VLANs. 2. miniflow_extract() initialises these new variables based on received packet. 3. Altered flow sequence is modified in all the required places. 4. commit_vlan_action() is modified to support stacked VLANs
Signed-off-by: Avinash <avinashand...@gmail.com> --- lib/flow.c | 53 +++++++++++++++++++++++++++++++++++++++++++---------- lib/flow.h | 8 +++++--- lib/match.c | 2 +- lib/nx-match.c | 2 +- lib/odp-util.c | 39 ++++++++++++++++++++++++++++++--------- lib/ofp-util.c | 2 +- lib/packets.c | 2 +- lib/packets.h | 16 ++++++++++++++++ 8 files changed, 98 insertions(+), 26 deletions(-) diff --git a/lib/flow.c b/lib/flow.c index 1f7f310..06e45ac 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -57,6 +57,11 @@ BUILD_ASSERT_DECL(offsetof(struct flow, dl_type) + 2 offsetof(struct flow, dl_type) / 4 == offsetof(struct flow, vlan_tci) / 4 ); +BUILD_ASSERT_DECL(offsetof(struct flow, vlan_tpid) + 2 + == offsetof(struct flow, vlan_count) && + offsetof(struct flow, vlan_tpid) / 4 + == offsetof(struct flow, vlan_count) / 4 ); + BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) + 3 == offsetof(struct flow, nw_proto) && offsetof(struct flow, nw_tos) + 2 @@ -121,7 +126,7 @@ struct mf_ctx { * away. Some GCC versions gave warnigns on ALWAYS_INLINE, so these are * defined as macros. */ -#if (FLOW_WC_SEQ != 26) +#if (FLOW_WC_SEQ != 27) #define MINIFLOW_ASSERT(X) ovs_assert(X) #else #define MINIFLOW_ASSERT(X) @@ -214,21 +219,24 @@ parse_mpls(void **datap, size_t *sizep) } static inline ovs_be16 -parse_vlan(void **datap, size_t *sizep) +parse_vlan(void **datap, size_t *sizep, ovs_be16 *tpid, uint8_t *vlan_count) { const struct eth_header *eth = *datap; struct qtag_prefix { - ovs_be16 eth_type; /* ETH_TYPE_VLAN */ + ovs_be16 eth_type; /* ETH_TYPE_VLAN or ETH_TYPE_VLAN_8021AD */ ovs_be16 tci; }; data_pull(datap, sizep, ETH_ADDR_LEN * 2); - - if (eth->eth_type == htons(ETH_TYPE_VLAN)) { + *tpid = htons(0); + *vlan_count = 0; + 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); + *tpid = qp->eth_type; + *vlan_count = 1; return qp->tci | htons(VLAN_CFI); } } @@ -268,6 +276,27 @@ parse_ethertype(void **datap, size_t *sizep) return htons(FLOW_DL_TYPE_NONE); } +static inline ovs_be16 +parse_remaining_vlans(void **datap, size_t *sizep, uint8_t *vlan_count) +{ + ovs_be16 dl_type; + struct qtag_prefix { + ovs_be16 eth_type; /* ETH_TYPE_VLAN or ETH_TYPE_VLAN_8021AD */ + ovs_be16 tci; + }; + + dl_type = parse_ethertype(datap, sizep); + while (eth_type_vlan(dl_type) + && (OVS_LIKELY(*sizep + >= sizeof(struct qtag_prefix) + sizeof(ovs_be16)))) { + const struct qtag_prefix *qp = data_pull(datap, sizep, sizeof *qp); + /* 'tci' field contains the actual ether type */ + dl_type = qp->tci; + (*vlan_count)++; + } + return dl_type; +} + static inline bool parse_icmpv6(void **datap, size_t *sizep, const struct icmp6_hdr *icmp, const struct in6_addr **nd_target, @@ -391,16 +420,20 @@ miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md, goto out; } else { ovs_be16 vlan_tci; + ovs_be16 vlan_tpid; + uint8_t vlan_count; /* Link layer. */ BUILD_ASSERT(offsetof(struct flow, dl_dst) + 6 == offsetof(struct flow, dl_src)); miniflow_push_words(mf, dl_dst, data, ETH_ADDR_LEN * 2 / 4); /* dl_type, vlan_tci. */ - vlan_tci = parse_vlan(&data, &size); - dl_type = parse_ethertype(&data, &size); + vlan_tci = parse_vlan(&data, &size, &vlan_tpid, &vlan_count); + dl_type = parse_remaining_vlans(&data, &size, &vlan_count); miniflow_push_be16(mf, dl_type, dl_type); miniflow_push_be16(mf, vlan_tci, vlan_tci); + miniflow_push_be16(mf, vlan_tpid, vlan_tpid); + miniflow_push_be16(mf, vlan_count, vlan_count); } /* Parse mpls. */ @@ -656,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 == 26); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); fmd->dp_hash = flow->dp_hash; fmd->recirc_id = flow->recirc_id; @@ -1316,7 +1349,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 == 26); + BUILD_ASSERT(FLOW_WC_SEQ == 27); memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0, sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT); } @@ -1487,7 +1520,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) } if (flow->vlan_tci & htons(VLAN_CFI)) { - eth_push_vlan(b, htons(ETH_TYPE_VLAN), flow->vlan_tci); + eth_push_vlan(b, get_vlan_tpid(flow->vlan_count), flow->vlan_tci); } if (flow->dl_type == htons(ETH_TYPE_IP)) { diff --git a/lib/flow.h b/lib/flow.h index 139e7f6..07314dc 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 26 +#define FLOW_WC_SEQ 27 #define FLOW_N_REGS 8 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS); @@ -98,6 +98,8 @@ struct flow { uint8_t dl_src[6]; /* 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_tpid; /* VLAN identifier; Either 802.1Q or 802.1AD */ + uint8_t vlan_count; /* Number of stacked VLANs */ ovs_be32 mpls_lse[FLOW_MAX_MPLS_LABELS]; /* MPLS label stack entry. */ /* L3 */ @@ -129,8 +131,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) + 172 - && FLOW_WC_SEQ == 26); + == sizeof(struct flow_tnl) + 176 + && FLOW_WC_SEQ == 27); /* Incremental points at which flow classification may be performed in * segments. diff --git a/lib/match.c b/lib/match.c index 93b725a..ff84d61 100644 --- a/lib/match.c +++ b/lib/match.c @@ -944,7 +944,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 26); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "priority=%u,", priority); diff --git a/lib/nx-match.c b/lib/nx-match.c index 295eec0..678e6f3 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -616,7 +616,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 == 26); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); /* Metadata. */ if (match->wc.masks.dp_hash) { diff --git a/lib/odp-util.c b/lib/odp-util.c index 3c69ada..e9f4dd8 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -3252,10 +3252,13 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], /* Set vlan_tci. * Remove the TPID from dl_type since it's not the real Ethertype. */ + flow->vlan_tpid = flow->dl_type; 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)); + flow->vlan_count = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN) + ? 1: 0); if (!is_mask) { if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) { return ODP_FIT_TOO_LITTLE; @@ -3378,7 +3381,7 @@ 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)) { + : (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); } @@ -3568,24 +3571,42 @@ pop_vlan(struct flow *base, } } +/* This function determines and commits the VLAN action. + * + * POP VLAN: + * - Number of stacked VLANs is more in base flow than current flow. + * - Same number of stacked VLANs but difference in VLAN tci. + * + * PUSH VLAN: Number of stacked VLANs in current flow greater than + * or equal to base flow. + * + * Neither PUSH nor POP: Base and current flow has same vlan tci and count + */ static void -commit_vlan_action(ovs_be16 vlan_tci, struct flow *base, +commit_vlan_action(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { - if (base->vlan_tci == vlan_tci) { + if ((base->vlan_tci == flow->vlan_tci) && + (base->vlan_count == flow->vlan_count)) { return; } - pop_vlan(base, odp_actions, wc); - if (vlan_tci & htons(VLAN_CFI)) { + if (base->vlan_count > flow->vlan_count || + (base->vlan_tci != flow->vlan_tci && base->vlan_count == flow->vlan_count)) + pop_vlan(base, odp_actions, wc); + + if (flow->vlan_count >= base->vlan_count && + flow->vlan_tci & htons(VLAN_CFI)) { struct ovs_action_push_vlan vlan; - vlan.vlan_tpid = htons(ETH_TYPE_VLAN); - vlan.vlan_tci = vlan_tci; + vlan.vlan_tpid = get_vlan_tpid(flow->vlan_count); + vlan.vlan_tci = flow->vlan_tci; nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, &vlan, sizeof vlan); } - base->vlan_tci = vlan_tci; + base->vlan_tci = flow->vlan_tci; + base->vlan_tpid = flow->vlan_tpid; + base->vlan_count = flow->vlan_count; } static void @@ -3885,7 +3906,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base, slow = commit_set_nw_action(flow, base, odp_actions, wc); commit_set_port_action(flow, base, odp_actions, wc); commit_mpls_action(flow, base, odp_actions, wc); - 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); commit_set_pkt_mark_action(flow, base, odp_actions, wc); diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 09e4438..9037fd4 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -132,7 +132,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 == 26); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); diff --git a/lib/packets.c b/lib/packets.c index 6244c3f..ef1aebe 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -199,7 +199,7 @@ eth_pop_vlan(struct ofpbuf *packet) struct vlan_eth_header *veh = ofpbuf_l2(packet); if (veh && ofpbuf_size(packet) >= sizeof *veh - && veh->veth_type == htons(ETH_TYPE_VLAN)) { + && (eth_type_vlan(veh->veth_type))) { memmove((char *)veh + VLAN_HEADER_LEN, veh, 2 * ETH_ADDR_LEN); ofpbuf_resize_l2(packet, -VLAN_HEADER_LEN); diff --git a/lib/packets.h b/lib/packets.h index 4575dd0..0d175f4 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -249,6 +249,22 @@ 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) || + eth_type == htons(ETH_TYPE_VLAN_8021AD); +} + +/* + * Returns vlan tpid in network byte order considering vlan count + */ +static inline ovs_be16 get_vlan_tpid(uint8_t vlan_count) +{ + return (vlan_count >= 2) + ? htons(ETH_TYPE_VLAN_8021AD) + : htons(ETH_TYPE_VLAN); +} + /* Minimum value for an Ethernet type. Values below this are IEEE 802.2 frame * lengths. */ #define ETH_TYPE_MIN 0x600 -- 1.9.0 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev