diff options
author | Brian Haley <brian.haley@hp.com> | 2010-05-03 11:44:27 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-05-06 00:32:40 -0400 |
commit | d40a4de0be08f005814a4fddac748fe5353208ec (patch) | |
tree | 82b5bf934623d92ec178ec62339e4ff29f23c25b /net | |
parent | 7df9c43fbe470628a755dfd028e58fdd7ab9b44e (diff) |
IPv6: fix IPV6_RECVERR handling of locally-generated errors
I noticed when I added support for IPV6_DONTFRAG that if you set
IPV6_RECVERR and tried to send a UDP packet larger than 64K to an
IPv6 destination, you'd correctly get an EMSGSIZE, but reading from
MSG_ERRQUEUE returned the incorrect address in the cmsg:
struct msghdr:
msg_name 0x7fff8f3c96d0
msg_namelen 28
struct sockaddr_in6:
sin6_family 10
sin6_port 7639
sin6_flowinfo 0
sin6_addr ::ffff:38.32.0.0
sin6_scope_id 0 ((null))
It should have returned this in my case:
struct msghdr:
msg_name 0x7fffd866b510
msg_namelen 28
struct sockaddr_in6:
sin6_family 10
sin6_port 7639
sin6_flowinfo 0
sin6_addr 2620:0:a09:e000:21f:29ff:fe57:f88b
sin6_scope_id 0 ((null))
The problem is that ipv6_recv_error() assumes that if the error
wasn't generated by ICMPv6, it's an IPv4 address sitting there,
and proceeds to create a v4-mapped address from it.
Change ipv6_icmp_error() and ipv6_local_error() to set skb->protocol
to htons(ETH_P_IPV6) so that ipv6_recv_error() knows the address
sitting right after the extended error is IPv6, else it will
incorrectly map the first octet into an IPv4-mapped IPv6 address
in the cmsg structure returned in a recvmsg() call to obtain
the error.
Signed-off-by: Brian Haley <brian.haley@hp.com>
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/datagram.c | 8 |
1 files changed, 6 insertions, 2 deletions
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 622dc7939a1b..61573885e451 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c | |||
@@ -222,6 +222,8 @@ void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, | |||
222 | if (!skb) | 222 | if (!skb) |
223 | return; | 223 | return; |
224 | 224 | ||
225 | skb->protocol = htons(ETH_P_IPV6); | ||
226 | |||
225 | serr = SKB_EXT_ERR(skb); | 227 | serr = SKB_EXT_ERR(skb); |
226 | serr->ee.ee_errno = err; | 228 | serr->ee.ee_errno = err; |
227 | serr->ee.ee_origin = SO_EE_ORIGIN_ICMP6; | 229 | serr->ee.ee_origin = SO_EE_ORIGIN_ICMP6; |
@@ -255,6 +257,8 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info) | |||
255 | if (!skb) | 257 | if (!skb) |
256 | return; | 258 | return; |
257 | 259 | ||
260 | skb->protocol = htons(ETH_P_IPV6); | ||
261 | |||
258 | skb_put(skb, sizeof(struct ipv6hdr)); | 262 | skb_put(skb, sizeof(struct ipv6hdr)); |
259 | skb_reset_network_header(skb); | 263 | skb_reset_network_header(skb); |
260 | iph = ipv6_hdr(skb); | 264 | iph = ipv6_hdr(skb); |
@@ -319,7 +323,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) | |||
319 | sin->sin6_flowinfo = 0; | 323 | sin->sin6_flowinfo = 0; |
320 | sin->sin6_port = serr->port; | 324 | sin->sin6_port = serr->port; |
321 | sin->sin6_scope_id = 0; | 325 | sin->sin6_scope_id = 0; |
322 | if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) { | 326 | if (skb->protocol == htons(ETH_P_IPV6)) { |
323 | ipv6_addr_copy(&sin->sin6_addr, | 327 | ipv6_addr_copy(&sin->sin6_addr, |
324 | (struct in6_addr *)(nh + serr->addr_offset)); | 328 | (struct in6_addr *)(nh + serr->addr_offset)); |
325 | if (np->sndflow) | 329 | if (np->sndflow) |
@@ -341,7 +345,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) | |||
341 | sin->sin6_family = AF_INET6; | 345 | sin->sin6_family = AF_INET6; |
342 | sin->sin6_flowinfo = 0; | 346 | sin->sin6_flowinfo = 0; |
343 | sin->sin6_scope_id = 0; | 347 | sin->sin6_scope_id = 0; |
344 | if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) { | 348 | if (skb->protocol == htons(ETH_P_IPV6)) { |
345 | ipv6_addr_copy(&sin->sin6_addr, &ipv6_hdr(skb)->saddr); | 349 | ipv6_addr_copy(&sin->sin6_addr, &ipv6_hdr(skb)->saddr); |
346 | if (np->rxopt.all) | 350 | if (np->rxopt.all) |
347 | datagram_recv_ctl(sk, msg, skb); | 351 | datagram_recv_ctl(sk, msg, skb); |