diff options
| author | Steffen Klassert <steffen.klassert@secunet.com> | 2011-10-11 01:44:30 +0000 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2011-10-18 23:53:10 -0400 | 
| commit | dd767856a36e00b631d65ebc4bb81b19915532d6 (patch) | |
| tree | 02fda49186e29ffeb47b5683236d6fcbfbc379b5 | |
| parent | 299b0767642a65f0c5446ab6d35e6df0daf43d33 (diff) | |
| download | olio-linux-3.10-dd767856a36e00b631d65ebc4bb81b19915532d6.tar.xz olio-linux-3.10-dd767856a36e00b631d65ebc4bb81b19915532d6.zip  | |
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.c | 56 | 
1 files changed, 54 insertions, 2 deletions
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 49a91c5f562..faae41737fc 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,  EXPORT_SYMBOL(xfrm6_find_1stfragopt); +static int xfrm6_local_dontfrag(struct sk_buff *skb) +{ +	int proto; +	struct sock *sk = skb->sk; + +	if (sk) { +		proto = sk->sk_protocol; + +		if (proto == IPPROTO_UDP || proto == IPPROTO_RAW) +			return inet6_sk(sk)->dontfrag; +	} + +	return 0; +} + +static void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu) +{ +	struct flowi6 fl6; +	struct sock *sk = skb->sk; + +	fl6.flowi6_oif = sk->sk_bound_dev_if; +	ipv6_addr_copy(&fl6.daddr, &ipv6_hdr(skb)->daddr); + +	ipv6_local_rxpmtu(sk, &fl6, mtu); +} + +static void xfrm6_local_error(struct sk_buff *skb, u32 mtu) +{ +	struct flowi6 fl6; +	struct sock *sk = skb->sk; + +	fl6.fl6_dport = inet_sk(sk)->inet_dport; +	ipv6_addr_copy(&fl6.daddr, &ipv6_hdr(skb)->daddr); + +	ipv6_local_error(sk, EMSGSIZE, &fl6, mtu); +} +  static int xfrm6_tunnel_check_size(struct sk_buff *skb)  {  	int mtu, ret = 0; @@ -39,7 +76,13 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)  	if (!skb->local_df && skb->len > mtu) {  		skb->dev = dst->dev; -		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + +		if (xfrm6_local_dontfrag(skb)) +			xfrm6_local_rxpmtu(skb, mtu); +		else if (skb->sk) +			xfrm6_local_error(skb, mtu); +		else +			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);  		ret = -EMSGSIZE;  	} @@ -93,9 +136,18 @@ static int __xfrm6_output(struct sk_buff *skb)  {  	struct dst_entry *dst = skb_dst(skb);  	struct xfrm_state *x = dst->xfrm; +	int mtu = ip6_skb_dst_mtu(skb); + +	if (skb->len > mtu && xfrm6_local_dontfrag(skb)) { +		xfrm6_local_rxpmtu(skb, mtu); +		return -EMSGSIZE; +	} else if (!skb->local_df && skb->len > mtu && skb->sk) { +		xfrm6_local_error(skb, mtu); +		return -EMSGSIZE; +	}  	if ((x && x->props.mode == XFRM_MODE_TUNNEL) && -	    ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || +	    ((skb->len > mtu && !skb_is_gso(skb)) ||  		dst_allfrag(skb_dst(skb)))) {  			return ip6_fragment(skb, x->outer_mode->afinfo->output_finish);  	}  |