netfilter pull request 26-07-23

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEjF9xRqF1emXiQiqU1w0aZmrPKyEFAmpiQwsACgkQ1w0aZmrP
 KyEYhhAAqWpGxZnyCNSRSb1HudQOUIIud4yyPdCtTuSMqSol8WW0lPGxs9woJs01
 vT2pbcDg0gnMMPzTCpIY/cj38aFQ5kB1wLn1/rj1XiPxUA2sqpT7vU+6kceGYj7m
 HR0jpvw40b6CgMfQjVjLl1Qdv98LixAgGIskERMuBL64BCt9ZZwpNfbafCRoFYBg
 /R3ehDJivpVbgWifgq2cIsEurEX2q/apEDtn+nO/e9Uv4RcLLOqsEprcR8NaZNns
 Mbv8ZkxeqJ0m2yccz3NcESydLBrapZ0DQjAadxx9i6ApbdbxYo1ApajSl/7HbW7y
 WgW1lO40T+Ngp6GtW7bgJnTkedcNb26RApdhXgH+IwNUqQiKq4Lv8LE+U3FyJFed
 wf442yo6k7jPz0orc9RLsTOJkayR1IwOSV5n05sk7/NMWS3WL8ToCf3L6rk/FonW
 7doZtEj8i8womCgovep5Ct+3q9Mpud2/vzQAquaHT2mPWraKM/ywlClH4N7XO1fx
 jNWohXqsuB/BgkV78yOy4GRqEqQHDIl0B850MoKDiPhH2YgMyuR5lbR97fHsFFp8
 VzEbTa/6AAs8WIFk79DNNnWNgX4z1YuVBXsqQmHD7T/wxOc1CT8xEm1zKJu4lhJf
 DXxz8jOa4gH5SKchXGRhPrKYE0Am+2eX5KjASI+lSrxJsN+dzWM=
 =5ouZ
 -----END PGP SIGNATURE-----

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

Pablo Neira Ayuso says:

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

The following batch contains Netfilter/IPVS fixes for net. This batch
includes a mix of IPVS follow ups related to Sashiko reports, as well as
crash fixes for connection tracking expectation, helpers, ipset and
nf_tables mostly for old bugs. This also includes a fix for the
flowtable tunnel selftest.

1) Use s32 instead of s16 to calculate the remaining payload containing
   SIP messages, otherwise underflow is possible allowing out-of-bound
   memory access beyond the skb->data area. From Xiang Mei.

2) Fix the counter check in the flowtable selftest for tunnels, from
   Lorenzo Bianconi.

3) Add and use nf_ct_expect_related_pair() to add the RTP and RTCP
   expectations under the expectation lock, this is required by the SIP
   and H.323 NAT helpers. This fixes a possible reinsertion of an
   expectation with the DEAD flag set on while looping to find
   consecutive ports.

4) Fix ipset UaF during table resize by blocking comment updates on
   kernel-side adds. From David Lee.

5) Do not propagate the IP_VS_CONN_F_ONE_PACKET flag when using IPVS
   state synchronization, otherwise reaching stale freed from
   ip_vs_conn struct is possible, Zhiling Zou.

6) Adjust the hn1 hash node when the forwarding method changes between
   MASQ and non-MASQ for an already hashed connection.  This can leave
   stale hash nodes pointing to a freed struct ip_vs_conn and trigger
   UaF while reading /proc/net/ip_vs_conn. From Julian Anastasov.

7) nft_object rhltable needs to be per table, just like chain rhltable,
   otherwise UaF from object lookup path while netns is being released.
   There is also the nlevent path that can reach stale objects. Placing
   this rhltable under the table hierarchy fixes this issue.

8) Reject invalid combined usage of hashlimit tables with and without
   XT_HASHLIMIT_RATE_MATCH flag mode, otherwise access to uninitialized
   .burst field of dsthash_ent is possible.

9) Fix checksum validations in IPVS performed from LOCAL_IN,
   from Julian Anastasov.

10) Fix incorrect packet offset to layer 4 protocol in IPVS, uncovered
    by Sashiko, from Julian Anastasov.

11) Skip the mangling of ICMP replies for non-first fragments, also
    reported by Sashiko. Also from Julian.

12) Clear ip_vs_conn flags under the spinlock to fix a possible data
    race. From Julian Anastasov.

13) Fix incorrect calculation of the payload bitmask in the nf_tables
    hardware offload support, leading to UBSAN splat. From Xiang Mei.

* tag 'nf-26-07-23' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nft_payload: fix mask build for partial field offload
  ipvs: clear the nfct flag under lock
  ipvs: do not mangle ICMP replies for non-first fragments
  ipvs: fix places with wrong packet offsets
  ipvs: fix the checksum validations
  netfilter: xt_hashlimit: validate hashtable supports XT_HASHLIMIT_RATE_MATCH
  netfilter: nf_tables: make nft_object rhltable per table
  ipvs: adjust double hashing when fwd method changes
  ipvs: do not propagate one-packet flag to synced conns
  netfilter: ipset: do not update comments from kernel-side hash adds
  netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair()
  selftests: netfilter: nft_flowtable.sh: fix offload counter verification for tunnel tests
  netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in sip_help_tcp()
====================

Link: https://patch.msgid.link/20260723163910.274695-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-07-24 17:10:53 -07:00
commit 53658c6f36
20 changed files with 449 additions and 313 deletions

View File

