netfilter pull request 26-10-19

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEjF9xRqF1emXiQiqU1w0aZmrPKyEFAmrWLcoACgkQ1w0aZmrP
 KyGlNg/+OGCFkcwos/7Ph9XHFwZWmSAfqB9dJefnJC3Pz4s1q22ZQzZNaKfUHD8y
 LDlgLzXjBw8XIfiM9yT6/1vaJiBE8ciWYnynDBxOB9QX01t23GqPDA/EdPJ16ket
 LrkJm2tpjNXdqaTm1D8144TaP0/mUGzjqPpH8PwsjCxuDMx0WaaQG4tcU9nWE0S/
 YzfgRq0JmWX0um/TIGA3PCZeCVeJ65yhjpDdWm+/ExzmPr29tTlmTedqLz82EPAf
 Bw/D1MKk3qAs+IJgUCoTTScLPPsS+zMRvO0Go/ftc9A9hztw/iN/Pfd0J8ZdhMcp
 ekwPWvWaZQ1M0WLaffCClao3+86OOSPG0vAQkTHD943asAzu2sv01pnLMopxmJzJ
 gq0irC9Zf7X01M6VazGmJtkvWl7MFee1JJDBO3VsRBw4qBm4pu1D+qTJoqwzgAcG
 X+p3f6mp7rNL7qT7tgG41BaQ727zI9IOFeZstcqDbn+QyCwdgDZkziF3CynUT2St
 FmrhbzxEIQGLMlMRAxNfoX2u64pZ0CU+p2bQku613sFA9WkYLAbsUfyuHGXeIgec
 DqMDRxeuI+82mUdPy4+cEAgPlvN+/tCFdTyoeALM/8GyvUCI1Y6Gj80ky1+Y4Bku
 kA0GVevusfv0H3Fek/Zozi7hXxOctpLDCnNo2PRB4FM0/mqkUQU=
 =ZrLH
 -----END PGP SIGNATURE-----

Merge tag 'nf-26-10-19' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Pablo Neira Ayuso says:

====================
Netfilter/IPVS fixes for net

The following patchset contains Netfilter/IPVS for net:

1) Fix IPv6 nexthdr offset truncation in fragment reassembly,
   this has been uncovered by LLM, from Jeremy Jean.

2) Restrict checksums in nfqueue to known protocols and offset,
   after this patch the plan is to re-enable nfqueue in userns,
   this came via LLM report. from Florian Westhphal.

3) Fix races when handling overload flag in IPVS, this patch
   is not perfect but Julian considers this improves the situation.
   This was uncovered by LLM. From Julian Anastasov.

4) Error handling for IPVS sync_threshold sysctl, from Joel Granados.

5) Use skb_vlan_tag_get_id() to retrieve the VLAN id, discard tci
   bits, when handling VLAN in the flowtable, from Shuangfeng He.

6) Initialize IPCB for inet/ingress reject, uncovered by LLM.

7) Restrict completion of binary search array to anonymous set.

8) Restrict DCCP option matching to avoid out-of-bound read,
   from Sung Byeongchan.

9) Reset transport header offset in flowtable after tunnel
   decapsulation to fix issue with qdisc xmit path,
   from Lorenzo Bianconi.

10) Fix inverted matching in ctnetlink dump filtering,
    from Liu Chao.

* tag 'nf-26-10-19' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nf_conntrack_netlink: fix inverted IPv6 tuple filter
  netfilter: flowtable: reset transport header after nf_flow_encap_pop()
  netfilter: xt_dccp: handle reserved options as single-byte
  netfilter: nft_set_rbtree: narrow down range completion to anonymous sets
  netfilter: nf_reject: initialize IPCB at inet ingress
  netfilter: flowtable: use the vlan id, not the full tci, in the tuple key
  ipvs: skip committing sync_threshold when proc_dointvec fails
  ipvs: fix more races around the overload flag
  netfilter: nft_payload: restrict checksum offsets to known values
  netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset
====================

Link: https://patch.msgid.link/20261019150249.1879516-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-10-20 18:55:19 -07:00
commit d5d524f469
25 changed files with 134 additions and 92 deletions

View File

