netfilter pull request nf-26-07-08

-----BEGIN PGP SIGNATURE-----
 
 iQJdBAABCABHFiEEgKkgxbID4Gn1hq6fcJGo2a1f9gAFAmpOUggbFIAAAAAABAAO
 bWFudTIsMi41KzEuMTIsMiwyDRxmd0BzdHJsZW4uZGUACgkQcJGo2a1f9gBl3g//
 eKE4GL8PgftaxvGbgbPhf8FOeP8zLvR8aC7JU3BhI5HGcRrs8a9rE+jRVauffKqf
 c9bQhcwWLtOKa9kg2MIcPKUhu71TT+eelkfXjLQoMdLdRBOZlDqtFDAVOgHn94ZA
 k5UCmur34u9Kxwh3NASbiwCP53tr4w7Uw48y8oCG0f8avzESv7WRvZn+fECDTdcS
 tTuTmEtislR8NphI03oX1u0s/6k/VnPCz+C+fo5eKp0l5x9tjcpBJtcdZgkwRmYa
 C8p3N4XxoqlpI92hAOoRhSzArOcT01MjMIJXrg1MTuT8G17OtMb1s7EMFsmeDNnD
 omOBiokkN6lYUi9TAWt3hFe2JB4T2kAaJf/eJpznvyNJiLvcPjJtUTOwbagtVy6w
 wOr5/RuRDn0lXbfrGwwZflPbH7EL1WnaWpWMlWnLdH+S/J/Q+mmIY/tkl1v/ydNe
 D6lIM5TbxedDZ/Pk6xor8B0ZdpABVrE14JHge6ChpmT31phQfx+xbcUADAPjpDxF
 B4gjZkXCurDlVUmNIfIK84y+gyB3nC0UtwzsyMU/VtC4JT6Q+oi8Rq4eUyCebyCQ
 vbBdoe/tAt+5B+WqYvtG1pv3ooYorR0f25TJq5p5QbGtEG5sv3FXBhRD8ADGCUPz
 JRQngzjK/D3+nAiUd+3vDJIZwHKwG8QgehBdDQlsHx0=
 =/dia
 -----END PGP SIGNATURE-----

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

Florian Westphal says:

====================
netfilter: updates for net

The following patchset contains Netfilter fixes for *net*.

Most of these are LLM fixes for old issues flagged by sashiko/LLMs.

Many of these trigger drive-by-findings in sashiko. In particular:

- many load/store tearing and missing memory barriers, races
  etc. in ipset, esp. with GC and resizing.
  Keeping the proposed patches spinning for yet-another-iteration
  keeps legit fixes back, so I prefer to add these now and follow
  up with other reports later.
- flowtable work queue still has possible races with teardown,
  but same rationale as with ipset: drive-by findings, not
  problems coming with the flowtable IPIP changeset in this PR.
- ever since unreadable frag skb support was added in 6.12, we can no
  longer do: BUG_ON(skb_copy_bits( ...): it will fire with such skbs.
  Mina Almasry is looking at similar patterns elsewhere in the stack.

1) Guard skb->mac_header adjustment after IPv6 defragmentation in
nf_conntrack_reasm.  From Xiang Mei.

2) NUL-terminate ebtables table names before calling find_table_lock() to
prevent stack-out-of-bounds reads.  Also from Xiang Mei.

3) Zero the ebtables chainstack array, else error unwind may free bogus
pointer when CPU mask is sparse.  All three issues date from 2.6 days.

4) Ensure ebtables module names are c-strings, same bug pattern as 2).
Bug added in 4.6.

5) Fix catchall element handling for inverted lookups in nft_lookup. Fold the
catchall lookup into ext before computing the match status.  Was like
this ever since catchall elements got introduced in 5.13.
From Tamaki Yanagawa.

6-9) ipset updates from Jozsef Kadlecsik:
- mark rcu protected areas correctly
- address gc and resize clash in the comment extension
- add/del backlog cleanup in the error path
- allocate right size for the generic hash structure

10-12): IPIP flowtable updates from Pablo Neira Ayuso:
 - Use the current direction's route when pushing IPIP headers
   Fix incorrect headroom and fragmentation offset calculations.
 - Avoid hardware offload for IPIP tunnels due to lack of driver support.
 - Support IPIP tunnels with direct xmit in netfilter flowtable.
   dst_cache and dst_cookie are moved outside the union to share route
   state across flows.  This is a followup to work done in 6.19 cycle.

