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>
This commit is contained in:
parent
7df9c43fbe
commit
d40a4de0be
1 changed files with 6 additions and 2 deletions
|
@ -222,6 +222,8 @@ void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
|
|||
if (!skb)
|
||||
return;
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
|
||||
serr = SKB_EXT_ERR(skb);
|
||||
serr->ee.ee_errno = err;
|
||||
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)
|
|||
if (!skb)
|
||||
return;
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
|
||||
skb_put(skb, sizeof(struct ipv6hdr));
|
||||
skb_reset_network_header(skb);
|
||||
iph = ipv6_hdr(skb);
|
||||
|
@ -319,7 +323,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
|
|||
sin->sin6_flowinfo = 0;
|
||||
sin->sin6_port = serr->port;
|
||||
sin->sin6_scope_id = 0;
|
||||
if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
|
||||
if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
ipv6_addr_copy(&sin->sin6_addr,
|
||||
(struct in6_addr *)(nh + serr->addr_offset));
|
||||
if (np->sndflow)
|
||||
|
@ -341,7 +345,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
|
|||
sin->sin6_family = AF_INET6;
|
||||
sin->sin6_flowinfo = 0;
|
||||
sin->sin6_scope_id = 0;
|
||||
if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
|
||||
if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
ipv6_addr_copy(&sin->sin6_addr, &ipv6_hdr(skb)->saddr);
|
||||
if (np->rxopt.all)
|
||||
datagram_recv_ctl(sk, msg, skb);
|
||||
|
|
Loading…
Reference in a new issue