Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Partial pull of the nf-26-07-31 tag

Pablo says:

====================
The following patchset contains Netfilter/IPVS fixes net, this
includes fixes for ebtables nflog target, ipset hash type,
IPVS kthread estimator

1) Prevent IPVS kthread estimator from draining the est_temp_list
   when netns is being dismantled. From Zhiling Zou.

2) Missing module nflog refcount bump from ebtables nflog target from
   .checkentry path. Similar dependency exists already in xt_NFLOG and
   nft_log. From Chengfeng Ye.

3) Use RCU to fix ipset bookkeeping of cidr values on weakly-ordered
   architectures. From Jozsef Kadlecsik.

4) Use atomic64_t for set->ext_size in ipset to fix parallel inserts
   and deletes racing on updating it. From Jozsef Kadlecsik.

5) Add small wrappers for hash and bucket size to prepare the update
   of ipset hash set types to rhashtable, from Florian Westphal.

6) Add mtype_del_cidr_all() and use it to prepare the migration of
   ipset hash types to rhashtable. From Florian Westphal.

7) Replace existing ipset call_rcu() based destruction with rcu_work
   api also to ease the transition to rhashtable. Also from Florian.

8) Avoid reading the IPv4 ihl field multiple times to prevent local
   attacker to cause out-of-bounds write in ip_vs_nat_icmp(), from
   Julian Anastasov.

9) Restore the checksum validations that could be needed by the IPVS
   FORWARD hook. Also from Julian.
====================

Link: https://patch.msgid.link/20260731151806.849724-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-08-05 16:23:02 -07:00
17 changed files with 333 additions and 200 deletions

View File

@@ -244,8 +244,8 @@ extern void ip_set_type_unregister(struct ip_set_type *set_type);
/* A generic IP set */
struct ip_set {
/* For call_cru in destroy */
struct rcu_head rcu;
/* for set destruction */
struct rcu_work rwork;
/* The name of the set */
char name[IPSET_MAXNAMELEN];
/* Lock protecting the set data */
@@ -273,7 +273,7 @@ struct ip_set {
/* Number of elements (vs timeout) */
u32 elements;
/* Size of the dynamic extensions (vs timeout) */
size_t ext_size;
atomic64_t ext_size;
/* Element data size */
size_t dsize;
/* Offsets to extensions in elements */

View File

@@ -25,9 +25,7 @@
#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
@@ -2062,7 +2060,7 @@ 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, unsigned int toff,
bool has_ports);
bool has_ports, struct ip_vs_iphdr *ciph);
#ifdef CONFIG_IP_VS_IPV6
void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
@@ -2095,30 +2093,23 @@ 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)
static inline bool ip_vs_checksum_needed(struct sk_buff *skb)
{
/* Checksum unnecessary or already validated? */
if (skb_csum_unnecessary(skb))
return false;
/* LOCAL_OUT ? */
if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
/* Locally generated ? */
if (!skb->dev)
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))
if (!ip_vs_checksum_needed(skb))
return true;
/* Validate csum even for FORWARD */
return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
}

View File

@@ -41,11 +41,25 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par)
static int ebt_nflog_tg_check(const struct xt_tgchk_param *par)
{
struct ebt_nflog_info *info = par->targinfo;
int ret;
if (info->flags & ~EBT_NFLOG_MASK)
return -EINVAL;
info->prefix[EBT_NFLOG_PREFIX_SIZE - 1] = '\0';
return 0;
ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
if (ret != 0 && !par->nft_compat) {
request_module("%s", "nfnetlink_log");
ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
}
return ret;
}
static void ebt_nflog_tg_destroy(const struct xt_tgdtor_param *par)
{
nf_logger_put(par->family, NF_LOG_TYPE_ULOG);
}
static struct xt_target ebt_nflog_tg_reg __read_mostly = {
@@ -54,6 +68,7 @@ static struct xt_target ebt_nflog_tg_reg __read_mostly = {
.family = NFPROTO_BRIDGE,
.target = ebt_nflog_tg,
.checkentry = ebt_nflog_tg_check,
.destroy = ebt_nflog_tg_destroy,
.targetsize = sizeof(struct ebt_nflog_info),
.me = THIS_MODULE,
};

View File

@@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set)
mtype_ext_cleanup(set);
bitmap_zero(map->members, map->elements);
set->elements = 0;
set->ext_size = 0;
atomic64_set(&set->ext_size, 0);
}
/* Calculate the actual memory size of the set data */
@@ -93,7 +93,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
{
const struct mtype *map = set->data;
struct nlattr *nested;
size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size;
size_t memsize = mtype_memsize(map, set->dsize) + atomic64_read(&set->ext_size);
nested = nla_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)