13) Don't BUG() on skb_copy_bits error. Handle unreadable fragments by
either returning an error or restricting the copy operations to linear area,
This became an issue when unreable frag support was merged in 6.12.

14-16): IPVS updates from Yizhou Zhao:
 - Pass parsed transport offset to IPVS state handlers.
   update callback signatures.
 - use correct transport header offset on state lookp in TCP.
   As-is it was possible for ipv6 extension header data to be
   treated as L4 header.
 - same for SCTP.  This was also broken since 2.6 days.

17) Ensure inner IP headers in ICMP errors are in the skb headroom after
stripping outer headers. Add more checks for the length of inner headers.
This was broken since 3.7 days.
From Julian Anastasov.

netfilter pull request nf-26-07-08

* tag 'nf-26-07-08' of https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  ipvs: ensure inner headers in ICMP errors are in headroom
  ipvs: use parsed transport offset in SCTP state lookup
  ipvs: use parsed transport offset in TCP state lookup
  ipvs: pass parsed transport offset to state handlers
  netfilter: handle unreadable frags
  netfilter: flowtable: support IPIP tunnel with direct xmit
  netfilter: flowtable: IPIP tunnel hardware offload is not yet support
  netfilter: flowtable: use dst in this direction when pushing IPIP header
  netfilter: ipset: allocate the proper memory for the generic hash structure
  netfilter: ipset: cleanup the add/del backlog when resize failed
  netfilter: ipset: exclude gc when resize is in progress
  netfilter: ipset: mark the rcu locked areas properly
  netfilter: nft_lookup: fix catchall element handling with inverted lookups
  netfilter: ebtables: module names must be null-terminated
  netfilter: ebtables: zero chainstack array
  netfilter: ebtables: terminate table name before find_table_lock()
  netfilter: nf_conntrack_reasm: guard mac_header adjustment after IPv6 defrag
====================

Link: https://patch.msgid.link/20260708140309.19633-1-fw@strlen.de
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-07-09 11:42:56 +02:00
commit 24c4c88259
16 changed files with 196 additions and 109 deletions

View File

@ -752,7 +752,8 @@ struct ip_vs_protocol {
void (*state_transition)(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
struct ip_vs_proto_data *pd);
struct ip_vs_proto_data *pd,
unsigned int iph_len);
int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc);

View File

@ -155,11 +155,12 @@ struct flow_offload_tuple {
tun_num:2,
in_vlan_ingress:2;
u16 mtu;
u32 dst_cookie;
struct dst_entry *dst_cache;
union {
struct {
struct dst_entry *dst_cache;
u32 ifidx;
u32 dst_cookie;
};
struct {
u32 ifidx;
@ -357,6 +358,8 @@ static inline int nf_flow_register_bpf(void)
void nf_flow_offload_add(struct nf_flowtable *flowtable,
struct flow_offload *flow);
void nf_flow_offload_refresh(struct nf_flowtable *flowtable,
struct flow_offload *flow);
void nf_flow_offload_del(struct nf_flowtable *flowtable,
struct flow_offload *flow);
void nf_flow_offload_stats(struct nf_flowtable *flowtable,

View File

@ -403,6 +403,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par,
left - sizeof(struct ebt_entry_match) < m->match_size)
return -EINVAL;
if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
return -EINVAL;
match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision);
if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) {
if (!IS_ERR(match))
@ -921,8 +924,7 @@ static int translate_table(struct net *net, const char *name,
* if an error occurs
*/
newinfo->chainstack =
vmalloc_array(nr_cpu_ids,
sizeof(*(newinfo->chainstack)));
vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack)));
if (!newinfo->chainstack)
return -ENOMEM;
for_each_possible_cpu(i) {
@ -1434,6 +1436,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len)
if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
return -EFAULT;
hlp.name[sizeof(hlp.name) - 1] = '\0';
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
return -EINVAL;
@ -2273,6 +2277,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl,
memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry));
repl->name[sizeof(repl->name) - 1] = '\0';
/* starting with hook_entry, 32 vs. 64 bit structures are different */
for (i = 0; i < NF_BR_NUMHOOKS; i++)
repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]);
@ -2395,6 +2401,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg,
if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
return -EFAULT;
hlp.name[sizeof(hlp.name) - 1] = '\0';
/* try real handler in case userland supplied needed padding */
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
return update_counters(net, arg, len);

