diff options
author | Scott Wood <scottwood@freescale.com> | 2014-04-07 23:49:35 (GMT) |
---|---|---|
committer | Scott Wood <scottwood@freescale.com> | 2014-04-07 23:49:35 (GMT) |
commit | 62b8c978ee6b8d135d9e7953221de58000dba986 (patch) | |
tree | 683b04b2e627f6710c22c151b23c8cc9a165315e /net/netfilter | |
parent | 78fd82238d0e5716578c326404184a27ba67fd6e (diff) | |
download | linux-fsl-qoriq-62b8c978ee6b8d135d9e7953221de58000dba986.tar.xz |
Rewind v3.13-rc3+ (78fd82238d0e5716) to v3.12
Diffstat (limited to 'net/netfilter')
70 files changed, 1893 insertions, 10130 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index c3398cd..6e839b6 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -413,58 +413,6 @@ config NETFILTER_SYNPROXY endif # NF_CONNTRACK -config NF_TABLES - depends on NETFILTER_NETLINK - tristate "Netfilter nf_tables support" - -config NFT_EXTHDR - depends on NF_TABLES - tristate "Netfilter nf_tables IPv6 exthdr module" - -config NFT_META - depends on NF_TABLES - tristate "Netfilter nf_tables meta module" - -config NFT_CT - depends on NF_TABLES - depends on NF_CONNTRACK - tristate "Netfilter nf_tables conntrack module" - -config NFT_RBTREE - depends on NF_TABLES - tristate "Netfilter nf_tables rbtree set module" - -config NFT_HASH - depends on NF_TABLES - tristate "Netfilter nf_tables hash set module" - -config NFT_COUNTER - depends on NF_TABLES - tristate "Netfilter nf_tables counter module" - -config NFT_LOG - depends on NF_TABLES - tristate "Netfilter nf_tables log module" - -config NFT_LIMIT - depends on NF_TABLES - tristate "Netfilter nf_tables limit module" - -config NFT_NAT - depends on NF_TABLES - depends on NF_CONNTRACK - depends on NF_NAT - tristate "Netfilter nf_tables nat module" - -config NFT_COMPAT - depends on NF_TABLES - depends on NETFILTER_XTABLES - tristate "Netfilter x_tables over nf_tables module" - help - This is required if you intend to use any of existing - x_tables match/target extensions over the nf_tables - framework. - config NETFILTER_XTABLES tristate "Netfilter Xtables support (required for ip_tables)" default m if NETFILTER_ADVANCED=n @@ -909,7 +857,7 @@ config NETFILTER_XT_MATCH_CONNLABEL connection simultaneously. config NETFILTER_XT_MATCH_CONNLIMIT - tristate '"connlimit" match support' + tristate '"connlimit" match support"' depends on NF_CONNTRACK depends on NETFILTER_ADVANCED ---help--- diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 394483b..c3a0a12 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -64,24 +64,6 @@ obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o # SYNPROXY obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o -# nf_tables -nf_tables-objs += nf_tables_core.o nf_tables_api.o -nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o -nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o - -obj-$(CONFIG_NF_TABLES) += nf_tables.o -obj-$(CONFIG_NFT_COMPAT) += nft_compat.o -obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o -obj-$(CONFIG_NFT_META) += nft_meta.o -obj-$(CONFIG_NFT_CT) += nft_ct.o -obj-$(CONFIG_NFT_LIMIT) += nft_limit.o -obj-$(CONFIG_NFT_NAT) += nft_nat.o -#nf_tables-objs += nft_meta_target.o -obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o -obj-$(CONFIG_NFT_HASH) += nft_hash.o -obj-$(CONFIG_NFT_COUNTER) += nft_counter.o -obj-$(CONFIG_NFT_LOG) += nft_log.o - # generic X tables obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 1fbab0c..593b16e 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -146,7 +146,7 @@ unsigned int nf_iterate(struct list_head *head, /* Optimization: we don't need to hold module reference here, since function can't sleep. --RR */ repeat: - verdict = (*elemp)->hook(*elemp, skb, indev, outdev, okfn); + verdict = (*elemp)->hook(hook, skb, indev, outdev, okfn); if (verdict != NF_ACCEPT) { #ifdef CONFIG_NETFILTER_DEBUG if (unlikely((verdict & NF_VERDICT_MASK) diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index a2d6263..ba36c28 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -1,7 +1,7 @@ menuconfig IP_SET tristate "IP set support" depends on INET && NETFILTER - select NETFILTER_NETLINK + depends on NETFILTER_NETLINK help This option adds IP set support to the kernel. In order to define and use the sets, you need the userspace utility @@ -90,15 +90,6 @@ config IP_SET_HASH_IPPORTNET To compile it as a module, choose M here. If unsure, say N. -config IP_SET_HASH_NETPORTNET - tristate "hash:net,port,net set support" - depends on IP_SET - help - This option adds the hash:net,port,net set type support, by which - one can store two IPv4/IPv6 subnets, and a protocol/port in a set. - - To compile it as a module, choose M here. If unsure, say N. - config IP_SET_HASH_NET tristate "hash:net set support" depends on IP_SET @@ -108,15 +99,6 @@ config IP_SET_HASH_NET To compile it as a module, choose M here. If unsure, say N. -config IP_SET_HASH_NETNET - tristate "hash:net,net set support" - depends on IP_SET - help - This option adds the hash:net,net set type support, by which - one can store IPv4/IPv6 network address/prefix pairs in a set. - - To compile it as a module, choose M here. If unsure, say N. - config IP_SET_HASH_NETPORT tristate "hash:net,port set support" depends on IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index 44b2d38..6e965ec 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile @@ -20,8 +20,6 @@ obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o obj-$(CONFIG_IP_SET_HASH_NETIFACE) += ip_set_hash_netiface.o -obj-$(CONFIG_IP_SET_HASH_NETNET) += ip_set_hash_netnet.o -obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o # list types obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index f2c7d83..2524337 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -8,32 +8,38 @@ #ifndef __IP_SET_BITMAP_IP_GEN_H #define __IP_SET_BITMAP_IP_GEN_H -#define mtype_do_test IPSET_TOKEN(MTYPE, _do_test) -#define mtype_gc_test IPSET_TOKEN(MTYPE, _gc_test) -#define mtype_is_filled IPSET_TOKEN(MTYPE, _is_filled) -#define mtype_do_add IPSET_TOKEN(MTYPE, _do_add) -#define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup) -#define mtype_do_del IPSET_TOKEN(MTYPE, _do_del) -#define mtype_do_list IPSET_TOKEN(MTYPE, _do_list) -#define mtype_do_head IPSET_TOKEN(MTYPE, _do_head) -#define mtype_adt_elem IPSET_TOKEN(MTYPE, _adt_elem) -#define mtype_add_timeout IPSET_TOKEN(MTYPE, _add_timeout) -#define mtype_gc_init IPSET_TOKEN(MTYPE, _gc_init) -#define mtype_kadt IPSET_TOKEN(MTYPE, _kadt) -#define mtype_uadt IPSET_TOKEN(MTYPE, _uadt) -#define mtype_destroy IPSET_TOKEN(MTYPE, _destroy) -#define mtype_flush IPSET_TOKEN(MTYPE, _flush) -#define mtype_head IPSET_TOKEN(MTYPE, _head) -#define mtype_same_set IPSET_TOKEN(MTYPE, _same_set) -#define mtype_elem IPSET_TOKEN(MTYPE, _elem) -#define mtype_test IPSET_TOKEN(MTYPE, _test) -#define mtype_add IPSET_TOKEN(MTYPE, _add) -#define mtype_del IPSET_TOKEN(MTYPE, _del) -#define mtype_list IPSET_TOKEN(MTYPE, _list) -#define mtype_gc IPSET_TOKEN(MTYPE, _gc) +#define CONCAT(a, b) a##b +#define TOKEN(a,b) CONCAT(a, b) + +#define mtype_do_test TOKEN(MTYPE, _do_test) +#define mtype_gc_test TOKEN(MTYPE, _gc_test) +#define mtype_is_filled TOKEN(MTYPE, _is_filled) +#define mtype_do_add TOKEN(MTYPE, _do_add) +#define mtype_do_del TOKEN(MTYPE, _do_del) +#define mtype_do_list TOKEN(MTYPE, _do_list) +#define mtype_do_head TOKEN(MTYPE, _do_head) +#define mtype_adt_elem TOKEN(MTYPE, _adt_elem) +#define mtype_add_timeout TOKEN(MTYPE, _add_timeout) +#define mtype_gc_init TOKEN(MTYPE, _gc_init) +#define mtype_kadt TOKEN(MTYPE, _kadt) +#define mtype_uadt TOKEN(MTYPE, _uadt) +#define mtype_destroy TOKEN(MTYPE, _destroy) +#define mtype_flush TOKEN(MTYPE, _flush) +#define mtype_head TOKEN(MTYPE, _head) +#define mtype_same_set TOKEN(MTYPE, _same_set) +#define mtype_elem TOKEN(MTYPE, _elem) +#define mtype_test TOKEN(MTYPE, _test) +#define mtype_add TOKEN(MTYPE, _add) +#define mtype_del TOKEN(MTYPE, _del) +#define mtype_list TOKEN(MTYPE, _list) +#define mtype_gc TOKEN(MTYPE, _gc) #define mtype MTYPE -#define get_ext(set, map, id) ((map)->extensions + (set)->dsize * (id)) +#define ext_timeout(e, m) \ + (unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT]) +#define ext_counter(e, m) \ + (struct ip_set_counter *)((e) + (m)->offset[IPSET_OFFSET_COUNTER]) +#define get_ext(map, id) ((map)->extensions + (map)->dsize * (id)) static void mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) @@ -43,22 +49,11 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) init_timer(&map->gc); map->gc.data = (unsigned long) set; map->gc.function = gc; - map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; add_timer(&map->gc); } static void -mtype_ext_cleanup(struct ip_set *set) -{ - struct mtype *map = set->data; - u32 id; - - for (id = 0; id < map->elements; id++) - if (test_bit(id, map->members)) - ip_set_ext_destroy(set, get_ext(set, map, id)); -} - -static void mtype_destroy(struct ip_set *set) { struct mtype *map = set->data; @@ -67,11 +62,8 @@ mtype_destroy(struct ip_set *set) del_timer_sync(&map->gc); ip_set_free(map->members); - if (set->dsize) { - if (set->extensions & IPSET_EXT_DESTROY) - mtype_ext_cleanup(set); + if (map->dsize) ip_set_free(map->extensions); - } kfree(map); set->data = NULL; @@ -82,8 +74,6 @@ mtype_flush(struct ip_set *set) { struct mtype *map = set->data; - if (set->extensions & IPSET_EXT_DESTROY) - mtype_ext_cleanup(set); memset(map->members, 0, map->memsize); } @@ -101,9 +91,12 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(sizeof(*map) + map->memsize + - set->dsize * map->elements))) - goto nla_put_failure; - if (unlikely(ip_set_put_flags(skb, set))) + map->dsize * map->elements)) || + (SET_WITH_TIMEOUT(set) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) || + (SET_WITH_COUNTER(set) && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, + htonl(IPSET_FLAG_WITH_COUNTERS)))) goto nla_put_failure; ipset_nest_end(skb, nested); @@ -118,16 +111,16 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, { struct mtype *map = set->data; const struct mtype_adt_elem *e = value; - void *x = get_ext(set, map, e->id); - int ret = mtype_do_test(e, map, set->dsize); + void *x = get_ext(map, e->id); + int ret = mtype_do_test(e, map); if (ret <= 0) return ret; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(x, set))) + ip_set_timeout_expired(ext_timeout(x, map))) return 0; if (SET_WITH_COUNTER(set)) - ip_set_update_counter(ext_counter(x, set), ext, mext, flags); + ip_set_update_counter(ext_counter(x, map), ext, mext, flags); return 1; } @@ -137,30 +130,26 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, { struct mtype *map = set->data; const struct mtype_adt_elem *e = value; - void *x = get_ext(set, map, e->id); - int ret = mtype_do_add(e, map, flags, set->dsize); + void *x = get_ext(map, e->id); + int ret = mtype_do_add(e, map, flags); if (ret == IPSET_ADD_FAILED) { if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(x, set))) + ip_set_timeout_expired(ext_timeout(x, map))) ret = 0; else if (!(flags & IPSET_FLAG_EXIST)) return -IPSET_ERR_EXIST; - /* Element is re-added, cleanup extensions */ - ip_set_ext_destroy(set, x); } if (SET_WITH_TIMEOUT(set)) #ifdef IP_SET_BITMAP_STORED_TIMEOUT - mtype_add_timeout(ext_timeout(x, set), e, ext, set, map, ret); + mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret); #else - ip_set_timeout_set(ext_timeout(x, set), ext->timeout); + ip_set_timeout_set(ext_timeout(x, map), ext->timeout); #endif if (SET_WITH_COUNTER(set)) - ip_set_init_counter(ext_counter(x, set), ext); - if (SET_WITH_COMMENT(set)) - ip_set_init_comment(ext_comment(x, set), ext); + ip_set_init_counter(ext_counter(x, map), ext); return 0; } @@ -170,27 +159,16 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, { struct mtype *map = set->data; const struct mtype_adt_elem *e = value; - void *x = get_ext(set, map, e->id); + const void *x = get_ext(map, e->id); - if (mtype_do_del(e, map)) - return -IPSET_ERR_EXIST; - - ip_set_ext_destroy(set, x); - if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(x, set))) + if (mtype_do_del(e, map) || + (SET_WITH_TIMEOUT(set) && + ip_set_timeout_expired(ext_timeout(x, map)))) return -IPSET_ERR_EXIST; return 0; } -#ifndef IP_SET_BITMAP_STORED_TIMEOUT -static inline bool -mtype_is_filled(const struct mtype_elem *x) -{ - return true; -} -#endif - static int mtype_list(const struct ip_set *set, struct sk_buff *skb, struct netlink_callback *cb) @@ -198,21 +176,20 @@ mtype_list(const struct ip_set *set, struct mtype *map = set->data; struct nlattr *adt, *nested; void *x; - u32 id, first = cb->args[IPSET_CB_ARG0]; + u32 id, first = cb->args[2]; adt = ipset_nest_start(skb, IPSET_ATTR_ADT); if (!adt) return -EMSGSIZE; - for (; cb->args[IPSET_CB_ARG0] < map->elements; - cb->args[IPSET_CB_ARG0]++) { - id = cb->args[IPSET_CB_ARG0]; - x = get_ext(set, map, id); + for (; cb->args[2] < map->elements; cb->args[2]++) { + id = cb->args[2]; + x = get_ext(map, id); if (!test_bit(id, map->members) || (SET_WITH_TIMEOUT(set) && #ifdef IP_SET_BITMAP_STORED_TIMEOUT mtype_is_filled((const struct mtype_elem *) x) && #endif - ip_set_timeout_expired(ext_timeout(x, set)))) + ip_set_timeout_expired(ext_timeout(x, map)))) continue; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) { @@ -222,27 +199,40 @@ mtype_list(const struct ip_set *set, } else goto nla_put_failure; } - if (mtype_do_list(skb, map, id, set->dsize)) + if (mtype_do_list(skb, map, id)) goto nla_put_failure; - if (ip_set_put_extensions(skb, set, x, - mtype_is_filled((const struct mtype_elem *) x))) + if (SET_WITH_TIMEOUT(set)) { +#ifdef IP_SET_BITMAP_STORED_TIMEOUT + if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_stored(map, id, + ext_timeout(x, map))))) + goto nla_put_failure; +#else + if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get( + ext_timeout(x, map))))) + goto nla_put_failure; +#endif + } + if (SET_WITH_COUNTER(set) && + ip_set_put_counter(skb, ext_counter(x, map))) goto nla_put_failure; ipset_nest_end(skb, nested); } ipset_nest_end(skb, adt); /* Set listing finished */ - cb->args[IPSET_CB_ARG0] = 0; + cb->args[2] = 0; return 0; nla_put_failure: nla_nest_cancel(skb, nested); + ipset_nest_end(skb, adt); if (unlikely(id == first)) { - cb->args[IPSET_CB_ARG0] = 0; + cb->args[2] = 0; return -EMSGSIZE; } - ipset_nest_end(skb, adt); return 0; } @@ -251,23 +241,21 @@ mtype_gc(unsigned long ul_set) { struct ip_set *set = (struct ip_set *) ul_set; struct mtype *map = set->data; - void *x; + const void *x; u32 id; /* We run parallel with other readers (test element) * but adding/deleting new entries is locked out */ read_lock_bh(&set->lock); for (id = 0; id < map->elements; id++) - if (mtype_gc_test(id, map, set->dsize)) { - x = get_ext(set, map, id); - if (ip_set_timeout_expired(ext_timeout(x, set))) { + if (mtype_gc_test(id, map)) { + x = get_ext(map, id); + if (ip_set_timeout_expired(ext_timeout(x, map))) clear_bit(id, map->members); - ip_set_ext_destroy(set, x); - } } read_unlock_bh(&set->lock); - map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; add_timer(&map->gc); } diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index 6f1f9f4..f1a8128 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -25,13 +25,12 @@ #include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set_bitmap.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 Counter support added */ -#define IPSET_TYPE_REV_MAX 2 /* Comment support added */ +#define REVISION_MIN 0 +#define REVISION_MAX 1 /* Counter support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("bitmap:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("bitmap:ip", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_bitmap:ip"); #define MTYPE bitmap_ip @@ -45,7 +44,10 @@ struct bitmap_ip { u32 elements; /* number of max elements in the set */ u32 hosts; /* number of hosts in a subnet */ size_t memsize; /* members size */ + size_t dsize; /* extensions struct size */ + size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ u8 netmask; /* subnet netmask */ + u32 timeout; /* timeout parameter */ struct timer_list gc; /* garbage collection */ }; @@ -63,21 +65,20 @@ ip_to_id(const struct bitmap_ip *m, u32 ip) /* Common functions */ static inline int -bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, - struct bitmap_ip *map, size_t dsize) +bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map) { return !!test_bit(e->id, map->members); } static inline int -bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map, size_t dsize) +bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map) { return !!test_bit(id, map->members); } static inline int bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map, - u32 flags, size_t dsize) + u32 flags) { return !!test_and_set_bit(e->id, map->members); } @@ -89,8 +90,7 @@ bitmap_ip_do_del(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map) } static inline int -bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id, - size_t dsize) +bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id) { return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip + id * map->hosts)); @@ -113,7 +113,7 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, struct bitmap_ip *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_ip_adt_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); u32 ip; ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC)); @@ -131,9 +131,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], { struct bitmap_ip *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - u32 ip = 0, ip_to = 0; + u32 ip, ip_to; struct bitmap_ip_adt_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(map); int ret = 0; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -200,7 +200,7 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b) return x->first_ip == y->first_ip && x->last_ip == y->last_ip && x->netmask == y->netmask && - a->timeout == b->timeout && + x->timeout == y->timeout && a->extensions == b->extensions; } @@ -209,6 +209,25 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b) struct bitmap_ip_elem { }; +/* Timeout variant */ + +struct bitmap_ipt_elem { + unsigned long timeout; +}; + +/* Plain variant with counter */ + +struct bitmap_ipc_elem { + struct ip_set_counter counter; +}; + +/* Timeout variant with counter */ + +struct bitmap_ipct_elem { + unsigned long timeout; + struct ip_set_counter counter; +}; + #include "ip_set_bitmap_gen.h" /* Create bitmap:ip type of sets */ @@ -221,8 +240,8 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, map->members = ip_set_alloc(map->memsize); if (!map->members) return false; - if (set->dsize) { - map->extensions = ip_set_alloc(set->dsize * elements); + if (map->dsize) { + map->extensions = ip_set_alloc(map->dsize * elements); if (!map->extensions) { kfree(map->members); return false; @@ -233,7 +252,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, map->elements = elements; map->hosts = hosts; map->netmask = netmask; - set->timeout = IPSET_NO_TIMEOUT; + map->timeout = IPSET_NO_TIMEOUT; set->data = map; set->family = NFPROTO_IPV4; @@ -242,11 +261,10 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, } static int -bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[], - u32 flags) +bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) { struct bitmap_ip *map; - u32 first_ip = 0, last_ip = 0, hosts; + u32 first_ip, last_ip, hosts, cadt_flags = 0; u64 elements; u8 netmask = 32; int ret; @@ -318,15 +336,61 @@ bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[], map->memsize = bitmap_bytes(0, elements - 1); set->variant = &bitmap_ip; - set->dsize = ip_set_elem_len(set, tb, 0); - if (!init_map_ip(set, map, first_ip, last_ip, - elements, hosts, netmask)) { - kfree(map); - return -ENOMEM; - } - if (tb[IPSET_ATTR_TIMEOUT]) { - set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + if (tb[IPSET_ATTR_CADT_FLAGS]) + cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { + set->extensions |= IPSET_EXT_COUNTER; + if (tb[IPSET_ATTR_TIMEOUT]) { + map->dsize = sizeof(struct bitmap_ipct_elem); + map->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct bitmap_ipct_elem, timeout); + map->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct bitmap_ipct_elem, counter); + + if (!init_map_ip(set, map, first_ip, last_ip, + elements, hosts, netmask)) { + kfree(map); + return -ENOMEM; + } + + map->timeout = ip_set_timeout_uget( + tb[IPSET_ATTR_TIMEOUT]); + set->extensions |= IPSET_EXT_TIMEOUT; + + bitmap_ip_gc_init(set, bitmap_ip_gc); + } else { + map->dsize = sizeof(struct bitmap_ipc_elem); + map->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct bitmap_ipc_elem, counter); + + if (!init_map_ip(set, map, first_ip, last_ip, + elements, hosts, netmask)) { + kfree(map); + return -ENOMEM; + } + } + } else if (tb[IPSET_ATTR_TIMEOUT]) { + map->dsize = sizeof(struct bitmap_ipt_elem); + map->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct bitmap_ipt_elem, timeout); + + if (!init_map_ip(set, map, first_ip, last_ip, + elements, hosts, netmask)) { + kfree(map); + return -ENOMEM; + } + + map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + set->extensions |= IPSET_EXT_TIMEOUT; + bitmap_ip_gc_init(set, bitmap_ip_gc); + } else { + map->dsize = 0; + if (!init_map_ip(set, map, first_ip, last_ip, + elements, hosts, netmask)) { + kfree(map); + return -ENOMEM; + } } return 0; } @@ -337,8 +401,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = NFPROTO_IPV4, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = bitmap_ip_create, .create_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, @@ -356,7 +420,6 @@ static struct ip_set_type bitmap_ip_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 740eabe..3b30e0b 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -25,13 +25,12 @@ #include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set_bitmap.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 Counter support added */ -#define IPSET_TYPE_REV_MAX 2 /* Comment support added */ +#define REVISION_MIN 0 +#define REVISION_MAX 1 /* Counter support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("bitmap:ip,mac", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("bitmap:ip,mac", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_bitmap:ip,mac"); #define MTYPE bitmap_ipmac @@ -49,8 +48,11 @@ struct bitmap_ipmac { u32 first_ip; /* host byte order, included in range */ u32 last_ip; /* host byte order, included in range */ u32 elements; /* number of max elements in the set */ - size_t memsize; /* members size */ + u32 timeout; /* timeout value */ struct timer_list gc; /* garbage collector */ + size_t memsize; /* members size */ + size_t dsize; /* size of element */ + size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ }; /* ADT structure for generic function args */ @@ -80,13 +82,13 @@ get_elem(void *extensions, u16 id, size_t dsize) static inline int bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, - const struct bitmap_ipmac *map, size_t dsize) + const struct bitmap_ipmac *map) { const struct bitmap_ipmac_elem *elem; if (!test_bit(e->id, map->members)) return 0; - elem = get_elem(map->extensions, e->id, dsize); + elem = get_elem(map->extensions, e->id, map->dsize); if (elem->filled == MAC_FILLED) return e->ether == NULL || ether_addr_equal(e->ether, elem->ether); @@ -95,13 +97,13 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, } static inline int -bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map, size_t dsize) +bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map) { const struct bitmap_ipmac_elem *elem; if (!test_bit(id, map->members)) return 0; - elem = get_elem(map->extensions, id, dsize); + elem = get_elem(map->extensions, id, map->dsize); /* Timer not started for the incomplete elements */ return elem->filled == MAC_FILLED; } @@ -115,13 +117,13 @@ bitmap_ipmac_is_filled(const struct bitmap_ipmac_elem *elem) static inline int bitmap_ipmac_add_timeout(unsigned long *timeout, const struct bitmap_ipmac_adt_elem *e, - const struct ip_set_ext *ext, struct ip_set *set, + const struct ip_set_ext *ext, struct bitmap_ipmac *map, int mode) { u32 t = ext->timeout; if (mode == IPSET_ADD_START_STORED_TIMEOUT) { - if (t == set->timeout) + if (t == map->timeout) /* Timeout was not specified, get stored one */ t = *timeout; ip_set_timeout_set(timeout, t); @@ -140,11 +142,11 @@ bitmap_ipmac_add_timeout(unsigned long *timeout, static inline int bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, - struct bitmap_ipmac *map, u32 flags, size_t dsize) + struct bitmap_ipmac *map, u32 flags) { struct bitmap_ipmac_elem *elem; - elem = get_elem(map->extensions, e->id, dsize); + elem = get_elem(map->extensions, e->id, map->dsize); if (test_and_set_bit(e->id, map->members)) { if (elem->filled == MAC_FILLED) { if (e->ether && (flags & IPSET_FLAG_EXIST)) @@ -176,12 +178,22 @@ bitmap_ipmac_do_del(const struct bitmap_ipmac_adt_elem *e, return !test_and_clear_bit(e->id, map->members); } +static inline unsigned long +ip_set_timeout_stored(struct bitmap_ipmac *map, u32 id, unsigned long *timeout) +{ + const struct bitmap_ipmac_elem *elem = + get_elem(map->extensions, id, map->dsize); + + return elem->filled == MAC_FILLED ? ip_set_timeout_get(timeout) : + *timeout; +} + static inline int bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map, - u32 id, size_t dsize) + u32 id) { const struct bitmap_ipmac_elem *elem = - get_elem(map->extensions, id, dsize); + get_elem(map->extensions, id, map->dsize); return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip + id)) || @@ -204,7 +216,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, struct bitmap_ipmac *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_ipmac_adt_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); u32 ip; /* MAC can be src only */ @@ -233,8 +245,8 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[], const struct bitmap_ipmac *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_ipmac_adt_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip = 0; + struct ip_set_ext ext = IP_SET_INIT_UEXT(map); + u32 ip; int ret = 0; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -273,12 +285,43 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b) return x->first_ip == y->first_ip && x->last_ip == y->last_ip && - a->timeout == b->timeout && + x->timeout == y->timeout && a->extensions == b->extensions; } /* Plain variant */ +/* Timeout variant */ + +struct bitmap_ipmact_elem { + struct { + unsigned char ether[ETH_ALEN]; + unsigned char filled; + } __attribute__ ((aligned)); + unsigned long timeout; +}; + +/* Plain variant with counter */ + +struct bitmap_ipmacc_elem { + struct { + unsigned char ether[ETH_ALEN]; + unsigned char filled; + } __attribute__ ((aligned)); + struct ip_set_counter counter; +}; + +/* Timeout variant with counter */ + +struct bitmap_ipmacct_elem { + struct { + unsigned char ether[ETH_ALEN]; + unsigned char filled; + } __attribute__ ((aligned)); + unsigned long timeout; + struct ip_set_counter counter; +}; + #include "ip_set_bitmap_gen.h" /* Create bitmap:ip,mac type of sets */ @@ -287,11 +330,11 @@ static bool init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, u32 first_ip, u32 last_ip, u32 elements) { - map->members = ip_set_alloc(map->memsize); + map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize); if (!map->members) return false; - if (set->dsize) { - map->extensions = ip_set_alloc(set->dsize * elements); + if (map->dsize) { + map->extensions = ip_set_alloc(map->dsize * elements); if (!map->extensions) { kfree(map->members); return false; @@ -300,7 +343,7 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, map->first_ip = first_ip; map->last_ip = last_ip; map->elements = elements; - set->timeout = IPSET_NO_TIMEOUT; + map->timeout = IPSET_NO_TIMEOUT; set->data = map; set->family = NFPROTO_IPV4; @@ -309,10 +352,10 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, } static int -bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[], +bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[], u32 flags) { - u32 first_ip = 0, last_ip = 0; + u32 first_ip, last_ip, cadt_flags = 0; u64 elements; struct bitmap_ipmac *map; int ret; @@ -356,15 +399,57 @@ bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[], map->memsize = bitmap_bytes(0, elements - 1); set->variant = &bitmap_ipmac; - set->dsize = ip_set_elem_len(set, tb, - sizeof(struct bitmap_ipmac_elem)); - if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { - kfree(map); - return -ENOMEM; - } - if (tb[IPSET_ATTR_TIMEOUT]) { - set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + if (tb[IPSET_ATTR_CADT_FLAGS]) + cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { + set->extensions |= IPSET_EXT_COUNTER; + if (tb[IPSET_ATTR_TIMEOUT]) { + map->dsize = sizeof(struct bitmap_ipmacct_elem); + map->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct bitmap_ipmacct_elem, timeout); + map->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct bitmap_ipmacct_elem, counter); + + if (!init_map_ipmac(set, map, first_ip, last_ip, + elements)) { + kfree(map); + return -ENOMEM; + } + map->timeout = ip_set_timeout_uget( + tb[IPSET_ATTR_TIMEOUT]); + set->extensions |= IPSET_EXT_TIMEOUT; + bitmap_ipmac_gc_init(set, bitmap_ipmac_gc); + } else { + map->dsize = sizeof(struct bitmap_ipmacc_elem); + map->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct bitmap_ipmacc_elem, counter); + + if (!init_map_ipmac(set, map, first_ip, last_ip, + elements)) { + kfree(map); + return -ENOMEM; + } + } + } else if (tb[IPSET_ATTR_TIMEOUT]) { + map->dsize = sizeof(struct bitmap_ipmact_elem); + map->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct bitmap_ipmact_elem, timeout); + + if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { + kfree(map); + return -ENOMEM; + } + map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + set->extensions |= IPSET_EXT_TIMEOUT; bitmap_ipmac_gc_init(set, bitmap_ipmac_gc); + } else { + map->dsize = sizeof(struct bitmap_ipmac_elem); + + if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { + kfree(map); + return -ENOMEM; + } + set->variant = &bitmap_ipmac; } return 0; } @@ -375,8 +460,8 @@ static struct ip_set_type bitmap_ipmac_type = { .features = IPSET_TYPE_IP | IPSET_TYPE_MAC, .dimension = IPSET_DIM_TWO, .family = NFPROTO_IPV4, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = bitmap_ipmac_create, .create_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, @@ -393,7 +478,6 @@ static struct ip_set_type bitmap_ipmac_type = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index cf99676..8207d1f 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -20,13 +20,12 @@ #include <linux/netfilter/ipset/ip_set_bitmap.h> #include <linux/netfilter/ipset/ip_set_getport.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 Counter support added */ -#define IPSET_TYPE_REV_MAX 2 /* Comment support added */ +#define REVISION_MIN 0 +#define REVISION_MAX 1 /* Counter support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("bitmap:port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("bitmap:port", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_bitmap:port"); #define MTYPE bitmap_port @@ -39,6 +38,9 @@ struct bitmap_port { u16 last_port; /* host byte order, included in range */ u32 elements; /* number of max elements in the set */ size_t memsize; /* members size */ + size_t dsize; /* extensions struct size */ + size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ + u32 timeout; /* timeout parameter */ struct timer_list gc; /* garbage collection */ }; @@ -57,20 +59,20 @@ port_to_id(const struct bitmap_port *m, u16 port) static inline int bitmap_port_do_test(const struct bitmap_port_adt_elem *e, - const struct bitmap_port *map, size_t dsize) + const struct bitmap_port *map) { return !!test_bit(e->id, map->members); } static inline int -bitmap_port_gc_test(u16 id, const struct bitmap_port *map, size_t dsize) +bitmap_port_gc_test(u16 id, const struct bitmap_port *map) { return !!test_bit(id, map->members); } static inline int bitmap_port_do_add(const struct bitmap_port_adt_elem *e, - struct bitmap_port *map, u32 flags, size_t dsize) + struct bitmap_port *map, u32 flags) { return !!test_and_set_bit(e->id, map->members); } @@ -83,8 +85,7 @@ bitmap_port_do_del(const struct bitmap_port_adt_elem *e, } static inline int -bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id, - size_t dsize) +bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id) { return nla_put_net16(skb, IPSET_ATTR_PORT, htons(map->first_port + id)); @@ -105,7 +106,7 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, struct bitmap_port *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_port_adt_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); __be16 __port; u16 port = 0; @@ -130,7 +131,7 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[], struct bitmap_port *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_port_adt_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(map); u32 port; /* wraparound */ u16 port_to; int ret = 0; @@ -190,7 +191,7 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b) return x->first_port == y->first_port && x->last_port == y->last_port && - a->timeout == b->timeout && + x->timeout == y->timeout && a->extensions == b->extensions; } @@ -199,6 +200,25 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b) struct bitmap_port_elem { }; +/* Timeout variant */ + +struct bitmap_portt_elem { + unsigned long timeout; +}; + +/* Plain variant with counter */ + +struct bitmap_portc_elem { + struct ip_set_counter counter; +}; + +/* Timeout variant with counter */ + +struct bitmap_portct_elem { + unsigned long timeout; + struct ip_set_counter counter; +}; + #include "ip_set_bitmap_gen.h" /* Create bitmap:ip type of sets */ @@ -210,8 +230,8 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, map->members = ip_set_alloc(map->memsize); if (!map->members) return false; - if (set->dsize) { - map->extensions = ip_set_alloc(set->dsize * map->elements); + if (map->dsize) { + map->extensions = ip_set_alloc(map->dsize * map->elements); if (!map->extensions) { kfree(map->members); return false; @@ -219,7 +239,7 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, } map->first_port = first_port; map->last_port = last_port; - set->timeout = IPSET_NO_TIMEOUT; + map->timeout = IPSET_NO_TIMEOUT; set->data = map; set->family = NFPROTO_UNSPEC; @@ -228,11 +248,11 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, } static int -bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[], - u32 flags) +bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags) { struct bitmap_port *map; u16 first_port, last_port; + u32 cadt_flags = 0; if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) || @@ -254,16 +274,55 @@ bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[], return -ENOMEM; map->elements = last_port - first_port + 1; - map->memsize = bitmap_bytes(0, map->elements); + map->memsize = map->elements * sizeof(unsigned long); set->variant = &bitmap_port; - set->dsize = ip_set_elem_len(set, tb, 0); - if (!init_map_port(set, map, first_port, last_port)) { - kfree(map); - return -ENOMEM; - } - if (tb[IPSET_ATTR_TIMEOUT]) { - set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + if (tb[IPSET_ATTR_CADT_FLAGS]) + cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { + set->extensions |= IPSET_EXT_COUNTER; + if (tb[IPSET_ATTR_TIMEOUT]) { + map->dsize = sizeof(struct bitmap_portct_elem); + map->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct bitmap_portct_elem, timeout); + map->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct bitmap_portct_elem, counter); + if (!init_map_port(set, map, first_port, last_port)) { + kfree(map); + return -ENOMEM; + } + + map->timeout = + ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + set->extensions |= IPSET_EXT_TIMEOUT; + bitmap_port_gc_init(set, bitmap_port_gc); + } else { + map->dsize = sizeof(struct bitmap_portc_elem); + map->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct bitmap_portc_elem, counter); + if (!init_map_port(set, map, first_port, last_port)) { + kfree(map); + return -ENOMEM; + } + } + } else if (tb[IPSET_ATTR_TIMEOUT]) { + map->dsize = sizeof(struct bitmap_portt_elem); + map->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct bitmap_portt_elem, timeout); + if (!init_map_port(set, map, first_port, last_port)) { + kfree(map); + return -ENOMEM; + } + + map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + set->extensions |= IPSET_EXT_TIMEOUT; bitmap_port_gc_init(set, bitmap_port_gc); + } else { + map->dsize = 0; + if (!init_map_port(set, map, first_port, last_port)) { + kfree(map); + return -ENOMEM; + } + } return 0; } @@ -274,8 +333,8 @@ static struct ip_set_type bitmap_port_type = { .features = IPSET_TYPE_PORT, .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = bitmap_port_create, .create_policy = { [IPSET_ATTR_PORT] = { .type = NLA_U16 }, @@ -290,7 +349,6 @@ static struct ip_set_type bitmap_port_type = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index bac7e01..f2e30fb 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -17,8 +17,6 @@ #include <linux/spinlock.h> #include <linux/rculist.h> #include <net/netlink.h> -#include <net/net_namespace.h> -#include <net/netns/generic.h> #include <linux/netfilter.h> #include <linux/netfilter/x_tables.h> @@ -29,17 +27,8 @@ 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 */ -struct ip_set_net { - struct ip_set * __rcu *ip_set_list; /* all individual sets */ - ip_set_id_t ip_set_max; /* max number of sets */ - int is_deleted; /* deleted by ip_set_net_exit */ -}; -static int ip_set_net_id __read_mostly; - -static inline struct ip_set_net *ip_set_pernet(struct net *net) -{ - return net_generic(net, ip_set_net_id); -} +static struct ip_set * __rcu *ip_set_list; /* all individual sets */ +static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ #define IP_SET_INC 64 #define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0) @@ -56,8 +45,8 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); /* When the nfnl mutex is held: */ #define nfnl_dereference(p) \ rcu_dereference_protected(p, 1) -#define nfnl_set(inst, id) \ - nfnl_dereference((inst)->ip_set_list)[id] +#define nfnl_set(id) \ + nfnl_dereference(ip_set_list)[id] /* * The set types are implemented in modules and registered set types @@ -326,60 +315,6 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr) } EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6); -typedef void (*destroyer)(void *); -/* ipset data extension types, in size order */ - -const struct ip_set_ext_type ip_set_extensions[] = { - [IPSET_EXT_ID_COUNTER] = { - .type = IPSET_EXT_COUNTER, - .flag = IPSET_FLAG_WITH_COUNTERS, - .len = sizeof(struct ip_set_counter), - .align = __alignof__(struct ip_set_counter), - }, - [IPSET_EXT_ID_TIMEOUT] = { - .type = IPSET_EXT_TIMEOUT, - .len = sizeof(unsigned long), - .align = __alignof__(unsigned long), - }, - [IPSET_EXT_ID_COMMENT] = { - .type = IPSET_EXT_COMMENT | IPSET_EXT_DESTROY, - .flag = IPSET_FLAG_WITH_COMMENT, - .len = sizeof(struct ip_set_comment), - .align = __alignof__(struct ip_set_comment), - .destroy = (destroyer) ip_set_comment_free, - }, -}; -EXPORT_SYMBOL_GPL(ip_set_extensions); - -static inline bool -add_extension(enum ip_set_ext_id id, u32 flags, struct nlattr *tb[]) -{ - return ip_set_extensions[id].flag ? - (flags & ip_set_extensions[id].flag) : - !!tb[IPSET_ATTR_TIMEOUT]; -} - -size_t -ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len) -{ - enum ip_set_ext_id id; - size_t offset = 0; - u32 cadt_flags = 0; - - if (tb[IPSET_ATTR_CADT_FLAGS]) - cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - for (id = 0; id < IPSET_EXT_ID_MAX; id++) { - if (!add_extension(id, cadt_flags, tb)) - continue; - offset += ALIGN(len + offset, ip_set_extensions[id].align); - set->offset[id] = offset; - set->extensions |= ip_set_extensions[id].type; - offset += ip_set_extensions[id].len; - } - return len + offset; -} -EXPORT_SYMBOL_GPL(ip_set_elem_len); - int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], struct ip_set_ext *ext) @@ -399,12 +334,6 @@ ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], ext->packets = be64_to_cpu(nla_get_be64( tb[IPSET_ATTR_PACKETS])); } - if (tb[IPSET_ATTR_COMMENT]) { - if (!(set->extensions & IPSET_EXT_COMMENT)) - return -IPSET_ERR_COMMENT; - ext->comment = ip_set_comment_uget(tb[IPSET_ATTR_COMMENT]); - } - return 0; } EXPORT_SYMBOL_GPL(ip_set_get_extensions); @@ -445,14 +374,13 @@ __ip_set_put(struct ip_set *set) */ static inline struct ip_set * -ip_set_rcu_get(struct net *net, ip_set_id_t index) +ip_set_rcu_get(ip_set_id_t index) { struct ip_set *set; - struct ip_set_net *inst = ip_set_pernet(net); rcu_read_lock(); /* ip_set_list itself needs to be protected */ - set = rcu_dereference(inst->ip_set_list)[index]; + set = rcu_dereference(ip_set_list)[index]; rcu_read_unlock(); return set; @@ -462,8 +390,7 @@ int ip_set_test(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get( - dev_net(par->in ? par->in : par->out), index); + struct ip_set *set = ip_set_rcu_get(index); int ret = 0; BUG_ON(set == NULL); @@ -501,8 +428,7 @@ int ip_set_add(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get( - dev_net(par->in ? par->in : par->out), index); + struct ip_set *set = ip_set_rcu_get(index); int ret; BUG_ON(set == NULL); @@ -524,8 +450,7 @@ int ip_set_del(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get( - dev_net(par->in ? par->in : par->out), index); + struct ip_set *set = ip_set_rcu_get(index); int ret = 0; BUG_ON(set == NULL); @@ -549,15 +474,14 @@ EXPORT_SYMBOL_GPL(ip_set_del); * */ ip_set_id_t -ip_set_get_byname(struct net *net, const char *name, struct ip_set **set) +ip_set_get_byname(const char *name, struct ip_set **set) { ip_set_id_t i, index = IPSET_INVALID_ID; struct ip_set *s; - struct ip_set_net *inst = ip_set_pernet(net); rcu_read_lock(); - for (i = 0; i < inst->ip_set_max; i++) { - s = rcu_dereference(inst->ip_set_list)[i]; + for (i = 0; i < ip_set_max; i++) { + s = rcu_dereference(ip_set_list)[i]; if (s != NULL && STREQ(s->name, name)) { __ip_set_get(s); index = i; @@ -577,26 +501,17 @@ EXPORT_SYMBOL_GPL(ip_set_get_byname); * to be valid, after calling this function. * */ - -static inline void -__ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index) +void +ip_set_put_byindex(ip_set_id_t index) { struct ip_set *set; rcu_read_lock(); - set = rcu_dereference(inst->ip_set_list)[index]; + set = rcu_dereference(ip_set_list)[index]; if (set != NULL) __ip_set_put(set); rcu_read_unlock(); } - -void -ip_set_put_byindex(struct net *net, ip_set_id_t index) -{ - struct ip_set_net *inst = ip_set_pernet(net); - - __ip_set_put_byindex(inst, index); -} EXPORT_SYMBOL_GPL(ip_set_put_byindex); /* @@ -607,9 +522,9 @@ EXPORT_SYMBOL_GPL(ip_set_put_byindex); * */ const char * -ip_set_name_byindex(struct net *net, ip_set_id_t index) +ip_set_name_byindex(ip_set_id_t index) { - const struct ip_set *set = ip_set_rcu_get(net, index); + const struct ip_set *set = ip_set_rcu_get(index); BUG_ON(set == NULL); BUG_ON(set->ref == 0); @@ -631,15 +546,14 @@ EXPORT_SYMBOL_GPL(ip_set_name_byindex); * The nfnl mutex is used in the function. */ ip_set_id_t -ip_set_nfnl_get(struct net *net, const char *name) +ip_set_nfnl_get(const char *name) { ip_set_id_t i, index = IPSET_INVALID_ID; struct ip_set *s; - struct ip_set_net *inst = ip_set_pernet(net); nfnl_lock(NFNL_SUBSYS_IPSET); - for (i = 0; i < inst->ip_set_max; i++) { - s = nfnl_set(inst, i); + for (i = 0; i < ip_set_max; i++) { + s = nfnl_set(i); if (s != NULL && STREQ(s->name, name)) { __ip_set_get(s); index = i; @@ -659,16 +573,15 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get); * The nfnl mutex is used in the function. */ ip_set_id_t -ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index) +ip_set_nfnl_get_byindex(ip_set_id_t index) { struct ip_set *set; - struct ip_set_net *inst = ip_set_pernet(net); - if (index > inst->ip_set_max) + if (index > ip_set_max) return IPSET_INVALID_ID; nfnl_lock(NFNL_SUBSYS_IPSET); - set = nfnl_set(inst, index); + set = nfnl_set(index); if (set) __ip_set_get(set); else @@ -687,17 +600,13 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex); * The nfnl mutex is used in the function. */ void -ip_set_nfnl_put(struct net *net, ip_set_id_t index) +ip_set_nfnl_put(ip_set_id_t index) { struct ip_set *set; - struct ip_set_net *inst = ip_set_pernet(net); - nfnl_lock(NFNL_SUBSYS_IPSET); - if (!inst->is_deleted) { /* already deleted from ip_set_net_exit() */ - set = nfnl_set(inst, index); - if (set != NULL) - __ip_set_put(set); - } + set = nfnl_set(index); + if (set != NULL) + __ip_set_put(set); nfnl_unlock(NFNL_SUBSYS_IPSET); } EXPORT_SYMBOL_GPL(ip_set_nfnl_put); @@ -755,14 +664,14 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = { }; static struct ip_set * -find_set_and_id(struct ip_set_net *inst, const char *name, ip_set_id_t *id) +find_set_and_id(const char *name, ip_set_id_t *id) { struct ip_set *set = NULL; ip_set_id_t i; *id = IPSET_INVALID_ID; - for (i = 0; i < inst->ip_set_max; i++) { - set = nfnl_set(inst, i); + for (i = 0; i < ip_set_max; i++) { + set = nfnl_set(i); if (set != NULL && STREQ(set->name, name)) { *id = i; break; @@ -772,23 +681,22 @@ find_set_and_id(struct ip_set_net *inst, const char *name, ip_set_id_t *id) } static inline struct ip_set * -find_set(struct ip_set_net *inst, const char *name) +find_set(const char *name) { ip_set_id_t id; - return find_set_and_id(inst, name, &id); + return find_set_and_id(name, &id); } static int -find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index, - struct ip_set **set) +find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set) { struct ip_set *s; ip_set_id_t i; *index = IPSET_INVALID_ID; - for (i = 0; i < inst->ip_set_max; i++) { - s = nfnl_set(inst, i); + for (i = 0; i < ip_set_max; i++) { + s = nfnl_set(i); if (s == NULL) { if (*index == IPSET_INVALID_ID) *index = i; @@ -817,8 +725,6 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct net *net = sock_net(ctnl); - struct ip_set_net *inst = ip_set_pernet(net); struct ip_set *set, *clash = NULL; ip_set_id_t index = IPSET_INVALID_ID; struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {}; @@ -877,7 +783,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, goto put_out; } - ret = set->type->create(net, set, tb, flags); + ret = set->type->create(set, tb, flags); if (ret != 0) goto put_out; @@ -888,7 +794,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, * by the nfnl mutex. Find the first free index in ip_set_list * and check clashing. */ - ret = find_free_id(inst, set->name, &index, &clash); + ret = find_free_id(set->name, &index, &clash); if (ret == -EEXIST) { /* If this is the same set and requested, ignore error */ if ((flags & IPSET_FLAG_EXIST) && @@ -901,9 +807,9 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, goto cleanup; } else if (ret == -IPSET_ERR_MAX_SETS) { struct ip_set **list, **tmp; - ip_set_id_t i = inst->ip_set_max + IP_SET_INC; + ip_set_id_t i = ip_set_max + IP_SET_INC; - if (i < inst->ip_set_max || i == IPSET_INVALID_ID) + if (i < ip_set_max || i == IPSET_INVALID_ID) /* Wraparound */ goto cleanup; @@ -911,14 +817,14 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, if (!list) goto cleanup; /* nfnl mutex is held, both lists are valid */ - tmp = nfnl_dereference(inst->ip_set_list); - memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max); - rcu_assign_pointer(inst->ip_set_list, list); + tmp = nfnl_dereference(ip_set_list); + memcpy(list, tmp, sizeof(struct ip_set *) * ip_set_max); + rcu_assign_pointer(ip_set_list, list); /* Make sure all current packets have passed through */ synchronize_net(); /* Use new list */ - index = inst->ip_set_max; - inst->ip_set_max = i; + index = ip_set_max; + ip_set_max = i; kfree(tmp); ret = 0; } else if (ret) @@ -928,7 +834,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, * Finally! Add our shiny new set to the list, and be done. */ pr_debug("create: '%s' created with index %u!\n", set->name, index); - nfnl_set(inst, index) = set; + nfnl_set(index) = set; return ret; @@ -951,12 +857,12 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = { }; static void -ip_set_destroy_set(struct ip_set_net *inst, ip_set_id_t index) +ip_set_destroy_set(ip_set_id_t index) { - struct ip_set *set = nfnl_set(inst, index); + struct ip_set *set = nfnl_set(index); pr_debug("set: %s\n", set->name); - nfnl_set(inst, index) = NULL; + nfnl_set(index) = NULL; /* Must call it without holding any lock */ set->variant->destroy(set); @@ -969,7 +875,6 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *s; ip_set_id_t i; int ret = 0; @@ -989,22 +894,21 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, */ read_lock_bh(&ip_set_ref_lock); if (!attr[IPSET_ATTR_SETNAME]) { - for (i = 0; i < inst->ip_set_max; i++) { - s = nfnl_set(inst, i); + for (i = 0; i < ip_set_max; i++) { + s = nfnl_set(i); if (s != NULL && s->ref) { ret = -IPSET_ERR_BUSY; goto out; } } read_unlock_bh(&ip_set_ref_lock); - for (i = 0; i < inst->ip_set_max; i++) { - s = nfnl_set(inst, i); + for (i = 0; i < ip_set_max; i++) { + s = nfnl_set(i); if (s != NULL) - ip_set_destroy_set(inst, i); + ip_set_destroy_set(i); } } else { - s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]), - &i); + s = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &i); if (s == NULL) { ret = -ENOENT; goto out; @@ -1014,7 +918,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, } read_unlock_bh(&ip_set_ref_lock); - ip_set_destroy_set(inst, i); + ip_set_destroy_set(i); } return 0; out: @@ -1039,7 +943,6 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *s; ip_set_id_t i; @@ -1047,13 +950,13 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb, return -IPSET_ERR_PROTOCOL; if (!attr[IPSET_ATTR_SETNAME]) { - for (i = 0; i < inst->ip_set_max; i++) { - s = nfnl_set(inst, i); + for (i = 0; i < ip_set_max; i++) { + s = nfnl_set(i); if (s != NULL) ip_set_flush_set(s); } } else { - s = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); + s = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); if (s == NULL) return -ENOENT; @@ -1079,7 +982,6 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *set, *s; const char *name2; ip_set_id_t i; @@ -1090,7 +992,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_SETNAME2] == NULL)) return -IPSET_ERR_PROTOCOL; - set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1101,8 +1003,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, } name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); - for (i = 0; i < inst->ip_set_max; i++) { - s = nfnl_set(inst, i); + for (i = 0; i < ip_set_max; i++) { + s = nfnl_set(i); if (s != NULL && STREQ(s->name, name2)) { ret = -IPSET_ERR_EXIST_SETNAME2; goto out; @@ -1129,7 +1031,6 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *from, *to; ip_set_id_t from_id, to_id; char from_name[IPSET_MAXNAMELEN]; @@ -1139,13 +1040,11 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_SETNAME2] == NULL)) return -IPSET_ERR_PROTOCOL; - from = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]), - &from_id); + from = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &from_id); if (from == NULL) return -ENOENT; - to = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME2]), - &to_id); + to = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME2]), &to_id); if (to == NULL) return -IPSET_ERR_EXIST_SETNAME2; @@ -1162,8 +1061,8 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, write_lock_bh(&ip_set_ref_lock); swap(from->ref, to->ref); - nfnl_set(inst, from_id) = to; - nfnl_set(inst, to_id) = from; + nfnl_set(from_id) = to; + nfnl_set(to_id) = from; write_unlock_bh(&ip_set_ref_lock); return 0; @@ -1182,12 +1081,9 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, static int ip_set_dump_done(struct netlink_callback *cb) { - struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET]; - if (cb->args[IPSET_CB_ARG0]) { - pr_debug("release set %s\n", - nfnl_set(inst, cb->args[IPSET_CB_INDEX])->name); - __ip_set_put_byindex(inst, - (ip_set_id_t) cb->args[IPSET_CB_INDEX]); + if (cb->args[2]) { + pr_debug("release set %s\n", nfnl_set(cb->args[1])->name); + ip_set_put_byindex((ip_set_id_t) cb->args[1]); } return 0; } @@ -1205,7 +1101,7 @@ dump_attrs(struct nlmsghdr *nlh) } static int -dump_init(struct netlink_callback *cb, struct ip_set_net *inst) +dump_init(struct netlink_callback *cb) { struct nlmsghdr *nlh = nlmsg_hdr(cb->skb); int min_len = nlmsg_total_size(sizeof(struct nfgenmsg)); @@ -1218,22 +1114,21 @@ dump_init(struct netlink_callback *cb, struct ip_set_net *inst) nla_parse(cda, IPSET_ATTR_CMD_MAX, attr, nlh->nlmsg_len - min_len, ip_set_setname_policy); - /* cb->args[IPSET_CB_NET]: net namespace - * [IPSET_CB_DUMP]: dump single set/all sets - * [IPSET_CB_INDEX]: set index - * [IPSET_CB_ARG0]: type specific + /* cb->args[0] : dump single set/all sets + * [1] : set index + * [..]: type specific */ if (cda[IPSET_ATTR_SETNAME]) { struct ip_set *set; - set = find_set_and_id(inst, nla_data(cda[IPSET_ATTR_SETNAME]), + set = find_set_and_id(nla_data(cda[IPSET_ATTR_SETNAME]), &index); if (set == NULL) return -ENOENT; dump_type = DUMP_ONE; - cb->args[IPSET_CB_INDEX] = index; + cb->args[1] = index; } else dump_type = DUMP_ALL; @@ -1241,8 +1136,7 @@ dump_init(struct netlink_callback *cb, struct ip_set_net *inst) u32 f = ip_set_get_h32(cda[IPSET_ATTR_FLAGS]); dump_type |= (f << 16); } - cb->args[IPSET_CB_NET] = (unsigned long)inst; - cb->args[IPSET_CB_DUMP] = dump_type; + cb->args[0] = dump_type; return 0; } @@ -1254,12 +1148,11 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) struct ip_set *set = NULL; struct nlmsghdr *nlh = NULL; unsigned int flags = NETLINK_CB(cb->skb).portid ? NLM_F_MULTI : 0; - struct ip_set_net *inst = ip_set_pernet(sock_net(skb->sk)); u32 dump_type, dump_flags; int ret = 0; - if (!cb->args[IPSET_CB_DUMP]) { - ret = dump_init(cb, inst); + if (!cb->args[0]) { + ret = dump_init(cb); if (ret < 0) { nlh = nlmsg_hdr(cb->skb); /* We have to create and send the error message @@ -1270,19 +1163,18 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) } } - if (cb->args[IPSET_CB_INDEX] >= inst->ip_set_max) + if (cb->args[1] >= ip_set_max) goto out; - dump_type = DUMP_TYPE(cb->args[IPSET_CB_DUMP]); - dump_flags = DUMP_FLAGS(cb->args[IPSET_CB_DUMP]); - max = dump_type == DUMP_ONE ? cb->args[IPSET_CB_INDEX] + 1 - : inst->ip_set_max; + dump_type = DUMP_TYPE(cb->args[0]); + dump_flags = DUMP_FLAGS(cb->args[0]); + max = dump_type == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; dump_last: - pr_debug("dump type, flag: %u %u index: %ld\n", - dump_type, dump_flags, cb->args[IPSET_CB_INDEX]); - for (; cb->args[IPSET_CB_INDEX] < max; cb->args[IPSET_CB_INDEX]++) { - index = (ip_set_id_t) cb->args[IPSET_CB_INDEX]; - set = nfnl_set(inst, index); + pr_debug("args[0]: %u %u args[1]: %ld\n", + dump_type, dump_flags, cb->args[1]); + for (; cb->args[1] < max; cb->args[1]++) { + index = (ip_set_id_t) cb->args[1]; + set = nfnl_set(index); if (set == NULL) { if (dump_type == DUMP_ONE) { ret = -ENOENT; @@ -1298,7 +1190,7 @@ dump_last: !!(set->type->features & IPSET_DUMP_LAST))) continue; pr_debug("List set: %s\n", set->name); - if (!cb->args[IPSET_CB_ARG0]) { + if (!cb->args[2]) { /* Start listing: make sure set won't be destroyed */ pr_debug("reference set\n"); __ip_set_get(set); @@ -1315,7 +1207,7 @@ dump_last: goto nla_put_failure; if (dump_flags & IPSET_FLAG_LIST_SETNAME) goto next_set; - switch (cb->args[IPSET_CB_ARG0]) { + switch (cb->args[2]) { case 0: /* Core header data */ if (nla_put_string(skb, IPSET_ATTR_TYPENAME, @@ -1335,7 +1227,7 @@ dump_last: read_lock_bh(&set->lock); ret = set->variant->list(set, skb, cb); read_unlock_bh(&set->lock); - if (!cb->args[IPSET_CB_ARG0]) + if (!cb->args[2]) /* Set is done, proceed with next one */ goto next_set; goto release_refcount; @@ -1344,8 +1236,8 @@ dump_last: /* If we dump all sets, continue with dumping last ones */ if (dump_type == DUMP_ALL) { dump_type = DUMP_LAST; - cb->args[IPSET_CB_DUMP] = dump_type | (dump_flags << 16); - cb->args[IPSET_CB_INDEX] = 0; + cb->args[0] = dump_type | (dump_flags << 16); + cb->args[1] = 0; goto dump_last; } goto out; @@ -1354,15 +1246,15 @@ nla_put_failure: ret = -EFAULT; next_set: if (dump_type == DUMP_ONE) - cb->args[IPSET_CB_INDEX] = IPSET_INVALID_ID; + cb->args[1] = IPSET_INVALID_ID; else - cb->args[IPSET_CB_INDEX]++; + cb->args[1]++; release_refcount: /* If there was an error or set is done, release set */ - if (ret || !cb->args[IPSET_CB_ARG0]) { - pr_debug("release set %s\n", nfnl_set(inst, index)->name); - __ip_set_put_byindex(inst, index); - cb->args[IPSET_CB_ARG0] = 0; + if (ret || !cb->args[2]) { + pr_debug("release set %s\n", nfnl_set(index)->name); + ip_set_put_byindex(index); + cb->args[2] = 0; } out: if (nlh) { @@ -1464,7 +1356,6 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *set; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; const struct nlattr *nla; @@ -1483,7 +1374,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_LINENO] == NULL)))) return -IPSET_ERR_PROTOCOL; - set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1519,7 +1410,6 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *set; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; const struct nlattr *nla; @@ -1538,7 +1428,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_LINENO] == NULL)))) return -IPSET_ERR_PROTOCOL; - set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1574,7 +1464,6 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *set; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; int ret = 0; @@ -1585,7 +1474,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb, !flag_nested(attr[IPSET_ATTR_DATA]))) return -IPSET_ERR_PROTOCOL; - set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1610,7 +1499,6 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { - struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); const struct ip_set *set; struct sk_buff *skb2; struct nlmsghdr *nlh2; @@ -1620,7 +1508,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_SETNAME] == NULL)) return -IPSET_ERR_PROTOCOL; - set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1845,10 +1733,8 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) unsigned int *op; void *data; int copylen = *len, ret = 0; - struct net *net = sock_net(sk); - struct ip_set_net *inst = ip_set_pernet(net); - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; if (optval != SO_IP_SET) return -EBADF; @@ -1897,39 +1783,22 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) } req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; nfnl_lock(NFNL_SUBSYS_IPSET); - find_set_and_id(inst, req_get->set.name, &id); + find_set_and_id(req_get->set.name, &id); req_get->set.index = id; nfnl_unlock(NFNL_SUBSYS_IPSET); goto copy; } - case IP_SET_OP_GET_FNAME: { - struct ip_set_req_get_set_family *req_get = data; - ip_set_id_t id; - - if (*len != sizeof(struct ip_set_req_get_set_family)) { - ret = -EINVAL; - goto done; - } - req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; - nfnl_lock(NFNL_SUBSYS_IPSET); - find_set_and_id(inst, req_get->set.name, &id); - req_get->set.index = id; - if (id != IPSET_INVALID_ID) - req_get->family = nfnl_set(inst, id)->family; - nfnl_unlock(NFNL_SUBSYS_IPSET); - goto copy; - } case IP_SET_OP_GET_BYINDEX: { struct ip_set_req_get_set *req_get = data; struct ip_set *set; if (*len != sizeof(struct ip_set_req_get_set) || - req_get->set.index >= inst->ip_set_max) { + req_get->set.index >= ip_set_max) { ret = -EINVAL; goto done; } nfnl_lock(NFNL_SUBSYS_IPSET); - set = nfnl_set(inst, req_get->set.index); + set = nfnl_set(req_get->set.index); strncpy(req_get->set.name, set ? set->name : "", IPSET_MAXNAMELEN); nfnl_unlock(NFNL_SUBSYS_IPSET); @@ -1958,81 +1827,49 @@ static struct nf_sockopt_ops so_set __read_mostly = { .owner = THIS_MODULE, }; -static int __net_init -ip_set_net_init(struct net *net) +static int __init +ip_set_init(void) { - struct ip_set_net *inst = ip_set_pernet(net); struct ip_set **list; + int ret; - inst->ip_set_max = max_sets ? max_sets : CONFIG_IP_SET_MAX; - if (inst->ip_set_max >= IPSET_INVALID_ID) - inst->ip_set_max = IPSET_INVALID_ID - 1; + if (max_sets) + ip_set_max = max_sets; + if (ip_set_max >= IPSET_INVALID_ID) + ip_set_max = IPSET_INVALID_ID - 1; - list = kzalloc(sizeof(struct ip_set *) * inst->ip_set_max, GFP_KERNEL); + list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL); if (!list) return -ENOMEM; - inst->is_deleted = 0; - rcu_assign_pointer(inst->ip_set_list, list); - pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL); - return 0; -} -static void __net_exit -ip_set_net_exit(struct net *net) -{ - struct ip_set_net *inst = ip_set_pernet(net); - - struct ip_set *set = NULL; - ip_set_id_t i; - - inst->is_deleted = 1; /* flag for ip_set_nfnl_put */ - - for (i = 0; i < inst->ip_set_max; i++) { - set = nfnl_set(inst, i); - if (set != NULL) - ip_set_destroy_set(inst, i); - } - kfree(rcu_dereference_protected(inst->ip_set_list, 1)); -} - -static struct pernet_operations ip_set_net_ops = { - .init = ip_set_net_init, - .exit = ip_set_net_exit, - .id = &ip_set_net_id, - .size = sizeof(struct ip_set_net) -}; - - -static int __init -ip_set_init(void) -{ - int ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); + rcu_assign_pointer(ip_set_list, list); + ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); if (ret != 0) { pr_err("ip_set: cannot register with nfnetlink.\n"); + kfree(list); return ret; } ret = nf_register_sockopt(&so_set); if (ret != 0) { pr_err("SO_SET registry failed: %d\n", ret); nfnetlink_subsys_unregister(&ip_set_netlink_subsys); + kfree(list); return ret; } - ret = register_pernet_subsys(&ip_set_net_ops); - if (ret) { - pr_err("ip_set: cannot register pernet_subsys.\n"); - nf_unregister_sockopt(&so_set); - nfnetlink_subsys_unregister(&ip_set_netlink_subsys); - return ret; - } + + pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL); return 0; } static void __exit ip_set_fini(void) { - unregister_pernet_subsys(&ip_set_net_ops); + struct ip_set **list = rcu_dereference_protected(ip_set_list, 1); + + /* There can't be any existing set */ nf_unregister_sockopt(&so_set); nfnetlink_subsys_unregister(&ip_set_netlink_subsys); + kfree(list); pr_debug("these are the famous last words\n"); } diff --git a/net/netfilter/ipset/ip_set_getport.c b/net/netfilter/ipset/ip_set_getport.c index 29fb01d..dac156f 100644 --- a/net/netfilter/ipset/ip_set_getport.c +++ b/net/netfilter/ipset/ip_set_getport.c @@ -102,25 +102,9 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src, int protocol = iph->protocol; /* See comments at tcp_match in ip_tables.c */ - if (protocol <= 0) + if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET)) return false; - if (ntohs(iph->frag_off) & IP_OFFSET) - switch (protocol) { - case IPPROTO_TCP: - case IPPROTO_SCTP: - case IPPROTO_UDP: - case IPPROTO_UDPLITE: - case IPPROTO_ICMP: - /* Port info not available for fragment offset > 0 */ - return false; - default: - /* Other protocols doesn't have ports, - so we can match fragments */ - *proto = protocol; - return true; - } - return get_port(skb, protocol, protooff, src, port, proto); } EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index be6932a..707bc52 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -15,7 +15,8 @@ #define rcu_dereference_bh(p) rcu_dereference(p) #endif -#define rcu_dereference_bh_nfnl(p) rcu_dereference_bh_check(p, 1) +#define CONCAT(a, b) a##b +#define TOKEN(a, b) CONCAT(a, b) /* Hashing which uses arrays to resolve clashing. The hash table is resized * (doubled) when searching becomes too long. @@ -77,14 +78,10 @@ struct htable { #define hbucket(h, i) (&((h)->bucket[i])) -#ifndef IPSET_NET_COUNT -#define IPSET_NET_COUNT 1 -#endif - /* Book-keeping of the prefixes added to the set */ struct net_prefixes { - u32 nets[IPSET_NET_COUNT]; /* number of elements per cidr */ - u8 cidr[IPSET_NET_COUNT]; /* the different cidr values in the set */ + u8 cidr; /* the different cidr values in the set */ + u32 nets; /* number of elements per cidr */ }; /* Compute the hash table size */ @@ -117,6 +114,23 @@ htable_bits(u32 hashsize) return bits; } +/* Destroy the hashtable part of the set */ +static void +ahash_destroy(struct htable *t) +{ + struct hbucket *n; + u32 i; + + for (i = 0; i < jhash_size(t->htable_bits); i++) { + n = hbucket(t, i); + if (n->size) + /* FIXME: use slab cache */ + kfree(n->value); + } + + ip_set_free(t); +} + static int hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) { @@ -142,30 +156,30 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) } #ifdef IP_SET_HASH_WITH_NETS -#if IPSET_NET_COUNT > 1 -#define __CIDR(cidr, i) (cidr[i]) -#else -#define __CIDR(cidr, i) (cidr) -#endif #ifdef IP_SET_HASH_WITH_NETS_PACKED /* When cidr is packed with nomatch, cidr - 1 is stored in the entry */ -#define CIDR(cidr, i) (__CIDR(cidr, i) + 1) +#define CIDR(cidr) (cidr + 1) #else -#define CIDR(cidr, i) (__CIDR(cidr, i)) +#define CIDR(cidr) (cidr) #endif #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) #ifdef IP_SET_HASH_WITH_MULTI -#define NLEN(family) (SET_HOST_MASK(family) + 1) +#define NETS_LENGTH(family) (SET_HOST_MASK(family) + 1) #else -#define NLEN(family) SET_HOST_MASK(family) +#define NETS_LENGTH(family) SET_HOST_MASK(family) #endif #else -#define NLEN(family) 0 +#define NETS_LENGTH(family) 0 #endif /* IP_SET_HASH_WITH_NETS */ +#define ext_timeout(e, h) \ +(unsigned long *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_TIMEOUT]) +#define ext_counter(e, h) \ +(struct ip_set_counter *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_COUNTER]) + #endif /* _IP_SET_HASH_GEN_H */ /* Family dependent templates */ @@ -180,8 +194,6 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) #undef mtype_data_next #undef mtype_elem -#undef mtype_ahash_destroy -#undef mtype_ext_cleanup #undef mtype_add_cidr #undef mtype_del_cidr #undef mtype_ahash_memsize @@ -208,43 +220,41 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) #undef HKEY -#define mtype_data_equal IPSET_TOKEN(MTYPE, _data_equal) +#define mtype_data_equal TOKEN(MTYPE, _data_equal) #ifdef IP_SET_HASH_WITH_NETS -#define mtype_do_data_match IPSET_TOKEN(MTYPE, _do_data_match) +#define mtype_do_data_match TOKEN(MTYPE, _do_data_match) #else #define mtype_do_data_match(d) 1 #endif -#define mtype_data_set_flags IPSET_TOKEN(MTYPE, _data_set_flags) -#define mtype_data_reset_elem IPSET_TOKEN(MTYPE, _data_reset_elem) -#define mtype_data_reset_flags IPSET_TOKEN(MTYPE, _data_reset_flags) -#define mtype_data_netmask IPSET_TOKEN(MTYPE, _data_netmask) -#define mtype_data_list IPSET_TOKEN(MTYPE, _data_list) -#define mtype_data_next IPSET_TOKEN(MTYPE, _data_next) -#define mtype_elem IPSET_TOKEN(MTYPE, _elem) -#define mtype_ahash_destroy IPSET_TOKEN(MTYPE, _ahash_destroy) -#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_ahash_memsize IPSET_TOKEN(MTYPE, _ahash_memsize) -#define mtype_flush IPSET_TOKEN(MTYPE, _flush) -#define mtype_destroy IPSET_TOKEN(MTYPE, _destroy) -#define mtype_gc_init IPSET_TOKEN(MTYPE, _gc_init) -#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_data_set_flags TOKEN(MTYPE, _data_set_flags) +#define mtype_data_reset_flags TOKEN(MTYPE, _data_reset_flags) +#define mtype_data_netmask TOKEN(MTYPE, _data_netmask) +#define mtype_data_list TOKEN(MTYPE, _data_list) +#define mtype_data_next TOKEN(MTYPE, _data_next) +#define mtype_elem TOKEN(MTYPE, _elem) +#define mtype_add_cidr TOKEN(MTYPE, _add_cidr) +#define mtype_del_cidr TOKEN(MTYPE, _del_cidr) +#define mtype_ahash_memsize TOKEN(MTYPE, _ahash_memsize) +#define mtype_flush TOKEN(MTYPE, _flush) +#define mtype_destroy TOKEN(MTYPE, _destroy) +#define mtype_gc_init TOKEN(MTYPE, _gc_init) +#define mtype_same_set TOKEN(MTYPE, _same_set) +#define mtype_kadt TOKEN(MTYPE, _kadt) +#define mtype_uadt TOKEN(MTYPE, _uadt) #define mtype MTYPE -#define mtype_add IPSET_TOKEN(MTYPE, _add) -#define mtype_del IPSET_TOKEN(MTYPE, _del) -#define mtype_test_cidrs IPSET_TOKEN(MTYPE, _test_cidrs) -#define mtype_test IPSET_TOKEN(MTYPE, _test) -#define mtype_expire IPSET_TOKEN(MTYPE, _expire) -#define mtype_resize IPSET_TOKEN(MTYPE, _resize) -#define mtype_head IPSET_TOKEN(MTYPE, _head) -#define mtype_list IPSET_TOKEN(MTYPE, _list) -#define mtype_gc IPSET_TOKEN(MTYPE, _gc) -#define mtype_variant IPSET_TOKEN(MTYPE, _variant) -#define mtype_data_match IPSET_TOKEN(MTYPE, _data_match) +#define mtype_elem TOKEN(MTYPE, _elem) +#define mtype_add TOKEN(MTYPE, _add) +#define mtype_del TOKEN(MTYPE, _del) +#define mtype_test_cidrs TOKEN(MTYPE, _test_cidrs) +#define mtype_test TOKEN(MTYPE, _test) +#define mtype_expire TOKEN(MTYPE, _expire) +#define mtype_resize TOKEN(MTYPE, _resize) +#define mtype_head TOKEN(MTYPE, _head) +#define mtype_list TOKEN(MTYPE, _list) +#define mtype_gc TOKEN(MTYPE, _gc) +#define mtype_variant TOKEN(MTYPE, _variant) +#define mtype_data_match TOKEN(MTYPE, _data_match) #ifndef HKEY_DATALEN #define HKEY_DATALEN sizeof(struct mtype_elem) @@ -259,10 +269,13 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) /* The generic hash structure */ struct htype { - struct htable __rcu *table; /* the hash table */ + struct htable *table; /* the hash table */ u32 maxelem; /* max elements in the hash */ u32 elements; /* current element (vs timeout) */ u32 initval; /* random jhash init value */ + u32 timeout; /* timeout value, if enabled */ + size_t dsize; /* data struct size */ + size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ struct timer_list gc; /* garbage collection when timeout enabled */ struct mtype_elem next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_MULTI @@ -284,49 +297,49 @@ struct htype { /* Network cidr size book keeping when the hash stores different * sized networks */ static void -mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) +mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length) { int i, j; /* Add in increasing prefix order, so larger cidr first */ - for (i = 0, j = -1; i < nets_length && h->nets[i].nets[n]; i++) { + for (i = 0, j = -1; i < nets_length && h->nets[i].nets; i++) { if (j != -1) continue; - else if (h->nets[i].cidr[n] < cidr) + else if (h->nets[i].cidr < cidr) j = i; - else if (h->nets[i].cidr[n] == cidr) { - h->nets[i].nets[n]++; + else if (h->nets[i].cidr == cidr) { + h->nets[i].nets++; return; } } if (j != -1) { for (; i > j; i--) { - h->nets[i].cidr[n] = h->nets[i - 1].cidr[n]; - h->nets[i].nets[n] = h->nets[i - 1].nets[n]; + h->nets[i].cidr = h->nets[i - 1].cidr; + h->nets[i].nets = h->nets[i - 1].nets; } } - h->nets[i].cidr[n] = cidr; - h->nets[i].nets[n] = 1; + h->nets[i].cidr = cidr; + h->nets[i].nets = 1; } static void -mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) +mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length) { u8 i, j, net_end = nets_length - 1; for (i = 0; i < nets_length; i++) { - if (h->nets[i].cidr[n] != cidr) + if (h->nets[i].cidr != cidr) continue; - if (h->nets[i].nets[n] > 1 || i == net_end || - h->nets[i + 1].nets[n] == 0) { - h->nets[i].nets[n]--; + if (h->nets[i].nets > 1 || i == net_end || + h->nets[i + 1].nets == 0) { + h->nets[i].nets--; return; } - for (j = i; j < net_end && h->nets[j].nets[n]; j++) { - h->nets[j].cidr[n] = h->nets[j + 1].cidr[n]; - h->nets[j].nets[n] = h->nets[j + 1].nets[n]; + for (j = i; j < net_end && h->nets[j].nets; j++) { + h->nets[j].cidr = h->nets[j + 1].cidr; + h->nets[j].nets = h->nets[j + 1].nets; } - h->nets[j].nets[n] = 0; + h->nets[j].nets = 0; return; } } @@ -334,10 +347,10 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) /* Calculate the actual memory size of the set data */ static size_t -mtype_ahash_memsize(const struct htype *h, const struct htable *t, - u8 nets_length, size_t dsize) +mtype_ahash_memsize(const struct htype *h, u8 nets_length) { u32 i; + struct htable *t = h->table; size_t memsize = sizeof(*h) + sizeof(*t) #ifdef IP_SET_HASH_WITH_NETS @@ -346,70 +359,35 @@ mtype_ahash_memsize(const struct htype *h, const struct htable *t, + jhash_size(t->htable_bits) * sizeof(struct hbucket); for (i = 0; i < jhash_size(t->htable_bits); i++) - memsize += t->bucket[i].size * dsize; + memsize += t->bucket[i].size * h->dsize; return memsize; } -/* Get the ith element from the array block n */ -#define ahash_data(n, i, dsize) \ - ((struct mtype_elem *)((n)->value + ((i) * (dsize)))) - -static void -mtype_ext_cleanup(struct ip_set *set, struct hbucket *n) -{ - int i; - - for (i = 0; i < n->pos; i++) - ip_set_ext_destroy(set, ahash_data(n, i, set->dsize)); -} - /* Flush a hash type of set: destroy all elements */ static void mtype_flush(struct ip_set *set) { struct htype *h = set->data; - struct htable *t; + struct htable *t = h->table; struct hbucket *n; u32 i; - t = rcu_dereference_bh_nfnl(h->table); for (i = 0; i < jhash_size(t->htable_bits); i++) { n = hbucket(t, i); if (n->size) { - if (set->extensions & IPSET_EXT_DESTROY) - mtype_ext_cleanup(set, n); n->size = n->pos = 0; /* FIXME: use slab cache */ kfree(n->value); } } #ifdef IP_SET_HASH_WITH_NETS - memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family)); + memset(h->nets, 0, sizeof(struct net_prefixes) + * NETS_LENGTH(set->family)); #endif h->elements = 0; } -/* Destroy the hashtable part of the set */ -static void -mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy) -{ - struct hbucket *n; - u32 i; - - for (i = 0; i < jhash_size(t->htable_bits); i++) { - n = hbucket(t, i); - if (n->size) { - if (set->extensions & IPSET_EXT_DESTROY && ext_destroy) - mtype_ext_cleanup(set, n); - /* FIXME: use slab cache */ - kfree(n->value); - } - } - - ip_set_free(t); -} - /* Destroy a hash type of set */ static void mtype_destroy(struct ip_set *set) @@ -419,7 +397,7 @@ mtype_destroy(struct ip_set *set) if (set->extensions & IPSET_EXT_TIMEOUT) del_timer_sync(&h->gc); - mtype_ahash_destroy(set, rcu_dereference_bh_nfnl(h->table), true); + ahash_destroy(h->table); #ifdef IP_SET_HASH_WITH_RBTREE rbtree_destroy(&h->rbtree); #endif @@ -436,10 +414,10 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) init_timer(&h->gc); h->gc.data = (unsigned long) set; h->gc.function = gc; - h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; + h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; add_timer(&h->gc); pr_debug("gc initialized, run in every %u\n", - IPSET_GC_PERIOD(set->timeout)); + IPSET_GC_PERIOD(h->timeout)); } static bool @@ -450,40 +428,37 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b) /* Resizing changes htable_bits, so we ignore it */ return x->maxelem == y->maxelem && - a->timeout == b->timeout && + x->timeout == y->timeout && #ifdef IP_SET_HASH_WITH_NETMASK x->netmask == y->netmask && #endif a->extensions == b->extensions; } +/* Get the ith element from the array block n */ +#define ahash_data(n, i, dsize) \ + ((struct mtype_elem *)((n)->value + ((i) * (dsize)))) + /* Delete expired elements from the hashtable */ static void -mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) +mtype_expire(struct htype *h, u8 nets_length, size_t dsize) { - struct htable *t; + struct htable *t = h->table; struct hbucket *n; struct mtype_elem *data; u32 i; int j; -#ifdef IP_SET_HASH_WITH_NETS - u8 k; -#endif - rcu_read_lock_bh(); - t = rcu_dereference_bh(h->table); for (i = 0; i < jhash_size(t->htable_bits); i++) { n = hbucket(t, i); for (j = 0; j < n->pos; j++) { data = ahash_data(n, j, dsize); - if (ip_set_timeout_expired(ext_timeout(data, set))) { + if (ip_set_timeout_expired(ext_timeout(data, h))) { pr_debug("expired %u/%u\n", i, j); #ifdef IP_SET_HASH_WITH_NETS - for (k = 0; k < IPSET_NET_COUNT; k++) - mtype_del_cidr(h, CIDR(data->cidr, k), - nets_length, k); + mtype_del_cidr(h, CIDR(data->cidr), + nets_length); #endif - ip_set_ext_destroy(set, data); if (j != n->pos - 1) /* Not last one */ memcpy(data, @@ -506,7 +481,6 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) n->value = tmp; } } - rcu_read_unlock_bh(); } static void @@ -517,10 +491,10 @@ mtype_gc(unsigned long ul_set) pr_debug("called\n"); write_lock_bh(&set->lock); - mtype_expire(set, h, NLEN(set->family), set->dsize); + mtype_expire(h, NETS_LENGTH(set->family), h->dsize); write_unlock_bh(&set->lock); - h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; + h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; add_timer(&h->gc); } @@ -531,7 +505,7 @@ static int mtype_resize(struct ip_set *set, bool retried) { struct htype *h = set->data; - struct htable *t, *orig = rcu_dereference_bh_nfnl(h->table); + struct htable *t, *orig = h->table; u8 htable_bits = orig->htable_bits; #ifdef IP_SET_HASH_WITH_NETS u8 flags; @@ -546,7 +520,8 @@ mtype_resize(struct ip_set *set, bool retried) if (SET_WITH_TIMEOUT(set) && !retried) { i = h->elements; write_lock_bh(&set->lock); - mtype_expire(set, set->data, NLEN(set->family), set->dsize); + mtype_expire(set->data, NETS_LENGTH(set->family), + h->dsize); write_unlock_bh(&set->lock); if (h->elements < i) return 0; @@ -573,25 +548,25 @@ retry: for (i = 0; i < jhash_size(orig->htable_bits); i++) { n = hbucket(orig, i); for (j = 0; j < n->pos; j++) { - data = ahash_data(n, j, set->dsize); + data = ahash_data(n, j, h->dsize); #ifdef IP_SET_HASH_WITH_NETS flags = 0; mtype_data_reset_flags(data, &flags); #endif m = hbucket(t, HKEY(data, h->initval, htable_bits)); - ret = hbucket_elem_add(m, AHASH_MAX(h), set->dsize); + ret = hbucket_elem_add(m, AHASH_MAX(h), h->dsize); if (ret < 0) { #ifdef IP_SET_HASH_WITH_NETS mtype_data_reset_flags(data, &flags); #endif read_unlock_bh(&set->lock); - mtype_ahash_destroy(set, t, false); + ahash_destroy(t); if (ret == -EAGAIN) goto retry; return ret; } - d = ahash_data(m, m->pos++, set->dsize); - memcpy(d, data, set->dsize); + d = ahash_data(m, m->pos++, h->dsize); + memcpy(d, data, h->dsize); #ifdef IP_SET_HASH_WITH_NETS mtype_data_reset_flags(d, &flags); #endif @@ -606,7 +581,7 @@ retry: pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name, orig->htable_bits, orig, t->htable_bits, t); - mtype_ahash_destroy(set, orig, false); + ahash_destroy(orig); return 0; } @@ -629,7 +604,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem) /* FIXME: when set is full, we slow down here */ - mtype_expire(set, h, NLEN(set->family), set->dsize); + mtype_expire(h, NETS_LENGTH(set->family), h->dsize); if (h->elements >= h->maxelem) { if (net_ratelimit()) @@ -643,11 +618,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, key = HKEY(value, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { - data = ahash_data(n, i, set->dsize); + data = ahash_data(n, i, h->dsize); if (mtype_data_equal(data, d, &multi)) { if (flag_exist || (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(data, set)))) { + ip_set_timeout_expired(ext_timeout(data, h)))) { /* Just the extensions could be overwritten */ j = i; goto reuse_slot; @@ -658,37 +633,30 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, } /* Reuse first timed out entry */ if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(data, set)) && + ip_set_timeout_expired(ext_timeout(data, h)) && j != AHASH_MAX(h) + 1) j = i; } reuse_slot: if (j != AHASH_MAX(h) + 1) { /* Fill out reused slot */ - data = ahash_data(n, j, set->dsize); + data = ahash_data(n, j, h->dsize); #ifdef IP_SET_HASH_WITH_NETS - for (i = 0; i < IPSET_NET_COUNT; i++) { - mtype_del_cidr(h, CIDR(data->cidr, i), - NLEN(set->family), i); - mtype_add_cidr(h, CIDR(d->cidr, i), - NLEN(set->family), i); - } + mtype_del_cidr(h, CIDR(data->cidr), NETS_LENGTH(set->family)); + mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); #endif - ip_set_ext_destroy(set, data); } else { /* Use/create a new slot */ TUNE_AHASH_MAX(h, multi); - ret = hbucket_elem_add(n, AHASH_MAX(h), set->dsize); + ret = hbucket_elem_add(n, AHASH_MAX(h), h->dsize); if (ret != 0) { if (ret == -EAGAIN) mtype_data_next(&h->next, d); goto out; } - data = ahash_data(n, n->pos++, set->dsize); + data = ahash_data(n, n->pos++, h->dsize); #ifdef IP_SET_HASH_WITH_NETS - for (i = 0; i < IPSET_NET_COUNT; i++) - mtype_add_cidr(h, CIDR(d->cidr, i), NLEN(set->family), - i); + mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); #endif h->elements++; } @@ -697,11 +665,9 @@ reuse_slot: mtype_data_set_flags(data, flags); #endif if (SET_WITH_TIMEOUT(set)) - ip_set_timeout_set(ext_timeout(data, set), ext->timeout); + ip_set_timeout_set(ext_timeout(data, h), ext->timeout); if (SET_WITH_COUNTER(set)) - ip_set_init_counter(ext_counter(data, set), ext); - if (SET_WITH_COMMENT(set)) - ip_set_init_comment(ext_comment(data, set), ext); + ip_set_init_counter(ext_counter(data, h), ext); out: rcu_read_unlock_bh(); @@ -716,60 +682,47 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, struct ip_set_ext *mext, u32 flags) { struct htype *h = set->data; - struct htable *t; + struct htable *t = h->table; const struct mtype_elem *d = value; struct mtype_elem *data; struct hbucket *n; - int i, ret = -IPSET_ERR_EXIST; -#ifdef IP_SET_HASH_WITH_NETS - u8 j; -#endif + int i; u32 key, multi = 0; - rcu_read_lock_bh(); - t = rcu_dereference_bh(h->table); key = HKEY(value, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { - data = ahash_data(n, i, set->dsize); + data = ahash_data(n, i, h->dsize); if (!mtype_data_equal(data, d, &multi)) continue; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(data, set))) - goto out; + ip_set_timeout_expired(ext_timeout(data, h))) + return -IPSET_ERR_EXIST; if (i != n->pos - 1) /* Not last one */ - memcpy(data, ahash_data(n, n->pos - 1, set->dsize), - set->dsize); + memcpy(data, ahash_data(n, n->pos - 1, h->dsize), + h->dsize); n->pos--; h->elements--; #ifdef IP_SET_HASH_WITH_NETS - for (j = 0; j < IPSET_NET_COUNT; j++) - mtype_del_cidr(h, CIDR(d->cidr, j), NLEN(set->family), - j); + mtype_del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); #endif - ip_set_ext_destroy(set, data); if (n->pos + AHASH_INIT_SIZE < n->size) { void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) - * set->dsize, + * h->dsize, GFP_ATOMIC); - if (!tmp) { - ret = 0; - goto out; - } + if (!tmp) + return 0; n->size -= AHASH_INIT_SIZE; - memcpy(tmp, n->value, n->size * set->dsize); + memcpy(tmp, n->value, n->size * h->dsize); kfree(n->value); n->value = tmp; } - ret = 0; - goto out; + return 0; } -out: - rcu_read_unlock_bh(); - return ret; + return -IPSET_ERR_EXIST; } static inline int @@ -777,7 +730,8 @@ mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext, struct ip_set_ext *mext, struct ip_set *set, u32 flags) { if (SET_WITH_COUNTER(set)) - ip_set_update_counter(ext_counter(data, set), + ip_set_update_counter(ext_counter(data, + (struct htype *)(set->data)), ext, mext, flags); return mtype_do_data_match(data); } @@ -791,38 +745,25 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, struct ip_set_ext *mext, u32 flags) { struct htype *h = set->data; - struct htable *t = rcu_dereference_bh(h->table); + struct htable *t = h->table; struct hbucket *n; struct mtype_elem *data; -#if IPSET_NET_COUNT == 2 - struct mtype_elem orig = *d; - int i, j = 0, k; -#else int i, j = 0; -#endif u32 key, multi = 0; - u8 nets_length = NLEN(set->family); + u8 nets_length = NETS_LENGTH(set->family); pr_debug("test by nets\n"); - for (; j < nets_length && h->nets[j].nets[0] && !multi; j++) { -#if IPSET_NET_COUNT == 2 - mtype_data_reset_elem(d, &orig); - mtype_data_netmask(d, h->nets[j].cidr[0], false); - for (k = 0; k < nets_length && h->nets[k].nets[1] && !multi; - k++) { - mtype_data_netmask(d, h->nets[k].cidr[1], true); -#else - mtype_data_netmask(d, h->nets[j].cidr[0]); -#endif + for (; j < nets_length && h->nets[j].nets && !multi; j++) { + mtype_data_netmask(d, h->nets[j].cidr); key = HKEY(d, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { - data = ahash_data(n, i, set->dsize); + data = ahash_data(n, i, h->dsize); if (!mtype_data_equal(data, d, &multi)) continue; if (SET_WITH_TIMEOUT(set)) { if (!ip_set_timeout_expired( - ext_timeout(data, set))) + ext_timeout(data, h))) return mtype_data_match(data, ext, mext, set, flags); @@ -833,9 +774,6 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, return mtype_data_match(data, ext, mext, set, flags); } -#if IPSET_NET_COUNT == 2 - } -#endif } return 0; } @@ -847,41 +785,30 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, struct ip_set_ext *mext, u32 flags) { struct htype *h = set->data; - struct htable *t; + struct htable *t = h->table; struct mtype_elem *d = value; struct hbucket *n; struct mtype_elem *data; - int i, ret = 0; + int i; u32 key, multi = 0; - rcu_read_lock_bh(); - t = rcu_dereference_bh(h->table); #ifdef IP_SET_HASH_WITH_NETS /* If we test an IP address and not a network address, * try all possible network sizes */ - for (i = 0; i < IPSET_NET_COUNT; i++) - if (CIDR(d->cidr, i) != SET_HOST_MASK(set->family)) - break; - if (i == IPSET_NET_COUNT) { - ret = mtype_test_cidrs(set, d, ext, mext, flags); - goto out; - } + if (CIDR(d->cidr) == SET_HOST_MASK(set->family)) + return mtype_test_cidrs(set, d, ext, mext, flags); #endif key = HKEY(d, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { - data = ahash_data(n, i, set->dsize); + data = ahash_data(n, i, h->dsize); if (mtype_data_equal(data, d, &multi) && !(SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(data, set)))) { - ret = mtype_data_match(data, ext, mext, set, flags); - goto out; - } + ip_set_timeout_expired(ext_timeout(data, h)))) + return mtype_data_match(data, ext, mext, set, flags); } -out: - rcu_read_unlock_bh(); - return ret; + return 0; } /* Reply a HEADER request: fill out the header part of the set */ @@ -889,18 +816,18 @@ static int mtype_head(struct ip_set *set, struct sk_buff *skb) { const struct htype *h = set->data; - const struct htable *t; struct nlattr *nested; size_t memsize; - t = rcu_dereference_bh_nfnl(h->table); - memsize = mtype_ahash_memsize(h, t, NLEN(set->family), set->dsize); + read_lock_bh(&set->lock); + memsize = mtype_ahash_memsize(h, NETS_LENGTH(set->family)); + read_unlock_bh(&set->lock); nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, - htonl(jhash_size(t->htable_bits))) || + htonl(jhash_size(h->table->htable_bits))) || nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) goto nla_put_failure; #ifdef IP_SET_HASH_WITH_NETMASK @@ -909,9 +836,12 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) goto nla_put_failure; #endif if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || - nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize))) - goto nla_put_failure; - if (unlikely(ip_set_put_flags(skb, set))) + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || + ((set->extensions & IPSET_EXT_TIMEOUT) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))) || + ((set->extensions & IPSET_EXT_COUNTER) && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, + htonl(IPSET_FLAG_WITH_COUNTERS)))) goto nla_put_failure; ipset_nest_end(skb, nested); @@ -926,11 +856,11 @@ mtype_list(const struct ip_set *set, struct sk_buff *skb, struct netlink_callback *cb) { const struct htype *h = set->data; - const struct htable *t = rcu_dereference_bh_nfnl(h->table); + const struct htable *t = h->table; struct nlattr *atd, *nested; const struct hbucket *n; const struct mtype_elem *e; - u32 first = cb->args[IPSET_CB_ARG0]; + u32 first = cb->args[2]; /* We assume that one hash bucket fills into one page */ void *incomplete; int i; @@ -939,22 +869,20 @@ mtype_list(const struct ip_set *set, if (!atd) return -EMSGSIZE; pr_debug("list hash set %s\n", set->name); - for (; cb->args[IPSET_CB_ARG0] < jhash_size(t->htable_bits); - cb->args[IPSET_CB_ARG0]++) { + for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { incomplete = skb_tail_pointer(skb); - n = hbucket(t, cb->args[IPSET_CB_ARG0]); - pr_debug("cb->arg bucket: %lu, t %p n %p\n", - cb->args[IPSET_CB_ARG0], t, n); + n = hbucket(t, cb->args[2]); + pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n); for (i = 0; i < n->pos; i++) { - e = ahash_data(n, i, set->dsize); + e = ahash_data(n, i, h->dsize); if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, set))) + ip_set_timeout_expired(ext_timeout(e, h))) continue; pr_debug("list hash %lu hbucket %p i %u, data %p\n", - cb->args[IPSET_CB_ARG0], n, i, e); + cb->args[2], n, i, e); nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) { - if (cb->args[IPSET_CB_ARG0] == first) { + if (cb->args[2] == first) { nla_nest_cancel(skb, atd); return -EMSGSIZE; } else @@ -962,37 +890,43 @@ mtype_list(const struct ip_set *set, } if (mtype_data_list(skb, e)) goto nla_put_failure; - if (ip_set_put_extensions(skb, set, e, true)) + if (SET_WITH_TIMEOUT(set) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get( + ext_timeout(e, h))))) + goto nla_put_failure; + if (SET_WITH_COUNTER(set) && + ip_set_put_counter(skb, ext_counter(e, h))) goto nla_put_failure; ipset_nest_end(skb, nested); } } ipset_nest_end(skb, atd); /* Set listing finished */ - cb->args[IPSET_CB_ARG0] = 0; + cb->args[2] = 0; return 0; nla_put_failure: nlmsg_trim(skb, incomplete); - if (unlikely(first == cb->args[IPSET_CB_ARG0])) { + ipset_nest_end(skb, atd); + if (unlikely(first == cb->args[2])) { pr_warning("Can't list set %s: one bucket does not fit into " "a message. Please report it!\n", set->name); - cb->args[IPSET_CB_ARG0] = 0; + cb->args[2] = 0; return -EMSGSIZE; } - ipset_nest_end(skb, atd); return 0; } static int -IPSET_TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb, - const struct xt_action_param *par, - enum ipset_adt adt, struct ip_set_adt_opt *opt); +TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, struct ip_set_adt_opt *opt); static int -IPSET_TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags, bool retried); +TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried); static const struct ip_set_type_variant mtype_variant = { .kadt = mtype_kadt, @@ -1012,17 +946,16 @@ static const struct ip_set_type_variant mtype_variant = { #ifdef IP_SET_EMIT_CREATE static int -IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, - struct nlattr *tb[], u32 flags) +TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags) { u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; + u32 cadt_flags = 0; u8 hbits; #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; #endif size_t hsize; struct HTYPE *h; - struct htable *t; if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; @@ -1072,7 +1005,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, h->netmask = netmask; #endif get_random_bytes(&h->initval, sizeof(h->initval)); - set->timeout = IPSET_NO_TIMEOUT; + h->timeout = IPSET_NO_TIMEOUT; hbits = htable_bits(hashsize); hsize = htable_size(hbits); @@ -1080,37 +1013,91 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, kfree(h); return -ENOMEM; } - t = ip_set_alloc(hsize); - if (!t) { + h->table = ip_set_alloc(hsize); + if (!h->table) { kfree(h); return -ENOMEM; } - t->htable_bits = hbits; - rcu_assign_pointer(h->table, t); + h->table->htable_bits = hbits; set->data = h; - if (set->family == NFPROTO_IPV4) { - set->variant = &IPSET_TOKEN(HTYPE, 4_variant); - set->dsize = ip_set_elem_len(set, tb, - sizeof(struct IPSET_TOKEN(HTYPE, 4_elem))); + if (set->family == NFPROTO_IPV4) + set->variant = &TOKEN(HTYPE, 4_variant); + else + set->variant = &TOKEN(HTYPE, 6_variant); + + if (tb[IPSET_ATTR_CADT_FLAGS]) + cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { + set->extensions |= IPSET_EXT_COUNTER; + if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = + ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + set->extensions |= IPSET_EXT_TIMEOUT; + if (set->family == NFPROTO_IPV4) { + h->dsize = + sizeof(struct TOKEN(HTYPE, 4ct_elem)); + h->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct TOKEN(HTYPE, 4ct_elem), + timeout); + h->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct TOKEN(HTYPE, 4ct_elem), + counter); + TOKEN(HTYPE, 4_gc_init)(set, + TOKEN(HTYPE, 4_gc)); + } else { + h->dsize = + sizeof(struct TOKEN(HTYPE, 6ct_elem)); + h->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct TOKEN(HTYPE, 6ct_elem), + timeout); + h->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct TOKEN(HTYPE, 6ct_elem), + counter); + TOKEN(HTYPE, 6_gc_init)(set, + TOKEN(HTYPE, 6_gc)); + } + } else { + if (set->family == NFPROTO_IPV4) { + h->dsize = + sizeof(struct TOKEN(HTYPE, 4c_elem)); + h->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct TOKEN(HTYPE, 4c_elem), + counter); + } else { + h->dsize = + sizeof(struct TOKEN(HTYPE, 6c_elem)); + h->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct TOKEN(HTYPE, 6c_elem), + counter); + } + } + } else if (tb[IPSET_ATTR_TIMEOUT]) { + h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + set->extensions |= IPSET_EXT_TIMEOUT; + if (set->family == NFPROTO_IPV4) { + h->dsize = sizeof(struct TOKEN(HTYPE, 4t_elem)); + h->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct TOKEN(HTYPE, 4t_elem), + timeout); + TOKEN(HTYPE, 4_gc_init)(set, TOKEN(HTYPE, 4_gc)); + } else { + h->dsize = sizeof(struct TOKEN(HTYPE, 6t_elem)); + h->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct TOKEN(HTYPE, 6t_elem), + timeout); + TOKEN(HTYPE, 6_gc_init)(set, TOKEN(HTYPE, 6_gc)); + } } else { - set->variant = &IPSET_TOKEN(HTYPE, 6_variant); - set->dsize = ip_set_elem_len(set, tb, - sizeof(struct IPSET_TOKEN(HTYPE, 6_elem))); - } - if (tb[IPSET_ATTR_TIMEOUT]) { - set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); if (set->family == NFPROTO_IPV4) - IPSET_TOKEN(HTYPE, 4_gc_init)(set, - IPSET_TOKEN(HTYPE, 4_gc)); + h->dsize = sizeof(struct TOKEN(HTYPE, 4_elem)); else - IPSET_TOKEN(HTYPE, 6_gc_init)(set, - IPSET_TOKEN(HTYPE, 6_gc)); + h->dsize = sizeof(struct TOKEN(HTYPE, 6_elem)); } pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", - set->name, jhash_size(t->htable_bits), - t->htable_bits, h->maxelem, set->data, t); + set->name, jhash_size(h->table->htable_bits), + h->table->htable_bits, h->maxelem, set->data, h->table); return 0; } diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index e65fc24..c74e6e1 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -23,20 +23,19 @@ #include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set_hash.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 Counters support */ -#define IPSET_TYPE_REV_MAX 2 /* Comments support */ +#define REVISION_MIN 0 +#define REVISION_MAX 1 /* Counters support */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("hash:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("hash:ip", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_hash:ip"); /* Type specific function prefix */ #define HTYPE hash_ip #define IP_SET_HASH_WITH_NETMASK -/* IPv4 variant */ +/* IPv4 variants */ /* Member elements */ struct hash_ip4_elem { @@ -44,6 +43,22 @@ struct hash_ip4_elem { __be32 ip; }; +struct hash_ip4t_elem { + __be32 ip; + unsigned long timeout; +}; + +struct hash_ip4c_elem { + __be32 ip; + struct ip_set_counter counter; +}; + +struct hash_ip4ct_elem { + __be32 ip; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -84,7 +99,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip4_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); __be32 ip; ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip); @@ -103,8 +118,8 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip4_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip = 0, ip_to = 0, hosts; + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + u32 ip, ip_to, hosts; int ret = 0; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -163,13 +178,29 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variant */ +/* IPv6 variants */ /* Member elements */ struct hash_ip6_elem { union nf_inet_addr ip; }; +struct hash_ip6t_elem { + union nf_inet_addr ip; + unsigned long timeout; +}; + +struct hash_ip6c_elem { + union nf_inet_addr ip; + struct ip_set_counter counter; +}; + +struct hash_ip6ct_elem { + union nf_inet_addr ip; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -222,7 +253,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip6_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); hash_ip6_netmask(&e.ip, h->netmask); @@ -239,7 +270,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip6_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); int ret; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -273,8 +304,8 @@ static struct ip_set_type hash_ip_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = hash_ip_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -293,7 +324,6 @@ static struct ip_set_type hash_ip_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 525a595..7a2d2bd 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -24,20 +24,19 @@ #include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_hash.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 SCTP and UDPLITE support added */ -/* 2 Counters support added */ -#define IPSET_TYPE_REV_MAX 3 /* Comments support added */ +#define REVISION_MIN 0 +/* 1 SCTP and UDPLITE support added */ +#define REVISION_MAX 2 /* Counters support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("hash:ip,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("hash:ip,port", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_hash:ip,port"); /* Type specific function prefix */ #define HTYPE hash_ipport -/* IPv4 variant */ +/* IPv4 variants */ /* Member elements */ struct hash_ipport4_elem { @@ -47,6 +46,31 @@ struct hash_ipport4_elem { u8 padding; }; +struct hash_ipport4t_elem { + __be32 ip; + __be16 port; + u8 proto; + u8 padding; + unsigned long timeout; +}; + +struct hash_ipport4c_elem { + __be32 ip; + __be16 port; + u8 proto; + u8 padding; + struct ip_set_counter counter; +}; + +struct hash_ipport4ct_elem { + __be32 ip; + __be16 port; + u8 proto; + u8 padding; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -92,9 +116,10 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { + const struct hash_ipport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) @@ -111,8 +136,8 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip, ip_to = 0, p = 0, port, port_to; + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + u32 ip, ip_to, p = 0, port, port_to; bool with_ports = false; int ret; @@ -197,7 +222,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variant */ +/* IPv6 variants */ struct hash_ipport6_elem { union nf_inet_addr ip; @@ -206,6 +231,31 @@ struct hash_ipport6_elem { u8 padding; }; +struct hash_ipport6t_elem { + union nf_inet_addr ip; + __be16 port; + u8 proto; + u8 padding; + unsigned long timeout; +}; + +struct hash_ipport6c_elem { + union nf_inet_addr ip; + __be16 port; + u8 proto; + u8 padding; + struct ip_set_counter counter; +}; + +struct hash_ipport6ct_elem { + union nf_inet_addr ip; + __be16 port; + u8 proto; + u8 padding; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -256,9 +306,10 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { + const struct hash_ipport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) @@ -275,7 +326,7 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); u32 port, port_to; bool with_ports = false; int ret; @@ -345,8 +396,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .dimension = IPSET_DIM_TWO, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = hash_ipport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -368,7 +419,6 @@ static struct ip_set_type hash_ipport_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index f563663..34e8a1a 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -24,20 +24,19 @@ #include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_hash.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 SCTP and UDPLITE support added */ -/* 2 Counters support added */ -#define IPSET_TYPE_REV_MAX 3 /* Comments support added */ +#define REVISION_MIN 0 +/* 1 SCTP and UDPLITE support added */ +#define REVISION_MAX 2 /* Counters support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("hash:ip,port,ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("hash:ip,port,ip", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_hash:ip,port,ip"); /* Type specific function prefix */ #define HTYPE hash_ipportip -/* IPv4 variant */ +/* IPv4 variants */ /* Member elements */ struct hash_ipportip4_elem { @@ -48,6 +47,34 @@ struct hash_ipportip4_elem { u8 padding; }; +struct hash_ipportip4t_elem { + __be32 ip; + __be32 ip2; + __be16 port; + u8 proto; + u8 padding; + unsigned long timeout; +}; + +struct hash_ipportip4c_elem { + __be32 ip; + __be32 ip2; + __be16 port; + u8 proto; + u8 padding; + struct ip_set_counter counter; +}; + +struct hash_ipportip4ct_elem { + __be32 ip; + __be32 ip2; + __be16 port; + u8 proto; + u8 padding; + struct ip_set_counter counter; + unsigned long timeout; +}; + static inline bool hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, const struct hash_ipportip4_elem *ip2, @@ -93,9 +120,10 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { + const struct hash_ipportip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) @@ -113,8 +141,8 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipportip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip, ip_to = 0, p = 0, port, port_to; + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + u32 ip, ip_to, p = 0, port, port_to; bool with_ports = false; int ret; @@ -203,7 +231,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variant */ +/* IPv6 variants */ struct hash_ipportip6_elem { union nf_inet_addr ip; @@ -213,6 +241,34 @@ struct hash_ipportip6_elem { u8 padding; }; +struct hash_ipportip6t_elem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + __be16 port; + u8 proto; + u8 padding; + unsigned long timeout; +}; + +struct hash_ipportip6c_elem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + __be16 port; + u8 proto; + u8 padding; + struct ip_set_counter counter; +}; + +struct hash_ipportip6ct_elem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + __be16 port; + u8 proto; + u8 padding; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -263,9 +319,10 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { + const struct hash_ipportip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) @@ -283,7 +340,7 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipportip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); u32 port, port_to; bool with_ports = false; int ret; @@ -357,8 +414,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = hash_ipportip_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -380,7 +437,6 @@ static struct ip_set_type hash_ipportip_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 5d87fe8..f15f3e2 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -24,16 +24,15 @@ #include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_hash.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 SCTP and UDPLITE support added */ -/* 2 Range as input support for IPv4 added */ -/* 3 nomatch flag support added */ -/* 4 Counters support added */ -#define IPSET_TYPE_REV_MAX 5 /* Comments support added */ +#define REVISION_MIN 0 +/* 1 SCTP and UDPLITE support added */ +/* 2 Range as input support for IPv4 added */ +/* 3 nomatch flag support added */ +#define REVISION_MAX 4 /* Counters support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("hash:ip,port,net", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_hash:ip,port,net"); /* Type specific function prefix */ @@ -47,7 +46,7 @@ MODULE_ALIAS("ip_set_hash:ip,port,net"); #define IP_SET_HASH_WITH_PROTO #define IP_SET_HASH_WITH_NETS -/* IPv4 variant */ +/* IPv4 variants */ /* Member elements */ struct hash_ipportnet4_elem { @@ -59,6 +58,37 @@ struct hash_ipportnet4_elem { u8 proto; }; +struct hash_ipportnet4t_elem { + __be32 ip; + __be32 ip2; + __be16 port; + u8 cidr:7; + u8 nomatch:1; + u8 proto; + unsigned long timeout; +}; + +struct hash_ipportnet4c_elem { + __be32 ip; + __be32 ip2; + __be16 port; + u8 cidr:7; + u8 nomatch:1; + u8 proto; + struct ip_set_counter counter; +}; + +struct hash_ipportnet4ct_elem { + __be32 ip; + __be32 ip2; + __be16 port; + u8 cidr:7; + u8 nomatch:1; + u8 proto; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -140,9 +170,9 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ipportnet *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem e = { - .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, + .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (adt == IPSET_TEST) e.cidr = HOST_MASK - 1; @@ -165,9 +195,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipportnet *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip = 0, ip_to = 0, p = 0, port, port_to; - u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2; + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + u32 ip, ip_to, p = 0, port, port_to; + u32 ip2_from, ip2_to, ip2_last, ip2; bool with_ports = false; u8 cidr; int ret; @@ -242,7 +272,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (ip > ip_to) swap(ip, ip_to); } else if (tb[IPSET_ATTR_CIDR]) { - cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); if (!cidr || cidr > 32) return -IPSET_ERR_INVALID_CIDR; @@ -276,9 +306,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], : port; for (; p <= port_to; p++) { e.port = htons(p); - ip2 = retried && - ip == ntohl(h->next.ip) && - p == ntohs(h->next.port) + ip2 = retried + && ip == ntohl(h->next.ip) + && p == ntohs(h->next.port) ? ntohl(h->next.ip2) : ip2_from; while (!after(ip2, ip2_to)) { e.ip2 = htonl(ip2); @@ -298,7 +328,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variant */ +/* IPv6 variants */ struct hash_ipportnet6_elem { union nf_inet_addr ip; @@ -309,6 +339,37 @@ struct hash_ipportnet6_elem { u8 proto; }; +struct hash_ipportnet6t_elem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + __be16 port; + u8 cidr:7; + u8 nomatch:1; + u8 proto; + unsigned long timeout; +}; + +struct hash_ipportnet6c_elem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + __be16 port; + u8 cidr:7; + u8 nomatch:1; + u8 proto; + struct ip_set_counter counter; +}; + +struct hash_ipportnet6ct_elem { + union nf_inet_addr ip; + union nf_inet_addr ip2; + __be16 port; + u8 cidr:7; + u8 nomatch:1; + u8 proto; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -393,9 +454,9 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ipportnet *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem e = { - .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, + .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (adt == IPSET_TEST) e.cidr = HOST_MASK - 1; @@ -418,7 +479,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipportnet *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); u32 port, port_to; bool with_ports = false; u8 cidr; @@ -513,8 +574,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { IPSET_TYPE_NOMATCH, .dimension = IPSET_DIM_THREE, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = hash_ipportnet_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -539,7 +600,6 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 8295cf4..223e9f5 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -22,22 +22,21 @@ #include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set_hash.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 Range as input support for IPv4 added */ -/* 2 nomatch flag support added */ -/* 3 Counters support added */ -#define IPSET_TYPE_REV_MAX 4 /* Comments support added */ +#define REVISION_MIN 0 +/* 1 Range as input support for IPv4 added */ +/* 2 nomatch flag support added */ +#define REVISION_MAX 3 /* Counters support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("hash:net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("hash:net", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_hash:net"); /* Type specific function prefix */ #define HTYPE hash_net #define IP_SET_HASH_WITH_NETS -/* IPv4 variant */ +/* IPv4 variants */ /* Member elements */ struct hash_net4_elem { @@ -47,6 +46,31 @@ struct hash_net4_elem { u8 cidr; }; +struct hash_net4t_elem { + __be32 ip; + u16 padding0; + u8 nomatch; + u8 cidr; + unsigned long timeout; +}; + +struct hash_net4c_elem { + __be32 ip; + u16 padding0; + u8 nomatch; + u8 cidr; + struct ip_set_counter counter; +}; + +struct hash_net4ct_elem { + __be32 ip; + u16 padding0; + u8 nomatch; + u8 cidr; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -119,9 +143,9 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_net *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem e = { - .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (e.cidr == 0) return -EINVAL; @@ -141,8 +165,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_net *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem e = { .cidr = HOST_MASK }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip = 0, ip_to = 0, last; + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + u32 ip = 0, ip_to, last; int ret; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -204,7 +228,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variant */ +/* IPv6 variants */ struct hash_net6_elem { union nf_inet_addr ip; @@ -213,6 +237,31 @@ struct hash_net6_elem { u8 cidr; }; +struct hash_net6t_elem { + union nf_inet_addr ip; + u16 padding0; + u8 nomatch; + u8 cidr; + unsigned long timeout; +}; + +struct hash_net6c_elem { + union nf_inet_addr ip; + u16 padding0; + u8 nomatch; + u8 cidr; + struct ip_set_counter counter; +}; + +struct hash_net6ct_elem { + union nf_inet_addr ip; + u16 padding0; + u8 nomatch; + u8 cidr; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -289,9 +338,9 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_net *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem e = { - .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (e.cidr == 0) return -EINVAL; @@ -308,9 +357,10 @@ static int hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { + const struct hash_net *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem e = { .cidr = HOST_MASK }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); int ret; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -356,8 +406,8 @@ static struct ip_set_type hash_net_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_NOMATCH, .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = hash_net_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -375,7 +425,6 @@ static struct ip_set_type hash_net_type __read_mostly = { [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index 3f64a66..7d798d5 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -23,15 +23,14 @@ #include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set_hash.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 nomatch flag support added */ -/* 2 /0 support added */ -/* 3 Counters support added */ -#define IPSET_TYPE_REV_MAX 4 /* Comments support added */ +#define REVISION_MIN 0 +/* 1 nomatch flag support added */ +/* 2 /0 support added */ +#define REVISION_MAX 3 /* Counters support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("hash:net,iface", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("hash:net,iface", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_hash:net,iface"); /* Interface name rbtree */ @@ -135,7 +134,7 @@ iface_add(struct rb_root *root, const char **iface) #define STREQ(a, b) (strcmp(a, b) == 0) -/* IPv4 variant */ +/* IPv4 variants */ struct hash_netiface4_elem_hashed { __be32 ip; @@ -145,7 +144,7 @@ struct hash_netiface4_elem_hashed { u8 elem; }; -/* Member elements */ +/* Member elements without timeout */ struct hash_netiface4_elem { __be32 ip; u8 physdev; @@ -155,6 +154,37 @@ struct hash_netiface4_elem { const char *iface; }; +struct hash_netiface4t_elem { + __be32 ip; + u8 physdev; + u8 cidr; + u8 nomatch; + u8 elem; + const char *iface; + unsigned long timeout; +}; + +struct hash_netiface4c_elem { + __be32 ip; + u8 physdev; + u8 cidr; + u8 nomatch; + u8 elem; + const char *iface; + struct ip_set_counter counter; +}; + +struct hash_netiface4ct_elem { + __be32 ip; + u8 physdev; + u8 cidr; + u8 nomatch; + u8 elem; + const char *iface; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -235,10 +265,10 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netiface *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem e = { - .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, .elem = 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); int ret; if (e.cidr == 0) @@ -289,8 +319,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], struct hash_netiface *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip = 0, ip_to = 0, last; + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + u32 ip = 0, ip_to, last; char iface[IFNAMSIZ]; int ret; @@ -369,7 +399,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variant */ +/* IPv6 variants */ struct hash_netiface6_elem_hashed { union nf_inet_addr ip; @@ -388,6 +418,37 @@ struct hash_netiface6_elem { const char *iface; }; +struct hash_netiface6t_elem { + union nf_inet_addr ip; + u8 physdev; + u8 cidr; + u8 nomatch; + u8 elem; + const char *iface; + unsigned long timeout; +}; + +struct hash_netiface6c_elem { + union nf_inet_addr ip; + u8 physdev; + u8 cidr; + u8 nomatch; + u8 elem; + const char *iface; + struct ip_set_counter counter; +}; + +struct hash_netiface6ct_elem { + union nf_inet_addr ip; + u8 physdev; + u8 cidr; + u8 nomatch; + u8 elem; + const char *iface; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -473,10 +534,10 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netiface *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface6_elem e = { - .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, .elem = 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); int ret; if (e.cidr == 0) @@ -523,7 +584,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], struct hash_netiface *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); char iface[IFNAMSIZ]; int ret; @@ -584,8 +645,8 @@ static struct ip_set_type hash_netiface_type __read_mostly = { IPSET_TYPE_NOMATCH, .dimension = IPSET_DIM_TWO, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = hash_netiface_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -607,7 +668,6 @@ static struct ip_set_type hash_netiface_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c deleted file mode 100644 index 2bc2dec..0000000 --- a/net/netfilter/ipset/ip_set_hash_netnet.c +++ /dev/null @@ -1,481 +0,0 @@ -/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> - * Copyright (C) 2013 Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* Kernel module implementing an IP set type: the hash:net type */ - -#include <linux/jhash.h> -#include <linux/module.h> -#include <linux/ip.h> -#include <linux/skbuff.h> -#include <linux/errno.h> -#include <linux/random.h> -#include <net/ip.h> -#include <net/ipv6.h> -#include <net/netlink.h> - -#include <linux/netfilter.h> -#include <linux/netfilter/ipset/pfxlen.h> -#include <linux/netfilter/ipset/ip_set.h> -#include <linux/netfilter/ipset/ip_set_hash.h> - -#define IPSET_TYPE_REV_MIN 0 -#define IPSET_TYPE_REV_MAX 0 - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>"); -IP_SET_MODULE_DESC("hash:net,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); -MODULE_ALIAS("ip_set_hash:net,net"); - -/* Type specific function prefix */ -#define HTYPE hash_netnet -#define IP_SET_HASH_WITH_NETS -#define IPSET_NET_COUNT 2 - -/* IPv4 variants */ - -/* Member elements */ -struct hash_netnet4_elem { - union { - __be32 ip[2]; - __be64 ipcmp; - }; - u8 nomatch; - union { - u8 cidr[2]; - u16 ccmp; - }; -}; - -/* Common functions */ - -static inline bool -hash_netnet4_data_equal(const struct hash_netnet4_elem *ip1, - const struct hash_netnet4_elem *ip2, - u32 *multi) -{ - return ip1->ipcmp == ip2->ipcmp && - ip2->ccmp == ip2->ccmp; -} - -static inline int -hash_netnet4_do_data_match(const struct hash_netnet4_elem *elem) -{ - return elem->nomatch ? -ENOTEMPTY : 1; -} - -static inline void -hash_netnet4_data_set_flags(struct hash_netnet4_elem *elem, u32 flags) -{ - elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; -} - -static inline void -hash_netnet4_data_reset_flags(struct hash_netnet4_elem *elem, u8 *flags) -{ - swap(*flags, elem->nomatch); -} - -static inline void -hash_netnet4_data_reset_elem(struct hash_netnet4_elem *elem, - struct hash_netnet4_elem *orig) -{ - elem->ip[1] = orig->ip[1]; -} - -static inline void -hash_netnet4_data_netmask(struct hash_netnet4_elem *elem, u8 cidr, bool inner) -{ - if (inner) { - elem->ip[1] &= ip_set_netmask(cidr); - elem->cidr[1] = cidr; - } else { - elem->ip[0] &= ip_set_netmask(cidr); - elem->cidr[0] = cidr; - } -} - -static bool -hash_netnet4_data_list(struct sk_buff *skb, - const struct hash_netnet4_elem *data) -{ - u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - - if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) || - nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) || - nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || - nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || - (flags && - nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return 1; -} - -static inline void -hash_netnet4_data_next(struct hash_netnet4_elem *next, - const struct hash_netnet4_elem *d) -{ - next->ipcmp = d->ipcmp; -} - -#define MTYPE hash_netnet4 -#define PF 4 -#define HOST_MASK 32 -#include "ip_set_hash_gen.h" - -static int -hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, - const struct xt_action_param *par, - enum ipset_adt adt, struct ip_set_adt_opt *opt) -{ - const struct hash_netnet *h = set->data; - ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netnet4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - - e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); - if (adt == IPSET_TEST) - e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; - - ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]); - ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]); - e.ip[0] &= ip_set_netmask(e.cidr[0]); - e.ip[1] &= ip_set_netmask(e.cidr[1]); - - return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); -} - -static int -hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) -{ - const struct hash_netnet *h = set->data; - ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netnet4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip = 0, ip_to = 0, last; - u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2; - u8 cidr, cidr2; - int ret; - - e.cidr[0] = e.cidr[1] = HOST_MASK; - if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) - return -IPSET_ERR_PROTOCOL; - - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - - ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) || - ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) || - ip_set_get_extensions(set, tb, &ext); - if (ret) - return ret; - - if (tb[IPSET_ATTR_CIDR]) { - cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!cidr || cidr > HOST_MASK) - return -IPSET_ERR_INVALID_CIDR; - e.cidr[0] = cidr; - } - - if (tb[IPSET_ATTR_CIDR2]) { - cidr2 = nla_get_u8(tb[IPSET_ATTR_CIDR2]); - if (!cidr2 || cidr2 > HOST_MASK) - return -IPSET_ERR_INVALID_CIDR; - e.cidr[1] = cidr2; - } - - if (tb[IPSET_ATTR_CADT_FLAGS]) { - u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_NOMATCH) - flags |= (IPSET_FLAG_NOMATCH << 16); - } - - if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] && - tb[IPSET_ATTR_IP2_TO])) { - e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0])); - e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1])); - ret = adtfn(set, &e, &ext, &ext, flags); - return ip_set_enomatch(ret, flags, adt, set) ? -ret : - ip_set_eexist(ret, flags) ? 0 : ret; - } - - ip_to = ip; - if (tb[IPSET_ATTR_IP_TO]) { - ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); - if (ret) - return ret; - if (ip_to < ip) - swap(ip, ip_to); - if (ip + UINT_MAX == ip_to) - return -IPSET_ERR_HASH_RANGE; - } - - ip2_to = ip2_from; - if (tb[IPSET_ATTR_IP2_TO]) { - ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); - if (ret) - return ret; - if (ip2_to < ip2_from) - swap(ip2_from, ip2_to); - if (ip2_from + UINT_MAX == ip2_to) - return -IPSET_ERR_HASH_RANGE; - - } - - if (retried) - ip = ntohl(h->next.ip[0]); - - while (!after(ip, ip_to)) { - e.ip[0] = htonl(ip); - last = ip_set_range_to_cidr(ip, ip_to, &cidr); - e.cidr[0] = cidr; - ip2 = (retried && - ip == ntohl(h->next.ip[0])) ? ntohl(h->next.ip[1]) - : ip2_from; - while (!after(ip2, ip2_to)) { - e.ip[1] = htonl(ip2); - last2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr2); - e.cidr[1] = cidr2; - ret = adtfn(set, &e, &ext, &ext, flags); - if (ret && !ip_set_eexist(ret, flags)) - return ret; - else - ret = 0; - ip2 = last2 + 1; - } - ip = last + 1; - } - return ret; -} - -/* IPv6 variants */ - -struct hash_netnet6_elem { - union nf_inet_addr ip[2]; - u8 nomatch; - union { - u8 cidr[2]; - u16 ccmp; - }; -}; - -/* Common functions */ - -static inline bool -hash_netnet6_data_equal(const struct hash_netnet6_elem *ip1, - const struct hash_netnet6_elem *ip2, - u32 *multi) -{ - return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) && - ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) && - ip1->ccmp == ip2->ccmp; -} - -static inline int -hash_netnet6_do_data_match(const struct hash_netnet6_elem *elem) -{ - return elem->nomatch ? -ENOTEMPTY : 1; -} - -static inline void -hash_netnet6_data_set_flags(struct hash_netnet6_elem *elem, u32 flags) -{ - elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; -} - -static inline void -hash_netnet6_data_reset_flags(struct hash_netnet6_elem *elem, u8 *flags) -{ - swap(*flags, elem->nomatch); -} - -static inline void -hash_netnet6_data_reset_elem(struct hash_netnet6_elem *elem, - struct hash_netnet6_elem *orig) -{ - elem->ip[1] = orig->ip[1]; -} - -static inline void -hash_netnet6_data_netmask(struct hash_netnet6_elem *elem, u8 cidr, bool inner) -{ - if (inner) { - ip6_netmask(&elem->ip[1], cidr); - elem->cidr[1] = cidr; - } else { - ip6_netmask(&elem->ip[0], cidr); - elem->cidr[0] = cidr; - } -} - -static bool -hash_netnet6_data_list(struct sk_buff *skb, - const struct hash_netnet6_elem *data) -{ - u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - - if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) || - nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) || - nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || - nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || - (flags && - nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return 1; -} - -static inline void -hash_netnet6_data_next(struct hash_netnet4_elem *next, - const struct hash_netnet6_elem *d) -{ -} - -#undef MTYPE -#undef PF -#undef HOST_MASK - -#define MTYPE hash_netnet6 -#define PF 6 -#define HOST_MASK 128 -#define IP_SET_EMIT_CREATE -#include "ip_set_hash_gen.h" - -static int -hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, - const struct xt_action_param *par, - enum ipset_adt adt, struct ip_set_adt_opt *opt) -{ - const struct hash_netnet *h = set->data; - ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netnet6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - - e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); - if (adt == IPSET_TEST) - e.ccmp = (HOST_MASK << (sizeof(u8)*8)) | HOST_MASK; - - ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6); - ip6addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1].in6); - ip6_netmask(&e.ip[0], e.cidr[0]); - ip6_netmask(&e.ip[1], e.cidr[1]); - - return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); -} - -static int -hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) -{ - ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netnet6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - int ret; - - e.cidr[0] = e.cidr[1] = HOST_MASK; - if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) - return -IPSET_ERR_PROTOCOL; - if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO])) - return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; - - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - - ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) || - ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) || - ip_set_get_extensions(set, tb, &ext); - if (ret) - return ret; - - if (tb[IPSET_ATTR_CIDR]) - e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]); - - if (tb[IPSET_ATTR_CIDR2]) - e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]); - - if (!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] || - e.cidr[1] > HOST_MASK) - return -IPSET_ERR_INVALID_CIDR; - - ip6_netmask(&e.ip[0], e.cidr[0]); - ip6_netmask(&e.ip[1], e.cidr[1]); - - if (tb[IPSET_ATTR_CADT_FLAGS]) { - u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_NOMATCH) - flags |= (IPSET_FLAG_NOMATCH << 16); - } - - ret = adtfn(set, &e, &ext, &ext, flags); - - return ip_set_enomatch(ret, flags, adt, set) ? -ret : - ip_set_eexist(ret, flags) ? 0 : ret; -} - -static struct ip_set_type hash_netnet_type __read_mostly = { - .name = "hash:net,net", - .protocol = IPSET_PROTOCOL, - .features = IPSET_TYPE_IP | IPSET_TYPE_IP2 | IPSET_TYPE_NOMATCH, - .dimension = IPSET_DIM_TWO, - .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, - .create = hash_netnet_create, - .create_policy = { - [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, - [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, - [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, - [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, - [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, - [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, - }, - .adt_policy = { - [IPSET_ATTR_IP] = { .type = NLA_NESTED }, - [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, - [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, - [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, - [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, - [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, - [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, - [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, - [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, - [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, - }, - .me = THIS_MODULE, -}; - -static int __init -hash_netnet_init(void) -{ - return ip_set_type_register(&hash_netnet_type); -} - -static void __exit -hash_netnet_fini(void) -{ - ip_set_type_unregister(&hash_netnet_type); -} - -module_init(hash_netnet_init); -module_exit(hash_netnet_fini); diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 7097fb0..09d6690 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -23,16 +23,15 @@ #include <linux/netfilter/ipset/ip_set_getport.h> #include <linux/netfilter/ipset/ip_set_hash.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 SCTP and UDPLITE support added */ -/* 2 Range as input support for IPv4 added */ -/* 3 nomatch flag support added */ -/* 4 Counters support added */ -#define IPSET_TYPE_REV_MAX 5 /* Comments support added */ +#define REVISION_MIN 0 +/* 1 SCTP and UDPLITE support added */ +/* 2 Range as input support for IPv4 added */ +/* 3 nomatch flag support added */ +#define REVISION_MAX 4 /* Counters support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("hash:net,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("hash:net,port", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_hash:net,port"); /* Type specific function prefix */ @@ -46,7 +45,7 @@ MODULE_ALIAS("ip_set_hash:net,port"); */ #define IP_SET_HASH_WITH_NETS_PACKED -/* IPv4 variant */ +/* IPv4 variants */ /* Member elements */ struct hash_netport4_elem { @@ -57,6 +56,34 @@ struct hash_netport4_elem { u8 nomatch:1; }; +struct hash_netport4t_elem { + __be32 ip; + __be16 port; + u8 proto; + u8 cidr:7; + u8 nomatch:1; + unsigned long timeout; +}; + +struct hash_netport4c_elem { + __be32 ip; + __be16 port; + u8 proto; + u8 cidr:7; + u8 nomatch:1; + struct ip_set_counter counter; +}; + +struct hash_netport4ct_elem { + __be32 ip; + __be16 port; + u8 proto; + u8 cidr:7; + u8 nomatch:1; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -135,9 +162,9 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_netport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem e = { - .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, + .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (adt == IPSET_TEST) e.cidr = HOST_MASK - 1; @@ -159,8 +186,8 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_netport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 port, port_to, p = 0, ip = 0, ip_to = 0, last; + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + u32 port, port_to, p = 0, ip = 0, ip_to, last; bool with_ports = false; u8 cidr; int ret; @@ -260,7 +287,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variant */ +/* IPv6 variants */ struct hash_netport6_elem { union nf_inet_addr ip; @@ -270,6 +297,34 @@ struct hash_netport6_elem { u8 nomatch:1; }; +struct hash_netport6t_elem { + union nf_inet_addr ip; + __be16 port; + u8 proto; + u8 cidr:7; + u8 nomatch:1; + unsigned long timeout; +}; + +struct hash_netport6c_elem { + union nf_inet_addr ip; + __be16 port; + u8 proto; + u8 cidr:7; + u8 nomatch:1; + struct ip_set_counter counter; +}; + +struct hash_netport6ct_elem { + union nf_inet_addr ip; + __be16 port; + u8 proto; + u8 cidr:7; + u8 nomatch:1; + struct ip_set_counter counter; + unsigned long timeout; +}; + /* Common functions */ static inline bool @@ -352,9 +407,9 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_netport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem e = { - .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, + .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); if (adt == IPSET_TEST) e.cidr = HOST_MASK - 1; @@ -376,7 +431,7 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_netport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem e = { .cidr = HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(h); u32 port, port_to; bool with_ports = false; u8 cidr; @@ -463,8 +518,8 @@ static struct ip_set_type hash_netport_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_NOMATCH, .dimension = IPSET_DIM_TWO, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = hash_netport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -487,7 +542,6 @@ static struct ip_set_type hash_netport_type __read_mostly = { [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c deleted file mode 100644 index 703d119..0000000 --- a/net/netfilter/ipset/ip_set_hash_netportnet.c +++ /dev/null @@ -1,586 +0,0 @@ -/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* Kernel module implementing an IP set type: the hash:ip,port,net type */ - -#include <linux/jhash.h> -#include <linux/module.h> -#include <linux/ip.h> -#include <linux/skbuff.h> -#include <linux/errno.h> -#include <linux/random.h> -#include <net/ip.h> -#include <net/ipv6.h> -#include <net/netlink.h> -#include <net/tcp.h> - -#include <linux/netfilter.h> -#include <linux/netfilter/ipset/pfxlen.h> -#include <linux/netfilter/ipset/ip_set.h> -#include <linux/netfilter/ipset/ip_set_getport.h> -#include <linux/netfilter/ipset/ip_set_hash.h> - -#define IPSET_TYPE_REV_MIN 0 -#define IPSET_TYPE_REV_MAX 0 /* Comments support added */ - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>"); -IP_SET_MODULE_DESC("hash:net,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); -MODULE_ALIAS("ip_set_hash:net,port,net"); - -/* Type specific function prefix */ -#define HTYPE hash_netportnet -#define IP_SET_HASH_WITH_PROTO -#define IP_SET_HASH_WITH_NETS -#define IPSET_NET_COUNT 2 - -/* IPv4 variant */ - -/* Member elements */ -struct hash_netportnet4_elem { - union { - __be32 ip[2]; - __be64 ipcmp; - }; - __be16 port; - union { - u8 cidr[2]; - u16 ccmp; - }; - u8 nomatch:1; - u8 proto; -}; - -/* Common functions */ - -static inline bool -hash_netportnet4_data_equal(const struct hash_netportnet4_elem *ip1, - const struct hash_netportnet4_elem *ip2, - u32 *multi) -{ - return ip1->ipcmp == ip2->ipcmp && - ip1->ccmp == ip2->ccmp && - ip1->port == ip2->port && - ip1->proto == ip2->proto; -} - -static inline int -hash_netportnet4_do_data_match(const struct hash_netportnet4_elem *elem) -{ - return elem->nomatch ? -ENOTEMPTY : 1; -} - -static inline void -hash_netportnet4_data_set_flags(struct hash_netportnet4_elem *elem, u32 flags) -{ - elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); -} - -static inline void -hash_netportnet4_data_reset_flags(struct hash_netportnet4_elem *elem, u8 *flags) -{ - swap(*flags, elem->nomatch); -} - -static inline void -hash_netportnet4_data_reset_elem(struct hash_netportnet4_elem *elem, - struct hash_netportnet4_elem *orig) -{ - elem->ip[1] = orig->ip[1]; -} - -static inline void -hash_netportnet4_data_netmask(struct hash_netportnet4_elem *elem, - u8 cidr, bool inner) -{ - if (inner) { - elem->ip[1] &= ip_set_netmask(cidr); - elem->cidr[1] = cidr; - } else { - elem->ip[0] &= ip_set_netmask(cidr); - elem->cidr[0] = cidr; - } -} - -static bool -hash_netportnet4_data_list(struct sk_buff *skb, - const struct hash_netportnet4_elem *data) -{ - u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - - if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) || - nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) || - nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || - nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || - nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || - nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || - (flags && - nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return 1; -} - -static inline void -hash_netportnet4_data_next(struct hash_netportnet4_elem *next, - const struct hash_netportnet4_elem *d) -{ - next->ipcmp = d->ipcmp; - next->port = d->port; -} - -#define MTYPE hash_netportnet4 -#define PF 4 -#define HOST_MASK 32 -#include "ip_set_hash_gen.h" - -static int -hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, - const struct xt_action_param *par, - enum ipset_adt adt, struct ip_set_adt_opt *opt) -{ - const struct hash_netportnet *h = set->data; - ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netportnet4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - - e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); - if (adt == IPSET_TEST) - e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; - - if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, - &e.port, &e.proto)) - return -EINVAL; - - ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]); - ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1]); - e.ip[0] &= ip_set_netmask(e.cidr[0]); - e.ip[1] &= ip_set_netmask(e.cidr[1]); - - return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); -} - -static int -hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) -{ - const struct hash_netportnet *h = set->data; - ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netportnet4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to; - u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2; - bool with_ports = false; - u8 cidr, cidr2; - int ret; - - e.cidr[0] = e.cidr[1] = HOST_MASK; - if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || - !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) - return -IPSET_ERR_PROTOCOL; - - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - - ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) || - ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) || - ip_set_get_extensions(set, tb, &ext); - if (ret) - return ret; - - if (tb[IPSET_ATTR_CIDR]) { - cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); - if (!cidr || cidr > HOST_MASK) - return -IPSET_ERR_INVALID_CIDR; - e.cidr[0] = cidr; - } - - if (tb[IPSET_ATTR_CIDR2]) { - cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); - if (!cidr || cidr > HOST_MASK) - return -IPSET_ERR_INVALID_CIDR; - e.cidr[1] = cidr; - } - - if (tb[IPSET_ATTR_PORT]) - e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); - else - return -IPSET_ERR_PROTOCOL; - - if (tb[IPSET_ATTR_PROTO]) { - e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - with_ports = ip_set_proto_with_ports(e.proto); - - if (e.proto == 0) - return -IPSET_ERR_INVALID_PROTO; - } else - return -IPSET_ERR_MISSING_PROTO; - - if (!(with_ports || e.proto == IPPROTO_ICMP)) - e.port = 0; - - if (tb[IPSET_ATTR_CADT_FLAGS]) { - u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_NOMATCH) - flags |= (IPSET_FLAG_NOMATCH << 16); - } - - with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; - if (adt == IPSET_TEST || - !(tb[IPSET_ATTR_IP_TO] || with_ports || tb[IPSET_ATTR_IP2_TO])) { - e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0])); - e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1])); - ret = adtfn(set, &e, &ext, &ext, flags); - return ip_set_enomatch(ret, flags, adt, set) ? -ret : - ip_set_eexist(ret, flags) ? 0 : ret; - } - - ip_to = ip; - if (tb[IPSET_ATTR_IP_TO]) { - ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); - if (ret) - return ret; - if (ip > ip_to) - swap(ip, ip_to); - if (unlikely(ip + UINT_MAX == ip_to)) - return -IPSET_ERR_HASH_RANGE; - } - - port_to = port = ntohs(e.port); - if (tb[IPSET_ATTR_PORT_TO]) { - port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); - if (port > port_to) - swap(port, port_to); - } - - ip2_to = ip2_from; - if (tb[IPSET_ATTR_IP2_TO]) { - ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); - if (ret) - return ret; - if (ip2_from > ip2_to) - swap(ip2_from, ip2_to); - if (unlikely(ip2_from + UINT_MAX == ip2_to)) - return -IPSET_ERR_HASH_RANGE; - } - - if (retried) - ip = ntohl(h->next.ip[0]); - - while (!after(ip, ip_to)) { - e.ip[0] = htonl(ip); - ip_last = ip_set_range_to_cidr(ip, ip_to, &cidr); - e.cidr[0] = cidr; - p = retried && ip == ntohl(h->next.ip[0]) ? ntohs(h->next.port) - : port; - for (; p <= port_to; p++) { - e.port = htons(p); - ip2 = (retried && ip == ntohl(h->next.ip[0]) && - p == ntohs(h->next.port)) ? ntohl(h->next.ip[1]) - : ip2_from; - while (!after(ip2, ip2_to)) { - e.ip[1] = htonl(ip2); - ip2_last = ip_set_range_to_cidr(ip2, ip2_to, - &cidr2); - e.cidr[1] = cidr2; - ret = adtfn(set, &e, &ext, &ext, flags); - if (ret && !ip_set_eexist(ret, flags)) - return ret; - else - ret = 0; - ip2 = ip2_last + 1; - } - } - ip = ip_last + 1; - } - return ret; -} - -/* IPv6 variant */ - -struct hash_netportnet6_elem { - union nf_inet_addr ip[2]; - __be16 port; - union { - u8 cidr[2]; - u16 ccmp; - }; - u8 nomatch:1; - u8 proto; -}; - -/* Common functions */ - -static inline bool -hash_netportnet6_data_equal(const struct hash_netportnet6_elem *ip1, - const struct hash_netportnet6_elem *ip2, - u32 *multi) -{ - return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) && - ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) && - ip1->ccmp == ip2->ccmp && - ip1->port == ip2->port && - ip1->proto == ip2->proto; -} - -static inline int -hash_netportnet6_do_data_match(const struct hash_netportnet6_elem *elem) -{ - return elem->nomatch ? -ENOTEMPTY : 1; -} - -static inline void -hash_netportnet6_data_set_flags(struct hash_netportnet6_elem *elem, u32 flags) -{ - elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); -} - -static inline void -hash_netportnet6_data_reset_flags(struct hash_netportnet6_elem *elem, u8 *flags) -{ - swap(*flags, elem->nomatch); -} - -static inline void -hash_netportnet6_data_reset_elem(struct hash_netportnet6_elem *elem, - struct hash_netportnet6_elem *orig) -{ - elem->ip[1] = orig->ip[1]; -} - -static inline void -hash_netportnet6_data_netmask(struct hash_netportnet6_elem *elem, - u8 cidr, bool inner) -{ - if (inner) { - ip6_netmask(&elem->ip[1], cidr); - elem->cidr[1] = cidr; - } else { - ip6_netmask(&elem->ip[0], cidr); - elem->cidr[0] = cidr; - } -} - -static bool -hash_netportnet6_data_list(struct sk_buff *skb, - const struct hash_netportnet6_elem *data) -{ - u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - - if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) || - nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) || - nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || - nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || - nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || - nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || - (flags && - nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return 1; -} - -static inline void -hash_netportnet6_data_next(struct hash_netportnet4_elem *next, - const struct hash_netportnet6_elem *d) -{ - next->port = d->port; -} - -#undef MTYPE -#undef PF -#undef HOST_MASK - -#define MTYPE hash_netportnet6 -#define PF 6 -#define HOST_MASK 128 -#define IP_SET_EMIT_CREATE -#include "ip_set_hash_gen.h" - -static int -hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, - const struct xt_action_param *par, - enum ipset_adt adt, struct ip_set_adt_opt *opt) -{ - const struct hash_netportnet *h = set->data; - ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netportnet6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - - e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); - if (adt == IPSET_TEST) - e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK; - - if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, - &e.port, &e.proto)) - return -EINVAL; - - ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6); - ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1].in6); - ip6_netmask(&e.ip[0], e.cidr[0]); - ip6_netmask(&e.ip[1], e.cidr[1]); - - return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); -} - -static int -hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) -{ - const struct hash_netportnet *h = set->data; - ipset_adtfn adtfn = set->variant->adt[adt]; - struct hash_netportnet6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); - u32 port, port_to; - bool with_ports = false; - int ret; - - e.cidr[0] = e.cidr[1] = HOST_MASK; - if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || - !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || - !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) - return -IPSET_ERR_PROTOCOL; - if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO])) - return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; - - if (tb[IPSET_ATTR_LINENO]) - *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); - - ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) || - ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) || - ip_set_get_extensions(set, tb, &ext); - if (ret) - return ret; - - if (tb[IPSET_ATTR_CIDR]) - e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]); - - if (tb[IPSET_ATTR_CIDR2]) - e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]); - - if (unlikely(!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] || - e.cidr[1] > HOST_MASK)) - return -IPSET_ERR_INVALID_CIDR; - - ip6_netmask(&e.ip[0], e.cidr[0]); - ip6_netmask(&e.ip[1], e.cidr[1]); - - if (tb[IPSET_ATTR_PORT]) - e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); - else - return -IPSET_ERR_PROTOCOL; - - if (tb[IPSET_ATTR_PROTO]) { - e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); - with_ports = ip_set_proto_with_ports(e.proto); - - if (e.proto == 0) - return -IPSET_ERR_INVALID_PROTO; - } else - return -IPSET_ERR_MISSING_PROTO; - - if (!(with_ports || e.proto == IPPROTO_ICMPV6)) - e.port = 0; - - if (tb[IPSET_ATTR_CADT_FLAGS]) { - u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_NOMATCH) - flags |= (IPSET_FLAG_NOMATCH << 16); - } - - if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { - ret = adtfn(set, &e, &ext, &ext, flags); - return ip_set_enomatch(ret, flags, adt, set) ? -ret : - ip_set_eexist(ret, flags) ? 0 : ret; - } - - port = ntohs(e.port); - port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); - if (port > port_to) - swap(port, port_to); - - if (retried) - port = ntohs(h->next.port); - for (; port <= port_to; port++) { - e.port = htons(port); - ret = adtfn(set, &e, &ext, &ext, flags); - - if (ret && !ip_set_eexist(ret, flags)) - return ret; - else - ret = 0; - } - return ret; -} - -static struct ip_set_type hash_netportnet_type __read_mostly = { - .name = "hash:net,port,net", - .protocol = IPSET_PROTOCOL, - .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 | - IPSET_TYPE_NOMATCH, - .dimension = IPSET_DIM_THREE, - .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, - .create = hash_netportnet_create, - .create_policy = { - [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, - [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, - [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, - [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, - [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, - [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, - }, - .adt_policy = { - [IPSET_ATTR_IP] = { .type = NLA_NESTED }, - [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, - [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, - [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, - [IPSET_ATTR_PORT] = { .type = NLA_U16 }, - [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, - [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, - [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, - [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, - [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, - [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, - [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, - [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, - [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, - }, - .me = THIS_MODULE, -}; - -static int __init -hash_netportnet_init(void) -{ - return ip_set_type_register(&hash_netportnet_type); -} - -static void __exit -hash_netportnet_fini(void) -{ - ip_set_type_unregister(&hash_netportnet_type); -} - -module_init(hash_netportnet_init); -module_exit(hash_netportnet_fini); diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 3e2317f..979b8c9 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -15,13 +15,12 @@ #include <linux/netfilter/ipset/ip_set.h> #include <linux/netfilter/ipset/ip_set_list.h> -#define IPSET_TYPE_REV_MIN 0 -/* 1 Counters support added */ -#define IPSET_TYPE_REV_MAX 2 /* Comments support added */ +#define REVISION_MIN 0 +#define REVISION_MAX 1 /* Counters support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); -IP_SET_MODULE_DESC("list:set", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX); MODULE_ALIAS("ip_set_list:set"); /* Member elements */ @@ -29,6 +28,28 @@ struct set_elem { ip_set_id_t id; }; +struct sett_elem { + struct { + ip_set_id_t id; + } __attribute__ ((aligned)); + unsigned long timeout; +}; + +struct setc_elem { + struct { + ip_set_id_t id; + } __attribute__ ((aligned)); + struct ip_set_counter counter; +}; + +struct setct_elem { + struct { + ip_set_id_t id; + } __attribute__ ((aligned)); + struct ip_set_counter counter; + unsigned long timeout; +}; + struct set_adt_elem { ip_set_id_t id; ip_set_id_t refid; @@ -37,14 +58,24 @@ struct set_adt_elem { /* Type structure */ struct list_set { + size_t dsize; /* element size */ + size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ u32 size; /* size of set list array */ + u32 timeout; /* timeout value */ struct timer_list gc; /* garbage collection */ - struct net *net; /* namespace */ struct set_elem members[0]; /* the set members */ }; -#define list_set_elem(set, map, id) \ - (struct set_elem *)((void *)(map)->members + (id) * (set)->dsize) +static inline struct set_elem * +list_set_elem(const struct list_set *map, u32 id) +{ + return (struct set_elem *)((void *)map->members + id * map->dsize); +} + +#define ext_timeout(e, m) \ +(unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT]) +#define ext_counter(e, m) \ +(struct ip_set_counter *)((void *)(e) + (m)->offset[IPSET_OFFSET_COUNTER]) static int list_set_ktest(struct ip_set *set, const struct sk_buff *skb, @@ -61,16 +92,16 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb, if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE) opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE; for (i = 0; i < map->size; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); if (e->id == IPSET_INVALID_ID) return 0; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, set))) + ip_set_timeout_expired(ext_timeout(e, map))) continue; ret = ip_set_test(e->id, skb, par, opt); if (ret > 0) { if (SET_WITH_COUNTER(set)) - ip_set_update_counter(ext_counter(e, set), + ip_set_update_counter(ext_counter(e, map), ext, &opt->ext, cmdflags); return ret; @@ -90,11 +121,11 @@ list_set_kadd(struct ip_set *set, const struct sk_buff *skb, int ret; for (i = 0; i < map->size; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); if (e->id == IPSET_INVALID_ID) return 0; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, set))) + ip_set_timeout_expired(ext_timeout(e, map))) continue; ret = ip_set_add(e->id, skb, par, opt); if (ret == 0) @@ -114,11 +145,11 @@ list_set_kdel(struct ip_set *set, const struct sk_buff *skb, int ret; for (i = 0; i < map->size; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); if (e->id == IPSET_INVALID_ID) return 0; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, set))) + ip_set_timeout_expired(ext_timeout(e, map))) continue; ret = ip_set_del(e->id, skb, par, opt); if (ret == 0) @@ -132,7 +163,8 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + struct list_set *map = set->data; + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); switch (adt) { case IPSET_TEST: @@ -156,10 +188,10 @@ id_eq(const struct ip_set *set, u32 i, ip_set_id_t id) if (i >= map->size) return 0; - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); return !!(e->id == id && !(SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, set)))); + ip_set_timeout_expired(ext_timeout(e, map)))); } static int @@ -167,36 +199,28 @@ list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d, const struct ip_set_ext *ext) { struct list_set *map = set->data; - struct set_elem *e = list_set_elem(set, map, i); + struct set_elem *e = list_set_elem(map, i); if (e->id != IPSET_INVALID_ID) { - if (i == map->size - 1) { + if (i == map->size - 1) /* Last element replaced: e.g. add new,before,last */ - ip_set_put_byindex(map->net, e->id); - ip_set_ext_destroy(set, e); - } else { - struct set_elem *x = list_set_elem(set, map, - map->size - 1); + ip_set_put_byindex(e->id); + else { + struct set_elem *x = list_set_elem(map, map->size - 1); /* Last element pushed off */ - if (x->id != IPSET_INVALID_ID) { - ip_set_put_byindex(map->net, x->id); - ip_set_ext_destroy(set, x); - } - memmove(list_set_elem(set, map, i + 1), e, - set->dsize * (map->size - (i + 1))); - /* Extensions must be initialized to zero */ - memset(e, 0, set->dsize); + if (x->id != IPSET_INVALID_ID) + ip_set_put_byindex(x->id); + memmove(list_set_elem(map, i + 1), e, + map->dsize * (map->size - (i + 1))); } } e->id = d->id; if (SET_WITH_TIMEOUT(set)) - ip_set_timeout_set(ext_timeout(e, set), ext->timeout); + ip_set_timeout_set(ext_timeout(e, map), ext->timeout); if (SET_WITH_COUNTER(set)) - ip_set_init_counter(ext_counter(e, set), ext); - if (SET_WITH_COMMENT(set)) - ip_set_init_comment(ext_comment(e, set), ext); + ip_set_init_counter(ext_counter(e, map), ext); return 0; } @@ -204,17 +228,16 @@ static int list_set_del(struct ip_set *set, u32 i) { struct list_set *map = set->data; - struct set_elem *e = list_set_elem(set, map, i); + struct set_elem *e = list_set_elem(map, i); - ip_set_put_byindex(map->net, e->id); - ip_set_ext_destroy(set, e); + ip_set_put_byindex(e->id); if (i < map->size - 1) - memmove(e, list_set_elem(set, map, i + 1), - set->dsize * (map->size - (i + 1))); + memmove(e, list_set_elem(map, i + 1), + map->dsize * (map->size - (i + 1))); /* Last element */ - e = list_set_elem(set, map, map->size - 1); + e = list_set_elem(map, map->size - 1); e->id = IPSET_INVALID_ID; return 0; } @@ -224,16 +247,13 @@ set_cleanup_entries(struct ip_set *set) { struct list_set *map = set->data; struct set_elem *e; - u32 i = 0; + u32 i; - while (i < map->size) { - e = list_set_elem(set, map, i); + for (i = 0; i < map->size; i++) { + e = list_set_elem(map, i); if (e->id != IPSET_INVALID_ID && - ip_set_timeout_expired(ext_timeout(e, set))) + ip_set_timeout_expired(ext_timeout(e, map))) list_set_del(set, i); - /* Check element moved to position i in next loop */ - else - i++; } } @@ -248,11 +268,11 @@ list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext, int ret; for (i = 0; i < map->size; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); if (e->id == IPSET_INVALID_ID) return 0; else if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, set))) + ip_set_timeout_expired(ext_timeout(e, map))) continue; else if (e->id != d->id) continue; @@ -279,14 +299,14 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, bool flag_exist = flags & IPSET_FLAG_EXIST; u32 i, ret = 0; - if (SET_WITH_TIMEOUT(set)) - set_cleanup_entries(set); - /* Check already added element */ for (i = 0; i < map->size; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); if (e->id == IPSET_INVALID_ID) goto insert; + else if (SET_WITH_TIMEOUT(set) && + ip_set_timeout_expired(ext_timeout(e, map))) + continue; else if (e->id != d->id) continue; @@ -299,22 +319,18 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, /* Can't re-add */ return -IPSET_ERR_EXIST; /* Update extensions */ - ip_set_ext_destroy(set, e); - if (SET_WITH_TIMEOUT(set)) - ip_set_timeout_set(ext_timeout(e, set), ext->timeout); + ip_set_timeout_set(ext_timeout(e, map), ext->timeout); if (SET_WITH_COUNTER(set)) - ip_set_init_counter(ext_counter(e, set), ext); - if (SET_WITH_COMMENT(set)) - ip_set_init_comment(ext_comment(e, set), ext); + ip_set_init_counter(ext_counter(e, map), ext); /* Set is already added to the list */ - ip_set_put_byindex(map->net, d->id); + ip_set_put_byindex(d->id); return 0; } insert: ret = -IPSET_ERR_LIST_FULL; for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); if (e->id == IPSET_INVALID_ID) ret = d->before != 0 ? -IPSET_ERR_REF_EXIST : list_set_add(set, i, d, ext); @@ -339,12 +355,12 @@ list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext, u32 i; for (i = 0; i < map->size; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); if (e->id == IPSET_INVALID_ID) return d->before != 0 ? -IPSET_ERR_REF_EXIST : -IPSET_ERR_EXIST; else if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, set))) + ip_set_timeout_expired(ext_timeout(e, map))) continue; else if (e->id != d->id) continue; @@ -370,7 +386,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], struct list_set *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct set_adt_elem e = { .refid = IPSET_INVALID_ID }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + struct ip_set_ext ext = IP_SET_INIT_UEXT(map); struct ip_set *s; int ret = 0; @@ -387,7 +403,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], ret = ip_set_get_extensions(set, tb, &ext); if (ret) return ret; - e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s); + e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); if (e.id == IPSET_INVALID_ID) return -IPSET_ERR_NAME; /* "Loop detection" */ @@ -407,8 +423,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], } if (tb[IPSET_ATTR_NAMEREF]) { - e.refid = ip_set_get_byname(map->net, - nla_data(tb[IPSET_ATTR_NAMEREF]), + e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), &s); if (e.refid == IPSET_INVALID_ID) { ret = -IPSET_ERR_NAMEREF; @@ -424,9 +439,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], finish: if (e.refid != IPSET_INVALID_ID) - ip_set_put_byindex(map->net, e.refid); + ip_set_put_byindex(e.refid); if (adt != IPSET_ADD || ret) - ip_set_put_byindex(map->net, e.id); + ip_set_put_byindex(e.id); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -439,10 +454,9 @@ list_set_flush(struct ip_set *set) u32 i; for (i = 0; i < map->size; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); if (e->id != IPSET_INVALID_ID) { - ip_set_put_byindex(map->net, e->id); - ip_set_ext_destroy(set, e); + ip_set_put_byindex(e->id); e->id = IPSET_INVALID_ID; } } @@ -471,11 +485,14 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) if (!nested) goto nla_put_failure; if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) || + (SET_WITH_TIMEOUT(set) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) || + (SET_WITH_COUNTER(set) && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, + htonl(IPSET_FLAG_WITH_COUNTERS))) || nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || nla_put_net32(skb, IPSET_ATTR_MEMSIZE, - htonl(sizeof(*map) + map->size * set->dsize))) - goto nla_put_failure; - if (unlikely(ip_set_put_flags(skb, set))) + htonl(sizeof(*map) + map->size * map->dsize))) goto nla_put_failure; ipset_nest_end(skb, nested); @@ -490,20 +507,19 @@ list_set_list(const struct ip_set *set, { const struct list_set *map = set->data; struct nlattr *atd, *nested; - u32 i, first = cb->args[IPSET_CB_ARG0]; + u32 i, first = cb->args[2]; const struct set_elem *e; atd = ipset_nest_start(skb, IPSET_ATTR_ADT); if (!atd) return -EMSGSIZE; - for (; cb->args[IPSET_CB_ARG0] < map->size; - cb->args[IPSET_CB_ARG0]++) { - i = cb->args[IPSET_CB_ARG0]; - e = list_set_elem(set, map, i); + for (; cb->args[2] < map->size; cb->args[2]++) { + i = cb->args[2]; + e = list_set_elem(map, i); if (e->id == IPSET_INVALID_ID) goto finish; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, set))) + ip_set_timeout_expired(ext_timeout(e, map))) continue; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) { @@ -514,25 +530,31 @@ list_set_list(const struct ip_set *set, goto nla_put_failure; } if (nla_put_string(skb, IPSET_ATTR_NAME, - ip_set_name_byindex(map->net, e->id))) + ip_set_name_byindex(e->id))) + goto nla_put_failure; + if (SET_WITH_TIMEOUT(set) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get( + ext_timeout(e, map))))) goto nla_put_failure; - if (ip_set_put_extensions(skb, set, e, true)) + if (SET_WITH_COUNTER(set) && + ip_set_put_counter(skb, ext_counter(e, map))) goto nla_put_failure; ipset_nest_end(skb, nested); } finish: ipset_nest_end(skb, atd); /* Set listing finished */ - cb->args[IPSET_CB_ARG0] = 0; + cb->args[2] = 0; return 0; nla_put_failure: nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); if (unlikely(i == first)) { - cb->args[IPSET_CB_ARG0] = 0; + cb->args[2] = 0; return -EMSGSIZE; } - ipset_nest_end(skb, atd); return 0; } @@ -543,7 +565,7 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b) const struct list_set *y = b->data; return x->size == y->size && - a->timeout == b->timeout && + x->timeout == y->timeout && a->extensions == b->extensions; } @@ -572,7 +594,7 @@ list_set_gc(unsigned long ul_set) set_cleanup_entries(set); write_unlock_bh(&set->lock); - map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; add_timer(&map->gc); } @@ -584,40 +606,43 @@ list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) init_timer(&map->gc); map->gc.data = (unsigned long) set; map->gc.function = gc; - map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; add_timer(&map->gc); } /* Create list:set type of sets */ -static bool -init_list_set(struct net *net, struct ip_set *set, u32 size) +static struct list_set * +init_list_set(struct ip_set *set, u32 size, size_t dsize, + unsigned long timeout) { struct list_set *map; struct set_elem *e; u32 i; - map = kzalloc(sizeof(*map) + size * set->dsize, GFP_KERNEL); + map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); if (!map) - return false; + return NULL; map->size = size; - map->net = net; + map->dsize = dsize; + map->timeout = timeout; set->data = map; for (i = 0; i < size; i++) { - e = list_set_elem(set, map, i); + e = list_set_elem(map, i); e->id = IPSET_INVALID_ID; } - return true; + return map; } static int -list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[], - u32 flags) +list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) { - u32 size = IP_SET_LIST_DEFAULT_SIZE; + struct list_set *map; + u32 size = IP_SET_LIST_DEFAULT_SIZE, cadt_flags = 0; + unsigned long timeout = 0; if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || @@ -629,13 +654,45 @@ list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[], if (size < IP_SET_LIST_MIN_SIZE) size = IP_SET_LIST_MIN_SIZE; + if (tb[IPSET_ATTR_CADT_FLAGS]) + cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (tb[IPSET_ATTR_TIMEOUT]) + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); set->variant = &set_variant; - set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem)); - if (!init_list_set(net, set, size)) - return -ENOMEM; - if (tb[IPSET_ATTR_TIMEOUT]) { - set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { + set->extensions |= IPSET_EXT_COUNTER; + if (tb[IPSET_ATTR_TIMEOUT]) { + map = init_list_set(set, size, + sizeof(struct setct_elem), timeout); + if (!map) + return -ENOMEM; + set->extensions |= IPSET_EXT_TIMEOUT; + map->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct setct_elem, timeout); + map->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct setct_elem, counter); + list_set_gc_init(set, list_set_gc); + } else { + map = init_list_set(set, size, + sizeof(struct setc_elem), 0); + if (!map) + return -ENOMEM; + map->offset[IPSET_OFFSET_COUNTER] = + offsetof(struct setc_elem, counter); + } + } else if (tb[IPSET_ATTR_TIMEOUT]) { + map = init_list_set(set, size, + sizeof(struct sett_elem), timeout); + if (!map) + return -ENOMEM; + set->extensions |= IPSET_EXT_TIMEOUT; + map->offset[IPSET_OFFSET_TIMEOUT] = + offsetof(struct sett_elem, timeout); list_set_gc_init(set, list_set_gc); + } else { + map = init_list_set(set, size, sizeof(struct set_elem), 0); + if (!map) + return -ENOMEM; } return 0; } @@ -646,8 +703,8 @@ static struct ip_set_type list_set_type __read_mostly = { .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, - .revision_min = IPSET_TYPE_REV_MIN, - .revision_max = IPSET_TYPE_REV_MAX, + .revision_min = REVISION_MIN, + .revision_max = REVISION_MAX, .create = list_set_create, .create_policy = { [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, @@ -664,7 +721,6 @@ static struct ip_set_type list_set_type __read_mostly = { [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, - [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 4f26ee4..74fd00c 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1139,6 +1139,12 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) ip_vs_fill_iph_skb(af, skb, &iph); #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { + if (!iph.fragoffs && skb_nfct_reasm(skb)) { + struct sk_buff *reasm = skb_nfct_reasm(skb); + /* Save fw mark for coming frags */ + reasm->ipvs_property = 1; + reasm->mark = skb->mark; + } if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { int related; int verdict = ip_vs_out_icmp_v6(skb, &related, @@ -1233,11 +1239,11 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) * Check if packet is reply for established ip_vs_conn. */ static unsigned int -ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_reply4(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_out(ops->hooknum, skb, AF_INET); + return ip_vs_out(hooknum, skb, AF_INET); } /* @@ -1245,11 +1251,11 @@ ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, * Check if packet is reply for established ip_vs_conn. */ static unsigned int -ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_local_reply4(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_out(ops->hooknum, skb, AF_INET); + return ip_vs_out(hooknum, skb, AF_INET); } #ifdef CONFIG_IP_VS_IPV6 @@ -1260,11 +1266,11 @@ ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, * Check if packet is reply for established ip_vs_conn. */ static unsigned int -ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_reply6(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_out(ops->hooknum, skb, AF_INET6); + return ip_vs_out(hooknum, skb, AF_INET6); } /* @@ -1272,11 +1278,11 @@ ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb, * Check if packet is reply for established ip_vs_conn. */ static unsigned int -ip_vs_local_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_local_reply6(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_out(ops->hooknum, skb, AF_INET6); + return ip_vs_out(hooknum, skb, AF_INET6); } #endif @@ -1608,6 +1614,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { + if (!iph.fragoffs && skb_nfct_reasm(skb)) { + struct sk_buff *reasm = skb_nfct_reasm(skb); + /* Save fw mark for coming frags. */ + reasm->ipvs_property = 1; + reasm->mark = skb->mark; + } if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { int related; int verdict = ip_vs_in_icmp_v6(skb, &related, hooknum, @@ -1659,8 +1671,9 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) /* sorry, all this trouble for a no-hit :) */ IP_VS_DBG_PKT(12, af, pp, skb, 0, "ip_vs_in: packet continues traversal as normal"); - if (iph.fragoffs) { + if (iph.fragoffs && !skb_nfct_reasm(skb)) { /* Fragment that couldn't be mapped to a conn entry + * and don't have any pointer to a reasm skb * is missing module nf_defrag_ipv6 */ IP_VS_DBG_RL("Unhandled frag, load nf_defrag_ipv6\n"); @@ -1720,12 +1733,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) * Schedule and forward packets from remote clients */ static unsigned int -ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_remote_request4(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_in(ops->hooknum, skb, AF_INET); + return ip_vs_in(hooknum, skb, AF_INET); } /* @@ -1733,26 +1746,58 @@ ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, * Schedule and forward packets from local clients */ static unsigned int -ip_vs_local_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_local_request4(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_in(ops->hooknum, skb, AF_INET); + return ip_vs_in(hooknum, skb, AF_INET); } #ifdef CONFIG_IP_VS_IPV6 /* + * AF_INET6 fragment handling + * Copy info from first fragment, to the rest of them. + */ +static unsigned int +ip_vs_preroute_frag6(unsigned int hooknum, struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct sk_buff *reasm = skb_nfct_reasm(skb); + struct net *net; + + /* Skip if not a "replay" from nf_ct_frag6_output or first fragment. + * ipvs_property is set when checking first fragment + * in ip_vs_in() and ip_vs_out(). + */ + if (reasm) + IP_VS_DBG(2, "Fragment recv prop:%d\n", reasm->ipvs_property); + if (!reasm || !reasm->ipvs_property) + return NF_ACCEPT; + + net = skb_net(skb); + if (!net_ipvs(net)->enable) + return NF_ACCEPT; + + /* Copy stored fw mark, saved in ip_vs_{in,out} */ + skb->mark = reasm->mark; + + return NF_ACCEPT; +} + +/* * AF_INET6 handler in NF_INET_LOCAL_IN chain * Schedule and forward packets from remote clients */ static unsigned int -ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_remote_request6(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_in(ops->hooknum, skb, AF_INET6); + return ip_vs_in(hooknum, skb, AF_INET6); } /* @@ -1760,11 +1805,11 @@ ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, * Schedule and forward packets from local clients */ static unsigned int -ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_local_request6(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_in(ops->hooknum, skb, AF_INET6); + return ip_vs_in(hooknum, skb, AF_INET6); } #endif @@ -1780,7 +1825,7 @@ ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, * and send them to ip_vs_in_icmp. */ static unsigned int -ip_vs_forward_icmp(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { @@ -1797,12 +1842,12 @@ ip_vs_forward_icmp(const struct nf_hook_ops *ops, struct sk_buff *skb, if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable)) return NF_ACCEPT; - return ip_vs_in_icmp(skb, &r, ops->hooknum); + return ip_vs_in_icmp(skb, &r, hooknum); } #ifdef CONFIG_IP_VS_IPV6 static unsigned int -ip_vs_forward_icmp_v6(const struct nf_hook_ops *ops, struct sk_buff *skb, +ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { @@ -1821,7 +1866,7 @@ ip_vs_forward_icmp_v6(const struct nf_hook_ops *ops, struct sk_buff *skb, if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable)) return NF_ACCEPT; - return ip_vs_in_icmp_v6(skb, &r, ops->hooknum, &iphdr); + return ip_vs_in_icmp_v6(skb, &r, hooknum, &iphdr); } #endif @@ -1879,6 +1924,14 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = { .priority = 100, }, #ifdef CONFIG_IP_VS_IPV6 + /* After mangle & nat fetch 2:nd fragment and following */ + { + .hook = ip_vs_preroute_frag6, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP6_PRI_NAT_DST + 1, + }, /* After packet filtering, change source only for VS/NAT */ { .hook = ip_vs_reply6, diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 35be035..a3df9bd 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -704,7 +704,7 @@ static void ip_vs_dest_free(struct ip_vs_dest *dest) __ip_vs_dst_cache_reset(dest); __ip_vs_svc_put(svc, false); free_percpu(dest->stats.cpustats); - ip_vs_dest_put_and_free(dest); + kfree(dest); } /* @@ -842,7 +842,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, struct ip_vs_dest **dest_p) { struct ip_vs_dest *dest; - unsigned int atype, i; + unsigned int atype; EnterFunction(2); @@ -869,12 +869,6 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, if (!dest->stats.cpustats) goto err_alloc; - for_each_possible_cpu(i) { - struct ip_vs_cpu_stats *ip_vs_dest_stats; - ip_vs_dest_stats = per_cpu_ptr(dest->stats.cpustats, i); - u64_stats_init(&ip_vs_dest_stats->syncp); - } - dest->af = svc->af; dest->protocol = svc->protocol; dest->vaddr = svc->addr; @@ -1140,7 +1134,7 @@ static int ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, struct ip_vs_service **svc_p) { - int ret = 0, i; + int ret = 0; struct ip_vs_scheduler *sched = NULL; struct ip_vs_pe *pe = NULL; struct ip_vs_service *svc = NULL; @@ -1190,13 +1184,6 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, goto out_err; } - for_each_possible_cpu(i) { - struct ip_vs_cpu_stats *ip_vs_stats; - ip_vs_stats = per_cpu_ptr(svc->stats.cpustats, i); - u64_stats_init(&ip_vs_stats->syncp); - } - - /* I'm the first user of the service */ atomic_set(&svc->refcnt, 0); @@ -3580,7 +3567,7 @@ out: } -static const struct genl_ops ip_vs_genl_ops[] __read_mostly = { +static struct genl_ops ip_vs_genl_ops[] __read_mostly = { { .cmd = IPVS_CMD_NEW_SERVICE, .flags = GENL_ADMIN_PERM, @@ -3679,7 +3666,7 @@ static const struct genl_ops ip_vs_genl_ops[] __read_mostly = { static int __init ip_vs_genl_register(void) { return genl_register_family_with_ops(&ip_vs_genl_family, - ip_vs_genl_ops); + ip_vs_genl_ops, ARRAY_SIZE(ip_vs_genl_ops)); } static void ip_vs_genl_unregister(void) @@ -3793,7 +3780,7 @@ static struct notifier_block ip_vs_dst_notifier = { int __net_init ip_vs_control_net_init(struct net *net) { - int i, idx; + int idx; struct netns_ipvs *ipvs = net_ipvs(net); /* Initialize rs_table */ @@ -3812,12 +3799,6 @@ int __net_init ip_vs_control_net_init(struct net *net) if (!ipvs->tot_stats.cpustats) return -ENOMEM; - for_each_possible_cpu(i) { - struct ip_vs_cpu_stats *ipvs_tot_stats; - ipvs_tot_stats = per_cpu_ptr(ipvs->tot_stats.cpustats, i); - u64_stats_init(&ipvs_tot_stats->syncp); - } - spin_lock_init(&ipvs->tot_stats.lock); proc_create("ip_vs", 0, net->proc_net, &ip_vs_info_fops); @@ -3839,6 +3820,10 @@ void __net_exit ip_vs_control_net_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); + /* Some dest can be in grace period even before cleanup, we have to + * defer ip_vs_trash_cleanup until ip_vs_dest_wait_readers is called. + */ + rcu_barrier(); ip_vs_trash_cleanup(net); ip_vs_stop_estimator(net, &ipvs->tot_stats); ip_vs_control_net_cleanup_sysctl(net); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index ca056a3..eff13c9 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -136,7 +136,7 @@ static void ip_vs_lblc_rcu_free(struct rcu_head *head) struct ip_vs_lblc_entry, rcu_head); - ip_vs_dest_put_and_free(en->dest); + ip_vs_dest_put(en->dest); kfree(en); } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 3f21a2f..0b85500 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -130,7 +130,7 @@ static void ip_vs_lblcr_elem_rcu_free(struct rcu_head *head) struct ip_vs_dest_set_elem *e; e = container_of(head, struct ip_vs_dest_set_elem, rcu_head); - ip_vs_dest_put_and_free(e->dest); + ip_vs_dest_put(e->dest); kfree(e); } diff --git a/net/netfilter/ipvs/ip_vs_pe_sip.c b/net/netfilter/ipvs/ip_vs_pe_sip.c index bed5f70..9ef22bd 100644 --- a/net/netfilter/ipvs/ip_vs_pe_sip.c +++ b/net/netfilter/ipvs/ip_vs_pe_sip.c @@ -65,6 +65,7 @@ static int get_callid(const char *dptr, unsigned int dataoff, static int ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb) { + struct sk_buff *reasm = skb_nfct_reasm(skb); struct ip_vs_iphdr iph; unsigned int dataoff, datalen, matchoff, matchlen; const char *dptr; @@ -78,10 +79,15 @@ ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb) /* todo: IPv6 fragments: * I think this only should be done for the first fragment. /HS */ - dataoff = iph.len + sizeof(struct udphdr); + if (reasm) { + skb = reasm; + dataoff = iph.thoff_reasm + sizeof(struct udphdr); + } else + dataoff = iph.len + sizeof(struct udphdr); if (dataoff >= skb->len) return -EINVAL; + /* todo: Check if this will mess-up the reasm skb !!! /HS */ retc = skb_linearize(skb); if (retc < 0) return retc; diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 2f7ea75..23e596e 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -20,18 +20,13 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, sctp_sctphdr_t *sh, _sctph; sh = skb_header_pointer(skb, iph->len, sizeof(_sctph), &_sctph); - if (sh == NULL) { - *verdict = NF_DROP; + if (sh == NULL) return 0; - } sch = skb_header_pointer(skb, iph->len + sizeof(sctp_sctphdr_t), sizeof(_schunkh), &_schunkh); - if (sch == NULL) { - *verdict = NF_DROP; + if (sch == NULL) return 0; - } - net = skb_net(skb); ipvs = net_ipvs(net); rcu_read_lock(); @@ -81,7 +76,6 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, { sctp_sctphdr_t *sctph; unsigned int sctphoff = iph->len; - bool payload_csum = false; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6 && iph->fragoffs) @@ -93,31 +87,19 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, return 0; if (unlikely(cp->app != NULL)) { - int ret; - /* Some checks before mangling */ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) return 0; /* Call application helper if needed */ - ret = ip_vs_app_pkt_out(cp, skb); - if (ret == 0) + if (!ip_vs_app_pkt_out(cp, skb)) return 0; - /* ret=2: csum update is needed after payload mangling */ - if (ret == 2) - payload_csum = true; } sctph = (void *) skb_network_header(skb) + sctphoff; + sctph->source = cp->vport; - /* Only update csum if we really have to */ - if (sctph->source != cp->vport || payload_csum || - skb->ip_summed == CHECKSUM_PARTIAL) { - sctph->source = cp->vport; - sctp_nat_csum(skb, sctph, sctphoff); - } else { - skb->ip_summed = CHECKSUM_UNNECESSARY; - } + sctp_nat_csum(skb, sctph, sctphoff); return 1; } @@ -128,7 +110,6 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, { sctp_sctphdr_t *sctph; unsigned int sctphoff = iph->len; - bool payload_csum = false; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6 && iph->fragoffs) @@ -140,32 +121,19 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, return 0; if (unlikely(cp->app != NULL)) { - int ret; - /* Some checks before mangling */ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) return 0; /* Call application helper if needed */ - ret = ip_vs_app_pkt_in(cp, skb); - if (ret == 0) + if (!ip_vs_app_pkt_in(cp, skb)) return 0; - /* ret=2: csum update is needed after payload mangling */ - if (ret == 2) - payload_csum = true; } sctph = (void *) skb_network_header(skb) + sctphoff; + sctph->dest = cp->dport; - /* Only update csum if we really have to */ - if (sctph->dest != cp->dport || payload_csum || - (skb->ip_summed == CHECKSUM_PARTIAL && - !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CSUM))) { - sctph->dest = cp->dport; - sctp_nat_csum(skb, sctph, sctphoff); - } else if (skb->ip_summed != CHECKSUM_PARTIAL) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - } + sctp_nat_csum(skb, sctph, sctphoff); return 1; } diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index cc65b2f..3588fae 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -115,46 +115,27 @@ ip_vs_sh_get(struct ip_vs_service *svc, struct ip_vs_sh_state *s, } -/* As ip_vs_sh_get, but with fallback if selected server is unavailable - * - * The fallback strategy loops around the table starting from a "random" - * point (in fact, it is chosen to be the original hash value to make the - * algorithm deterministic) to find a new server. - */ +/* As ip_vs_sh_get, but with fallback if selected server is unavailable */ static inline struct ip_vs_dest * ip_vs_sh_get_fallback(struct ip_vs_service *svc, struct ip_vs_sh_state *s, const union nf_inet_addr *addr, __be16 port) { - unsigned int offset, roffset; - unsigned int hash, ihash; + unsigned int offset; + unsigned int hash; struct ip_vs_dest *dest; - /* first try the dest it's supposed to go to */ - ihash = ip_vs_sh_hashkey(svc->af, addr, port, 0); - dest = rcu_dereference(s->buckets[ihash].dest); - if (!dest) - return NULL; - if (!is_unavailable(dest)) - return dest; - - IP_VS_DBG_BUF(6, "SH: selected unavailable server %s:%d, reselecting", - IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port)); - - /* if the original dest is unavailable, loop around the table - * starting from ihash to find a new dest - */ for (offset = 0; offset < IP_VS_SH_TAB_SIZE; offset++) { - roffset = (offset + ihash) % IP_VS_SH_TAB_SIZE; - hash = ip_vs_sh_hashkey(svc->af, addr, port, roffset); + hash = ip_vs_sh_hashkey(svc->af, addr, port, offset); dest = rcu_dereference(s->buckets[hash].dest); if (!dest) break; - if (!is_unavailable(dest)) + if (is_unavailable(dest)) + IP_VS_DBG_BUF(6, "SH: selected unavailable server " + "%s:%d (offset %d)", + IP_VS_DBG_ADDR(svc->af, &dest->addr), + ntohs(dest->port), offset); + else return dest; - IP_VS_DBG_BUF(6, "SH: selected unavailable " - "server %s:%d (offset %d), reselecting", - IP_VS_DBG_ADDR(svc->af, &dest->addr), - ntohs(dest->port), roffset); } return NULL; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index f63c238..f448471 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1637,9 +1637,12 @@ static int sync_thread_master(void *data) continue; } while (ip_vs_send_sync_msg(tinfo->sock, sb->mesg) < 0) { - int ret = __wait_event_interruptible(*sk_sleep(sk), + int ret = 0; + + __wait_event_interruptible(*sk_sleep(sk), sock_writeable(sk) || - kthread_should_stop()); + kthread_should_stop(), + ret); if (unlikely(kthread_should_stop())) goto done; } diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c index a4b5e2a..2d3030a 100644 --- a/net/netfilter/nf_conntrack_acct.c +++ b/net/netfilter/nf_conntrack_acct.c @@ -39,23 +39,21 @@ static struct ctl_table acct_sysctl_table[] = { unsigned int seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir) { - struct nf_conn_acct *acct; - struct nf_conn_counter *counter; + struct nf_conn_counter *acct; acct = nf_conn_acct_find(ct); if (!acct) return 0; - counter = acct->counter; return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)atomic64_read(&counter[dir].packets), - (unsigned long long)atomic64_read(&counter[dir].bytes)); + (unsigned long long)atomic64_read(&acct[dir].packets), + (unsigned long long)atomic64_read(&acct[dir].bytes)); }; EXPORT_SYMBOL_GPL(seq_print_acct); static struct nf_ct_ext_type acct_extend __read_mostly = { - .len = sizeof(struct nf_conn_acct), - .align = __alignof__(struct nf_conn_acct), + .len = sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]), + .align = __alignof__(struct nf_conn_counter[IP_CT_DIR_MAX]), .id = NF_CT_EXT_ACCT, }; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 43549eb..5d892fe 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -764,10 +764,9 @@ void nf_conntrack_free(struct nf_conn *ct) struct net *net = nf_ct_net(ct); nf_ct_ext_destroy(ct); + atomic_dec(&net->ct.count); nf_ct_ext_free(ct); kmem_cache_free(net->ct.nf_conntrack_cachep, ct); - smp_mb__before_atomic_dec(); - atomic_dec(&net->ct.count); } EXPORT_SYMBOL_GPL(nf_conntrack_free); @@ -1110,14 +1109,12 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, acct: if (do_acct) { - struct nf_conn_acct *acct; + struct nf_conn_counter *acct; acct = nf_conn_acct_find(ct); if (acct) { - struct nf_conn_counter *counter = acct->counter; - - atomic64_inc(&counter[CTINFO2DIR(ctinfo)].packets); - atomic64_add(skb->len, &counter[CTINFO2DIR(ctinfo)].bytes); + atomic64_inc(&acct[CTINFO2DIR(ctinfo)].packets); + atomic64_add(skb->len, &acct[CTINFO2DIR(ctinfo)].bytes); } } } @@ -1129,15 +1126,13 @@ bool __nf_ct_kill_acct(struct nf_conn *ct, int do_acct) { if (do_acct) { - struct nf_conn_acct *acct; + struct nf_conn_counter *acct; acct = nf_conn_acct_find(ct); if (acct) { - struct nf_conn_counter *counter = acct->counter; - - atomic64_inc(&counter[CTINFO2DIR(ctinfo)].packets); + atomic64_inc(&acct[CTINFO2DIR(ctinfo)].packets); atomic64_add(skb->len - skb_network_offset(skb), - &counter[CTINFO2DIR(ctinfo)].bytes); + &acct[CTINFO2DIR(ctinfo)].bytes); } } diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 08870b8..eea936b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -211,23 +211,13 @@ nla_put_failure: } static int -dump_counters(struct sk_buff *skb, struct nf_conn_acct *acct, - enum ip_conntrack_dir dir, int type) +dump_counters(struct sk_buff *skb, u64 pkts, u64 bytes, + enum ip_conntrack_dir dir) { - enum ctattr_type attr = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; - struct nf_conn_counter *counter = acct->counter; + enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; struct nlattr *nest_count; - u64 pkts, bytes; - if (type == IPCTNL_MSG_CT_GET_CTRZERO) { - pkts = atomic64_xchg(&counter[dir].packets, 0); - bytes = atomic64_xchg(&counter[dir].bytes, 0); - } else { - pkts = atomic64_read(&counter[dir].packets); - bytes = atomic64_read(&counter[dir].bytes); - } - - nest_count = nla_nest_start(skb, attr | NLA_F_NESTED); + nest_count = nla_nest_start(skb, type | NLA_F_NESTED); if (!nest_count) goto nla_put_failure; @@ -244,19 +234,24 @@ nla_put_failure: } static int -ctnetlink_dump_acct(struct sk_buff *skb, const struct nf_conn *ct, int type) +ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, + enum ip_conntrack_dir dir, int type) { - struct nf_conn_acct *acct = nf_conn_acct_find(ct); + struct nf_conn_counter *acct; + u64 pkts, bytes; + acct = nf_conn_acct_find(ct); if (!acct) return 0; - if (dump_counters(skb, acct, IP_CT_DIR_ORIGINAL, type) < 0) - return -1; - if (dump_counters(skb, acct, IP_CT_DIR_REPLY, type) < 0) - return -1; - - return 0; + if (type == IPCTNL_MSG_CT_GET_CTRZERO) { + pkts = atomic64_xchg(&acct[dir].packets, 0); + bytes = atomic64_xchg(&acct[dir].bytes, 0); + } else { + pkts = atomic64_read(&acct[dir].packets); + bytes = atomic64_read(&acct[dir].bytes); + } + return dump_counters(skb, pkts, bytes, dir); } static int @@ -493,7 +488,8 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (ctnetlink_dump_status(skb, ct) < 0 || ctnetlink_dump_timeout(skb, ct) < 0 || - ctnetlink_dump_acct(skb, ct, type) < 0 || + ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL, type) < 0 || + ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY, type) < 0 || ctnetlink_dump_timestamp(skb, ct) < 0 || ctnetlink_dump_protoinfo(skb, ct) < 0 || ctnetlink_dump_helpinfo(skb, ct) < 0 || @@ -534,7 +530,7 @@ ctnetlink_proto_size(const struct nf_conn *ct) } static inline size_t -ctnetlink_acct_size(const struct nf_conn *ct) +ctnetlink_counters_size(const struct nf_conn *ct) { if (!nf_ct_ext_exist(ct, NF_CT_EXT_ACCT)) return 0; @@ -583,7 +579,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct) + 3 * nla_total_size(sizeof(u_int8_t)) /* CTA_PROTO_NUM */ + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */ + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */ - + ctnetlink_acct_size(ct) + + ctnetlink_counters_size(ct) + ctnetlink_timestamp_size(ct) + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */ + nla_total_size(0) /* CTA_PROTOINFO */ @@ -677,7 +673,10 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) goto nla_put_failure; if (events & (1 << IPCT_DESTROY)) { - if (ctnetlink_dump_acct(skb, ct, type) < 0 || + if (ctnetlink_dump_counters(skb, ct, + IP_CT_DIR_ORIGINAL, type) < 0 || + ctnetlink_dump_counters(skb, ct, + IP_CT_DIR_REPLY, type) < 0 || ctnetlink_dump_timestamp(skb, ct) < 0) goto nla_put_failure; } else { diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index 17c1bcb..5f9bfd0 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -41,8 +41,8 @@ int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, spin_lock_bh(&ct->lock); this_way = &seqadj->seq[dir]; if (this_way->offset_before == this_way->offset_after || - before(this_way->correction_pos, ntohl(seq))) { - this_way->correction_pos = ntohl(seq); + before(this_way->correction_pos, seq)) { + this_way->correction_pos = seq; this_way->offset_before = this_way->offset_after; this_way->offset_after += off; } diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 466410e..e0c4373 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -52,8 +52,66 @@ module_param(sip_direct_media, int, 0600); MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling " "endpoints only (default 1)"); -const struct nf_nat_sip_hooks *nf_nat_sip_hooks; -EXPORT_SYMBOL_GPL(nf_nat_sip_hooks); +unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, const char **dptr, + unsigned int *datalen) __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_sip_hook); + +void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff, + s16 off) __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook); + +unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + struct nf_conntrack_expect *exp, + unsigned int matchoff, + unsigned int matchlen) __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook); + +unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + unsigned int sdpoff, + enum sdp_header_types type, + enum sdp_header_types term, + const union nf_inet_addr *addr) + __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook); + +unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + unsigned int matchoff, + unsigned int matchlen, + u_int16_t port) __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook); + +unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + unsigned int sdpoff, + const union nf_inet_addr *addr) + __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook); + +unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + struct nf_conntrack_expect *rtp_exp, + struct nf_conntrack_expect *rtcp_exp, + unsigned int mediaoff, + unsigned int medialen, + union nf_inet_addr *rtp_addr) + __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook); static int string_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) @@ -856,7 +914,8 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, int direct_rtp = 0, skip_expect = 0, ret = NF_DROP; u_int16_t base_port; __be16 rtp_port, rtcp_port; - const struct nf_nat_sip_hooks *hooks; + typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port; + typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media; saddr = NULL; if (sip_direct_media) { @@ -907,23 +966,22 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, #endif skip_expect = 1; } while (!skip_expect); + rcu_read_unlock(); base_port = ntohs(tuple.dst.u.udp.port) & ~1; rtp_port = htons(base_port); rtcp_port = htons(base_port + 1); if (direct_rtp) { - hooks = rcu_dereference(nf_nat_sip_hooks); - if (hooks && - !hooks->sdp_port(skb, protoff, dataoff, dptr, datalen, + nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook); + if (nf_nat_sdp_port && + !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, mediaoff, medialen, ntohs(rtp_port))) goto err1; } - if (skip_expect) { - rcu_read_unlock(); + if (skip_expect) return NF_ACCEPT; - } rtp_exp = nf_ct_expect_alloc(ct); if (rtp_exp == NULL) @@ -937,10 +995,10 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr, IPPROTO_UDP, NULL, &rtcp_port); - hooks = rcu_dereference(nf_nat_sip_hooks); - if (hooks && ct->status & IPS_NAT_MASK && !direct_rtp) - ret = hooks->sdp_media(skb, protoff, dataoff, dptr, - datalen, rtp_exp, rtcp_exp, + nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); + if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp) + ret = nf_nat_sdp_media(skb, protoff, dataoff, dptr, datalen, + rtp_exp, rtcp_exp, mediaoff, medialen, daddr); else { if (nf_ct_expect_related(rtp_exp) == 0) { @@ -954,7 +1012,6 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, err2: nf_ct_expect_put(rtp_exp); err1: - rcu_read_unlock(); return ret; } @@ -994,12 +1051,13 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, unsigned int caddr_len, maddr_len; unsigned int i; union nf_inet_addr caddr, maddr, rtp_addr; - const struct nf_nat_sip_hooks *hooks; unsigned int port; const struct sdp_media_type *t; int ret = NF_ACCEPT; + typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr; + typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session; - hooks = rcu_dereference(nf_nat_sip_hooks); + nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); /* Find beginning of session description */ if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, @@ -1067,11 +1125,10 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, } /* Update media connection address if present */ - if (maddr_len && hooks && ct->status & IPS_NAT_MASK) { - ret = hooks->sdp_addr(skb, protoff, dataoff, + if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { + ret = nf_nat_sdp_addr(skb, protoff, dataoff, dptr, datalen, mediaoff, - SDP_HDR_CONNECTION, - SDP_HDR_MEDIA, + SDP_HDR_CONNECTION, SDP_HDR_MEDIA, &rtp_addr); if (ret != NF_ACCEPT) { nf_ct_helper_log(skb, ct, "cannot mangle SDP"); @@ -1082,11 +1139,10 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, } /* Update session connection and owner addresses */ - hooks = rcu_dereference(nf_nat_sip_hooks); - if (hooks && ct->status & IPS_NAT_MASK) - ret = hooks->sdp_session(skb, protoff, dataoff, - dptr, datalen, sdpoff, - &rtp_addr); + nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook); + if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK) + ret = nf_nat_sdp_session(skb, protoff, dataoff, + dptr, datalen, sdpoff, &rtp_addr); return ret; } @@ -1186,11 +1242,11 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, unsigned int matchoff, matchlen; struct nf_conntrack_expect *exp; union nf_inet_addr *saddr, daddr; - const struct nf_nat_sip_hooks *hooks; __be16 port; u8 proto; unsigned int expires = 0; int ret; + typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect; /* Expected connections can not register again. */ if (ct->status & IPS_EXPECTED) @@ -1253,10 +1309,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, exp->helper = nfct_help(ct)->helper; exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; - hooks = rcu_dereference(nf_nat_sip_hooks); - if (hooks && ct->status & IPS_NAT_MASK) - ret = hooks->expect(skb, protoff, dataoff, dptr, datalen, - exp, matchoff, matchlen); + nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook); + if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK) + ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen, + exp, matchoff, matchlen); else { if (nf_ct_expect_related(exp) != 0) { nf_ct_helper_log(skb, ct, "cannot add expectation"); @@ -1459,7 +1515,7 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen) { - const struct nf_nat_sip_hooks *hooks; + typeof(nf_nat_sip_hook) nf_nat_sip; int ret; if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0) @@ -1468,9 +1524,9 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, ret = process_sip_response(skb, protoff, dataoff, dptr, datalen); if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { - hooks = rcu_dereference(nf_nat_sip_hooks); - if (hooks && !hooks->msg(skb, protoff, dataoff, - dptr, datalen)) { + nf_nat_sip = rcu_dereference(nf_nat_sip_hook); + if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff, + dptr, datalen)) { nf_ct_helper_log(skb, ct, "cannot NAT SIP message"); ret = NF_DROP; } @@ -1490,6 +1546,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, s16 diff, tdiff = 0; int ret = NF_ACCEPT; bool term; + typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust; if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) @@ -1553,11 +1610,9 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, } if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { - const struct nf_nat_sip_hooks *hooks; - - hooks = rcu_dereference(nf_nat_sip_hooks); - if (hooks) - hooks->seq_adjust(skb, protoff, tdiff); + nf_nat_sip_seq_adjust = rcu_dereference(nf_nat_sip_seq_adjust_hook); + if (nf_nat_sip_seq_adjust) + nf_nat_sip_seq_adjust(skb, protoff, tdiff); } return ret; diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index 61a3c92..3deec99 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -13,20 +13,26 @@ /* core.c */ -unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, - unsigned int hook, const struct net_device *indev, - const struct net_device *outdev, - struct nf_hook_ops **elemp, - int (*okfn)(struct sk_buff *), int hook_thresh); +extern unsigned int nf_iterate(struct list_head *head, + struct sk_buff *skb, + unsigned int hook, + const struct net_device *indev, + const struct net_device *outdev, + struct nf_hook_ops **elemp, + int (*okfn)(struct sk_buff *), + int hook_thresh); /* nf_queue.c */ -int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, u_int8_t pf, - unsigned int hook, struct net_device *indev, - struct net_device *outdev, int (*okfn)(struct sk_buff *), - unsigned int queuenum); -int __init netfilter_queue_init(void); +extern int nf_queue(struct sk_buff *skb, + struct nf_hook_ops *elem, + u_int8_t pf, unsigned int hook, + struct net_device *indev, + struct net_device *outdev, + int (*okfn)(struct sk_buff *), + unsigned int queuenum); +extern int __init netfilter_queue_init(void); /* nf_log.c */ -int __init netfilter_log_init(void); +extern int __init netfilter_log_init(void); #endif diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 63a8154..6f0f4f7 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -432,26 +432,6 @@ nf_nat_setup_info(struct nf_conn *ct, } EXPORT_SYMBOL(nf_nat_setup_info); -unsigned int -nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) -{ - /* Force range to this IP; let proto decide mapping for - * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). - * Use reply in case it's already been mangled (eg local packet). - */ - union nf_inet_addr ip = - (HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ? - ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3 : - ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3); - struct nf_nat_range range = { - .flags = NF_NAT_RANGE_MAP_IPS, - .min_addr = ip, - .max_addr = ip, - }; - return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); -} -EXPORT_SYMBOL_GPL(nf_nat_alloc_null_binding); - /* Do packet manipulations according to nf_nat_setup_info. */ unsigned int nf_nat_packet(struct nf_conn *ct, enum ip_conntrack_info ctinfo, diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index b4d691d..f979040 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -625,26 +625,33 @@ static struct nf_ct_helper_expectfn sip_nat = { static void __exit nf_nat_sip_fini(void) { - RCU_INIT_POINTER(nf_nat_sip_hooks, NULL); - + RCU_INIT_POINTER(nf_nat_sip_hook, NULL); + RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL); + RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL); + RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL); + RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL); + RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL); + RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL); nf_ct_helper_expectfn_unregister(&sip_nat); synchronize_rcu(); } -static const struct nf_nat_sip_hooks sip_hooks = { - .msg = nf_nat_sip, - .seq_adjust = nf_nat_sip_seq_adjust, - .expect = nf_nat_sip_expect, - .sdp_addr = nf_nat_sdp_addr, - .sdp_port = nf_nat_sdp_port, - .sdp_session = nf_nat_sdp_session, - .sdp_media = nf_nat_sdp_media, -}; - static int __init nf_nat_sip_init(void) { - BUG_ON(nf_nat_sip_hooks != NULL); - RCU_INIT_POINTER(nf_nat_sip_hooks, &sip_hooks); + BUG_ON(nf_nat_sip_hook != NULL); + BUG_ON(nf_nat_sip_seq_adjust_hook != NULL); + BUG_ON(nf_nat_sip_expect_hook != NULL); + BUG_ON(nf_nat_sdp_addr_hook != NULL); + BUG_ON(nf_nat_sdp_port_hook != NULL); + BUG_ON(nf_nat_sdp_session_hook != NULL); + BUG_ON(nf_nat_sdp_media_hook != NULL); + RCU_INIT_POINTER(nf_nat_sip_hook, nf_nat_sip); + RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, nf_nat_sip_seq_adjust); + RCU_INIT_POINTER(nf_nat_sip_expect_hook, nf_nat_sip_expect); + RCU_INIT_POINTER(nf_nat_sdp_addr_hook, nf_nat_sdp_addr); + RCU_INIT_POINTER(nf_nat_sdp_port_hook, nf_nat_sdp_port); + RCU_INIT_POINTER(nf_nat_sdp_session_hook, nf_nat_sdp_session); + RCU_INIT_POINTER(nf_nat_sdp_media_hook, nf_nat_sdp_media); nf_ct_helper_expectfn_register(&sip_nat); return 0; } diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 9858e3e..cdf4567 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -151,10 +151,9 @@ void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info, opts->tsecr = opts->tsval; opts->tsval = tcp_time_stamp & ~0x3f; - if (opts->options & XT_SYNPROXY_OPT_WSCALE) { - opts->tsval |= opts->wscale; - opts->wscale = info->wscale; - } else + if (opts->options & XT_SYNPROXY_OPT_WSCALE) + opts->tsval |= info->wscale; + else opts->tsval |= 0xf; if (opts->options & XT_SYNPROXY_OPT_SACK_PERM) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c deleted file mode 100644 index dcddc49..0000000 --- a/net/netfilter/nf_tables_api.c +++ /dev/null @@ -1,3275 +0,0 @@ -/* - * Copyright (c) 2007-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/skbuff.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nfnetlink.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables_core.h> -#include <net/netfilter/nf_tables.h> -#include <net/net_namespace.h> -#include <net/sock.h> - -static LIST_HEAD(nf_tables_expressions); - -/** - * nft_register_afinfo - register nf_tables address family info - * - * @afi: address family info to register - * - * Register the address family for use with nf_tables. Returns zero on - * success or a negative errno code otherwise. - */ -int nft_register_afinfo(struct net *net, struct nft_af_info *afi) -{ - INIT_LIST_HEAD(&afi->tables); - nfnl_lock(NFNL_SUBSYS_NFTABLES); - list_add_tail(&afi->list, &net->nft.af_info); - nfnl_unlock(NFNL_SUBSYS_NFTABLES); - return 0; -} -EXPORT_SYMBOL_GPL(nft_register_afinfo); - -/** - * nft_unregister_afinfo - unregister nf_tables address family info - * - * @afi: address family info to unregister - * - * Unregister the address family for use with nf_tables. - */ -void nft_unregister_afinfo(struct nft_af_info *afi) -{ - nfnl_lock(NFNL_SUBSYS_NFTABLES); - list_del(&afi->list); - nfnl_unlock(NFNL_SUBSYS_NFTABLES); -} -EXPORT_SYMBOL_GPL(nft_unregister_afinfo); - -static struct nft_af_info *nft_afinfo_lookup(struct net *net, int family) -{ - struct nft_af_info *afi; - - list_for_each_entry(afi, &net->nft.af_info, list) { - if (afi->family == family) - return afi; - } - return NULL; -} - -static struct nft_af_info * -nf_tables_afinfo_lookup(struct net *net, int family, bool autoload) -{ - struct nft_af_info *afi; - - afi = nft_afinfo_lookup(net, family); - if (afi != NULL) - return afi; -#ifdef CONFIG_MODULES - if (autoload) { - nfnl_unlock(NFNL_SUBSYS_NFTABLES); - request_module("nft-afinfo-%u", family); - nfnl_lock(NFNL_SUBSYS_NFTABLES); - afi = nft_afinfo_lookup(net, family); - if (afi != NULL) - return ERR_PTR(-EAGAIN); - } -#endif - return ERR_PTR(-EAFNOSUPPORT); -} - -/* - * Tables - */ - -static struct nft_table *nft_table_lookup(const struct nft_af_info *afi, - const struct nlattr *nla) -{ - struct nft_table *table; - - list_for_each_entry(table, &afi->tables, list) { - if (!nla_strcmp(nla, table->name)) - return table; - } - return NULL; -} - -static struct nft_table *nf_tables_table_lookup(const struct nft_af_info *afi, - const struct nlattr *nla) -{ - struct nft_table *table; - - if (nla == NULL) - return ERR_PTR(-EINVAL); - - table = nft_table_lookup(afi, nla); - if (table != NULL) - return table; - - return ERR_PTR(-ENOENT); -} - -static inline u64 nf_tables_alloc_handle(struct nft_table *table) -{ - return ++table->hgenerator; -} - -static struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX]; - -static int __nf_tables_chain_type_lookup(int family, const struct nlattr *nla) -{ - int i; - - for (i=0; i<NFT_CHAIN_T_MAX; i++) { - if (chain_type[family][i] != NULL && - !nla_strcmp(nla, chain_type[family][i]->name)) - return i; - } - return -1; -} - -static int nf_tables_chain_type_lookup(const struct nft_af_info *afi, - const struct nlattr *nla, - bool autoload) -{ - int type; - - type = __nf_tables_chain_type_lookup(afi->family, nla); -#ifdef CONFIG_MODULES - if (type < 0 && autoload) { - nfnl_unlock(NFNL_SUBSYS_NFTABLES); - request_module("nft-chain-%u-%*.s", afi->family, - nla_len(nla)-1, (const char *)nla_data(nla)); - nfnl_lock(NFNL_SUBSYS_NFTABLES); - type = __nf_tables_chain_type_lookup(afi->family, nla); - } -#endif - return type; -} - -static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { - [NFTA_TABLE_NAME] = { .type = NLA_STRING }, - [NFTA_TABLE_FLAGS] = { .type = NLA_U32 }, -}; - -static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq, - int event, u32 flags, int family, - const struct nft_table *table) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - - event |= NFNL_SUBSYS_NFTABLES << 8; - nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); - if (nlh == NULL) - goto nla_put_failure; - - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = family; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) || - nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags))) - goto nla_put_failure; - - return nlmsg_end(skb, nlh); - -nla_put_failure: - nlmsg_trim(skb, nlh); - return -1; -} - -static int nf_tables_table_notify(const struct sk_buff *oskb, - const struct nlmsghdr *nlh, - const struct nft_table *table, - int event, int family) -{ - struct sk_buff *skb; - u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - u32 seq = nlh ? nlh->nlmsg_seq : 0; - struct net *net = oskb ? sock_net(oskb->sk) : &init_net; - bool report; - int err; - - report = nlh ? nlmsg_report(nlh) : false; - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) - return 0; - - err = -ENOBUFS; - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (skb == NULL) - goto err; - - err = nf_tables_fill_table_info(skb, portid, seq, event, 0, - family, table); - if (err < 0) { - kfree_skb(skb); - goto err; - } - - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); -err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); - return err; -} - -static int nf_tables_dump_tables(struct sk_buff *skb, - struct netlink_callback *cb) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); - const struct nft_af_info *afi; - const struct nft_table *table; - unsigned int idx = 0, s_idx = cb->args[0]; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - - list_for_each_entry(afi, &net->nft.af_info, list) { - if (family != NFPROTO_UNSPEC && family != afi->family) - continue; - - list_for_each_entry(table, &afi->tables, list) { - if (idx < s_idx) - goto cont; - if (idx > s_idx) - memset(&cb->args[1], 0, - sizeof(cb->args) - sizeof(cb->args[0])); - if (nf_tables_fill_table_info(skb, - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, - NFT_MSG_NEWTABLE, - NLM_F_MULTI, - afi->family, table) < 0) - goto done; -cont: - idx++; - } - } -done: - cb->args[0] = idx; - return skb->len; -} - -static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - const struct nft_table *table; - struct sk_buff *skb2; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - int err; - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .dump = nf_tables_dump_tables, - }; - return netlink_dump_start(nlsk, skb, nlh, &c); - } - - afi = nf_tables_afinfo_lookup(net, family, false); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); - if (IS_ERR(table)) - return PTR_ERR(table); - - skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb2) - return -ENOMEM; - - err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0, - family, table); - if (err < 0) - goto err; - - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); - -err: - kfree_skb(skb2); - return err; -} - -static int nf_tables_table_enable(struct nft_table *table) -{ - struct nft_chain *chain; - int err, i = 0; - - list_for_each_entry(chain, &table->chains, list) { - err = nf_register_hook(&nft_base_chain(chain)->ops); - if (err < 0) - goto err; - - i++; - } - return 0; -err: - list_for_each_entry(chain, &table->chains, list) { - if (i-- <= 0) - break; - - nf_unregister_hook(&nft_base_chain(chain)->ops); - } - return err; -} - -static int nf_tables_table_disable(struct nft_table *table) -{ - struct nft_chain *chain; - - list_for_each_entry(chain, &table->chains, list) - nf_unregister_hook(&nft_base_chain(chain)->ops); - - return 0; -} - -static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[], - struct nft_af_info *afi, struct nft_table *table) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - int family = nfmsg->nfgen_family, ret = 0; - - if (nla[NFTA_TABLE_FLAGS]) { - __be32 flags; - - flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS])); - if (flags & ~NFT_TABLE_F_DORMANT) - return -EINVAL; - - if ((flags & NFT_TABLE_F_DORMANT) && - !(table->flags & NFT_TABLE_F_DORMANT)) { - ret = nf_tables_table_disable(table); - if (ret >= 0) - table->flags |= NFT_TABLE_F_DORMANT; - } else if (!(flags & NFT_TABLE_F_DORMANT) && - table->flags & NFT_TABLE_F_DORMANT) { - ret = nf_tables_table_enable(table); - if (ret >= 0) - table->flags &= ~NFT_TABLE_F_DORMANT; - } - if (ret < 0) - goto err; - } - - nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); -err: - return ret; -} - -static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nlattr *name; - struct nft_af_info *afi; - struct nft_table *table; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - - afi = nf_tables_afinfo_lookup(net, family, true); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - name = nla[NFTA_TABLE_NAME]; - table = nf_tables_table_lookup(afi, name); - if (IS_ERR(table)) { - if (PTR_ERR(table) != -ENOENT) - return PTR_ERR(table); - table = NULL; - } - - if (table != NULL) { - if (nlh->nlmsg_flags & NLM_F_EXCL) - return -EEXIST; - if (nlh->nlmsg_flags & NLM_F_REPLACE) - return -EOPNOTSUPP; - return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table); - } - - table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL); - if (table == NULL) - return -ENOMEM; - - nla_strlcpy(table->name, name, nla_len(name)); - INIT_LIST_HEAD(&table->chains); - INIT_LIST_HEAD(&table->sets); - - if (nla[NFTA_TABLE_FLAGS]) { - __be32 flags; - - flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS])); - if (flags & ~NFT_TABLE_F_DORMANT) { - kfree(table); - return -EINVAL; - } - - table->flags |= flags; - } - - list_add_tail(&table->list, &afi->tables); - nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); - return 0; -} - -static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - struct nft_af_info *afi; - struct nft_table *table; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - - afi = nf_tables_afinfo_lookup(net, family, false); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); - if (IS_ERR(table)) - return PTR_ERR(table); - - if (table->use) - return -EBUSY; - - list_del(&table->list); - nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family); - kfree(table); - return 0; -} - -int nft_register_chain_type(struct nf_chain_type *ctype) -{ - int err = 0; - - nfnl_lock(NFNL_SUBSYS_NFTABLES); - if (chain_type[ctype->family][ctype->type] != NULL) { - err = -EBUSY; - goto out; - } - - if (!try_module_get(ctype->me)) - goto out; - - chain_type[ctype->family][ctype->type] = ctype; -out: - nfnl_unlock(NFNL_SUBSYS_NFTABLES); - return err; -} -EXPORT_SYMBOL_GPL(nft_register_chain_type); - -void nft_unregister_chain_type(struct nf_chain_type *ctype) -{ - nfnl_lock(NFNL_SUBSYS_NFTABLES); - chain_type[ctype->family][ctype->type] = NULL; - module_put(ctype->me); - nfnl_unlock(NFNL_SUBSYS_NFTABLES); -} -EXPORT_SYMBOL_GPL(nft_unregister_chain_type); - -/* - * Chains - */ - -static struct nft_chain * -nf_tables_chain_lookup_byhandle(const struct nft_table *table, u64 handle) -{ - struct nft_chain *chain; - - list_for_each_entry(chain, &table->chains, list) { - if (chain->handle == handle) - return chain; - } - - return ERR_PTR(-ENOENT); -} - -static struct nft_chain *nf_tables_chain_lookup(const struct nft_table *table, - const struct nlattr *nla) -{ - struct nft_chain *chain; - - if (nla == NULL) - return ERR_PTR(-EINVAL); - - list_for_each_entry(chain, &table->chains, list) { - if (!nla_strcmp(nla, chain->name)) - return chain; - } - - return ERR_PTR(-ENOENT); -} - -static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { - [NFTA_CHAIN_TABLE] = { .type = NLA_STRING }, - [NFTA_CHAIN_HANDLE] = { .type = NLA_U64 }, - [NFTA_CHAIN_NAME] = { .type = NLA_STRING, - .len = NFT_CHAIN_MAXNAMELEN - 1 }, - [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED }, - [NFTA_CHAIN_POLICY] = { .type = NLA_U32 }, - [NFTA_CHAIN_TYPE] = { .type = NLA_NUL_STRING }, - [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED }, -}; - -static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = { - [NFTA_HOOK_HOOKNUM] = { .type = NLA_U32 }, - [NFTA_HOOK_PRIORITY] = { .type = NLA_U32 }, -}; - -static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats) -{ - struct nft_stats *cpu_stats, total; - struct nlattr *nest; - int cpu; - - memset(&total, 0, sizeof(total)); - for_each_possible_cpu(cpu) { - cpu_stats = per_cpu_ptr(stats, cpu); - total.pkts += cpu_stats->pkts; - total.bytes += cpu_stats->bytes; - } - nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS); - if (nest == NULL) - goto nla_put_failure; - - if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.pkts)) || - nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes))) - goto nla_put_failure; - - nla_nest_end(skb, nest); - return 0; - -nla_put_failure: - return -ENOSPC; -} - -static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, - int event, u32 flags, int family, - const struct nft_table *table, - const struct nft_chain *chain) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - - event |= NFNL_SUBSYS_NFTABLES << 8; - nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); - if (nlh == NULL) - goto nla_put_failure; - - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = family; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name)) - goto nla_put_failure; - if (nla_put_be64(skb, NFTA_CHAIN_HANDLE, cpu_to_be64(chain->handle))) - goto nla_put_failure; - if (nla_put_string(skb, NFTA_CHAIN_NAME, chain->name)) - goto nla_put_failure; - - if (chain->flags & NFT_BASE_CHAIN) { - const struct nft_base_chain *basechain = nft_base_chain(chain); - const struct nf_hook_ops *ops = &basechain->ops; - struct nlattr *nest; - - nest = nla_nest_start(skb, NFTA_CHAIN_HOOK); - if (nest == NULL) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority))) - goto nla_put_failure; - nla_nest_end(skb, nest); - - if (nla_put_be32(skb, NFTA_CHAIN_POLICY, - htonl(basechain->policy))) - goto nla_put_failure; - - if (nla_put_string(skb, NFTA_CHAIN_TYPE, - chain_type[ops->pf][nft_base_chain(chain)->type]->name)) - goto nla_put_failure; - - if (nft_dump_stats(skb, nft_base_chain(chain)->stats)) - goto nla_put_failure; - } - - if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use))) - goto nla_put_failure; - - return nlmsg_end(skb, nlh); - -nla_put_failure: - nlmsg_trim(skb, nlh); - return -1; -} - -static int nf_tables_chain_notify(const struct sk_buff *oskb, - const struct nlmsghdr *nlh, - const struct nft_table *table, - const struct nft_chain *chain, - int event, int family) -{ - struct sk_buff *skb; - u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - struct net *net = oskb ? sock_net(oskb->sk) : &init_net; - u32 seq = nlh ? nlh->nlmsg_seq : 0; - bool report; - int err; - - report = nlh ? nlmsg_report(nlh) : false; - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) - return 0; - - err = -ENOBUFS; - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (skb == NULL) - goto err; - - err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family, - table, chain); - if (err < 0) { - kfree_skb(skb); - goto err; - } - - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); -err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); - return err; -} - -static int nf_tables_dump_chains(struct sk_buff *skb, - struct netlink_callback *cb) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); - const struct nft_af_info *afi; - const struct nft_table *table; - const struct nft_chain *chain; - unsigned int idx = 0, s_idx = cb->args[0]; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - - list_for_each_entry(afi, &net->nft.af_info, list) { - if (family != NFPROTO_UNSPEC && family != afi->family) - continue; - - list_for_each_entry(table, &afi->tables, list) { - list_for_each_entry(chain, &table->chains, list) { - if (idx < s_idx) - goto cont; - if (idx > s_idx) - memset(&cb->args[1], 0, - sizeof(cb->args) - sizeof(cb->args[0])); - if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, - NFT_MSG_NEWCHAIN, - NLM_F_MULTI, - afi->family, table, chain) < 0) - goto done; -cont: - idx++; - } - } - } -done: - cb->args[0] = idx; - return skb->len; -} - - -static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - const struct nft_table *table; - const struct nft_chain *chain; - struct sk_buff *skb2; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - int err; - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .dump = nf_tables_dump_chains, - }; - return netlink_dump_start(nlsk, skb, nlh, &c); - } - - afi = nf_tables_afinfo_lookup(net, family, false); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - - chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); - if (IS_ERR(chain)) - return PTR_ERR(chain); - - skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb2) - return -ENOMEM; - - err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0, - family, table, chain); - if (err < 0) - goto err; - - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); - -err: - kfree_skb(skb2); - return err; -} - -static int -nf_tables_chain_policy(struct nft_base_chain *chain, const struct nlattr *attr) -{ - switch (ntohl(nla_get_be32(attr))) { - case NF_DROP: - chain->policy = NF_DROP; - break; - case NF_ACCEPT: - chain->policy = NF_ACCEPT; - break; - default: - return -EINVAL; - } - return 0; -} - -static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { - [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, - [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, -}; - -static int -nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) -{ - struct nlattr *tb[NFTA_COUNTER_MAX+1]; - struct nft_stats __percpu *newstats; - struct nft_stats *stats; - int err; - - err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy); - if (err < 0) - return err; - - if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS]) - return -EINVAL; - - newstats = alloc_percpu(struct nft_stats); - if (newstats == NULL) - return -ENOMEM; - - /* Restore old counters on this cpu, no problem. Per-cpu statistics - * are not exposed to userspace. - */ - stats = this_cpu_ptr(newstats); - stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); - stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); - - if (chain->stats) { - /* nfnl_lock is held, add some nfnl function for this, later */ - struct nft_stats __percpu *oldstats = - rcu_dereference_protected(chain->stats, 1); - - rcu_assign_pointer(chain->stats, newstats); - synchronize_rcu(); - free_percpu(oldstats); - } else - rcu_assign_pointer(chain->stats, newstats); - - return 0; -} - -static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nlattr * uninitialized_var(name); - const struct nft_af_info *afi; - struct nft_table *table; - struct nft_chain *chain; - struct nft_base_chain *basechain = NULL; - struct nlattr *ha[NFTA_HOOK_MAX + 1]; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - u64 handle = 0; - int err; - bool create; - - create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; - - afi = nf_tables_afinfo_lookup(net, family, true); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - - if (table->use == UINT_MAX) - return -EOVERFLOW; - - chain = NULL; - name = nla[NFTA_CHAIN_NAME]; - - if (nla[NFTA_CHAIN_HANDLE]) { - handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE])); - chain = nf_tables_chain_lookup_byhandle(table, handle); - if (IS_ERR(chain)) - return PTR_ERR(chain); - } else { - chain = nf_tables_chain_lookup(table, name); - if (IS_ERR(chain)) { - if (PTR_ERR(chain) != -ENOENT) - return PTR_ERR(chain); - chain = NULL; - } - } - - if (chain != NULL) { - if (nlh->nlmsg_flags & NLM_F_EXCL) - return -EEXIST; - if (nlh->nlmsg_flags & NLM_F_REPLACE) - return -EOPNOTSUPP; - - if (nla[NFTA_CHAIN_HANDLE] && name && - !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]))) - return -EEXIST; - - if (nla[NFTA_CHAIN_POLICY]) { - if (!(chain->flags & NFT_BASE_CHAIN)) - return -EOPNOTSUPP; - - err = nf_tables_chain_policy(nft_base_chain(chain), - nla[NFTA_CHAIN_POLICY]); - if (err < 0) - return err; - } - - if (nla[NFTA_CHAIN_COUNTERS]) { - if (!(chain->flags & NFT_BASE_CHAIN)) - return -EOPNOTSUPP; - - err = nf_tables_counters(nft_base_chain(chain), - nla[NFTA_CHAIN_COUNTERS]); - if (err < 0) - return err; - } - - if (nla[NFTA_CHAIN_HANDLE] && name) - nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); - - goto notify; - } - - if (nla[NFTA_CHAIN_HOOK]) { - struct nf_hook_ops *ops; - nf_hookfn *hookfn; - u32 hooknum; - int type = NFT_CHAIN_T_DEFAULT; - - if (nla[NFTA_CHAIN_TYPE]) { - type = nf_tables_chain_type_lookup(afi, - nla[NFTA_CHAIN_TYPE], - create); - if (type < 0) - return -ENOENT; - } - - err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK], - nft_hook_policy); - if (err < 0) - return err; - if (ha[NFTA_HOOK_HOOKNUM] == NULL || - ha[NFTA_HOOK_PRIORITY] == NULL) - return -EINVAL; - - hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); - if (hooknum >= afi->nhooks) - return -EINVAL; - - hookfn = chain_type[family][type]->fn[hooknum]; - if (hookfn == NULL) - return -EOPNOTSUPP; - - basechain = kzalloc(sizeof(*basechain), GFP_KERNEL); - if (basechain == NULL) - return -ENOMEM; - - basechain->type = type; - chain = &basechain->chain; - - ops = &basechain->ops; - ops->pf = family; - ops->owner = afi->owner; - ops->hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); - ops->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); - ops->priv = chain; - ops->hook = hookfn; - if (afi->hooks[ops->hooknum]) - ops->hook = afi->hooks[ops->hooknum]; - - chain->flags |= NFT_BASE_CHAIN; - - if (nla[NFTA_CHAIN_POLICY]) { - err = nf_tables_chain_policy(basechain, - nla[NFTA_CHAIN_POLICY]); - if (err < 0) { - free_percpu(basechain->stats); - kfree(basechain); - return err; - } - } else - basechain->policy = NF_ACCEPT; - - if (nla[NFTA_CHAIN_COUNTERS]) { - err = nf_tables_counters(basechain, - nla[NFTA_CHAIN_COUNTERS]); - if (err < 0) { - free_percpu(basechain->stats); - kfree(basechain); - return err; - } - } else { - struct nft_stats __percpu *newstats; - - newstats = alloc_percpu(struct nft_stats); - if (newstats == NULL) - return -ENOMEM; - - rcu_assign_pointer(nft_base_chain(chain)->stats, - newstats); - } - } else { - chain = kzalloc(sizeof(*chain), GFP_KERNEL); - if (chain == NULL) - return -ENOMEM; - } - - INIT_LIST_HEAD(&chain->rules); - chain->handle = nf_tables_alloc_handle(table); - chain->net = net; - chain->table = table; - nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); - - if (!(table->flags & NFT_TABLE_F_DORMANT) && - chain->flags & NFT_BASE_CHAIN) { - err = nf_register_hook(&nft_base_chain(chain)->ops); - if (err < 0) { - free_percpu(basechain->stats); - kfree(basechain); - return err; - } - } - list_add_tail(&chain->list, &table->chains); - table->use++; -notify: - nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, - family); - return 0; -} - -static void nf_tables_rcu_chain_destroy(struct rcu_head *head) -{ - struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head); - - BUG_ON(chain->use > 0); - - if (chain->flags & NFT_BASE_CHAIN) { - free_percpu(nft_base_chain(chain)->stats); - kfree(nft_base_chain(chain)); - } else - kfree(chain); -} - -static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - struct nft_table *table; - struct nft_chain *chain; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - - afi = nf_tables_afinfo_lookup(net, family, false); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - - chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); - if (IS_ERR(chain)) - return PTR_ERR(chain); - - if (!list_empty(&chain->rules)) - return -EBUSY; - - list_del(&chain->list); - table->use--; - - if (!(table->flags & NFT_TABLE_F_DORMANT) && - chain->flags & NFT_BASE_CHAIN) - nf_unregister_hook(&nft_base_chain(chain)->ops); - - nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN, - family); - - /* Make sure all rule references are gone before this is released */ - call_rcu(&chain->rcu_head, nf_tables_rcu_chain_destroy); - return 0; -} - -static void nft_ctx_init(struct nft_ctx *ctx, - const struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nft_af_info *afi, - const struct nft_table *table, - const struct nft_chain *chain, - const struct nlattr * const *nla) -{ - ctx->net = sock_net(skb->sk); - ctx->skb = skb; - ctx->nlh = nlh; - ctx->afi = afi; - ctx->table = table; - ctx->chain = chain; - ctx->nla = nla; -} - -/* - * Expressions - */ - -/** - * nft_register_expr - register nf_tables expr type - * @ops: expr type - * - * Registers the expr type for use with nf_tables. Returns zero on - * success or a negative errno code otherwise. - */ -int nft_register_expr(struct nft_expr_type *type) -{ - nfnl_lock(NFNL_SUBSYS_NFTABLES); - list_add_tail(&type->list, &nf_tables_expressions); - nfnl_unlock(NFNL_SUBSYS_NFTABLES); - return 0; -} -EXPORT_SYMBOL_GPL(nft_register_expr); - -/** - * nft_unregister_expr - unregister nf_tables expr type - * @ops: expr type - * - * Unregisters the expr typefor use with nf_tables. - */ -void nft_unregister_expr(struct nft_expr_type *type) -{ - nfnl_lock(NFNL_SUBSYS_NFTABLES); - list_del(&type->list); - nfnl_unlock(NFNL_SUBSYS_NFTABLES); -} -EXPORT_SYMBOL_GPL(nft_unregister_expr); - -static const struct nft_expr_type *__nft_expr_type_get(struct nlattr *nla) -{ - const struct nft_expr_type *type; - - list_for_each_entry(type, &nf_tables_expressions, list) { - if (!nla_strcmp(nla, type->name)) - return type; - } - return NULL; -} - -static const struct nft_expr_type *nft_expr_type_get(struct nlattr *nla) -{ - const struct nft_expr_type *type; - - if (nla == NULL) - return ERR_PTR(-EINVAL); - - type = __nft_expr_type_get(nla); - if (type != NULL && try_module_get(type->owner)) - return type; - -#ifdef CONFIG_MODULES - if (type == NULL) { - nfnl_unlock(NFNL_SUBSYS_NFTABLES); - request_module("nft-expr-%.*s", - nla_len(nla), (char *)nla_data(nla)); - nfnl_lock(NFNL_SUBSYS_NFTABLES); - if (__nft_expr_type_get(nla)) - return ERR_PTR(-EAGAIN); - } -#endif - return ERR_PTR(-ENOENT); -} - -static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = { - [NFTA_EXPR_NAME] = { .type = NLA_STRING }, - [NFTA_EXPR_DATA] = { .type = NLA_NESTED }, -}; - -static int nf_tables_fill_expr_info(struct sk_buff *skb, - const struct nft_expr *expr) -{ - if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name)) - goto nla_put_failure; - - if (expr->ops->dump) { - struct nlattr *data = nla_nest_start(skb, NFTA_EXPR_DATA); - if (data == NULL) - goto nla_put_failure; - if (expr->ops->dump(skb, expr) < 0) - goto nla_put_failure; - nla_nest_end(skb, data); - } - - return skb->len; - -nla_put_failure: - return -1; -}; - -struct nft_expr_info { - const struct nft_expr_ops *ops; - struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; -}; - -static int nf_tables_expr_parse(const struct nft_ctx *ctx, - const struct nlattr *nla, - struct nft_expr_info *info) -{ - const struct nft_expr_type *type; - const struct nft_expr_ops *ops; - struct nlattr *tb[NFTA_EXPR_MAX + 1]; - int err; - - err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla, nft_expr_policy); - if (err < 0) - return err; - - type = nft_expr_type_get(tb[NFTA_EXPR_NAME]); - if (IS_ERR(type)) - return PTR_ERR(type); - - if (tb[NFTA_EXPR_DATA]) { - err = nla_parse_nested(info->tb, type->maxattr, - tb[NFTA_EXPR_DATA], type->policy); - if (err < 0) - goto err1; - } else - memset(info->tb, 0, sizeof(info->tb[0]) * (type->maxattr + 1)); - - if (type->select_ops != NULL) { - ops = type->select_ops(ctx, - (const struct nlattr * const *)info->tb); - if (IS_ERR(ops)) { - err = PTR_ERR(ops); - goto err1; - } - } else - ops = type->ops; - - info->ops = ops; - return 0; - -err1: - module_put(type->owner); - return err; -} - -static int nf_tables_newexpr(const struct nft_ctx *ctx, - const struct nft_expr_info *info, - struct nft_expr *expr) -{ - const struct nft_expr_ops *ops = info->ops; - int err; - - expr->ops = ops; - if (ops->init) { - err = ops->init(ctx, expr, (const struct nlattr **)info->tb); - if (err < 0) - goto err1; - } - - return 0; - -err1: - expr->ops = NULL; - return err; -} - -static void nf_tables_expr_destroy(struct nft_expr *expr) -{ - if (expr->ops->destroy) - expr->ops->destroy(expr); - module_put(expr->ops->type->owner); -} - -/* - * Rules - */ - -static struct nft_rule *__nf_tables_rule_lookup(const struct nft_chain *chain, - u64 handle) -{ - struct nft_rule *rule; - - // FIXME: this sucks - list_for_each_entry(rule, &chain->rules, list) { - if (handle == rule->handle) - return rule; - } - - return ERR_PTR(-ENOENT); -} - -static struct nft_rule *nf_tables_rule_lookup(const struct nft_chain *chain, - const struct nlattr *nla) -{ - if (nla == NULL) - return ERR_PTR(-EINVAL); - - return __nf_tables_rule_lookup(chain, be64_to_cpu(nla_get_be64(nla))); -} - -static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = { - [NFTA_RULE_TABLE] = { .type = NLA_STRING }, - [NFTA_RULE_CHAIN] = { .type = NLA_STRING, - .len = NFT_CHAIN_MAXNAMELEN - 1 }, - [NFTA_RULE_HANDLE] = { .type = NLA_U64 }, - [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED }, - [NFTA_RULE_COMPAT] = { .type = NLA_NESTED }, - [NFTA_RULE_POSITION] = { .type = NLA_U64 }, -}; - -static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, - int event, u32 flags, int family, - const struct nft_table *table, - const struct nft_chain *chain, - const struct nft_rule *rule) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - const struct nft_expr *expr, *next; - struct nlattr *list; - const struct nft_rule *prule; - int type = event | NFNL_SUBSYS_NFTABLES << 8; - - nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), - flags); - if (nlh == NULL) - goto nla_put_failure; - - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = family; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (nla_put_string(skb, NFTA_RULE_TABLE, table->name)) - goto nla_put_failure; - if (nla_put_string(skb, NFTA_RULE_CHAIN, chain->name)) - goto nla_put_failure; - if (nla_put_be64(skb, NFTA_RULE_HANDLE, cpu_to_be64(rule->handle))) - goto nla_put_failure; - - if ((event != NFT_MSG_DELRULE) && (rule->list.prev != &chain->rules)) { - prule = list_entry(rule->list.prev, struct nft_rule, list); - if (nla_put_be64(skb, NFTA_RULE_POSITION, - cpu_to_be64(prule->handle))) - goto nla_put_failure; - } - - list = nla_nest_start(skb, NFTA_RULE_EXPRESSIONS); - if (list == NULL) - goto nla_put_failure; - nft_rule_for_each_expr(expr, next, rule) { - struct nlattr *elem = nla_nest_start(skb, NFTA_LIST_ELEM); - if (elem == NULL) - goto nla_put_failure; - if (nf_tables_fill_expr_info(skb, expr) < 0) - goto nla_put_failure; - nla_nest_end(skb, elem); - } - nla_nest_end(skb, list); - - return nlmsg_end(skb, nlh); - -nla_put_failure: - nlmsg_trim(skb, nlh); - return -1; -} - -static int nf_tables_rule_notify(const struct sk_buff *oskb, - const struct nlmsghdr *nlh, - const struct nft_table *table, - const struct nft_chain *chain, - const struct nft_rule *rule, - int event, u32 flags, int family) -{ - struct sk_buff *skb; - u32 portid = NETLINK_CB(oskb).portid; - struct net *net = oskb ? sock_net(oskb->sk) : &init_net; - u32 seq = nlh->nlmsg_seq; - bool report; - int err; - - report = nlmsg_report(nlh); - if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) - return 0; - - err = -ENOBUFS; - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (skb == NULL) - goto err; - - err = nf_tables_fill_rule_info(skb, portid, seq, event, flags, - family, table, chain, rule); - if (err < 0) { - kfree_skb(skb); - goto err; - } - - err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); -err: - if (err < 0) - nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); - return err; -} - -static inline bool -nft_rule_is_active(struct net *net, const struct nft_rule *rule) -{ - return (rule->genmask & (1 << net->nft.gencursor)) == 0; -} - -static inline int gencursor_next(struct net *net) -{ - return net->nft.gencursor+1 == 1 ? 1 : 0; -} - -static inline int -nft_rule_is_active_next(struct net *net, const struct nft_rule *rule) -{ - return (rule->genmask & (1 << gencursor_next(net))) == 0; -} - -static inline void -nft_rule_activate_next(struct net *net, struct nft_rule *rule) -{ - /* Now inactive, will be active in the future */ - rule->genmask = (1 << net->nft.gencursor); -} - -static inline void -nft_rule_disactivate_next(struct net *net, struct nft_rule *rule) -{ - rule->genmask = (1 << gencursor_next(net)); -} - -static inline void nft_rule_clear(struct net *net, struct nft_rule *rule) -{ - rule->genmask = 0; -} - -static int nf_tables_dump_rules(struct sk_buff *skb, - struct netlink_callback *cb) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); - const struct nft_af_info *afi; - const struct nft_table *table; - const struct nft_chain *chain; - const struct nft_rule *rule; - unsigned int idx = 0, s_idx = cb->args[0]; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - u8 genctr = ACCESS_ONCE(net->nft.genctr); - u8 gencursor = ACCESS_ONCE(net->nft.gencursor); - - list_for_each_entry(afi, &net->nft.af_info, list) { - if (family != NFPROTO_UNSPEC && family != afi->family) - continue; - - list_for_each_entry(table, &afi->tables, list) { - list_for_each_entry(chain, &table->chains, list) { - list_for_each_entry(rule, &chain->rules, list) { - if (!nft_rule_is_active(net, rule)) - goto cont; - if (idx < s_idx) - goto cont; - if (idx > s_idx) - memset(&cb->args[1], 0, - sizeof(cb->args) - sizeof(cb->args[0])); - if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, - NFT_MSG_NEWRULE, - NLM_F_MULTI | NLM_F_APPEND, - afi->family, table, chain, rule) < 0) - goto done; -cont: - idx++; - } - } - } - } -done: - /* Invalidate this dump, a transition to the new generation happened */ - if (gencursor != net->nft.gencursor || genctr != net->nft.genctr) - return -EBUSY; - - cb->args[0] = idx; - return skb->len; -} - -static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - const struct nft_table *table; - const struct nft_chain *chain; - const struct nft_rule *rule; - struct sk_buff *skb2; - struct net *net = sock_net(skb->sk); - int family = nfmsg->nfgen_family; - int err; - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .dump = nf_tables_dump_rules, - }; - return netlink_dump_start(nlsk, skb, nlh, &c); - } - - afi = nf_tables_afinfo_lookup(net, family, false); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - - chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); - if (IS_ERR(chain)) - return PTR_ERR(chain); - - rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); - if (IS_ERR(rule)) - return PTR_ERR(rule); - - skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb2) - return -ENOMEM; - - err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0, - family, table, chain, rule); - if (err < 0) - goto err; - - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); - -err: - kfree_skb(skb2); - return err; -} - -static void nf_tables_rcu_rule_destroy(struct rcu_head *head) -{ - struct nft_rule *rule = container_of(head, struct nft_rule, rcu_head); - struct nft_expr *expr; - - /* - * Careful: some expressions might not be initialized in case this - * is called on error from nf_tables_newrule(). - */ - expr = nft_expr_first(rule); - while (expr->ops && expr != nft_expr_last(rule)) { - nf_tables_expr_destroy(expr); - expr = nft_expr_next(expr); - } - kfree(rule); -} - -static void nf_tables_rule_destroy(struct nft_rule *rule) -{ - call_rcu(&rule->rcu_head, nf_tables_rcu_rule_destroy); -} - -#define NFT_RULE_MAXEXPRS 128 - -static struct nft_expr_info *info; - -static struct nft_rule_trans * -nf_tables_trans_add(struct nft_rule *rule, const struct nft_ctx *ctx) -{ - struct nft_rule_trans *rupd; - - rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL); - if (rupd == NULL) - return NULL; - - rupd->chain = ctx->chain; - rupd->table = ctx->table; - rupd->rule = rule; - rupd->family = ctx->afi->family; - rupd->nlh = ctx->nlh; - list_add_tail(&rupd->list, &ctx->net->nft.commit_list); - - return rupd; -} - -static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - struct net *net = sock_net(skb->sk); - struct nft_table *table; - struct nft_chain *chain; - struct nft_rule *rule, *old_rule = NULL; - struct nft_rule_trans *repl = NULL; - struct nft_expr *expr; - struct nft_ctx ctx; - struct nlattr *tmp; - unsigned int size, i, n; - int err, rem; - bool create; - u64 handle, pos_handle; - - create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; - - afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - - chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); - if (IS_ERR(chain)) - return PTR_ERR(chain); - - if (nla[NFTA_RULE_HANDLE]) { - handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE])); - rule = __nf_tables_rule_lookup(chain, handle); - if (IS_ERR(rule)) - return PTR_ERR(rule); - - if (nlh->nlmsg_flags & NLM_F_EXCL) - return -EEXIST; - if (nlh->nlmsg_flags & NLM_F_REPLACE) - old_rule = rule; - else - return -EOPNOTSUPP; - } else { - if (!create || nlh->nlmsg_flags & NLM_F_REPLACE) - return -EINVAL; - handle = nf_tables_alloc_handle(table); - } - - if (nla[NFTA_RULE_POSITION]) { - if (!(nlh->nlmsg_flags & NLM_F_CREATE)) - return -EOPNOTSUPP; - - pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION])); - old_rule = __nf_tables_rule_lookup(chain, pos_handle); - if (IS_ERR(old_rule)) - return PTR_ERR(old_rule); - } - - nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); - - n = 0; - size = 0; - if (nla[NFTA_RULE_EXPRESSIONS]) { - nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) { - err = -EINVAL; - if (nla_type(tmp) != NFTA_LIST_ELEM) - goto err1; - if (n == NFT_RULE_MAXEXPRS) - goto err1; - err = nf_tables_expr_parse(&ctx, tmp, &info[n]); - if (err < 0) - goto err1; - size += info[n].ops->size; - n++; - } - } - - err = -ENOMEM; - rule = kzalloc(sizeof(*rule) + size, GFP_KERNEL); - if (rule == NULL) - goto err1; - - nft_rule_activate_next(net, rule); - - rule->handle = handle; - rule->dlen = size; - - expr = nft_expr_first(rule); - for (i = 0; i < n; i++) { - err = nf_tables_newexpr(&ctx, &info[i], expr); - if (err < 0) - goto err2; - info[i].ops = NULL; - expr = nft_expr_next(expr); - } - - if (nlh->nlmsg_flags & NLM_F_REPLACE) { - if (nft_rule_is_active_next(net, old_rule)) { - repl = nf_tables_trans_add(old_rule, &ctx); - if (repl == NULL) { - err = -ENOMEM; - goto err2; - } - nft_rule_disactivate_next(net, old_rule); - list_add_tail(&rule->list, &old_rule->list); - } else { - err = -ENOENT; - goto err2; - } - } else if (nlh->nlmsg_flags & NLM_F_APPEND) - if (old_rule) - list_add_rcu(&rule->list, &old_rule->list); - else - list_add_tail_rcu(&rule->list, &chain->rules); - else { - if (old_rule) - list_add_tail_rcu(&rule->list, &old_rule->list); - else - list_add_rcu(&rule->list, &chain->rules); - } - - if (nf_tables_trans_add(rule, &ctx) == NULL) { - err = -ENOMEM; - goto err3; - } - return 0; - -err3: - list_del_rcu(&rule->list); - if (repl) { - list_del_rcu(&repl->rule->list); - list_del(&repl->list); - nft_rule_clear(net, repl->rule); - kfree(repl); - } -err2: - nf_tables_rule_destroy(rule); -err1: - for (i = 0; i < n; i++) { - if (info[i].ops != NULL) - module_put(info[i].ops->type->owner); - } - return err; -} - -static int -nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule) -{ - /* You cannot delete the same rule twice */ - if (nft_rule_is_active_next(ctx->net, rule)) { - if (nf_tables_trans_add(rule, ctx) == NULL) - return -ENOMEM; - nft_rule_disactivate_next(ctx->net, rule); - return 0; - } - return -ENOENT; -} - -static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - struct net *net = sock_net(skb->sk); - const struct nft_table *table; - struct nft_chain *chain; - struct nft_rule *rule, *tmp; - int family = nfmsg->nfgen_family, err = 0; - struct nft_ctx ctx; - - afi = nf_tables_afinfo_lookup(net, family, false); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - - chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); - if (IS_ERR(chain)) - return PTR_ERR(chain); - - nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); - - if (nla[NFTA_RULE_HANDLE]) { - rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); - if (IS_ERR(rule)) - return PTR_ERR(rule); - - err = nf_tables_delrule_one(&ctx, rule); - } else { - /* Remove all rules in this chain */ - list_for_each_entry_safe(rule, tmp, &chain->rules, list) { - err = nf_tables_delrule_one(&ctx, rule); - if (err < 0) - break; - } - } - - return err; -} - -static int nf_tables_commit(struct sk_buff *skb) -{ - struct net *net = sock_net(skb->sk); - struct nft_rule_trans *rupd, *tmp; - - /* Bump generation counter, invalidate any dump in progress */ - net->nft.genctr++; - - /* A new generation has just started */ - net->nft.gencursor = gencursor_next(net); - - /* Make sure all packets have left the previous generation before - * purging old rules. - */ - synchronize_rcu(); - - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - /* Delete this rule from the dirty list */ - list_del(&rupd->list); - - /* This rule was inactive in the past and just became active. - * Clear the next bit of the genmask since its meaning has - * changed, now it is the future. - */ - if (nft_rule_is_active(net, rupd->rule)) { - nft_rule_clear(net, rupd->rule); - nf_tables_rule_notify(skb, rupd->nlh, rupd->table, - rupd->chain, rupd->rule, - NFT_MSG_NEWRULE, 0, - rupd->family); - kfree(rupd); - continue; - } - - /* This rule is in the past, get rid of it */ - list_del_rcu(&rupd->rule->list); - nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain, - rupd->rule, NFT_MSG_DELRULE, 0, - rupd->family); - nf_tables_rule_destroy(rupd->rule); - kfree(rupd); - } - - return 0; -} - -static int nf_tables_abort(struct sk_buff *skb) -{ - struct net *net = sock_net(skb->sk); - struct nft_rule_trans *rupd, *tmp; - - list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - /* Delete all rules from the dirty list */ - list_del(&rupd->list); - - if (!nft_rule_is_active_next(net, rupd->rule)) { - nft_rule_clear(net, rupd->rule); - kfree(rupd); - continue; - } - - /* This rule is inactive, get rid of it */ - list_del_rcu(&rupd->rule->list); - nf_tables_rule_destroy(rupd->rule); - kfree(rupd); - } - return 0; -} - -/* - * Sets - */ - -static LIST_HEAD(nf_tables_set_ops); - -int nft_register_set(struct nft_set_ops *ops) -{ - nfnl_lock(NFNL_SUBSYS_NFTABLES); - list_add_tail(&ops->list, &nf_tables_set_ops); - nfnl_unlock(NFNL_SUBSYS_NFTABLES); - return 0; -} -EXPORT_SYMBOL_GPL(nft_register_set); - -void nft_unregister_set(struct nft_set_ops *ops) -{ - nfnl_lock(NFNL_SUBSYS_NFTABLES); - list_del(&ops->list); - nfnl_unlock(NFNL_SUBSYS_NFTABLES); -} -EXPORT_SYMBOL_GPL(nft_unregister_set); - -static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const nla[]) -{ - const struct nft_set_ops *ops; - u32 features; - -#ifdef CONFIG_MODULES - if (list_empty(&nf_tables_set_ops)) { - nfnl_unlock(NFNL_SUBSYS_NFTABLES); - request_module("nft-set"); - nfnl_lock(NFNL_SUBSYS_NFTABLES); - if (!list_empty(&nf_tables_set_ops)) - return ERR_PTR(-EAGAIN); - } -#endif - features = 0; - if (nla[NFTA_SET_FLAGS] != NULL) { - features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS])); - features &= NFT_SET_INTERVAL | NFT_SET_MAP; - } - - // FIXME: implement selection properly - list_for_each_entry(ops, &nf_tables_set_ops, list) { - if ((ops->features & features) != features) - continue; - if (!try_module_get(ops->owner)) - continue; - return ops; - } - - return ERR_PTR(-EOPNOTSUPP); -} - -static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { - [NFTA_SET_TABLE] = { .type = NLA_STRING }, - [NFTA_SET_NAME] = { .type = NLA_STRING }, - [NFTA_SET_FLAGS] = { .type = NLA_U32 }, - [NFTA_SET_KEY_TYPE] = { .type = NLA_U32 }, - [NFTA_SET_KEY_LEN] = { .type = NLA_U32 }, - [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 }, - [NFTA_SET_DATA_LEN] = { .type = NLA_U32 }, -}; - -static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, - const struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - struct net *net = sock_net(skb->sk); - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - const struct nft_table *table = NULL; - - afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - if (nla[NFTA_SET_TABLE] != NULL) { - table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - } - - nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); - return 0; -} - -struct nft_set *nf_tables_set_lookup(const struct nft_table *table, - const struct nlattr *nla) -{ - struct nft_set *set; - - if (nla == NULL) - return ERR_PTR(-EINVAL); - - list_for_each_entry(set, &table->sets, list) { - if (!nla_strcmp(nla, set->name)) - return set; - } - return ERR_PTR(-ENOENT); -} - -static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, - const char *name) -{ - const struct nft_set *i; - const char *p; - unsigned long *inuse; - unsigned int n = 0; - - p = strnchr(name, IFNAMSIZ, '%'); - if (p != NULL) { - if (p[1] != 'd' || strchr(p + 2, '%')) - return -EINVAL; - - inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL); - if (inuse == NULL) - return -ENOMEM; - - list_for_each_entry(i, &ctx->table->sets, list) { - if (!sscanf(i->name, name, &n)) - continue; - if (n < 0 || n > BITS_PER_LONG * PAGE_SIZE) - continue; - set_bit(n, inuse); - } - - n = find_first_zero_bit(inuse, BITS_PER_LONG * PAGE_SIZE); - free_page((unsigned long)inuse); - } - - snprintf(set->name, sizeof(set->name), name, n); - list_for_each_entry(i, &ctx->table->sets, list) { - if (!strcmp(set->name, i->name)) - return -ENFILE; - } - return 0; -} - -static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, - const struct nft_set *set, u16 event, u16 flags) -{ - struct nfgenmsg *nfmsg; - struct nlmsghdr *nlh; - u32 portid = NETLINK_CB(ctx->skb).portid; - u32 seq = ctx->nlh->nlmsg_seq; - - event |= NFNL_SUBSYS_NFTABLES << 8; - nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), - flags); - if (nlh == NULL) - goto nla_put_failure; - - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = ctx->afi->family; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) - goto nla_put_failure; - if (nla_put_string(skb, NFTA_SET_NAME, set->name)) - goto nla_put_failure; - if (set->flags != 0) - if (nla_put_be32(skb, NFTA_SET_FLAGS, htonl(set->flags))) - goto nla_put_failure; - - if (nla_put_be32(skb, NFTA_SET_KEY_TYPE, htonl(set->ktype))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_SET_KEY_LEN, htonl(set->klen))) - goto nla_put_failure; - if (set->flags & NFT_SET_MAP) { - if (nla_put_be32(skb, NFTA_SET_DATA_TYPE, htonl(set->dtype))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen))) - goto nla_put_failure; - } - - return nlmsg_end(skb, nlh); - -nla_put_failure: - nlmsg_trim(skb, nlh); - return -1; -} - -static int nf_tables_set_notify(const struct nft_ctx *ctx, - const struct nft_set *set, - int event) -{ - struct sk_buff *skb; - u32 portid = NETLINK_CB(ctx->skb).portid; - bool report; - int err; - - report = nlmsg_report(ctx->nlh); - if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) - return 0; - - err = -ENOBUFS; - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (skb == NULL) - goto err; - - err = nf_tables_fill_set(skb, ctx, set, event, 0); - if (err < 0) { - kfree_skb(skb); - goto err; - } - - err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report, - GFP_KERNEL); -err: - if (err < 0) - nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err); - return err; -} - -static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb, - struct netlink_callback *cb) -{ - const struct nft_set *set; - unsigned int idx = 0, s_idx = cb->args[0]; - - if (cb->args[1]) - return skb->len; - - list_for_each_entry(set, &ctx->table->sets, list) { - if (idx < s_idx) - goto cont; - if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET, - NLM_F_MULTI) < 0) { - cb->args[0] = idx; - goto done; - } -cont: - idx++; - } - cb->args[1] = 1; -done: - return skb->len; -} - -static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, - struct netlink_callback *cb) -{ - const struct nft_set *set; - unsigned int idx = 0, s_idx = cb->args[0]; - struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; - - if (cb->args[1]) - return skb->len; - - list_for_each_entry(table, &ctx->afi->tables, list) { - if (cur_table && cur_table != table) - continue; - - ctx->table = table; - list_for_each_entry(set, &ctx->table->sets, list) { - if (idx < s_idx) - goto cont; - if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET, - NLM_F_MULTI) < 0) { - cb->args[0] = idx; - cb->args[2] = (unsigned long) table; - goto done; - } -cont: - idx++; - } - } - cb->args[1] = 1; -done: - return skb->len; -} - -static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); - struct nlattr *nla[NFTA_SET_MAX + 1]; - struct nft_ctx ctx; - int err, ret; - - err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_MAX, - nft_set_policy); - if (err < 0) - return err; - - err = nft_ctx_init_from_setattr(&ctx, cb->skb, cb->nlh, (void *)nla); - if (err < 0) - return err; - - if (ctx.table == NULL) - ret = nf_tables_dump_sets_all(&ctx, skb, cb); - else - ret = nf_tables_dump_sets_table(&ctx, skb, cb); - - return ret; -} - -static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nft_set *set; - struct nft_ctx ctx; - struct sk_buff *skb2; - int err; - - /* Verify existance before starting dump */ - err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla); - if (err < 0) - return err; - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .dump = nf_tables_dump_sets, - }; - return netlink_dump_start(nlsk, skb, nlh, &c); - } - - set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); - if (IS_ERR(set)) - return PTR_ERR(set); - - skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (skb2 == NULL) - return -ENOMEM; - - err = nf_tables_fill_set(skb2, &ctx, set, NFT_MSG_NEWSET, 0); - if (err < 0) - goto err; - - return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); - -err: - kfree_skb(skb2); - return err; -} - -static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_set_ops *ops; - const struct nft_af_info *afi; - struct net *net = sock_net(skb->sk); - struct nft_table *table; - struct nft_set *set; - struct nft_ctx ctx; - char name[IFNAMSIZ]; - unsigned int size; - bool create; - u32 ktype, klen, dlen, dtype, flags; - int err; - - if (nla[NFTA_SET_TABLE] == NULL || - nla[NFTA_SET_NAME] == NULL || - nla[NFTA_SET_KEY_LEN] == NULL) - return -EINVAL; - - ktype = NFT_DATA_VALUE; - if (nla[NFTA_SET_KEY_TYPE] != NULL) { - ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE])); - if ((ktype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK) - return -EINVAL; - } - - klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); - if (klen == 0 || klen > FIELD_SIZEOF(struct nft_data, data)) - return -EINVAL; - - flags = 0; - if (nla[NFTA_SET_FLAGS] != NULL) { - flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS])); - if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | - NFT_SET_INTERVAL | NFT_SET_MAP)) - return -EINVAL; - } - - dtype = 0; - dlen = 0; - if (nla[NFTA_SET_DATA_TYPE] != NULL) { - if (!(flags & NFT_SET_MAP)) - return -EINVAL; - - dtype = ntohl(nla_get_be32(nla[NFTA_SET_DATA_TYPE])); - if ((dtype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK && - dtype != NFT_DATA_VERDICT) - return -EINVAL; - - if (dtype != NFT_DATA_VERDICT) { - if (nla[NFTA_SET_DATA_LEN] == NULL) - return -EINVAL; - dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); - if (dlen == 0 || - dlen > FIELD_SIZEOF(struct nft_data, data)) - return -EINVAL; - } else - dlen = sizeof(struct nft_data); - } else if (flags & NFT_SET_MAP) - return -EINVAL; - - create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; - - afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - - nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); - - set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]); - if (IS_ERR(set)) { - if (PTR_ERR(set) != -ENOENT) - return PTR_ERR(set); - set = NULL; - } - - if (set != NULL) { - if (nlh->nlmsg_flags & NLM_F_EXCL) - return -EEXIST; - if (nlh->nlmsg_flags & NLM_F_REPLACE) - return -EOPNOTSUPP; - return 0; - } - - if (!(nlh->nlmsg_flags & NLM_F_CREATE)) - return -ENOENT; - - ops = nft_select_set_ops(nla); - if (IS_ERR(ops)) - return PTR_ERR(ops); - - size = 0; - if (ops->privsize != NULL) - size = ops->privsize(nla); - - err = -ENOMEM; - set = kzalloc(sizeof(*set) + size, GFP_KERNEL); - if (set == NULL) - goto err1; - - nla_strlcpy(name, nla[NFTA_SET_NAME], sizeof(set->name)); - err = nf_tables_set_alloc_name(&ctx, set, name); - if (err < 0) - goto err2; - - INIT_LIST_HEAD(&set->bindings); - set->ops = ops; - set->ktype = ktype; - set->klen = klen; - set->dtype = dtype; - set->dlen = dlen; - set->flags = flags; - - err = ops->init(set, nla); - if (err < 0) - goto err2; - - list_add_tail(&set->list, &table->sets); - nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET); - return 0; - -err2: - kfree(set); -err1: - module_put(ops->owner); - return err; -} - -static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) -{ - list_del(&set->list); - if (!(set->flags & NFT_SET_ANONYMOUS)) - nf_tables_set_notify(ctx, set, NFT_MSG_DELSET); - - set->ops->destroy(set); - module_put(set->ops->owner); - kfree(set); -} - -static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - struct nft_set *set; - struct nft_ctx ctx; - int err; - - if (nla[NFTA_SET_TABLE] == NULL) - return -EINVAL; - - err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla); - if (err < 0) - return err; - - set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); - if (IS_ERR(set)) - return PTR_ERR(set); - if (!list_empty(&set->bindings)) - return -EBUSY; - - nf_tables_set_destroy(&ctx, set); - return 0; -} - -static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx, - const struct nft_set *set, - const struct nft_set_iter *iter, - const struct nft_set_elem *elem) -{ - enum nft_registers dreg; - - dreg = nft_type_to_reg(set->dtype); - return nft_validate_data_load(ctx, dreg, &elem->data, set->dtype); -} - -int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set, - struct nft_set_binding *binding) -{ - struct nft_set_binding *i; - struct nft_set_iter iter; - - if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS) - return -EBUSY; - - if (set->flags & NFT_SET_MAP) { - /* If the set is already bound to the same chain all - * jumps are already validated for that chain. - */ - list_for_each_entry(i, &set->bindings, list) { - if (i->chain == binding->chain) - goto bind; - } - - iter.skip = 0; - iter.count = 0; - iter.err = 0; - iter.fn = nf_tables_bind_check_setelem; - - set->ops->walk(ctx, set, &iter); - if (iter.err < 0) { - /* Destroy anonymous sets if binding fails */ - if (set->flags & NFT_SET_ANONYMOUS) - nf_tables_set_destroy(ctx, set); - - return iter.err; - } - } -bind: - binding->chain = ctx->chain; - list_add_tail(&binding->list, &set->bindings); - return 0; -} - -void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, - struct nft_set_binding *binding) -{ - list_del(&binding->list); - - if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS) - nf_tables_set_destroy(ctx, set); -} - -/* - * Set elements - */ - -static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { - [NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED }, - [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED }, - [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 }, -}; - -static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { - [NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING }, - [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING }, - [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED }, -}; - -static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, - const struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nfgenmsg *nfmsg = nlmsg_data(nlh); - const struct nft_af_info *afi; - const struct nft_table *table; - struct net *net = sock_net(skb->sk); - - afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); - if (IS_ERR(afi)) - return PTR_ERR(afi); - - table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]); - if (IS_ERR(table)) - return PTR_ERR(table); - - nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); - return 0; -} - -static int nf_tables_fill_setelem(struct sk_buff *skb, - const struct nft_set *set, - const struct nft_set_elem *elem) -{ - unsigned char *b = skb_tail_pointer(skb); - struct nlattr *nest; - - nest = nla_nest_start(skb, NFTA_LIST_ELEM); - if (nest == NULL) - goto nla_put_failure; - - if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE, - set->klen) < 0) - goto nla_put_failure; - - if (set->flags & NFT_SET_MAP && - !(elem->flags & NFT_SET_ELEM_INTERVAL_END) && - nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data, - set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE, - set->dlen) < 0) - goto nla_put_failure; - - if (elem->flags != 0) - if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags))) - goto nla_put_failure; - - nla_nest_end(skb, nest); - return 0; - -nla_put_failure: - nlmsg_trim(skb, b); - return -EMSGSIZE; -} - -struct nft_set_dump_args { - const struct netlink_callback *cb; - struct nft_set_iter iter; - struct sk_buff *skb; -}; - -static int nf_tables_dump_setelem(const struct nft_ctx *ctx, - const struct nft_set *set, - const struct nft_set_iter *iter, - const struct nft_set_elem *elem) -{ - struct nft_set_dump_args *args; - - args = container_of(iter, struct nft_set_dump_args, iter); - return nf_tables_fill_setelem(args->skb, set, elem); -} - -static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) -{ - const struct nft_set *set; - struct nft_set_dump_args args; - struct nft_ctx ctx; - struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1]; - struct nfgenmsg *nfmsg; - struct nlmsghdr *nlh; - struct nlattr *nest; - u32 portid, seq; - int event, err; - - nfmsg = nlmsg_data(cb->nlh); - err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_ELEM_LIST_MAX, - nft_set_elem_list_policy); - if (err < 0) - return err; - - err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla); - if (err < 0) - return err; - - set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); - - event = NFT_MSG_NEWSETELEM; - event |= NFNL_SUBSYS_NFTABLES << 8; - portid = NETLINK_CB(cb->skb).portid; - seq = cb->nlh->nlmsg_seq; - - nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), - NLM_F_MULTI); - if (nlh == NULL) - goto nla_put_failure; - - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = NFPROTO_UNSPEC; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name)) - goto nla_put_failure; - if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name)) - goto nla_put_failure; - - nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS); - if (nest == NULL) - goto nla_put_failure; - - args.cb = cb; - args.skb = skb; - args.iter.skip = cb->args[0]; - args.iter.count = 0; - args.iter.err = 0; - args.iter.fn = nf_tables_dump_setelem; - set->ops->walk(&ctx, set, &args.iter); - - nla_nest_end(skb, nest); - nlmsg_end(skb, nlh); - - if (args.iter.err && args.iter.err != -EMSGSIZE) - return args.iter.err; - if (args.iter.count == cb->args[0]) - return 0; - - cb->args[0] = args.iter.count; - return skb->len; - -nla_put_failure: - return -ENOSPC; -} - -static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nft_set *set; - struct nft_ctx ctx; - int err; - - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); - if (err < 0) - return err; - - set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .dump = nf_tables_dump_set, - }; - return netlink_dump_start(nlsk, skb, nlh, &c); - } - return -EOPNOTSUPP; -} - -static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, - const struct nlattr *attr) -{ - struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; - struct nft_data_desc d1, d2; - struct nft_set_elem elem; - struct nft_set_binding *binding; - enum nft_registers dreg; - int err; - - err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, - nft_set_elem_policy); - if (err < 0) - return err; - - if (nla[NFTA_SET_ELEM_KEY] == NULL) - return -EINVAL; - - elem.flags = 0; - if (nla[NFTA_SET_ELEM_FLAGS] != NULL) { - elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS])); - if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END) - return -EINVAL; - } - - if (set->flags & NFT_SET_MAP) { - if (nla[NFTA_SET_ELEM_DATA] == NULL && - !(elem.flags & NFT_SET_ELEM_INTERVAL_END)) - return -EINVAL; - } else { - if (nla[NFTA_SET_ELEM_DATA] != NULL) - return -EINVAL; - } - - err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]); - if (err < 0) - goto err1; - err = -EINVAL; - if (d1.type != NFT_DATA_VALUE || d1.len != set->klen) - goto err2; - - err = -EEXIST; - if (set->ops->get(set, &elem) == 0) - goto err2; - - if (nla[NFTA_SET_ELEM_DATA] != NULL) { - err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]); - if (err < 0) - goto err2; - - err = -EINVAL; - if (set->dtype != NFT_DATA_VERDICT && d2.len != set->dlen) - goto err3; - - dreg = nft_type_to_reg(set->dtype); - list_for_each_entry(binding, &set->bindings, list) { - struct nft_ctx bind_ctx = { - .afi = ctx->afi, - .table = ctx->table, - .chain = binding->chain, - }; - - err = nft_validate_data_load(&bind_ctx, dreg, - &elem.data, d2.type); - if (err < 0) - goto err3; - } - } - - err = set->ops->insert(set, &elem); - if (err < 0) - goto err3; - - return 0; - -err3: - if (nla[NFTA_SET_ELEM_DATA] != NULL) - nft_data_uninit(&elem.data, d2.type); -err2: - nft_data_uninit(&elem.key, d1.type); -err1: - return err; -} - -static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nlattr *attr; - struct nft_set *set; - struct nft_ctx ctx; - int rem, err; - - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); - if (err < 0) - return err; - - set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); - if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) - return -EBUSY; - - nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { - err = nft_add_set_elem(&ctx, set, attr); - if (err < 0) - return err; - } - return 0; -} - -static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, - const struct nlattr *attr) -{ - struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; - struct nft_data_desc desc; - struct nft_set_elem elem; - int err; - - err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, - nft_set_elem_policy); - if (err < 0) - goto err1; - - err = -EINVAL; - if (nla[NFTA_SET_ELEM_KEY] == NULL) - goto err1; - - err = nft_data_init(ctx, &elem.key, &desc, nla[NFTA_SET_ELEM_KEY]); - if (err < 0) - goto err1; - - err = -EINVAL; - if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) - goto err2; - - err = set->ops->get(set, &elem); - if (err < 0) - goto err2; - - set->ops->remove(set, &elem); - - nft_data_uninit(&elem.key, NFT_DATA_VALUE); - if (set->flags & NFT_SET_MAP) - nft_data_uninit(&elem.data, set->dtype); - -err2: - nft_data_uninit(&elem.key, desc.type); -err1: - return err; -} - -static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nla[]) -{ - const struct nlattr *attr; - struct nft_set *set; - struct nft_ctx ctx; - int rem, err; - - err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); - if (err < 0) - return err; - - set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); - if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) - return -EBUSY; - - nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { - err = nft_del_setelem(&ctx, set, attr); - if (err < 0) - return err; - } - return 0; -} - -static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { - [NFT_MSG_NEWTABLE] = { - .call = nf_tables_newtable, - .attr_count = NFTA_TABLE_MAX, - .policy = nft_table_policy, - }, - [NFT_MSG_GETTABLE] = { - .call = nf_tables_gettable, - .attr_count = NFTA_TABLE_MAX, - .policy = nft_table_policy, - }, - [NFT_MSG_DELTABLE] = { - .call = nf_tables_deltable, - .attr_count = NFTA_TABLE_MAX, - .policy = nft_table_policy, - }, - [NFT_MSG_NEWCHAIN] = { - .call = nf_tables_newchain, - .attr_count = NFTA_CHAIN_MAX, - .policy = nft_chain_policy, - }, - [NFT_MSG_GETCHAIN] = { - .call = nf_tables_getchain, - .attr_count = NFTA_CHAIN_MAX, - .policy = nft_chain_policy, - }, - [NFT_MSG_DELCHAIN] = { - .call = nf_tables_delchain, - .attr_count = NFTA_CHAIN_MAX, - .policy = nft_chain_policy, - }, - [NFT_MSG_NEWRULE] = { - .call_batch = nf_tables_newrule, - .attr_count = NFTA_RULE_MAX, - .policy = nft_rule_policy, - }, - [NFT_MSG_GETRULE] = { - .call = nf_tables_getrule, - .attr_count = NFTA_RULE_MAX, - .policy = nft_rule_policy, - }, - [NFT_MSG_DELRULE] = { - .call_batch = nf_tables_delrule, - .attr_count = NFTA_RULE_MAX, - .policy = nft_rule_policy, - }, - [NFT_MSG_NEWSET] = { - .call = nf_tables_newset, - .attr_count = NFTA_SET_MAX, - .policy = nft_set_policy, - }, - [NFT_MSG_GETSET] = { - .call = nf_tables_getset, - .attr_count = NFTA_SET_MAX, - .policy = nft_set_policy, - }, - [NFT_MSG_DELSET] = { - .call = nf_tables_delset, - .attr_count = NFTA_SET_MAX, - .policy = nft_set_policy, - }, - [NFT_MSG_NEWSETELEM] = { - .call = nf_tables_newsetelem, - .attr_count = NFTA_SET_ELEM_LIST_MAX, - .policy = nft_set_elem_list_policy, - }, - [NFT_MSG_GETSETELEM] = { - .call = nf_tables_getsetelem, - .attr_count = NFTA_SET_ELEM_LIST_MAX, - .policy = nft_set_elem_list_policy, - }, - [NFT_MSG_DELSETELEM] = { - .call = nf_tables_delsetelem, - .attr_count = NFTA_SET_ELEM_LIST_MAX, - .policy = nft_set_elem_list_policy, - }, -}; - -static const struct nfnetlink_subsystem nf_tables_subsys = { - .name = "nf_tables", - .subsys_id = NFNL_SUBSYS_NFTABLES, - .cb_count = NFT_MSG_MAX, - .cb = nf_tables_cb, - .commit = nf_tables_commit, - .abort = nf_tables_abort, -}; - -/* - * Loop detection - walk through the ruleset beginning at the destination chain - * of a new jump until either the source chain is reached (loop) or all - * reachable chains have been traversed. - * - * The loop check is performed whenever a new jump verdict is added to an - * expression or verdict map or a verdict map is bound to a new chain. - */ - -static int nf_tables_check_loops(const struct nft_ctx *ctx, - const struct nft_chain *chain); - -static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx, - const struct nft_set *set, - const struct nft_set_iter *iter, - const struct nft_set_elem *elem) -{ - switch (elem->data.verdict) { - case NFT_JUMP: - case NFT_GOTO: - return nf_tables_check_loops(ctx, elem->data.chain); - default: - return 0; - } -} - -static int nf_tables_check_loops(const struct nft_ctx *ctx, - const struct nft_chain *chain) -{ - const struct nft_rule *rule; - const struct nft_expr *expr, *last; - const struct nft_set *set; - struct nft_set_binding *binding; - struct nft_set_iter iter; - - if (ctx->chain == chain) - return -ELOOP; - - list_for_each_entry(rule, &chain->rules, list) { - nft_rule_for_each_expr(expr, last, rule) { - const struct nft_data *data = NULL; - int err; - - if (!expr->ops->validate) - continue; - - err = expr->ops->validate(ctx, expr, &data); - if (err < 0) - return err; - - if (data == NULL) - continue; - - switch (data->verdict) { - case NFT_JUMP: - case NFT_GOTO: - err = nf_tables_check_loops(ctx, data->chain); - if (err < 0) - return err; - default: - break; - } - } - } - - list_for_each_entry(set, &ctx->table->sets, list) { - if (!(set->flags & NFT_SET_MAP) || - set->dtype != NFT_DATA_VERDICT) - continue; - - list_for_each_entry(binding, &set->bindings, list) { - if (binding->chain != chain) - continue; - - iter.skip = 0; - iter.count = 0; - iter.err = 0; - iter.fn = nf_tables_loop_check_setelem; - - set->ops->walk(ctx, set, &iter); - if (iter.err < 0) - return iter.err; - } - } - - return 0; -} - -/** - * nft_validate_input_register - validate an expressions' input register - * - * @reg: the register number - * - * Validate that the input register is one of the general purpose - * registers. - */ -int nft_validate_input_register(enum nft_registers reg) -{ - if (reg <= NFT_REG_VERDICT) - return -EINVAL; - if (reg > NFT_REG_MAX) - return -ERANGE; - return 0; -} -EXPORT_SYMBOL_GPL(nft_validate_input_register); - -/** - * nft_validate_output_register - validate an expressions' output register - * - * @reg: the register number - * - * Validate that the output register is one of the general purpose - * registers or the verdict register. - */ -int nft_validate_output_register(enum nft_registers reg) -{ - if (reg < NFT_REG_VERDICT) - return -EINVAL; - if (reg > NFT_REG_MAX) - return -ERANGE; - return 0; -} -EXPORT_SYMBOL_GPL(nft_validate_output_register); - -/** - * nft_validate_data_load - validate an expressions' data load - * - * @ctx: context of the expression performing the load - * @reg: the destination register number - * @data: the data to load - * @type: the data type - * - * Validate that a data load uses the appropriate data type for - * the destination register. A value of NULL for the data means - * that its runtime gathered data, which is always of type - * NFT_DATA_VALUE. - */ -int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg, - const struct nft_data *data, - enum nft_data_types type) -{ - int err; - - switch (reg) { - case NFT_REG_VERDICT: - if (data == NULL || type != NFT_DATA_VERDICT) - return -EINVAL; - - if (data->verdict == NFT_GOTO || data->verdict == NFT_JUMP) { - err = nf_tables_check_loops(ctx, data->chain); - if (err < 0) - return err; - - if (ctx->chain->level + 1 > data->chain->level) { - if (ctx->chain->level + 1 == NFT_JUMP_STACK_SIZE) - return -EMLINK; - data->chain->level = ctx->chain->level + 1; - } - } - - return 0; - default: - if (data != NULL && type != NFT_DATA_VALUE) - return -EINVAL; - return 0; - } -} -EXPORT_SYMBOL_GPL(nft_validate_data_load); - -static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = { - [NFTA_VERDICT_CODE] = { .type = NLA_U32 }, - [NFTA_VERDICT_CHAIN] = { .type = NLA_STRING, - .len = NFT_CHAIN_MAXNAMELEN - 1 }, -}; - -static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, - struct nft_data_desc *desc, const struct nlattr *nla) -{ - struct nlattr *tb[NFTA_VERDICT_MAX + 1]; - struct nft_chain *chain; - int err; - - err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy); - if (err < 0) - return err; - - if (!tb[NFTA_VERDICT_CODE]) - return -EINVAL; - data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE])); - - switch (data->verdict) { - case NF_ACCEPT: - case NF_DROP: - case NF_QUEUE: - case NFT_CONTINUE: - case NFT_BREAK: - case NFT_RETURN: - desc->len = sizeof(data->verdict); - break; - case NFT_JUMP: - case NFT_GOTO: - if (!tb[NFTA_VERDICT_CHAIN]) - return -EINVAL; - chain = nf_tables_chain_lookup(ctx->table, - tb[NFTA_VERDICT_CHAIN]); - if (IS_ERR(chain)) - return PTR_ERR(chain); - if (chain->flags & NFT_BASE_CHAIN) - return -EOPNOTSUPP; - - chain->use++; - data->chain = chain; - desc->len = sizeof(data); - break; - default: - return -EINVAL; - } - - desc->type = NFT_DATA_VERDICT; - return 0; -} - -static void nft_verdict_uninit(const struct nft_data *data) -{ - switch (data->verdict) { - case NFT_JUMP: - case NFT_GOTO: - data->chain->use--; - break; - } -} - -static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data) -{ - struct nlattr *nest; - - nest = nla_nest_start(skb, NFTA_DATA_VERDICT); - if (!nest) - goto nla_put_failure; - - if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict))) - goto nla_put_failure; - - switch (data->verdict) { - case NFT_JUMP: - case NFT_GOTO: - if (nla_put_string(skb, NFTA_VERDICT_CHAIN, data->chain->name)) - goto nla_put_failure; - } - nla_nest_end(skb, nest); - return 0; - -nla_put_failure: - return -1; -} - -static int nft_value_init(const struct nft_ctx *ctx, struct nft_data *data, - struct nft_data_desc *desc, const struct nlattr *nla) -{ - unsigned int len; - - len = nla_len(nla); - if (len == 0) - return -EINVAL; - if (len > sizeof(data->data)) - return -EOVERFLOW; - - nla_memcpy(data->data, nla, sizeof(data->data)); - desc->type = NFT_DATA_VALUE; - desc->len = len; - return 0; -} - -static int nft_value_dump(struct sk_buff *skb, const struct nft_data *data, - unsigned int len) -{ - return nla_put(skb, NFTA_DATA_VALUE, len, data->data); -} - -static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { - [NFTA_DATA_VALUE] = { .type = NLA_BINARY, - .len = FIELD_SIZEOF(struct nft_data, data) }, - [NFTA_DATA_VERDICT] = { .type = NLA_NESTED }, -}; - -/** - * nft_data_init - parse nf_tables data netlink attributes - * - * @ctx: context of the expression using the data - * @data: destination struct nft_data - * @desc: data description - * @nla: netlink attribute containing data - * - * Parse the netlink data attributes and initialize a struct nft_data. - * The type and length of data are returned in the data description. - * - * The caller can indicate that it only wants to accept data of type - * NFT_DATA_VALUE by passing NULL for the ctx argument. - */ -int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, - struct nft_data_desc *desc, const struct nlattr *nla) -{ - struct nlattr *tb[NFTA_DATA_MAX + 1]; - int err; - - err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy); - if (err < 0) - return err; - - if (tb[NFTA_DATA_VALUE]) - return nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]); - if (tb[NFTA_DATA_VERDICT] && ctx != NULL) - return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]); - return -EINVAL; -} -EXPORT_SYMBOL_GPL(nft_data_init); - -/** - * nft_data_uninit - release a nft_data item - * - * @data: struct nft_data to release - * @type: type of data - * - * Release a nft_data item. NFT_DATA_VALUE types can be silently discarded, - * all others need to be released by calling this function. - */ -void nft_data_uninit(const struct nft_data *data, enum nft_data_types type) -{ - switch (type) { - case NFT_DATA_VALUE: - return; - case NFT_DATA_VERDICT: - return nft_verdict_uninit(data); - default: - WARN_ON(1); - } -} -EXPORT_SYMBOL_GPL(nft_data_uninit); - -int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data, - enum nft_data_types type, unsigned int len) -{ - struct nlattr *nest; - int err; - - nest = nla_nest_start(skb, attr); - if (nest == NULL) - return -1; - - switch (type) { - case NFT_DATA_VALUE: - err = nft_value_dump(skb, data, len); - break; - case NFT_DATA_VERDICT: - err = nft_verdict_dump(skb, data); - break; - default: - err = -EINVAL; - WARN_ON(1); - } - - nla_nest_end(skb, nest); - return err; -} -EXPORT_SYMBOL_GPL(nft_data_dump); - -static int nf_tables_init_net(struct net *net) -{ - INIT_LIST_HEAD(&net->nft.af_info); - INIT_LIST_HEAD(&net->nft.commit_list); - return 0; -} - -static struct pernet_operations nf_tables_net_ops = { - .init = nf_tables_init_net, -}; - -static int __init nf_tables_module_init(void) -{ - int err; - - info = kmalloc(sizeof(struct nft_expr_info) * NFT_RULE_MAXEXPRS, - GFP_KERNEL); - if (info == NULL) { - err = -ENOMEM; - goto err1; - } - - err = nf_tables_core_module_init(); - if (err < 0) - goto err2; - - err = nfnetlink_subsys_register(&nf_tables_subsys); - if (err < 0) - goto err3; - - pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n"); - return register_pernet_subsys(&nf_tables_net_ops); -err3: - nf_tables_core_module_exit(); -err2: - kfree(info); -err1: - return err; -} - -static void __exit nf_tables_module_exit(void) -{ - unregister_pernet_subsys(&nf_tables_net_ops); - nfnetlink_subsys_unregister(&nf_tables_subsys); - nf_tables_core_module_exit(); - kfree(info); -} - -module_init(nf_tables_module_init); -module_exit(nf_tables_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFTABLES); diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c deleted file mode 100644 index cb9e685..0000000 --- a/net/netfilter/nf_tables_core.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/rculist.h> -#include <linux/skbuff.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nfnetlink.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables_core.h> -#include <net/netfilter/nf_tables.h> -#include <net/netfilter/nf_log.h> - -static void nft_cmp_fast_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1]) -{ - const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); - u32 mask; - - mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - priv->len); - if ((data[priv->sreg].data[0] & mask) == priv->data) - return; - data[NFT_REG_VERDICT].verdict = NFT_BREAK; -} - -static bool nft_payload_fast_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_payload *priv = nft_expr_priv(expr); - const struct sk_buff *skb = pkt->skb; - struct nft_data *dest = &data[priv->dreg]; - unsigned char *ptr; - - if (priv->base == NFT_PAYLOAD_NETWORK_HEADER) - ptr = skb_network_header(skb); - else - ptr = skb_network_header(skb) + pkt->xt.thoff; - - ptr += priv->offset; - - if (unlikely(ptr + priv->len >= skb_tail_pointer(skb))) - return false; - - if (priv->len == 2) - *(u16 *)dest->data = *(u16 *)ptr; - else if (priv->len == 4) - *(u32 *)dest->data = *(u32 *)ptr; - else - *(u8 *)dest->data = *(u8 *)ptr; - return true; -} - -struct nft_jumpstack { - const struct nft_chain *chain; - const struct nft_rule *rule; - int rulenum; -}; - -static inline void -nft_chain_stats(const struct nft_chain *this, const struct nft_pktinfo *pkt, - struct nft_jumpstack *jumpstack, unsigned int stackptr) -{ - struct nft_stats __percpu *stats; - const struct nft_chain *chain = stackptr ? jumpstack[0].chain : this; - - rcu_read_lock_bh(); - stats = rcu_dereference(nft_base_chain(chain)->stats); - __this_cpu_inc(stats->pkts); - __this_cpu_add(stats->bytes, pkt->skb->len); - rcu_read_unlock_bh(); -} - -enum nft_trace { - NFT_TRACE_RULE, - NFT_TRACE_RETURN, - NFT_TRACE_POLICY, -}; - -static const char *const comments[] = { - [NFT_TRACE_RULE] = "rule", - [NFT_TRACE_RETURN] = "return", - [NFT_TRACE_POLICY] = "policy", -}; - -static struct nf_loginfo trace_loginfo = { - .type = NF_LOG_TYPE_LOG, - .u = { - .log = { - .level = 4, - .logflags = NF_LOG_MASK, - }, - }, -}; - -static inline void nft_trace_packet(const struct nft_pktinfo *pkt, - const struct nft_chain *chain, - int rulenum, enum nft_trace type) -{ - struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); - - nf_log_packet(net, pkt->xt.family, pkt->hooknum, pkt->skb, pkt->in, - pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", - chain->table->name, chain->name, comments[type], - rulenum); -} - -unsigned int -nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) -{ - const struct nft_chain *chain = ops->priv; - const struct nft_rule *rule; - const struct nft_expr *expr, *last; - struct nft_data data[NFT_REG_MAX + 1]; - unsigned int stackptr = 0; - struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; - int rulenum = 0; - /* - * Cache cursor to avoid problems in case that the cursor is updated - * while traversing the ruleset. - */ - unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor); - -do_chain: - rule = list_entry(&chain->rules, struct nft_rule, list); -next_rule: - data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; - list_for_each_entry_continue_rcu(rule, &chain->rules, list) { - - /* This rule is not active, skip. */ - if (unlikely(rule->genmask & (1 << gencursor))) - continue; - - rulenum++; - - nft_rule_for_each_expr(expr, last, rule) { - if (expr->ops == &nft_cmp_fast_ops) - nft_cmp_fast_eval(expr, data); - else if (expr->ops != &nft_payload_fast_ops || - !nft_payload_fast_eval(expr, data, pkt)) - expr->ops->eval(expr, data, pkt); - - if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE) - break; - } - - switch (data[NFT_REG_VERDICT].verdict) { - case NFT_BREAK: - data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; - /* fall through */ - case NFT_CONTINUE: - continue; - } - break; - } - - switch (data[NFT_REG_VERDICT].verdict) { - case NF_ACCEPT: - case NF_DROP: - case NF_QUEUE: - if (unlikely(pkt->skb->nf_trace)) - nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); - - return data[NFT_REG_VERDICT].verdict; - case NFT_JUMP: - if (unlikely(pkt->skb->nf_trace)) - nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); - - BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE); - jumpstack[stackptr].chain = chain; - jumpstack[stackptr].rule = rule; - jumpstack[stackptr].rulenum = rulenum; - stackptr++; - /* fall through */ - case NFT_GOTO: - chain = data[NFT_REG_VERDICT].chain; - goto do_chain; - case NFT_RETURN: - if (unlikely(pkt->skb->nf_trace)) - nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN); - - /* fall through */ - case NFT_CONTINUE: - break; - default: - WARN_ON(1); - } - - if (stackptr > 0) { - if (unlikely(pkt->skb->nf_trace)) - nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN); - - stackptr--; - chain = jumpstack[stackptr].chain; - rule = jumpstack[stackptr].rule; - rulenum = jumpstack[stackptr].rulenum; - goto next_rule; - } - nft_chain_stats(chain, pkt, jumpstack, stackptr); - - if (unlikely(pkt->skb->nf_trace)) - nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY); - - return nft_base_chain(chain)->policy; -} -EXPORT_SYMBOL_GPL(nft_do_chain_pktinfo); - -int __init nf_tables_core_module_init(void) -{ - int err; - - err = nft_immediate_module_init(); - if (err < 0) - goto err1; - - err = nft_cmp_module_init(); - if (err < 0) - goto err2; - - err = nft_lookup_module_init(); - if (err < 0) - goto err3; - - err = nft_bitwise_module_init(); - if (err < 0) - goto err4; - - err = nft_byteorder_module_init(); - if (err < 0) - goto err5; - - err = nft_payload_module_init(); - if (err < 0) - goto err6; - - return 0; - -err6: - nft_byteorder_module_exit(); -err5: - nft_bitwise_module_exit(); -err4: - nft_lookup_module_exit(); -err3: - nft_cmp_module_exit(); -err2: - nft_immediate_module_exit(); -err1: - return err; -} - -void nf_tables_core_module_exit(void) -{ - nft_payload_module_exit(); - nft_byteorder_module_exit(); - nft_bitwise_module_exit(); - nft_lookup_module_exit(); - nft_cmp_module_exit(); - nft_immediate_module_exit(); -} diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 046aa13..572d87d 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -147,6 +147,9 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) const struct nfnetlink_subsystem *ss; int type, err; + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + return -EPERM; + /* All the messages must at least contain nfgenmsg */ if (nlmsg_len(nlh) < sizeof(struct nfgenmsg)) return 0; @@ -214,181 +217,9 @@ replay: } } -static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, - u_int16_t subsys_id) -{ - struct sk_buff *nskb, *oskb = skb; - struct net *net = sock_net(skb->sk); - const struct nfnetlink_subsystem *ss; - const struct nfnl_callback *nc; - bool success = true, done = false; - int err; - - if (subsys_id >= NFNL_SUBSYS_COUNT) - return netlink_ack(skb, nlh, -EINVAL); -replay: - nskb = netlink_skb_clone(oskb, GFP_KERNEL); - if (!nskb) - return netlink_ack(oskb, nlh, -ENOMEM); - - nskb->sk = oskb->sk; - skb = nskb; - - nfnl_lock(subsys_id); - ss = rcu_dereference_protected(table[subsys_id].subsys, - lockdep_is_held(&table[subsys_id].mutex)); - if (!ss) { -#ifdef CONFIG_MODULES - nfnl_unlock(subsys_id); - request_module("nfnetlink-subsys-%d", subsys_id); - nfnl_lock(subsys_id); - ss = rcu_dereference_protected(table[subsys_id].subsys, - lockdep_is_held(&table[subsys_id].mutex)); - if (!ss) -#endif - { - nfnl_unlock(subsys_id); - kfree_skb(nskb); - return netlink_ack(skb, nlh, -EOPNOTSUPP); - } - } - - if (!ss->commit || !ss->abort) { - nfnl_unlock(subsys_id); - kfree_skb(nskb); - return netlink_ack(skb, nlh, -EOPNOTSUPP); - } - - while (skb->len >= nlmsg_total_size(0)) { - int msglen, type; - - nlh = nlmsg_hdr(skb); - err = 0; - - if (nlh->nlmsg_len < NLMSG_HDRLEN) { - err = -EINVAL; - goto ack; - } - - /* Only requests are handled by the kernel */ - if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) { - err = -EINVAL; - goto ack; - } - - type = nlh->nlmsg_type; - if (type == NFNL_MSG_BATCH_BEGIN) { - /* Malformed: Batch begin twice */ - success = false; - goto done; - } else if (type == NFNL_MSG_BATCH_END) { - done = true; - goto done; - } else if (type < NLMSG_MIN_TYPE) { - err = -EINVAL; - goto ack; - } - - /* We only accept a batch with messages for the same - * subsystem. - */ - if (NFNL_SUBSYS_ID(type) != subsys_id) { - err = -EINVAL; - goto ack; - } - - nc = nfnetlink_find_client(type, ss); - if (!nc) { - err = -EINVAL; - goto ack; - } - - { - int min_len = nlmsg_total_size(sizeof(struct nfgenmsg)); - u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); - struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; - struct nlattr *attr = (void *)nlh + min_len; - int attrlen = nlh->nlmsg_len - min_len; - - err = nla_parse(cda, ss->cb[cb_id].attr_count, - attr, attrlen, ss->cb[cb_id].policy); - if (err < 0) - goto ack; - - if (nc->call_batch) { - err = nc->call_batch(net->nfnl, skb, nlh, - (const struct nlattr **)cda); - } - - /* The lock was released to autoload some module, we - * have to abort and start from scratch using the - * original skb. - */ - if (err == -EAGAIN) { - ss->abort(skb); - nfnl_unlock(subsys_id); - kfree_skb(nskb); - goto replay; - } - } -ack: - if (nlh->nlmsg_flags & NLM_F_ACK || err) { - /* We don't stop processing the batch on errors, thus, - * userspace gets all the errors that the batch - * triggers. - */ - netlink_ack(skb, nlh, err); - if (err) - success = false; - } - - msglen = NLMSG_ALIGN(nlh->nlmsg_len); - if (msglen > skb->len) - msglen = skb->len; - skb_pull(skb, msglen); - } -done: - if (success && done) - ss->commit(skb); - else - ss->abort(skb); - - nfnl_unlock(subsys_id); - kfree_skb(nskb); -} - static void nfnetlink_rcv(struct sk_buff *skb) { - struct nlmsghdr *nlh = nlmsg_hdr(skb); - struct net *net = sock_net(skb->sk); - int msglen; - - if (nlh->nlmsg_len < NLMSG_HDRLEN || - skb->len < nlh->nlmsg_len) - return; - - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) { - netlink_ack(skb, nlh, -EPERM); - return; - } - - if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN) { - struct nfgenmsg *nfgenmsg; - - msglen = NLMSG_ALIGN(nlh->nlmsg_len); - if (msglen > skb->len) - msglen = skb->len; - - if (nlh->nlmsg_len < NLMSG_HDRLEN || - skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg)) - return; - - nfgenmsg = nlmsg_data(nlh); - skb_pull(skb, msglen); - nfnetlink_rcv_batch(skb, nlh, nfgenmsg->res_id); - } else { - netlink_rcv_skb(skb, &nfnetlink_rcv_msg); - } + netlink_rcv_skb(skb, &nfnetlink_rcv_msg); } #ifdef CONFIG_MODULES diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 476accd..5058049 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -49,8 +49,10 @@ static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = { }; static int -ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto, - struct net *net, const struct nlattr *attr) +ctnl_timeout_parse_policy(struct ctnl_timeout *timeout, + struct nf_conntrack_l4proto *l4proto, + struct net *net, + const struct nlattr *attr) { int ret = 0; @@ -62,7 +64,8 @@ ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto, if (ret < 0) return ret; - ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeouts); + ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, + &timeout->data); } return ret; } @@ -120,8 +123,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb, goto err_proto_put; } - ret = ctnl_timeout_parse_policy(&matching->data, - l4proto, net, + ret = ctnl_timeout_parse_policy(matching, l4proto, net, cda[CTA_TIMEOUT_DATA]); return ret; } @@ -136,7 +138,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb, goto err_proto_put; } - ret = ctnl_timeout_parse_policy(&timeout->data, l4proto, net, + ret = ctnl_timeout_parse_policy(timeout, l4proto, net, cda[CTA_TIMEOUT_DATA]); if (ret < 0) goto err; @@ -340,147 +342,6 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb, return ret; } -static int -cttimeout_default_set(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const cda[]) -{ - __u16 l3num; - __u8 l4num; - struct nf_conntrack_l4proto *l4proto; - struct net *net = sock_net(skb->sk); - unsigned int *timeouts; - int ret; - - if (!cda[CTA_TIMEOUT_L3PROTO] || - !cda[CTA_TIMEOUT_L4PROTO] || - !cda[CTA_TIMEOUT_DATA]) - return -EINVAL; - - l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO])); - l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]); - l4proto = nf_ct_l4proto_find_get(l3num, l4num); - - /* This protocol is not supported, skip. */ - if (l4proto->l4proto != l4num) { - ret = -EOPNOTSUPP; - goto err; - } - - timeouts = l4proto->get_timeouts(net); - - ret = ctnl_timeout_parse_policy(timeouts, l4proto, net, - cda[CTA_TIMEOUT_DATA]); - if (ret < 0) - goto err; - - nf_ct_l4proto_put(l4proto); - return 0; -err: - nf_ct_l4proto_put(l4proto); - return ret; -} - -static int -cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid, - u32 seq, u32 type, int event, - struct nf_conntrack_l4proto *l4proto) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - unsigned int flags = portid ? NLM_F_MULTI : 0; - - event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8; - nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); - if (nlh == NULL) - goto nlmsg_failure; - - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = AF_UNSPEC; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(l4proto->l3proto)) || - nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto)) - goto nla_put_failure; - - if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) { - struct nlattr *nest_parms; - unsigned int *timeouts = l4proto->get_timeouts(net); - int ret; - - nest_parms = nla_nest_start(skb, - CTA_TIMEOUT_DATA | NLA_F_NESTED); - if (!nest_parms) - goto nla_put_failure; - - ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, timeouts); - if (ret < 0) - goto nla_put_failure; - - nla_nest_end(skb, nest_parms); - } - - nlmsg_end(skb, nlh); - return skb->len; - -nlmsg_failure: -nla_put_failure: - nlmsg_cancel(skb, nlh); - return -1; -} - -static int cttimeout_default_get(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const cda[]) -{ - __u16 l3num; - __u8 l4num; - struct nf_conntrack_l4proto *l4proto; - struct net *net = sock_net(skb->sk); - struct sk_buff *skb2; - int ret, err; - - if (!cda[CTA_TIMEOUT_L3PROTO] || !cda[CTA_TIMEOUT_L4PROTO]) - return -EINVAL; - - l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO])); - l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]); - l4proto = nf_ct_l4proto_find_get(l3num, l4num); - - /* This protocol is not supported, skip. */ - if (l4proto->l4proto != l4num) { - err = -EOPNOTSUPP; - goto err; - } - - skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (skb2 == NULL) { - err = -ENOMEM; - goto err; - } - - ret = cttimeout_default_fill_info(net, skb2, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, - NFNL_MSG_TYPE(nlh->nlmsg_type), - IPCTNL_MSG_TIMEOUT_DEFAULT_SET, - l4proto); - if (ret <= 0) { - kfree_skb(skb2); - err = -ENOMEM; - goto err; - } - ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); - if (ret > 0) - ret = 0; - - /* this avoids a loop in nfnetlink. */ - return ret == -EAGAIN ? -ENOBUFS : ret; -err: - nf_ct_l4proto_put(l4proto); - return err; -} - #ifdef CONFIG_NF_CONNTRACK_TIMEOUT static struct ctnl_timeout *ctnl_timeout_find_get(const char *name) { @@ -523,12 +384,6 @@ static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = { [IPCTNL_MSG_TIMEOUT_DELETE] = { .call = cttimeout_del_timeout, .attr_count = CTA_TIMEOUT_MAX, .policy = cttimeout_nla_policy }, - [IPCTNL_MSG_TIMEOUT_DEFAULT_SET]= { .call = cttimeout_default_set, - .attr_count = CTA_TIMEOUT_MAX, - .policy = cttimeout_nla_policy }, - [IPCTNL_MSG_TIMEOUT_DEFAULT_GET]= { .call = cttimeout_default_get, - .attr_count = CTA_TIMEOUT_MAX, - .policy = cttimeout_nla_policy }, }; static const struct nfnetlink_subsystem cttimeout_subsys = { diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 3c4b69e..d92cc31 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -319,8 +319,7 @@ nfulnl_set_flags(struct nfulnl_instance *inst, u_int16_t flags) } static struct sk_buff * -nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size, - unsigned int pkt_size) +nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size) { struct sk_buff *skb; unsigned int n; @@ -329,13 +328,13 @@ nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size, * message. WARNING: has to be <= 128k due to slab restrictions */ n = max(inst_size, pkt_size); - skb = nfnetlink_alloc_skb(net, n, peer_portid, GFP_ATOMIC); + skb = nfnetlink_alloc_skb(&init_net, n, peer_portid, GFP_ATOMIC); if (!skb) { if (n > pkt_size) { /* try to allocate only as much as we need for current * packet */ - skb = nfnetlink_alloc_skb(net, pkt_size, + skb = nfnetlink_alloc_skb(&init_net, pkt_size, peer_portid, GFP_ATOMIC); if (!skb) pr_err("nfnetlink_log: can't even alloc %u bytes\n", @@ -703,8 +702,8 @@ nfulnl_log_packet(struct net *net, } if (!inst->skb) { - inst->skb = nfulnl_alloc_skb(net, inst->peer_portid, - inst->nlbufsiz, size); + inst->skb = nfulnl_alloc_skb(inst->peer_portid, inst->nlbufsiz, + size); if (!inst->skb) goto alloc_failure; } diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 21258cf..ae2e5c1 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -298,7 +298,7 @@ nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, } static struct sk_buff * -nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, +nfqnl_build_packet_message(struct nfqnl_instance *queue, struct nf_queue_entry *entry, __be32 **packet_id_ptr) { @@ -372,7 +372,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, if (queue->flags & NFQA_CFG_F_CONNTRACK) ct = nfqnl_ct_get(entskb, &size, &ctinfo); - skb = nfnetlink_alloc_skb(net, size, queue->peer_portid, + skb = nfnetlink_alloc_skb(&init_net, size, queue->peer_portid, GFP_ATOMIC); if (!skb) return NULL; @@ -525,7 +525,7 @@ __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue, __be32 *packet_id_ptr; int failopen = 0; - nskb = nfqnl_build_packet_message(net, queue, entry, &packet_id_ptr); + nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr); if (nskb == NULL) { err = -ENOMEM; goto err_out; diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c deleted file mode 100644 index 4fb6ee2..0000000 --- a/net/netfilter/nft_bitwise.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables_core.h> -#include <net/netfilter/nf_tables.h> - -struct nft_bitwise { - enum nft_registers sreg:8; - enum nft_registers dreg:8; - u8 len; - struct nft_data mask; - struct nft_data xor; -}; - -static void nft_bitwise_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_bitwise *priv = nft_expr_priv(expr); - const struct nft_data *src = &data[priv->sreg]; - struct nft_data *dst = &data[priv->dreg]; - unsigned int i; - - for (i = 0; i < DIV_ROUND_UP(priv->len, 4); i++) { - dst->data[i] = (src->data[i] & priv->mask.data[i]) ^ - priv->xor.data[i]; - } -} - -static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = { - [NFTA_BITWISE_SREG] = { .type = NLA_U32 }, - [NFTA_BITWISE_DREG] = { .type = NLA_U32 }, - [NFTA_BITWISE_LEN] = { .type = NLA_U32 }, - [NFTA_BITWISE_MASK] = { .type = NLA_NESTED }, - [NFTA_BITWISE_XOR] = { .type = NLA_NESTED }, -}; - -static int nft_bitwise_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_bitwise *priv = nft_expr_priv(expr); - struct nft_data_desc d1, d2; - int err; - - if (tb[NFTA_BITWISE_SREG] == NULL || - tb[NFTA_BITWISE_DREG] == NULL || - tb[NFTA_BITWISE_LEN] == NULL || - tb[NFTA_BITWISE_MASK] == NULL || - tb[NFTA_BITWISE_XOR] == NULL) - return -EINVAL; - - priv->sreg = ntohl(nla_get_be32(tb[NFTA_BITWISE_SREG])); - err = nft_validate_input_register(priv->sreg); - if (err < 0) - return err; - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_BITWISE_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); - if (err < 0) - return err; - - priv->len = ntohl(nla_get_be32(tb[NFTA_BITWISE_LEN])); - - err = nft_data_init(NULL, &priv->mask, &d1, tb[NFTA_BITWISE_MASK]); - if (err < 0) - return err; - if (d1.len != priv->len) - return -EINVAL; - - err = nft_data_init(NULL, &priv->xor, &d2, tb[NFTA_BITWISE_XOR]); - if (err < 0) - return err; - if (d2.len != priv->len) - return -EINVAL; - - return 0; -} - -static int nft_bitwise_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_bitwise *priv = nft_expr_priv(expr); - - if (nla_put_be32(skb, NFTA_BITWISE_SREG, htonl(priv->sreg))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_BITWISE_DREG, htonl(priv->dreg))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_BITWISE_LEN, htonl(priv->len))) - goto nla_put_failure; - - if (nft_data_dump(skb, NFTA_BITWISE_MASK, &priv->mask, - NFT_DATA_VALUE, priv->len) < 0) - goto nla_put_failure; - - if (nft_data_dump(skb, NFTA_BITWISE_XOR, &priv->xor, - NFT_DATA_VALUE, priv->len) < 0) - goto nla_put_failure; - - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_bitwise_type; -static const struct nft_expr_ops nft_bitwise_ops = { - .type = &nft_bitwise_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise)), - .eval = nft_bitwise_eval, - .init = nft_bitwise_init, - .dump = nft_bitwise_dump, -}; - -static struct nft_expr_type nft_bitwise_type __read_mostly = { - .name = "bitwise", - .ops = &nft_bitwise_ops, - .policy = nft_bitwise_policy, - .maxattr = NFTA_BITWISE_MAX, - .owner = THIS_MODULE, -}; - -int __init nft_bitwise_module_init(void) -{ - return nft_register_expr(&nft_bitwise_type); -} - -void nft_bitwise_module_exit(void) -{ - nft_unregister_expr(&nft_bitwise_type); -} diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c deleted file mode 100644 index c39ed8d..0000000 --- a/net/netfilter/nft_byteorder.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables_core.h> -#include <net/netfilter/nf_tables.h> - -struct nft_byteorder { - enum nft_registers sreg:8; - enum nft_registers dreg:8; - enum nft_byteorder_ops op:8; - u8 len; - u8 size; -}; - -static void nft_byteorder_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_byteorder *priv = nft_expr_priv(expr); - struct nft_data *src = &data[priv->sreg], *dst = &data[priv->dreg]; - union { u32 u32; u16 u16; } *s, *d; - unsigned int i; - - s = (void *)src->data; - d = (void *)dst->data; - - switch (priv->size) { - case 4: - switch (priv->op) { - case NFT_BYTEORDER_NTOH: - for (i = 0; i < priv->len / 4; i++) - d[i].u32 = ntohl((__force __be32)s[i].u32); - break; - case NFT_BYTEORDER_HTON: - for (i = 0; i < priv->len / 4; i++) - d[i].u32 = (__force __u32)htonl(s[i].u32); - break; - } - break; - case 2: - switch (priv->op) { - case NFT_BYTEORDER_NTOH: - for (i = 0; i < priv->len / 2; i++) - d[i].u16 = ntohs((__force __be16)s[i].u16); - break; - case NFT_BYTEORDER_HTON: - for (i = 0; i < priv->len / 2; i++) - d[i].u16 = (__force __u16)htons(s[i].u16); - break; - } - break; - } -} - -static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = { - [NFTA_BYTEORDER_SREG] = { .type = NLA_U32 }, - [NFTA_BYTEORDER_DREG] = { .type = NLA_U32 }, - [NFTA_BYTEORDER_OP] = { .type = NLA_U32 }, - [NFTA_BYTEORDER_LEN] = { .type = NLA_U32 }, - [NFTA_BYTEORDER_SIZE] = { .type = NLA_U32 }, -}; - -static int nft_byteorder_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_byteorder *priv = nft_expr_priv(expr); - int err; - - if (tb[NFTA_BYTEORDER_SREG] == NULL || - tb[NFTA_BYTEORDER_DREG] == NULL || - tb[NFTA_BYTEORDER_LEN] == NULL || - tb[NFTA_BYTEORDER_SIZE] == NULL || - tb[NFTA_BYTEORDER_OP] == NULL) - return -EINVAL; - - priv->sreg = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_SREG])); - err = nft_validate_input_register(priv->sreg); - if (err < 0) - return err; - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); - if (err < 0) - return err; - - priv->op = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_OP])); - switch (priv->op) { - case NFT_BYTEORDER_NTOH: - case NFT_BYTEORDER_HTON: - break; - default: - return -EINVAL; - } - - priv->len = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_LEN])); - if (priv->len == 0 || priv->len > FIELD_SIZEOF(struct nft_data, data)) - return -EINVAL; - - priv->size = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_SIZE])); - switch (priv->size) { - case 2: - case 4: - break; - default: - return -EINVAL; - } - - return 0; -} - -static int nft_byteorder_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_byteorder *priv = nft_expr_priv(expr); - - if (nla_put_be32(skb, NFTA_BYTEORDER_SREG, htonl(priv->sreg))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_BYTEORDER_DREG, htonl(priv->dreg))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_BYTEORDER_OP, htonl(priv->op))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->len))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_BYTEORDER_SIZE, htonl(priv->size))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_byteorder_type; -static const struct nft_expr_ops nft_byteorder_ops = { - .type = &nft_byteorder_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_byteorder)), - .eval = nft_byteorder_eval, - .init = nft_byteorder_init, - .dump = nft_byteorder_dump, -}; - -static struct nft_expr_type nft_byteorder_type __read_mostly = { - .name = "byteorder", - .ops = &nft_byteorder_ops, - .policy = nft_byteorder_policy, - .maxattr = NFTA_BYTEORDER_MAX, - .owner = THIS_MODULE, -}; - -int __init nft_byteorder_module_init(void) -{ - return nft_register_expr(&nft_byteorder_type); -} - -void nft_byteorder_module_exit(void) -{ - nft_unregister_expr(&nft_byteorder_type); -} diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c deleted file mode 100644 index 954925d..0000000 --- a/net/netfilter/nft_cmp.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables_core.h> -#include <net/netfilter/nf_tables.h> - -struct nft_cmp_expr { - struct nft_data data; - enum nft_registers sreg:8; - u8 len; - enum nft_cmp_ops op:8; -}; - -static void nft_cmp_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_cmp_expr *priv = nft_expr_priv(expr); - int d; - - d = nft_data_cmp(&data[priv->sreg], &priv->data, priv->len); - switch (priv->op) { - case NFT_CMP_EQ: - if (d != 0) - goto mismatch; - break; - case NFT_CMP_NEQ: - if (d == 0) - goto mismatch; - break; - case NFT_CMP_LT: - if (d == 0) - goto mismatch; - case NFT_CMP_LTE: - if (d > 0) - goto mismatch; - break; - case NFT_CMP_GT: - if (d == 0) - goto mismatch; - case NFT_CMP_GTE: - if (d < 0) - goto mismatch; - break; - } - return; - -mismatch: - data[NFT_REG_VERDICT].verdict = NFT_BREAK; -} - -static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = { - [NFTA_CMP_SREG] = { .type = NLA_U32 }, - [NFTA_CMP_OP] = { .type = NLA_U32 }, - [NFTA_CMP_DATA] = { .type = NLA_NESTED }, -}; - -static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_cmp_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; - int err; - - priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); - priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); - - err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]); - BUG_ON(err < 0); - - priv->len = desc.len; - return 0; -} - -static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_cmp_expr *priv = nft_expr_priv(expr); - - if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op))) - goto nla_put_failure; - - if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data, - NFT_DATA_VALUE, priv->len) < 0) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_cmp_type; -static const struct nft_expr_ops nft_cmp_ops = { - .type = &nft_cmp_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)), - .eval = nft_cmp_eval, - .init = nft_cmp_init, - .dump = nft_cmp_dump, -}; - -static int nft_cmp_fast_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; - struct nft_data data; - u32 mask; - int err; - - priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); - - err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); - BUG_ON(err < 0); - desc.len *= BITS_PER_BYTE; - - mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - desc.len); - priv->data = data.data[0] & mask; - priv->len = desc.len; - return 0; -} - -static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); - struct nft_data data; - - if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ))) - goto nla_put_failure; - - data.data[0] = priv->data; - if (nft_data_dump(skb, NFTA_CMP_DATA, &data, - NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -const struct nft_expr_ops nft_cmp_fast_ops = { - .type = &nft_cmp_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)), - .eval = NULL, /* inlined */ - .init = nft_cmp_fast_init, - .dump = nft_cmp_fast_dump, -}; - -static const struct nft_expr_ops * -nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) -{ - struct nft_data_desc desc; - struct nft_data data; - enum nft_registers sreg; - enum nft_cmp_ops op; - int err; - - if (tb[NFTA_CMP_SREG] == NULL || - tb[NFTA_CMP_OP] == NULL || - tb[NFTA_CMP_DATA] == NULL) - return ERR_PTR(-EINVAL); - - sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); - err = nft_validate_input_register(sreg); - if (err < 0) - return ERR_PTR(err); - - op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); - switch (op) { - case NFT_CMP_EQ: - case NFT_CMP_NEQ: - case NFT_CMP_LT: - case NFT_CMP_LTE: - case NFT_CMP_GT: - case NFT_CMP_GTE: - break; - default: - return ERR_PTR(-EINVAL); - } - - err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); - if (err < 0) - return ERR_PTR(err); - - if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ) - return &nft_cmp_fast_ops; - else - return &nft_cmp_ops; -} - -static struct nft_expr_type nft_cmp_type __read_mostly = { - .name = "cmp", - .select_ops = nft_cmp_select_ops, - .policy = nft_cmp_policy, - .maxattr = NFTA_CMP_MAX, - .owner = THIS_MODULE, -}; - -int __init nft_cmp_module_init(void) -{ - return nft_register_expr(&nft_cmp_type); -} - -void nft_cmp_module_exit(void) -{ - nft_unregister_expr(&nft_cmp_type); -} diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c deleted file mode 100644 index da0c1f4..0000000 --- a/net/netfilter/nft_compat.c +++ /dev/null @@ -1,775 +0,0 @@ -/* - * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This software has been sponsored by Sophos Astaro <http://www.sophos.com> - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nfnetlink.h> -#include <linux/netfilter/nf_tables.h> -#include <linux/netfilter/nf_tables_compat.h> -#include <linux/netfilter/x_tables.h> -#include <linux/netfilter_ipv4/ip_tables.h> -#include <linux/netfilter_ipv6/ip6_tables.h> -#include <asm/uaccess.h> /* for set_fs */ -#include <net/netfilter/nf_tables.h> - -union nft_entry { - struct ipt_entry e4; - struct ip6t_entry e6; -}; - -static inline void -nft_compat_set_par(struct xt_action_param *par, void *xt, const void *xt_info) -{ - par->target = xt; - par->targinfo = xt_info; - par->hotdrop = false; -} - -static void nft_target_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - void *info = nft_expr_priv(expr); - struct xt_target *target = expr->ops->data; - struct sk_buff *skb = pkt->skb; - int ret; - - nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info); - - ret = target->target(skb, &pkt->xt); - - if (pkt->xt.hotdrop) - ret = NF_DROP; - - switch(ret) { - case XT_CONTINUE: - data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; - break; - default: - data[NFT_REG_VERDICT].verdict = ret; - break; - } - return; -} - -static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX + 1] = { - [NFTA_TARGET_NAME] = { .type = NLA_NUL_STRING }, - [NFTA_TARGET_REV] = { .type = NLA_U32 }, - [NFTA_TARGET_INFO] = { .type = NLA_BINARY }, -}; - -static void -nft_target_set_tgchk_param(struct xt_tgchk_param *par, - const struct nft_ctx *ctx, - struct xt_target *target, void *info, - union nft_entry *entry, u8 proto, bool inv) -{ - par->net = &init_net; - par->table = ctx->table->name; - switch (ctx->afi->family) { - case AF_INET: - entry->e4.ip.proto = proto; - entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0; - break; - case AF_INET6: - entry->e6.ipv6.proto = proto; - entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; - break; - } - par->entryinfo = entry; - par->target = target; - par->targinfo = info; - if (ctx->chain->flags & NFT_BASE_CHAIN) { - const struct nft_base_chain *basechain = - nft_base_chain(ctx->chain); - const struct nf_hook_ops *ops = &basechain->ops; - - par->hook_mask = 1 << ops->hooknum; - } - par->family = ctx->afi->family; -} - -static void target_compat_from_user(struct xt_target *t, void *in, void *out) -{ -#ifdef CONFIG_COMPAT - if (t->compat_from_user) { - int pad; - - t->compat_from_user(out, in); - pad = XT_ALIGN(t->targetsize) - t->targetsize; - if (pad > 0) - memset(out + t->targetsize, 0, pad); - } else -#endif - memcpy(out, in, XT_ALIGN(t->targetsize)); -} - -static inline int nft_compat_target_offset(struct xt_target *target) -{ -#ifdef CONFIG_COMPAT - return xt_compat_target_offset(target); -#else - return 0; -#endif -} - -static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = { - [NFTA_RULE_COMPAT_PROTO] = { .type = NLA_U32 }, - [NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 }, -}; - -static int nft_parse_compat(const struct nlattr *attr, u8 *proto, bool *inv) -{ - struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1]; - u32 flags; - int err; - - err = nla_parse_nested(tb, NFTA_RULE_COMPAT_MAX, attr, - nft_rule_compat_policy); - if (err < 0) - return err; - - if (!tb[NFTA_RULE_COMPAT_PROTO] || !tb[NFTA_RULE_COMPAT_FLAGS]) - return -EINVAL; - - flags = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_FLAGS])); - if (flags & ~NFT_RULE_COMPAT_F_MASK) - return -EINVAL; - if (flags & NFT_RULE_COMPAT_F_INV) - *inv = true; - - *proto = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO])); - return 0; -} - -static int -nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - void *info = nft_expr_priv(expr); - struct xt_target *target = expr->ops->data; - struct xt_tgchk_param par; - size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO])); - u8 proto = 0; - bool inv = false; - union nft_entry e = {}; - int ret; - - target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info); - - if (ctx->nla[NFTA_RULE_COMPAT]) { - ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv); - if (ret < 0) - goto err; - } - - nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv); - - ret = xt_check_target(&par, size, proto, inv); - if (ret < 0) - goto err; - - /* The standard target cannot be used */ - if (target->target == NULL) { - ret = -EINVAL; - goto err; - } - - return 0; -err: - module_put(target->me); - return ret; -} - -static void -nft_target_destroy(const struct nft_expr *expr) -{ - struct xt_target *target = expr->ops->data; - - module_put(target->me); -} - -static int -target_dump_info(struct sk_buff *skb, const struct xt_target *t, const void *in) -{ - int ret; - -#ifdef CONFIG_COMPAT - if (t->compat_to_user) { - mm_segment_t old_fs; - void *out; - - out = kmalloc(XT_ALIGN(t->targetsize), GFP_ATOMIC); - if (out == NULL) - return -ENOMEM; - - /* We want to reuse existing compat_to_user */ - old_fs = get_fs(); - set_fs(KERNEL_DS); - t->compat_to_user(out, in); - set_fs(old_fs); - ret = nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(t->targetsize), out); - kfree(out); - } else -#endif - ret = nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(t->targetsize), in); - - return ret; -} - -static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct xt_target *target = expr->ops->data; - void *info = nft_expr_priv(expr); - - if (nla_put_string(skb, NFTA_TARGET_NAME, target->name) || - nla_put_be32(skb, NFTA_TARGET_REV, htonl(target->revision)) || - target_dump_info(skb, target, info)) - goto nla_put_failure; - - return 0; - -nla_put_failure: - return -1; -} - -static int nft_target_validate(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nft_data **data) -{ - struct xt_target *target = expr->ops->data; - unsigned int hook_mask = 0; - - if (ctx->chain->flags & NFT_BASE_CHAIN) { - const struct nft_base_chain *basechain = - nft_base_chain(ctx->chain); - const struct nf_hook_ops *ops = &basechain->ops; - - hook_mask = 1 << ops->hooknum; - if (hook_mask & target->hooks) - return 0; - - /* This target is being called from an invalid chain */ - return -EINVAL; - } - return 0; -} - -static void nft_match_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - void *info = nft_expr_priv(expr); - struct xt_match *match = expr->ops->data; - struct sk_buff *skb = pkt->skb; - bool ret; - - nft_compat_set_par((struct xt_action_param *)&pkt->xt, match, info); - - ret = match->match(skb, (struct xt_action_param *)&pkt->xt); - - if (pkt->xt.hotdrop) { - data[NFT_REG_VERDICT].verdict = NF_DROP; - return; - } - - switch(ret) { - case true: - data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; - break; - case false: - data[NFT_REG_VERDICT].verdict = NFT_BREAK; - break; - } -} - -static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = { - [NFTA_MATCH_NAME] = { .type = NLA_NUL_STRING }, - [NFTA_MATCH_REV] = { .type = NLA_U32 }, - [NFTA_MATCH_INFO] = { .type = NLA_BINARY }, -}; - -/* struct xt_mtchk_param and xt_tgchk_param look very similar */ -static void -nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, - struct xt_match *match, void *info, - union nft_entry *entry, u8 proto, bool inv) -{ - par->net = &init_net; - par->table = ctx->table->name; - switch (ctx->afi->family) { - case AF_INET: - entry->e4.ip.proto = proto; - entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0; - break; - case AF_INET6: - entry->e6.ipv6.proto = proto; - entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; - break; - } - par->entryinfo = entry; - par->match = match; - par->matchinfo = info; - if (ctx->chain->flags & NFT_BASE_CHAIN) { - const struct nft_base_chain *basechain = - nft_base_chain(ctx->chain); - const struct nf_hook_ops *ops = &basechain->ops; - - par->hook_mask = 1 << ops->hooknum; - } - par->family = ctx->afi->family; -} - -static void match_compat_from_user(struct xt_match *m, void *in, void *out) -{ -#ifdef CONFIG_COMPAT - if (m->compat_from_user) { - int pad; - - m->compat_from_user(out, in); - pad = XT_ALIGN(m->matchsize) - m->matchsize; - if (pad > 0) - memset(out + m->matchsize, 0, pad); - } else -#endif - memcpy(out, in, XT_ALIGN(m->matchsize)); -} - -static int -nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - void *info = nft_expr_priv(expr); - struct xt_match *match = expr->ops->data; - struct xt_mtchk_param par; - size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO])); - u8 proto = 0; - bool inv = false; - union nft_entry e = {}; - int ret; - - match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info); - - if (ctx->nla[NFTA_RULE_COMPAT]) { - ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv); - if (ret < 0) - goto err; - } - - nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv); - - ret = xt_check_match(&par, size, proto, inv); - if (ret < 0) - goto err; - - return 0; -err: - module_put(match->me); - return ret; -} - -static void -nft_match_destroy(const struct nft_expr *expr) -{ - struct xt_match *match = expr->ops->data; - - module_put(match->me); -} - -static int -match_dump_info(struct sk_buff *skb, const struct xt_match *m, const void *in) -{ - int ret; - -#ifdef CONFIG_COMPAT - if (m->compat_to_user) { - mm_segment_t old_fs; - void *out; - - out = kmalloc(XT_ALIGN(m->matchsize), GFP_ATOMIC); - if (out == NULL) - return -ENOMEM; - - /* We want to reuse existing compat_to_user */ - old_fs = get_fs(); - set_fs(KERNEL_DS); - m->compat_to_user(out, in); - set_fs(old_fs); - ret = nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(m->matchsize), out); - kfree(out); - } else -#endif - ret = nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(m->matchsize), in); - - return ret; -} - -static inline int nft_compat_match_offset(struct xt_match *match) -{ -#ifdef CONFIG_COMPAT - return xt_compat_match_offset(match); -#else - return 0; -#endif -} - -static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - void *info = nft_expr_priv(expr); - struct xt_match *match = expr->ops->data; - - if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) || - nla_put_be32(skb, NFTA_MATCH_REV, htonl(match->revision)) || - match_dump_info(skb, match, info)) - goto nla_put_failure; - - return 0; - -nla_put_failure: - return -1; -} - -static int nft_match_validate(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nft_data **data) -{ - struct xt_match *match = expr->ops->data; - unsigned int hook_mask = 0; - - if (ctx->chain->flags & NFT_BASE_CHAIN) { - const struct nft_base_chain *basechain = - nft_base_chain(ctx->chain); - const struct nf_hook_ops *ops = &basechain->ops; - - hook_mask = 1 << ops->hooknum; - if (hook_mask & match->hooks) - return 0; - - /* This match is being called from an invalid chain */ - return -EINVAL; - } - return 0; -} - -static int -nfnl_compat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, - int event, u16 family, const char *name, - int rev, int target) -{ - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - unsigned int flags = portid ? NLM_F_MULTI : 0; - - event |= NFNL_SUBSYS_NFT_COMPAT << 8; - nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); - if (nlh == NULL) - goto nlmsg_failure; - - nfmsg = nlmsg_data(nlh); - nfmsg->nfgen_family = family; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = 0; - - if (nla_put_string(skb, NFTA_COMPAT_NAME, name) || - nla_put_be32(skb, NFTA_COMPAT_REV, htonl(rev)) || - nla_put_be32(skb, NFTA_COMPAT_TYPE, htonl(target))) - goto nla_put_failure; - - nlmsg_end(skb, nlh); - return skb->len; - -nlmsg_failure: -nla_put_failure: - nlmsg_cancel(skb, nlh); - return -1; -} - -static int -nfnl_compat_get(struct sock *nfnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, const struct nlattr * const tb[]) -{ - int ret = 0, target; - struct nfgenmsg *nfmsg; - const char *fmt; - const char *name; - u32 rev; - struct sk_buff *skb2; - - if (tb[NFTA_COMPAT_NAME] == NULL || - tb[NFTA_COMPAT_REV] == NULL || - tb[NFTA_COMPAT_TYPE] == NULL) - return -EINVAL; - - name = nla_data(tb[NFTA_COMPAT_NAME]); - rev = ntohl(nla_get_be32(tb[NFTA_COMPAT_REV])); - target = ntohl(nla_get_be32(tb[NFTA_COMPAT_TYPE])); - - nfmsg = nlmsg_data(nlh); - - switch(nfmsg->nfgen_family) { - case AF_INET: - fmt = "ipt_%s"; - break; - case AF_INET6: - fmt = "ip6t_%s"; - break; - default: - pr_err("nft_compat: unsupported protocol %d\n", - nfmsg->nfgen_family); - return -EINVAL; - } - - try_then_request_module(xt_find_revision(nfmsg->nfgen_family, name, - rev, target, &ret), - fmt, name); - - if (ret < 0) - return ret; - - skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (skb2 == NULL) - return -ENOMEM; - - /* include the best revision for this extension in the message */ - if (nfnl_compat_fill_info(skb2, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, - NFNL_MSG_TYPE(nlh->nlmsg_type), - NFNL_MSG_COMPAT_GET, - nfmsg->nfgen_family, - name, ret, target) <= 0) { - kfree_skb(skb2); - return -ENOSPC; - } - - ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, - MSG_DONTWAIT); - if (ret > 0) - ret = 0; - - return ret == -EAGAIN ? -ENOBUFS : ret; -} - -static const struct nla_policy nfnl_compat_policy_get[NFTA_COMPAT_MAX+1] = { - [NFTA_COMPAT_NAME] = { .type = NLA_NUL_STRING, - .len = NFT_COMPAT_NAME_MAX-1 }, - [NFTA_COMPAT_REV] = { .type = NLA_U32 }, - [NFTA_COMPAT_TYPE] = { .type = NLA_U32 }, -}; - -static const struct nfnl_callback nfnl_nft_compat_cb[NFNL_MSG_COMPAT_MAX] = { - [NFNL_MSG_COMPAT_GET] = { .call = nfnl_compat_get, - .attr_count = NFTA_COMPAT_MAX, - .policy = nfnl_compat_policy_get }, -}; - -static const struct nfnetlink_subsystem nfnl_compat_subsys = { - .name = "nft-compat", - .subsys_id = NFNL_SUBSYS_NFT_COMPAT, - .cb_count = NFNL_MSG_COMPAT_MAX, - .cb = nfnl_nft_compat_cb, -}; - -static LIST_HEAD(nft_match_list); - -struct nft_xt { - struct list_head head; - struct nft_expr_ops ops; -}; - -static struct nft_expr_type nft_match_type; - -static const struct nft_expr_ops * -nft_match_select_ops(const struct nft_ctx *ctx, - const struct nlattr * const tb[]) -{ - struct nft_xt *nft_match; - struct xt_match *match; - char *mt_name; - __u32 rev, family; - - if (tb[NFTA_MATCH_NAME] == NULL || - tb[NFTA_MATCH_REV] == NULL || - tb[NFTA_MATCH_INFO] == NULL) - return ERR_PTR(-EINVAL); - - mt_name = nla_data(tb[NFTA_MATCH_NAME]); - rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV])); - family = ctx->afi->family; - - /* Re-use the existing match if it's already loaded. */ - list_for_each_entry(nft_match, &nft_match_list, head) { - struct xt_match *match = nft_match->ops.data; - - if (strcmp(match->name, mt_name) == 0 && - match->revision == rev && match->family == family) - return &nft_match->ops; - } - - match = xt_request_find_match(family, mt_name, rev); - if (IS_ERR(match)) - return ERR_PTR(-ENOENT); - - /* This is the first time we use this match, allocate operations */ - nft_match = kzalloc(sizeof(struct nft_xt), GFP_KERNEL); - if (nft_match == NULL) - return ERR_PTR(-ENOMEM); - - nft_match->ops.type = &nft_match_type; - nft_match->ops.size = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize) + - nft_compat_match_offset(match)); - nft_match->ops.eval = nft_match_eval; - nft_match->ops.init = nft_match_init; - nft_match->ops.destroy = nft_match_destroy; - nft_match->ops.dump = nft_match_dump; - nft_match->ops.validate = nft_match_validate; - nft_match->ops.data = match; - - list_add(&nft_match->head, &nft_match_list); - - return &nft_match->ops; -} - -static void nft_match_release(void) -{ - struct nft_xt *nft_match, *tmp; - - list_for_each_entry_safe(nft_match, tmp, &nft_match_list, head) - kfree(nft_match); -} - -static struct nft_expr_type nft_match_type __read_mostly = { - .name = "match", - .select_ops = nft_match_select_ops, - .policy = nft_match_policy, - .maxattr = NFTA_MATCH_MAX, - .owner = THIS_MODULE, -}; - -static LIST_HEAD(nft_target_list); - -static struct nft_expr_type nft_target_type; - -static const struct nft_expr_ops * -nft_target_select_ops(const struct nft_ctx *ctx, - const struct nlattr * const tb[]) -{ - struct nft_xt *nft_target; - struct xt_target *target; - char *tg_name; - __u32 rev, family; - - if (tb[NFTA_TARGET_NAME] == NULL || - tb[NFTA_TARGET_REV] == NULL || - tb[NFTA_TARGET_INFO] == NULL) - return ERR_PTR(-EINVAL); - - tg_name = nla_data(tb[NFTA_TARGET_NAME]); - rev = ntohl(nla_get_be32(tb[NFTA_TARGET_REV])); - family = ctx->afi->family; - - /* Re-use the existing target if it's already loaded. */ - list_for_each_entry(nft_target, &nft_match_list, head) { - struct xt_target *target = nft_target->ops.data; - - if (strcmp(target->name, tg_name) == 0 && - target->revision == rev && target->family == family) - return &nft_target->ops; - } - - target = xt_request_find_target(family, tg_name, rev); - if (IS_ERR(target)) - return ERR_PTR(-ENOENT); - - /* This is the first time we use this target, allocate operations */ - nft_target = kzalloc(sizeof(struct nft_xt), GFP_KERNEL); - if (nft_target == NULL) - return ERR_PTR(-ENOMEM); - - nft_target->ops.type = &nft_target_type; - nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize) + - nft_compat_target_offset(target)); - nft_target->ops.eval = nft_target_eval; - nft_target->ops.init = nft_target_init; - nft_target->ops.destroy = nft_target_destroy; - nft_target->ops.dump = nft_target_dump; - nft_target->ops.validate = nft_target_validate; - nft_target->ops.data = target; - - list_add(&nft_target->head, &nft_target_list); - - return &nft_target->ops; -} - -static void nft_target_release(void) -{ - struct nft_xt *nft_target, *tmp; - - list_for_each_entry_safe(nft_target, tmp, &nft_target_list, head) - kfree(nft_target); -} - -static struct nft_expr_type nft_target_type __read_mostly = { - .name = "target", - .select_ops = nft_target_select_ops, - .policy = nft_target_policy, - .maxattr = NFTA_TARGET_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_compat_module_init(void) -{ - int ret; - - ret = nft_register_expr(&nft_match_type); - if (ret < 0) - return ret; - - ret = nft_register_expr(&nft_target_type); - if (ret < 0) - goto err_match; - - ret = nfnetlink_subsys_register(&nfnl_compat_subsys); - if (ret < 0) { - pr_err("nft_compat: cannot register with nfnetlink.\n"); - goto err_target; - } - - pr_info("nf_tables_compat: (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>\n"); - - return ret; - -err_target: - nft_unregister_expr(&nft_target_type); -err_match: - nft_unregister_expr(&nft_match_type); - return ret; -} - -static void __exit nft_compat_module_exit(void) -{ - nfnetlink_subsys_unregister(&nfnl_compat_subsys); - nft_unregister_expr(&nft_target_type); - nft_unregister_expr(&nft_match_type); - nft_match_release(); - nft_target_release(); -} - -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT); - -module_init(nft_compat_module_init); -module_exit(nft_compat_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); -MODULE_ALIAS_NFT_EXPR("match"); -MODULE_ALIAS_NFT_EXPR("target"); diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c deleted file mode 100644 index c89ee48..0000000 --- a/net/netfilter/nft_counter.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/seqlock.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> - -struct nft_counter { - seqlock_t lock; - u64 bytes; - u64 packets; -}; - -static void nft_counter_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - struct nft_counter *priv = nft_expr_priv(expr); - - write_seqlock_bh(&priv->lock); - priv->bytes += pkt->skb->len; - priv->packets++; - write_sequnlock_bh(&priv->lock); -} - -static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - struct nft_counter *priv = nft_expr_priv(expr); - unsigned int seq; - u64 bytes; - u64 packets; - - do { - seq = read_seqbegin(&priv->lock); - bytes = priv->bytes; - packets = priv->packets; - } while (read_seqretry(&priv->lock, seq)); - - if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(bytes))) - goto nla_put_failure; - if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(packets))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { - [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, - [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, -}; - -static int nft_counter_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_counter *priv = nft_expr_priv(expr); - - if (tb[NFTA_COUNTER_PACKETS]) - priv->packets = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); - if (tb[NFTA_COUNTER_BYTES]) - priv->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); - - seqlock_init(&priv->lock); - return 0; -} - -static struct nft_expr_type nft_counter_type; -static const struct nft_expr_ops nft_counter_ops = { - .type = &nft_counter_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_counter)), - .eval = nft_counter_eval, - .init = nft_counter_init, - .dump = nft_counter_dump, -}; - -static struct nft_expr_type nft_counter_type __read_mostly = { - .name = "counter", - .ops = &nft_counter_ops, - .policy = nft_counter_policy, - .maxattr = NFTA_COUNTER_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_counter_module_init(void) -{ - return nft_register_expr(&nft_counter_type); -} - -static void __exit nft_counter_module_exit(void) -{ - nft_unregister_expr(&nft_counter_type); -} - -module_init(nft_counter_module_init); -module_exit(nft_counter_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_EXPR("counter"); diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c deleted file mode 100644 index 955f4e6..0000000 --- a/net/netfilter/nft_ct.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> -#include <net/netfilter/nf_conntrack.h> -#include <net/netfilter/nf_conntrack_tuple.h> -#include <net/netfilter/nf_conntrack_helper.h> - -struct nft_ct { - enum nft_ct_keys key:8; - enum ip_conntrack_dir dir:8; - enum nft_registers dreg:8; - uint8_t family; -}; - -static void nft_ct_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_ct *priv = nft_expr_priv(expr); - struct nft_data *dest = &data[priv->dreg]; - enum ip_conntrack_info ctinfo; - const struct nf_conn *ct; - const struct nf_conn_help *help; - const struct nf_conntrack_tuple *tuple; - const struct nf_conntrack_helper *helper; - long diff; - unsigned int state; - - ct = nf_ct_get(pkt->skb, &ctinfo); - - switch (priv->key) { - case NFT_CT_STATE: - if (ct == NULL) - state = NF_CT_STATE_INVALID_BIT; - else if (nf_ct_is_untracked(ct)) - state = NF_CT_STATE_UNTRACKED_BIT; - else - state = NF_CT_STATE_BIT(ctinfo); - dest->data[0] = state; - return; - } - - if (ct == NULL) - goto err; - - switch (priv->key) { - case NFT_CT_DIRECTION: - dest->data[0] = CTINFO2DIR(ctinfo); - return; - case NFT_CT_STATUS: - dest->data[0] = ct->status; - return; -#ifdef CONFIG_NF_CONNTRACK_MARK - case NFT_CT_MARK: - dest->data[0] = ct->mark; - return; -#endif -#ifdef CONFIG_NF_CONNTRACK_SECMARK - case NFT_CT_SECMARK: - dest->data[0] = ct->secmark; - return; -#endif - case NFT_CT_EXPIRATION: - diff = (long)jiffies - (long)ct->timeout.expires; - if (diff < 0) - diff = 0; - dest->data[0] = jiffies_to_msecs(diff); - return; - case NFT_CT_HELPER: - if (ct->master == NULL) - goto err; - help = nfct_help(ct->master); - if (help == NULL) - goto err; - helper = rcu_dereference(help->helper); - if (helper == NULL) - goto err; - if (strlen(helper->name) >= sizeof(dest->data)) - goto err; - strncpy((char *)dest->data, helper->name, sizeof(dest->data)); - return; - } - - tuple = &ct->tuplehash[priv->dir].tuple; - switch (priv->key) { - case NFT_CT_L3PROTOCOL: - dest->data[0] = nf_ct_l3num(ct); - return; - case NFT_CT_SRC: - memcpy(dest->data, tuple->src.u3.all, - nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); - return; - case NFT_CT_DST: - memcpy(dest->data, tuple->dst.u3.all, - nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); - return; - case NFT_CT_PROTOCOL: - dest->data[0] = nf_ct_protonum(ct); - return; - case NFT_CT_PROTO_SRC: - dest->data[0] = (__force __u16)tuple->src.u.all; - return; - case NFT_CT_PROTO_DST: - dest->data[0] = (__force __u16)tuple->dst.u.all; - return; - } - return; -err: - data[NFT_REG_VERDICT].verdict = NFT_BREAK; -} - -static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { - [NFTA_CT_DREG] = { .type = NLA_U32 }, - [NFTA_CT_KEY] = { .type = NLA_U32 }, - [NFTA_CT_DIRECTION] = { .type = NLA_U8 }, -}; - -static int nft_ct_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_ct *priv = nft_expr_priv(expr); - int err; - - if (tb[NFTA_CT_DREG] == NULL || - tb[NFTA_CT_KEY] == NULL) - return -EINVAL; - - priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); - if (tb[NFTA_CT_DIRECTION] != NULL) { - priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); - switch (priv->dir) { - case IP_CT_DIR_ORIGINAL: - case IP_CT_DIR_REPLY: - break; - default: - return -EINVAL; - } - } - - switch (priv->key) { - case NFT_CT_STATE: - case NFT_CT_DIRECTION: - case NFT_CT_STATUS: -#ifdef CONFIG_NF_CONNTRACK_MARK - case NFT_CT_MARK: -#endif -#ifdef CONFIG_NF_CONNTRACK_SECMARK - case NFT_CT_SECMARK: -#endif - case NFT_CT_EXPIRATION: - case NFT_CT_HELPER: - if (tb[NFTA_CT_DIRECTION] != NULL) - return -EINVAL; - break; - case NFT_CT_PROTOCOL: - case NFT_CT_SRC: - case NFT_CT_DST: - case NFT_CT_PROTO_SRC: - case NFT_CT_PROTO_DST: - if (tb[NFTA_CT_DIRECTION] == NULL) - return -EINVAL; - break; - default: - return -EOPNOTSUPP; - } - - err = nf_ct_l3proto_try_module_get(ctx->afi->family); - if (err < 0) - return err; - priv->family = ctx->afi->family; - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - goto err1; - - err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); - if (err < 0) - goto err1; - return 0; - -err1: - nf_ct_l3proto_module_put(ctx->afi->family); - return err; -} - -static void nft_ct_destroy(const struct nft_expr *expr) -{ - struct nft_ct *priv = nft_expr_priv(expr); - - nf_ct_l3proto_module_put(priv->family); -} - -static int nft_ct_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_ct *priv = nft_expr_priv(expr); - - if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) - goto nla_put_failure; - if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_ct_type; -static const struct nft_expr_ops nft_ct_ops = { - .type = &nft_ct_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), - .eval = nft_ct_eval, - .init = nft_ct_init, - .destroy = nft_ct_destroy, - .dump = nft_ct_dump, -}; - -static struct nft_expr_type nft_ct_type __read_mostly = { - .name = "ct", - .ops = &nft_ct_ops, - .policy = nft_ct_policy, - .maxattr = NFTA_CT_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_ct_module_init(void) -{ - return nft_register_expr(&nft_ct_type); -} - -static void __exit nft_ct_module_exit(void) -{ - nft_unregister_expr(&nft_ct_type); -} - -module_init(nft_ct_module_init); -module_exit(nft_ct_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_EXPR("ct"); diff --git a/net/netfilter/nft_expr_template.c b/net/netfilter/nft_expr_template.c deleted file mode 100644 index b6eed4d..0000000 --- a/net/netfilter/nft_expr_template.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> - -struct nft_template { - -}; - -static void nft_template_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - struct nft_template *priv = nft_expr_priv(expr); - -} - -static const struct nla_policy nft_template_policy[NFTA_TEMPLATE_MAX + 1] = { - [NFTA_TEMPLATE_ATTR] = { .type = NLA_U32 }, -}; - -static int nft_template_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_template *priv = nft_expr_priv(expr); - - return 0; -} - -static void nft_template_destroy(const struct nft_ctx *ctx, - const struct nft_expr *expr) -{ - struct nft_template *priv = nft_expr_priv(expr); - -} - -static int nft_template_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_template *priv = nft_expr_priv(expr); - - NLA_PUT_BE32(skb, NFTA_TEMPLATE_ATTR, priv->field); - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_template_type; -static const struct nft_expr_ops nft_template_ops = { - .type = &nft_template_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_template)), - .eval = nft_template_eval, - .init = nft_template_init, - .destroy = nft_template_destroy, - .dump = nft_template_dump, -}; - -static struct nft_expr_type nft_template_type __read_mostly = { - .name = "template", - .ops = &nft_template_ops, - .policy = nft_template_policy, - .maxattr = NFTA_TEMPLATE_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_template_module_init(void) -{ - return nft_register_expr(&nft_template_type); -} - -static void __exit nft_template_module_exit(void) -{ - nft_unregister_expr(&nft_template_type); -} - -module_init(nft_template_module_init); -module_exit(nft_template_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_EXPR("template"); diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c deleted file mode 100644 index 8e0bb75..0000000 --- a/net/netfilter/nft_exthdr.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> -// FIXME: -#include <net/ipv6.h> - -struct nft_exthdr { - u8 type; - u8 offset; - u8 len; - enum nft_registers dreg:8; -}; - -static void nft_exthdr_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - struct nft_exthdr *priv = nft_expr_priv(expr); - struct nft_data *dest = &data[priv->dreg]; - unsigned int offset; - int err; - - err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL); - if (err < 0) - goto err; - offset += priv->offset; - - if (skb_copy_bits(pkt->skb, offset, dest->data, priv->len) < 0) - goto err; - return; -err: - data[NFT_REG_VERDICT].verdict = NFT_BREAK; -} - -static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { - [NFTA_EXTHDR_DREG] = { .type = NLA_U32 }, - [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 }, - [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 }, - [NFTA_EXTHDR_LEN] = { .type = NLA_U32 }, -}; - -static int nft_exthdr_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_exthdr *priv = nft_expr_priv(expr); - int err; - - if (tb[NFTA_EXTHDR_DREG] == NULL || - tb[NFTA_EXTHDR_TYPE] == NULL || - tb[NFTA_EXTHDR_OFFSET] == NULL || - tb[NFTA_EXTHDR_LEN] == NULL) - return -EINVAL; - - priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); - priv->offset = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OFFSET])); - priv->len = ntohl(nla_get_be32(tb[NFTA_EXTHDR_LEN])); - if (priv->len == 0 || - priv->len > FIELD_SIZEOF(struct nft_data, data)) - return -EINVAL; - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_EXTHDR_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); -} - -static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_exthdr *priv = nft_expr_priv(expr); - - if (nla_put_be32(skb, NFTA_EXTHDR_DREG, htonl(priv->dreg))) - goto nla_put_failure; - if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type)) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_exthdr_type; -static const struct nft_expr_ops nft_exthdr_ops = { - .type = &nft_exthdr_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), - .eval = nft_exthdr_eval, - .init = nft_exthdr_init, - .dump = nft_exthdr_dump, -}; - -static struct nft_expr_type nft_exthdr_type __read_mostly = { - .name = "exthdr", - .ops = &nft_exthdr_ops, - .policy = nft_exthdr_policy, - .maxattr = NFTA_EXTHDR_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_exthdr_module_init(void) -{ - return nft_register_expr(&nft_exthdr_type); -} - -static void __exit nft_exthdr_module_exit(void) -{ - nft_unregister_expr(&nft_exthdr_type); -} - -module_init(nft_exthdr_module_init); -module_exit(nft_exthdr_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_EXPR("exthdr"); diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c deleted file mode 100644 index 3d3f8fc..0000000 --- a/net/netfilter/nft_hash.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/list.h> -#include <linux/jhash.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> - -struct nft_hash { - struct hlist_head *hash; - unsigned int hsize; -}; - -struct nft_hash_elem { - struct hlist_node hnode; - struct nft_data key; - struct nft_data data[]; -}; - -static u32 nft_hash_rnd __read_mostly; -static bool nft_hash_rnd_initted __read_mostly; - -static unsigned int nft_hash_data(const struct nft_data *data, - unsigned int hsize, unsigned int len) -{ - unsigned int h; - - h = jhash(data->data, len, nft_hash_rnd); - return ((u64)h * hsize) >> 32; -} - -static bool nft_hash_lookup(const struct nft_set *set, - const struct nft_data *key, - struct nft_data *data) -{ - const struct nft_hash *priv = nft_set_priv(set); - const struct nft_hash_elem *he; - unsigned int h; - - h = nft_hash_data(key, priv->hsize, set->klen); - hlist_for_each_entry(he, &priv->hash[h], hnode) { - if (nft_data_cmp(&he->key, key, set->klen)) - continue; - if (set->flags & NFT_SET_MAP) - nft_data_copy(data, he->data); - return true; - } - return false; -} - -static void nft_hash_elem_destroy(const struct nft_set *set, - struct nft_hash_elem *he) -{ - nft_data_uninit(&he->key, NFT_DATA_VALUE); - if (set->flags & NFT_SET_MAP) - nft_data_uninit(he->data, set->dtype); - kfree(he); -} - -static int nft_hash_insert(const struct nft_set *set, - const struct nft_set_elem *elem) -{ - struct nft_hash *priv = nft_set_priv(set); - struct nft_hash_elem *he; - unsigned int size, h; - - if (elem->flags != 0) - return -EINVAL; - - size = sizeof(*he); - if (set->flags & NFT_SET_MAP) - size += sizeof(he->data[0]); - - he = kzalloc(size, GFP_KERNEL); - if (he == NULL) - return -ENOMEM; - - nft_data_copy(&he->key, &elem->key); - if (set->flags & NFT_SET_MAP) - nft_data_copy(he->data, &elem->data); - - h = nft_hash_data(&he->key, priv->hsize, set->klen); - hlist_add_head_rcu(&he->hnode, &priv->hash[h]); - return 0; -} - -static void nft_hash_remove(const struct nft_set *set, - const struct nft_set_elem *elem) -{ - struct nft_hash_elem *he = elem->cookie; - - hlist_del_rcu(&he->hnode); - kfree(he); -} - -static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) -{ - const struct nft_hash *priv = nft_set_priv(set); - struct nft_hash_elem *he; - unsigned int h; - - h = nft_hash_data(&elem->key, priv->hsize, set->klen); - hlist_for_each_entry(he, &priv->hash[h], hnode) { - if (nft_data_cmp(&he->key, &elem->key, set->klen)) - continue; - - elem->cookie = he; - elem->flags = 0; - if (set->flags & NFT_SET_MAP) - nft_data_copy(&elem->data, he->data); - return 0; - } - return -ENOENT; -} - -static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, - struct nft_set_iter *iter) -{ - const struct nft_hash *priv = nft_set_priv(set); - const struct nft_hash_elem *he; - struct nft_set_elem elem; - unsigned int i; - - for (i = 0; i < priv->hsize; i++) { - hlist_for_each_entry(he, &priv->hash[i], hnode) { - if (iter->count < iter->skip) - goto cont; - - memcpy(&elem.key, &he->key, sizeof(elem.key)); - if (set->flags & NFT_SET_MAP) - memcpy(&elem.data, he->data, sizeof(elem.data)); - elem.flags = 0; - - iter->err = iter->fn(ctx, set, iter, &elem); - if (iter->err < 0) - return; -cont: - iter->count++; - } - } -} - -static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) -{ - return sizeof(struct nft_hash); -} - -static int nft_hash_init(const struct nft_set *set, - const struct nlattr * const tb[]) -{ - struct nft_hash *priv = nft_set_priv(set); - unsigned int cnt, i; - - if (unlikely(!nft_hash_rnd_initted)) { - get_random_bytes(&nft_hash_rnd, 4); - nft_hash_rnd_initted = true; - } - - /* Aim for a load factor of 0.75 */ - // FIXME: temporarily broken until we have set descriptions - cnt = 100; - cnt = cnt * 4 / 3; - - priv->hash = kcalloc(cnt, sizeof(struct hlist_head), GFP_KERNEL); - if (priv->hash == NULL) - return -ENOMEM; - priv->hsize = cnt; - - for (i = 0; i < cnt; i++) - INIT_HLIST_HEAD(&priv->hash[i]); - - return 0; -} - -static void nft_hash_destroy(const struct nft_set *set) -{ - const struct nft_hash *priv = nft_set_priv(set); - const struct hlist_node *next; - struct nft_hash_elem *elem; - unsigned int i; - - for (i = 0; i < priv->hsize; i++) { - hlist_for_each_entry_safe(elem, next, &priv->hash[i], hnode) { - hlist_del(&elem->hnode); - nft_hash_elem_destroy(set, elem); - } - } - kfree(priv->hash); -} - -static struct nft_set_ops nft_hash_ops __read_mostly = { - .privsize = nft_hash_privsize, - .init = nft_hash_init, - .destroy = nft_hash_destroy, - .get = nft_hash_get, - .insert = nft_hash_insert, - .remove = nft_hash_remove, - .lookup = nft_hash_lookup, - .walk = nft_hash_walk, - .features = NFT_SET_MAP, - .owner = THIS_MODULE, -}; - -static int __init nft_hash_module_init(void) -{ - return nft_register_set(&nft_hash_ops); -} - -static void __exit nft_hash_module_exit(void) -{ - nft_unregister_set(&nft_hash_ops); -} - -module_init(nft_hash_module_init); -module_exit(nft_hash_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_SET(); diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c deleted file mode 100644 index f169501..0000000 --- a/net/netfilter/nft_immediate.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables_core.h> -#include <net/netfilter/nf_tables.h> - -struct nft_immediate_expr { - struct nft_data data; - enum nft_registers dreg:8; - u8 dlen; -}; - -static void nft_immediate_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_immediate_expr *priv = nft_expr_priv(expr); - - nft_data_copy(&data[priv->dreg], &priv->data); -} - -static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = { - [NFTA_IMMEDIATE_DREG] = { .type = NLA_U32 }, - [NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED }, -}; - -static int nft_immediate_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_immediate_expr *priv = nft_expr_priv(expr); - struct nft_data_desc desc; - int err; - - if (tb[NFTA_IMMEDIATE_DREG] == NULL || - tb[NFTA_IMMEDIATE_DATA] == NULL) - return -EINVAL; - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_IMMEDIATE_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - - err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]); - if (err < 0) - return err; - priv->dlen = desc.len; - - err = nft_validate_data_load(ctx, priv->dreg, &priv->data, desc.type); - if (err < 0) - goto err1; - - return 0; - -err1: - nft_data_uninit(&priv->data, desc.type); - return err; -} - -static void nft_immediate_destroy(const struct nft_expr *expr) -{ - const struct nft_immediate_expr *priv = nft_expr_priv(expr); - return nft_data_uninit(&priv->data, nft_dreg_to_type(priv->dreg)); -} - -static int nft_immediate_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_immediate_expr *priv = nft_expr_priv(expr); - - if (nla_put_be32(skb, NFTA_IMMEDIATE_DREG, htonl(priv->dreg))) - goto nla_put_failure; - - return nft_data_dump(skb, NFTA_IMMEDIATE_DATA, &priv->data, - nft_dreg_to_type(priv->dreg), priv->dlen); - -nla_put_failure: - return -1; -} - -static int nft_immediate_validate(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nft_data **data) -{ - const struct nft_immediate_expr *priv = nft_expr_priv(expr); - - if (priv->dreg == NFT_REG_VERDICT) - *data = &priv->data; - - return 0; -} - -static struct nft_expr_type nft_imm_type; -static const struct nft_expr_ops nft_imm_ops = { - .type = &nft_imm_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), - .eval = nft_immediate_eval, - .init = nft_immediate_init, - .destroy = nft_immediate_destroy, - .dump = nft_immediate_dump, - .validate = nft_immediate_validate, -}; - -static struct nft_expr_type nft_imm_type __read_mostly = { - .name = "immediate", - .ops = &nft_imm_ops, - .policy = nft_immediate_policy, - .maxattr = NFTA_IMMEDIATE_MAX, - .owner = THIS_MODULE, -}; - -int __init nft_immediate_module_init(void) -{ - return nft_register_expr(&nft_imm_type); -} - -void nft_immediate_module_exit(void) -{ - nft_unregister_expr(&nft_imm_type); -} diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c deleted file mode 100644 index 85da5bd..0000000 --- a/net/netfilter/nft_limit.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> - -static DEFINE_SPINLOCK(limit_lock); - -struct nft_limit { - u64 tokens; - u64 rate; - u64 unit; - unsigned long stamp; -}; - -static void nft_limit_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - struct nft_limit *priv = nft_expr_priv(expr); - - spin_lock_bh(&limit_lock); - if (time_after_eq(jiffies, priv->stamp)) { - priv->tokens = priv->rate; - priv->stamp = jiffies + priv->unit * HZ; - } - - if (priv->tokens >= 1) { - priv->tokens--; - spin_unlock_bh(&limit_lock); - return; - } - spin_unlock_bh(&limit_lock); - - data[NFT_REG_VERDICT].verdict = NFT_BREAK; -} - -static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = { - [NFTA_LIMIT_RATE] = { .type = NLA_U64 }, - [NFTA_LIMIT_UNIT] = { .type = NLA_U64 }, -}; - -static int nft_limit_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_limit *priv = nft_expr_priv(expr); - - if (tb[NFTA_LIMIT_RATE] == NULL || - tb[NFTA_LIMIT_UNIT] == NULL) - return -EINVAL; - - priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE])); - priv->unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT])); - priv->stamp = jiffies + priv->unit * HZ; - priv->tokens = priv->rate; - return 0; -} - -static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_limit *priv = nft_expr_priv(expr); - - if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate))) - goto nla_put_failure; - if (nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(priv->unit))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_limit_type; -static const struct nft_expr_ops nft_limit_ops = { - .type = &nft_limit_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_limit)), - .eval = nft_limit_eval, - .init = nft_limit_init, - .dump = nft_limit_dump, -}; - -static struct nft_expr_type nft_limit_type __read_mostly = { - .name = "limit", - .ops = &nft_limit_ops, - .policy = nft_limit_policy, - .maxattr = NFTA_LIMIT_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_limit_module_init(void) -{ - return nft_register_expr(&nft_limit_type); -} - -static void __exit nft_limit_module_exit(void) -{ - nft_unregister_expr(&nft_limit_type); -} - -module_init(nft_limit_module_init); -module_exit(nft_limit_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_EXPR("limit"); diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c deleted file mode 100644 index 57cad07..0000000 --- a/net/netfilter/nft_log.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> -#include <net/netfilter/nf_log.h> -#include <linux/netdevice.h> - -static const char *nft_log_null_prefix = ""; - -struct nft_log { - struct nf_loginfo loginfo; - char *prefix; - int family; -}; - -static void nft_log_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_log *priv = nft_expr_priv(expr); - struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); - - nf_log_packet(net, priv->family, pkt->hooknum, pkt->skb, pkt->in, - pkt->out, &priv->loginfo, "%s", priv->prefix); -} - -static const struct nla_policy nft_log_policy[NFTA_LOG_MAX + 1] = { - [NFTA_LOG_GROUP] = { .type = NLA_U16 }, - [NFTA_LOG_PREFIX] = { .type = NLA_STRING }, - [NFTA_LOG_SNAPLEN] = { .type = NLA_U32 }, - [NFTA_LOG_QTHRESHOLD] = { .type = NLA_U16 }, -}; - -static int nft_log_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_log *priv = nft_expr_priv(expr); - struct nf_loginfo *li = &priv->loginfo; - const struct nlattr *nla; - - priv->family = ctx->afi->family; - - nla = tb[NFTA_LOG_PREFIX]; - if (nla != NULL) { - priv->prefix = kmalloc(nla_len(nla) + 1, GFP_KERNEL); - if (priv->prefix == NULL) - return -ENOMEM; - nla_strlcpy(priv->prefix, nla, nla_len(nla) + 1); - } else - priv->prefix = (char *)nft_log_null_prefix; - - li->type = NF_LOG_TYPE_ULOG; - if (tb[NFTA_LOG_GROUP] != NULL) - li->u.ulog.group = ntohs(nla_get_be16(tb[NFTA_LOG_GROUP])); - - if (tb[NFTA_LOG_SNAPLEN] != NULL) - li->u.ulog.copy_len = ntohl(nla_get_be32(tb[NFTA_LOG_SNAPLEN])); - if (tb[NFTA_LOG_QTHRESHOLD] != NULL) { - li->u.ulog.qthreshold = - ntohs(nla_get_be16(tb[NFTA_LOG_QTHRESHOLD])); - } - - return 0; -} - -static void nft_log_destroy(const struct nft_expr *expr) -{ - struct nft_log *priv = nft_expr_priv(expr); - - if (priv->prefix != nft_log_null_prefix) - kfree(priv->prefix); -} - -static int nft_log_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_log *priv = nft_expr_priv(expr); - const struct nf_loginfo *li = &priv->loginfo; - - if (priv->prefix != nft_log_null_prefix) - if (nla_put_string(skb, NFTA_LOG_PREFIX, priv->prefix)) - goto nla_put_failure; - if (li->u.ulog.group) - if (nla_put_be16(skb, NFTA_LOG_GROUP, htons(li->u.ulog.group))) - goto nla_put_failure; - if (li->u.ulog.copy_len) - if (nla_put_be32(skb, NFTA_LOG_SNAPLEN, - htonl(li->u.ulog.copy_len))) - goto nla_put_failure; - if (li->u.ulog.qthreshold) - if (nla_put_be16(skb, NFTA_LOG_QTHRESHOLD, - htons(li->u.ulog.qthreshold))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_log_type; -static const struct nft_expr_ops nft_log_ops = { - .type = &nft_log_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_log)), - .eval = nft_log_eval, - .init = nft_log_init, - .destroy = nft_log_destroy, - .dump = nft_log_dump, -}; - -static struct nft_expr_type nft_log_type __read_mostly = { - .name = "log", - .ops = &nft_log_ops, - .policy = nft_log_policy, - .maxattr = NFTA_LOG_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_log_module_init(void) -{ - return nft_register_expr(&nft_log_type); -} - -static void __exit nft_log_module_exit(void) -{ - nft_unregister_expr(&nft_log_type); -} - -module_init(nft_log_module_init); -module_exit(nft_log_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_EXPR("log"); diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c deleted file mode 100644 index 8a6116b..0000000 --- a/net/netfilter/nft_lookup.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/rbtree.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> - -struct nft_lookup { - struct nft_set *set; - enum nft_registers sreg:8; - enum nft_registers dreg:8; - struct nft_set_binding binding; -}; - -static void nft_lookup_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_lookup *priv = nft_expr_priv(expr); - const struct nft_set *set = priv->set; - - if (set->ops->lookup(set, &data[priv->sreg], &data[priv->dreg])) - return; - data[NFT_REG_VERDICT].verdict = NFT_BREAK; -} - -static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = { - [NFTA_LOOKUP_SET] = { .type = NLA_STRING }, - [NFTA_LOOKUP_SREG] = { .type = NLA_U32 }, - [NFTA_LOOKUP_DREG] = { .type = NLA_U32 }, -}; - -static int nft_lookup_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_lookup *priv = nft_expr_priv(expr); - struct nft_set *set; - int err; - - if (tb[NFTA_LOOKUP_SET] == NULL || - tb[NFTA_LOOKUP_SREG] == NULL) - return -EINVAL; - - set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]); - if (IS_ERR(set)) - return PTR_ERR(set); - - priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG])); - err = nft_validate_input_register(priv->sreg); - if (err < 0) - return err; - - if (tb[NFTA_LOOKUP_DREG] != NULL) { - if (!(set->flags & NFT_SET_MAP)) - return -EINVAL; - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - - if (priv->dreg == NFT_REG_VERDICT) { - if (set->dtype != NFT_DATA_VERDICT) - return -EINVAL; - } else if (set->dtype == NFT_DATA_VERDICT) - return -EINVAL; - } else if (set->flags & NFT_SET_MAP) - return -EINVAL; - - err = nf_tables_bind_set(ctx, set, &priv->binding); - if (err < 0) - return err; - - priv->set = set; - return 0; -} - -static void nft_lookup_destroy(const struct nft_expr *expr) -{ - struct nft_lookup *priv = nft_expr_priv(expr); - - nf_tables_unbind_set(NULL, priv->set, &priv->binding); -} - -static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_lookup *priv = nft_expr_priv(expr); - - if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name)) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_LOOKUP_SREG, htonl(priv->sreg))) - goto nla_put_failure; - if (priv->set->flags & NFT_SET_MAP) - if (nla_put_be32(skb, NFTA_LOOKUP_DREG, htonl(priv->dreg))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_lookup_type; -static const struct nft_expr_ops nft_lookup_ops = { - .type = &nft_lookup_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), - .eval = nft_lookup_eval, - .init = nft_lookup_init, - .destroy = nft_lookup_destroy, - .dump = nft_lookup_dump, -}; - -static struct nft_expr_type nft_lookup_type __read_mostly = { - .name = "lookup", - .ops = &nft_lookup_ops, - .policy = nft_lookup_policy, - .maxattr = NFTA_LOOKUP_MAX, - .owner = THIS_MODULE, -}; - -int __init nft_lookup_module_init(void) -{ - return nft_register_expr(&nft_lookup_type); -} - -void nft_lookup_module_exit(void) -{ - nft_unregister_expr(&nft_lookup_type); -} diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c deleted file mode 100644 index 8c28220..0000000 --- a/net/netfilter/nft_meta.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/dst.h> -#include <net/sock.h> -#include <net/tcp_states.h> /* for TCP_TIME_WAIT */ -#include <net/netfilter/nf_tables.h> - -struct nft_meta { - enum nft_meta_keys key:8; - enum nft_registers dreg:8; -}; - -static void nft_meta_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_meta *priv = nft_expr_priv(expr); - const struct sk_buff *skb = pkt->skb; - const struct net_device *in = pkt->in, *out = pkt->out; - struct nft_data *dest = &data[priv->dreg]; - - switch (priv->key) { - case NFT_META_LEN: - dest->data[0] = skb->len; - break; - case NFT_META_PROTOCOL: - *(__be16 *)dest->data = skb->protocol; - break; - case NFT_META_PRIORITY: - dest->data[0] = skb->priority; - break; - case NFT_META_MARK: - dest->data[0] = skb->mark; - break; - case NFT_META_IIF: - if (in == NULL) - goto err; - dest->data[0] = in->ifindex; - break; - case NFT_META_OIF: - if (out == NULL) - goto err; - dest->data[0] = out->ifindex; - break; - case NFT_META_IIFNAME: - if (in == NULL) - goto err; - strncpy((char *)dest->data, in->name, sizeof(dest->data)); - break; - case NFT_META_OIFNAME: - if (out == NULL) - goto err; - strncpy((char *)dest->data, out->name, sizeof(dest->data)); - break; - case NFT_META_IIFTYPE: - if (in == NULL) - goto err; - *(u16 *)dest->data = in->type; - break; - case NFT_META_OIFTYPE: - if (out == NULL) - goto err; - *(u16 *)dest->data = out->type; - break; - case NFT_META_SKUID: - if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT) - goto err; - - read_lock_bh(&skb->sk->sk_callback_lock); - if (skb->sk->sk_socket == NULL || - skb->sk->sk_socket->file == NULL) { - read_unlock_bh(&skb->sk->sk_callback_lock); - goto err; - } - - dest->data[0] = - from_kuid_munged(&init_user_ns, - skb->sk->sk_socket->file->f_cred->fsuid); - read_unlock_bh(&skb->sk->sk_callback_lock); - break; - case NFT_META_SKGID: - if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT) - goto err; - - read_lock_bh(&skb->sk->sk_callback_lock); - if (skb->sk->sk_socket == NULL || - skb->sk->sk_socket->file == NULL) { - read_unlock_bh(&skb->sk->sk_callback_lock); - goto err; - } - dest->data[0] = - from_kgid_munged(&init_user_ns, - skb->sk->sk_socket->file->f_cred->fsgid); - read_unlock_bh(&skb->sk->sk_callback_lock); - break; -#ifdef CONFIG_NET_CLS_ROUTE - case NFT_META_RTCLASSID: { - const struct dst_entry *dst = skb_dst(skb); - - if (dst == NULL) - goto err; - dest->data[0] = dst->tclassid; - break; - } -#endif -#ifdef CONFIG_NETWORK_SECMARK - case NFT_META_SECMARK: - dest->data[0] = skb->secmark; - break; -#endif - default: - WARN_ON(1); - goto err; - } - return; - -err: - data[NFT_REG_VERDICT].verdict = NFT_BREAK; -} - -static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { - [NFTA_META_DREG] = { .type = NLA_U32 }, - [NFTA_META_KEY] = { .type = NLA_U32 }, -}; - -static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_meta *priv = nft_expr_priv(expr); - int err; - - if (tb[NFTA_META_DREG] == NULL || - tb[NFTA_META_KEY] == NULL) - return -EINVAL; - - priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); - switch (priv->key) { - case NFT_META_LEN: - case NFT_META_PROTOCOL: - case NFT_META_PRIORITY: - case NFT_META_MARK: - case NFT_META_IIF: - case NFT_META_OIF: - case NFT_META_IIFNAME: - case NFT_META_OIFNAME: - case NFT_META_IIFTYPE: - case NFT_META_OIFTYPE: - case NFT_META_SKUID: - case NFT_META_SKGID: -#ifdef CONFIG_NET_CLS_ROUTE - case NFT_META_RTCLASSID: -#endif -#ifdef CONFIG_NETWORK_SECMARK - case NFT_META_SECMARK: -#endif - break; - default: - return -EOPNOTSUPP; - } - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); -} - -static int nft_meta_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_meta *priv = nft_expr_priv(expr); - - if (nla_put_be32(skb, NFTA_META_DREG, htonl(priv->dreg))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_meta_type; -static const struct nft_expr_ops nft_meta_ops = { - .type = &nft_meta_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), - .eval = nft_meta_eval, - .init = nft_meta_init, - .dump = nft_meta_dump, -}; - -static struct nft_expr_type nft_meta_type __read_mostly = { - .name = "meta", - .ops = &nft_meta_ops, - .policy = nft_meta_policy, - .maxattr = NFTA_META_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_meta_module_init(void) -{ - return nft_register_expr(&nft_meta_type); -} - -static void __exit nft_meta_module_exit(void) -{ - nft_unregister_expr(&nft_meta_type); -} - -module_init(nft_meta_module_init); -module_exit(nft_meta_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_EXPR("meta"); diff --git a/net/netfilter/nft_meta_target.c b/net/netfilter/nft_meta_target.c deleted file mode 100644 index 71177df..0000000 --- a/net/netfilter/nft_meta_target.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/rbtree.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> - -struct nft_meta { - enum nft_meta_keys key; -}; - -static void nft_meta_eval(const struct nft_expr *expr, - struct nft_data *nfres, - struct nft_data *data, - const struct nft_pktinfo *pkt) -{ - const struct nft_meta *meta = nft_expr_priv(expr); - struct sk_buff *skb = pkt->skb; - u32 val = data->data[0]; - - switch (meta->key) { - case NFT_META_MARK: - skb->mark = val; - break; - case NFT_META_PRIORITY: - skb->priority = val; - break; - case NFT_META_NFTRACE: - skb->nf_trace = val; - break; -#ifdef CONFIG_NETWORK_SECMARK - case NFT_META_SECMARK: - skb->secmark = val; - break; -#endif - default: - WARN_ON(1); - } -} - -static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { - [NFTA_META_KEY] = { .type = NLA_U32 }, -}; - -static int nft_meta_init(const struct nft_expr *expr, struct nlattr *tb[]) -{ - struct nft_meta *meta = nft_expr_priv(expr); - - if (tb[NFTA_META_KEY] == NULL) - return -EINVAL; - - meta->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); - switch (meta->key) { - case NFT_META_MARK: - case NFT_META_PRIORITY: - case NFT_META_NFTRACE: -#ifdef CONFIG_NETWORK_SECMARK - case NFT_META_SECMARK: -#endif - break; - default: - return -EINVAL; - } - - return 0; -} - -static int nft_meta_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - struct nft_meta *meta = nft_expr_priv(expr); - - NLA_PUT_BE32(skb, NFTA_META_KEY, htonl(meta->key)); - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_ops meta_target __read_mostly = { - .name = "meta", - .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), - .owner = THIS_MODULE, - .eval = nft_meta_eval, - .init = nft_meta_init, - .dump = nft_meta_dump, - .policy = nft_meta_policy, - .maxattr = NFTA_META_MAX, -}; - -static int __init nft_meta_target_init(void) -{ - return nft_register_expr(&meta_target); -} - -static void __exit nft_meta_target_exit(void) -{ - nft_unregister_expr(&meta_target); -} - -module_init(nft_meta_target_init); -module_exit(nft_meta_target_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_EXPR("meta"); diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c deleted file mode 100644 index d3b1ffe..0000000 --- a/net/netfilter/nft_nat.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org> - * Copyright (c) 2012 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/ip.h> -#include <linux/string.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter_ipv4.h> -#include <linux/netfilter/nfnetlink.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_conntrack.h> -#include <net/netfilter/nf_nat.h> -#include <net/netfilter/nf_nat_core.h> -#include <net/netfilter/nf_tables.h> -#include <net/netfilter/nf_nat_l3proto.h> -#include <net/ip.h> - -struct nft_nat { - enum nft_registers sreg_addr_min:8; - enum nft_registers sreg_addr_max:8; - enum nft_registers sreg_proto_min:8; - enum nft_registers sreg_proto_max:8; - int family; - enum nf_nat_manip_type type; -}; - -static void nft_nat_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_nat *priv = nft_expr_priv(expr); - enum ip_conntrack_info ctinfo; - struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); - struct nf_nat_range range; - - memset(&range, 0, sizeof(range)); - if (priv->sreg_addr_min) { - if (priv->family == AF_INET) { - range.min_addr.ip = (__force __be32) - data[priv->sreg_addr_min].data[0]; - range.max_addr.ip = (__force __be32) - data[priv->sreg_addr_max].data[0]; - - } else { - memcpy(range.min_addr.ip6, - data[priv->sreg_addr_min].data, - sizeof(struct nft_data)); - memcpy(range.max_addr.ip6, - data[priv->sreg_addr_max].data, - sizeof(struct nft_data)); - } - range.flags |= NF_NAT_RANGE_MAP_IPS; - } - - if (priv->sreg_proto_min) { - range.min_proto.all = (__force __be16) - data[priv->sreg_proto_min].data[0]; - range.max_proto.all = (__force __be16) - data[priv->sreg_proto_max].data[0]; - range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; - } - - data[NFT_REG_VERDICT].verdict = - nf_nat_setup_info(ct, &range, priv->type); -} - -static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = { - [NFTA_NAT_TYPE] = { .type = NLA_U32 }, - [NFTA_NAT_FAMILY] = { .type = NLA_U32 }, - [NFTA_NAT_REG_ADDR_MIN] = { .type = NLA_U32 }, - [NFTA_NAT_REG_ADDR_MAX] = { .type = NLA_U32 }, - [NFTA_NAT_REG_PROTO_MIN] = { .type = NLA_U32 }, - [NFTA_NAT_REG_PROTO_MAX] = { .type = NLA_U32 }, -}; - -static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_nat *priv = nft_expr_priv(expr); - int err; - - if (tb[NFTA_NAT_TYPE] == NULL) - return -EINVAL; - - switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { - case NFT_NAT_SNAT: - priv->type = NF_NAT_MANIP_SRC; - break; - case NFT_NAT_DNAT: - priv->type = NF_NAT_MANIP_DST; - break; - default: - return -EINVAL; - } - - if (tb[NFTA_NAT_FAMILY] == NULL) - return -EINVAL; - - priv->family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY])); - if (priv->family != AF_INET && priv->family != AF_INET6) - return -EINVAL; - - if (tb[NFTA_NAT_REG_ADDR_MIN]) { - priv->sreg_addr_min = ntohl(nla_get_be32( - tb[NFTA_NAT_REG_ADDR_MIN])); - err = nft_validate_input_register(priv->sreg_addr_min); - if (err < 0) - return err; - } - - if (tb[NFTA_NAT_REG_ADDR_MAX]) { - priv->sreg_addr_max = ntohl(nla_get_be32( - tb[NFTA_NAT_REG_ADDR_MAX])); - err = nft_validate_input_register(priv->sreg_addr_max); - if (err < 0) - return err; - } else - priv->sreg_addr_max = priv->sreg_addr_min; - - if (tb[NFTA_NAT_REG_PROTO_MIN]) { - priv->sreg_proto_min = ntohl(nla_get_be32( - tb[NFTA_NAT_REG_PROTO_MIN])); - err = nft_validate_input_register(priv->sreg_proto_min); - if (err < 0) - return err; - } - - if (tb[NFTA_NAT_REG_PROTO_MAX]) { - priv->sreg_proto_max = ntohl(nla_get_be32( - tb[NFTA_NAT_REG_PROTO_MAX])); - err = nft_validate_input_register(priv->sreg_proto_max); - if (err < 0) - return err; - } else - priv->sreg_proto_max = priv->sreg_proto_min; - - return 0; -} - -static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_nat *priv = nft_expr_priv(expr); - - switch (priv->type) { - case NF_NAT_MANIP_SRC: - if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT))) - goto nla_put_failure; - break; - case NF_NAT_MANIP_DST: - if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT))) - goto nla_put_failure; - break; - } - - if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family))) - goto nla_put_failure; - if (nla_put_be32(skb, - NFTA_NAT_REG_ADDR_MIN, htonl(priv->sreg_addr_min))) - goto nla_put_failure; - if (nla_put_be32(skb, - NFTA_NAT_REG_ADDR_MAX, htonl(priv->sreg_addr_max))) - goto nla_put_failure; - if (nla_put_be32(skb, - NFTA_NAT_REG_PROTO_MIN, htonl(priv->sreg_proto_min))) - goto nla_put_failure; - if (nla_put_be32(skb, - NFTA_NAT_REG_PROTO_MAX, htonl(priv->sreg_proto_max))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_nat_type; -static const struct nft_expr_ops nft_nat_ops = { - .type = &nft_nat_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_nat)), - .eval = nft_nat_eval, - .init = nft_nat_init, - .dump = nft_nat_dump, -}; - -static struct nft_expr_type nft_nat_type __read_mostly = { - .name = "nat", - .ops = &nft_nat_ops, - .policy = nft_nat_policy, - .maxattr = NFTA_NAT_MAX, - .owner = THIS_MODULE, -}; - -static int __init nft_nat_module_init(void) -{ - int err; - - err = nft_register_expr(&nft_nat_type); - if (err < 0) - return err; - - return 0; -} - -static void __exit nft_nat_module_exit(void) -{ - nft_unregister_expr(&nft_nat_type); -} - -module_init(nft_nat_module_init); -module_exit(nft_nat_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>"); -MODULE_ALIAS_NFT_EXPR("nat"); diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c deleted file mode 100644 index a2aeb31..0000000 --- a/net/netfilter/nft_payload.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables_core.h> -#include <net/netfilter/nf_tables.h> - -static void nft_payload_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) -{ - const struct nft_payload *priv = nft_expr_priv(expr); - const struct sk_buff *skb = pkt->skb; - struct nft_data *dest = &data[priv->dreg]; - int offset; - - switch (priv->base) { - case NFT_PAYLOAD_LL_HEADER: - if (!skb_mac_header_was_set(skb)) - goto err; - offset = skb_mac_header(skb) - skb->data; - break; - case NFT_PAYLOAD_NETWORK_HEADER: - offset = skb_network_offset(skb); - break; - case NFT_PAYLOAD_TRANSPORT_HEADER: - offset = pkt->xt.thoff; - break; - default: - BUG(); - } - offset += priv->offset; - - if (skb_copy_bits(skb, offset, dest->data, priv->len) < 0) - goto err; - return; -err: - data[NFT_REG_VERDICT].verdict = NFT_BREAK; -} - -static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = { - [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 }, - [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 }, - [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 }, - [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 }, -}; - -static int nft_payload_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) -{ - struct nft_payload *priv = nft_expr_priv(expr); - int err; - - priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); - priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); - priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); - - priv->dreg = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_DREG])); - err = nft_validate_output_register(priv->dreg); - if (err < 0) - return err; - return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); -} - -static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr) -{ - const struct nft_payload *priv = nft_expr_priv(expr); - - if (nla_put_be32(skb, NFTA_PAYLOAD_DREG, htonl(priv->dreg)) || - nla_put_be32(skb, NFTA_PAYLOAD_BASE, htonl(priv->base)) || - nla_put_be32(skb, NFTA_PAYLOAD_OFFSET, htonl(priv->offset)) || - nla_put_be32(skb, NFTA_PAYLOAD_LEN, htonl(priv->len))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -1; -} - -static struct nft_expr_type nft_payload_type; -static const struct nft_expr_ops nft_payload_ops = { - .type = &nft_payload_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), - .eval = nft_payload_eval, - .init = nft_payload_init, - .dump = nft_payload_dump, -}; - -const struct nft_expr_ops nft_payload_fast_ops = { - .type = &nft_payload_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), - .eval = nft_payload_eval, - .init = nft_payload_init, - .dump = nft_payload_dump, -}; - -static const struct nft_expr_ops * -nft_payload_select_ops(const struct nft_ctx *ctx, - const struct nlattr * const tb[]) -{ - enum nft_payload_bases base; - unsigned int offset, len; - - if (tb[NFTA_PAYLOAD_DREG] == NULL || - tb[NFTA_PAYLOAD_BASE] == NULL || - tb[NFTA_PAYLOAD_OFFSET] == NULL || - tb[NFTA_PAYLOAD_LEN] == NULL) - return ERR_PTR(-EINVAL); - - base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); - switch (base) { - case NFT_PAYLOAD_LL_HEADER: - case NFT_PAYLOAD_NETWORK_HEADER: - case NFT_PAYLOAD_TRANSPORT_HEADER: - break; - default: - return ERR_PTR(-EOPNOTSUPP); - } - - offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); - len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); - if (len == 0 || len > FIELD_SIZEOF(struct nft_data, data)) - return ERR_PTR(-EINVAL); - - if (len <= 4 && IS_ALIGNED(offset, len) && base != NFT_PAYLOAD_LL_HEADER) - return &nft_payload_fast_ops; - else - return &nft_payload_ops; -} - -static struct nft_expr_type nft_payload_type __read_mostly = { - .name = "payload", - .select_ops = nft_payload_select_ops, - .policy = nft_payload_policy, - .maxattr = NFTA_PAYLOAD_MAX, - .owner = THIS_MODULE, -}; - -int __init nft_payload_module_init(void) -{ - return nft_register_expr(&nft_payload_type); -} - -void nft_payload_module_exit(void) -{ - nft_unregister_expr(&nft_payload_type); -} diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c deleted file mode 100644 index ca0c1b2..0000000 --- a/net/netfilter/nft_rbtree.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Development of this code funded by Astaro AG (http://www.astaro.com/) - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/list.h> -#include <linux/rbtree.h> -#include <linux/netlink.h> -#include <linux/netfilter.h> -#include <linux/netfilter/nf_tables.h> -#include <net/netfilter/nf_tables.h> - -struct nft_rbtree { - struct rb_root root; -}; - -struct nft_rbtree_elem { - struct rb_node node; - u16 flags; - struct nft_data key; - struct nft_data data[]; -}; - -static bool nft_rbtree_lookup(const struct nft_set *set, - const struct nft_data *key, - struct nft_data *data) -{ - const struct nft_rbtree *priv = nft_set_priv(set); - const struct nft_rbtree_elem *rbe, *interval = NULL; - const struct rb_node *parent = priv->root.rb_node; - int d; - - while (parent != NULL) { - rbe = rb_entry(parent, struct nft_rbtree_elem, node); - - d = nft_data_cmp(&rbe->key, key, set->klen); - if (d < 0) { - parent = parent->rb_left; - interval = rbe; - } else if (d > 0) - parent = parent->rb_right; - else { -found: - if (rbe->flags & NFT_SET_ELEM_INTERVAL_END) - goto out; - if (set->flags & NFT_SET_MAP) - nft_data_copy(data, rbe->data); - return true; - } - } - - if (set->flags & NFT_SET_INTERVAL && interval != NULL) { - rbe = interval; - goto found; - } -out: - return false; -} - -static void nft_rbtree_elem_destroy(const struct nft_set *set, - struct nft_rbtree_elem *rbe) -{ - nft_data_uninit(&rbe->key, NFT_DATA_VALUE); - if (set->flags & NFT_SET_MAP) - nft_data_uninit(rbe->data, set->dtype); - kfree(rbe); -} - -static int __nft_rbtree_insert(const struct nft_set *set, - struct nft_rbtree_elem *new) -{ - struct nft_rbtree *priv = nft_set_priv(set); - struct nft_rbtree_elem *rbe; - struct rb_node *parent, **p; - int d; - - parent = NULL; - p = &priv->root.rb_node; - while (*p != NULL) { - parent = *p; - rbe = rb_entry(parent, struct nft_rbtree_elem, node); - d = nft_data_cmp(&rbe->key, &new->key, set->klen); - if (d < 0) - p = &parent->rb_left; - else if (d > 0) - p = &parent->rb_right; - else - return -EEXIST; - } - rb_link_node(&new->node, parent, p); - rb_insert_color(&new->node, &priv->root); - return 0; -} - -static int nft_rbtree_insert(const struct nft_set *set, - const struct nft_set_elem *elem) -{ - struct nft_rbtree_elem *rbe; - unsigned int size; - int err; - - size = sizeof(*rbe); - if (set->flags & NFT_SET_MAP) - size += sizeof(rbe->data[0]); - - rbe = kzalloc(size, GFP_KERNEL); - if (rbe == NULL) - return -ENOMEM; - - rbe->flags = elem->flags; - nft_data_copy(&rbe->key, &elem->key); - if (set->flags & NFT_SET_MAP) - nft_data_copy(rbe->data, &elem->data); - - err = __nft_rbtree_insert(set, rbe); - if (err < 0) - kfree(rbe); - return err; -} - -static void nft_rbtree_remove(const struct nft_set *set, - const struct nft_set_elem *elem) -{ - struct nft_rbtree *priv = nft_set_priv(set); - struct nft_rbtree_elem *rbe = elem->cookie; - - rb_erase(&rbe->node, &priv->root); - kfree(rbe); -} - -static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem) -{ - const struct nft_rbtree *priv = nft_set_priv(set); - const struct rb_node *parent = priv->root.rb_node; - struct nft_rbtree_elem *rbe; - int d; - - while (parent != NULL) { - rbe = rb_entry(parent, struct nft_rbtree_elem, node); - - d = nft_data_cmp(&rbe->key, &elem->key, set->klen); - if (d < 0) - parent = parent->rb_left; - else if (d > 0) - parent = parent->rb_right; - else { - elem->cookie = rbe; - if (set->flags & NFT_SET_MAP) - nft_data_copy(&elem->data, rbe->data); - elem->flags = rbe->flags; - return 0; - } - } - return -ENOENT; -} - -static void nft_rbtree_walk(const struct nft_ctx *ctx, - const struct nft_set *set, - struct nft_set_iter *iter) -{ - const struct nft_rbtree *priv = nft_set_priv(set); - const struct nft_rbtree_elem *rbe; - struct nft_set_elem elem; - struct rb_node *node; - - for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) { - if (iter->count < iter->skip) - goto cont; - - rbe = rb_entry(node, struct nft_rbtree_elem, node); - nft_data_copy(&elem.key, &rbe->key); - if (set->flags & NFT_SET_MAP) - nft_data_copy(&elem.data, rbe->data); - elem.flags = rbe->flags; - - iter->err = iter->fn(ctx, set, iter, &elem); - if (iter->err < 0) - return; -cont: - iter->count++; - } -} - -static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[]) -{ - return sizeof(struct nft_rbtree); -} - -static int nft_rbtree_init(const struct nft_set *set, - const struct nlattr * const nla[]) -{ - struct nft_rbtree *priv = nft_set_priv(set); - - priv->root = RB_ROOT; - return 0; -} - -static void nft_rbtree_destroy(const struct nft_set *set) -{ - struct nft_rbtree *priv = nft_set_priv(set); - struct nft_rbtree_elem *rbe; - struct rb_node *node; - - while ((node = priv->root.rb_node) != NULL) { - rb_erase(node, &priv->root); - rbe = rb_entry(node, struct nft_rbtree_elem, node); - nft_rbtree_elem_destroy(set, rbe); - } -} - -static struct nft_set_ops nft_rbtree_ops __read_mostly = { - .privsize = nft_rbtree_privsize, - .init = nft_rbtree_init, - .destroy = nft_rbtree_destroy, - .insert = nft_rbtree_insert, - .remove = nft_rbtree_remove, - .get = nft_rbtree_get, - .lookup = nft_rbtree_lookup, - .walk = nft_rbtree_walk, - .features = NFT_SET_INTERVAL | NFT_SET_MAP, - .owner = THIS_MODULE, -}; - -static int __init nft_rbtree_module_init(void) -{ - return nft_register_set(&nft_rbtree_ops); -} - -static void __exit nft_rbtree_module_exit(void) -{ - nft_unregister_set(&nft_rbtree_ops); -} - -module_init(nft_rbtree_module_init); -module_exit(nft_rbtree_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); -MODULE_ALIAS_NFT_SET(); diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 227aa11..8b03028 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -845,13 +845,8 @@ xt_replace_table(struct xt_table *table, return NULL; } - newinfo->initial_entries = private->initial_entries; - /* - * Ensure contents of newinfo are visible before assigning to - * private. - */ - smp_wmb(); table->private = newinfo; + newinfo->initial_entries = private->initial_entries; /* * Even though table entries have now been swapped, other CPU's diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index ed00fef..1e2fae3 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -147,7 +147,6 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_NFQ_info_v3 *info = par->targinfo; u32 queue = info->queuenum; - int ret; if (info->queues_total > 1) { if (info->flags & NFQ_FLAG_CPU_FANOUT) { @@ -158,11 +157,7 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) queue = nfqueue_hash(skb, par); } - ret = NF_QUEUE_NR(queue); - if (info->flags & NFQ_FLAG_BYPASS) - ret |= NF_VERDICT_FLAG_QUEUE_BYPASS; - - return ret; + return NF_QUEUE_NR(queue); } static struct xt_target nfqueue_tg_reg[] __read_mostly = { diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index e762de5..cd24290 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -43,42 +43,10 @@ optlen(const u_int8_t *opt, unsigned int offset) return opt[offset+1]; } -static u_int32_t tcpmss_reverse_mtu(struct net *net, - const struct sk_buff *skb, - unsigned int family) -{ - struct flowi fl; - const struct nf_afinfo *ai; - struct rtable *rt = NULL; - u_int32_t mtu = ~0U; - - if (family == PF_INET) { - struct flowi4 *fl4 = &fl.u.ip4; - memset(fl4, 0, sizeof(*fl4)); - fl4->daddr = ip_hdr(skb)->saddr; - } else { - struct flowi6 *fl6 = &fl.u.ip6; - - memset(fl6, 0, sizeof(*fl6)); - fl6->daddr = ipv6_hdr(skb)->saddr; - } - rcu_read_lock(); - ai = nf_get_afinfo(family); - if (ai != NULL) - ai->route(net, (struct dst_entry **)&rt, &fl, false); - rcu_read_unlock(); - - if (rt != NULL) { - mtu = dst_mtu(&rt->dst); - dst_release(&rt->dst); - } - return mtu; -} - static int tcpmss_mangle_packet(struct sk_buff *skb, const struct xt_action_param *par, - unsigned int family, + unsigned int in_mtu, unsigned int tcphoff, unsigned int minlen) { @@ -108,9 +76,6 @@ tcpmss_mangle_packet(struct sk_buff *skb, return -1; if (info->mss == XT_TCPMSS_CLAMP_PMTU) { - struct net *net = dev_net(par->in ? par->in : par->out); - unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family); - if (dst_mtu(skb_dst(skb)) <= minlen) { net_err_ratelimited("unknown or invalid path-MTU (%u)\n", dst_mtu(skb_dst(skb))); @@ -200,6 +165,37 @@ tcpmss_mangle_packet(struct sk_buff *skb, return TCPOLEN_MSS; } +static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, + unsigned int family) +{ + struct flowi fl; + const struct nf_afinfo *ai; + struct rtable *rt = NULL; + u_int32_t mtu = ~0U; + + if (family == PF_INET) { + struct flowi4 *fl4 = &fl.u.ip4; + memset(fl4, 0, sizeof(*fl4)); + fl4->daddr = ip_hdr(skb)->saddr; + } else { + struct flowi6 *fl6 = &fl.u.ip6; + + memset(fl6, 0, sizeof(*fl6)); + fl6->daddr = ipv6_hdr(skb)->saddr; + } + rcu_read_lock(); + ai = nf_get_afinfo(family); + if (ai != NULL) + ai->route(&init_net, (struct dst_entry **)&rt, &fl, false); + rcu_read_unlock(); + + if (rt != NULL) { + mtu = dst_mtu(&rt->dst); + dst_release(&rt->dst); + } + return mtu; +} + static unsigned int tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) { @@ -208,7 +204,7 @@ tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) int ret; ret = tcpmss_mangle_packet(skb, par, - PF_INET, + tcpmss_reverse_mtu(skb, PF_INET), iph->ihl * 4, sizeof(*iph) + sizeof(struct tcphdr)); if (ret < 0) @@ -237,7 +233,7 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) if (tcphoff < 0) return NF_DROP; ret = tcpmss_mangle_packet(skb, par, - PF_INET6, + tcpmss_reverse_mtu(skb, PF_INET6), tcphoff, sizeof(*ipv6h) + sizeof(struct tcphdr)); if (ret < 0) diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index ef8a926..5d8a3a3 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -200,7 +200,7 @@ nf_tproxy_get_sock_v6(struct net *net, const u8 protocol, in->ifindex); if (sk) { int connected = (sk->sk_state == TCP_ESTABLISHED); - int wildcard = ipv6_addr_any(&sk->sk_v6_rcv_saddr); + int wildcard = ipv6_addr_any(&inet6_sk(sk)->rcv_saddr); /* NOTE: we return listeners even if bound to * 0.0.0.0, those are filtered out in diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index 1e63461..e595e07 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -26,18 +26,16 @@ connbytes_mt(const struct sk_buff *skb, struct xt_action_param *par) u_int64_t what = 0; /* initialize to make gcc happy */ u_int64_t bytes = 0; u_int64_t pkts = 0; - const struct nf_conn_acct *acct; const struct nf_conn_counter *counters; ct = nf_ct_get(skb, &ctinfo); if (!ct) return false; - acct = nf_conn_acct_find(ct); - if (!acct) + counters = nf_conn_acct_find(ct); + if (!counters) return false; - counters = acct->counter; switch (sinfo->what) { case XT_CONNBYTES_PKTS: switch (sinfo->direction) { diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index 80c2e2d..31790e7 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -81,17 +81,17 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par) struct xt_set_info_match_v0 *info = par->matchinfo; ip_set_id_t index; - index = ip_set_nfnl_get_byindex(par->net, info->match_set.index); + index = ip_set_nfnl_get_byindex(info->match_set.index); if (index == IPSET_INVALID_ID) { - pr_warning("Cannot find set identified by id %u to match\n", + pr_warning("Cannot find set indentified by id %u to match\n", info->match_set.index); return -ENOENT; } if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { pr_warning("Protocol error: set match dimension " "is over the limit!\n"); - ip_set_nfnl_put(par->net, info->match_set.index); + ip_set_nfnl_put(info->match_set.index); return -ERANGE; } @@ -106,104 +106,9 @@ set_match_v0_destroy(const struct xt_mtdtor_param *par) { struct xt_set_info_match_v0 *info = par->matchinfo; - ip_set_nfnl_put(par->net, info->match_set.index); + ip_set_nfnl_put(info->match_set.index); } -/* Revision 1 match */ - -static bool -set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) -{ - const struct xt_set_info_match_v1 *info = par->matchinfo; - ADT_OPT(opt, par->family, info->match_set.dim, - info->match_set.flags, 0, UINT_MAX); - - if (opt.flags & IPSET_RETURN_NOMATCH) - opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH; - - return match_set(info->match_set.index, skb, par, &opt, - info->match_set.flags & IPSET_INV_MATCH); -} - -static int -set_match_v1_checkentry(const struct xt_mtchk_param *par) -{ - struct xt_set_info_match_v1 *info = par->matchinfo; - ip_set_id_t index; - - index = ip_set_nfnl_get_byindex(par->net, info->match_set.index); - - if (index == IPSET_INVALID_ID) { - pr_warning("Cannot find set identified by id %u to match\n", - info->match_set.index); - return -ENOENT; - } - if (info->match_set.dim > IPSET_DIM_MAX) { - pr_warning("Protocol error: set match dimension " - "is over the limit!\n"); - ip_set_nfnl_put(par->net, info->match_set.index); - return -ERANGE; - } - - return 0; -} - -static void -set_match_v1_destroy(const struct xt_mtdtor_param *par) -{ - struct xt_set_info_match_v1 *info = par->matchinfo; - - ip_set_nfnl_put(par->net, info->match_set.index); -} - -/* Revision 3 match */ - -static bool -match_counter(u64 counter, const struct ip_set_counter_match *info) -{ - switch (info->op) { - case IPSET_COUNTER_NONE: - return true; - case IPSET_COUNTER_EQ: - return counter == info->value; - case IPSET_COUNTER_NE: - return counter != info->value; - case IPSET_COUNTER_LT: - return counter < info->value; - case IPSET_COUNTER_GT: - return counter > info->value; - } - return false; -} - -static bool -set_match_v3(const struct sk_buff *skb, struct xt_action_param *par) -{ - const struct xt_set_info_match_v3 *info = par->matchinfo; - ADT_OPT(opt, par->family, info->match_set.dim, - info->match_set.flags, info->flags, UINT_MAX); - int ret; - - if (info->packets.op != IPSET_COUNTER_NONE || - info->bytes.op != IPSET_COUNTER_NONE) - opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; - - ret = match_set(info->match_set.index, skb, par, &opt, - info->match_set.flags & IPSET_INV_MATCH); - - if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) - return ret; - - if (!match_counter(opt.ext.packets, &info->packets)) - return 0; - return match_counter(opt.ext.bytes, &info->bytes); -} - -#define set_match_v3_checkentry set_match_v1_checkentry -#define set_match_v3_destroy set_match_v1_destroy - -/* Revision 0 interface: backward compatible with netfilter/iptables */ - static unsigned int set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) { @@ -228,7 +133,7 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) ip_set_id_t index; if (info->add_set.index != IPSET_INVALID_ID) { - index = ip_set_nfnl_get_byindex(par->net, info->add_set.index); + index = ip_set_nfnl_get_byindex(info->add_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find add_set index %u as target\n", info->add_set.index); @@ -237,12 +142,12 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) } if (info->del_set.index != IPSET_INVALID_ID) { - index = ip_set_nfnl_get_byindex(par->net, info->del_set.index); + index = ip_set_nfnl_get_byindex(info->del_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find del_set index %u as target\n", info->del_set.index); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->add_set.index); + ip_set_nfnl_put(info->add_set.index); return -ENOENT; } } @@ -251,9 +156,9 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) pr_warning("Protocol error: SET target dimension " "is over the limit!\n"); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->add_set.index); + ip_set_nfnl_put(info->add_set.index); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->del_set.index); + ip_set_nfnl_put(info->del_set.index); return -ERANGE; } @@ -270,12 +175,57 @@ set_target_v0_destroy(const struct xt_tgdtor_param *par) const struct xt_set_info_target_v0 *info = par->targinfo; if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->add_set.index); + ip_set_nfnl_put(info->add_set.index); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->del_set.index); + ip_set_nfnl_put(info->del_set.index); +} + +/* Revision 1 match and target */ + +static bool +set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_set_info_match_v1 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.dim, + info->match_set.flags, 0, UINT_MAX); + + if (opt.flags & IPSET_RETURN_NOMATCH) + opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH; + + return match_set(info->match_set.index, skb, par, &opt, + info->match_set.flags & IPSET_INV_MATCH); +} + +static int +set_match_v1_checkentry(const struct xt_mtchk_param *par) +{ + struct xt_set_info_match_v1 *info = par->matchinfo; + ip_set_id_t index; + + index = ip_set_nfnl_get_byindex(info->match_set.index); + + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find set indentified by id %u to match\n", + info->match_set.index); + return -ENOENT; + } + if (info->match_set.dim > IPSET_DIM_MAX) { + pr_warning("Protocol error: set match dimension " + "is over the limit!\n"); + ip_set_nfnl_put(info->match_set.index); + return -ERANGE; + } + + return 0; } -/* Revision 1 target */ +static void +set_match_v1_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_set_info_match_v1 *info = par->matchinfo; + + ip_set_nfnl_put(info->match_set.index); +} static unsigned int set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) @@ -301,7 +251,7 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par) ip_set_id_t index; if (info->add_set.index != IPSET_INVALID_ID) { - index = ip_set_nfnl_get_byindex(par->net, info->add_set.index); + index = ip_set_nfnl_get_byindex(info->add_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find add_set index %u as target\n", info->add_set.index); @@ -310,12 +260,12 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par) } if (info->del_set.index != IPSET_INVALID_ID) { - index = ip_set_nfnl_get_byindex(par->net, info->del_set.index); + index = ip_set_nfnl_get_byindex(info->del_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find del_set index %u as target\n", info->del_set.index); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->add_set.index); + ip_set_nfnl_put(info->add_set.index); return -ENOENT; } } @@ -324,9 +274,9 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par) pr_warning("Protocol error: SET target dimension " "is over the limit!\n"); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->add_set.index); + ip_set_nfnl_put(info->add_set.index); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->del_set.index); + ip_set_nfnl_put(info->del_set.index); return -ERANGE; } @@ -339,9 +289,9 @@ set_target_v1_destroy(const struct xt_tgdtor_param *par) const struct xt_set_info_target_v1 *info = par->targinfo; if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->add_set.index); + ip_set_nfnl_put(info->add_set.index); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(par->net, info->del_set.index); + ip_set_nfnl_put(info->del_set.index); } /* Revision 2 target */ @@ -370,6 +320,52 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) #define set_target_v2_checkentry set_target_v1_checkentry #define set_target_v2_destroy set_target_v1_destroy +/* Revision 3 match */ + +static bool +match_counter(u64 counter, const struct ip_set_counter_match *info) +{ + switch (info->op) { + case IPSET_COUNTER_NONE: + return true; + case IPSET_COUNTER_EQ: + return counter == info->value; + case IPSET_COUNTER_NE: + return counter != info->value; + case IPSET_COUNTER_LT: + return counter < info->value; + case IPSET_COUNTER_GT: + return counter > info->value; + } + return false; +} + +static bool +set_match_v3(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_set_info_match_v3 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.dim, + info->match_set.flags, info->flags, UINT_MAX); + int ret; + + if (info->packets.op != IPSET_COUNTER_NONE || + info->bytes.op != IPSET_COUNTER_NONE) + opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; + + ret = match_set(info->match_set.index, skb, par, &opt, + info->match_set.flags & IPSET_INV_MATCH); + + if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) + return ret; + + if (!match_counter(opt.ext.packets, &info->packets)) + return 0; + return match_counter(opt.ext.bytes, &info->bytes); +} + +#define set_match_v3_checkentry set_match_v1_checkentry +#define set_match_v3_destroy set_match_v1_destroy + static struct xt_match set_matches[] __read_mostly = { { .name = "set", diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 1ba6793..06df2b9 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -35,6 +35,15 @@ #include <net/netfilter/nf_conntrack.h> #endif +static void +xt_socket_put_sk(struct sock *sk) +{ + if (sk->sk_state == TCP_TIME_WAIT) + inet_twsk_put(inet_twsk(sk)); + else + sock_put(sk); +} + static int extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol, @@ -207,7 +216,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, inet_twsk(sk)->tw_transparent)); if (sk != skb->sk) - sock_gen_put(sk); + xt_socket_put_sk(sk); if (wildcard || !transparent) sk = NULL; @@ -361,7 +370,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) */ wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) && sk->sk_state != TCP_TIME_WAIT && - ipv6_addr_any(&sk->sk_v6_rcv_saddr)); + ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)); /* Ignore non-transparent sockets, if XT_SOCKET_TRANSPARENT is used */ @@ -372,7 +381,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) inet_twsk(sk)->tw_transparent)); if (sk != skb->sk) - sock_gen_put(sk); + xt_socket_put_sk(sk); if (wildcard || !transparent) sk = NULL; |