View File

@@ -25,6 +25,7 @@
static LIST_HEAD(ip_set_type_list); /* all registered set types */
static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */
static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */
static struct workqueue_struct *ipset_destroy_wq;
struct ip_set_net {
struct ip_set * __rcu *ip_set_list; /* all individual sets */
@@ -350,7 +351,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
size_t len = ext->comment ? strlen(ext->comment) : 0;
if (unlikely(c)) {
set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
rcu_assign_pointer(comment->c, NULL);
kfree_rcu(c, rcu);
}
@@ -362,7 +363,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
if (unlikely(!c))
return;
strscpy(c->str, ext->comment, len + 1);
set->ext_size += sizeof(*c) + strlen(c->str) + 1;
atomic64_add(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
rcu_assign_pointer(comment->c, c);
}
EXPORT_SYMBOL_GPL(ip_set_init_comment);
@@ -392,7 +393,7 @@ ip_set_comment_free(struct ip_set *set, void *ptr)
c = rcu_dereference_protected(comment->c, 1);
if (unlikely(!c))
return;
set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
rcu_assign_pointer(comment->c, NULL);
kfree_rcu(c, rcu);
}
@@ -1178,22 +1179,26 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = {
.len = IPSET_MAXNAMELEN - 1 },
};
/* In order to return quickly when destroying a single set, it is split
* into two stages:
* - Cancel garbage collector
* - Destroy the set itself via call_rcu()
*/
static void
ip_set_destroy_set_rcu(struct rcu_head *head)
destroy_and_free_set(struct ip_set *set)
{
struct ip_set *set = container_of(head, struct ip_set, rcu);
set->variant->destroy(set);
module_put(set->type->me);
kfree(set);
}
/* In order to return quickly when destroying a single set,
* destruction is done asynchronously via work queues.
*/
static void
ip_set_destroy_set_work(struct work_struct *work)
{
struct ip_set *set = container_of(to_rcu_work(work),
struct ip_set, rwork);
destroy_and_free_set(set);
}
static void
_destroy_all_sets(struct ip_set_net *inst)
{
@@ -1283,7 +1288,8 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info,
/* Must wait for flush to be really finished */
rcu_barrier();
}
call_rcu(&s->rcu, ip_set_destroy_set_rcu);
INIT_RCU_WORK(&s->rwork, ip_set_destroy_set_work);
queue_rcu_work(ipset_destroy_wq, &s->rwork);
}
return 0;
out:
@@ -2421,18 +2427,23 @@ static struct pernet_operations ip_set_net_ops = {
static int __init
ip_set_init(void)
{
int ret = register_pernet_subsys(&ip_set_net_ops);
int ret;
ipset_destroy_wq = alloc_ordered_workqueue("ipset_destroy_wq", 0);
if (!ipset_destroy_wq)
return -ENOMEM;
ret = register_pernet_subsys(&ip_set_net_ops);
if (ret) {
pr_err("ip_set: cannot register pernet_subsys.\n");
return ret;
goto out_wq;
}
ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
if (ret != 0) {
pr_err("ip_set: cannot register with nfnetlink.\n");
unregister_pernet_subsys(&ip_set_net_ops);
return ret;
goto out_wq;
}
ret = nf_register_sockopt(&so_set);
@@ -2440,10 +2451,13 @@ ip_set_init(void)
pr_err("SO_SET registry failed: %d\n", ret);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
unregister_pernet_subsys(&ip_set_net_ops);
return ret;
goto out_wq;
}
return 0;
out_wq:
destroy_workqueue(ipset_destroy_wq);
return ret;
}
static void __exit
@@ -2453,9 +2467,7 @@ ip_set_fini(void)
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
unregister_pernet_subsys(&ip_set_net_ops);
/* Wait for call_rcu() in destroy */
rcu_barrier();
destroy_workqueue(ipset_destroy_wq);
pr_debug("these are the famous last words\n");
}

View File