@ -115,7 +115,7 @@ struct nf_nat_sip_hooks {
unsigned int *datalen);
void (*seq_adjust)(struct sk_buff *skb,
unsigned int protoff, s16 off);
unsigned int protoff, s32 off);
unsigned int (*expect)(struct sk_buff *skb,
unsigned int protoff,

View File

@ -25,7 +25,9 @@
#include <linux/netfilter.h> /* for union nf_inet_addr */
#include <linux/ip.h>
#include <linux/ipv6.h> /* for struct ipv6hdr */
#include <net/route.h>
#include <net/ipv6.h>
#include <net/ip6_fib.h>
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h>
#endif
@ -1972,8 +1974,8 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, int offset,
unsigned int hooknum, struct ip_vs_iphdr *iph);
struct ip_vs_protocol *pp, unsigned int toff,
unsigned int hooknum, struct ip_vs_iphdr *ciph);
void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
#ifdef CONFIG_IP_VS_IPV6
@ -1986,8 +1988,8 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, int offset,
unsigned int hooknum, struct ip_vs_iphdr *iph);
struct ip_vs_protocol *pp, unsigned int toff,
unsigned int hooknum, struct ip_vs_iphdr *ciph);
#endif
#ifdef CONFIG_SYSCTL
@ -2059,15 +2061,15 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp)
}
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int dir);
struct ip_vs_conn *cp, int dir, unsigned int toff,
bool has_ports);
#ifdef CONFIG_IP_VS_IPV6
void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int dir);
struct ip_vs_conn *cp, int dir, unsigned int toff,
bool has_ports, struct ip_vs_iphdr *ciph);
#endif
__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
{
__be32 diff[2] = { ~old, new };
@ -2093,6 +2095,33 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
return csum_partial(diff, sizeof(diff), oldsum);
}
static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
{
/* Checksum unnecessary or already validated? */
if (skb_csum_unnecessary(skb))
return false;
/* LOCAL_OUT ? */
if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
return false;
/* !LOCAL_IN (FORWARD) ? */
if (af == AF_INET6) {
if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
return false;
} else {
if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
return false;
}
return true;
}
static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
int offset, int proto, int af)
{
if (!ip_vs_checksum_needed(skb, af))
return true;
return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
}
/* Forget current conntrack (unconfirmed) and attach notrack entry */
static inline void ip_vs_notrack(struct sk_buff *skb)
{

View File

@ -161,6 +161,9 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect,
return nf_ct_expect_related_report(expect, 0, 0, flags);
}
int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
unsigned int flag);
struct nf_conn_help;
void nf_ct_expectation_gc(struct nf_conn_help *master_help);

View File

@ -1294,6 +1294,7 @@ static inline void nft_use_inc_restore(u32 *use)
* @sets: sets in the table
* @objects: stateful objects in the table
* @flowtables: flow tables in the table
* @objname_ht: hashtable for objects lookup by name
* @hgenerator: handle generator state
* @handle: table handle
* @use: number of chain references to this table
@ -1313,6 +1314,7 @@ struct nft_table {
struct list_head sets;
struct list_head objects;
struct list_head flowtables;
struct rhltable objname_ht;
u64 hgenerator;
u64 handle;
u32 use;
@ -1400,7 +1402,7 @@ static inline void *nft_obj_data(const struct nft_object *obj)
#define nft_expr_obj(expr) *((struct nft_object **)nft_expr_priv(expr))
struct nft_object *nft_obj_lookup(const struct net *net,
const struct nft_table *table,
struct nft_table *table,
const struct nlattr *nla, u32 objtype,
u8 genmask);

View File

@ -182,6 +182,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
struct nf_conntrack_expect *rtp_exp,
struct nf_conntrack_expect *rtcp_exp)
{
struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };
struct nf_ct_h323_master *info = nfct_help_data(ct);
int dir = CTINFO2DIR(ctinfo);
int i;
@ -227,22 +228,13 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
int ret;
rtp_exp->tuple.dst.u.udp.port = htons(nated_port);
ret = nf_ct_expect_related(rtp_exp, 0);
rtcp_exp->tuple.dst.u.udp.port = htons(nated_port + 1);
ret = nf_ct_expect_related_pair(rtp_pair, 0);
if (ret == 0) {
rtcp_exp->tuple.dst.u.udp.port =
htons(nated_port + 1);
ret = nf_ct_expect_related(rtcp_exp, 0);
if (ret == 0)
break;
else if (ret == -EBUSY) {
nf_ct_unexpect_related(rtp_exp);
continue;
} else if (ret < 0) {
nf_ct_unexpect_related(rtp_exp);
nated_port = 0;
break;
}
} else if (ret != -EBUSY) {
break;
} else if (ret == -EBUSY) {
continue;
} else if (ret < 0) {
nated_port = 0;
break;
}

View File

@ -1005,7 +1005,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
#endif
if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(data, set), ext);
if (SET_WITH_COMMENT(set))
if (SET_WITH_COMMENT(set) && !ext->target)
ip_set_init_comment(set, ext_comment(data, set), ext);
if (SET_WITH_SKBINFO(set))
ip_set_init_skbinfo(ext_skbinfo(data, set), ext);

View File

