Skip to content

Commit f6d0cbc

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nf_tables: add fib expression
Add FIB expression, supported for ipv4, ipv6 and inet family (the latter just dispatches to ipv4 or ipv6 one based on nfproto). Currently supports fetching output interface index/name and the rtm_type associated with an address. This can be used for adding path filtering. rtm_type is useful to e.g. enforce a strong-end host model where packets are only accepted if daddr is configured on the interface the packet arrived on. The fib expression is a native nftables alternative to the xtables addrtype and rp_filter matches. FIB result order for oif/oifname retrieval is as follows: - if packet is local (skb has rtable, RTF_LOCAL set, this will also catch looped-back multicast packets), set oif to the loopback interface. - if fib lookup returns an error, or result points to local, store zero result. This means '--local' option of -m rpfilter is not supported. It is possible to use 'fib type local' or add explicit saddr/daddr matching rules to create exceptions if this is really needed. - store result in the destination register. In case of multiple routes, search set for desired oif in case strict matching is requested. ipv4 and ipv6 behave fib expressions are supposed to behave the same. [ I have collapsed Arnd Bergmann's ("netfilter: nf_tables: fib warnings") http://patchwork.ozlabs.org/patch/688615/ to address fallout from this patch after rebasing nf-next, that was posted to address compilation warnings. --pablo ] Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 2544326 commit f6d0cbc

File tree

12 files changed

+854
-0
lines changed

12 files changed

+854
-0
lines changed

include/net/netfilter/nft_fib.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef _NFT_FIB_H_
2+
#define _NFT_FIB_H_
3+
4+
struct nft_fib {
5+
enum nft_registers dreg:8;
6+
u8 result;
7+
u32 flags;
8+
};
9+
10+
extern const struct nla_policy nft_fib_policy[];
11+
12+
int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr);
13+
int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
14+
const struct nlattr * const tb[]);
15+
int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
16+
const struct nft_data **data);
17+
18+
19+
void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
20+
const struct nft_pktinfo *pkt);
21+
void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
22+
const struct nft_pktinfo *pkt);
23+
24+
void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
25+
const struct nft_pktinfo *pkt);
26+
void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
27+
const struct nft_pktinfo *pkt);
28+
29+
void nft_fib_store_result(void *reg, enum nft_fib_result r,
30+
const struct nft_pktinfo *pkt, int index);
31+
#endif

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,42 @@ enum nft_gen_attributes {
11091109
};
11101110
#define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1)
11111111

