Commit fe55565e authored by David S. Miller's avatar David S. Miller

Merge branch 'vrf-inetpeer'

David Ahern says:

====================
net: Refactor inetpeer cache and add support for VRFs

Per Dave's comment on the version 1 patch adding VRF support to inetpeer
cache by explicitly making the address + index a key. Refactored the
inetpeer code in the process; mostly impacts the use by tcp_metrics.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8f58336d 192132b9
...@@ -15,16 +15,20 @@ ...@@ -15,16 +15,20 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <linux/atomic.h> #include <linux/atomic.h>
struct inetpeer_addr_base { /* IPv4 address key for cache lookups */
union { struct ipv4_addr_key {
__be32 a4; __be32 addr;
__be32 a6[4]; int vif;
struct in6_addr in6;
};
}; };
#define INETPEER_MAXKEYSZ (sizeof(struct in6_addr) / sizeof(u32))
struct inetpeer_addr { struct inetpeer_addr {
struct inetpeer_addr_base addr; union {
struct ipv4_addr_key a4;
struct in6_addr a6;
u32 key[INETPEER_MAXKEYSZ];
};
__u16 family; __u16 family;
}; };
...@@ -71,6 +75,29 @@ void inet_initpeers(void) __init; ...@@ -71,6 +75,29 @@ void inet_initpeers(void) __init;
#define INETPEER_METRICS_NEW (~(u32) 0) #define INETPEER_METRICS_NEW (~(u32) 0)
static inline void inetpeer_set_addr_v4(struct inetpeer_addr *iaddr, __be32 ip)
{
iaddr->a4.addr = ip;
iaddr->family = AF_INET;
}
static inline __be32 inetpeer_get_addr_v4(struct inetpeer_addr *iaddr)
{
return iaddr->a4.addr;
}
static inline void inetpeer_set_addr_v6(struct inetpeer_addr *iaddr,
struct in6_addr *in6)
{
iaddr->a6 = *in6;
iaddr->family = AF_INET6;
}
static inline struct in6_addr *inetpeer_get_addr_v6(struct inetpeer_addr *iaddr)
{
return &iaddr->a6;
}
/* can be called with or without local BH being disabled */ /* can be called with or without local BH being disabled */
struct inet_peer *inet_getpeer(struct inet_peer_base *base, struct inet_peer *inet_getpeer(struct inet_peer_base *base,
const struct inetpeer_addr *daddr, const struct inetpeer_addr *daddr,
...@@ -78,11 +105,12 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base, ...@@ -78,11 +105,12 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
static inline struct inet_peer *inet_getpeer_v4(struct inet_peer_base *base, static inline struct inet_peer *inet_getpeer_v4(struct inet_peer_base *base,
__be32 v4daddr, __be32 v4daddr,
int create) int vif, int create)
{ {
struct inetpeer_addr daddr; struct inetpeer_addr daddr;
daddr.addr.a4 = v4daddr; daddr.a4.addr = v4daddr;
daddr.a4.vif = vif;
daddr.family = AF_INET; daddr.family = AF_INET;
return inet_getpeer(base, &daddr, create); return inet_getpeer(base, &daddr, create);
} }
...@@ -93,11 +121,32 @@ static inline struct inet_peer *inet_getpeer_v6(struct inet_peer_base *base, ...@@ -93,11 +121,32 @@ static inline struct inet_peer *inet_getpeer_v6(struct inet_peer_base *base,
{ {
struct inetpeer_addr daddr; struct inetpeer_addr daddr;
daddr.addr.in6 = *v6daddr; daddr.a6 = *v6daddr;
daddr.family = AF_INET6; daddr.family = AF_INET6;
return inet_getpeer(base, &daddr, create); return inet_getpeer(base, &daddr, create);
} }
static inline int inetpeer_addr_cmp(const struct inetpeer_addr *a,
const struct inetpeer_addr *b)
{
int i, n;
if (a->family == AF_INET)
n = sizeof(a->a4) / sizeof(u32);
else
n = sizeof(a->a6) / sizeof(u32);
for (i = 0; i < n; i++) {
if (a->key[i] == b->key[i])
continue;
if (a->key[i] < b->key[i])
return -1;
return 1;
}
return 0;
}
/* can be called from BH context or outside */ /* can be called from BH context or outside */
void inet_putpeer(struct inet_peer *p); void inet_putpeer(struct inet_peer *p);
bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout);
......
...@@ -458,6 +458,11 @@ static __inline__ void inet_reset_saddr(struct sock *sk) ...@@ -458,6 +458,11 @@ static __inline__ void inet_reset_saddr(struct sock *sk)
#endif #endif
static inline unsigned int ipv4_addr_hash(__be32 ip)
{
return (__force unsigned int) ip;
}
bool ip_call_ra_chain(struct sk_buff *skb); bool ip_call_ra_chain(struct sk_buff *skb);
/* /*
......
...@@ -309,9 +309,10 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, ...@@ -309,9 +309,10 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
rc = false; rc = false;
if (icmp_global_allow()) { if (icmp_global_allow()) {
int vif = vrf_master_ifindex(dst->dev);
struct inet_peer *peer; struct inet_peer *peer;
peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, 1); peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, vif, 1);
rc = inet_peer_xrlim_allow(peer, rc = inet_peer_xrlim_allow(peer,
net->ipv4.sysctl_icmp_ratelimit); net->ipv4.sysctl_icmp_ratelimit);
if (peer) if (peer)
......
...@@ -157,22 +157,6 @@ void __init inet_initpeers(void) ...@@ -157,22 +157,6 @@ void __init inet_initpeers(void)
INIT_DEFERRABLE_WORK(&gc_work, inetpeer_gc_worker); INIT_DEFERRABLE_WORK(&gc_work, inetpeer_gc_worker);
} }
static int addr_compare(const struct inetpeer_addr *a,
const struct inetpeer_addr *b)
{
int i, n = (a->family == AF_INET ? 1 : 4);
for (i = 0; i < n; i++) {
if (a->addr.a6[i] == b->addr.a6[i])
continue;
if ((__force u32)a->addr.a6[i] < (__force u32)b->addr.a6[i])
return -1;
return 1;
}
return 0;
}
#define rcu_deref_locked(X, BASE) \ #define rcu_deref_locked(X, BASE) \
rcu_dereference_protected(X, lockdep_is_held(&(BASE)->lock.lock)) rcu_dereference_protected(X, lockdep_is_held(&(BASE)->lock.lock))
...@@ -188,7 +172,7 @@ static int addr_compare(const struct inetpeer_addr *a, ...@@ -188,7 +172,7 @@ static int addr_compare(const struct inetpeer_addr *a,
*stackptr++ = &_base->root; \ *stackptr++ = &_base->root; \
for (u = rcu_deref_locked(_base->root, _base); \ for (u = rcu_deref_locked(_base->root, _base); \
u != peer_avl_empty;) { \ u != peer_avl_empty;) { \
int cmp = addr_compare(_daddr, &u->daddr); \ int cmp = inetpeer_addr_cmp(_daddr, &u->daddr); \
if (cmp == 0) \ if (cmp == 0) \
break; \ break; \
if (cmp == -1) \ if (cmp == -1) \
...@@ -215,7 +199,7 @@ static struct inet_peer *lookup_rcu(const struct inetpeer_addr *daddr, ...@@ -215,7 +199,7 @@ static struct inet_peer *lookup_rcu(const struct inetpeer_addr *daddr,
int count = 0; int count = 0;
while (u != peer_avl_empty) { while (u != peer_avl_empty) {
int cmp = addr_compare(daddr, &u->daddr); int cmp = inetpeer_addr_cmp(daddr, &u->daddr);
if (cmp == 0) { if (cmp == 0) {
/* Before taking a reference, check if this entry was /* Before taking a reference, check if this entry was
* deleted (refcnt=-1) * deleted (refcnt=-1)
......
...@@ -151,7 +151,8 @@ static void ip4_frag_init(struct inet_frag_queue *q, const void *a) ...@@ -151,7 +151,8 @@ static void ip4_frag_init(struct inet_frag_queue *q, const void *a)
qp->vif = arg->vif; qp->vif = arg->vif;
qp->user = arg->user; qp->user = arg->user;
qp->peer = sysctl_ipfrag_max_dist ? qp->peer = sysctl_ipfrag_max_dist ?
inet_getpeer_v4(net->ipv4.peers, arg->iph->saddr, 1) : NULL; inet_getpeer_v4(net->ipv4.peers, arg->iph->saddr, arg->vif, 1) :
NULL;
} }
static void ip4_frag_free(struct inet_frag_queue *q) static void ip4_frag_free(struct inet_frag_queue *q)
......
...@@ -838,6 +838,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) ...@@ -838,6 +838,7 @@ void ip_rt_send_redirect(struct sk_buff *skb)
struct inet_peer *peer; struct inet_peer *peer;
struct net *net; struct net *net;
int log_martians; int log_martians;
int vif;
rcu_read_lock(); rcu_read_lock();
in_dev = __in_dev_get_rcu(rt->dst.dev); in_dev = __in_dev_get_rcu(rt->dst.dev);
...@@ -846,10 +847,11 @@ void ip_rt_send_redirect(struct sk_buff *skb) ...@@ -846,10 +847,11 @@ void ip_rt_send_redirect(struct sk_buff *skb)
return; return;
} }
log_martians = IN_DEV_LOG_MARTIANS(in_dev); log_martians = IN_DEV_LOG_MARTIANS(in_dev);
vif = vrf_master_ifindex_rcu(rt->dst.dev);
rcu_read_unlock(); rcu_read_unlock();
net = dev_net(rt->dst.dev); net = dev_net(rt->dst.dev);
peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, 1); peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif, 1);
if (!peer) { if (!peer) {
icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST,
rt_nexthop(rt, ip_hdr(skb)->daddr)); rt_nexthop(rt, ip_hdr(skb)->daddr));
...@@ -938,7 +940,8 @@ static int ip_error(struct sk_buff *skb) ...@@ -938,7 +940,8 @@ static int ip_error(struct sk_buff *skb)
break; break;
} }
peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, 1); peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr,
vrf_master_ifindex(skb->dev), 1);
send = true; send = true;
if (peer) { if (peer) {
......
...@@ -81,11 +81,7 @@ static void tcp_metric_set(struct tcp_metrics_block *tm, ...@@ -81,11 +81,7 @@ static void tcp_metric_set(struct tcp_metrics_block *tm,
static bool addr_same(const struct inetpeer_addr *a, static bool addr_same(const struct inetpeer_addr *a,
const struct inetpeer_addr *b) const struct inetpeer_addr *b)
{ {
if (a->family != b->family) return inetpeer_addr_cmp(a, b) == 0;
return false;
if (a->family == AF_INET)
return a->addr.a4 == b->addr.a4;
return ipv6_addr_equal(&a->addr.in6, &b->addr.in6);
} }
struct tcpm_hash_bucket { struct tcpm_hash_bucket {
...@@ -247,14 +243,14 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, ...@@ -247,14 +243,14 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req,
daddr.family = req->rsk_ops->family; daddr.family = req->rsk_ops->family;
switch (daddr.family) { switch (daddr.family) {
case AF_INET: case AF_INET:
saddr.addr.a4 = inet_rsk(req)->ir_loc_addr; inetpeer_set_addr_v4(&saddr, inet_rsk(req)->ir_loc_addr);
daddr.addr.a4 = inet_rsk(req)->ir_rmt_addr; inetpeer_set_addr_v4(&daddr, inet_rsk(req)->ir_rmt_addr);
hash = (__force unsigned int) daddr.addr.a4; hash = ipv4_addr_hash(inet_rsk(req)->ir_rmt_addr);
break; break;
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
case AF_INET6: case AF_INET6:
saddr.addr.in6 = inet_rsk(req)->ir_v6_loc_addr; inetpeer_set_addr_v6(&saddr, &inet_rsk(req)->ir_v6_loc_addr);
daddr.addr.in6 = inet_rsk(req)->ir_v6_rmt_addr; inetpeer_set_addr_v6(&daddr, &inet_rsk(req)->ir_v6_rmt_addr);
hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr); hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr);
break; break;
#endif #endif
...@@ -285,25 +281,19 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock ...@@ -285,25 +281,19 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock
struct net *net; struct net *net;
if (tw->tw_family == AF_INET) { if (tw->tw_family == AF_INET) {
saddr.family = AF_INET; inetpeer_set_addr_v4(&saddr, tw->tw_rcv_saddr);
saddr.addr.a4 = tw->tw_rcv_saddr; inetpeer_set_addr_v4(&daddr, tw->tw_daddr);
daddr.family = AF_INET; hash = ipv4_addr_hash(tw->tw_daddr);
daddr.addr.a4 = tw->tw_daddr;
hash = (__force unsigned int) daddr.addr.a4;
} }
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
else if (tw->tw_family == AF_INET6) { else if (tw->tw_family == AF_INET6) {
if (ipv6_addr_v4mapped(&tw->tw_v6_daddr)) { if (ipv6_addr_v4mapped(&tw->tw_v6_daddr)) {
saddr.family = AF_INET; inetpeer_set_addr_v4(&saddr, tw->tw_rcv_saddr);
saddr.addr.a4 = tw->tw_rcv_saddr; inetpeer_set_addr_v4(&daddr, tw->tw_daddr);
daddr.family = AF_INET; hash = ipv4_addr_hash(tw->tw_daddr);
daddr.addr.a4 = tw->tw_daddr;
hash = (__force unsigned int) daddr.addr.a4;
} else { } else {
saddr.family = AF_INET6; inetpeer_set_addr_v6(&saddr, &tw->tw_v6_rcv_saddr);
saddr.addr.in6 = tw->tw_v6_rcv_saddr; inetpeer_set_addr_v6(&daddr, &tw->tw_v6_daddr);
daddr.family = AF_INET6;
daddr.addr.in6 = tw->tw_v6_daddr;
hash = ipv6_addr_hash(&tw->tw_v6_daddr); hash = ipv6_addr_hash(&tw->tw_v6_daddr);
} }
} }
...@@ -335,25 +325,19 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, ...@@ -335,25 +325,19 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk,
struct net *net; struct net *net;
if (sk->sk_family == AF_INET) { if (sk->sk_family == AF_INET) {
saddr.family = AF_INET; inetpeer_set_addr_v4(&saddr, inet_sk(sk)->inet_saddr);
saddr.addr.a4 = inet_sk(sk)->inet_saddr; inetpeer_set_addr_v4(&daddr, inet_sk(sk)->inet_daddr);
daddr.family = AF_INET; hash = ipv4_addr_hash(inet_sk(sk)->inet_daddr);
daddr.addr.a4 = inet_sk(sk)->inet_daddr;
hash = (__force unsigned int) daddr.addr.a4;
} }
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
else if (sk->sk_family == AF_INET6) { else if (sk->sk_family == AF_INET6) {
if (ipv6_addr_v4mapped(&sk->sk_v6_daddr)) { if (ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
saddr.family = AF_INET; inetpeer_set_addr_v4(&saddr, inet_sk(sk)->inet_saddr);
saddr.addr.a4 = inet_sk(sk)->inet_saddr; inetpeer_set_addr_v4(&daddr, inet_sk(sk)->inet_daddr);
daddr.family = AF_INET; hash = ipv4_addr_hash(inet_sk(sk)->inet_daddr);
daddr.addr.a4 = inet_sk(sk)->inet_daddr;
hash = (__force unsigned int) daddr.addr.a4;
} else { } else {
saddr.family = AF_INET6; inetpeer_set_addr_v6(&saddr, &sk->sk_v6_rcv_saddr);
saddr.addr.in6 = sk->sk_v6_rcv_saddr; inetpeer_set_addr_v6(&daddr, &sk->sk_v6_daddr);
daddr.family = AF_INET6;
daddr.addr.in6 = sk->sk_v6_daddr;
hash = ipv6_addr_hash(&sk->sk_v6_daddr); hash = ipv6_addr_hash(&sk->sk_v6_daddr);
} }
} }
...@@ -796,18 +780,18 @@ static int tcp_metrics_fill_info(struct sk_buff *msg, ...@@ -796,18 +780,18 @@ static int tcp_metrics_fill_info(struct sk_buff *msg,
switch (tm->tcpm_daddr.family) { switch (tm->tcpm_daddr.family) {
case AF_INET: case AF_INET:
if (nla_put_in_addr(msg, TCP_METRICS_ATTR_ADDR_IPV4, if (nla_put_in_addr(msg, TCP_METRICS_ATTR_ADDR_IPV4,
tm->tcpm_daddr.addr.a4) < 0) inetpeer_get_addr_v4(&tm->tcpm_daddr)) < 0)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_in_addr(msg, TCP_METRICS_ATTR_SADDR_IPV4, if (nla_put_in_addr(msg, TCP_METRICS_ATTR_SADDR_IPV4,
tm->tcpm_saddr.addr.a4) < 0) inetpeer_get_addr_v4(&tm->tcpm_saddr)) < 0)
goto nla_put_failure; goto nla_put_failure;
break; break;
case AF_INET6: case AF_INET6:
if (nla_put_in6_addr(msg, TCP_METRICS_ATTR_ADDR_IPV6, if (nla_put_in6_addr(msg, TCP_METRICS_ATTR_ADDR_IPV6,
&tm->tcpm_daddr.addr.in6) < 0) inetpeer_get_addr_v6(&tm->tcpm_daddr)) < 0)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_in6_addr(msg, TCP_METRICS_ATTR_SADDR_IPV6, if (nla_put_in6_addr(msg, TCP_METRICS_ATTR_SADDR_IPV6,
&tm->tcpm_saddr.addr.in6) < 0) inetpeer_get_addr_v6(&tm->tcpm_saddr)) < 0)
goto nla_put_failure; goto nla_put_failure;
break; break;
default: default:
...@@ -956,20 +940,21 @@ static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, ...@@ -956,20 +940,21 @@ static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr,
a = info->attrs[v4]; a = info->attrs[v4];
if (a) { if (a) {
addr->family = AF_INET; inetpeer_set_addr_v4(addr, nla_get_in_addr(a));
addr->addr.a4 = nla_get_in_addr(a);
if (hash) if (hash)
*hash = (__force unsigned int) addr->addr.a4; *hash = ipv4_addr_hash(inetpeer_get_addr_v4(addr));
return 0; return 0;
} }
a = info->attrs[v6]; a = info->attrs[v6];
if (a) { if (a) {
struct in6_addr in6;
if (nla_len(a) != sizeof(struct in6_addr)) if (nla_len(a) != sizeof(struct in6_addr))
return -EINVAL; return -EINVAL;
addr->family = AF_INET6; in6 = nla_get_in6_addr(a);
addr->addr.in6 = nla_get_in6_addr(a); inetpeer_set_addr_v6(addr, &in6);
if (hash) if (hash)
*hash = ipv6_addr_hash(&addr->addr.in6); *hash = ipv6_addr_hash(inetpeer_get_addr_v6(addr));
return 0; return 0;
} }
return optional ? 1 : -EAFNOSUPPORT; return optional ? 1 : -EAFNOSUPPORT;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment