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

netfilter: fib: avoid lookup if socket is available

In case the fib match is used from the input hook we can avoid the fib
lookup if early demux assigned a socket for us: check that the input
interface matches sk-cached one.

Rework the existing 'lo bypass' logic to first check sk, then
for loopback interface type to elide the fib lookup.

This speeds up fib matching a little, before:
93.08 GBit/s (no rules at all)
75.1 GBit/s ("fib saddr . iif oif missing drop" in prerouting)
75.62 GBit/s ("fib saddr . iif oif missing drop" in input)

After:
92.48 GBit/s (no rules at all)
75.62 GBit/s (fib rule in prerouting)
90.37 GBit/s (fib rule in input).

Numbers for the 'no rules' and 'prerouting' are expected to
closely match in-between runs, the 3rd/input test case exercises the
the 'avoid lookup if cached ifindex in sk matches' case.

Test used iperf3 via veth interface, lo can't be used due to existing
loopback test.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
eaaff9b6 8b686139

+36 -15
+21
include/net/netfilter/nft_fib.h
··· 18 18 return skb->pkt_type == PACKET_LOOPBACK || in->flags & IFF_LOOPBACK; 19 19 } 20 20 21 + static inline bool nft_fib_can_skip(const struct nft_pktinfo *pkt) 22 + { 23 + const struct net_device *indev = nft_in(pkt); 24 + const struct sock *sk; 25 + 26 + switch (nft_hook(pkt)) { 27 + case NF_INET_PRE_ROUTING: 28 + case NF_INET_INGRESS: 29 + case NF_INET_LOCAL_IN: 30 + break; 31 + default: 32 + return false; 33 + } 34 + 35 + sk = pkt->skb->sk; 36 + if (sk && sk_fullsock(sk)) 37 + return sk->sk_rx_dst_ifindex == indev->ifindex; 38 + 39 + return nft_fib_is_loopback(pkt->skb, indev); 40 + } 41 + 21 42 int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset); 22 43 int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 23 44 const struct nlattr * const tb[]);
+5 -6
net/ipv4/netfilter/nft_fib_ipv4.c
··· 71 71 const struct net_device *oif; 72 72 const struct net_device *found; 73 73 74 + if (nft_fib_can_skip(pkt)) { 75 + nft_fib_store_result(dest, priv, nft_in(pkt)); 76 + return; 77 + } 78 + 74 79 /* 75 80 * Do not set flowi4_oif, it restricts results (for example, asking 76 81 * for oif 3 will get RTN_UNICAST result even if the daddr exits ··· 89 84 oif = nft_in(pkt); 90 85 else 91 86 oif = NULL; 92 - 93 - if (nft_hook(pkt) == NF_INET_PRE_ROUTING && 94 - nft_fib_is_loopback(pkt->skb, nft_in(pkt))) { 95 - nft_fib_store_result(dest, priv, nft_in(pkt)); 96 - return; 97 - } 98 87 99 88 iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph); 100 89 if (!iph) {
+10 -9
net/ipv6/netfilter/nft_fib_ipv6.c
··· 170 170 struct rt6_info *rt; 171 171 int lookup_flags; 172 172 173 + if (nft_fib_can_skip(pkt)) { 174 + nft_fib_store_result(dest, priv, nft_in(pkt)); 175 + return; 176 + } 177 + 173 178 if (priv->flags & NFTA_FIB_F_IIF) 174 179 oif = nft_in(pkt); 175 180 else if (priv->flags & NFTA_FIB_F_OIF) ··· 186 181 return; 187 182 } 188 183 189 - lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif, iph); 190 - 191 - if (nft_hook(pkt) == NF_INET_PRE_ROUTING || 192 - nft_hook(pkt) == NF_INET_INGRESS) { 193 - if (nft_fib_is_loopback(pkt->skb, nft_in(pkt)) || 194 - nft_fib_v6_skip_icmpv6(pkt->skb, pkt->tprot, iph)) { 195 - nft_fib_store_result(dest, priv, nft_in(pkt)); 196 - return; 197 - } 184 + if (nft_fib_v6_skip_icmpv6(pkt->skb, pkt->tprot, iph)) { 185 + nft_fib_store_result(dest, priv, nft_in(pkt)); 186 + return; 198 187 } 188 + 189 + lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif, iph); 199 190 200 191 *dest = 0; 201 192 rt = (void *)ip6_route_lookup(nft_net(pkt), &fl6, pkt->skb,