Merge tag 'nf-next-26-07-24' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next

Pablo Neira Ayuso says:

====================
Netfilter/IPVS updates for net-next

The following patchset contains Netfilter/IPVS updates for net-next,
just a small batch with accumulated pending updates:

1) In IPVS, use system_dfl_long_wq instead of system_long_wq, from
   Ismael Luceno.

2) Add missing .checkentry in xt_tcpmss for IPv6, this is a follow up
   to a recent harderning, from Florian Westphal.

3) Address a sashiko report in the NAT SIP helper, from Florian Westphal.

4) Tear down flow entries with stale routes using the GC, this is to
   detect route updates when hardware offload is enabled.

5) Pass master conntrack as parameter to functions instead of using
   exp->master as preparation work to turn exp->master into a cookie.

6) Move expectation event_mask to the nf_conntrack_expect object,
   again as preparation work to turn exp->master into a cookie.

7) In IPVS, use kzalloc_obj{s}() typesafe allocations, from Subasri S.

* tag 'nf-next-26-07-24' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
  ipvs: use type-safe allocation helpers in ip_vs_rht_alloc
  netfilter: nf_conntrack_expect: store event cache in expectation
  netfilter: conntrack_helper: pass master conntrack to helper functions
  netfilter: flowtable: tear down flow entries with stale dst from GC
  netfilter: nf_nat_sip: rewind offset when NAT shrinks the packet
  netfilter: xt_tcpmss: extend checkentry to ipv6
  ipvs: Move defense_work and est_reload_work to system_dfl_long_wq
====================

Link: https://patch.msgid.link/20260724104932.437729-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-07-27 18:00:07 -07:00
25 changed files with 61 additions and 36 deletions

View File

