IPv6: fix IPV6_RECVERR handling of locally-generated errors
Subsystems (2)
PATCH 1/1 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.
net/ipv6/datagram.c +6 -2
@@ -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);
@@ -358,7 +362,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)
@@ -380,7 +384,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);