@ -367,7 +367,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
return 0;
th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
th = (struct tcphdr *)(skb->data + ipvsh->len);
/*
* Remember seq number in case this pkt gets resized
@ -443,7 +443,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
return 0;
th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
th = (struct tcphdr *)(skb->data + ipvsh->len);
/*
* Remember seq number in case this pkt gets resized

View File

@ -70,25 +70,45 @@ static struct kmem_cache *ip_vs_conn_cachep __read_mostly;
* bucket or hash table
* - hash table resize works like rehash but always rehashes into new table
* - bit lock on bucket serializes all operations that modify the chain
* - on resize, bucket from the old table is locked before bucket from the
* new table
* - cp->lock protects conn fields like cp->flags, cp->dest
*/
/* Lock conn_tab bucket for conn hash/unhash, not for rehash */
/**
* conn_tab_lock - Lock conn_tab buckets for conn hash/unhash, not for rehash
* @t: hash table for hn0, new_tbl when new_hash=true
* @t2: hash table for hn1, new_tbl when new_hash2=true
* @cp: connection
* @hash_key: hash key for hn0
* @hash_key2: hash key for hn1
* @use2: using hn1 (double hashing) based on the forwarding method
* @new_hash: mode for hn0, hash node (true) or seek node (false)
* @new_hash2: mode for hn1, hash node (true) or seek node (false)
* @head_ret: returned head for hn0
* @head2_ret: returned head for hn1
*
* We support 3 modes:
* - seek mode for both nodes, used for unhashing
* - hash mode for both nodes, used for hashing
* - seek hn0 and hash hn1, used when forwarding method is changed
*/
static __always_inline void
conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,
u32 hash_key2, bool use2, bool new_hash,
struct hlist_bl_head **head_ret, struct hlist_bl_head **head2_ret)
conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_rht *t2, struct ip_vs_conn *cp,
u32 hash_key, u32 hash_key2, bool use2, bool new_hash,
bool new_hash2, struct hlist_bl_head **head_ret,
struct hlist_bl_head **head2_ret)
{
struct hlist_bl_head *head, *head2;
u32 hash_key_new, hash_key_new2;
struct ip_vs_rht *t2 = t;
u32 idx, idx2;
int idx = 0, idx2 = 0;
/* Advance idx2 when new_hash is not set but hash_key2
* is for new table
*/
if (new_hash2 && use2 && t != t2)
idx2++;
idx = hash_key & t->mask;
if (use2)
idx2 = hash_key2 & t->mask;
else
idx2 = idx;
if (!new_hash) {
/* We need to lock the bucket in the right table */
@ -100,46 +120,45 @@ conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,
* both nodes in different tables, use idx/idx2
* for proper lock ordering for heads.
*/
idx = hash_key & t->mask;
idx |= IP_VS_RHT_TABLE_ID_MASK;
}
if (use2) {
if (!ip_vs_rht_same_table(t2, hash_key2)) {
/* It is already moved to new table */
t2 = rcu_dereference(t2->new_tbl);
idx2 = hash_key2 & t2->mask;
idx2 |= IP_VS_RHT_TABLE_ID_MASK;
}
} else {
idx2 = idx;
idx++;
}
}
if (use2 && !new_hash2 && !ip_vs_rht_same_table(t2, hash_key2)) {
/* It is already moved to new table */
t2 = rcu_dereference(t2->new_tbl);
idx2++;
}
if (!use2)
idx2 = idx;
head = t->buckets + (hash_key & t->mask);
head2 = use2 ? t2->buckets + (hash_key2 & t2->mask) : head;
local_bh_disable();
/* Do not touch seqcount, this is a safe operation */
if (idx <= idx2) {
if (idx > idx2 || (head > head2 && idx == idx2)) {
hlist_bl_lock(head2);
hlist_bl_lock(head);
} else {
hlist_bl_lock(head);
if (head != head2)
hlist_bl_lock(head2);
} else {
hlist_bl_lock(head2);
hlist_bl_lock(head);
}
if (!new_hash) {
bool changed;
/* Ensure hash_key is read under lock */
hash_key_new = READ_ONCE(cp->hn0.hash_key);
hash_key_new2 = READ_ONCE(cp->hn1.hash_key);
changed = hash_key != hash_key_new;
if (use2 && !new_hash2) {
hash_key_new2 = READ_ONCE(cp->hn1.hash_key);
changed |= hash_key2 != hash_key_new2;
} else {
hash_key_new2 = hash_key2;
}
/* Hash changed ? */
if (hash_key != hash_key_new ||
(hash_key2 != hash_key_new2 && use2)) {
if (changed) {
if (head != head2)
hlist_bl_unlock(head2);
hlist_bl_unlock(head);
local_bh_enable();
hash_key = hash_key_new;
hash_key2 = hash_key_new2;
goto retry;
@ -155,7 +174,6 @@ static inline void conn_tab_unlock(struct hlist_bl_head *head,
if (head != head2)
hlist_bl_unlock(head2);
hlist_bl_unlock(head);
local_bh_enable();
}
static void ip_vs_conn_expire(struct timer_list *t);
@ -268,8 +286,9 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
use2 = false;
}
conn_tab_lock(t, cp, hash_key, hash_key2, use2, true /* new_hash */,
&head, &head2);
local_bh_disable();
conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, true /* new_hash */,
true /* new_hash2 */, &head, &head2);
cp->flags |= IP_VS_CONN_F_HASHED;
WRITE_ONCE(cp->hn0.hash_key, hash_key);
@ -280,6 +299,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
hlist_bl_add_head_rcu(&cp->hn1.node, head2);
conn_tab_unlock(head, head2);
local_bh_enable();
ret = 1;
/* Schedule resizing if load increases */
@ -306,18 +326,20 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
return refcount_dec_if_one(&cp->refcnt);
rcu_read_lock();
local_bh_disable();
t = rcu_dereference(ipvs->conn_tab);
hash_key = READ_ONCE(cp->hn0.hash_key);
hash_key2 = READ_ONCE(cp->hn1.hash_key);
use2 = ip_vs_conn_use_hash2(cp);
conn_tab_lock(t, cp, hash_key, hash_key2, use2, false /* new_hash */,
&head, &head2);
conn_tab_lock(t, t, cp, hash_key, hash_key2, use2, false /* new_hash */,
false /* new_hash2 */, &head, &head2);
if (cp->flags & IP_VS_CONN_F_HASHED) {
/* Decrease refcnt and unlink conn only if we are last user */
if (refcount_dec_if_one(&cp->refcnt)) {
if (use2 == ip_vs_conn_use_hash2(cp) &&
refcount_dec_if_one(&cp->refcnt)) {
hlist_bl_del_rcu(&cp->hn0.node);
if (use2)
hlist_bl_del_rcu(&cp->hn1.node);
@ -328,6 +350,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
conn_tab_unlock(head, head2);
local_bh_enable();
rcu_read_unlock();
return ret;
@ -632,6 +655,7 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
int ntbl;
int dir;
restart:
/* No packets from inside, so we can do it in 2 steps. */
dir = use2 ? 1 : 0;
@ -686,6 +710,23 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
/* Protect the cp->flags modification */
spin_lock_bh(&cp->lock);
/* Recheck the forwarding method under lock */
if (use2 != ip_vs_conn_use_hash2(cp)) {
use2 = !use2;
if (use2) {
spin_unlock_bh(&cp->lock);
/* Restart with new use2 value */
goto restart;
}
if (dir) {
/* Not started yet, so just skip dir 1 */
spin_unlock_bh(&cp->lock);
dir--;
goto next_dir;
}
/* Just finish dir 0 */
}
/* Lock seqcount only for the old bucket, even if we are on new table
* because it affects the del operation, not the adding.
*/
@ -752,6 +793,61 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
goto next_dir;
}
/* Change forwarding method for hashed conn */
static void ip_vs_conn_change_fwd_mask(struct ip_vs_conn *cp, u32 new_flags)
{
struct netns_ipvs *ipvs = cp->ipvs;
struct hlist_bl_head *head, *head2;
u32 hash2, hash_key, hash_key2;
struct ip_vs_rht *t, *t2;
/* See ip_vs_conn_use_hash2() for reference */
if ((cp->flags & IP_VS_CONN_F_TEMPLATE) ||
/* No change in double hashing ? */
(IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ) ==
((new_flags & IP_VS_CONN_F_FWD_MASK) == IP_VS_CONN_F_MASQ)) {
cp->flags = new_flags;
return;
}
t = rcu_dereference(ipvs->conn_tab);
if (ip_vs_conn_use_hash2(cp)) {
/* Stop double hashing */
hash_key = READ_ONCE(cp->hn0.hash_key);
hash_key2 = READ_ONCE(cp->hn1.hash_key);
conn_tab_lock(t, t, cp, hash_key, hash_key2, true /* use2 */,
false /* new_hash */, false /* new_hash2 */,
&head, &head2);
/* Keep both hash keys in same table */
hash_key = READ_ONCE(cp->hn0.hash_key);
WRITE_ONCE(cp->hn1.hash_key, hash_key);
hlist_bl_del_rcu(&cp->hn1.node);
cp->flags = new_flags;
conn_tab_unlock(head, head2);
} else {
/* Start double hashing */
hash_key = READ_ONCE(cp->hn0.hash_key);
t2 = rcu_dereference(t->new_tbl);
hash2 = ip_vs_conn_hashkey_conn(t2, cp, true);
hash_key2 = ip_vs_rht_build_hash_key(t2, hash2);
/* Change the forwarding method under locked hn0 */
conn_tab_lock(t, t2, cp, hash_key, hash_key2, true /* use2 */,
false /* new_hash */, true /* new_hash2 */,
&head, &head2);
WRITE_ONCE(cp->hn1.hash_key, hash_key2);
cp->flags = new_flags;
hlist_bl_add_head_rcu(&cp->hn1.node, head2);
conn_tab_unlock(head, head2);
}
}
/* Get default load factor to map conn_count/u_thresh to t->size */
static int ip_vs_conn_default_load_factor(struct netns_ipvs *ipvs)
{
@ -1014,6 +1110,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
flags = cp->flags;
/* Bind with the destination and its corresponding transmitter */
if (flags & IP_VS_CONN_F_SYNC) {
/* Synced conns are hashed, so they can not get this flag */
conn_flags &= ~IP_VS_CONN_F_ONE_PACKET;
/* if the connection is not template and is created
* by sync, preserve the activity flag.
*/
@ -1021,9 +1120,18 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
conn_flags &= ~IP_VS_CONN_F_INACTIVE;
/* connections inherit forwarding method from dest */
flags &= ~(IP_VS_CONN_F_FWD_MASK | IP_VS_CONN_F_NOOUTPUT);
flags |= conn_flags;
/* Changing forwarding method for hashed conn can
* happen only under locks
*/
if (cp->flags & IP_VS_CONN_F_HASHED)
ip_vs_conn_change_fwd_mask(cp, flags);
else
cp->flags = flags;
} else {
flags |= conn_flags;
cp->flags = flags;
}
flags |= conn_flags;
cp->flags = flags;
cp->dest = dest;
IP_VS_DBG_BUF(7, "Bind-dest %s c:%s:%d v:%s:%d "

View File

@ -867,7 +867,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }
#endif
__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
{
return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0));
}
@ -924,13 +924,13 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
* - inout: 1=in->out, 0=out->in
*/
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int inout)
struct ip_vs_conn *cp, int inout, unsigned int toff,
bool has_ports)
{
struct iphdr *iph = ip_hdr(skb);
unsigned int icmp_offset = iph->ihl*4;
struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) +
icmp_offset);
struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff);
struct iphdr *ciph = (struct iphdr *)(icmph + 1);
unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
if (inout) {
iph->saddr = cp->vaddr.ip;
@ -945,8 +945,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
}
/* the TCP/UDP/SCTP port */
if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
IPPROTO_SCTP == ciph->protocol) {
if (has_ports) {
__be16 *ports = (void *)ciph + ciph->ihl*4;
if (inout)
@ -957,48 +956,40 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
/* And finally the ICMP checksum */
icmph->checksum = 0;
icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset);
icmph->checksum = ip_vs_checksum_complete(skb, toff);
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (inout)
IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
"Forwarding altered outgoing ICMP");
IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
"Forwarding altered outgoing ICMP");
else
IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
"Forwarding altered incoming ICMP");
IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
"Forwarding altered incoming ICMP");
}
#ifdef CONFIG_IP_VS_IPV6
void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int inout)
struct ip_vs_conn *cp, int inout, unsigned int toff,
bool has_ports, struct ip_vs_iphdr *ciph)
{
struct ipv6hdr *iph = ipv6_hdr(skb);
unsigned int icmp_offset = 0;
unsigned int offs = 0; /* header offset*/
int protocol;
struct icmp6hdr *icmph;
struct ipv6hdr *ciph;
unsigned short fragoffs;
struct ipv6hdr *cih;
ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
offs = icmp_offset + sizeof(struct icmp6hdr);
ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
icmph = (struct icmp6hdr *)(skb->data + toff);
cih = (struct ipv6hdr *)(skb->data + ciph->off);
if (inout) {
iph->saddr = cp->vaddr.in6;
ciph->daddr = cp->vaddr.in6;
cih->daddr = cp->vaddr.in6;
} else {
iph->daddr = cp->daddr.in6;
ciph->saddr = cp->daddr.in6;
cih->saddr = cp->daddr.in6;
}
/* the TCP/UDP/SCTP port */
if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
IPPROTO_SCTP == protocol)) {
__be16 *ports = (void *)(skb_network_header(skb) + offs);
if (has_ports) {
__be16 *ports = (void *)(skb->data + ciph->len);
IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
ntohs(inout ? ports[1] : ports[0]),
@ -1011,19 +1002,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
/* And finally the ICMP checksum */
icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr,
skb->len - icmp_offset,
skb->len - toff,
IPPROTO_ICMPV6, 0);
skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset;
skb->csum_start = skb_headroom(skb) + toff;
skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum);
skb->ip_summed = CHECKSUM_PARTIAL;
if (inout)
IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
(void *)ciph - (void *)iph,
IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
"Forwarding altered outgoing ICMPv6");
else
IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
(void *)ciph - (void *)iph,
IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
"Forwarding altered incoming ICMPv6");
}
#endif
@ -1033,36 +1022,41 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
*/
static int handle_response_icmp(int af, struct sk_buff *skb,
union nf_inet_addr *snet,
__u8 protocol, struct ip_vs_conn *cp,
struct ip_vs_conn *cp,
struct ip_vs_protocol *pp,
unsigned int offset, unsigned int ihl,
unsigned int hooknum)
struct ip_vs_iphdr *ciph,
unsigned int toff, unsigned int hooknum)
{
int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
unsigned int verdict = NF_DROP;
unsigned int ctoff = ciph->len;
bool has_ports = false;
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
goto after_nat;
/* Ensure the checksum is correct */
if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) {
/* Failed checksum! */
IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
IP_VS_DBG_ADDR(af, snet));
goto out;
}
if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
IPPROTO_SCTP == protocol)
offset += 2 * sizeof(__u16);
if (skb_ensure_writable(skb, offset))
if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
ciph->protocol == IPPROTO_SCTP) {
ctoff += 2 * sizeof(__u16);
has_ports = true;
}
if (skb_ensure_writable(skb, ctoff))
goto out;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
ip_vs_nat_icmp_v6(skb, pp, cp, 1);
ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
else
#endif
ip_vs_nat_icmp(skb, pp, cp, 1);
ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
goto out;
@ -1090,9 +1084,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
* Currently handles error types - unreachable, quench, ttl exceeded.
*/
static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
int *related, unsigned int hooknum)
int *related, unsigned int hooknum,
struct ip_vs_iphdr *ipvsh)
{
struct iphdr *iph;
struct icmphdr _icmph, *ic;
struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */
struct ip_vs_iphdr ciph;
@ -1107,17 +1101,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
if (ip_is_fragment(ip_hdr(skb))) {
if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh))
return NF_ACCEPT;
}
iph = ip_hdr(skb);
offset = ihl = iph->ihl * 4;
ihl = ipvsh->len;
offset = ipvsh->len;
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
if (ic == NULL)
return NF_DROP;
IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
ic->type, ntohs(icmp_id(ic)),
&iph->saddr, &iph->daddr);
&ipvsh->saddr.ip, &ipvsh->daddr.ip);
/*
* Work through seeing if this is for us.
@ -1136,7 +1132,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
/* 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 */
pp = ip_vs_proto_get(cih->protocol);
@ -1144,8 +1140,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
/* Is the embedded protocol header present? */
if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
pp->dont_defrag))
if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
return NF_ACCEPT;
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
@ -1159,9 +1154,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
if (!cp)
return NF_ACCEPT;
snet.ip = iph->saddr;
return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
pp, ciph.len, ihl, hooknum);
snet.ip = ipvsh->saddr.ip;
return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
hooknum);
}
#ifdef CONFIG_IP_VS_IPV6
@ -1174,7 +1169,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
union nf_inet_addr snet;
unsigned int offset;
*related = 1;
ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph);
@ -1210,6 +1204,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
if (!pp)
return NF_ACCEPT;
/* Is the embedded protocol header present? */
if (unlikely(ciph.fragoffs && !pp->dont_defrag))
return NF_ACCEPT;
/* The embedded headers contain source and dest in reverse order */
cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
ipvs, AF_INET6, skb, &ciph);
@ -1217,9 +1215,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
snet.in6 = ciph.saddr.in6;
offset = ciph.len;
return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
pp, offset, ipvsh->len, hooknum);
return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph,
ipvsh->len, hooknum);
}
#endif
@ -1545,7 +1542,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat
#endif
if (unlikely(iph.protocol == IPPROTO_ICMP)) {
int related;
int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum);
int verdict = ip_vs_out_icmp(ipvs, skb, &related,
hooknum, &iph);
if (related)
return verdict;
@ -1753,9 +1751,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
*/
static int
ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
unsigned int hooknum)
unsigned int hooknum, struct ip_vs_iphdr *iph)
{
struct iphdr *iph;
struct icmphdr _icmph, *ic;
struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */
struct ip_vs_iphdr ciph;
@ -1765,7 +1762,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
unsigned int offset, offset2, ihl, verdict;
bool tunnel, new_cp = false;
union nf_inet_addr *raddr;
char *outer_proto = "IPIP";
char *outer_proto __maybe_unused = "IPIP";
unsigned int hlen_ipip;
int ulen = 0;
@ -1775,17 +1772,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (ip_is_fragment(ip_hdr(skb))) {
if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph))
return NF_ACCEPT;
}
iph = ip_hdr(skb);
offset = ihl = iph->ihl * 4;
ihl = iph->len;
offset = iph->len;
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
if (ic == NULL)
return NF_DROP;
IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
ic->type, ntohs(icmp_id(ic)),
&iph->saddr, &iph->daddr);
&iph->saddr.ip, &iph->daddr.ip);
/*
* Work through seeing if this is for us.
@ -1867,8 +1866,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
pp = pd->pp;
/* Is the embedded protocol header present? */
if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
pp->dont_defrag))
if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
return NF_ACCEPT;
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
@ -1876,7 +1874,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
offset2 = offset;
ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
offset = ciph.len;
/* The embedded headers contain source and dest in reverse order.
* For IPIP/UDP/GRE tunnel this is error for request, not for reply.
@ -1898,10 +1895,11 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
verdict = NF_DROP;
/* Ensure the checksum is correct */
if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) &&
!ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
/* Failed checksum! */
IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
&iph->saddr);
&iph->saddr.ip);
goto out;
}
@ -1969,10 +1967,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* do the statistics and put it back */
ip_vs_in_stats(cp, skb);
if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
IPPROTO_SCTP == cih->protocol)
offset += 2 * sizeof(__u16);
verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph);
out:
if (likely(!new_cp))
@ -2032,8 +2027,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
pp = pd->pp;
/* Cannot handle fragmented embedded protocol */
if (ciph.fragoffs)
/* Is the embedded protocol header present? */
if (ciph.fragoffs && !pp->dont_defrag)
return NF_ACCEPT;
IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
@ -2057,23 +2052,22 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
new_cp = true;
}
/* VS/TUN, VS/DR and LOCALNODE just let it go */
if ((hooknum == NF_INET_LOCAL_OUT) &&
(IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
verdict = NF_ACCEPT;
verdict = NF_DROP;
/* Ensure the checksum is correct */
if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ &&
!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6,
AF_INET6)) {
/* Failed checksum! */
IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n",
&iph->saddr);
goto out;
}
/* do the statistics and put it back */
ip_vs_in_stats(cp, skb);
/* Need to mangle contained IPv6 header in ICMPv6 packet */
offset = ciph.len;
if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
IPPROTO_SCTP == ciph.protocol)
offset += 2 * sizeof(__u16); /* Also mangle ports */
verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph);
verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph);
out:
if (likely(!new_cp))
@ -2152,7 +2146,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
if (unlikely(iph.protocol == IPPROTO_ICMP)) {
int related;
int verdict = ip_vs_in_icmp(ipvs, skb, &related,
hooknum);
hooknum, &iph);
if (related)
return verdict;
@ -2200,8 +2194,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
}
if (resched) {
if (!old_ct)
if (!old_ct) {
spin_lock_bh(&cp->lock);
cp->flags &= ~IP_VS_CONN_F_NFCT;
spin_unlock_bh(&cp->lock);
}
if (!atomic_read(&cp->n_control))
ip_vs_conn_expire_now(cp);
__ip_vs_conn_put(cp);
@ -2217,8 +2214,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
if (sysctl_expire_nodest_conn(ipvs)) {
bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
if (!old_ct)
if (!old_ct) {
spin_lock_bh(&cp->lock);
cp->flags &= ~IP_VS_CONN_F_NFCT;
spin_unlock_bh(&cp->lock);
}
ip_vs_conn_expire_now(cp);
__ip_vs_conn_put(cp);
@ -2288,6 +2288,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct netns_ipvs *ipvs = net_ipvs(state->net);
struct ip_vs_iphdr iphdr;
int r;
/* ipvs enabled in this netns ? */
@ -2297,10 +2298,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
if (state->pf == NFPROTO_IPV4) {
if (ip_hdr(skb)->protocol != IPPROTO_ICMP)
return NF_ACCEPT;
ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr);
#ifdef CONFIG_IP_VS_IPV6
} else {
struct ip_vs_iphdr iphdr;
ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr);
if (iphdr.protocol != IPPROTO_ICMPV6)
@ -2310,7 +2310,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
#endif
}
return ip_vs_in_icmp(ipvs, skb, &r, state->hook);
return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr);
}
static const struct nf_hook_ops ip_vs_ops4[] = {

View File

@ -11,7 +11,7 @@
static int
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int sctphoff);
struct ip_vs_iphdr *iph);
static int
sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
if (!sctp_csum_check(cp->af, skb, pp, iph))
return 0;
/* Call application helper if needed */
@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
sctph = (void *) skb_network_header(skb) + sctphoff;
sctph = (void *)skb->data + sctphoff;
/* Only update csum if we really have to */
if (sctph->source != cp->vport || payload_csum ||
@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
if (!sctp_csum_check(cp->af, skb, pp, iph))
return 0;
/* Call application helper if needed */
@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
sctph = (void *) skb_network_header(skb) + sctphoff;
sctph = (void *)skb->data + sctphoff;
/* Only update csum if we really have to */
if (sctph->dest != cp->dport || payload_csum ||
@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int sctphoff)
struct ip_vs_iphdr *iph)
{
unsigned int sctphoff = iph->len;
struct sctphdr *sh;
__le32 cmp, val;
if (!ip_vs_checksum_needed(skb, af))
return 1;
sh = (struct sctphdr *)(skb->data + sctphoff);
cmp = sh->checksum;
val = sctp_compute_cksum(skb, sctphoff);
if (val != cmp) {
/* CRC failure, dump it. */
IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
"Failed checksum for");
IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
"Failed checksum for");
return 0;
}
return 1;