View File

@ -348,7 +348,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb,
skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0];
memmove(skb->head + sizeof(struct frag_hdr), skb->head,
(skb->data - skb->head) - sizeof(struct frag_hdr));
skb->mac_header += sizeof(struct frag_hdr);
if (skb_mac_header_was_set(skb))
skb->mac_header += sizeof(struct frag_hdr);
skb->network_header += sizeof(struct frag_hdr);
skb_reset_transport_header(skb);
@ -418,7 +419,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
return -1;
}
if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
BUG();
return -1;
if (nexthdr == NEXTHDR_AUTH)
hdrlen = ipv6_authlen(&hdr);
else

View File

@ -75,15 +75,17 @@ struct hbucket {
struct htable_gc {
struct delayed_work dwork;
struct ip_set *set; /* Set the gc belongs to */
spinlock_t lock; /* Lock to exclude gc and resize */
u32 region; /* Last gc run position */
};
/* The hash table: the table size stored here in order to make resizing easy */
struct htable {
atomic_t ref; /* References for resizing */
bool resizing; /* Mark ongoing resize */
atomic_t uref; /* References for dumping and gc */
u8 htable_bits; /* size of hash table == 2^htable_bits */
u32 maxelem; /* Maxelem per region */
struct list_head ad; /* Resize add|del backlist */
struct ip_set_region *hregion; /* Region locks and ext sizes */
struct hbucket __rcu *bucket[]; /* hashtable buckets */
};
@ -301,11 +303,13 @@ struct htype {
u8 netmask; /* netmask value for subnets to store */
union nf_inet_addr bitmask; /* stores bitmask */
#endif
struct list_head ad; /* Resize add|del backlist */
struct mtype_elem next; /* temporary storage for uadd */
#ifdef IP_SET_HASH_WITH_NETS
struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */
#endif
/* Because 'next' is IPv4/IPv6 dependent, no elements of this
* structure and referred in create() may come after 'next'.
*/
struct mtype_elem next; /* temporary storage for uadd */
};
/* ADD|DEL entries saved during resize */
@ -451,13 +455,14 @@ static void
mtype_destroy(struct ip_set *set)
{
struct htype *h = set->data;
struct htable *t = (__force struct htable *)h->table;
struct list_head *l, *lt;
mtype_ahash_destroy(set, (__force struct htable *)h->table, true);
list_for_each_safe(l, lt, &h->ad) {
list_for_each_safe(l, lt, &t->ad) {
list_del(l);
kfree(l);
}
mtype_ahash_destroy(set, t, true);
kfree(h);
set->data = NULL;
@ -569,9 +574,10 @@ mtype_gc(struct work_struct *work)
set = gc->set;
h = set->data;
spin_lock_bh(&set->lock);
t = ipset_dereference_set(h->table, set);
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
atomic_inc(&t->uref);
rcu_read_unlock_bh();
numof_locks = ahash_numof_locks(t->htable_bits);
r = gc->region++;
if (r >= numof_locks) {
@ -580,11 +586,13 @@ mtype_gc(struct work_struct *work)
next_run = (IPSET_GC_PERIOD(set->timeout) * HZ) / numof_locks;
if (next_run < HZ/10)
next_run = HZ/10;
spin_unlock_bh(&set->lock);
mtype_gc_do(set, h, t, r);
spin_lock_bh(&gc->lock);
if (!t->resizing)
mtype_gc_do(set, h, t, r);
spin_unlock_bh(&gc->lock);
if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
if (atomic_dec_and_test(&t->uref) && t->resizing) {
pr_debug("Table destroy after resize by expire: %p\n", t);
mtype_ahash_destroy(set, t, false);
}
@ -668,15 +676,18 @@ mtype_resize(struct ip_set *set, bool retried)
}
t->htable_bits = htable_bits;
t->maxelem = h->maxelem / ahash_numof_locks(htable_bits);
INIT_LIST_HEAD(&t->ad);
for (i = 0; i < ahash_numof_locks(htable_bits); i++)
spin_lock_init(&t->hregion[i].lock);
/* There can't be another parallel resizing,
* but dumping, gc, kernel side add/del are possible
* but dumping and kernel side add/del are possible
*/
orig = ipset_dereference_bh_nfnl(h->table);
atomic_set(&orig->ref, 1);
atomic_inc(&orig->uref);
spin_lock_bh(&h->gc.lock);
orig->resizing = true;
spin_unlock_bh(&h->gc.lock);
pr_debug("attempt to resize set %s from %u to %u, t %p\n",
set->name, orig->htable_bits, htable_bits, orig);
for (r = 0; r < ahash_numof_locks(orig->htable_bits); r++) {
@ -768,7 +779,7 @@ mtype_resize(struct ip_set *set, bool retried)
* Kernel-side add cannot trigger a resize and userspace actions
* are serialized by the mutex.
*/
list_for_each_safe(l, lt, &h->ad) {
list_for_each_safe(l, lt, &orig->ad) {
x = list_entry(l, struct mtype_resize_ad, list);
if (x->ad == IPSET_ADD) {
mtype_add(set, &x->d, &x->ext, &x->mext, x->flags);
@ -792,11 +803,24 @@ mtype_resize(struct ip_set *set, bool retried)
cleanup:
rcu_read_unlock_bh();
atomic_set(&orig->ref, 0);
spin_lock_bh(&h->gc.lock);
orig->resizing = false;
spin_unlock_bh(&h->gc.lock);
/* Make sure parallel readers see that orig->resizing is false
* before we decrement uref */
synchronize_rcu();
atomic_dec(&orig->uref);
mtype_ahash_destroy(set, t, false);
if (ret == -EAGAIN)
goto retry;
/* Cleanup the backlog of ADD/DEL elements */
spin_lock_bh(&set->lock);
list_for_each_safe(l, lt, &orig->ad) {
list_del(l);
kfree(l);
}
spin_unlock_bh(&set->lock);
goto out;
hbwarn:
@ -860,15 +884,13 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
key = HKEY(value, h->initval, t->htable_bits);
r = ahash_region(key);
atomic_inc(&t->uref);
rcu_read_unlock_bh();
elements = t->hregion[r].elements;
maxelem = t->maxelem;
if (elements >= maxelem) {
u32 e;
if (SET_WITH_TIMEOUT(set)) {
rcu_read_unlock_bh();
if (SET_WITH_TIMEOUT(set))
mtype_gc_do(set, h, t, r);
rcu_read_lock_bh();
}
maxelem = h->maxelem;
elements = 0;
for (e = 0; e < ahash_numof_locks(t->htable_bits); e++)
@ -876,7 +898,6 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
if (elements >= maxelem && SET_WITH_FORCEADD(set))
forceadd = true;
}
rcu_read_unlock_bh();
spin_lock_bh(&t->hregion[r].lock);
n = rcu_dereference_bh(hbucket(t, key));
@ -1003,7 +1024,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
ret = 0;
resize:
spin_unlock_bh(&t->hregion[r].lock);
if (atomic_read(&t->ref) && ext->target) {
if (t->resizing && ext && ext->target) {
/* Resize is in process and kernel side add, save values */
struct mtype_resize_ad *x;
@ -1017,7 +1038,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
memcpy(&x->mext, mext, sizeof(struct ip_set_ext));
x->flags = flags;
spin_lock_bh(&set->lock);
list_add_tail(&x->list, &h->ad);
list_add_tail(&x->list, &t->ad);
spin_unlock_bh(&set->lock);
}
goto out;
@ -1030,7 +1051,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
unlock:
spin_unlock_bh(&t->hregion[r].lock);
out:
if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
if (atomic_dec_and_test(&t->uref) && t->resizing) {
pr_debug("Table destroy after resize by add: %p\n", t);
mtype_ahash_destroy(set, t, false);
}
@ -1093,7 +1114,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
#endif
ip_set_ext_destroy(set, data);
if (atomic_read(&t->ref) && ext->target) {
if (t->resizing && ext && ext->target) {
/* Resize is in process and kernel side del,
* save values
*/
@ -1141,10 +1162,10 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
spin_unlock_bh(&t->hregion[r].lock);
if (x) {
spin_lock_bh(&set->lock);
list_add(&x->list, &h->ad);
list_add(&x->list, &t->ad);
spin_unlock_bh(&set->lock);
}
if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
if (atomic_dec_and_test(&t->uref) && t->resizing) {
pr_debug("Table destroy after resize by del: %p\n", t);
mtype_ahash_destroy(set, t, false);
}
@ -1353,7 +1374,7 @@ mtype_uref(struct ip_set *set, struct netlink_callback *cb, bool start)
rcu_read_unlock_bh();
} else if (cb->args[IPSET_CB_PRIVATE]) {
t = (struct htable *)cb->args[IPSET_CB_PRIVATE];
if (atomic_dec_and_test(&t->uref) && atomic_read(&t->ref)) {
if (atomic_dec_and_test(&t->uref) && t->resizing) {
pr_debug("Table destroy after resize "
" by dump: %p\n", t);
mtype_ahash_destroy(set, t, false);
@ -1566,7 +1587,13 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
if (tb[IPSET_ATTR_MAXELEM])
maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]);
hsize = sizeof(*h);
#ifdef IP_SET_PROTO_UNDEF
hsize = sizeof(struct htype);
#else
hsize = set->family == NFPROTO_IPV6 ?
sizeof(struct IPSET_TOKEN(HTYPE, 6)) :
sizeof(struct IPSET_TOKEN(HTYPE, 4));
#endif
h = kzalloc(hsize, GFP_KERNEL);
if (!h)
return -ENOMEM;
@ -1593,6 +1620,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
return -ENOMEM;
}
h->gc.set = set;
spin_lock_init(&h->gc.lock);
for (i = 0; i < ahash_numof_locks(hbits); i++)
spin_lock_init(&t->hregion[i].lock);
h->maxelem = maxelem;
@ -1619,9 +1647,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
}
t->htable_bits = hbits;
t->maxelem = h->maxelem / ahash_numof_locks(hbits);
INIT_LIST_HEAD(&t->ad);
RCU_INIT_POINTER(h->table, t);
INIT_LIST_HEAD(&h->ad);
set->data = h;
#ifndef IP_SET_PROTO_UNDEF
if (set->family == NFPROTO_IPV4) {

View File

@ -398,10 +398,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
static inline void
ip_vs_set_state(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
struct ip_vs_proto_data *pd)
struct ip_vs_proto_data *pd, unsigned int iph_len)
{
if (likely(pd->pp->state_transition))
pd->pp->state_transition(cp, direction, skb, pd);
pd->pp->state_transition(cp, direction, skb, pd, iph_len);
}
static inline int
@ -803,7 +803,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
ip_vs_in_stats(cp, skb);
/* set state */
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len);
/* transmit the first SYN packet */
ret = cp->packet_xmit(skb, cp, pd->pp, iph);
@ -1484,7 +1484,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
after_nat:
ip_vs_out_stats(cp, skb);
ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd);
ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len);
skb->ipvs_property = 1;
if (!(cp->flags & IP_VS_CONN_F_NFCT))
ip_vs_notrack(skb);
@ -1767,6 +1767,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
bool tunnel, new_cp = false;
union nf_inet_addr *raddr;
char *outer_proto = "IPIP";
unsigned int hlen_ipip;
int ulen = 0;
*related = 1;
@ -1804,9 +1805,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Now find the contained IP header */
offset += sizeof(_icmph);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
if (cih == NULL)
if (!(cih && cih->version == 4 && cih->ihl >= 5))
return NF_ACCEPT; /* The packet looks wrong, ignore */
raddr = (union nf_inet_addr *)&cih->daddr;
hlen_ipip = cih->ihl * 4;
/* Special case for errors for IPIP/UDP/GRE tunnel packets */
tunnel = false;
@ -1822,9 +1824,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Only for known tunnel */
if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP)
return NF_ACCEPT;
offset += cih->ihl * 4;
offset += hlen_ipip;
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
if (cih == NULL)
if (!(cih && cih->version == 4 && cih->ihl >= 5))
return NF_ACCEPT; /* The packet looks wrong, ignore */
tunnel = true;
} else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */
@ -1836,7 +1838,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Non-first fragment has no UDP/GRE header */
if (unlikely(cih->frag_off & htons(IP_OFFSET)))
return NF_ACCEPT;
offset2 = offset + cih->ihl * 4;
offset2 = offset + hlen_ipip;
if (cih->protocol == IPPROTO_UDP) {
ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET,
raddr, &iproto);
@ -1905,6 +1907,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
}
if (tunnel) {
unsigned int hlen_orig = cih->ihl * 4;
__be32 info = ic->un.gateway;
__u8 type = ic->type;
__u8 code = ic->code;
@ -1921,6 +1924,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
goto ignore_tunnel;
offset2 -= ihl + sizeof(_icmph);
skb_reset_network_header(skb);
/* Ensure the IP header is present in headroom */
if (!pskb_may_pull(skb, hlen_ipip))
goto ignore_tunnel;
IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n",
outer_proto, &ip_hdr(skb)->saddr,
&ip_hdr(skb)->daddr, mtu);
@ -1936,8 +1942,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (dest_dst)
mtu = dst_mtu(dest_dst->dst_cache);
}
if (mtu > 68 + sizeof(struct iphdr) + ulen)
mtu -= sizeof(struct iphdr) + ulen;
if (mtu > 68 + hlen_ipip + ulen)
mtu -= hlen_ipip + ulen;
info = htonl(mtu);
}
/* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of
@ -1946,6 +1952,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (pskb_pull(skb, offset2) == NULL)
goto ignore_tunnel;
skb_reset_network_header(skb);
/* Ensure the IP header is present in headroom */
if (!pskb_may_pull(skb, hlen_orig))
goto ignore_tunnel;
IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n",
&ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
type, code, ntohl(info));
@ -2233,7 +2242,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet");
ip_vs_in_stats(cp, skb);
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len);
if (cp->packet_xmit)
ret = cp->packet_xmit(skb, cp, pp, &iph);
/* do not touch skb anymore */

