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

net: ensure unbound datagram socket to be chosen when not in a VRF

Ensure an unbound datagram skt is chosen when not in a VRF. The check
for a device match in compute_score() for UDP must be performed when
there is no device match. For this, a failure is returned when there is
no device match. This ensures that bound sockets are never selected,
even if there is no unbound socket.

Allow IPv6 packets to be sent over a datagram skt bound to a VRF. These
packets are currently blocked, as flowi6_oif was set to that of the
master vrf device, and the ipi6_ifindex is that of the slave device.
Allow these packets to be sent by checking the device with ipi6_ifindex
has the same L3 scope as that of the bound device of the skt, which is
the master vrf device. Note that this check always succeeds if the skt
is unbound.

Even though the right datagram skt is now selected by compute_score(),
a different skt is being returned that is bound to the wrong vrf. The
difference between these and stream sockets is the handling of the skt
option for SO_REUSEPORT. While the handling when adding a skt for reuse
correctly checks that the bound device of the skt is a match, the skts
in the hashslot are already incorrect. So for the same hash, a skt for
the wrong vrf may be selected for the required port. The root cause is
that the skt is immediately placed into a slot when it is created,
but when the skt is then bound using SO_BINDTODEVICE, it remains in the
same slot. The solution is to move the skt to the correct slot by
forcing a rehash.

Signed-off-by: Mike Manning <mmanning@vyatta.att-mail.com>
Reviewed-by: David Ahern <dsahern@gmail.com>
Tested-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Mike Manning and committed by
David S. Miller
6da5b0f0 e7819058

+31 -21
+11
include/net/udp.h
··· 252 252 return sk_rmem_alloc_get(sk) - READ_ONCE(udp_sk(sk)->forward_deficit); 253 253 } 254 254 255 + static inline bool udp_sk_bound_dev_eq(struct net *net, int bound_dev_if, 256 + int dif, int sdif) 257 + { 258 + #if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) 259 + return inet_bound_dev_eq(!!net->ipv4.sysctl_udp_l3mdev_accept, 260 + bound_dev_if, dif, sdif); 261 + #else 262 + return inet_bound_dev_eq(true, bound_dev_if, dif, sdif); 263 + #endif 264 + } 265 + 255 266 /* net/ipv4/udp.c */ 256 267 void udp_destruct_sock(struct sock *sk); 257 268 void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len);
+2
net/core/sock.c
··· 567 567 568 568 lock_sock(sk); 569 569 sk->sk_bound_dev_if = index; 570 + if (sk->sk_prot->rehash) 571 + sk->sk_prot->rehash(sk); 570 572 sk_dst_reset(sk); 571 573 release_sock(sk); 572 574
+6 -9
net/ipv4/udp.c
··· 371 371 { 372 372 int score; 373 373 struct inet_sock *inet; 374 + bool dev_match; 374 375 375 376 if (!net_eq(sock_net(sk), net) || 376 377 udp_sk(sk)->udp_port_hash != hnum || ··· 399 398 score += 4; 400 399 } 401 400 402 - if (sk->sk_bound_dev_if || exact_dif) { 403 - bool dev_match = (sk->sk_bound_dev_if == dif || 404 - sk->sk_bound_dev_if == sdif); 405 - 406 - if (!dev_match) 407 - return -1; 408 - if (sk->sk_bound_dev_if) 409 - score += 4; 410 - } 401 + dev_match = udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if, 402 + dif, sdif); 403 + if (!dev_match) 404 + return -1; 405 + score += 4; 411 406 412 407 if (sk->sk_incoming_cpu == raw_smp_processor_id()) 413 408 score++;
+7 -3
net/ipv6/datagram.c
··· 772 772 case IPV6_2292PKTINFO: 773 773 { 774 774 struct net_device *dev = NULL; 775 + int src_idx; 775 776 776 777 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) { 777 778 err = -EINVAL; ··· 780 779 } 781 780 782 781 src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); 782 + src_idx = src_info->ipi6_ifindex; 783 783 784 - if (src_info->ipi6_ifindex) { 784 + if (src_idx) { 785 785 if (fl6->flowi6_oif && 786 - src_info->ipi6_ifindex != fl6->flowi6_oif) 786 + src_idx != fl6->flowi6_oif && 787 + (sk->sk_bound_dev_if != fl6->flowi6_oif || 788 + !sk_dev_equal_l3scope(sk, src_idx))) 787 789 return -EINVAL; 788 - fl6->flowi6_oif = src_info->ipi6_ifindex; 790 + fl6->flowi6_oif = src_idx; 789 791 } 790 792 791 793 addr_type = __ipv6_addr_type(&src_info->ipi6_addr);
+5 -9
net/ipv6/udp.c
··· 117 117 { 118 118 int score; 119 119 struct inet_sock *inet; 120 + bool dev_match; 120 121 121 122 if (!net_eq(sock_net(sk), net) || 122 123 udp_sk(sk)->udp_port_hash != hnum || ··· 145 144 score++; 146 145 } 147 146 148 - if (sk->sk_bound_dev_if || exact_dif) { 149 - bool dev_match = (sk->sk_bound_dev_if == dif || 150 - sk->sk_bound_dev_if == sdif); 151 - 152 - if (!dev_match) 153 - return -1; 154 - if (sk->sk_bound_dev_if) 155 - score++; 156 - } 147 + dev_match = udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif); 148 + if (!dev_match) 149 + return -1; 150 + score++; 157 151 158 152 if (sk->sk_incoming_cpu == raw_smp_processor_id()) 159 153 score++;