@@ -99,9 +99,15 @@ struct htable {
#endif
/* Book-keeping of the prefixes added to the set */
struct net_prefix {
u8 cidr; /* the cidr value */
u32 count; /* number of elements of this cidr */
};
struct net_prefixes {
u32 nets[IPSET_NET_COUNT]; /* number of elements for this cidr */
u8 cidr[IPSET_NET_COUNT]; /* the cidr value */
struct rcu_head rcu;
u8 len;
struct net_prefix nets[] __counted_by(len);
};
/* Compute the hash table size */
@@ -127,11 +133,6 @@ htable_size(u8 hbits)
#else
#define __CIDR(cidr, i) (cidr)
#endif
/* cidr + 1 is stored in net_prefixes to support /0 */
#define NCIDR_PUT(cidr) ((cidr) + 1)
#define NCIDR_GET(cidr) ((cidr) - 1)
#ifdef IP_SET_HASH_WITH_NETS_PACKED
/* When cidr is packed with nomatch, cidr - 1 is stored in the data entry */
#define DCIDR_PUT(cidr) ((cidr) - 1)
@@ -141,21 +142,11 @@ htable_size(u8 hbits)
#define DCIDR_GET(cidr, i) __CIDR(cidr, i)
#endif
#define INIT_CIDR(cidr, host_mask) \
DCIDR_PUT(((cidr) ? NCIDR_GET(cidr) : host_mask))
#define INIT_CIDR(n, host_mask) ({ \
const struct net_prefixes *__n = rcu_dereference(n); \
DCIDR_PUT((__n)->len ? (__n)->nets[0].cidr : host_mask);\
})
#ifdef IP_SET_HASH_WITH_NET0
/* cidr from 0 to HOST_MASK value and c = cidr + 1 */
#define NLEN (HOST_MASK + 1)
#define CIDR_POS(c) ((c) - 1)
#else
/* cidr from 1 to HOST_MASK value and c = cidr + 1 */
#define NLEN HOST_MASK
#define CIDR_POS(c) ((c) - 2)
#endif
#else
#define NLEN 0
#endif /* IP_SET_HASH_WITH_NETS */
#define SET_ELEM_EXPIRED(set, d) \
@@ -204,12 +195,15 @@ static const union nf_inet_addr zeromask = {};
#undef mtype_ext_cleanup
#undef mtype_add_cidr
#undef mtype_del_cidr
#undef mtype_del_cidr_all
#undef mtype_ahash_memsize
#undef mtype_flush
#undef mtype_destroy
#undef mtype_same_set
#undef mtype_kadt
#undef mtype_uadt
#undef mtype_bucket_size
#undef mtype_hash_size
#undef mtype_add
#undef mtype_del
@@ -249,12 +243,15 @@ static const union nf_inet_addr zeromask = {};
#define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup)
#define mtype_add_cidr IPSET_TOKEN(MTYPE, _add_cidr)
#define mtype_del_cidr IPSET_TOKEN(MTYPE, _del_cidr)
#define mtype_del_cidr_all IPSET_TOKEN(MTYPE, _del_cidr_all)
#define mtype_ahash_memsize IPSET_TOKEN(MTYPE, _ahash_memsize)
#define mtype_flush IPSET_TOKEN(MTYPE, _flush)
#define mtype_destroy IPSET_TOKEN(MTYPE, _destroy)
#define mtype_same_set IPSET_TOKEN(MTYPE, _same_set)
#define mtype_kadt IPSET_TOKEN(MTYPE, _kadt)
#define mtype_uadt IPSET_TOKEN(MTYPE, _uadt)
#define mtype_bucket_size IPSET_TOKEN(MTYPE, _bucket_size)
#define mtype_hash_size IPSET_TOKEN(MTYPE, _hash_size)
#define mtype_add IPSET_TOKEN(MTYPE, _add)
#define mtype_del IPSET_TOKEN(MTYPE, _del)
@@ -292,6 +289,7 @@ static const union nf_inet_addr zeromask = {};
/* The generic hash structure */
struct htype {
struct htable __rcu *table; /* the hash table */
struct net_prefixes __rcu *rnets[IPSET_NET_COUNT]; /* cidr prefixes */
struct htable_gc gc; /* gc workqueue */
u32 maxelem; /* max elements in the hash */
u32 initval; /* random jhash init value */
@@ -302,9 +300,6 @@ struct htype {
#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
u8 netmask; /* netmask value for subnets to store */
union nf_inet_addr bitmask; /* stores bitmask */
#endif
#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'.
@@ -326,55 +321,108 @@ struct mtype_resize_ad {
/* Network cidr size book keeping when the hash stores different
* sized networks. cidr == real cidr + 1 to support /0.
*/
static void
static int
mtype_add_cidr(struct ip_set *set, struct htype *h, u8 cidr, u8 n)
{
int i, j;
struct net_prefixes *nets, *tmp;
int i, j, found, len = 0, ret = 0;
spin_lock_bh(&set->lock);
nets = __ipset_dereference(h->rnets[n]);
/* Add in increasing prefix order, so larger cidr first */
for (i = 0, j = -1; i < NLEN && h->nets[i].cidr[n]; i++) {
if (j != -1) {
for (i = 0, found = -1; i < nets->len; i++) {
if (nets->nets[i].count)
len++;
if (found != -1) {
continue;
} else if (h->nets[i].cidr[n] < cidr) {
j = i;
} else if (h->nets[i].cidr[n] == cidr) {
h->nets[CIDR_POS(cidr)].nets[n]++;
} else if (nets->nets[i].cidr < cidr) {
found = i;
} else if (nets->nets[i].cidr == cidr) {
nets->nets[i].count++;
goto unlock;
}
}
if (j != -1) {
for (; i > j; i--)
h->nets[i].cidr[n] = h->nets[i - 1].cidr[n];
len++;
tmp = kzalloc_flex(*tmp, nets, len, GFP_ATOMIC);
if (!tmp) {
ret = -ENOMEM;
goto unlock;
}
h->nets[i].cidr[n] = cidr;
h->nets[CIDR_POS(cidr)].nets[n] = 1;
tmp->len = len;
for (i = 0, j = 0; i < nets->len; i++) {
if (i == found) {
tmp->nets[j].cidr = cidr;
tmp->nets[j++].count = 1;
}
if (!nets->nets[i].count)
continue;
tmp->nets[j].cidr = nets->nets[i].cidr;
tmp->nets[j++].count = nets->nets[i].count;
}
if (found == -1) {
tmp->nets[j].cidr = cidr;
tmp->nets[j].count = 1;
}
rcu_assign_pointer(h->rnets[n], tmp);
kfree_rcu(nets, rcu);
unlock:
spin_unlock_bh(&set->lock);
return ret;
}
static void
mtype_del_cidr(struct ip_set *set, struct htype *h, u8 cidr, u8 n)
{
u8 i, j, net_end = NLEN - 1;
struct net_prefixes *nets, *tmp;
u8 i, j, len = 0;
int found;
spin_lock_bh(&set->lock);
for (i = 0; i < NLEN; i++) {
if (h->nets[i].cidr[n] != cidr)
continue;
h->nets[CIDR_POS(cidr)].nets[n]--;
if (h->nets[CIDR_POS(cidr)].nets[n] > 0)
goto unlock;
for (j = i; j < net_end && h->nets[j].cidr[n]; j++)
h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
h->nets[j].cidr[n] = 0;
goto unlock;
nets = __ipset_dereference(h->rnets[n]);
for (i = 0, found = -1; i < nets->len; i++) {
if (nets->nets[i].count)
len++;
if (nets->nets[i].cidr == cidr)
found = i;
}
if (unlikely(found == -1))
goto unlock;
nets->nets[found].count--;
if (nets->nets[found].count)
goto unlock;
len--;
tmp = kzalloc_flex(*tmp, nets, len, GFP_ATOMIC);
if (!tmp)
/* Leave a hole */
goto unlock;
tmp->len = len;
for (i = 0, j = 0; i < nets->len; i++) {
if (!nets->nets[i].count || i == found)
continue;
tmp->nets[j].cidr = nets->nets[i].cidr;
tmp->nets[j++].count = nets->nets[i].count;
}
rcu_assign_pointer(h->rnets[n], tmp);
kfree_rcu(nets, rcu);
unlock:
spin_unlock_bh(&set->lock);
}
#endif
static void
mtype_del_cidr_all(struct ip_set *set, struct htype *h, const struct mtype_elem *data)
{
#ifdef IP_SET_HASH_WITH_NETS
int k;
for (k = 0; k < IPSET_NET_COUNT; k++)
mtype_del_cidr(set, h, DCIDR_GET(data->cidr, k), k);
#endif
}
/* Calculate the actual memory size of the set data */
static size_t
mtype_ahash_memsize(const struct htype *h, const struct htable *t)
@@ -402,6 +450,9 @@ static void
mtype_flush(struct ip_set *set)
{
struct htype *h = set->data;
#ifdef IP_SET_HASH_WITH_NETS
struct net_prefixes *nets, *tmp;
#endif
struct htable *t;
struct hbucket *n;
u32 r, i;
@@ -425,7 +476,19 @@ mtype_flush(struct ip_set *set)
spin_unlock_bh(&t->hregion[r].lock);
}
#ifdef IP_SET_HASH_WITH_NETS
memset(h->nets, 0, sizeof(h->nets));
for (i = 0; i < IPSET_NET_COUNT; i++) {
nets = ipset_dereference_nfnl(h->rnets[i]);
tmp = kzalloc_obj(*tmp, GFP_ATOMIC);
if (!tmp) {
u8 j;
for (j = 0; j < nets->len; j++)
nets->nets[j].count = 0;
} else {
rcu_assign_pointer(h->rnets[i], tmp);
kfree_rcu(nets, rcu);
}
}
#endif
}
@@ -433,6 +496,9 @@ mtype_flush(struct ip_set *set)
static void
mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy)
{
#ifdef IP_SET_HASH_WITH_NETS
struct htype *h = set->data;
#endif
struct hbucket *n;
u32 i;
@@ -446,6 +512,11 @@ mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy)
kfree(n);
}
#ifdef IP_SET_HASH_WITH_NETS
if (ext_destroy)
for (i = 0; i < IPSET_NET_COUNT; i++)
kfree(rcu_dereference_raw(h->rnets[i]));
#endif
ip_set_free(t->hregion);
ip_set_free(t);
}
@@ -493,9 +564,6 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r)
struct mtype_elem *data;
u32 i, j, d;
size_t dsize = set->dsize;
#ifdef IP_SET_HASH_WITH_NETS
u8 k;
#endif
u8 pos, htable_bits = t->htable_bits;
spin_lock_bh(&t->hregion[r].lock);
@@ -516,12 +584,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r)
pr_debug("expired %u/%u\n", i, j);
clear_bit(j, n->used);
smp_mb__after_atomic();
#ifdef IP_SET_HASH_WITH_NETS
for (k = 0; k < IPSET_NET_COUNT; k++)
mtype_del_cidr(set, h,
NCIDR_PUT(DCIDR_GET(data->cidr, k)),
k);
#endif
mtype_del_cidr_all(set, h, data);
t->hregion[r].elements--;
ip_set_ext_destroy(set, data);
d++;
@@ -947,12 +1010,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
j = 0;
data = ahash_data(n, j, set->dsize);
if (!deleted) {
#ifdef IP_SET_HASH_WITH_NETS
for (i = 0; i < IPSET_NET_COUNT; i++)
mtype_del_cidr(set, h,
NCIDR_PUT(DCIDR_GET(data->cidr, i)),
i);
#endif
mtype_del_cidr_all(set, h, data);
ip_set_ext_destroy(set, data);
t->hregion[r].elements--;
}
@@ -996,7 +1054,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
t->hregion[r].elements++;
#ifdef IP_SET_HASH_WITH_NETS
for (i = 0; i < IPSET_NET_COUNT; i++)
mtype_add_cidr(set, h, NCIDR_PUT(DCIDR_GET(d->cidr, i)), i);
mtype_add_cidr(set, h, DCIDR_GET(d->cidr, i), i);
#endif
memcpy(data, d, sizeof(struct mtype_elem));
overwrite_extensions:
@@ -1107,11 +1165,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
if (i + 1 == pos)
smp_store_release(&n->pos, --pos);
t->hregion[r].elements--;
#ifdef IP_SET_HASH_WITH_NETS
for (j = 0; j < IPSET_NET_COUNT; j++)
mtype_del_cidr(set, h,
NCIDR_PUT(DCIDR_GET(d->cidr, j)), j);
#endif
mtype_del_cidr_all(set, h, d);
ip_set_ext_destroy(set, data);
if (t->resizing && ext && ext->target) {
@@ -1193,28 +1247,37 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
{
struct htype *h = set->data;
struct htable *t = rcu_dereference_bh(h->table);
struct net_prefixes *nets0;
struct hbucket *n;
struct mtype_elem *data;
#if IPSET_NET_COUNT == 2
struct net_prefixes *nets1;
struct mtype_elem orig = *d;
int ret, i, j = 0, k;
int ret, i, j, k;
#else
int ret, i, j = 0;
int ret, i, j;
#endif
u32 key, multi = 0;
u8 pos;
pr_debug("test by nets\n");
for (; j < NLEN && h->nets[j].cidr[0] && !multi; j++) {
rcu_read_lock_bh();
nets0 = rcu_dereference_bh(h->rnets[0]);
#if IPSET_NET_COUNT == 2
nets1 = rcu_dereference_bh(h->rnets[1]);
#endif
for (j = 0; j < nets0->len && !multi; j++) {
if (!nets0->nets[j].count)
continue;
#if IPSET_NET_COUNT == 2
mtype_data_reset_elem(d, &orig);
mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0]), false);
for (k = 0; k < NLEN && h->nets[k].cidr[1] && !multi;
k++) {
mtype_data_netmask(d, NCIDR_GET(h->nets[k].cidr[1]),
true);
mtype_data_netmask(d, nets0->nets[j].cidr, false);
for (k = 0; k < nets1->len && !multi; k++) {
if (!nets1->nets[k].count)
continue;
mtype_data_netmask(d, nets1->nets[k].cidr, true);
#else
mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0]));
mtype_data_netmask(d, nets0->nets[j].cidr);
#endif
key = HKEY(d, h->initval, t->htable_bits);
n = rcu_dereference_bh(hbucket(t, key));
@@ -1229,7 +1292,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
continue;
ret = mtype_data_match(data, ext, mext, set, flags);
if (ret != 0)
return ret;
goto unlock;
#ifdef IP_SET_HASH_WITH_MULTI
/* No match, reset multiple match flag */
multi = 0;
@@ -1239,7 +1302,10 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
}
#endif
}
return 0;
ret = 0;
unlock:
rcu_read_unlock_bh();
return ret;
}
#endif
@@ -1294,6 +1360,24 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
return ret;
}
static u32 mtype_hash_size(const struct htype *h)
{
const struct htable *t;
u8 htable_bits;
rcu_read_lock();
t = rcu_dereference(h->table);
htable_bits = t->htable_bits;
rcu_read_unlock();
return jhash_size(htable_bits);
}
static u32 mtype_bucket_size(const struct htype *h)
{
return h->bucketsize;
}
/* Reply a HEADER request: fill out the header part of the set */
static int
mtype_head(struct ip_set *set, struct sk_buff *skb)
@@ -1304,21 +1388,20 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
size_t memsize;
u32 elements = 0;
size_t ext_size = 0;
u8 htable_bits;
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
mtype_ext_size(set, &elements, &ext_size);
memsize = mtype_ahash_memsize(h, t) + ext_size + set->ext_size;
htable_bits = t->htable_bits;
memsize = mtype_ahash_memsize(h, t) + ext_size + atomic64_read(&set->ext_size);
rcu_read_unlock_bh();
nested = nla_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
goto nla_put_failure;
if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE,
htonl(jhash_size(htable_bits))) ||
nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, htonl(mtype_hash_size(h))))
goto nla_put_failure;
if (nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
goto nla_put_failure;
#ifdef IP_SET_HASH_WITH_BITMASK
/* if netmask is set to anything other than HOST_MASK we know that the user supplied netmask
@@ -1342,8 +1425,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
goto nla_put_failure;
#endif
if (set->flags & IPSET_CREATE_FLAG_BUCKETSIZE) {
if (nla_put_u8(skb, IPSET_ATTR_BUCKETSIZE, h->bucketsize) ||
nla_put_net32(skb, IPSET_ATTR_INITVAL, htonl(h->initval)))
if (nla_put_u8(skb, IPSET_ATTR_BUCKETSIZE, mtype_bucket_size(h)))
goto nla_put_failure;
if (nla_put_net32(skb, IPSET_ATTR_INITVAL, htonl(h->initval)))
goto nla_put_failure;
}
if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) ||
@@ -1504,6 +1588,9 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
int ret __attribute__((unused)) = 0;
u8 netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
union nf_inet_addr bitmask = onesmask;
#endif
#ifdef IP_SET_HASH_WITH_NETS
struct net_prefixes *nets;
#endif
size_t hsize;
struct htype *h;
@@ -1604,21 +1691,25 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
*/
hbits = fls(hashsize - 1);
hsize = htable_size(hbits);
if (hsize == 0) {
kfree(h);
return -ENOMEM;
}
if (hsize == 0)
goto free_h;
t = ip_set_alloc(hsize);
if (!t) {
kfree(h);
return -ENOMEM;
}
if (!t)
goto free_h;
t->hregion = ip_set_alloc(ahash_sizeof_regions(hbits));
if (!t->hregion) {
ip_set_free(t);
kfree(h);
return -ENOMEM;
if (!t->hregion)
goto free_t;
#ifdef IP_SET_HASH_WITH_NETS
for (i = 0; i < IPSET_NET_COUNT; i++) {
nets = kzalloc_obj(*nets);
if (!nets) {
while (i > 0)
kfree(rcu_dereference_raw(h->rnets[--i]));
goto free_hregion;
}
RCU_INIT_POINTER(h->rnets[i], nets);
}
#endif
h->gc.set = set;
spin_lock_init(&h->gc.lock);
for (i = 0; i < ahash_numof_locks(hbits); i++)
@@ -1650,6 +1741,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
INIT_LIST_HEAD(&t->ad);
RCU_INIT_POINTER(h->table, t);
set->data = h;
#ifndef IP_SET_PROTO_UNDEF
if (set->family == NFPROTO_IPV4) {
#endif
@@ -1678,10 +1770,20 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
#endif
}
pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n",
set->name, jhash_size(t->htable_bits),
set->name, mtype_hash_size(h),
t->htable_bits, h->maxelem, set->data, t);
return 0;
#ifdef IP_SET_HASH_WITH_NETS
free_hregion:
ip_set_free(t->hregion);
#endif
free_t:
ip_set_free(t);
free_h:
kfree(h);
return -ENOMEM;
}
#endif /* IP_SET_EMIT_CREATE */