View File

@ -372,20 +372,15 @@ static const char *sctp_state_name(int state)
static inline void
set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
int direction, const struct sk_buff *skb)
int direction, const struct sk_buff *skb,
unsigned int iph_len)
{
struct sctp_chunkhdr _sctpch, *sch;
unsigned char chunk_type;
int event, next_state;
int ihl, cofs;
int cofs;
#ifdef CONFIG_IP_VS_IPV6
ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
#else
ihl = ip_hdrlen(skb);
#endif
cofs = ihl + sizeof(struct sctphdr);
cofs = iph_len + sizeof(struct sctphdr);
sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch);
if (sch == NULL)
return;
@ -468,10 +463,11 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
static void
sctp_state_transition(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb, struct ip_vs_proto_data *pd)
const struct sk_buff *skb, struct ip_vs_proto_data *pd,
unsigned int iph_len)
{
spin_lock_bh(&cp->lock);
set_sctp_state(pd, cp, direction, skb);
set_sctp_state(pd, cp, direction, skb, iph_len);
spin_unlock_bh(&cp->lock);
}

View File

@ -579,17 +579,12 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
static void
tcp_state_transition(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
struct ip_vs_proto_data *pd)
struct ip_vs_proto_data *pd,
unsigned int iph_len)
{
struct tcphdr _tcph, *th;
#ifdef CONFIG_IP_VS_IPV6
int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
#else
int ihl = ip_hdrlen(skb);
#endif
th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph);
th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph);
if (th == NULL)
return;

