Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: honor IPV6_PKTINFO with v4 mapped addresses on sendmsg

In case we decide in udp6_sendmsg to send the packet down the ipv4
udp_sendmsg path because the destination is either of family AF_INET or
the destination is an ipv4 mapped ipv6 address, we don't honor the
maybe specified ipv4 mapped ipv6 address in IPV6_PKTINFO.

We simply can check for this option in ip_cmsg_send because no calls to
ipv6 module functions are needed to do so.

Reported-by: Gert Doering <gert@space.net>
Cc: Tore Anderson <tore@fud.no>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Hannes Frederic Sowa and committed by
David S. Miller
c8e6ad08 21f374c6

+24 -5
+2 -1
include/net/ip.h
··· 489 489 490 490 void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb); 491 491 void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb); 492 - int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc); 492 + int ip_cmsg_send(struct net *net, struct msghdr *msg, 493 + struct ipcm_cookie *ipc, bool allow_ipv6); 493 494 int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, 494 495 unsigned int optlen); 495 496 int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
+18 -1
net/ipv4/ip_sockglue.c
··· 186 186 } 187 187 EXPORT_SYMBOL(ip_cmsg_recv); 188 188 189 - int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) 189 + int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, 190 + bool allow_ipv6) 190 191 { 191 192 int err, val; 192 193 struct cmsghdr *cmsg; ··· 195 194 for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { 196 195 if (!CMSG_OK(msg, cmsg)) 197 196 return -EINVAL; 197 + #if defined(CONFIG_IPV6) 198 + if (allow_ipv6 && 199 + cmsg->cmsg_level == SOL_IPV6 && 200 + cmsg->cmsg_type == IPV6_PKTINFO) { 201 + struct in6_pktinfo *src_info; 202 + 203 + if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info))) 204 + return -EINVAL; 205 + src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 206 + if (!ipv6_addr_v4mapped(&src_info->ipi6_addr)) 207 + return -EINVAL; 208 + ipc->oif = src_info->ipi6_ifindex; 209 + ipc->addr = src_info->ipi6_addr.s6_addr32[3]; 210 + continue; 211 + } 212 + #endif 198 213 if (cmsg->cmsg_level != SOL_IP) 199 214 continue; 200 215 switch (cmsg->cmsg_type) {
+1 -1
net/ipv4/ping.c
··· 727 727 sock_tx_timestamp(sk, &ipc.tx_flags); 728 728 729 729 if (msg->msg_controllen) { 730 - err = ip_cmsg_send(sock_net(sk), msg, &ipc); 730 + err = ip_cmsg_send(sock_net(sk), msg, &ipc, false); 731 731 if (err) 732 732 return err; 733 733 if (ipc.opt)
+1 -1
net/ipv4/raw.c
··· 524 524 ipc.oif = sk->sk_bound_dev_if; 525 525 526 526 if (msg->msg_controllen) { 527 - err = ip_cmsg_send(sock_net(sk), msg, &ipc); 527 + err = ip_cmsg_send(sock_net(sk), msg, &ipc, false); 528 528 if (err) 529 529 goto out; 530 530 if (ipc.opt)
+2 -1
net/ipv4/udp.c
··· 931 931 sock_tx_timestamp(sk, &ipc.tx_flags); 932 932 933 933 if (msg->msg_controllen) { 934 - err = ip_cmsg_send(sock_net(sk), msg, &ipc); 934 + err = ip_cmsg_send(sock_net(sk), msg, &ipc, 935 + sk->sk_family == AF_INET6); 935 936 if (err) 936 937 return err; 937 938 if (ipc.opt)