Re: [PATCH net-next V2 1/5] flow_dissector: For stripped vlan, get vlan info from skb->vlan_tci
From: Jiri Pirko <jiri@resnulli.us>
Date: 2016-08-17 13:03:01
Wed, Aug 17, 2016 at 01:05:33PM CEST, hadarh@dev.mellanox.co.il wrote:
On Wed, Aug 17, 2016 at 1:46 PM, Jiri Pirko [off-list ref] wrote:quoted
Wed, Aug 17, 2016 at 12:36:10PM CEST, hadarh@mellanox.com wrote:quoted
Early in the datapath skb_vlan_untag function is called, stripped the vlan from the skb and set skb->vlan_tci and skb->vlan_proto fields. The current dissection doesn't handle stripped vlan packets correctly. In some flows, vlan doesn't exist in skb->data anymore when applying flow dissection on the skb, fix that. In case vlan info wasn't stripped before applying flow_dissector (RPS flow for example), or in case of skb with multiple vlans (e.g. 802.1ad), get the vlan info from skb->data. The flow_dissector correctly skips any number of vlans and stores only the first level vlan. Fixes: 0744dd00c1b1 ('net: introduce skb_flow_dissect()') Signed-off-by: Hadar Hen Zion <redacted> --- net/core/flow_dissector.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-)diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 91028ae..362d693 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c@@ -119,12 +119,14 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_dissector_key_ports *key_ports; struct flow_dissector_key_tags *key_tags; struct flow_dissector_key_keyid *key_keyid; + bool skip_vlan = false; u8 ip_proto = 0; bool ret = false; if (!data) { data = skb->data; - proto = skb->protocol; + proto = skb_vlan_tag_present(skb) ? + skb->vlan_proto : skb->protocol; nhoff = skb_network_offset(skb); hlen = skb_headlen(skb); }@@ -243,23 +245,39 @@ ipv6: case htons(ETH_P_8021AD): case htons(ETH_P_8021Q): { const struct vlan_hdr *vlan; - struct vlan_hdr _vlan; - vlan = __skb_header_pointer(skb, nhoff, sizeof(_vlan), data, hlen, &_vlan); - if (!vlan) - goto out_bad; + if (skb_vlan_tag_present(skb)) + proto = skb->protocol; + + if (!skb_vlan_tag_present(skb) || + proto == cpu_to_be16(ETH_P_8021Q) || + proto == cpu_to_be16(ETH_P_8021AD)) {How this can happen? Could you give me an example?This can happen in 2 cases: 1. vlan wasn't stripped yet from the skb. In RPS flow for example, get_rps_cpu function is using flow-dissector before vlan_untag is called by __netif_receive_skb_core.
right, sigh...
2. skb with multiple vlan tags. Only the first vlan is stripped while the inner vlans are still in skb->data. In this case skb->vlan_proto is 802.1AD and skb->protocol is 802.1Q (for example) so I have to take the next header from skb->data.
Hmm I think that whoever removes the outermost vlan from skb->vlan_* should strip the next header into skb->vlan_*