View File

@ -29,7 +29,7 @@
static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int tcphoff);
struct ip_vs_iphdr *iph);
static int
tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@ -166,7 +166,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
if (!tcp_csum_check(cp->af, skb, pp, iph))
return 0;
/* Call application helper if needed */
@ -179,7 +179,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
tcph = (void *)skb_network_header(skb) + tcphoff;
tcph = (void *)skb->data + tcphoff;
tcph->source = cp->vport;
/* Adjust TCP checksums */
@ -244,7 +244,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
if (!tcp_csum_check(cp->af, skb, pp, iph))
return 0;
/*
@ -260,7 +260,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
tcph = (void *)skb_network_header(skb) + tcphoff;
tcph = (void *)skb->data + tcphoff;
tcph->dest = cp->dport;
/*
@ -302,41 +302,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int tcphoff)
struct ip_vs_iphdr *iph)
{
switch (skb->ip_summed) {
case CHECKSUM_NONE:
skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
fallthrough;
case CHECKSUM_COMPLETE:
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
skb->len - tcphoff,
IPPROTO_TCP,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
"Failed checksum for");
return 0;
}
} else
#endif
if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr,
skb->len - tcphoff,
ip_hdr(skb)->protocol,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
"Failed checksum for");
return 0;
}
break;
default:
/* No need to checksum. */
break;
if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) {
IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
"Failed checksum for");
return 0;
}
return 1;
}

