aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteffen Klassert <steffen.klassert@secunet.com>2011-10-10 21:44:30 -0400
committerDavid S. Miller <davem@davemloft.net>2011-10-18 23:53:10 -0400
commitdd767856a36e00b631d65ebc4bb81b19915532d6 (patch)
tree02fda49186e29ffeb47b5683236d6fcbfbc379b5
parent299b0767642a65f0c5446ab6d35e6df0daf43d33 (diff)
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>
-rw-r--r--net/ipv6/xfrm6_output.c56
1 files changed, 54 insertions, 2 deletions
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 49a91c5f5623..faae41737fca 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -28,6 +28,43 @@ int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
28 28
29EXPORT_SYMBOL(xfrm6_find_1stfragopt); 29EXPORT_SYMBOL(xfrm6_find_1stfragopt);
30 30
31static 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
46static 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
57static 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
31static int xfrm6_tunnel_check_size(struct sk_buff *skb) 68static int xfrm6_tunnel_check_size(struct sk_buff *skb)
32{ 69{
33 int mtu, ret = 0; 70 int mtu, ret = 0;
@@ -39,7 +76,13 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
39 76
40 if (!skb->local_df && skb->len > mtu) { 77 if (!skb->local_df && skb->len > mtu) {
41 skb->dev = dst->dev; 78 skb->dev = dst->dev;
42 icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); 79
80 if (xfrm6_local_dontfrag(skb))
81 xfrm6_local_rxpmtu(skb, mtu);
82 else if (skb->sk)
83 xfrm6_local_error(skb, mtu);
84 else
85 icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
43 ret = -EMSGSIZE; 86 ret = -EMSGSIZE;
44 } 87 }
45 88
@@ -93,9 +136,18 @@ static int __xfrm6_output(struct sk_buff *skb)
93{ 136{
94 struct dst_entry *dst = skb_dst(skb); 137 struct dst_entry *dst = skb_dst(skb);
95 struct xfrm_state *x = dst->xfrm; 138 struct xfrm_state *x = dst->xfrm;
139 int mtu = ip6_skb_dst_mtu(skb);
140
141 if (skb->len > mtu && xfrm6_local_dontfrag(skb)) {
142 xfrm6_local_rxpmtu(skb, mtu);
143 return -EMSGSIZE;
144 } else if (!skb->local_df && skb->len > mtu && skb->sk) {
145 xfrm6_local_error(skb, mtu);
146 return -EMSGSIZE;
147 }
96 148
97 if ((x && x->props.mode == XFRM_MODE_TUNNEL) && 149 if ((x && x->props.mode == XFRM_MODE_TUNNEL) &&
98 ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || 150 ((skb->len > mtu && !skb_is_gso(skb)) ||
99 dst_allfrag(skb_dst(skb)))) { 151 dst_allfrag(skb_dst(skb)))) {
100 return ip6_fragment(skb, x->outer_mode->afinfo->output_finish); 152 return ip6_fragment(skb, x->outer_mode->afinfo->output_finish);
101 } 153 }