diff options
| -rw-r--r-- | include/linux/netfilter/ipset/ip_set.h | 6 | ||||
| -rw-r--r-- | include/net/ip_vs.h | 21 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_nflog.c | 17 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_bitmap_gen.h | 4 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_core.c | 52 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_hash_gen.h | 304 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_hash_ipportnet.c | 4 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_hash_net.c | 4 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_hash_netiface.c | 4 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_hash_netnet.c | 12 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_hash_netport.c | 4 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_hash_netportnet.c | 12 | ||||
| -rw-r--r-- | net/netfilter/ipset/ip_set_list_set.c | 4 | ||||
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_core.c | 67 | ||||
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_est.c | 10 | ||||
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 | ||||
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_xmit.c | 2 |
17 files changed, 331 insertions, 198 deletions
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index b98331572ad2..c46864cc6623 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -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 */ diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e6ca930a3507..d2813eb795be 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -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); } diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 61bf8f4465ab..426f8adc912c 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -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, }; diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index bb9b5bed10e1..226fdf17b683 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -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) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 6cfad152d7d1..543851a923d0 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -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"); } diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index b2d77973272d..f00c82acd7f0 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -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)) - -#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 +#define INIT_CIDR(n, host_mask) ({ \ + const struct net_prefixes *__n = rcu_dereference(n); \ + DCIDR_PUT((__n)->len ? (__n)->nets[0].cidr : host_mask);\ +}) -#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 */ @@ -303,9 +301,6 @@ struct htype { 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; + } + + 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; } - h->nets[i].cidr[n] = cidr; - h->nets[CIDR_POS(cidr)].nets[n] = 1; + 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; + 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 @@ copy_data: 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 @@ out: 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)) || @@ -1505,6 +1589,9 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, 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; struct htable *t; @@ -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 */ diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 2d6652d43199..195853a25b06 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -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); diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index ce0a9ce5a91f..092f3c9281b8 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -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); diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index 30a655e5c4fd..b44b95f766b7 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -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); diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c index 8fbe649c9dd3..f7c8a1cc30fc 100644 --- a/net/netfilter/ipset/ip_set_hash_netnet.c +++ b/net/netfilter/ipset/ip_set_hash_netnet.c @@ -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; diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index d1a0628df4ef..5de4b511de76 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -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); diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c index bf4f91b78e1d..6291532be7a5 100644 --- a/net/netfilter/ipset/ip_set_hash_netportnet.c +++ b/net/netfilter/ipset/ip_set_hash_netportnet.c @@ -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; diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 1cef84f15e8c..ca3ef9479e83 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -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) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 6b79e0c4d9e2..0bdaeb4ed61e 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -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); + if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph)) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (!(cih && cih->version == 4 && cih->ihl >= 5)) + 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(cih->protocol); + 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 */ - raddr = (union nf_inet_addr *)&cih->daddr; 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; /* 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) { diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index ab09f5182951..05a216a47b45 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -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); diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 3dbd3096e163..c80567c73469 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -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; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 0b0c5304993a..c4508f3f43dd 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -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; |
