Skip to content

Commit d3519cb

Browse files
committed
netfilter: nf_tables: add inet ingress support
This patch adds a new ingress hook for the inet family. The inet ingress hook emulates the IP receive path code, therefore, unclean packets are drop before walking over the ruleset in this basechain. This patch also introduces the nft_base_chain_netdev() helper function to check if this hook is bound to one or more devices (through the hook list infrastructure). This check allows to perform the same handling for the inet ingress as it would be a netdev ingress chain from the control plane. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 60a3815 commit d3519cb

5 files changed

Lines changed: 126 additions & 8 deletions

File tree

include/net/netfilter/nf_tables.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,12 @@ struct nft_table {
10811081
u8 *udata;
10821082
};
10831083

1084+
static inline bool nft_base_chain_netdev(int family, u32 hooknum)
1085+
{
1086+
return family == NFPROTO_NETDEV ||
1087+
(family == NFPROTO_INET && hooknum == NF_INET_INGRESS);
1088+
}
1089+
10841090
void nft_register_chain_type(const struct nft_chain_type *);
10851091
void nft_unregister_chain_type(const struct nft_chain_type *);
10861092

include/net/netfilter/nf_tables_ipv4.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,37 @@ static inline void nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt,
5353
nft_set_pktinfo_unspec(pkt, skb);
5454
}
5555

56+
static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt,
57+
struct sk_buff *skb)
58+
{
59+
struct iphdr *iph;
60+
u32 len, thoff;
61+
62+
if (!pskb_may_pull(skb, sizeof(*iph)))
63+
return -1;
64+
65+
iph = ip_hdr(skb);
66+
if (iph->ihl < 5 || iph->version != 4)
67+
goto inhdr_error;
68+
69+
len = ntohs(iph->tot_len);
70+
thoff = iph->ihl * 4;
71+
if (skb->len < len) {
72+
__IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INTRUNCATEDPKTS);
73+
return -1;
74+
} else if (len < thoff) {
75+
goto inhdr_error;
76+
}
77+
78+
pkt->tprot_set = true;
79+
pkt->tprot = iph->protocol;
80+
pkt->xt.thoff = thoff;
81+
pkt->xt.fragoff = ntohs(iph->frag_off) & IP_OFFSET;
82+
83+
return 0;
84+
85+
inhdr_error:
86+
__IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INHDRERRORS);
87+
return -1;
88+
}
5689
#endif

include/net/netfilter/nf_tables_ipv6.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,50 @@ static inline void nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt,
7070
nft_set_pktinfo_unspec(pkt, skb);
7171
}
7272

73+
static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt,
74+
struct sk_buff *skb)
75+
{
76+
#if IS_ENABLED(CONFIG_IPV6)
77+
unsigned int flags = IP6_FH_F_AUTH;
78+
unsigned short frag_off;
79+
unsigned int thoff = 0;
80+
struct inet6_dev *idev;
81+
struct ipv6hdr *ip6h;
82+
int protohdr;
83+
u32 pkt_len;
84+
85+
if (!pskb_may_pull(skb, sizeof(*ip6h)))
86+
return -1;
87+
88+
ip6h = ipv6_hdr(skb);
89+
if (ip6h->version != 6)
90+
goto inhdr_error;
91+
92+
pkt_len = ntohs(ip6h->payload_len);
93+
if (pkt_len + sizeof(*ip6h) > skb->len) {
94+
idev = __in6_dev_get(nft_in(pkt));
95+
__IP6_INC_STATS(nft_net(pkt), idev, IPSTATS_MIB_INTRUNCATEDPKTS);
96+
return -1;
97+
}
98+
99+
protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
100+
if (protohdr < 0)
101+
goto inhdr_error;
102+
103+
pkt->tprot_set = true;
104+
pkt->tprot = protohdr;
105+
pkt->xt.thoff = thoff;
106+
pkt->xt.fragoff = frag_off;
107+
108+
return 0;
109+
110+
inhdr_error:
111+
idev = __in6_dev_get(nft_in(pkt));
112+
__IP6_INC_STATS(nft_net(pkt), idev, IPSTATS_MIB_INHDRERRORS);
113+
return -1;
114+
#else
115+
return -1;
116+
#endif
117+
}
118+
73119
#endif

net/netfilter/nf_tables_api.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ static int nf_tables_register_hook(struct net *net,
206206
if (basechain->type->ops_register)
207207
return basechain->type->ops_register(net, ops);
208208

209-
if (table->family == NFPROTO_NETDEV)
209+
if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
210210
return nft_netdev_register_hooks(net, &basechain->hook_list);
211211

212212
return nf_register_net_hook(net, &basechain->ops);
@@ -228,7 +228,7 @@ static void nf_tables_unregister_hook(struct net *net,
228228
if (basechain->type->ops_unregister)
229229
return basechain->type->ops_unregister(net, ops);
230230

231-
if (table->family == NFPROTO_NETDEV)
231+
if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
232232
nft_netdev_unregister_hooks(net, &basechain->hook_list);
233233
else
234234
nf_unregister_net_hook(net, &basechain->ops);
@@ -1381,7 +1381,7 @@ static int nft_dump_basechain_hook(struct sk_buff *skb, int family,
13811381
if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority)))
13821382
goto nla_put_failure;
13831383

