From: Lina Wang <lina.w...@mediatek.com> In tunnel mode, when inner interface is ipv4,outer interface is ipv6, flags of tunnel mode's xfrm state is af-unspec, if a larger packet who is bigger than mtu goes through tunnel interface, it enters ip6_fragment, goes to fail_toobig, and ICMPV6(ICMPV6_PKT_TOOBIG) will be sent. It is unnecessary to do so. Ip6_fragment will fragment such packet with outer interface's mtu minus tunnelled esp header,it wonot be too big.
The same things happen, when a larger fragmented packet whose frag_max_size is larger than mtu. When a larger fragmented packet is forwarded, it also meets the same scenary. This patch has handled three above scenaries, if it is tunnel mode,just ignore skb_len or frag_max_size, keep going. Signed-off-by: Lina Wang <lina.w...@mediatek.com> --- net/ipv6/ip6_output.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index c78e67d7747f..0e1e6fcd7a5d 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -402,12 +402,14 @@ static inline int ip6_forward_finish(struct net *net, struct sock *sk, static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu) { + struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); if (skb->len <= mtu) return false; /* ipv6 conntrack defrag sets max_frag_size + ignore_df */ if (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu) - return true; + if (rt && !(rt->dst.flags & DST_XFRM_TUNNEL)) + return true; if (skb->ignore_df) return false; @@ -787,16 +789,19 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, * or if the skb it not generated by a local socket. */ if (unlikely(!skb->ignore_df && skb->len > mtu)) - goto fail_toobig; - - if (IP6CB(skb)->frag_max_size) { - if (IP6CB(skb)->frag_max_size > mtu) + if (rt && (rt->dst.flags & DST_XFRM_TUNNEL)) goto fail_toobig; - /* don't send fragments larger than what we received */ - mtu = IP6CB(skb)->frag_max_size; - if (mtu < IPV6_MIN_MTU) - mtu = IPV6_MIN_MTU; + if (IP6CB(skb)->frag_max_size) { + if (IP6CB(skb)->frag_max_size > mtu) { + if (rt && !(rt->dst.flags & DST_XFRM_TUNNEL)) + goto fail_toobig; + } else { + /* don't send fragments larger than what we received */ + mtu = IP6CB(skb)->frag_max_size; + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + } } if (np && np->frag_size < mtu) { -- 2.18.0