View File

@@ -138,7 +138,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_ipportnet4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem e = {
.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr = INIT_CIDR(h->rnets[0], HOST_MASK),
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
@@ -398,7 +398,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_ipportnet6 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet6_elem e = {
.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr = INIT_CIDR(h->rnets[0], HOST_MASK),
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);

View File

@@ -117,7 +117,7 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_net4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem e = {
.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr = INIT_CIDR(h->rnets[0], HOST_MASK),
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
@@ -291,7 +291,7 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_net6 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net6_elem e = {
.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr = INIT_CIDR(h->rnets[0], HOST_MASK),
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);

View File

@@ -161,7 +161,7 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
struct hash_netiface4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem e = {
.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr = INIT_CIDR(h->rnets[0], HOST_MASK),
.elem = 1,
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
@@ -382,7 +382,7 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
struct hash_netiface6 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface6_elem e = {
.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr = INIT_CIDR(h->rnets[0], HOST_MASK),
.elem = 1,
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);

View File

@@ -149,8 +149,10 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
struct hash_netnet4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
rcu_read_lock_bh();
e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK);
e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK);
rcu_read_unlock_bh();
if (adt == IPSET_TEST)
e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
@@ -388,8 +390,10 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
struct hash_netnet6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
rcu_read_lock_bh();
e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK);
e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK);
rcu_read_unlock_bh();
if (adt == IPSET_TEST)
e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK;

