Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfrm6: Don't call icmpv6_send on local error

Calling icmpv6_send() on a local message size error leads to
an incorrect update of the path mtu. So use xfrm6_local_rxpmtu()
to notify about the pmtu if the IPV6_DONTFRAG socket option is
set on an udp or raw socket, according RFC 3542 and use
ipv6_local_error() otherwise.

Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Steffen Klassert and committed by
David S. Miller
dd767856 299b0767

+54 -2
+54 -2
net/ipv6/xfrm6_output.c
··· 28 28 29 29 EXPORT_SYMBOL(xfrm6_find_1stfragopt); 30 30 31 + static int xfrm6_local_dontfrag(struct sk_buff *skb) 32 + { 33 + int proto; 34 + struct sock *sk = skb->sk; 35 + 36 + if (sk) { 37 + proto = sk->sk_protocol; 38 + 39 + if (proto == IPPROTO_UDP || proto == IPPROTO_RAW) 40 + return inet6_sk(sk)->dontfrag; 41 + } 42 + 43 + return 0; 44 + } 45 + 46 + static void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu) 47 + { 48 + struct flowi6 fl6; 49 + struct sock *sk = skb->sk; 50 + 51 + fl6.flowi6_oif = sk->sk_bound_dev_if; 52 + ipv6_addr_copy(&fl6.daddr, &ipv6_hdr(skb)->daddr); 53 + 54 + ipv6_local_rxpmtu(sk, &fl6, mtu); 55 + } 56 + 57 + static void xfrm6_local_error(struct sk_buff *skb, u32 mtu) 58 + { 59 + struct flowi6 fl6; 60 + struct sock *sk = skb->sk; 61 + 62 + fl6.fl6_dport = inet_sk(sk)->inet_dport; 63 + ipv6_addr_copy(&fl6.daddr, &ipv6_hdr(skb)->daddr); 64 + 65 + ipv6_local_error(sk, EMSGSIZE, &fl6, mtu); 66 + } 67 + 31 68 static int xfrm6_tunnel_check_size(struct sk_buff *skb) 32 69 { 33 70 int mtu, ret = 0; ··· 76 39 77 40 if (!skb->local_df && skb->len > mtu) { 78 41 skb->dev = dst->dev; 79 - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); 42 + 43 + if (xfrm6_local_dontfrag(skb)) 44 + xfrm6_local_rxpmtu(skb, mtu); 45 + else if (skb->sk) 46 + xfrm6_local_error(skb, mtu); 47 + else 48 + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); 80 49 ret = -EMSGSIZE; 81 50 } 82 51 ··· 136 93 { 137 94 struct dst_entry *dst = skb_dst(skb); 138 95 struct xfrm_state *x = dst->xfrm; 96 + int mtu = ip6_skb_dst_mtu(skb); 97 + 98 + if (skb->len > mtu && xfrm6_local_dontfrag(skb)) { 99 + xfrm6_local_rxpmtu(skb, mtu); 100 + return -EMSGSIZE; 101 + } else if (!skb->local_df && skb->len > mtu && skb->sk) { 102 + xfrm6_local_error(skb, mtu); 103 + return -EMSGSIZE; 104 + } 139 105 140 106 if ((x && x->props.mode == XFRM_MODE_TUNNEL) && 141 - ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || 107 + ((skb->len > mtu && !skb_is_gso(skb)) || 142 108 dst_allfrag(skb_dst(skb)))) { 143 109 return ip6_fragment(skb, x->outer_mode->afinfo->output_finish); 144 110 }