net: ipv6: add second dif to udp socket lookups

Add a second device index, sdif, to udp socket lookups. sdif is the
index for ingress devices enslaved to an l3mdev. It allows the lookups
to consider the enslaved device as well as the L3 domain when searching
for a socket.

Early demux lookups are handled in the next patch as part of INET_MATCH
changes.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David Ahern 2017-08-07 08:44:20 -07:00 committed by David S. Miller
parent 60d9b03141
commit 1801b570dd
4 changed files with 35 additions and 21 deletions

View file

@ -158,6 +158,16 @@ static inline bool inet6_is_jumbogram(const struct sk_buff *skb)
return !!(IP6CB(skb)->flags & IP6SKB_JUMBOGRAM); return !!(IP6CB(skb)->flags & IP6SKB_JUMBOGRAM);
} }
/* can not be used in TCP layer after tcp_v6_fill_cb */
static inline int inet6_sdif(const struct sk_buff *skb)
{
#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
if (skb && ipv6_l3mdev_skb(IP6CB(skb)->flags))
return IP6CB(skb)->iif;
#endif
return 0;
}
/* can not be used in TCP layer after tcp_v6_fill_cb */ /* can not be used in TCP layer after tcp_v6_fill_cb */
static inline bool inet6_exact_dif_match(struct net *net, struct sk_buff *skb) static inline bool inet6_exact_dif_match(struct net *net, struct sk_buff *skb)
{ {

View file

@ -298,7 +298,7 @@ struct sock *udp6_lib_lookup(struct net *net,
struct sock *__udp6_lib_lookup(struct net *net, struct sock *__udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport, const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport, const struct in6_addr *daddr, __be16 dport,
int dif, struct udp_table *tbl, int dif, int sdif, struct udp_table *tbl,
struct sk_buff *skb); struct sk_buff *skb);
struct sock *udp6_lib_lookup_skb(struct sk_buff *skb, struct sock *udp6_lib_lookup_skb(struct sk_buff *skb,
__be16 sport, __be16 dport); __be16 sport, __be16 dport);

View file

@ -53,7 +53,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
req->id.idiag_sport, req->id.idiag_sport,
(struct in6_addr *)req->id.idiag_dst, (struct in6_addr *)req->id.idiag_dst,
req->id.idiag_dport, req->id.idiag_dport,
req->id.idiag_if, tbl, NULL); req->id.idiag_if, 0, tbl, NULL);
#endif #endif
if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) if (sk && !refcount_inc_not_zero(&sk->sk_refcnt))
sk = NULL; sk = NULL;
@ -198,7 +198,7 @@ static int __udp_diag_destroy(struct sk_buff *in_skb,
req->id.idiag_dport, req->id.idiag_dport,
(struct in6_addr *)req->id.idiag_src, (struct in6_addr *)req->id.idiag_src,
req->id.idiag_sport, req->id.idiag_sport,
req->id.idiag_if, tbl, NULL); req->id.idiag_if, 0, tbl, NULL);
} }
#endif #endif
else { else {

View file

@ -129,7 +129,7 @@ static void udp_v6_rehash(struct sock *sk)
static int compute_score(struct sock *sk, struct net *net, static int compute_score(struct sock *sk, struct net *net,
const struct in6_addr *saddr, __be16 sport, const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, unsigned short hnum, const struct in6_addr *daddr, unsigned short hnum,
int dif, bool exact_dif) int dif, int sdif, bool exact_dif)
{ {
int score; int score;
struct inet_sock *inet; struct inet_sock *inet;
@ -161,9 +161,13 @@ static int compute_score(struct sock *sk, struct net *net,
} }
if (sk->sk_bound_dev_if || exact_dif) { if (sk->sk_bound_dev_if || exact_dif) {
if (sk->sk_bound_dev_if != dif) bool dev_match = (sk->sk_bound_dev_if == dif ||
sk->sk_bound_dev_if == sdif);
if (exact_dif && !dev_match)
return -1; return -1;
score++; if (sk->sk_bound_dev_if && dev_match)
score++;
} }
if (sk->sk_incoming_cpu == raw_smp_processor_id()) if (sk->sk_incoming_cpu == raw_smp_processor_id())
@ -175,9 +179,9 @@ static int compute_score(struct sock *sk, struct net *net,
/* called with rcu_read_lock() */ /* called with rcu_read_lock() */
static struct sock *udp6_lib_lookup2(struct net *net, static struct sock *udp6_lib_lookup2(struct net *net,
const struct in6_addr *saddr, __be16 sport, const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, unsigned int hnum, int dif, const struct in6_addr *daddr, unsigned int hnum,
bool exact_dif, struct udp_hslot *hslot2, int dif, int sdif, bool exact_dif,
struct sk_buff *skb) struct udp_hslot *hslot2, struct sk_buff *skb)
{ {
struct sock *sk, *result; struct sock *sk, *result;
int score, badness, matches = 0, reuseport = 0; int score, badness, matches = 0, reuseport = 0;
@ -187,7 +191,7 @@ static struct sock *udp6_lib_lookup2(struct net *net,
badness = -1; badness = -1;
udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) {
score = compute_score(sk, net, saddr, sport, score = compute_score(sk, net, saddr, sport,
daddr, hnum, dif, exact_dif); daddr, hnum, dif, sdif, exact_dif);
if (score > badness) { if (score > badness) {
reuseport = sk->sk_reuseport; reuseport = sk->sk_reuseport;
if (reuseport) { if (reuseport) {
@ -214,10 +218,10 @@ static struct sock *udp6_lib_lookup2(struct net *net,
/* rcu_read_lock() must be held */ /* rcu_read_lock() must be held */
struct sock *__udp6_lib_lookup(struct net *net, struct sock *__udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport, const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport, const struct in6_addr *daddr, __be16 dport,
int dif, struct udp_table *udptable, int dif, int sdif, struct udp_table *udptable,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct sock *sk, *result; struct sock *sk, *result;
unsigned short hnum = ntohs(dport); unsigned short hnum = ntohs(dport);
@ -235,7 +239,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
goto begin; goto begin;
result = udp6_lib_lookup2(net, saddr, sport, result = udp6_lib_lookup2(net, saddr, sport,
daddr, hnum, dif, exact_dif, daddr, hnum, dif, sdif, exact_dif,
hslot2, skb); hslot2, skb);
if (!result) { if (!result) {
unsigned int old_slot2 = slot2; unsigned int old_slot2 = slot2;
@ -250,7 +254,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
goto begin; goto begin;
result = udp6_lib_lookup2(net, saddr, sport, result = udp6_lib_lookup2(net, saddr, sport,
daddr, hnum, dif, daddr, hnum, dif, sdif,
exact_dif, hslot2, exact_dif, hslot2,
skb); skb);
} }
@ -261,7 +265,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
badness = -1; badness = -1;
sk_for_each_rcu(sk, &hslot->head) { sk_for_each_rcu(sk, &hslot->head) {
score = compute_score(sk, net, saddr, sport, daddr, hnum, dif, score = compute_score(sk, net, saddr, sport, daddr, hnum, dif,
exact_dif); sdif, exact_dif);
if (score > badness) { if (score > badness) {
reuseport = sk->sk_reuseport; reuseport = sk->sk_reuseport;
if (reuseport) { if (reuseport) {
@ -294,7 +298,7 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport, return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport,
&iph->daddr, dport, inet6_iif(skb), &iph->daddr, dport, inet6_iif(skb),
udptable, skb); inet6_sdif(skb), udptable, skb);
} }
struct sock *udp6_lib_lookup_skb(struct sk_buff *skb, struct sock *udp6_lib_lookup_skb(struct sk_buff *skb,
@ -304,7 +308,7 @@ struct sock *udp6_lib_lookup_skb(struct sk_buff *skb,
return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport, return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport,
&iph->daddr, dport, inet6_iif(skb), &iph->daddr, dport, inet6_iif(skb),
&udp_table, skb); inet6_sdif(skb), &udp_table, skb);
} }
EXPORT_SYMBOL_GPL(udp6_lib_lookup_skb); EXPORT_SYMBOL_GPL(udp6_lib_lookup_skb);
@ -320,7 +324,7 @@ struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be
struct sock *sk; struct sock *sk;
sk = __udp6_lib_lookup(net, saddr, sport, daddr, dport, sk = __udp6_lib_lookup(net, saddr, sport, daddr, dport,
dif, &udp_table, NULL); dif, 0, &udp_table, NULL);
if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) if (sk && !refcount_inc_not_zero(&sk->sk_refcnt))
sk = NULL; sk = NULL;
return sk; return sk;
@ -501,7 +505,7 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source, sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
inet6_iif(skb), udptable, skb); inet6_iif(skb), 0, udptable, skb);
if (!sk) { if (!sk) {
__ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
ICMP6_MIB_INERRORS); ICMP6_MIB_INERRORS);