View File

@ -25,7 +25,7 @@
static int
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int udphoff);
struct ip_vs_iphdr *iph);
static int
udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@ -155,7 +155,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!udp_csum_check(cp->af, skb, pp, udphoff))
if (!udp_csum_check(cp->af, skb, pp, iph))
return 0;
/*
@ -170,7 +170,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
udph = (void *)skb_network_header(skb) + udphoff;
udph = (void *)skb->data + udphoff;
udph->source = cp->vport;
/*
@ -238,7 +238,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
if (!udp_csum_check(cp->af, skb, pp, udphoff))
if (!udp_csum_check(cp->af, skb, pp, iph))
return 0;
/*
@ -254,7 +254,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
udph = (void *)skb_network_header(skb) + udphoff;
udph = (void *)skb->data + udphoff;
udph->dest = cp->dport;
/*
@ -298,48 +298,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
unsigned int udphoff)
struct ip_vs_iphdr *iph)
{
struct udphdr _udph, *uh;
uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
if (uh == NULL)
return 0;
if (uh->check != 0) {
switch (skb->ip_summed) {
case CHECKSUM_NONE:
skb->csum = skb_checksum(skb, udphoff,
skb->len - udphoff, 0);
fallthrough;
case CHECKSUM_COMPLETE:
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
skb->len - udphoff,
IPPROTO_UDP,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
"Failed checksum for");
return 0;
}
} else
#endif
if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr,
skb->len - udphoff,
ip_hdr(skb)->protocol,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
"Failed checksum for");
return 0;
}
break;
default:
/* No need to checksum. */
break;
}
if (!uh->check)
return 1;
if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) {
IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
"Failed checksum for");
return 0;
}
return 1;
}

