diff options
| author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2013-08-14 07:05:23 -0400 |
|---|---|---|
| committer | Steffen Klassert <steffen.klassert@secunet.com> | 2013-08-14 07:07:12 -0400 |
| commit | 628e341f319f1a64a4639088faba952e4ec8f0a8 (patch) | |
| tree | 28fd4b1a02ca00a1abe0b1e71898ab8db6a81dad | |
| parent | d9bf5f130946695063469749bfd190087b7fad39 (diff) | |
xfrm: make local error reporting more robust
In xfrm4 and xfrm6 we need to take care about sockets of the other
address family. This could happen because a 6in4 or 4in6 tunnel could
get protected by ipsec.
Because we don't want to have a run-time dependency on ipv6 when only
using ipv4 xfrm we have to embed a pointer to the correct local_error
function in xfrm_state_afinet and look it up when returning an error
depending on the socket address family.
Thanks to vi0ss for the great bug report:
<https://bugzilla.kernel.org/show_bug.cgi?id=58691>
v2:
a) fix two more unsafe interpretations of skb->sk as ipv6 socket
(xfrm6_local_dontfrag and __xfrm6_output)
v3:
a) add an EXPORT_SYMBOL_GPL(xfrm_local_error) to fix a link error when
building ipv6 as a module (thanks to Steffen Klassert)
Reported-by: <vi0oss@gmail.com>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
| -rw-r--r-- | include/net/xfrm.h | 6 | ||||
| -rw-r--r-- | net/ipv4/xfrm4_output.c | 12 | ||||
| -rw-r--r-- | net/ipv4/xfrm4_state.c | 1 | ||||
| -rw-r--r-- | net/ipv6/xfrm6_output.c | 10 | ||||
| -rw-r--r-- | net/ipv6/xfrm6_state.c | 1 | ||||
| -rw-r--r-- | net/xfrm/xfrm_output.c | 13 | ||||
| -rw-r--r-- | net/xfrm/xfrm_state.c | 7 |
7 files changed, 39 insertions, 11 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 94ce082b29dc..e823786e7c66 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h | |||
| @@ -341,10 +341,13 @@ struct xfrm_state_afinfo { | |||
| 341 | struct sk_buff *skb); | 341 | struct sk_buff *skb); |
| 342 | int (*transport_finish)(struct sk_buff *skb, | 342 | int (*transport_finish)(struct sk_buff *skb, |
| 343 | int async); | 343 | int async); |
| 344 | void (*local_error)(struct sk_buff *skb, u32 mtu); | ||
| 344 | }; | 345 | }; |
| 345 | 346 | ||
| 346 | extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo); | 347 | extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo); |
| 347 | extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); | 348 | extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); |
| 349 | extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); | ||
| 350 | extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); | ||
| 348 | 351 | ||
| 349 | extern void xfrm_state_delete_tunnel(struct xfrm_state *x); | 352 | extern void xfrm_state_delete_tunnel(struct xfrm_state *x); |
| 350 | 353 | ||
| @@ -1477,6 +1480,7 @@ extern int xfrm_input_resume(struct sk_buff *skb, int nexthdr); | |||
| 1477 | extern int xfrm_output_resume(struct sk_buff *skb, int err); | 1480 | extern int xfrm_output_resume(struct sk_buff *skb, int err); |
| 1478 | extern int xfrm_output(struct sk_buff *skb); | 1481 | extern int xfrm_output(struct sk_buff *skb); |
| 1479 | extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb); | 1482 | extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb); |
| 1483 | extern void xfrm_local_error(struct sk_buff *skb, int mtu); | ||
| 1480 | extern int xfrm4_extract_header(struct sk_buff *skb); | 1484 | extern int xfrm4_extract_header(struct sk_buff *skb); |
| 1481 | extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb); | 1485 | extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb); |
| 1482 | extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, | 1486 | extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, |
| @@ -1497,6 +1501,7 @@ extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short fam | |||
| 1497 | extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); | 1501 | extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); |
| 1498 | extern int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler); | 1502 | extern int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler); |
| 1499 | extern int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler); | 1503 | extern int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler); |
| 1504 | extern void xfrm4_local_error(struct sk_buff *skb, u32 mtu); | ||
| 1500 | extern int xfrm6_extract_header(struct sk_buff *skb); | 1505 | extern int xfrm6_extract_header(struct sk_buff *skb); |
| 1501 | extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb); | 1506 | extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb); |
| 1502 | extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi); | 1507 | extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi); |
| @@ -1514,6 +1519,7 @@ extern int xfrm6_output(struct sk_buff *skb); | |||
| 1514 | extern int xfrm6_output_finish(struct sk_buff *skb); | 1519 | extern int xfrm6_output_finish(struct sk_buff *skb); |
| 1515 | extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb, | 1520 | extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb, |
| 1516 | u8 **prevhdr); | 1521 | u8 **prevhdr); |
| 1522 | extern void xfrm6_local_error(struct sk_buff *skb, u32 mtu); | ||
| 1517 | 1523 | ||
| 1518 | #ifdef CONFIG_XFRM | 1524 | #ifdef CONFIG_XFRM |
| 1519 | extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb); | 1525 | extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb); |
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 327a617d594c..7a5491ffa4de 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c | |||
| @@ -33,8 +33,7 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb) | |||
| 33 | mtu = dst_mtu(dst); | 33 | mtu = dst_mtu(dst); |
| 34 | if (skb->len > mtu) { | 34 | if (skb->len > mtu) { |
| 35 | if (skb->sk) | 35 | if (skb->sk) |
| 36 | ip_local_error(skb->sk, EMSGSIZE, ip_hdr(skb)->daddr, | 36 | xfrm_local_error(skb, mtu); |
| 37 | inet_sk(skb->sk)->inet_dport, mtu); | ||
| 38 | else | 37 | else |
| 39 | icmp_send(skb, ICMP_DEST_UNREACH, | 38 | icmp_send(skb, ICMP_DEST_UNREACH, |
| 40 | ICMP_FRAG_NEEDED, htonl(mtu)); | 39 | ICMP_FRAG_NEEDED, htonl(mtu)); |
| @@ -99,3 +98,12 @@ int xfrm4_output(struct sk_buff *skb) | |||
| 99 | x->outer_mode->afinfo->output_finish, | 98 | x->outer_mode->afinfo->output_finish, |
| 100 | !(IPCB(skb)->flags & IPSKB_REROUTED)); | 99 | !(IPCB(skb)->flags & IPSKB_REROUTED)); |
| 101 | } | 100 | } |
| 101 | |||
| 102 | void xfrm4_local_error(struct sk_buff *skb, u32 mtu) | ||
| 103 | { | ||
| 104 | struct iphdr *hdr; | ||
| 105 | |||
| 106 | hdr = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); | ||
| 107 | ip_local_error(skb->sk, EMSGSIZE, hdr->daddr, | ||
| 108 | inet_sk(skb->sk)->inet_dport, mtu); | ||
| 109 | } | ||
diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index 9258e751baba..0b2a0641526a 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c | |||
| @@ -83,6 +83,7 @@ static struct xfrm_state_afinfo xfrm4_state_afinfo = { | |||
| 83 | .extract_input = xfrm4_extract_input, | 83 | .extract_input = xfrm4_extract_input, |
| 84 | .extract_output = xfrm4_extract_output, | 84 | .extract_output = xfrm4_extract_output, |
| 85 | .transport_finish = xfrm4_transport_finish, | 85 | .transport_finish = xfrm4_transport_finish, |
| 86 | .local_error = xfrm4_local_error, | ||
| 86 | }; | 87 | }; |
| 87 | 88 | ||
| 88 | void __init xfrm4_state_init(void) | 89 | void __init xfrm4_state_init(void) |
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 8755a3079d0f..b64fff30eb06 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c | |||
| @@ -34,8 +34,10 @@ static int xfrm6_local_dontfrag(struct sk_buff *skb) | |||
| 34 | struct sock *sk = skb->sk; | 34 | struct sock *sk = skb->sk; |
| 35 | 35 | ||
| 36 | if (sk) { | 36 | if (sk) { |
| 37 | proto = sk->sk_protocol; | 37 | if (sk->sk_family != AF_INET6) |
| 38 | return 0; | ||
| 38 | 39 | ||
| 40 | proto = sk->sk_protocol; | ||
| 39 | if (proto == IPPROTO_UDP || proto == IPPROTO_RAW) | 41 | if (proto == IPPROTO_UDP || proto == IPPROTO_RAW) |
| 40 | return inet6_sk(sk)->dontfrag; | 42 | return inet6_sk(sk)->dontfrag; |
| 41 | } | 43 | } |
| @@ -54,7 +56,7 @@ static void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu) | |||
| 54 | ipv6_local_rxpmtu(sk, &fl6, mtu); | 56 | ipv6_local_rxpmtu(sk, &fl6, mtu); |
| 55 | } | 57 | } |
| 56 | 58 | ||
| 57 | static void xfrm6_local_error(struct sk_buff *skb, u32 mtu) | 59 | void xfrm6_local_error(struct sk_buff *skb, u32 mtu) |
| 58 | { | 60 | { |
| 59 | struct flowi6 fl6; | 61 | struct flowi6 fl6; |
| 60 | struct sock *sk = skb->sk; | 62 | struct sock *sk = skb->sk; |
| @@ -80,7 +82,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb) | |||
| 80 | if (xfrm6_local_dontfrag(skb)) | 82 | if (xfrm6_local_dontfrag(skb)) |
| 81 | xfrm6_local_rxpmtu(skb, mtu); | 83 | xfrm6_local_rxpmtu(skb, mtu); |
| 82 | else if (skb->sk) | 84 | else if (skb->sk) |
| 83 | xfrm6_local_error(skb, mtu); | 85 | xfrm_local_error(skb, mtu); |
| 84 | else | 86 | else |
| 85 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); | 87 | icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); |
| 86 | ret = -EMSGSIZE; | 88 | ret = -EMSGSIZE; |
| @@ -142,7 +144,7 @@ static int __xfrm6_output(struct sk_buff *skb) | |||
| 142 | xfrm6_local_rxpmtu(skb, mtu); | 144 | xfrm6_local_rxpmtu(skb, mtu); |
| 143 | return -EMSGSIZE; | 145 | return -EMSGSIZE; |
| 144 | } else if (!skb->local_df && skb->len > mtu && skb->sk) { | 146 | } else if (!skb->local_df && skb->len > mtu && skb->sk) { |
| 145 | xfrm6_local_error(skb, mtu); | 147 | xfrm_local_error(skb, mtu); |
| 146 | return -EMSGSIZE; | 148 | return -EMSGSIZE; |
| 147 | } | 149 | } |
| 148 | 150 | ||
diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index d8c70b8efc24..3fc970135fc6 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c | |||
| @@ -183,6 +183,7 @@ static struct xfrm_state_afinfo xfrm6_state_afinfo = { | |||
| 183 | .extract_input = xfrm6_extract_input, | 183 | .extract_input = xfrm6_extract_input, |
| 184 | .extract_output = xfrm6_extract_output, | 184 | .extract_output = xfrm6_extract_output, |
| 185 | .transport_finish = xfrm6_transport_finish, | 185 | .transport_finish = xfrm6_transport_finish, |
| 186 | .local_error = xfrm6_local_error, | ||
| 186 | }; | 187 | }; |
| 187 | 188 | ||
| 188 | int __init xfrm6_state_init(void) | 189 | int __init xfrm6_state_init(void) |
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index eb4a84288648..6f5fc612b162 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c | |||
| @@ -214,5 +214,18 @@ int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb) | |||
| 214 | return inner_mode->afinfo->extract_output(x, skb); | 214 | return inner_mode->afinfo->extract_output(x, skb); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | void xfrm_local_error(struct sk_buff *skb, int mtu) | ||
| 218 | { | ||
| 219 | struct xfrm_state_afinfo *afinfo; | ||
| 220 | |||
| 221 | afinfo = xfrm_state_get_afinfo(skb->sk->sk_family); | ||
| 222 | if (!afinfo) | ||
| 223 | return; | ||
| 224 | |||
| 225 | afinfo->local_error(skb, mtu); | ||
| 226 | xfrm_state_put_afinfo(afinfo); | ||
| 227 | } | ||
| 228 | |||
| 217 | EXPORT_SYMBOL_GPL(xfrm_output); | 229 | EXPORT_SYMBOL_GPL(xfrm_output); |
| 218 | EXPORT_SYMBOL_GPL(xfrm_inner_extract_output); | 230 | EXPORT_SYMBOL_GPL(xfrm_inner_extract_output); |
| 231 | EXPORT_SYMBOL_GPL(xfrm_local_error); | ||
diff --git a/net/xfrm/xfrm_state.c b/net/xf | |||
