diff options
Diffstat (limited to 'net/core/dev.c')
| -rw-r--r-- | net/core/dev.c | 18 | 
1 files changed, 13 insertions, 5 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index cd479f5f22f6..8453e14d301b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3512,7 +3512,7 @@ static netdev_features_t gso_features_check(const struct sk_buff *skb,  	if (gso_segs > READ_ONCE(dev->gso_max_segs))  		return features & ~NETIF_F_GSO_MASK; -	if (unlikely(skb->len >= READ_ONCE(dev->gso_max_size))) +	if (unlikely(skb->len >= netif_get_gso_max_size(dev, skb)))  		return features & ~NETIF_F_GSO_MASK;  	if (!skb_shinfo(skb)->gso_type) { @@ -3639,6 +3639,9 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,  		return 0;  	if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { +		if (vlan_get_protocol(skb) == htons(ETH_P_IPV6) && +		    skb_network_header_len(skb) != sizeof(struct ipv6hdr)) +			goto sw_checksum;  		switch (skb->csum_offset) {  		case offsetof(struct tcphdr, check):  		case offsetof(struct udphdr, check): @@ -3646,6 +3649,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,  		}  	} +sw_checksum:  	return skb_checksum_help(skb);  }  EXPORT_SYMBOL(skb_csum_hwoffload_help); @@ -3758,7 +3762,7 @@ static void qdisc_pkt_len_init(struct sk_buff *skb)  						sizeof(_tcphdr), &_tcphdr);  			if (likely(th))  				hdr_len += __tcp_hdrlen(th); -		} else { +		} else if (shinfo->gso_type & SKB_GSO_UDP_L4) {  			struct udphdr _udphdr;  			if (skb_header_pointer(skb, hdr_len, @@ -3766,10 +3770,14 @@ static void qdisc_pkt_len_init(struct sk_buff *skb)  				hdr_len += sizeof(struct udphdr);  		} -		if (shinfo->gso_type & SKB_GSO_DODGY) -			gso_segs = DIV_ROUND_UP(skb->len - hdr_len, -						shinfo->gso_size); +		if (unlikely(shinfo->gso_type & SKB_GSO_DODGY)) { +			int payload = skb->len - hdr_len; +			/* Malicious packet. */ +			if (payload <= 0) +				return; +			gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size); +		}  		qdisc_skb_cb(skb)->pkt_len += (gso_segs - 1) * hdr_len;  	}  }  | 
