From 5f4d087c768fb88ebca05b5f5b6dee59cf63017b Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Wed, 8 Jun 2011 12:23:05 -0700 Subject: [PATCH] datapath: IP fragments should include L4 header in flow length. If we can't parse a header because it is invalid or not present due to fragmentation, we still need to include the length of that header when comparing the flow key. The value of the field will be zero to indicate that header was not present, rather than effectively wildcarding the value. However, this was not done with fragments on flow extract but is effectively done on flow setup. Since the flow length also changes the hash, it caused all fragments to miss the hash table and be sent to useerspace. Reported-by: Ben Pfaff Signed-off-by: Jesse Gross Tested-by: Ben Pfaff --- datapath/flow.c | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/datapath/flow.c b/datapath/flow.c index d181cdef..7bc34bec 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -489,36 +489,36 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, key->ip.nw_proto = nh->protocol; /* Transport layer. */ - if (!(nh->frag_off & htons(IP_MF | IP_OFFSET)) && - !(skb_shinfo(skb)->gso_type & SKB_GSO_UDP)) { - if (key->ip.nw_proto == IPPROTO_TCP) { - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - if (tcphdr_ok(skb)) { - struct tcphdr *tcp = tcp_hdr(skb); - key->ipv4.tp.src = tcp->source; - key->ipv4.tp.dst = tcp->dest; - } - } else if (key->ip.nw_proto == IPPROTO_UDP) { - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - if (udphdr_ok(skb)) { - struct udphdr *udp = udp_hdr(skb); - key->ipv4.tp.src = udp->source; - key->ipv4.tp.dst = udp->dest; - } - } else if (key->ip.nw_proto == IPPROTO_ICMP) { - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - if (icmphdr_ok(skb)) { - struct icmphdr *icmp = icmp_hdr(skb); - /* The ICMP type and code fields use the 16-bit - * transport port fields, so we need to store them - * in 16-bit network byte order. */ - key->ipv4.tp.src = htons(icmp->type); - key->ipv4.tp.dst = htons(icmp->code); - } - } - } else + if ((nh->frag_off & htons(IP_MF | IP_OFFSET)) || + (skb_shinfo(skb)->gso_type & SKB_GSO_UDP)) *is_frag = true; + if (key->ip.nw_proto == IPPROTO_TCP) { + key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); + if (!*is_frag && tcphdr_ok(skb)) { + struct tcphdr *tcp = tcp_hdr(skb); + key->ipv4.tp.src = tcp->source; + key->ipv4.tp.dst = tcp->dest; + } + } else if (key->ip.nw_proto == IPPROTO_UDP) { + key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); + if (!*is_frag && udphdr_ok(skb)) { + struct udphdr *udp = udp_hdr(skb); + key->ipv4.tp.src = udp->source; + key->ipv4.tp.dst = udp->dest; + } + } else if (key->ip.nw_proto == IPPROTO_ICMP) { + key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); + if (!*is_frag && icmphdr_ok(skb)) { + struct icmphdr *icmp = icmp_hdr(skb); + /* The ICMP type and code fields use the 16-bit + * transport port fields, so we need to store them + * in 16-bit network byte order. */ + key->ipv4.tp.src = htons(icmp->type); + key->ipv4.tp.dst = htons(icmp->code); + } + } + } else if (key->eth.type == htons(ETH_P_ARP) && arphdr_ok(skb)) { struct arp_eth_header *arp; -- 2.30.2