View File

@ -444,7 +444,8 @@ static const char * udp_state_name(int state)
static void
udp_state_transition(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
struct ip_vs_proto_data *pd)
struct ip_vs_proto_data *pd,
unsigned int iph_len)
{
if (unlikely(!pd)) {
pr_err("UDP no ns data\n");

View File

@ -127,12 +127,18 @@ static int flow_offload_fill_route(struct flow_offload *flow,
switch (route->tuple[dir].xmit_type) {
case FLOW_OFFLOAD_XMIT_DIRECT:
if (flow_tuple->tun_num) {
flow_tuple->dst_cache = dst;
flow_tuple->dst_cookie =
flow_offload_dst_cookie(flow_tuple);
}
memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest,
ETH_ALEN);
memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source,
ETH_ALEN);
flow_tuple->out.ifidx = route->tuple[dir].out.ifindex;
dst_release(dst);
if (!flow_tuple->tun_num)
dst_release(dst);
break;
case FLOW_OFFLOAD_XMIT_XFRM:
case FLOW_OFFLOAD_XMIT_NEIGH:
@ -152,9 +158,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
static void nft_flow_dst_release(struct flow_offload *flow,
enum flow_offload_tuple_dir dir)
{
if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)
dst_release(flow->tuplehash[dir].tuple.dst_cache);
dst_release(flow->tuplehash[dir].tuple.dst_cache);
}
void flow_offload_route_init(struct flow_offload *flow,
@ -345,10 +349,8 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
nf_ct_refresh(flow->ct, NF_CT_DAY);
if (nf_flowtable_hw_offload(flow_table)) {
__set_bit(NF_FLOW_HW, &flow->flags);
if (nf_flowtable_hw_offload(flow_table))
nf_flow_offload_add(flow_table, flow);
}
return 0;
}
@ -369,7 +371,8 @@ void flow_offload_refresh(struct nf_flowtable *flow_table,
test_bit(NF_FLOW_CLOSING, &flow->flags))
return;
nf_flow_offload_add(flow_table, flow);
if (test_bit(NF_FLOW_HW, &flow->flags))
nf_flow_offload_refresh(flow_table, flow);
}
EXPORT_SYMBOL_GPL(flow_offload_refresh);

