Skip to content

Commit 6e617de

Browse files
Paolo Abenidavem330
authored andcommitted
net: avoid a full fib lookup when rp_filter is disabled.
Since commit 1dced6a ("ipv4: Restore accept_local behaviour in fib_validate_source()") a full fib lookup is needed even if the rp_filter is disabled, if accept_local is false - which is the default. What we really need in the above scenario is just checking that the source IP address is not local, and in most case we can do that is a cheaper way looking up the ifaddr hash table. This commit adds a helper for such lookup, and uses it to validate the src address when rp_filter is disabled and no 'local' routes are created by the user space in the relevant namespace. A new ipv4 netns flag is added to account for such routes. We need that to preserve the same behavior we had before this patch. It also drops the checks to bail early from __fib_validate_source, added by the commit 1dced6a ("ipv4: Restore accept_local behaviour in fib_validate_source()") they do not give any measurable performance improvement: if we do the lookup with are on a slower path. This improves UDP performances for unconnected sockets when rp_filter is disabled by 5% and also gives small but measurable performance improvement for TCP flood scenarios. v1 -> v2: - use the ifaddr lookup helper in __ip_dev_find(), as suggested by Eric - fall-back to full lookup if custom local routes are present Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent a99855d commit 6e617de

File tree

4 files changed

+37
-17
lines changed

4 files changed

+37
-17
lines changed

include/linux/inetdevice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, __be32 dst,
179179
__be32 local, int scope);
180180
struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
181181
__be32 mask);
182+
struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr);
182183
static __inline__ bool inet_ifa_match(__be32 addr, struct in_ifaddr *ifa)
183184
{
184185
return !((addr^ifa->ifa_address)&ifa->ifa_mask);

include/net/netns/ipv4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ struct netns_ipv4 {
4949
#ifdef CONFIG_IP_MULTIPLE_TABLES
5050
struct fib_rules_ops *rules_ops;
5151
bool fib_has_custom_rules;
52+
bool fib_has_custom_local_routes;
5253
struct fib_table __rcu *fib_main;
5354
struct fib_table __rcu *fib_default;
5455
#endif

net/ipv4/devinet.c

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,22 +137,12 @@ static void inet_hash_remove(struct in_ifaddr *ifa)
137137
*/
138138
struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
139139
{
140-
u32 hash = inet_addr_hash(net, addr);
141140
struct net_device *result = NULL;
142141
struct in_ifaddr *ifa;
143142

144143
rcu_read_lock();
145-
hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) {
146-
if (ifa->ifa_local == addr) {
147-
struct net_device *dev = ifa->ifa_dev->dev;
148-
149-
if (!net_eq(dev_net(dev), net))
150-
continue;
151-
result = dev;
152-
break;
153-
}
154-
}
155-
if (!result) {
144+
ifa = inet_lookup_ifaddr_rcu(net, addr);
145+
if (!ifa) {
156146
struct flowi4 fl4 = { .daddr = addr };
157147
struct fib_result res = { 0 };
158148
struct fib_table *local;
@@ -165,6 +155,8 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
165155
!fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
166156
res.type == RTN_LOCAL)
167157
result = FIB_RES_DEV(res);
158+
} else {
159+
result = ifa->ifa_dev->dev;
168160
}
169161
if (result && devref)
170162
dev_hold(result);
@@ -173,6 +165,20 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
173165
}
174166
EXPORT_SYMBOL(__ip_dev_find);
175167

168+
/* called under RCU lock */
169+
struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
170+
{
171+
u32 hash = inet_addr_hash(net, addr);
172+
struct in_ifaddr *ifa;
173+
174+
hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
175+
if (ifa->ifa_local == addr &&
176+
net_eq(dev_net(ifa->ifa_dev->dev), net))
177+
return ifa;
178+
179+
return NULL;
180+
}
181+
176182
static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
177183

178184
static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);

net/ipv4/fib_frontend.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,6 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
345345
if (res.type != RTN_UNICAST &&
346346
(res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev)))
347347
goto e_inval;
348-
if (!rpf && !fib_num_tclassid_users(net) &&
349-
(dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev)))
350-
goto last_resort;
351348
fib_combine_itag(itag, &res);
352349
dev_match = false;
353350

@@ -402,13 +399,26 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
402399
struct in_device *idev, u32 *itag)
403400
{
404401
int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev);
402+
struct net *net = dev_net(dev);
405403

406-
if (!r && !fib_num_tclassid_users(dev_net(dev)) &&
407-
IN_DEV_ACCEPT_LOCAL(idev) &&
404+
if (!r && !fib_num_tclassid_users(net) &&
408405
(dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev))) {
406+
if (IN_DEV_ACCEPT_LOCAL(idev))
407+
goto ok;
408+
/* if no local routes are added from user space we can check
409+
* for local addresses looking-up the ifaddr table
410+
*/
411+
if (net->ipv4.fib_has_custom_local_routes)
412+
goto full_check;
413+
if (inet_lookup_ifaddr_rcu(net, src))
414+
return -EINVAL;
415+
416+
ok:
409417
*itag = 0;
410418
return 0;
411419
}
420+
421+
full_check:
412422
return __fib_validate_source(skb, src, dst, tos, oif, dev, r, idev, itag);
413423
}
414424

@@ -759,6 +769,8 @@ static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
759769
}
760770

761771
err = fib_table_insert(net, tb, &cfg, extack);
772+
if (!err && cfg.fc_type == RTN_LOCAL)
773+
net->ipv4.fib_has_custom_local_routes = true;
762774
errout:
763775
return err;
764776
}

0 commit comments

Comments
 (0)