@ -36,9 +36,6 @@
#define IP_VS_HDR_INVERSE 1
#define IP_VS_HDR_ICMP 2
/* Destination Server Flags */
#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
/* Destination Server Config Flags */
#define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */
@ -973,33 +970,44 @@ struct ip_vs_dest_dst {
* and so on.
*/
struct ip_vs_dest {
/* Cacheline for hash table nodes - read-mostly */
struct list_head n_list; /* for the dests in the service */
struct hlist_node d_list; /* for table with all the dests */
u16 af; /* address family */
__be16 port; /* port number of the server */
union nf_inet_addr addr; /* IP address of the server */
volatile unsigned int flags; /* dest status flags */
atomic_t conn_flags; /* flags to copy to conn */
atomic_t weight; /* server weight */
unsigned long cflags; /* config flags */
atomic_t last_weight; /* server latest weight */
/* connection thresholds */
u32 l_threshold_val;/* used lower threshold */
u32 u_threshold_val;/* used upper threshold */
/* 32/48 */
u32 l_threshold; /* lower threshold */
u32 u_threshold; /* upper threshold */
unsigned long cflags; /* config flags */
/* 44/64 */
atomic_t conn_flags; /* flags to copy to conn */
__u16 tun_type; /* tunnel type */
__be16 tun_port; /* tunnel port */
__u16 tun_flags; /* tunnel flags */
u16 af; /* address family */
__be16 port; /* port number of the server */
/* 60/80 */
union nf_inet_addr addr; /* IP address of the server */
/* connection counters */
atomic_t totalconns; /* total connections */
atomic_t activeconns; /* active connections */
atomic_t persistconns; /* persistent connections */
refcount_t refcnt; /* reference counter */
struct ip_vs_stats stats; /* statistics */
unsigned long idle_start; /* start time, jiffies */
/* connection counters and thresholds */
atomic_t activeconns; /* active connections */
atomic_t totalconns; /* total connections */
atomic_t persistconns; /* persistent connections */
__u32 u_threshold; /* upper threshold */
__u32 l_threshold; /* lower threshold */
__u32 l_threshold_val;/* used lower threshold */
/* for destination cache */
spinlock_t dst_lock; /* lock of dst_cache */
struct ip_vs_dest_dst __rcu *dest_dst; /* cached dst info */
@ -1915,7 +1923,13 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
kfree(dest);
}
void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode);
void ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload);
/* Check if dest is in overloaded state */
static inline bool ip_vs_dest_is_overloaded(const struct ip_vs_dest *dest)
{
return READ_ONCE(dest->l_threshold_val);
}
/* IPVS sync daemon data and function prototypes
* (from ip_vs_sync.c)

View File

@ -357,6 +357,11 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
if (!skb_valid_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0)
return;
if (hook == NF_INET_INGRESS) {
memset(IPCB(skb_in), 0, sizeof(*IPCB(skb_in)));
IPCB(skb_in)->iif = skb_in->skb_iif;
}
if (skb_csum_unnecessary(skb_in) ||
!nf_reject_verify_csum(skb_in, dataoff, proto)) {
icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);

View File

@ -398,7 +398,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
{
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
const int netoff = skb_network_offset(skb);
u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
int prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
int start = netoff + sizeof(struct ipv6hdr);
int len = skb->len - start;
u8 prevhdr = NEXTHDR_IPV6;

View File

@ -444,6 +444,11 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
skb_in->dev = net->loopback_dev;
if (hooknum == NF_INET_INGRESS) {
memset(IP6CB(skb_in), 0, sizeof(*IP6CB(skb_in)));
IP6CB(skb_in)->iif = skb_in->skb_iif;
}
if (!skb_valid_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0)
return;

View File

@ -1153,8 +1153,8 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
if (!(flags & IP_VS_CONN_F_INACTIVE))
atomic_inc(&dest->activeconns);
tc = atomic_inc_return(&dest->totalconns);
if (tc == READ_ONCE(dest->u_threshold))
ip_vs_dest_update_overload(dest, 1);
if (tc >= READ_ONCE(dest->u_threshold_val))
ip_vs_dest_update_overload(dest, true);
} else {
/* It is a persistent connection/template, so increase
the persistent connection counter */
@ -1246,8 +1246,8 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
if (!(cp->flags & IP_VS_CONN_F_INACTIVE))
atomic_dec(&dest->activeconns);
tc = atomic_fetch_dec(&dest->totalconns);
if (tc == READ_ONCE(dest->l_threshold_val))
ip_vs_dest_update_overload(dest, -1);
if (tc <= READ_ONCE(dest->l_threshold_val))
ip_vs_dest_update_overload(dest, false);
} else {
/* It is a persistent connection/template, so decrease
the persistent connection counter */

View File

@ -1309,32 +1309,32 @@ void ip_vs_stats_free(struct ip_vs_stats *stats)
* - conns reach u_threshold and exceed it: set the flag
* - conns go below l_threshold (or 75% of u_threshold): clear the flag
*/
static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload)
{
int conns;
u32 l, u;
lockdep_assert_held(&dest->dst_lock);
u = READ_ONCE(dest->u_threshold);
if (!u)
goto unset;
l = READ_ONCE(dest->l_threshold_val);
conns = atomic_read(&dest->totalconns);
if (conns >= (mode > 0 ? l : u)) {
dest->flags |= IP_VS_DEST_F_OVERLOAD;
return;
if (u) {
/* Low threshold defaults to 75% of upper threshold */
l = READ_ONCE(dest->l_threshold) ? : (u - (u >> 2));
conns = atomic_read(&dest->totalconns);
if (conns >= (overload ? l : u)) {
WRITE_ONCE(dest->u_threshold_val, INT_MAX);
WRITE_ONCE(dest->l_threshold_val, l);
return;
}
}
if (conns >= (mode < 0 ? u : l))
return;
unset:
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
WRITE_ONCE(dest->u_threshold_val, u ? : INT_MAX);
WRITE_ONCE(dest->l_threshold_val, 0);
}
void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
void ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload)
{
spin_lock_bh(&dest->dst_lock);
__ip_vs_dest_update_overload(dest, mode);
__ip_vs_dest_update_overload(dest, overload);
spin_unlock_bh(&dest->dst_lock);
}
@ -1406,15 +1406,13 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
if (READ_ONCE(dest->u_threshold) != udest->u_threshold ||
READ_ONCE(dest->l_threshold) != udest->l_threshold) {
bool overload;
spin_lock_bh(&dest->dst_lock);
WRITE_ONCE(dest->u_threshold, udest->u_threshold);
WRITE_ONCE(dest->l_threshold, udest->l_threshold);
/* Low threshold defaults to 75% of upper threshold */
WRITE_ONCE(dest->l_threshold_val,
udest->l_threshold ? :
(udest->u_threshold -
(udest->u_threshold >> 2)));
__ip_vs_dest_update_overload(dest, 0);
overload = ip_vs_dest_is_overloaded(dest);
__ip_vs_dest_update_overload(dest, overload);
spin_unlock_bh(&dest->dst_lock);
}
@ -2454,6 +2452,8 @@ proc_do_sync_threshold(const struct ctl_table *table, int write,
mutex_lock(&ipvs->sync_mutex);
memcpy(val, valp, sizeof(val));
rc = proc_dointvec(&tmp, write, buffer, lenp, ppos);
if (rc)
goto out;
if (write) {
if (val[0] < 0 || val[1] < 0 ||
(val[0] >= val[1] && val[1]))
@ -2461,6 +2461,7 @@ proc_do_sync_threshold(const struct ctl_table *table, int write,
else
memcpy(valp, val, sizeof(val));
}
out:
mutex_unlock(&ipvs->sync_mutex);
return rc;
}

View File

@ -195,16 +195,6 @@ static int ip_vs_dh_dest_changed(struct ip_vs_service *svc,
}
/*
* If the dest flags is set with IP_VS_DEST_F_OVERLOAD,
* consider that the server is overloaded here.
*/
static inline int is_overloaded(struct ip_vs_dest *dest)
{
return dest->flags & IP_VS_DEST_F_OVERLOAD;
}
/*
* Destination hashing scheduling
*/
@ -219,10 +209,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
s = (struct ip_vs_dh_state *) svc->sched_data;
dest = ip_vs_dh_get(svc->af, s, &iph->daddr);
if (!dest ||
!(dest->cflags & IP_VS_DEST_CF_AVAILABLE)
|| atomic_read(&dest->weight) <= 0
|| is_overloaded(dest)) {
if (!dest || atomic_read(&dest->weight) <= 0 ||
ip_vs_dest_is_overloaded(dest)) {
ip_vs_scheduler_err(svc, "no destination available");
return NULL;
}

View File

@ -29,7 +29,7 @@ ip_vs_fo_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
* Find virtual server with highest weight and send it traffic
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) > hw) {
hweight = dest;
hw = atomic_read(&dest->weight);

View File

@ -418,7 +418,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
* new connection.
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(dest))
continue;
if (atomic_read(&dest->weight) > 0) {
least = dest;
@ -433,7 +433,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_dest_conn_overhead(dest);

View File

@ -166,7 +166,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
/* select the first destination server, whose weight > 0 */
list_for_each_entry_rcu(e, &set->list, list) {
least = e->dest;
if (least->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(least))
continue;
if ((atomic_read(&least->weight) > 0) &&
@ -181,7 +181,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
nextstage:
list_for_each_entry_continue_rcu(e, &set->list, list) {
dest = e->dest;
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_dest_conn_overhead(dest);
@ -580,7 +580,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
* new connection.
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(dest))
continue;
if (atomic_read(&dest->weight) > 0) {
@ -596,7 +596,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_dest_conn_overhead(dest);

View File

@ -38,7 +38,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
if (ip_vs_dest_is_overloaded(dest) ||
atomic_read(&dest->weight) == 0)
continue;
doh = ip_vs_dest_conn_overhead(dest);

View File

@ -80,7 +80,7 @@ static inline void generate_hash_secret(hsiphash_key_t *hash1,
static inline bool is_unavailable(struct ip_vs_dest *dest)
{
return atomic_read(&dest->weight) <= 0 ||
dest->flags & IP_VS_DEST_F_OVERLOAD;
ip_vs_dest_is_overloaded(dest);
}
/* Returns hash value for IPVS MH entry */

View File

@ -72,7 +72,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if (dest->flags & IP_VS_DEST_F_OVERLOAD ||
if (ip_vs_dest_is_overloaded(dest) ||
!atomic_read(&dest->weight))
continue;

View File

@ -33,7 +33,7 @@ ip_vs_ovf_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
w = atomic_read(&dest->weight);
if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
if (ip_vs_dest_is_overloaded(dest) ||
atomic_read(&dest->activeconns) > w ||
w == 0)
continue;

View File

@ -66,7 +66,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) > 0)
/* HIT */
goto out;

View File

@ -75,7 +75,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) > 0) {
least = dest;
loh = ip_vs_sed_dest_overhead(least);
@ -90,7 +90,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_sed_dest_overhead(dest);
if ((__s64)loh * atomic_read(&dest->weight) >

View File

@ -73,7 +73,7 @@ struct ip_vs_sh_state {
static inline bool is_unavailable(struct ip_vs_dest *dest)
{
return atomic_read(&dest->weight) <= 0 ||
dest->flags & IP_VS_DEST_F_OVERLOAD;
ip_vs_dest_is_overloaded(dest);
}
/*

View File

@ -52,7 +52,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct ip_vs_service *svc,
/* Generate a random weight between [0,sum of all weights) */
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if (!(dest->flags & IP_VS_DEST_F_OVERLOAD)) {
if (!ip_vs_dest_is_overloaded(dest)) {
weight = atomic_read(&dest->weight);
if (weight > 0) {
total_weight += weight;
@ -75,7 +75,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct ip_vs_service *svc,
/* Pick two weighted servers */
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(dest))
continue;
weight = atomic_read(&dest->weight);

View File

@ -47,7 +47,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) > 0) {
least = dest;
loh = ip_vs_dest_conn_overhead(least);
@ -62,7 +62,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_dest_conn_overhead(dest);
if ((__s64)loh * atomic_read(&dest->weight) >

View File

@ -176,7 +176,7 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) >= mark->cw)
goto found;
if (dest == stop)

View File

@ -1098,13 +1098,13 @@ static int ctnetlink_filter_match_tuple(struct nf_conntrack_tuple *filter_tuple,
break;
case NFPROTO_IPV6:
if ((flags & CTA_FILTER_FLAG(CTA_IP_SRC)) &&
!ipv6_addr_cmp(&filter_tuple->src.u3.in6,
&ct_tuple->src.u3.in6))
ipv6_addr_cmp(&filter_tuple->src.u3.in6,
&ct_tuple->src.u3.in6))
return 0;
if ((flags & CTA_FILTER_FLAG(CTA_IP_DST)) &&
!ipv6_addr_cmp(&filter_tuple->dst.u3.in6,
&ct_tuple->dst.u3.in6))
ipv6_addr_cmp(&filter_tuple->dst.u3.in6,
&ct_tuple->dst.u3.in6))
return 0;
break;
}

View File

@ -170,14 +170,14 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
int i = 0;
if (skb_vlan_tag_present(skb)) {
tuple->encap[i].id = skb_vlan_tag_get(skb);
tuple->encap[i].id = skb_vlan_tag_get_id(skb);
tuple->encap[i].proto = skb->vlan_proto;
i++;
}
switch (skb->protocol) {
case htons(ETH_P_8021Q):
veth = (struct vlan_ethhdr *)skb_mac_header(skb);
tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
tuple->encap[i].id = ntohs(veth->h_vlan_TCI) & VLAN_VID_MASK;
tuple->encap[i].proto = skb->protocol;
offset += VLAN_HLEN;
break;
@ -502,6 +502,7 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
nf_flow_encap_pop(ctx, skb, tuplehash);
thoff -= ctx->offset;
skb_set_transport_header(skb, thoff);
iph = ip_hdr(skb);
nf_flow_nat_ip(flow, skb, thoff, dir, iph);
@ -1097,6 +1098,7 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
flow_offload_refresh(flow_table, flow, false);
nf_flow_encap_pop(ctx, skb, tuplehash);
skb_set_transport_header(skb, sizeof(*ip6h));
ip6h = ipv6_hdr(skb);
nf_flow_nat_ipv6(flow, skb, dir, ip6h);

View File

@ -20,6 +20,7 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <net/gre.h>
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
@ -1008,11 +1009,14 @@ static bool nft_payload_validate_inet_csum_offset(const struct nft_ctx *ctx,
if (priv->csum_flags) /* makes no sense, asks for "re-update" of L4 checksum */
return false;
/* no further check here; offset can't be negative so bogus
* offsets can corrupt L4 or payload but not l3 headers.
* We already allow arbitrary l4/inner payload writes.
*/
return true;
/* Validate csum_offset is one of the supported transport header checksums */
if (priv->csum_offset == offsetof(struct tcphdr, check) ||
priv->csum_offset == offsetof(struct udphdr, check) ||
priv->csum_offset == offsetof(struct icmphdr, checksum) ||
priv->csum_offset == offsetof(struct icmp6hdr, icmp6_cksum))
return true;
return false;
case NFT_PAYLOAD_INNER_HEADER:
return true;
case NFT_PAYLOAD_TUN_HEADER:
@ -1046,6 +1050,27 @@ static bool nft_payload_csum_nh_write_ok(const struct nft_payload_set *priv,
return false;
}
static bool nft_payload_csum_th_write_ok(const struct nft_payload_set *priv,
const struct nft_pktinfo *pkt)
{
if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
return false;
switch (pkt->tprot) {
case IPPROTO_TCP:
return priv->csum_offset == offsetof(struct tcphdr, check);
case IPPROTO_UDP:
case IPPROTO_UDPLITE:
return priv->csum_offset == offsetof(struct udphdr, check);
case IPPROTO_ICMPV6:
return priv->csum_offset == offsetof(struct icmp6hdr, icmp6_cksum);
case IPPROTO_ICMP:
return priv->csum_offset == offsetof(struct icmphdr, checksum);
}
return false;
}
static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt,
const struct nft_payload_set *priv)
{
@ -1055,9 +1080,10 @@ static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt,
case NFT_PAYLOAD_NETWORK_HEADER:
return nft_payload_csum_nh_write_ok(priv, pkt);
case NFT_PAYLOAD_TRANSPORT_HEADER:
return nft_payload_csum_th_write_ok(priv, pkt);
case NFT_PAYLOAD_INNER_HEADER:
/* neither offsets are validated, offsets cannot be
* negative so real l3 headers cannot be mangled.
/* offset is not validated, offset cannot be
* negative so real l3/l4 headers cannot be mangled.
*/
return true;
case NFT_PAYLOAD_TUN_HEADER:

View File

@ -1091,7 +1091,8 @@ static void nft_rbtree_commit(struct nft_set *set)
* representation. Use next start element to complete this
* interval.
*/
if (nft_rbtree_interval_start(rbe) &&
if (nft_set_is_anonymous(set) &&
nft_rbtree_interval_start(rbe) &&
nft_rbtree_interval_start(prev_rbe) &&
priv->array_next->intervals[num_intervals].from)
priv->array_next->intervals[num_intervals++].to = &prev_rbe->ext;

View File

@ -62,7 +62,7 @@ dccp_find_option(u_int8_t option,
return true;
}
if (op[i] < 2 || i == optlen - 1)
if (op[i] <= DCCPO_MAX_RESERVED || i == optlen - 1)
i++;
else
i += op[i + 1] ? : 1;