1384-
if (family == NFPROTO_NETDEV) {
1384+
if (nft_base_chain_netdev(family, ops->hooknum)) {
13851385
nest_devs = nla_nest_start_noflag(skb, NFTA_HOOK_DEVS);
13861386
list_for_each_entry(hook, &basechain->hook_list, list) {
13871387
if (!first)
@@ -1685,7 +1685,7 @@ void nf_tables_chain_destroy(struct nft_ctx *ctx)
16851685
if (nft_is_base_chain(chain)) {
16861686
struct nft_base_chain *basechain = nft_base_chain(chain);
16871687

1688-
if (ctx->family == NFPROTO_NETDEV) {
1688+
if (nft_base_chain_netdev(ctx->family, basechain->ops.hooknum)) {
16891689
list_for_each_entry_safe(hook, next,
16901690
&basechain->hook_list, list) {
16911691
list_del_rcu(&hook->list);
@@ -1877,7 +1877,7 @@ static int nft_chain_parse_hook(struct net *net,
18771877
hook->type = type;
18781878

18791879
INIT_LIST_HEAD(&hook->list);
1880-
if (family == NFPROTO_NETDEV) {
1880+
if (nft_base_chain_netdev(family, hook->num)) {
18811881
err = nft_chain_parse_netdev(net, ha, &hook->list);
18821882
if (err < 0) {
18831883
module_put(type->owner);
@@ -1944,7 +1944,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
19441944
INIT_LIST_HEAD(&basechain->hook_list);
19451945
chain = &basechain->chain;
19461946

1947-
if (family == NFPROTO_NETDEV) {
1947+
if (nft_base_chain_netdev(family, hook->num)) {
19481948
list_splice_init(&hook->list, &basechain->hook_list);
19491949
list_for_each_entry(h, &basechain->hook_list, list)
19501950
nft_basechain_hook_init(&h->ops, family, hook, chain);
@@ -2168,7 +2168,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
21682168
return -EEXIST;
21692169
}
21702170

2171-
if (ctx->family == NFPROTO_NETDEV) {
2171+
if (nft_base_chain_netdev(ctx->family, hook.num)) {
21722172
if (!nft_hook_list_equal(&basechain->hook_list,
21732173
&hook.list)) {
21742174
nft_chain_release_hook(&hook);

net/netfilter/nft_chain_filter.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,49 @@ static unsigned int nft_do_chain_inet(void *priv, struct sk_buff *skb,
161161
return nft_do_chain(&pkt, priv);
162162
}
163163

164+
static unsigned int nft_do_chain_inet_ingress(void *priv, struct sk_buff *skb,
165+
const struct nf_hook_state *state)
166+
{
167+
struct nf_hook_state ingress_state = *state;
168+
struct nft_pktinfo pkt;
169+
170+
switch (skb->protocol) {
171+
case htons(ETH_P_IP):
172+
/* Original hook is NFPROTO_NETDEV and NF_NETDEV_INGRESS. */
173+
ingress_state.pf = NFPROTO_IPV4;
174+
ingress_state.hook = NF_INET_INGRESS;
175+
nft_set_pktinfo(&pkt, skb, &ingress_state);
176+
177+
if (nft_set_pktinfo_ipv4_ingress(&pkt, skb) < 0)
178+
return NF_DROP;
179+
break;
180+
case htons(ETH_P_IPV6):
181+
ingress_state.pf = NFPROTO_IPV6;
182+
ingress_state.hook = NF_INET_INGRESS;
183+
nft_set_pktinfo(&pkt, skb, &ingress_state);
184+
185+
if (nft_set_pktinfo_ipv6_ingress(&pkt, skb) < 0)
186+
return NF_DROP;
187+
break;
188+
default:
189+
return NF_ACCEPT;
190+
}
191+
192+
return nft_do_chain(&pkt, priv);
193+
}
194+
164195
static const struct nft_chain_type nft_chain_filter_inet = {
165196
.name = "filter",
166197
.type = NFT_CHAIN_T_DEFAULT,
167198
.family = NFPROTO_INET,
168-
.hook_mask = (1 << NF_INET_LOCAL_IN) |
199+
.hook_mask = (1 << NF_INET_INGRESS) |
200+
(1 << NF_INET_LOCAL_IN) |
169201
(1 << NF_INET_LOCAL_OUT) |
170202
(1 << NF_INET_FORWARD) |
171203
(1 << NF_INET_PRE_ROUTING) |
172204
(1 << NF_INET_POST_ROUTING),
173205
.hooks = {
206+
[NF_INET_INGRESS] = nft_do_chain_inet_ingress,
174207
[NF_INET_LOCAL_IN] = nft_do_chain_inet,
175208
[NF_INET_LOCAL_OUT] = nft_do_chain_inet,
176209
[NF_INET_FORWARD] = nft_do_chain_inet,

0 commit comments

Comments
 (0)