View File

@ -299,8 +299,7 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple)
{
if (tuple->xmit_type != FLOW_OFFLOAD_XMIT_NEIGH &&
tuple->xmit_type != FLOW_OFFLOAD_XMIT_XFRM)
if (!tuple->dst_cache)
return true;
return dst_check(tuple->dst_cache, tuple->dst_cookie);
@ -590,10 +589,10 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id,
static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
struct flow_offload_tuple *tuple,
__be32 *ip_daddr)
struct dst_entry *dst, __be32 *ip_daddr)
{
struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
struct rtable *rt = dst_rtable(tuple->dst_cache);
struct rtable *rt = dst_rtable(dst);
u8 tos = iph->tos, ttl = iph->ttl;
__be16 frag_off = iph->frag_off;
u32 headroom = sizeof(*iph);
@ -636,21 +635,22 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb,
struct flow_offload_tuple *tuple,
__be32 *ip_daddr)
struct dst_entry *dst, __be32 *ip_daddr)
{
if (tuple->tun_num)
return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
return nf_flow_tunnel_ipip_push(net, skb, tuple, dst, ip_daddr);
return 0;
}
static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
struct flow_offload_tuple *tuple,
struct dst_entry *dst,
struct in6_addr **ip6_daddr)
{
struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb);
struct rtable *rt = dst_rtable(tuple->dst_cache);
__u8 dsfield = ipv6_get_dsfield(ip6h);
struct rtable *rt = dst_rtable(dst);
struct flowi6 fl6 = {
.daddr = tuple->tun.src_v6,
.saddr = tuple->tun.dst_v6,
@ -696,10 +696,11 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,
struct flow_offload_tuple *tuple,
struct dst_entry *dst,
struct in6_addr **ip6_daddr)
{
if (tuple->tun_num)
return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr);
return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, dst, ip6_daddr);
return 0;
}
@ -842,7 +843,8 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
other_tuple = &flow->tuplehash[!dir].tuple;
ip_daddr = other_tuple->src_v4.s_addr;
if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0)
if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple,
tuplehash->tuple.dst_cache, &ip_daddr) < 0)
return NF_DROP;
switch (tuplehash->tuple.xmit_type) {
@ -1158,6 +1160,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
ip6_daddr = &other_tuple->src_v6;
if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple,
tuplehash->tuple.dst_cache,
&ip6_daddr) < 0)
return NF_DROP;

