diff options
Diffstat (limited to 'net/ipv6/xfrm6_output.c')
| -rw-r--r-- | net/ipv6/xfrm6_output.c | 16 | 
1 files changed, 16 insertions, 0 deletions
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index d0d280077721..ad07904642ca 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -45,6 +45,19 @@ static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buf  	return xfrm_output(sk, skb);  } +static int xfrm6_noneed_fragment(struct sk_buff *skb) +{ +	struct frag_hdr *fh; +	u8 prevhdr = ipv6_hdr(skb)->nexthdr; + +	if (prevhdr != NEXTHDR_FRAGMENT) +		return 0; +	fh = (struct frag_hdr *)(skb->data + sizeof(struct ipv6hdr)); +	if (fh->nexthdr == NEXTHDR_ESP || fh->nexthdr == NEXTHDR_AUTH) +		return 1; +	return 0; +} +  static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)  {  	struct dst_entry *dst = skb_dst(skb); @@ -73,6 +86,9 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)  		xfrm6_local_rxpmtu(skb, mtu);  		kfree_skb(skb);  		return -EMSGSIZE; +	} else if (toobig && xfrm6_noneed_fragment(skb)) { +		skb->ignore_df = 1; +		goto skip_frag;  	} else if (!skb->ignore_df && toobig && skb->sk) {  		xfrm_local_error(skb, mtu);  		kfree_skb(skb);  |