Skip to content

Commit 6da5b0f

Browse files
Mike Manningdavem330
authored andcommitted
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>
1 parent e781905 commit 6da5b0f

File tree

5 files changed

+31
-21
lines changed

5 files changed

+31
-21
lines changed

include/net/udp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,17 @@ static inline int udp_rqueue_get(struct sock *sk)
252252
return sk_rmem_alloc_get(sk) - READ_ONCE(udp_sk(sk)->forward_deficit);
253253
}
254254

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+
255266
/* net/ipv4/udp.c */
256267
void udp_destruct_sock(struct sock *sk);
257268
void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len);

net/core/sock.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,8 @@ static int sock_setbindtodevice(struct sock *sk, char __user *optval,
567567

568568
lock_sock(sk);
569569
sk->sk_bound_dev_if = index;
570+
if (sk->sk_prot->rehash)
571+
sk->sk_prot->rehash(sk);
570572
sk_dst_reset(sk);
571573
release_sock(sk);
572574

net/ipv4/udp.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ static int compute_score(struct sock *sk, struct net *net,
371371
{
372372
int score;
373373
struct inet_sock *inet;
374+
bool dev_match;
374375

375376
if (!net_eq(sock_net(sk), net) ||
376377
udp_sk(sk)->udp_port_hash != hnum ||
@@ -398,15 +399,11 @@ static int compute_score(struct sock *sk, struct net *net,
398399
score += 4;
399400
}
400401

401-
if (sk->sk_bound_dev_if || exact_dif) {
402-
bool dev_match = (sk->sk_bound_dev_if == dif ||
403-
sk->sk_bound_dev_if == sdif);
404-
405-
if (!dev_match)
406-
return -1;
407-
if (sk->sk_bound_dev_if)
408-
score += 4;
409-
}
402+
dev_match = udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
403+
dif, sdif);
404+
if (!dev_match)
405+
return -1;
406+
score += 4;
410407

411408
if (sk->sk_incoming_cpu == raw_smp_processor_id())
412409
score++;

net/ipv6/datagram.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -772,19 +772,23 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
772772
case IPV6_2292PKTINFO:
773773
{
774774
struct net_device *dev = NULL;
775+
int src_idx;
775776

776777
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
777778
err = -EINVAL;
778779
goto exit_f;
779780
}
780781

781782
src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
783+
src_idx = src_info->ipi6_ifindex;
782784

783-
if (src_info->ipi6_ifindex) {
785+
if (src_idx) {
784786
if (fl6->flowi6_oif &&
785-
src_info->ipi6_ifindex != fl6->flowi6_oif)
787+
src_idx != fl6->flowi6_oif &&
788+
(sk->sk_bound_dev_if != fl6->flowi6_oif ||
789+
!sk_dev_equal_l3scope(sk, src_idx)))
786790
return -EINVAL;
787-
fl6->flowi6_oif = src_info->ipi6_ifindex;
791+
fl6->flowi6_oif = src_idx;
788792
}
789793

790794
addr_type = __ipv6_addr_type(&src_info->ipi6_addr);

net/ipv6/udp.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ static int compute_score(struct sock *sk, struct net *net,
117117
{
118118
int score;
119119
struct inet_sock *inet;
120+
bool dev_match;
120121

121122
if (!net_eq(sock_net(sk), net) ||
122123
udp_sk(sk)->udp_port_hash != hnum ||
@@ -144,15 +145,10 @@ static int compute_score(struct sock *sk, struct net *net,
144145
score++;
145146
}
146147

147-
if (sk->sk_bound_dev_if || exact_dif) {
148-
bool dev_match = (sk->sk_bound_dev_if == dif ||
149-
sk->sk_bound_dev_if == sdif);
150-
151-
if (!dev_match)
152-
return -1;
153-
if (sk->sk_bound_dev_if)
154-
score++;
155-
}
148+
dev_match = udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif);
149+
if (!dev_match)
150+
return -1;
151+
score++;
156152

157153
if (sk->sk_incoming_cpu == raw_smp_processor_id())
158154
score++;

0 commit comments

Comments
 (0)