1112+
/*
1113+
* enum nft_fib_attributes - nf_tables fib expression netlink attributes
1114+
*
1115+
* @NFTA_FIB_DREG: destination register (NLA_U32)
1116+
* @NFTA_FIB_RESULT: desired result (NLA_U32)
1117+
* @NFTA_FIB_FLAGS: flowi fields to initialize when querying the FIB (NLA_U32)
1118+
*
1119+
* The FIB expression performs a route lookup according
1120+
* to the packet data.
1121+
*/
1122+
enum nft_fib_attributes {
1123+
NFTA_FIB_UNSPEC,
1124+
NFTA_FIB_DREG,
1125+
NFTA_FIB_RESULT,
1126+
NFTA_FIB_FLAGS,
1127+
__NFTA_FIB_MAX
1128+
};
1129+
#define NFTA_FIB_MAX (__NFTA_FIB_MAX - 1)
1130+
1131+
enum nft_fib_result {
1132+
NFT_FIB_RESULT_UNSPEC,
1133+
NFT_FIB_RESULT_OIF,
1134+
NFT_FIB_RESULT_OIFNAME,
1135+
NFT_FIB_RESULT_ADDRTYPE,
1136+
__NFT_FIB_RESULT_MAX
1137+
};
1138+
#define NFT_FIB_RESULT_MAX (__NFT_FIB_RESULT_MAX - 1)
1139+
1140+
enum nft_fib_flags {
1141+
NFTA_FIB_F_SADDR = 1 << 0, /* look up src */
1142+
NFTA_FIB_F_DADDR = 1 << 1, /* look up dst */
1143+
NFTA_FIB_F_MARK = 1 << 2, /* use skb->mark */
1144+
NFTA_FIB_F_IIF = 1 << 3, /* restrict to iif */
1145+
NFTA_FIB_F_OIF = 1 << 4, /* restrict to oif */
1146+
};
1147+
11121148
/**
11131149
* enum nft_trace_attributes - nf_tables trace netlink attributes
11141150
*

net/ipv4/netfilter/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ config NFT_DUP_IPV4
5454
help
5555
This module enables IPv4 packet duplication support for nf_tables.
5656

57+
config NFT_FIB_IPV4
58+
select NFT_FIB
59+
tristate "nf_tables fib / ip route lookup support"
60+
help
61+
This module enables IPv4 FIB lookups, e.g. for reverse path filtering.
62+
It also allows query of the FIB for the route type, e.g. local, unicast,
63+
multicast or blackhole.
64+
5765
endif # NF_TABLES_IPV4
5866

5967
config NF_TABLES_ARP

net/ipv4/netfilter/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o
3434
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o
3535
obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o
3636
obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o
37+
obj-$(CONFIG_NFT_FIB_IPV4) += nft_fib_ipv4.o
3738
obj-$(CONFIG_NFT_MASQ_IPV4) += nft_masq_ipv4.o
3839
obj-$(CONFIG_NFT_REDIR_IPV4) += nft_redir_ipv4.o
3940
obj-$(CONFIG_NFT_DUP_IPV4) += nft_dup_ipv4.o

net/ipv4/netfilter/nft_fib_ipv4.c

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/*
2+
* This program is free software; you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License version 2 as
4+
* published by the Free Software Foundation.
5+
*/
6+
7+
#include <linux/kernel.h>
8+
#include <linux/init.h>
9+
#include <linux/module.h>
10+
#include <linux/netlink.h>
11+
#include <linux/netfilter.h>
12+
#include <linux/netfilter/nf_tables.h>
13+
#include <net/netfilter/nf_tables_core.h>
14+
#include <net/netfilter/nf_tables.h>
15+
#include <net/netfilter/nft_fib.h>
16+
17+
#include <net/ip_fib.h>
18+
#include <net/route.h>
19+
20+
/* don't try to find route from mcast/bcast/zeronet */
21+
static __be32 get_saddr(__be32 addr)
22+
{
23+
if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) ||
24+
ipv4_is_zeronet(addr))
25+
return 0;
26+
return addr;
27+
}
28+
29+
static bool fib4_is_local(const struct sk_buff *skb)
30+
{
31+
const struct rtable *rt = skb_rtable(skb);
32+
33+
return rt && (rt->rt_flags & RTCF_LOCAL);
34+
}
35+
36+
#define DSCP_BITS 0xfc
37+
38+
void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
39+
const struct nft_pktinfo *pkt)
40+
{
41+
const struct nft_fib *priv = nft_expr_priv(expr);
42+
u32 *dst = &regs->data[priv->dreg];
43+
const struct net_device *dev = NULL;
44+
const struct iphdr *iph;
45+
__be32 addr;
46+
47+
if (priv->flags & NFTA_FIB_F_IIF)
48+
dev = pkt->in;
49+
else if (priv->flags & NFTA_FIB_F_OIF)
50+
dev = pkt->out;
51+
52+
iph = ip_hdr(pkt->skb);
53+
if (priv->flags & NFTA_FIB_F_DADDR)
54+
addr = iph->daddr;
55+
else
56+
addr = iph->saddr;
57+
58+
*dst = inet_dev_addr_type(pkt->net, dev, addr);
59+
}
60+
EXPORT_SYMBOL_GPL(nft_fib4_eval_type);
61+
62+
static int get_ifindex(const struct net_device *dev)
63+
{
64+
return dev ? dev->ifindex : 0;
65+
}
66+
67+
void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
68+
const struct nft_pktinfo *pkt)
69+
{
70+
const struct nft_fib *priv = nft_expr_priv(expr);
71+
u32 *dest = &regs->data[priv->dreg];
72+
const struct iphdr *iph;
73+
struct fib_result res;
74+
struct flowi4 fl4 = {
75+
.flowi4_scope = RT_SCOPE_UNIVERSE,
76+
.flowi4_iif = LOOPBACK_IFINDEX,
77+
};
78+
const struct net_device *oif;
79+
struct net_device *found;
80+
#ifdef CONFIG_IP_ROUTE_MULTIPATH
81+
int i;
82+
#endif
83+
84+
/*
85+
* Do not set flowi4_oif, it restricts results (for example, asking
86+
* for oif 3 will get RTN_UNICAST result even if the daddr exits
87+
* on another interface.
88+
*
89+
* Search results for the desired outinterface instead.
90+
*/
91+
if (priv->flags & NFTA_FIB_F_OIF)
92+
oif = pkt->out;
93+
else if (priv->flags & NFTA_FIB_F_IIF)
94+
oif = pkt->in;
95+
else
96+
oif = NULL;
97+
98+
if (pkt->hook == NF_INET_PRE_ROUTING && fib4_is_local(pkt->skb)) {
99+
nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX);
100+
return;
101+
}
102+
103+
iph = ip_hdr(pkt->skb);
104+
if (ipv4_is_multicast(iph->daddr) &&
105+
ipv4_is_zeronet(iph->saddr) &&
106+
ipv4_is_local_multicast(iph->daddr)) {
107+
nft_fib_store_result(dest, priv->result, pkt,
108+
get_ifindex(pkt->skb->dev));
109+
return;
110+
}
111+
112+
if (priv->flags & NFTA_FIB_F_MARK)
113+
fl4.flowi4_mark = pkt->skb->mark;
114+
115+
fl4.flowi4_tos = iph->tos & DSCP_BITS;
116+
117+
if (priv->flags & NFTA_FIB_F_DADDR) {
118+
fl4.daddr = iph->daddr;
119+
fl4.saddr = get_saddr(iph->saddr);
120+
} else {
121+
fl4.daddr = iph->saddr;
122+
fl4.saddr = get_saddr(iph->daddr);
123+
}
124+
125+
if (fib_lookup(pkt->net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
126+
return;
127+
128+
switch (res.type) {
129+
case RTN_UNICAST:
130+
break;
131+
case RTN_LOCAL: /* should not appear here, see fib4_is_local() above */
132+
return;
133+
default:
134+
break;
135+
}
136+
137+
if (!oif) {
138+
found = FIB_RES_DEV(res);
139+
goto ok;
140+
}
141+
142+
#ifdef CONFIG_IP_ROUTE_MULTIPATH
143+
for (i = 0; i < res.fi->fib_nhs; i++) {
144+
struct fib_nh *nh = &res.fi->fib_nh[i];
145+
146+
if (nh->nh_dev == oif) {
147+
found = nh->nh_dev;
148+
goto ok;
149+
}
150+
}
151+
return;
152+
#else
153+
found = FIB_RES_DEV(res);
154+
if (found != oif)
155+
return;
156+
#endif
157+
ok:
158+
switch (priv->result) {
159+
case NFT_FIB_RESULT_OIF:
160+
*dest = found->ifindex;
161+
break;
162+
case NFT_FIB_RESULT_OIFNAME:
163+
strncpy((char *)dest, found->name, IFNAMSIZ);
164+
break;
165+
default:
166+
WARN_ON_ONCE(1);
167+
break;
168+
}
169+
}
170+
EXPORT_SYMBOL_GPL(nft_fib4_eval);
171+
172+
static struct nft_expr_type nft_fib4_type;
173+
174+
static const struct nft_expr_ops nft_fib4_type_ops = {
175+
.type = &nft_fib4_type,
176+
.size = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
177+
.eval = nft_fib4_eval_type,
178+
.init = nft_fib_init,
179+
.dump = nft_fib_dump,
180+
.validate = nft_fib_validate,
181+
};
182+
183+
static const struct nft_expr_ops nft_fib4_ops = {
184+
.type = &nft_fib4_type,
185+
.size = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
186+
.eval = nft_fib4_eval,
187+
.init = nft_fib_init,
188+
.dump = nft_fib_dump,
189+
.validate = nft_fib_validate,
190+
};
191+
192+
static const struct nft_expr_ops *
193+
nft_fib4_select_ops(const struct nft_ctx *ctx,
194+
const struct nlattr * const tb[])
195+
{
196+
enum nft_fib_result result;
197+
198+
if (!tb[NFTA_FIB_RESULT])
199+
return ERR_PTR(-EINVAL);
200+
201+
result = htonl(nla_get_be32(tb[NFTA_FIB_RESULT]));
202+
203+
switch (result) {
204+
case NFT_FIB_RESULT_OIF:
205+
return &nft_fib4_ops;
206+
case NFT_FIB_RESULT_OIFNAME:
207+
return &nft_fib4_ops;
208+
case NFT_FIB_RESULT_ADDRTYPE:
209+
return &nft_fib4_type_ops;
210+
default:
211+
return ERR_PTR(-EOPNOTSUPP);
212+
}
213+
}
214+
215+
static struct nft_expr_type nft_fib4_type __read_mostly = {
216+
.name = "fib",
217+
.select_ops = &nft_fib4_select_ops,
218+
.policy = nft_fib_policy,
219+
.maxattr = NFTA_FIB_MAX,
220+
.family = NFPROTO_IPV4,
221+
.owner = THIS_MODULE,
222+
};
223+
224+
static int __init nft_fib4_module_init(void)
225+
{
226+
return nft_register_expr(&nft_fib4_type);
227+
}
228+
229+
static void __exit nft_fib4_module_exit(void)
230+
{
231+
nft_unregister_expr(&nft_fib4_type);
232+
}
233+
234+
module_init(nft_fib4_module_init);
235+
module_exit(nft_fib4_module_exit);
236+
MODULE_LICENSE("GPL");
237+
MODULE_AUTHOR("Florian Westphal <[email protected]>");
238+
MODULE_ALIAS_NFT_AF_EXPR(2, "fib");

net/ipv6/netfilter/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ config NFT_DUP_IPV6
5454
help
5555
This module enables IPv6 packet duplication support for nf_tables.
5656

57+
config NFT_FIB_IPV6
58+
tristate "nf_tables fib / ipv6 route lookup support"
59+
select NFT_FIB
60+
help
61+
This module enables IPv6 FIB lookups, e.g. for reverse path filtering.
62+
It also allows query of the FIB for the route type, e.g. local, unicast,
63+
multicast or blackhole.
64+
5765
endif # NF_TABLES_IPV6
5866
endif # NF_TABLES
5967

net/ipv6/netfilter/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o
4040
obj-$(CONFIG_NFT_MASQ_IPV6) += nft_masq_ipv6.o
4141
obj-$(CONFIG_NFT_REDIR_IPV6) += nft_redir_ipv6.o
4242
obj-$(CONFIG_NFT_DUP_IPV6) += nft_dup_ipv6.o
43+
obj-$(CONFIG_NFT_FIB_IPV6) += nft_fib_ipv6.o
4344

4445
# matches
4546
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o

0 commit comments

Comments
 (0)