View File

@ -1502,20 +1502,22 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/
int
ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
struct ip_vs_iphdr *iph)
struct ip_vs_protocol *pp, unsigned int toff,
unsigned int hooknum, struct ip_vs_iphdr *ciph)
{
struct rtable *rt; /* Route to the other host */
int rc;
int local;
int rt_mode, was_input;
bool has_ports = false;
unsigned int wlen;
/* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
forwarded directly here, because there is no need to
translate address/port back */
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
if (cp->packet_xmit)
rc = cp->packet_xmit(skb, cp, pp, iph);
rc = cp->packet_xmit(skb, cp, pp, ciph);
else
rc = NF_ACCEPT;
/* do not touch skb anymore */
@ -1533,7 +1535,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
NULL, iph);
NULL, ciph);
if (local < 0)
goto tx_error;
rt = skb_rtable(skb);
@ -1564,14 +1566,21 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
goto tx_error;
}
wlen = ciph->len;
if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
ciph->protocol == IPPROTO_SCTP) {
wlen += 2 * sizeof(__u16); /* Also mangle ports */
has_ports = true;
}
/* copy-on-write the packet before mangling it */
if (skb_ensure_writable(skb, offset))
if (skb_ensure_writable(skb, wlen))
goto tx_error;
if (skb_cow(skb, rt->dst.dev->hard_header_len))
goto tx_error;
ip_vs_nat_icmp(skb, pp, cp, 0);
ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;
@ -1587,10 +1596,12 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
struct ip_vs_iphdr *ipvsh)
struct ip_vs_protocol *pp, unsigned int toff,
unsigned int hooknum, struct ip_vs_iphdr *ciph)
{
bool has_ports = false;
struct rt6_info *rt; /* Route to the other host */
unsigned int wlen;
int rc;
int local;
int rt_mode;
@ -1600,7 +1611,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
translate address/port back */
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
if (cp->packet_xmit)
rc = cp->packet_xmit(skb, cp, pp, ipvsh);
rc = cp->packet_xmit(skb, cp, pp, ciph);
else
rc = NF_ACCEPT;
/* do not touch skb anymore */
@ -1617,7 +1628,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
&cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
&cp->daddr.in6, NULL, ciph, 0, rt_mode);
if (local < 0)
goto tx_error;
rt = dst_rt6_info(skb_dst(skb));
@ -1648,14 +1659,21 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
goto tx_error;
}
wlen = ciph->len;
if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
ciph->protocol == IPPROTO_SCTP) {
wlen += 2 * sizeof(__u16); /* Also mangle ports */
has_ports = true;
}
/* copy-on-write the packet before mangling it */
if (skb_ensure_writable(skb, offset))
if (skb_ensure_writable(skb, wlen))
goto tx_error;
if (skb_cow(skb, rt->dst.dev->hard_header_len))
goto tx_error;
ip_vs_nat_icmp_v6(skb, pp, cp, 0);
ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph);
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;

