Skip to content

Commit a308283

Browse files
committed
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
Pablo Neira Ayuso says: ==================== Netfilter/IPVS updates for net-next The following patchset contains Netfilter/IPVS updates for net-next: 1) Inspect the reply packets coming from DR/TUN and refresh connection state and timeout, from longguang yue and Julian Anastasov. 2) Series to add support for the inet ingress chain type in nf_tables. ==================== Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 547848a + 793d5d6 commit a308283

File tree

11 files changed

+282
-76
lines changed

11 files changed

+282
-76
lines changed

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

include/uapi/linux/netfilter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ enum nf_inet_hooks {
4545
NF_INET_FORWARD,
4646
NF_INET_LOCAL_OUT,
4747
NF_INET_POST_ROUTING,
48+
NF_INET_INGRESS,
4849
NF_INET_NUMHOOKS
4950
};
5051

net/netfilter/core.c

Lines changed: 103 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,16 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
281281
if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= hooknum))
282282
return NULL;
283283
return net->nf.hooks_bridge + hooknum;
284+
#endif
285+
#ifdef CONFIG_NETFILTER_INGRESS
286+
case NFPROTO_INET:
287+
if (WARN_ON_ONCE(hooknum != NF_INET_INGRESS))
288+
return NULL;
289+
if (!dev || dev_net(dev) != net) {
290+
WARN_ON_ONCE(1);
291+
return NULL;
292+
}
293+
return &dev->nf_hooks_ingress;
284294
#endif
285295
case NFPROTO_IPV4:
286296
if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= hooknum))
@@ -311,20 +321,80 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
311321
return NULL;
312322
}
313323