View File

@ -1101,9 +1101,17 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable,
return offload;
}
static bool nf_flow_offload_unsupported(struct flow_offload *flow)
{
if (flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.tun_num ||
flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.tun_num)
return true;
void nf_flow_offload_add(struct nf_flowtable *flowtable,
struct flow_offload *flow)
return false;
}
void nf_flow_offload_refresh(struct nf_flowtable *flowtable,
struct flow_offload *flow)
{
struct flow_offload_work *offload;
@ -1114,6 +1122,16 @@ void nf_flow_offload_add(struct nf_flowtable *flowtable,
flow_offload_queue_work(offload);
}
void nf_flow_offload_add(struct nf_flowtable *flowtable,
struct flow_offload *flow)
{
if (nf_flow_offload_unsupported(flow))
return;
set_bit(NF_FLOW_HW, &flow->flags);
nf_flow_offload_refresh(flowtable, flow);
}
void nf_flow_offload_del(struct nf_flowtable *flowtable,
struct flow_offload *flow)
{

View File

@ -676,7 +676,7 @@ __build_packet_message(struct nfnl_log_net *log,
goto nla_put_failure;
if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
BUG();
goto nla_put_failure;
}
nlh->nlmsg_len = inst->skb->tail - old_tail;
@ -698,6 +698,21 @@ static const struct nf_loginfo default_loginfo = {
},
};
static unsigned int nfulnl_get_copy_len(const struct nf_loginfo *li,
const struct sk_buff *skb,
unsigned int copy_len)
{
unsigned int len = skb->len;
if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) &&
li->u.ulog.copy_len < copy_len)
copy_len = li->u.ulog.copy_len;
if (!skb_frags_readable(skb))
len = skb_headlen(skb);
return min(len, copy_len);
}
/* log handler for internal netfilter logging api */
static void
nfulnl_log_packet(struct net *net,
@ -790,14 +805,7 @@ nfulnl_log_packet(struct net *net,
break;
case NFULNL_COPY_PACKET:
data_len = inst->copy_range;
if ((li->u.ulog.flags & NF_LOG_F_COPY_LEN) &&
(li->u.ulog.copy_len < data_len))
data_len = li->u.ulog.copy_len;
if (data_len > skb->len)
data_len = skb->len;
data_len = nfulnl_get_copy_len(li, skb, inst->copy_range);
size += nla_total_size(data_len);
break;

View File

@ -690,6 +690,17 @@ static int nfqnl_put_master_ifindex(struct sk_buff *nlskb, int attr,
}
#endif
static unsigned int nfqnl_get_data_len(const struct sk_buff *entskb,
unsigned int copy_range)
{
unsigned int data_len = entskb->len;
if (!skb_frags_readable(entskb))
data_len = skb_headlen(entskb);
return min(data_len, copy_range);
}
static struct sk_buff *
nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
struct nf_queue_entry *entry,
@ -755,10 +766,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
nf_queue_checksum_help(entskb))
return NULL;
data_len = READ_ONCE(queue->copy_range);
if (data_len > entskb->len)
data_len = entskb->len;
data_len = nfqnl_get_data_len(entskb, READ_ONCE(queue->copy_range));
hlen = skb_zerocopy_headlen(entskb);
hlen = min_t(unsigned int, hlen, data_len);
size += sizeof(struct nlattr) + hlen;

View File

@ -103,13 +103,13 @@ void nft_lookup_eval(const struct nft_expr *expr,
bool found;
ext = nft_set_do_lookup(net, set, &regs->data[priv->sreg]);
if (!ext)
ext = nft_set_catchall_lookup(net, set);
found = !!ext ^ priv->invert;
if (!found) {
ext = nft_set_catchall_lookup(net, set);
if (!ext) {
regs->verdict.code = NFT_BREAK;
return;
}
regs->verdict.code = NFT_BREAK;
return;
}
if (ext) {

View File

@ -14,8 +14,8 @@
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_u32.h>
static bool u32_match_it(const struct xt_u32 *data,
const struct sk_buff *skb)
static int u32_match_it(const struct xt_u32 *data,
const struct sk_buff *skb)
{
const struct xt_u32_test *ct;
unsigned int testind;
@ -40,7 +40,8 @@ static bool u32_match_it(const struct xt_u32 *data,
return false;
if (skb_copy_bits(skb, pos, &n, sizeof(n)) < 0)
BUG();
return -1;
val = ntohl(n);
nnums = ct->nnums;
@ -68,7 +69,7 @@ static bool u32_match_it(const struct xt_u32 *data,
if (skb_copy_bits(skb, at + pos, &n,
sizeof(n)) < 0)
BUG();
return -1;
val = ntohl(n);
break;
}
@ -90,9 +91,14 @@ static bool u32_match_it(const struct xt_u32 *data,
static bool u32_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_u32 *data = par->matchinfo;
bool ret;
int ret;
ret = u32_match_it(data, skb);
if (ret < 0) {
par->hotdrop = true;
return false;
}
return ret ^ data->invert;
}