View File

@ -427,7 +427,6 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp,
exp->timeout += helper->expect_policy[exp->class].timeout * HZ;
hlist_add_head_rcu(&exp->lnode, &master_help->expectations);
master_help->expecting[exp->class]++;
hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
cnet = nf_ct_pernet(net);
@ -534,6 +533,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
if (ret < 0)
goto out;
master_help->expecting[expect->class]++;
nf_ct_expect_insert(expect, master_help);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
@ -546,6 +546,39 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
int nf_ct_expect_related_pair(struct nf_conntrack_expect *expect[],
unsigned int flags)
{
struct nf_conn_help *master_help;
int i, ret;
spin_lock_bh(&nf_conntrack_expect_lock);
master_help = nfct_help(expect[0]->master);
if (!master_help || master_help != nfct_help(expect[1]->master)) {
ret = -EINVAL;
goto out;
}
for (i = 0; i < 2; i++) {
ret = __nf_ct_expect_check(expect[i], master_help, flags);
if (ret < 0) {
if (i == 1)
master_help->expecting[expect[0]->class]--;
goto out;
}
master_help->expecting[expect[i]->class]++;
}
for (i = 0; i < 2; i++) {
nf_ct_expect_insert(expect[i], master_help);
nf_ct_expect_event_report(IPEXP_NEW, expect[i], 0, 0);
}
out:
spin_unlock_bh(&nf_conntrack_expect_lock);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related_pair);
void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data),
void *data)
{

View File

@ -1663,7 +1663,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
unsigned int matchoff, matchlen;
unsigned int msglen, origlen;
const char *dptr, *end;
s16 diff, tdiff = 0;
s32 diff, tdiff = 0;
int ret = NF_ACCEPT;
unsigned long clen;
bool term;

View File

@ -321,7 +321,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
}
static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
s16 off)
s32 off)
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
@ -592,6 +592,7 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
unsigned int medialen,
union nf_inet_addr *rtp_addr)
{
struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
@ -622,24 +623,15 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
int ret;
rtp_exp->tuple.dst.u.udp.port = htons(port);
ret = nf_ct_expect_related(rtp_exp,
NF_CT_EXP_F_SKIP_MASTER);
if (ret == -EBUSY)
continue;
else if (ret < 0) {
port = 0;
break;
}
rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
ret = nf_ct_expect_related(rtcp_exp,
NF_CT_EXP_F_SKIP_MASTER);
ret = nf_ct_expect_related_pair(rtp_pair,
NF_CT_EXP_F_SKIP_MASTER);
if (ret == 0)
break;
else if (ret == -EBUSY) {
nf_ct_unexpect_related(rtp_exp);
else if (ret == -EBUSY)
continue;
} else if (ret < 0) {
nf_ct_unexpect_related(rtp_exp);
else if (ret < 0) {
port = 0;
break;
}

View File

@ -45,8 +45,6 @@ enum {
NFT_VALIDATE_DO,
};
static struct rhltable nft_objname_ht;
static u32 nft_chain_hash(const void *data, u32 len, u32 seed);
static u32 nft_chain_hash_obj(const void *data, u32 len, u32 seed);
static int nft_chain_hash_cmp(struct rhashtable_compare_arg *, const void *);
@ -1635,6 +1633,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
if (err)
goto err_chain_ht;
err = rhltable_init(&table->objname_ht, &nft_objname_ht_params);
if (err < 0)
goto err_obj_ht;
INIT_LIST_HEAD(&table->chains);
INIT_LIST_HEAD(&table->sets);
INIT_LIST_HEAD(&table->objects);
@ -1653,6 +1655,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
list_add_tail_rcu(&table->list, &nft_net->tables);
return 0;
err_trans:
rhltable_destroy(&table->objname_ht);
err_obj_ht:
rhltable_destroy(&table->chains_ht);
err_chain_ht:
kfree(table->udata);
@ -1819,6 +1823,7 @@ static void nf_tables_table_destroy(struct nft_table *table)
return;
rhltable_destroy(&table->chains_ht);
rhltable_destroy(&table->objname_ht);
kfree(table->name);
kfree(table->udata);
kfree(table);
@ -8086,7 +8091,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type)
EXPORT_SYMBOL_GPL(nft_unregister_obj);
struct nft_object *nft_obj_lookup(const struct net *net,
const struct nft_table *table,
struct nft_table *table,
const struct nlattr *nla, u32 objtype,
u8 genmask)
{
@ -8102,7 +8107,7 @@ struct nft_object *nft_obj_lookup(const struct net *net,
!lockdep_commit_lock_is_held(net));
rcu_read_lock();
list = rhltable_lookup(&nft_objname_ht, &k, nft_objname_ht_params);
list = rhltable_lookup(&table->objname_ht, &k, nft_objname_ht_params);
if (!list)
goto out;
@ -8382,7 +8387,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
if (err < 0)
goto err_trans;
err = rhltable_insert(&nft_objname_ht, &obj->rhlhead,
err = rhltable_insert(&table->objname_ht, &obj->rhlhead,
nft_objname_ht_params);
if (err < 0)
goto err_obj_ht;
@ -8567,8 +8572,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info,
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net);
u8 family = info->nfmsg->nfgen_family;
const struct nft_table *table;
struct net *net = info->net;
struct nft_table *table;
struct nft_object *obj;
struct sk_buff *skb2;
u32 objtype;
@ -10437,9 +10442,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain)
nf_tables_commit_chain_free_rules_old(g0);
}
static void nft_obj_del(struct nft_object *obj)
static void nft_obj_del(struct nft_table *table, struct nft_object *obj)
{
rhltable_remove(&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params);
rhltable_remove(&table->objname_ht, &obj->rhlhead, nft_objname_ht_params);
list_del_rcu(&obj->list);
}
@ -11124,7 +11129,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
break;
case NFT_MSG_DELOBJ:
case NFT_MSG_DESTROYOBJ:
nft_obj_del(nft_trans_obj(trans));
nft_obj_del(table, nft_trans_obj(trans));
nf_tables_obj_notify(&ctx, nft_trans_obj(trans),
trans->msg_type);
break;
@ -11416,7 +11421,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans);
} else {
nft_use_dec_restore(&table->use);
nft_obj_del(nft_trans_obj(trans));
nft_obj_del(table, nft_trans_obj(trans));
}
break;
case NFT_MSG_DELOBJ:
@ -12043,7 +12048,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
nft_set_destroy(&ctx, set);
}
list_for_each_entry_safe(obj, ne, &table->objects, list) {
nft_obj_del(obj);
nft_obj_del(table, obj);
nft_use_dec(&table->use);
nft_obj_destroy(&ctx, obj);
}
@ -12225,10 +12230,6 @@ static int __init nf_tables_module_init(void)
if (err < 0)
goto err_netdev_notifier;
err = rhltable_init(&nft_objname_ht, &nft_objname_ht_params);
if (err < 0)
goto err_rht_objname;
err = nft_offload_init();
if (err < 0)
goto err_offload;
@ -12251,8 +12252,6 @@ static int __init nf_tables_module_init(void)
err_netlink_notifier:
nft_offload_exit();
err_offload:
rhltable_destroy(&nft_objname_ht);
err_rht_objname:
unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
err_netdev_notifier:
nf_tables_core_module_exit();
@ -12274,7 +12273,6 @@ static void __exit nf_tables_module_exit(void)
unregister_pernet_subsys(&nf_tables_net_ops);
cancel_work_sync(&trans_gc_work);
rcu_barrier();
rhltable_destroy(&nft_objname_ht);
nf_tables_core_module_exit();
}