View File

@@ -133,7 +133,7 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_netport4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem e = {
.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr = INIT_CIDR(h->rnets[0], HOST_MASK),
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
@@ -353,7 +353,7 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct hash_netport6 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport6_elem e = {
.cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK),
.cidr = INIT_CIDR(h->rnets[0], HOST_MASK),
};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);

View File

@@ -157,8 +157,10 @@ hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
struct hash_netportnet4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
rcu_read_lock_bh();
e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK);
e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK);
rcu_read_unlock_bh();
if (adt == IPSET_TEST)
e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK;
@@ -452,8 +454,10 @@ hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
struct hash_netportnet6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK);
e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK);
rcu_read_lock_bh();
e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK);
e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK);
rcu_read_unlock_bh();
if (adt == IPSET_TEST)
e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK;

View File

@@ -421,7 +421,7 @@ list_set_flush(struct ip_set *set)
list_for_each_entry_safe(e, n, &map->members, list)
list_set_del(set, e);
set->elements = 0;
set->ext_size = 0;
atomic64_set(&set->ext_size, 0);
}
static void
@@ -455,7 +455,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
{
const struct list_set *map = set->data;
struct nlattr *nested;
size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size;
size_t memsize = list_set_memsize(map, set->dsize) + atomic64_read(&set->ext_size);
nested = nla_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)