324+
static int nf_ingress_check(struct net *net, const struct nf_hook_ops *reg,
325+
int hooknum)
326+
{
327+
#ifndef CONFIG_NETFILTER_INGRESS
328+
if (reg->hooknum == hooknum)
329+
return -EOPNOTSUPP;
330+
#endif
331+
if (reg->hooknum != hooknum ||
332+
!reg->dev || dev_net(reg->dev) != net)
333+
return -EINVAL;
334+
335+
return 0;
336+
}
337+
338+
static inline bool nf_ingress_hook(const struct nf_hook_ops *reg, int pf)
339+
{
340+
if ((pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) ||
341+
(pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS))
342+
return true;
343+
344+
return false;
345+
}
346+
347+
static void nf_static_key_inc(const struct nf_hook_ops *reg, int pf)
348+
{
349+
#ifdef CONFIG_JUMP_LABEL
350+
int hooknum;
351+
352+
if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) {
353+
pf = NFPROTO_NETDEV;
354+
hooknum = NF_NETDEV_INGRESS;
355+
} else {
356+
hooknum = reg->hooknum;
357+
}
358+
static_key_slow_inc(&nf_hooks_needed[pf][hooknum]);
359+
#endif
360+
}
361+
362+
static void nf_static_key_dec(const struct nf_hook_ops *reg, int pf)
363+
{
364+
#ifdef CONFIG_JUMP_LABEL
365+
int hooknum;
366+
367+
if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) {
368+
pf = NFPROTO_NETDEV;
369+
hooknum = NF_NETDEV_INGRESS;
370+
} else {
371+
hooknum = reg->hooknum;
372+
}
373+
static_key_slow_dec(&nf_hooks_needed[pf][hooknum]);
374+
#endif
375+
}
376+
314377
static int __nf_register_net_hook(struct net *net, int pf,
315378
const struct nf_hook_ops *reg)
316379
{
317380
struct nf_hook_entries *p, *new_hooks;
318381
struct nf_hook_entries __rcu **pp;
382+
int err;
319383

320-
if (pf == NFPROTO_NETDEV) {
321-
#ifndef CONFIG_NETFILTER_INGRESS
322-
if (reg->hooknum == NF_NETDEV_INGRESS)
323-
return -EOPNOTSUPP;
324-
#endif
325-
if (reg->hooknum != NF_NETDEV_INGRESS ||
326-
!reg->dev || dev_net(reg->dev) != net)
327-
return -EINVAL;
384+
switch (pf) {
385+
case NFPROTO_NETDEV:
386+
err = nf_ingress_check(net, reg, NF_NETDEV_INGRESS);
387+
if (err < 0)
388+
return err;
389+
break;
390+
case NFPROTO_INET:
391+
if (reg->hooknum != NF_INET_INGRESS)
392+
break;
393+
394+
err = nf_ingress_check(net, reg, NF_INET_INGRESS);
395+
if (err < 0)
396+
return err;
397+
break;
328398
}
329399

330400
pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev);
@@ -345,12 +415,11 @@ static int __nf_register_net_hook(struct net *net, int pf,
345415

346416
hooks_validate(new_hooks);
347417
#ifdef CONFIG_NETFILTER_INGRESS
348-
if (pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
418+
if (nf_ingress_hook(reg, pf))
349419
net_inc_ingress_queue();
350420
#endif
351-
#ifdef CONFIG_JUMP_LABEL
352-
static_key_slow_inc(&nf_hooks_needed[pf][reg->hooknum]);
353-
#endif
421+
nf_static_key_inc(reg, pf);
422+
354423
BUG_ON(p == new_hooks);
355424
nf_hook_entries_free(p);
356425
return 0;
@@ -403,12 +472,10 @@ static void __nf_unregister_net_hook(struct net *net, int pf,
403472

404473
if (nf_remove_net_hook(p, reg)) {
405474
#ifdef CONFIG_NETFILTER_INGRESS
406-
if (pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
475+
if (nf_ingress_hook(reg, pf))
407476
net_dec_ingress_queue();
408477
#endif
409-
#ifdef CONFIG_JUMP_LABEL
410-
static_key_slow_dec(&nf_hooks_needed[pf][reg->hooknum]);
411-
#endif
478+
nf_static_key_dec(reg, pf);
412479
} else {
413480
WARN_ONCE(1, "hook not found, pf %d num %d", pf, reg->hooknum);
414481
}
@@ -425,8 +492,12 @@ static void __nf_unregister_net_hook(struct net *net, int pf,
425492
void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
426493
{
427494
if (reg->pf == NFPROTO_INET) {
428-
__nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
429-
__nf_unregister_net_hook(net, NFPROTO_IPV6, reg);
495+
if (reg->hooknum == NF_INET_INGRESS) {
496+
__nf_unregister_net_hook(net, NFPROTO_INET, reg);
497+
} else {
498+
__nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
499+
__nf_unregister_net_hook(net, NFPROTO_IPV6, reg);
500+
}
430501
} else {
431502
__nf_unregister_net_hook(net, reg->pf, reg);
432503
}
@@ -451,14 +522,20 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
451522
int err;
452523

453524
if (reg->pf == NFPROTO_INET) {
454-
err = __nf_register_net_hook(net, NFPROTO_IPV4, reg);
455-
if (err < 0)
456-
return err;
457-
458-
err = __nf_register_net_hook(net, NFPROTO_IPV6, reg);
459-
if (err < 0) {
460-
__nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
461-
return err;
525+
if (reg->hooknum == NF_INET_INGRESS) {
526+
err = __nf_register_net_hook(net, NFPROTO_INET, reg);
527+
if (err < 0)
528+
return err;
529+
} else {
530+
err = __nf_register_net_hook(net, NFPROTO_IPV4, reg);
531+
if (err < 0)
532+
return err;
533+
534+
err = __nf_register_net_hook(net, NFPROTO_IPV6, reg);
535+
if (err < 0) {
536+
__nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
537+
return err;
538+
}
462539
}
463540
} else {
464541
err = __nf_register_net_hook(net, reg->pf, reg);

net/netfilter/ipvs/ip_vs_conn.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
402402
{
403403
unsigned int hash;
404404
struct ip_vs_conn *cp, *ret=NULL;
405+
const union nf_inet_addr *saddr;
406+
__be16 sport;
405407

406408
/*
407409
* Check for "full" addressed entries
@@ -411,10 +413,20 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
411413
rcu_read_lock();
412414

413415
hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
414-
if (p->vport == cp->cport && p->cport == cp->dport &&
415-
cp->af == p->af &&
416+
if (p->vport != cp->cport)
417+
continue;
418+
419+
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
420+
sport = cp->vport;
421+
saddr = &cp->vaddr;
422+
} else {
423+
sport = cp->dport;
424+
saddr = &cp->daddr;
425+
}
426+
427+
if (p->cport == sport && cp->af == p->af &&
416428
ip_vs_addr_equal(p->af, p->vaddr, &cp->caddr) &&
417-
ip_vs_addr_equal(p->af, p->caddr, &cp->daddr) &&
429+
ip_vs_addr_equal(p->af, p->caddr, saddr) &&
418430
p->protocol == cp->protocol &&
419431
cp->ipvs == p->ipvs) {
420432
if (!__ip_vs_conn_get(cp))

net/netfilter/ipvs/ip_vs_core.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
875875
unsigned int verdict = NF_DROP;
876876

877877
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
878-
goto ignore_cp;
878+
goto after_nat;
879879

880880
/* Ensure the checksum is correct */
881881
if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
@@ -901,6 +901,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
901901
if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
902902
goto out;
903903

904+
after_nat:
904905
/* do the statistics and put it back */
905906
ip_vs_out_stats(cp, skb);
906907

@@ -909,8 +910,6 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
909910
ip_vs_notrack(skb);
910911
else
911912
ip_vs_update_conntrack(skb, cp, 0);
912-
913-
ignore_cp:
914913
verdict = NF_ACCEPT;
915914

916915
out:
@@ -1276,6 +1275,9 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
12761275
{
12771276
struct ip_vs_protocol *pp = pd->pp;
12781277

1278+
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
1279+
goto after_nat;
1280+
12791281
IP_VS_DBG_PKT(11, af, pp, skb, iph->off, "Outgoing packet");
12801282

12811283
if (skb_ensure_writable(skb, iph->len))
@@ -1316,6 +1318,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
13161318

13171319
IP_VS_DBG_PKT(10, af, pp, skb, iph->off, "After SNAT");
13181320

1321+
after_nat:
13191322
ip_vs_out_stats(cp, skb);
13201323
ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd);
13211324
skb->ipvs_property = 1;
@@ -1412,11 +1415,8 @@ ip_vs_out(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, in
14121415
cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
14131416
ipvs, af, skb, &iph);
14141417

1415-
if (likely(cp)) {
1416-
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
1417-
goto ignore_cp;
1418+
if (likely(cp))
14181419
return handle_response(af, skb, pd, cp, &iph, hooknum);
1419-
}
14201420

14211421
/* Check for real-server-started requests */
14221422
if (atomic_read(&ipvs->conn_out_counter)) {
@@ -1475,14 +1475,9 @@ ip_vs_out(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, in
14751475
}
14761476
}
14771477

1478-
out:
14791478
IP_VS_DBG_PKT(12, af, pp, skb, iph.off,
14801479
"ip_vs_out: packet continues traversal as normal");
14811480
return NF_ACCEPT;
1482-
1483-
ignore_cp:
1484-
__ip_vs_conn_put(cp);
1485-
goto out;
14861481
}
14871482

14881483
/*

0 commit comments

Comments
 (0)