View File

@ -259,9 +259,7 @@ static int nft_payload_dump(struct sk_buff *skb,
static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
u32 priv_len, u32 field_len)
{
unsigned int remainder, delta, k;
struct nft_data mask = {};
__be32 remainder_mask;
if (priv_len == field_len) {
memset(&reg->mask, 0xff, priv_len);
@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,
return false;
}
memset(&mask, 0xff, field_len);
remainder = priv_len % sizeof(u32);
if (remainder) {
k = priv_len / sizeof(u32);
delta = field_len - priv_len;
remainder_mask = htonl(~((1 << (delta * BITS_PER_BYTE)) - 1));
mask.data[k] = (__force u32)remainder_mask;
}
memset(&mask, 0xff, priv_len);
memcpy(&reg->mask, &mask, field_len);
return true;

View File

@ -117,6 +117,7 @@ struct xt_hashlimit_htable {
refcount_t use;
u_int8_t family;
bool rnd_initialized;
bool ratematch;
struct hashlimit_cfg3 cfg; /* config */
@ -323,6 +324,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
kvfree(hinfo);
return -ENOMEM;
}
hinfo->ratematch = !!(cfg->mode & XT_HASHLIMIT_RATE_MATCH);
spin_lock_init(&hinfo->lock);
switch (revision) {
@ -872,7 +874,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
}
/* Check for overflow. */
if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
if (revision < 3)
return -EINVAL;
if (cfg->avg == 0 || cfg->avg > U32_MAX) {
pr_info_ratelimited("invalid rate\n");
return -ERANGE;
@ -905,6 +910,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
mutex_unlock(&hashlimit_mutex);
return ret;
}
} else {
if ((cfg->mode & XT_HASHLIMIT_RATE_MATCH &&
!(*hinfo)->ratematch) ||
(!(cfg->mode & XT_HASHLIMIT_RATE_MATCH) &&
(*hinfo)->ratematch)) {
mutex_unlock(&hashlimit_mutex);
htable_put(*hinfo);
return -EINVAL;
}
}
mutex_unlock(&hashlimit_mutex);

View File

@ -617,7 +617,11 @@ ip -6 -net "$nsr2" route add default via fee1:3::1
ip -net "$ns2" route add default via 10.0.2.1
ip -6 -net "$ns2" route add default via dead:2::1
ip netns exec "$nsr1" nft -a insert rule inet filter forward \
'meta oif tun0 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0 accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward \
'meta oif tun6 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6 accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward \
'meta oif "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept'
@ -629,7 +633,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel"; then
fi
if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel"
check_counters "flow offload for ns1/ns2 IP6IP6 tunnel"
else
echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel" 1>&2
ip netns exec "$nsr1" nft list ruleset
@ -642,6 +646,8 @@ ip -net "$nsr1" link set veth1.10 up
ip -net "$nsr1" addr add 192.168.20.1/24 dev veth1.10
ip -net "$nsr1" addr add fee1:4::1/64 dev veth1.10 nodad
ip netns exec "$nsr1" sysctl net.ipv4.conf.veth1/10.forwarding=1 > /dev/null
ip netns exec "$nsr1" nft -a insert rule inet filter forward \
'meta oif veth1.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif veth1.10 accept'
ip -net "$nsr1" link add name tun0.10 type ipip local 192.168.20.1 remote 192.168.20.2
@ -649,6 +655,8 @@ ip -net "$nsr1" link set tun0.10 up
ip -net "$nsr1" addr add 192.168.200.1/24 dev tun0.10
ip -net "$nsr1" route change default via 192.168.200.2
ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null
ip netns exec "$nsr1" nft -a insert rule inet filter forward \
'meta oif tun0.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0.10 accept'
ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none
@ -656,6 +664,8 @@ ip -net "$nsr1" link set tun6.10 up
ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad
ip -6 -net "$nsr1" route delete default
ip -6 -net "$nsr1" route add default via fee1:5::2
ip netns exec "$nsr1" nft -a insert rule inet filter forward \
'meta oif tun6.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6.10 accept'
ip -net "$nsr2" link add link veth0 name veth0.10 type vlan id 10
@ -683,7 +693,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel over vlan"; then
fi
if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel over vlan"
check_counters "flow offload for ns1/ns2 IP6IP6 tunnel over vlan"
else
echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel over vlan" 1>&2
ip netns exec "$nsr1" nft list ruleset