View File

@@ -925,28 +925,27 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
*/
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int inout, unsigned int toff,
bool has_ports)
bool has_ports, struct ip_vs_iphdr *ciph)
{
struct iphdr *iph = ip_hdr(skb);
struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff);
struct iphdr *ciph = (struct iphdr *)(icmph + 1);
unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
struct iphdr *cih = (struct iphdr *)(icmph + 1);
if (inout) {
iph->saddr = cp->vaddr.ip;
ip_send_check(iph);
ciph->daddr = cp->vaddr.ip;
ip_send_check(ciph);
cih->daddr = cp->vaddr.ip;
ip_send_check(cih);
} else {
iph->daddr = cp->daddr.ip;
ip_send_check(iph);
ciph->saddr = cp->daddr.ip;
ip_send_check(ciph);
cih->saddr = cp->daddr.ip;
ip_send_check(cih);
}
/* the TCP/UDP/SCTP port */
if (has_ports) {
__be16 *ports = (void *)ciph + ciph->ihl*4;
__be16 *ports = (void *)(skb->data + ciph->len);
if (inout)
ports[1] = cp->vport;
@@ -960,10 +959,10 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (inout)
IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off,
"Forwarding altered outgoing ICMP");
else
IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off,
"Forwarding altered incoming ICMP");
}
@@ -1056,7 +1055,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
else
#endif
ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph);
if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
goto out;
@@ -1092,7 +1091,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
struct ip_vs_iphdr ciph;
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
unsigned int offset, ihl;
unsigned int offset;
union nf_inet_addr snet;
*related = 1;
@@ -1105,7 +1104,6 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
}
ihl = ipvsh->len;
offset = ipvsh->len;
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
if (ic == NULL)
@@ -1131,11 +1129,15 @@ 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 && cih->version == 4 && cih->ihl >= 5))
if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph))
return NF_ACCEPT; /* The packet looks wrong, ignore */
pp = ip_vs_proto_get(cih->protocol);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
if (!(cih && cih->version == 4 &&
ciph.len - ciph.off >= sizeof(struct iphdr)))
return NF_ACCEPT; /* The packet looks wrong, ignore */
pp = ip_vs_proto_get(ciph.protocol);
if (!pp)
return NF_ACCEPT;
@@ -1146,8 +1148,6 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
"Checking outgoing ICMP for");
ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph);
/* 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_INET, skb, &ciph);
@@ -1155,8 +1155,8 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
snet.ip = ipvsh->saddr.ip;
return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
hooknum);
return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph,
ipvsh->len, hooknum);
}
#ifdef CONFIG_IP_VS_IPV6
@@ -1803,10 +1803,12 @@ 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 && cih->version == 4 && cih->ihl >= 5))
if (!cih)
return NF_ACCEPT; /* The packet looks wrong, ignore */
hlen_ipip = cih->ihl * 4;
if (!(cih->version == 4 && hlen_ipip >= sizeof(struct iphdr)))
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;
@@ -1823,9 +1825,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP)
return NF_ACCEPT;
offset += hlen_ipip;
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
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 */
cih->protocol == IPPROTO_GRE) && /* Can be GRE encap */
@@ -1850,21 +1849,25 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Skip IP and UDP/GRE tunnel headers */
offset = offset2 + ulen;
/* Now we should be at the original IP header */
cih = skb_header_pointer(skb, offset, sizeof(_ciph),
&_ciph);
if (cih && cih->version == 4 && cih->ihl >= 5 &&
iproto == IPPROTO_IPIP)
if (iproto == IPPROTO_IPIP)
tunnel = true;
else
return NF_ACCEPT;
}
}
pd = ip_vs_proto_data_get(ipvs, cih->protocol);
if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph))
return NF_ACCEPT;
pd = ip_vs_proto_data_get(ipvs, ciph.protocol);
if (!pd)
return NF_ACCEPT;
pp = pd->pp;
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
if (!(cih && cih->version == 4 &&
ciph.len - ciph.off >= sizeof(struct iphdr)))
return NF_ACCEPT; /* The packet looks wrong, ignore */
/* Is the embedded protocol header present? */
if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
return NF_ACCEPT;
@@ -1872,9 +1875,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
"Checking incoming ICMP for");
offset2 = offset;
ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
/* The embedded headers contain source and dest in reverse order.
* For IPIP/UDP/GRE tunnel this is error for request, not for reply.
*/
@@ -1904,11 +1904,12 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
}
if (tunnel) {
unsigned int hlen_orig = cih->ihl * 4;
unsigned int hlen_orig = ciph.len - ciph.off;
__be32 info = ic->un.gateway;
__u8 type = ic->type;
__u8 code = ic->code;
offset2 = offset;
/* Update the MTU */
if (ic->type == ICMP_DEST_UNREACH &&
ic->code == ICMP_FRAG_NEEDED) {

View File

@@ -191,8 +191,11 @@ static int ip_vs_estimation_kthread(void *data)
}
/* kthread 0 will handle the calc phase */
if (ipvs->est_calc_phase)
if (ipvs->est_calc_phase) {
ip_vs_est_calc_phase(ipvs);
if (kthread_should_stop() || !READ_ONCE(ipvs->enable))
return 0;
}
}
while (1) {
@@ -270,6 +273,7 @@ int ip_vs_est_kthread_start(struct netns_ipvs *ipvs,
kd->task = NULL;
goto out;
}
get_task_struct(kd->task);
set_user_nice(kd->task, sysctl_est_nice(ipvs));
if (sysctl_est_preferred_cpulist(ipvs))
@@ -286,7 +290,7 @@ void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd)
{
if (kd->task) {
pr_info("stopping estimator thread %d...\n", kd->id);
kthread_stop(kd->task);
kthread_stop_put(kd->task);
kd->task = NULL;
}
}
@@ -526,7 +530,7 @@ static void ip_vs_est_kthread_destroy(struct ip_vs_est_kt_data *kd)
if (kd) {
if (kd->task) {
pr_info("stop unused estimator thread %d...\n", kd->id);
kthread_stop(kd->task);
kthread_stop_put(kd->task);
}
ip_vs_stats_free(kd->calc_stats);
kfree(kd);

View File

@@ -193,7 +193,7 @@ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
struct sctphdr *sh;
__le32 cmp, val;
if (!ip_vs_checksum_needed(skb, af))
if (!ip_vs_checksum_needed(skb))
return 1;
sh = (struct sctphdr *)(skb->data + sctphoff);
cmp = sh->checksum;

View File

@@ -1580,7 +1580,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
if (skb_cow(skb, rt->dst.dev->hard_header_len))
goto tx_error;
ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph);
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;