diff --git a/net/ipv4/inet_lro.c b/net/ipv4/inet_lro.c index ac3b1d3..eba145b 100644 --- a/net/ipv4/inet_lro.c +++ b/net/ipv4/inet_lro.c @@ -58,9 +58,6 @@ static int lro_tcp_ip_check(struct iphdr if (ntohs(iph->tot_len) != len) return -1; - if (TCP_PAYLOAD_LENGTH(iph, tcph) == 0) - return -1; - if (iph->ihl != IPH_LEN_WO_OPTIONS) return -1; @@ -223,6 +220,11 @@ static void lro_add_packet(struct net_lr lro_add_common(lro_desc, iph, tcph, tcp_data_len); + if (tcp_data_len == 0) { + dev_kfree_skb_any(skb); + return; + } + skb_pull(skb, (skb->len - tcp_data_len)); parent->truesize += skb->truesize; @@ -244,6 +246,11 @@ static void lro_add_frags(struct net_lro lro_add_common(lro_desc, iph, tcph, tcp_data_len); + if (tcp_data_len == 0) { + put_page(skb_frags[0].page); + return; + } + skb->truesize += truesize; skb_frags[0].page_offset += hlen; @@ -338,6 +345,8 @@ static int __lro_proc_skb(struct net_lro struct tcphdr *tcph; u64 flags; int vlan_hdr_len = 0; + int pkt_len; + int trim; if (!lro_mgr->get_skb_header || lro_mgr->get_skb_header(skb, (void *)&iph, (void *)&tcph, @@ -355,6 +364,17 @@ static int __lro_proc_skb(struct net_lro && !test_bit(LRO_F_EXTRACT_VLAN_ID, &lro_mgr->features)) vlan_hdr_len = VLAN_HLEN; + /* strip padding from runts iff the padding is all zero */ + pkt_len = vlan_hdr_len + ntohs(iph->tot_len); + trim = skb->len - pkt_len; + if (trim > 0) { + u8 *pad = skb_tail_pointer(skb) - trim; + if (unlikely(ip_compute_csum(pad, trim) != 0xffff)) { + goto out; + } + skb_trim(skb, skb->len - trim); + } + if (!lro_desc->active) { /* start new lro session */ if (lro_tcp_ip_check(iph, tcph, skb->len - vlan_hdr_len, NULL)) goto out; @@ -368,7 +388,7 @@ static int __lro_proc_skb(struct net_lro if (lro_desc->tcp_next_seq != ntohl(tcph->seq)) goto out2; - if (lro_tcp_ip_check(iph, tcph, skb->len, lro_desc)) + if (lro_tcp_ip_check(iph, tcph, skb->len - vlan_hdr_len, lro_desc)) goto out2; lro_add_packet(lro_desc, skb, iph, tcph); @@ -412,18 +432,20 @@ static struct sk_buff *lro_gen_skb(struc memcpy(skb->data, mac_hdr, hdr_len); - skb_frags = skb_shinfo(skb)->frags; - while (data_len > 0) { - *skb_frags = *frags; - data_len -= frags->size; - skb_frags++; - frags++; - skb_shinfo(skb)->nr_frags++; + if (skb->data_len == 0) { + put_page(frags[0].page); + } else { + skb_frags = skb_shinfo(skb)->frags; + while (data_len > 0) { + *skb_frags = *frags; + data_len -= frags->size; + skb_frags++; + frags++; + skb_shinfo(skb)->nr_frags++; + } + skb_shinfo(skb)->frags[0].page_offset += hdr_len; + skb_shinfo(skb)->frags[0].size -= hdr_len; } - - skb_shinfo(skb)->frags[0].page_offset += hdr_len; - skb_shinfo(skb)->frags[0].size -= hdr_len; - skb->ip_summed = ip_summed; skb->csum = sum; skb->protocol = eth_type_trans(skb, lro_mgr->dev); @@ -445,6 +467,8 @@ static struct sk_buff *__lro_proc_segmen int mac_hdr_len; int hdr_len = LRO_MAX_PG_HLEN; int vlan_hdr_len = 0; + int pkt_len; + int trim; if (!lro_mgr->get_frag_header || lro_mgr->get_frag_header(frags, (void *)&mac_hdr, (void *)&iph, @@ -463,6 +487,19 @@ static struct sk_buff *__lro_proc_segmen if (!lro_desc) goto out1; + /* strip padding from runts iff the padding is all zero */ + pkt_len = mac_hdr_len + ntohs(iph->tot_len); + trim = len - pkt_len; + if (trim > 0 && pkt_len <= frags->size) { + u8 *pad = page_address(frags->page) + frags->page_offset + + pkt_len; + if (unlikely(ip_compute_csum(pad, trim) != 0xffff)) + goto out1; + frags->size -= trim; + len -= trim; + true_size -= trim; + } + if (!lro_desc->active) { /* start new lro session */ if (lro_tcp_ip_check(iph, tcph, len - mac_hdr_len, NULL)) goto out1;