@@ -9,6 +9,7 @@
typedef unsigned int
nf_nat_amanda_hook_fn(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int protoff,
unsigned int matchoff,

View File

@@ -28,6 +28,7 @@ struct nf_ct_ftp_master {
* connection we should expect. */
typedef unsigned int
nf_nat_ftp_hook_fn(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
enum nf_ct_ftp_type type,
unsigned int protoff,

View File

@@ -10,6 +10,7 @@
typedef unsigned int
nf_nat_irc_hook_fn(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int protoff,
unsigned int matchoff,

View File

@@ -19,6 +19,7 @@ struct tftphdr {
typedef unsigned int
nf_nat_tftp_hook_fn(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
struct nf_conntrack_expect *exp);

View File

@@ -42,6 +42,9 @@ struct nf_conntrack_expect {
/* Expectation class */
unsigned int class;
/* Event filter mask */
u16 event_mask;
/* Function to call after setup and insertion */
void (*expectfn)(struct nf_conn *new,
struct nf_conntrack_expect *this);

View File

@@ -310,6 +310,14 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
void flow_offload_refresh(struct nf_flowtable *flow_table,
struct flow_offload *flow, bool force);
static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple)
{
if (!tuple->dst_cache)
return true;
return dst_check(tuple->dst_cache, tuple->dst_cookie);
}
struct flow_offload_tuple_rhash *flow_offload_lookup(struct nf_flowtable *flow_table,
struct flow_offload_tuple *tuple);
void nf_flow_table_gc_run(struct nf_flowtable *flow_table);

View File

@@ -176,7 +176,7 @@ void ip_vs_rht_rcu_free(struct rcu_head *head)
struct ip_vs_rht *ip_vs_rht_alloc(int buckets, int scounts, int locks)
{
struct ip_vs_rht *t = kzalloc(sizeof(*t), GFP_KERNEL);
struct ip_vs_rht *t = kzalloc_obj(*t);
int i;
if (!t)
@@ -186,7 +186,7 @@ struct ip_vs_rht *ip_vs_rht_alloc(int buckets, int scounts, int locks)
scounts = min(scounts, buckets);
scounts = min(scounts, ml);
t->seqc = kvmalloc_array(scounts, sizeof(*t->seqc), GFP_KERNEL);
t->seqc = kvmalloc_objs(*t->seqc, scounts);
if (!t->seqc)
goto err;
for (i = 0; i < scounts; i++)
@@ -194,8 +194,7 @@ struct ip_vs_rht *ip_vs_rht_alloc(int buckets, int scounts, int locks)
if (locks) {
locks = min(locks, scounts);
t->lock = kvmalloc_array(locks, sizeof(*t->lock),
GFP_KERNEL);
t->lock = kvmalloc_objs(*t->lock, locks);
if (!t->lock)
goto err;
for (i = 0; i < locks; i++)
@@ -203,7 +202,7 @@ struct ip_vs_rht *ip_vs_rht_alloc(int buckets, int scounts, int locks)
}
}
t->buckets = kvmalloc_array(buckets, sizeof(*t->buckets), GFP_KERNEL);
t->buckets = kvmalloc_objs(*t->buckets, buckets);
if (!t->buckets)
goto err;
for (i = 0; i < buckets; i++)

View File

@@ -235,7 +235,7 @@ static void defense_work_handler(struct work_struct *work)
update_defense_level(ipvs);
if (atomic_read(&ipvs->dropentry))
ip_vs_random_dropentry(ipvs);
queue_delayed_work(system_long_wq, &ipvs->defense_work,
queue_delayed_work(system_dfl_long_wq, &ipvs->defense_work,
DEFENSE_TIMER_PERIOD);
}
#endif
@@ -290,7 +290,7 @@ static void est_reload_work_handler(struct work_struct *work)
atomic_set(&ipvs->est_genid_done, genid);
if (repeat)
queue_delayed_work(system_long_wq, &ipvs->est_reload_work,
queue_delayed_work(system_dfl_long_wq, &ipvs->est_reload_work,
delay);
unlock:
@@ -5126,7 +5126,7 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
goto err;
/* Schedule defense work */
queue_delayed_work(system_long_wq, &ipvs->defense_work,
queue_delayed_work(system_dfl_long_wq, &ipvs->defense_work,
DEFENSE_TIMER_PERIOD);
return 0;

View File

@@ -243,7 +243,7 @@ void ip_vs_est_reload_start(struct netns_ipvs *ipvs, bool restart)
/* Bump the kthread configuration genid if stopping is requested */
if (restart)
atomic_inc(&ipvs->est_genid);
queue_delayed_work(system_long_wq, &ipvs->est_reload_work, 0);
queue_delayed_work(system_dfl_long_wq, &ipvs->est_reload_work, 0);
}
/* Start kthread task with current configuration */

View File

@@ -151,7 +151,7 @@ static int amanda_help(struct sk_buff *skb,
nf_nat_amanda = rcu_dereference(nf_nat_amanda_hook);
if (nf_nat_amanda && ct->status & IPS_NAT_MASK)
ret = nf_nat_amanda(skb, ctinfo, protoff,
ret = nf_nat_amanda(skb, ct, ctinfo, protoff,
off - dataoff, len, exp);
else if (nf_ct_expect_related(exp, 0) != 0) {
nf_ct_helper_log(skb, ct, "cannot add expectation");

View File

@@ -14,6 +14,7 @@
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_ecache.h>
int nf_conntrack_broadcast_help(struct sk_buff *skb,
struct nf_conn *ct,
@@ -27,6 +28,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
struct rtable *rt = skb_rtable(skb);
struct in_device *in_dev;
struct nf_conn_help *help = nfct_help(ct);
struct nf_conntrack_ecache *ecache;
__be32 mask = 0;
if (!help)
@@ -79,6 +81,10 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
#ifdef CONFIG_NF_CONNTRACK_ZONES
exp->zone = ct->zone;
#endif
ecache = nf_ct_ecache_find(ct);
if (ecache)
exp->event_mask = ecache->expmask;
nf_ct_expect_related(exp, 0);
nf_ct_expect_put(exp);

View File

@@ -245,7 +245,6 @@ void nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
{
struct net *net = nf_ct_exp_net(exp);
struct nf_ct_event_notifier *notify;
struct nf_conntrack_ecache *e;
lockdep_nfct_expect_lock_held();
@@ -254,11 +253,7 @@ void nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
if (!notify)
goto out_unlock;
e = nf_ct_ecache_find(exp->master);
if (!e)
goto out_unlock;
if (e->expmask & (1 << event)) {
if (exp->event_mask & (1 << event)) {
struct nf_exp_event item = {
.exp = exp,
.portid = portid,

View File

@@ -330,6 +330,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
struct nf_conntrack_helper *helper = NULL;
struct nf_conn *ct = exp->master;
struct net *net = read_pnet(&ct->ct_net);
struct nf_conntrack_ecache *ecache;
struct nf_conn_help *help;
int len;
@@ -342,6 +343,10 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
exp->class = class;
exp->expectfn = NULL;
ecache = nf_ct_ecache_find(ct);
if (ecache)
exp->event_mask = ecache->expmask;
help = nfct_help(ct);
if (help)
helper = rcu_dereference(help->helper);

View File

@@ -515,7 +515,7 @@ static int help(struct sk_buff *skb,
* (possibly changed) expectation itself. */
nf_nat_ftp = rcu_dereference(nf_nat_ftp_hook);
if (nf_nat_ftp && ct->status & IPS_NAT_MASK)
ret = nf_nat_ftp(skb, ctinfo, search[dir][i].ftptype,
ret = nf_nat_ftp(skb, ct, ctinfo, search[dir][i].ftptype,
protoff, matchoff, matchlen, exp);
else {
/* Can't expect this? Best to drop packet now. */

View File

@@ -231,7 +231,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
nf_nat_irc = rcu_dereference(nf_nat_irc_hook);
if (nf_nat_irc && ct->status & IPS_NAT_MASK)
ret = nf_nat_irc(skb, ctinfo, protoff,
ret = nf_nat_irc(skb, ct, ctinfo, protoff,
addr_beg_p - ib_ptr,
addr_end_p - addr_beg_p,
exp);

View File

@@ -3524,6 +3524,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
{
struct net *net = read_pnet(&ct->ct_net);
struct nf_conntrack_helper *helper;
struct nf_conntrack_ecache *ecache;
struct nf_conntrack_expect *exp;
struct nf_conn_help *help;
u32 class = 0;
@@ -3575,6 +3576,10 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
exp->mask.src.u3 = mask->src.u3;
exp->mask.src.u.all = mask->src.u.all;
ecache = nf_ct_ecache_find(ct);
if (ecache)
exp->event_mask = ecache->expmask;
if (cda[CTA_EXPECT_NAT]) {
err = ctnetlink_parse_expect_nat(cda[CTA_EXPECT_NAT],
exp, nf_ct_l3num(ct));

View File

@@ -69,7 +69,7 @@ static int tftp_help(struct sk_buff *skb,
nf_nat_tftp = rcu_dereference(nf_nat_tftp_hook);
if (nf_nat_tftp && ct->status & IPS_NAT_MASK)
ret = nf_nat_tftp(skb, ctinfo, exp);
ret = nf_nat_tftp(skb, ct, ctinfo, exp);
else if (nf_ct_expect_related(exp, 0) != 0) {
nf_ct_helper_log(skb, ct, "cannot add expectation");
ret = NF_DROP;

View File

@@ -571,6 +571,8 @@ static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table,
if (nf_flow_has_expired(flow) ||
nf_ct_is_dying(flow->ct) ||
!nf_flow_dst_check(&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple) ||
!nf_flow_dst_check(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple) ||
nf_flow_custom_gc(flow_table, flow)) {
flow_offload_teardown(flow);
teardown = true;

View File

@@ -297,14 +297,6 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
return true;
}
static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple)
{
if (!tuple->dst_cache)
return true;
return dst_check(tuple->dst_cache, tuple->dst_cookie);
}
static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
const struct nf_hook_state *state,
struct dst_entry *dst)

View File

@@ -26,6 +26,7 @@ static struct nf_conntrack_nat_helper nat_helper_amanda =
NF_CT_NAT_HELPER_INIT(NAT_HELPER_NAME);
static unsigned int help(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int protoff,
unsigned int matchoff,
@@ -46,15 +47,15 @@ static unsigned int help(struct sk_buff *skb,
/* Try to get same port: if not, try to change it. */
port = nf_nat_exp_find_port(exp, ntohs(exp->saved_proto.tcp.port));
if (port == 0) {
nf_ct_helper_log(skb, exp->master, "all ports in use");
nf_ct_helper_log(skb, ct, "all ports in use");
return NF_DROP;
}
snprintf(buffer, sizeof(buffer), "%u", port);
if (!nf_nat_mangle_udp_packet(skb, exp->master, ctinfo,
if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
protoff, matchoff, matchlen,
buffer, strlen(buffer))) {
nf_ct_helper_log(skb, exp->master, "cannot mangle packet");
nf_ct_helper_log(skb, ct, "cannot mangle packet");
nf_ct_unexpect_related(exp);
return NF_DROP;
}

View File

@@ -61,6 +61,7 @@ static int nf_nat_ftp_fmt_cmd(struct nf_conn *ct, enum nf_ct_ftp_type type,
/* So, this packet has hit the connection tracking matching code.
Mangle it, and change the expectation to match the new version. */
static unsigned int nf_nat_ftp(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
enum nf_ct_ftp_type type,
unsigned int protoff,
@@ -71,7 +72,6 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb,
union nf_inet_addr newaddr;
u_int16_t port;
int dir = CTINFO2DIR(ctinfo);
struct nf_conn *ct = exp->master;
char buffer[sizeof("|1||65535|") + INET6_ADDRSTRLEN];
unsigned int buflen;
@@ -88,7 +88,7 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb,
port = nf_nat_exp_find_port(exp, ntohs(exp->saved_proto.tcp.port));
if (port == 0) {
nf_ct_helper_log(skb, exp->master, "all ports in use");
nf_ct_helper_log(skb, ct, "all ports in use");
return NF_DROP;
}

View File

@@ -30,6 +30,7 @@ static struct nf_conntrack_nat_helper nat_helper_irc =
NF_CT_NAT_HELPER_INIT(NAT_HELPER_NAME);
static unsigned int help(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
unsigned int protoff,
unsigned int matchoff,
@@ -37,7 +38,6 @@ static unsigned int help(struct sk_buff *skb,
struct nf_conntrack_expect *exp)
{
char buffer[sizeof("4294967296 65635")];
struct nf_conn *ct = exp->master;
union nf_inet_addr newaddr;
u_int16_t port;

View File

@@ -273,12 +273,17 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
SIP_HDR_CONTACT, &in_header,
&matchoff, &matchlen,
&addr, &port) > 0) {
int old_len = skb->len, delta;
if (!map_addr(skb, protoff, dataoff, dptr, datalen,
matchoff, matchlen,
&addr, port)) {
nf_ct_helper_log(skb, ct, "cannot mangle contact");
return NF_DROP;
}
delta = (int)skb->len - old_len;
coff += delta;
}
if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) ||

View File

@@ -21,17 +21,16 @@ static struct nf_conntrack_nat_helper nat_helper_tftp =
NF_CT_NAT_HELPER_INIT(NAT_HELPER_NAME);
static unsigned int help(struct sk_buff *skb,
struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
struct nf_conntrack_expect *exp)
{
const struct nf_conn *ct = exp->master;
exp->saved_proto.udp.port
= ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
exp->dir = IP_CT_DIR_REPLY;
exp->expectfn = nf_nat_follow_master;
if (nf_ct_expect_related(exp, 0) != 0) {
nf_ct_helper_log(skb, exp->master, "cannot add expectation");
nf_ct_helper_log(skb, ct, "cannot add expectation");
return NF_DROP;
}
return NF_ACCEPT;

View File

@@ -103,6 +103,7 @@ static struct xt_match tcpmss_mt_reg[] __read_mostly = {
{
.name = "tcpmss",
.family = NFPROTO_IPV6,
.checkentry = tcpmss_mt_check,
.match = tcpmss_mt,
.matchsize = sizeof(struct xt_tcpmss_match_info),
.proto = IPPROTO_TCP,