Commit 2ff81cd3 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next

Pablo Neira Ayuso says:

====================
Netfilter updates for next-net (part 2)

The following patchset contains Netfilter updates for net-next. This
patchset includes updates for nf_tables, removal of
CONFIG_NETFILTER_DEBUG and a new mode for xt_hashlimit. More
specifically, they:

1) Add new rate match mode for hashlimit, this introduces a new revision
   for this match. The idea is to stop matching packets until ratelimit
   criteria stands true. Patch from Vishwanath Pai.

2) Add ->select_ops indirection to nf_tables named objects, so we can
   choose between different flavours of the same object type, patch from
   Pablo M. Bermudo.

3) Shorter function names in nft_limit, basically:
   s/nft_limit_pkt_bytes/nft_limit_bytes, also from Pablo M. Bermudo.

4) Add new stateful limit named object type, this allows us to create
   limit policies that you can identify via name, also from Pablo.

5) Remove unused hooknum parameter in conntrack ->packet indirection.
   From Florian Westphal.

6) Patches to remove CONFIG_NETFILTER_DEBUG and macros such as
   IP_NF_ASSERT and IP_NF_ASSERT. From Varsha Rao.

7) Add nf_tables_updchain() helper function and use it from
   nf_tables_newchain() to make it more maintainable. Similarly,
   add nf_tables_addchain() and use it too.

8) Add new netlink NLM_F_NONREC flag, this flag should only be used for
   deletion requests, specifically, to support non-recursive deletion.
   Based on what we discussed during NFWS'17 in Faro.

9) Use NLM_F_NONREC from table and sets in nf_tables.

10) Support for recursive chain deletion. Table and set deletion
    commands come with an implicit content flush on deletion, while
    chains do not. This patch addresses this inconsistency by adding
    the code to perform recursive chain deletions. This also comes with
    the bits to deal with the new NLM_F_NONREC netlink flag.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 45865dab 9dee1474
...@@ -31,7 +31,6 @@ CONFIG_IP_PNP_BOOTP=y ...@@ -31,7 +31,6 @@ CONFIG_IP_PNP_BOOTP=y
CONFIG_INET6_IPCOMP=m CONFIG_INET6_IPCOMP=m
CONFIG_IPV6_TUNNEL=m CONFIG_IPV6_TUNNEL=m
CONFIG_NETFILTER=y CONFIG_NETFILTER=y
CONFIG_NETFILTER_DEBUG=y
CONFIG_NET_PKTGEN=m CONFIG_NET_PKTGEN=m
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS=y
......
...@@ -28,7 +28,6 @@ CONFIG_IP_PNP_RARP=y ...@@ -28,7 +28,6 @@ CONFIG_IP_PNP_RARP=y
# CONFIG_INET_LRO is not set # CONFIG_INET_LRO is not set
# CONFIG_IPV6 is not set # CONFIG_IPV6 is not set
CONFIG_NETFILTER=y CONFIG_NETFILTER=y
CONFIG_NETFILTER_DEBUG=y
CONFIG_IP_NF_QUEUE=y CONFIG_IP_NF_QUEUE=y
CONFIG_MTD=y CONFIG_MTD=y
CONFIG_MTD_PARTITIONS=y CONFIG_MTD_PARTITIONS=y
......
...@@ -5,5 +5,6 @@ ...@@ -5,5 +5,6 @@
#define XT_HASHLIMIT_ALL (XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT | \ #define XT_HASHLIMIT_ALL (XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT | \
XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | \ XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | \
XT_HASHLIMIT_INVERT | XT_HASHLIMIT_BYTES) XT_HASHLIMIT_INVERT | XT_HASHLIMIT_BYTES |\
XT_HASHLIMIT_RATE_MATCH)
#endif /*_XT_HASHLIMIT_H*/ #endif /*_XT_HASHLIMIT_H*/
...@@ -44,12 +44,6 @@ union nf_conntrack_expect_proto { ...@@ -44,12 +44,6 @@ union nf_conntrack_expect_proto {
#include <linux/types.h> #include <linux/types.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#ifdef CONFIG_NETFILTER_DEBUG
#define NF_CT_ASSERT(x) WARN_ON(!(x))
#else
#define NF_CT_ASSERT(x)
#endif
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h> #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
...@@ -159,7 +153,7 @@ nf_ct_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo) ...@@ -159,7 +153,7 @@ nf_ct_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
/* decrement reference count on a conntrack */ /* decrement reference count on a conntrack */
static inline void nf_ct_put(struct nf_conn *ct) static inline void nf_ct_put(struct nf_conn *ct)
{ {
NF_CT_ASSERT(ct); WARN_ON(!ct);
nf_conntrack_put(&ct->ct_general); nf_conntrack_put(&ct->ct_general);
} }
......
...@@ -43,7 +43,6 @@ struct nf_conntrack_l4proto { ...@@ -43,7 +43,6 @@ struct nf_conntrack_l4proto {
unsigned int dataoff, unsigned int dataoff,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum,
unsigned int *timeouts); unsigned int *timeouts);
/* Called when a new connection for this protocol found; /* Called when a new connection for this protocol found;
......
...@@ -1007,12 +1007,12 @@ int nft_verdict_dump(struct sk_buff *skb, int type, ...@@ -1007,12 +1007,12 @@ int nft_verdict_dump(struct sk_buff *skb, int type,
* *
* @list: table stateful object list node * @list: table stateful object list node
* @table: table this object belongs to * @table: table this object belongs to
* @type: pointer to object type
* @data: pointer to object data
* @name: name of this stateful object * @name: name of this stateful object
* @genmask: generation mask * @genmask: generation mask
* @use: number of references to this stateful object * @use: number of references to this stateful object
* @data: object data, layout depends on type * @data: object data, layout depends on type
* @ops: object operations
* @data: pointer to object data
*/ */
struct nft_object { struct nft_object {
struct list_head list; struct list_head list;
...@@ -1021,7 +1021,7 @@ struct nft_object { ...@@ -1021,7 +1021,7 @@ struct nft_object {
u32 genmask:2, u32 genmask:2,
use:30; use:30;
/* runtime data below here */ /* runtime data below here */
const struct nft_object_type *type ____cacheline_aligned; const struct nft_object_ops *ops ____cacheline_aligned;
unsigned char data[] unsigned char data[]
__attribute__((aligned(__alignof__(u64)))); __attribute__((aligned(__alignof__(u64))));
}; };
...@@ -1044,27 +1044,39 @@ void nft_obj_notify(struct net *net, struct nft_table *table, ...@@ -1044,27 +1044,39 @@ void nft_obj_notify(struct net *net, struct nft_table *table,
/** /**
* struct nft_object_type - stateful object type * struct nft_object_type - stateful object type
* *
* @eval: stateful object evaluation function * @select_ops: function to select nft_object_ops
* @ops: default ops, used when no select_ops functions is present
* @list: list node in list of object types * @list: list node in list of object types
* @type: stateful object numeric type * @type: stateful object numeric type
* @size: stateful object size
* @owner: module owner * @owner: module owner
* @maxattr: maximum netlink attribute * @maxattr: maximum netlink attribute
* @policy: netlink attribute policy * @policy: netlink attribute policy
*/
struct nft_object_type {
const struct nft_object_ops *(*select_ops)(const struct nft_ctx *,
const struct nlattr * const tb[]);
const struct nft_object_ops *ops;
struct list_head list;
u32 type;
unsigned int maxattr;
struct module *owner;
const struct nla_policy *policy;
};
/**
* struct nft_object_ops - stateful object operations
*
* @eval: stateful object evaluation function
* @size: stateful object size
* @init: initialize object from netlink attributes * @init: initialize object from netlink attributes
* @destroy: release existing stateful object * @destroy: release existing stateful object
* @dump: netlink dump stateful object * @dump: netlink dump stateful object
*/ */
struct nft_object_type { struct nft_object_ops {
void (*eval)(struct nft_object *obj, void (*eval)(struct nft_object *obj,
struct nft_regs *regs, struct nft_regs *regs,
const struct nft_pktinfo *pkt); const struct nft_pktinfo *pkt);
struct list_head list;
u32 type;
unsigned int size; unsigned int size;
unsigned int maxattr;
struct module *owner;
const struct nla_policy *policy;
int (*init)(const struct nft_ctx *ctx, int (*init)(const struct nft_ctx *ctx,
const struct nlattr *const tb[], const struct nlattr *const tb[],
struct nft_object *obj); struct nft_object *obj);
...@@ -1072,6 +1084,7 @@ struct nft_object_type { ...@@ -1072,6 +1084,7 @@ struct nft_object_type {
int (*dump)(struct sk_buff *skb, int (*dump)(struct sk_buff *skb,
struct nft_object *obj, struct nft_object *obj,
bool reset); bool reset);
const struct nft_object_type *type;
}; };
int nft_register_obj(struct nft_object_type *obj_type); int nft_register_obj(struct nft_object_type *obj_type);
......
...@@ -1282,7 +1282,8 @@ enum nft_ct_helper_attributes { ...@@ -1282,7 +1282,8 @@ enum nft_ct_helper_attributes {
#define NFT_OBJECT_COUNTER 1 #define NFT_OBJECT_COUNTER 1
#define NFT_OBJECT_QUOTA 2 #define NFT_OBJECT_QUOTA 2
#define NFT_OBJECT_CT_HELPER 3 #define NFT_OBJECT_CT_HELPER 3
#define __NFT_OBJECT_MAX 4 #define NFT_OBJECT_LIMIT 4
#define __NFT_OBJECT_MAX 5
#define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1)
/** /**
......
...@@ -25,6 +25,7 @@ enum { ...@@ -25,6 +25,7 @@ enum {
XT_HASHLIMIT_HASH_SPT = 1 << 3, XT_HASHLIMIT_HASH_SPT = 1 << 3,
XT_HASHLIMIT_INVERT = 1 << 4, XT_HASHLIMIT_INVERT = 1 << 4,
XT_HASHLIMIT_BYTES = 1 << 5, XT_HASHLIMIT_BYTES = 1 << 5,
XT_HASHLIMIT_RATE_MATCH = 1 << 6,
}; };
struct hashlimit_cfg { struct hashlimit_cfg {
...@@ -79,6 +80,21 @@ struct hashlimit_cfg2 { ...@@ -79,6 +80,21 @@ struct hashlimit_cfg2 {
__u8 srcmask, dstmask; __u8 srcmask, dstmask;
}; };
struct hashlimit_cfg3 {
__u64 avg; /* Average secs between packets * scale */
__u64 burst; /* Period multiplier for upper limit. */
__u32 mode; /* bitmask of XT_HASHLIMIT_HASH_* */
/* user specified */
__u32 size; /* how many buckets */
__u32 max; /* max number of entries */
__u32 gc_interval; /* gc interval */
__u32 expire; /* when do entries expire? */
__u32 interval;
__u8 srcmask, dstmask;
};
struct xt_hashlimit_mtinfo1 { struct xt_hashlimit_mtinfo1 {
char name[IFNAMSIZ]; char name[IFNAMSIZ];
struct hashlimit_cfg1 cfg; struct hashlimit_cfg1 cfg;
...@@ -95,4 +111,12 @@ struct xt_hashlimit_mtinfo2 { ...@@ -95,4 +111,12 @@ struct xt_hashlimit_mtinfo2 {
struct xt_hashlimit_htable *hinfo __attribute__((aligned(8))); struct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));
}; };
struct xt_hashlimit_mtinfo3 {
char name[NAME_MAX];
struct hashlimit_cfg3 cfg;
/* Used internally by the kernel */
struct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));
};
#endif /* _UAPI_XT_HASHLIMIT_H */ #endif /* _UAPI_XT_HASHLIMIT_H */
...@@ -69,6 +69,9 @@ struct nlmsghdr { ...@@ -69,6 +69,9 @@ struct nlmsghdr {
#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ #define NLM_F_CREATE 0x400 /* Create, if it does not exist */
#define NLM_F_APPEND 0x800 /* Add to end of list */ #define NLM_F_APPEND 0x800 /* Add to end of list */
/* Modifiers to DELETE request */
#define NLM_F_NONREC 0x100 /* Do not delete recursively */
/* Flags for ACK message */ /* Flags for ACK message */
#define NLM_F_CAPPED 0x100 /* request was capped */ #define NLM_F_CAPPED 0x100 /* request was capped */
#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ #define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
......
...@@ -166,13 +166,6 @@ menuconfig NETFILTER ...@@ -166,13 +166,6 @@ menuconfig NETFILTER
if NETFILTER if NETFILTER
config NETFILTER_DEBUG
bool "Network packet filtering debugging"
depends on NETFILTER
help
You can say Y here if you want to get additional messages useful in
debugging the netfilter code.
config NETFILTER_ADVANCED config NETFILTER_ADVANCED
bool "Advanced netfilter configuration" bool "Advanced netfilter configuration"
depends on NETFILTER depends on NETFILTER
......
...@@ -252,13 +252,11 @@ unsigned int ebt_do_table(struct sk_buff *skb, ...@@ -252,13 +252,11 @@ unsigned int ebt_do_table(struct sk_buff *skb,
} }
if (verdict == EBT_RETURN) { if (verdict == EBT_RETURN) {
letsreturn: letsreturn:
#ifdef CONFIG_NETFILTER_DEBUG if (WARN(sp == 0, "RETURN on base chain")) {
if (sp == 0) {
BUGPRINT("RETURN on base chain");
/* act like this is EBT_CONTINUE */ /* act like this is EBT_CONTINUE */
goto letscontinue; goto letscontinue;
} }
#endif
sp--; sp--;
/* put all the local variables right */ /* put all the local variables right */
i = cs[sp].n; i = cs[sp].n;
...@@ -271,26 +269,24 @@ unsigned int ebt_do_table(struct sk_buff *skb, ...@@ -271,26 +269,24 @@ unsigned int ebt_do_table(struct sk_buff *skb,
} }
if (verdict == EBT_CONTINUE) if (verdict == EBT_CONTINUE)
goto letscontinue; goto letscontinue;
#ifdef CONFIG_NETFILTER_DEBUG
if (verdict < 0) { if (WARN(verdict < 0, "bogus standard verdict\n")) {
BUGPRINT("bogus standard verdict\n");
read_unlock_bh(&table->lock); read_unlock_bh(&table->lock);
return NF_DROP; return NF_DROP;
} }
#endif
/* jump to a udc */ /* jump to a udc */
cs[sp].n = i + 1; cs[sp].n = i + 1;
cs[sp].chaininfo = chaininfo; cs[sp].chaininfo = chaininfo;
cs[sp].e = ebt_next_entry(point); cs[sp].e = ebt_next_entry(point);
i = 0; i = 0;
chaininfo = (struct ebt_entries *) (base + verdict); chaininfo = (struct ebt_entries *) (base + verdict);
#ifdef CONFIG_NETFILTER_DEBUG
if (chaininfo->distinguisher) { if (WARN(chaininfo->distinguisher, "jump to non-chain\n")) {
BUGPRINT("jump to non-chain\n");
read_unlock_bh(&table->lock); read_unlock_bh(&table->lock);
return NF_DROP; return NF_DROP;
} }
#endif
nentries = chaininfo->nentries; nentries = chaininfo->nentries;
point = (struct ebt_entry *)chaininfo->data; point = (struct ebt_entry *)chaininfo->data;
counter_base = cb_base + chaininfo->counter_offset; counter_base = cb_base + chaininfo->counter_offset;
......
...@@ -35,12 +35,6 @@ MODULE_LICENSE("GPL"); ...@@ -35,12 +35,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
MODULE_DESCRIPTION("IPv4 packet filter"); MODULE_DESCRIPTION("IPv4 packet filter");
#ifdef CONFIG_NETFILTER_DEBUG
#define IP_NF_ASSERT(x) WARN_ON(!(x))
#else
#define IP_NF_ASSERT(x)
#endif
void *ipt_alloc_initial_table(const struct xt_table *info) void *ipt_alloc_initial_table(const struct xt_table *info)
{ {
return xt_alloc_initial_table(ipt, IPT); return xt_alloc_initial_table(ipt, IPT);
...@@ -263,7 +257,7 @@ ipt_do_table(struct sk_buff *skb, ...@@ -263,7 +257,7 @@ ipt_do_table(struct sk_buff *skb,
acpar.hotdrop = false; acpar.hotdrop = false;
acpar.state = state; acpar.state = state;
IP_NF_ASSERT(table->valid_hooks & (1 << hook)); WARN_ON(!(table->valid_hooks & (1 << hook)));
local_bh_disable(); local_bh_disable();
addend = xt_write_recseq_begin(); addend = xt_write_recseq_begin();
private = table->private; private = table->private;
...@@ -293,7 +287,7 @@ ipt_do_table(struct sk_buff *skb, ...@@ -293,7 +287,7 @@ ipt_do_table(struct sk_buff *skb,
const struct xt_entry_match *ematch; const struct xt_entry_match *ematch;
struct xt_counters *counter; struct xt_counters *counter;
IP_NF_ASSERT(e); WARN_ON(!e);
if (!ip_packet_match(ip, indev, outdev, if (!ip_packet_match(ip, indev, outdev,
&e->ip, acpar.fragoff)) { &e->ip, acpar.fragoff)) {
no_match: no_match:
...@@ -312,7 +306,7 @@ ipt_do_table(struct sk_buff *skb, ...@@ -312,7 +306,7 @@ ipt_do_table(struct sk_buff *skb,
ADD_COUNTER(*counter, skb->len, 1); ADD_COUNTER(*counter, skb->len, 1);
t = ipt_get_target(e); t = ipt_get_target(e);
IP_NF_ASSERT(t->u.kernel.target); WARN_ON(!t->u.kernel.target);
#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
/* The packet is traced: log it */ /* The packet is traced: log it */
......
...@@ -82,7 +82,6 @@ static int icmp_packet(struct nf_conn *ct, ...@@ -82,7 +82,6 @@ static int icmp_packet(struct nf_conn *ct,
unsigned int dataoff, unsigned int dataoff,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum,
unsigned int *timeout) unsigned int *timeout)
{ {
/* Do not immediately delete the connection after the first /* Do not immediately delete the connection after the first
...@@ -127,7 +126,7 @@ icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, ...@@ -127,7 +126,7 @@ icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conntrack_zone tmp; struct nf_conntrack_zone tmp;
NF_CT_ASSERT(!skb_nfct(skb)); WARN_ON(skb_nfct(skb));
zone = nf_ct_zone_tmpl(tmpl, skb, &tmp); zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
/* Are they talking about one of our connections? */ /* Are they talking about one of our connections? */
......
...@@ -190,7 +190,7 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb, ...@@ -190,7 +190,7 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb,
struct nf_conntrack_tuple target; struct nf_conntrack_tuple target;
unsigned long statusbit; unsigned long statusbit;
NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY); WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
return 0; return 0;
...@@ -306,8 +306,8 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb, ...@@ -306,8 +306,8 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb,
default: default:
/* ESTABLISHED */ /* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || WARN_ON(ctinfo != IP_CT_ESTABLISHED &&
ctinfo == IP_CT_ESTABLISHED_REPLY); ctinfo != IP_CT_ESTABLISHED_REPLY);
if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out)) if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
goto oif_changed; goto oif_changed;
} }
......
...@@ -34,12 +34,12 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, ...@@ -34,12 +34,12 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum,
const struct rtable *rt; const struct rtable *rt;
__be32 newsrc, nh; __be32 newsrc, nh;
NF_CT_ASSERT(hooknum == NF_INET_POST_ROUTING); WARN_ON(hooknum != NF_INET_POST_ROUTING);
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
ctinfo == IP_CT_RELATED_REPLY)); ctinfo == IP_CT_RELATED_REPLY)));
/* Source address is 0.0.0.0 - locally generated packet that is /* Source address is 0.0.0.0 - locally generated packet that is
* probably not supposed to be masqueraded. * probably not supposed to be masqueraded.
...@@ -96,7 +96,7 @@ static int masq_device_event(struct notifier_block *this, ...@@ -96,7 +96,7 @@ static int masq_device_event(struct notifier_block *this,
* conntracks which were associated with that device, * conntracks which were associated with that device,
* and forget them. * and forget them.
*/ */
NF_CT_ASSERT(dev->ifindex != 0); WARN_ON(dev->ifindex == 0);
nf_ct_iterate_cleanup_net(net, device_cmp, nf_ct_iterate_cleanup_net(net, device_cmp,
(void *)(long)dev->ifindex, 0, 0); (void *)(long)dev->ifindex, 0, 0);
......
...@@ -39,12 +39,6 @@ MODULE_LICENSE("GPL"); ...@@ -39,12 +39,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
MODULE_DESCRIPTION("IPv6 packet filter"); MODULE_DESCRIPTION("IPv6 packet filter");
#ifdef CONFIG_NETFILTER_DEBUG
#define IP_NF_ASSERT(x) WARN_ON(!(x))
#else
#define IP_NF_ASSERT(x)
#endif
void *ip6t_alloc_initial_table(const struct xt_table *info) void *ip6t_alloc_initial_table(const struct xt_table *info)
{ {
return xt_alloc_initial_table(ip6t, IP6T); return xt_alloc_initial_table(ip6t, IP6T);
...@@ -284,7 +278,7 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -284,7 +278,7 @@ ip6t_do_table(struct sk_buff *skb,
acpar.hotdrop = false; acpar.hotdrop = false;
acpar.state = state; acpar.state = state;
IP_NF_ASSERT(table->valid_hooks & (1 << hook)); WARN_ON(!(table->valid_hooks & (1 << hook)));
local_bh_disable(); local_bh_disable();
addend = xt_write_recseq_begin(); addend = xt_write_recseq_begin();
...@@ -315,7 +309,7 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -315,7 +309,7 @@ ip6t_do_table(struct sk_buff *skb,
const struct xt_entry_match *ematch; const struct xt_entry_match *ematch;
struct xt_counters *counter; struct xt_counters *counter;
IP_NF_ASSERT(e); WARN_ON(!e);
acpar.thoff = 0; acpar.thoff = 0;
if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
&acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) {
...@@ -335,7 +329,7 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -335,7 +329,7 @@ ip6t_do_table(struct sk_buff *skb,
ADD_COUNTER(*counter, skb->len, 1); ADD_COUNTER(*counter, skb->len, 1);
t = ip6t_get_target_c(e); t = ip6t_get_target_c(e);
IP_NF_ASSERT(t->u.kernel.target); WARN_ON(!t->u.kernel.target);
#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
/* The packet is traced: log it */ /* The packet is traced: log it */
......
...@@ -95,7 +95,6 @@ static int icmpv6_packet(struct nf_conn *ct, ...@@ -95,7 +95,6 @@ static int icmpv6_packet(struct nf_conn *ct,
unsigned int dataoff, unsigned int dataoff,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum,
unsigned int *timeout) unsigned int *timeout)
{ {
/* Do not immediately delete the connection after the first /* Do not immediately delete the connection after the first
...@@ -129,8 +128,7 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -129,8 +128,7 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb,
static int static int
icmpv6_error_message(struct net *net, struct nf_conn *tmpl, icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
struct sk_buff *skb, struct sk_buff *skb,
unsigned int icmp6off, unsigned int icmp6off)
unsigned int hooknum)
{ {
struct nf_conntrack_tuple intuple, origtuple; struct nf_conntrack_tuple intuple, origtuple;
const struct nf_conntrack_tuple_hash *h; const struct nf_conntrack_tuple_hash *h;
...@@ -138,7 +136,7 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl, ...@@ -138,7 +136,7 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl,
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conntrack_zone tmp; struct nf_conntrack_zone tmp;
NF_CT_ASSERT(!skb_nfct(skb)); WARN_ON(skb_nfct(skb));
/* Are they talking about one of our connections? */ /* Are they talking about one of our connections? */
if (!nf_ct_get_tuplepr(skb, if (!nf_ct_get_tuplepr(skb,
...@@ -214,7 +212,7 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl, ...@@ -214,7 +212,7 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl,
if (icmp6h->icmp6_type >= 128) if (icmp6h->icmp6_type >= 128)
return NF_ACCEPT; return NF_ACCEPT;
return icmpv6_error_message(net, tmpl, skb, dataoff, hooknum); return icmpv6_error_message(net, tmpl, skb, dataoff);
} }
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
......
...@@ -196,7 +196,7 @@ int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, ...@@ -196,7 +196,7 @@ int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
struct nf_conntrack_tuple target; struct nf_conntrack_tuple target;
unsigned long statusbit; unsigned long statusbit;
NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY); WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
return 0; return 0;
...@@ -319,8 +319,8 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb, ...@@ -319,8 +319,8 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
default: default:
/* ESTABLISHED */ /* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || WARN_ON(ctinfo != IP_CT_ESTABLISHED &&
ctinfo == IP_CT_ESTABLISHED_REPLY); ctinfo != IP_CT_ESTABLISHED_REPLY);
if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out)) if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out))
goto oif_changed; goto oif_changed;
} }
......
...@@ -36,8 +36,8 @@ nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range *range, ...@@ -36,8 +36,8 @@ nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range *range,
struct nf_nat_range newrange; struct nf_nat_range newrange;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
ctinfo == IP_CT_RELATED_REPLY)); ctinfo == IP_CT_RELATED_REPLY)));
if (ipv6_dev_get_saddr(nf_ct_net(ct), out, if (ipv6_dev_get_saddr(nf_ct_net(ct), out,
&ipv6_hdr(skb)->daddr, 0, &src) < 0) &ipv6_hdr(skb)->daddr, 0, &src) < 0)
......
...@@ -403,7 +403,7 @@ destroy_conntrack(struct nf_conntrack *nfct) ...@@ -403,7 +403,7 @@ destroy_conntrack(struct nf_conntrack *nfct)
const struct nf_conntrack_l4proto *l4proto; const struct nf_conntrack_l4proto *l4proto;
pr_debug("destroy_conntrack(%p)\n", ct); pr_debug("destroy_conntrack(%p)\n", ct);
NF_CT_ASSERT(atomic_read(&nfct->use) == 0); WARN_ON(atomic_read(&nfct->use) != 0);
if (unlikely(nf_ct_is_template(ct))) { if (unlikely(nf_ct_is_template(ct))) {
nf_ct_tmpl_free(ct); nf_ct_tmpl_free(ct);
...@@ -756,12 +756,11 @@ __nf_conntrack_confirm(struct sk_buff *skb) ...@@ -756,12 +756,11 @@ __nf_conntrack_confirm(struct sk_buff *skb)
* connections for unconfirmed conns. But packet copies and * connections for unconfirmed conns. But packet copies and
* REJECT will give spurious warnings here. * REJECT will give spurious warnings here.
*/ */
/* NF_CT_ASSERT(atomic_read(&ct->ct_general.use) == 1); */
/* No external references means no one else could have /* No external references means no one else could have
* confirmed us. * confirmed us.
*/ */
NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); WARN_ON(nf_ct_is_confirmed(ct));
pr_debug("Confirming conntrack %p\n", ct); pr_debug("Confirming conntrack %p\n", ct);
/* We have to check the DYING flag after unlink to prevent /* We have to check the DYING flag after unlink to prevent
* a race against nf_ct_get_next_corpse() possibly called from * a race against nf_ct_get_next_corpse() possibly called from
...@@ -1160,7 +1159,7 @@ void nf_conntrack_free(struct nf_conn *ct) ...@@ -1160,7 +1159,7 @@ void nf_conntrack_free(struct nf_conn *ct)
/* A freed object has refcnt == 0, that's /* A freed object has refcnt == 0, that's
* the golden rule for SLAB_TYPESAFE_BY_RCU * the golden rule for SLAB_TYPESAFE_BY_RCU
*/ */
NF_CT_ASSERT(atomic_read(&ct->ct_general.use) == 0); WARN_ON(atomic_read(&ct->ct_general.use) != 0);
nf_ct_ext_destroy(ct); nf_ct_ext_destroy(ct);
nf_ct_ext_free(ct); nf_ct_ext_free(ct);
...@@ -1414,7 +1413,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, ...@@ -1414,7 +1413,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
/* Decide what timeout policy we want to apply to this flow. */ /* Decide what timeout policy we want to apply to this flow. */
timeouts = nf_ct_timeout_lookup(net, ct, l4proto); timeouts = nf_ct_timeout_lookup(net, ct, l4proto);
ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum, timeouts); ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, timeouts);
if (ret <= 0) { if (ret <= 0) {
/* Invalid: inverse of the return code tells /* Invalid: inverse of the return code tells
* the netfilter core what to do */ * the netfilter core what to do */
...@@ -1468,7 +1467,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, ...@@ -1468,7 +1467,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct,
struct nf_conn_help *help = nfct_help(ct); struct nf_conn_help *help = nfct_help(ct);
/* Should be unconfirmed, so not in hash table yet */ /* Should be unconfirmed, so not in hash table yet */
NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); WARN_ON(nf_ct_is_confirmed(ct));
pr_debug("Altering reply tuple of %p to ", ct); pr_debug("Altering reply tuple of %p to ", ct);
nf_ct_dump_tuple(newreply); nf_ct_dump_tuple(newreply);
...@@ -1490,7 +1489,7 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, ...@@ -1490,7 +1489,7 @@ void __nf_ct_refresh_acct(struct nf_conn *ct,
unsigned long extra_jiffies, unsigned long extra_jiffies,
int do_acct) int do_acct)
{ {
NF_CT_ASSERT(skb); WARN_ON(!skb);
/* Only update if this is not a fixed timeout */ /* Only update if this is not a fixed timeout */
if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status))
......
...@@ -51,8 +51,8 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, ...@@ -51,8 +51,8 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
struct nf_conn_help *master_help = nfct_help(exp->master); struct nf_conn_help *master_help = nfct_help(exp->master);
struct net *net = nf_ct_exp_net(exp); struct net *net = nf_ct_exp_net(exp);
NF_CT_ASSERT(master_help); WARN_ON(!master_help);
NF_CT_ASSERT(!timer_pending(&exp->timeout)); WARN_ON(timer_pending(&exp->timeout));
hlist_del_rcu(&exp->hnode); hlist_del_rcu(&exp->hnode);
net->ct.expect_count--; net->ct.expect_count--;
......
...@@ -47,7 +47,7 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) ...@@ -47,7 +47,7 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
struct nf_ct_ext_type *t; struct nf_ct_ext_type *t;
/* Conntrack must not be confirmed to avoid races on reallocation. */ /* Conntrack must not be confirmed to avoid races on reallocation. */
NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); WARN_ON(nf_ct_is_confirmed(ct));
old = ct->ext; old = ct->ext;
......
...@@ -469,7 +469,7 @@ static unsigned int *dccp_get_timeouts(struct net *net) ...@@ -469,7 +469,7 @@ static unsigned int *dccp_get_timeouts(struct net *net)
static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
unsigned int dataoff, enum ip_conntrack_info ctinfo, unsigned int dataoff, enum ip_conntrack_info ctinfo,
u_int8_t pf, unsigned int hooknum, u_int8_t pf,
unsigned int *timeouts) unsigned int *timeouts)
{ {
struct net *net = nf_ct_net(ct); struct net *net = nf_ct_net(ct);
......
...@@ -61,7 +61,6 @@ static int generic_packet(struct nf_conn *ct, ...@@ -61,7 +61,6 @@ static int generic_packet(struct nf_conn *ct,
unsigned int dataoff, unsigned int dataoff,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum,
unsigned int *timeout) unsigned int *timeout)
{ {
nf_ct_refresh_acct(ct, ctinfo, skb, *timeout); nf_ct_refresh_acct(ct, ctinfo, skb, *timeout);
......
...@@ -245,7 +245,6 @@ static int gre_packet(struct nf_conn *ct, ...@@ -245,7 +245,6 @@ static int gre_packet(struct nf_conn *ct,
unsigned int dataoff, unsigned int dataoff,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum,
unsigned int *timeouts) unsigned int *timeouts)
{ {
/* If we've seen traffic both ways, this is a GRE connection. /* If we've seen traffic both ways, this is a GRE connection.
......
...@@ -307,7 +307,6 @@ static int sctp_packet(struct nf_conn *ct, ...@@ -307,7 +307,6 @@ static int sctp_packet(struct nf_conn *ct,
unsigned int dataoff, unsigned int dataoff,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum,
unsigned int *timeouts) unsigned int *timeouts)
{ {
enum sctp_conntrack new_state, old_state; enum sctp_conntrack new_state, old_state;
......
...@@ -803,7 +803,6 @@ static int tcp_packet(struct nf_conn *ct, ...@@ -803,7 +803,6 @@ static int tcp_packet(struct nf_conn *ct,
unsigned int dataoff, unsigned int dataoff,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum,
unsigned int *timeouts) unsigned int *timeouts)
{ {
struct net *net = nf_ct_net(ct); struct net *net = nf_ct_net(ct);
......
...@@ -74,7 +74,6 @@ static int udp_packet(struct nf_conn *ct, ...@@ -74,7 +74,6 @@ static int udp_packet(struct nf_conn *ct,
unsigned int dataoff, unsigned int dataoff,
enum ip_conntrack_info ctinfo, enum ip_conntrack_info ctinfo,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum,
unsigned int *timeouts) unsigned int *timeouts)
{ {
/* If we've seen traffic both ways, this is some kind of UDP /* If we've seen traffic both ways, this is some kind of UDP
......
...@@ -287,7 +287,7 @@ static int ct_seq_show(struct seq_file *s, void *v) ...@@ -287,7 +287,7 @@ static int ct_seq_show(struct seq_file *s, void *v)
struct net *net = seq_file_net(s); struct net *net = seq_file_net(s);
int ret = 0; int ret = 0;
NF_CT_ASSERT(ct); WARN_ON(!ct);
if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use)))
return 0; return 0;
...@@ -304,9 +304,9 @@ static int ct_seq_show(struct seq_file *s, void *v) ...@@ -304,9 +304,9 @@ static int ct_seq_show(struct seq_file *s, void *v)
goto release; goto release;
l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct));
NF_CT_ASSERT(l3proto); WARN_ON(!l3proto);
l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
NF_CT_ASSERT(l4proto); WARN_ON(!l4proto);
ret = -ENOSPC; ret = -ENOSPC;
seq_printf(s, "%-8s %u %-8s %u %ld ", seq_printf(s, "%-8s %u %-8s %u %ld ",
......
...@@ -414,8 +414,8 @@ nf_nat_setup_info(struct nf_conn *ct, ...@@ -414,8 +414,8 @@ nf_nat_setup_info(struct nf_conn *ct,
if (nf_ct_is_confirmed(ct)) if (nf_ct_is_confirmed(ct))
return NF_ACCEPT; return NF_ACCEPT;
NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC || WARN_ON(maniptype != NF_NAT_MANIP_SRC &&
maniptype == NF_NAT_MANIP_DST); maniptype != NF_NAT_MANIP_DST);
BUG_ON(nf_nat_initialized(ct, maniptype)); BUG_ON(nf_nat_initialized(ct, maniptype));
/* What we've got will look like inverse of reply. Normally /* What we've got will look like inverse of reply. Normally
......
...@@ -38,11 +38,11 @@ nf_nat_redirect_ipv4(struct sk_buff *skb, ...@@ -38,11 +38,11 @@ nf_nat_redirect_ipv4(struct sk_buff *skb,
__be32 newdst; __be32 newdst;
struct nf_nat_range newrange; struct nf_nat_range newrange;
NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING || WARN_ON(hooknum != NF_INET_PRE_ROUTING &&
hooknum == NF_INET_LOCAL_OUT); hooknum != NF_INET_LOCAL_OUT);
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)));
/* Local packets: make them go to loopback */ /* Local packets: make them go to loopback */
if (hooknum == NF_INET_LOCAL_OUT) { if (hooknum == NF_INET_LOCAL_OUT) {
......
...@@ -860,6 +860,10 @@ static int nf_tables_deltable(struct net *net, struct sock *nlsk, ...@@ -860,6 +860,10 @@ static int nf_tables_deltable(struct net *net, struct sock *nlsk,
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (nlh->nlmsg_flags & NLM_F_NONREC &&
table->use > 0)
return -EBUSY;
ctx.afi = afi; ctx.afi = afi;
ctx.table = table; ctx.table = table;
...@@ -1335,91 +1339,126 @@ static void nft_chain_release_hook(struct nft_chain_hook *hook) ...@@ -1335,91 +1339,126 @@ static void nft_chain_release_hook(struct nft_chain_hook *hook)
dev_put(hook->dev); dev_put(hook->dev);
} }
static int nf_tables_newchain(struct net *net, struct sock *nlsk, static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
struct sk_buff *skb, const struct nlmsghdr *nlh, u8 policy, bool create)
const struct nlattr * const nla[],
struct netlink_ext_ack *extack)
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nlattr * const *nla = ctx->nla;
const struct nlattr * uninitialized_var(name); struct nft_table *table = ctx->table;
struct nft_af_info *afi; struct nft_af_info *afi = ctx->afi;
struct nft_table *table; struct nft_base_chain *basechain;
struct nft_stats __percpu *stats;
struct net *net = ctx->net;
struct nft_chain *chain; struct nft_chain *chain;
struct nft_base_chain *basechain = NULL;
u8 genmask = nft_genmask_next(net);
int family = nfmsg->nfgen_family;
u8 policy = NF_ACCEPT;
u64 handle = 0;
unsigned int i; unsigned int i;
struct nft_stats __percpu *stats;
int err; int err;
bool create;
struct nft_ctx ctx;
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; if (table->use == UINT_MAX)
return -EOVERFLOW;
afi = nf_tables_afinfo_lookup(net, family, true); if (nla[NFTA_CHAIN_HOOK]) {
if (IS_ERR(afi)) struct nft_chain_hook hook;
return PTR_ERR(afi); struct nf_hook_ops *ops;
nf_hookfn *hookfn;
table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE], genmask); err = nft_chain_parse_hook(net, nla, afi, &hook, create);
if (IS_ERR(table)) if (err < 0)
return PTR_ERR(table); return err;
chain = NULL; basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
name = nla[NFTA_CHAIN_NAME]; if (basechain == NULL) {
nft_chain_release_hook(&hook);
return -ENOMEM;
}
if (nla[NFTA_CHAIN_HANDLE]) { if (hook.dev != NULL)
handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE])); strncpy(basechain->dev_name, hook.dev->name, IFNAMSIZ);
chain = nf_tables_chain_lookup_byhandle(table, handle, genmask);
if (IS_ERR(chain)) if (nla[NFTA_CHAIN_COUNTERS]) {
return PTR_ERR(chain); stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
} else { if (IS_ERR(stats)) {
chain = nf_tables_chain_lookup(table, name, genmask); nft_chain_release_hook(&hook);
if (IS_ERR(chain)) { kfree(basechain);
if (PTR_ERR(chain) != -ENOENT) return PTR_ERR(stats);
return PTR_ERR(chain);
chain = NULL;
} }
basechain->stats = stats;
static_branch_inc(&nft_counters_enabled);
} }
if (nla[NFTA_CHAIN_POLICY]) { hookfn = hook.type->hooks[hook.num];
if (chain != NULL && basechain->type = hook.type;
!nft_is_base_chain(chain)) chain = &basechain->chain;
return -EOPNOTSUPP;
if (chain == NULL && for (i = 0; i < afi->nops; i++) {
nla[NFTA_CHAIN_HOOK] == NULL) ops = &basechain->ops[i];
return -EOPNOTSUPP; ops->pf = family;
ops->hooknum = hook.num;
ops->priority = hook.priority;
ops->priv = chain;
ops->hook = afi->hooks[ops->hooknum];
ops->dev = hook.dev;
if (hookfn)
ops->hook = hookfn;
if (afi->hook_ops_init)
afi->hook_ops_init(ops, i);
}
policy = ntohl(nla_get_be32(nla[NFTA_CHAIN_POLICY])); chain->flags |= NFT_BASE_CHAIN;
switch (policy) { basechain->policy = policy;
case NF_DROP: } else {
case NF_ACCEPT: chain = kzalloc(sizeof(*chain), GFP_KERNEL);
break; if (chain == NULL)
default: return -ENOMEM;
return -EINVAL;
} }
INIT_LIST_HEAD(&chain->rules);
chain->handle = nf_tables_alloc_handle(table);
chain->table = table;
chain->name = nla_strdup(nla[NFTA_CHAIN_NAME], GFP_KERNEL);
if (!chain->name) {
err = -ENOMEM;
goto err1;
} }
if (chain != NULL) { err = nf_tables_register_hooks(net, table, chain, afi->nops);
struct nft_stats *stats = NULL; if (err < 0)
struct nft_trans *trans; goto err1;
if (nlh->nlmsg_flags & NLM_F_EXCL) ctx->chain = chain;
return -EEXIST; err = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
if (nlh->nlmsg_flags & NLM_F_REPLACE) if (err < 0)
return -EOPNOTSUPP; goto err2;
if (nla[NFTA_CHAIN_HOOK]) { table->use++;
list_add_tail_rcu(&chain->list, &table->chains);
return 0;
err2:
nf_tables_unregister_hooks(net, table, chain, afi->nops);
err1:
nf_tables_chain_destroy(chain);
return err;
}
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
bool create)
{
const struct nlattr * const *nla = ctx->nla;
struct nft_table *table = ctx->table;
struct nft_chain *chain = ctx->chain;
struct nft_af_info *afi = ctx->afi;
struct nft_base_chain *basechain; struct nft_base_chain *basechain;
struct nft_stats *stats = NULL;
struct nft_chain_hook hook; struct nft_chain_hook hook;
const struct nlattr *name;
struct nf_hook_ops *ops; struct nf_hook_ops *ops;
struct nft_trans *trans;
int err, i;
if (nla[NFTA_CHAIN_HOOK]) {
if (!nft_is_base_chain(chain)) if (!nft_is_base_chain(chain))
return -EBUSY; return -EBUSY;
err = nft_chain_parse_hook(net, nla, afi, &hook, err = nft_chain_parse_hook(ctx->net, nla, ctx->afi, &hook,
create); create);
if (err < 0) if (err < 0)
return err; return err;
...@@ -1442,11 +1481,11 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, ...@@ -1442,11 +1481,11 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
nft_chain_release_hook(&hook); nft_chain_release_hook(&hook);
} }
if (nla[NFTA_CHAIN_HANDLE] && name) { if (nla[NFTA_CHAIN_HANDLE] &&
nla[NFTA_CHAIN_NAME]) {
struct nft_chain *chain2; struct nft_chain *chain2;
chain2 = nf_tables_chain_lookup(table, chain2 = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME],
nla[NFTA_CHAIN_NAME],
genmask); genmask);
if (IS_ERR(chain2)) if (IS_ERR(chain2))
return PTR_ERR(chain2); return PTR_ERR(chain2);
...@@ -1461,8 +1500,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, ...@@ -1461,8 +1500,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
return PTR_ERR(stats); return PTR_ERR(stats);
} }
nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla); trans = nft_trans_alloc(ctx, NFT_MSG_NEWCHAIN,
trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
sizeof(struct nft_trans_chain)); sizeof(struct nft_trans_chain));
if (trans == NULL) { if (trans == NULL) {
free_percpu(stats); free_percpu(stats);
...@@ -1477,6 +1515,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, ...@@ -1477,6 +1515,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
else else
nft_trans_chain_policy(trans) = -1; nft_trans_chain_policy(trans) = -1;
name = nla[NFTA_CHAIN_NAME];
if (nla[NFTA_CHAIN_HANDLE] && name) { if (nla[NFTA_CHAIN_HANDLE] && name) {
nft_trans_chain_name(trans) = nft_trans_chain_name(trans) =
nla_strdup(name, GFP_KERNEL); nla_strdup(name, GFP_KERNEL);
...@@ -1486,94 +1525,86 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, ...@@ -1486,94 +1525,86 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
return -ENOMEM; return -ENOMEM;
} }
} }
list_add_tail(&trans->list, &net->nft.commit_list); list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0; return 0;
} }
if (table->use == UINT_MAX) static int nf_tables_newchain(struct net *net, struct sock *nlsk,
return -EOVERFLOW; struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[],
struct netlink_ext_ack *extack)
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nlattr * uninitialized_var(name);
u8 genmask = nft_genmask_next(net);
int family = nfmsg->nfgen_family;
struct nft_af_info *afi;
struct nft_table *table;
struct nft_chain *chain;
u8 policy = NF_ACCEPT;
struct nft_ctx ctx;
u64 handle = 0;
bool create;
if (nla[NFTA_CHAIN_HOOK]) { create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
struct nft_chain_hook hook;
struct nf_hook_ops *ops;
nf_hookfn *hookfn;
err = nft_chain_parse_hook(net, nla, afi, &hook, create); afi = nf_tables_afinfo_lookup(net, family, true);
if (err < 0) if (IS_ERR(afi))
return err; return PTR_ERR(afi);
basechain = kzalloc(sizeof(*basechain), GFP_KERNEL); table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE], genmask);
if (basechain == NULL) { if (IS_ERR(table))
nft_chain_release_hook(&hook); return PTR_ERR(table);
return -ENOMEM;
}
if (hook.dev != NULL) chain = NULL;
strncpy(basechain->dev_name, hook.dev->name, IFNAMSIZ); name = nla[NFTA_CHAIN_NAME];
if (nla[NFTA_CHAIN_COUNTERS]) { if (nla[NFTA_CHAIN_HANDLE]) {
stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE]));
if (IS_ERR(stats)) { chain = nf_tables_chain_lookup_byhandle(table, handle, genmask);
nft_chain_release_hook(&hook); if (IS_ERR(chain))
kfree(basechain); return PTR_ERR(chain);
return PTR_ERR(stats); } else {
chain = nf_tables_chain_lookup(table, name, genmask);
if (IS_ERR(chain)) {
if (PTR_ERR(chain) != -ENOENT)
return PTR_ERR(chain);
chain = NULL;
} }
basechain->stats = stats;
static_branch_inc(&nft_counters_enabled);
} }
hookfn = hook.type->hooks[hook.num]; if (nla[NFTA_CHAIN_POLICY]) {
basechain->type = hook.type; if (chain != NULL &&
chain = &basechain->chain; !nft_is_base_chain(chain))
return -EOPNOTSUPP;
for (i = 0; i < afi->nops; i++) { if (chain == NULL &&
ops = &basechain->ops[i]; nla[NFTA_CHAIN_HOOK] == NULL)
ops->pf = family; return -EOPNOTSUPP;
ops->hooknum = hook.num;
ops->priority = hook.priority;
ops->priv = chain;
ops->hook = afi->hooks[ops->hooknum];
ops->dev = hook.dev;
if (hookfn)
ops->hook = hookfn;
if (afi->hook_ops_init)
afi->hook_ops_init(ops, i);
}
chain->flags |= NFT_BASE_CHAIN; policy = ntohl(nla_get_be32(nla[NFTA_CHAIN_POLICY]));
basechain->policy = policy; switch (policy) {
} else { case NF_DROP:
chain = kzalloc(sizeof(*chain), GFP_KERNEL); case NF_ACCEPT:
if (chain == NULL) break;
return -ENOMEM; default:
return -EINVAL;
} }
INIT_LIST_HEAD(&chain->rules);
chain->handle = nf_tables_alloc_handle(table);
chain->table = table;
chain->name = nla_strdup(name, GFP_KERNEL);
if (!chain->name) {
err = -ENOMEM;
goto err1;
} }
err = nf_tables_register_hooks(net, table, chain, afi->nops);
if (err < 0)
goto err1;
nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla); nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
if (err < 0)
goto err2;
table->use++; if (chain != NULL) {
list_add_tail_rcu(&chain->list, &table->chains); if (nlh->nlmsg_flags & NLM_F_EXCL)
return 0; return -EEXIST;
err2: if (nlh->nlmsg_flags & NLM_F_REPLACE)
nf_tables_unregister_hooks(net, table, chain, afi->nops); return -EOPNOTSUPP;
err1:
nf_tables_chain_destroy(chain); return nf_tables_updchain(&ctx, genmask, policy, create);
return err; }
return nf_tables_addchain(&ctx, family, genmask, policy, create);
} }
static int nf_tables_delchain(struct net *net, struct sock *nlsk, static int nf_tables_delchain(struct net *net, struct sock *nlsk,
...@@ -1586,8 +1617,11 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk, ...@@ -1586,8 +1617,11 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
struct nft_af_info *afi; struct nft_af_info *afi;
struct nft_table *table; struct nft_table *table;
struct nft_chain *chain; struct nft_chain *chain;
struct nft_rule *rule;
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
struct nft_ctx ctx; struct nft_ctx ctx;
u32 use;
int err;
afi = nf_tables_afinfo_lookup(net, family, false); afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi)) if (IS_ERR(afi))
...@@ -1600,11 +1634,30 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk, ...@@ -1600,11 +1634,30 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask); chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask);
if (IS_ERR(chain)) if (IS_ERR(chain))
return PTR_ERR(chain); return PTR_ERR(chain);
if (chain->use > 0)
if (nlh->nlmsg_flags & NLM_F_NONREC &&
chain->use > 0)
return -EBUSY; return -EBUSY;
nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla); nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
use = chain->use;
list_for_each_entry(rule, &chain->rules, list) {
if (!nft_is_active_next(net, rule))
continue;
use--;
err = nft_delrule(&ctx, rule);
if (err < 0)
return err;
}
/* There are rules and elements that are still holding references to us,
* we cannot do a recursive removal in this case.
*/
if (use > 0)
return -EBUSY;
return nft_delchain(&ctx); return nft_delchain(&ctx);
} }
...@@ -3198,7 +3251,9 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk, ...@@ -3198,7 +3251,9 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk,
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME], genmask); set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME], genmask);
if (IS_ERR(set)) if (IS_ERR(set))
return PTR_ERR(set); return PTR_ERR(set);
if (!list_empty(&set->bindings))
if (!list_empty(&set->bindings) ||
(nlh->nlmsg_flags & NLM_F_NONREC && atomic_read(&set->nelems) > 0))
return -EBUSY; return -EBUSY;
return nft_delset(&ctx, set); return nft_delset(&ctx, set);
...@@ -4248,7 +4303,7 @@ struct nft_object *nf_tables_obj_lookup(const struct nft_table *table, ...@@ -4248,7 +4303,7 @@ struct nft_object *nf_tables_obj_lookup(const struct nft_table *table,
list_for_each_entry(obj, &table->objects, list) { list_for_each_entry(obj, &table->objects, list) {
if (!nla_strcmp(nla, obj->name) && if (!nla_strcmp(nla, obj->name) &&
objtype == obj->type->type && objtype == obj->ops->type->type &&
nft_active_genmask(obj, genmask)) nft_active_genmask(obj, genmask))
return obj; return obj;
} }
...@@ -4270,6 +4325,7 @@ static struct nft_object *nft_obj_init(const struct nft_ctx *ctx, ...@@ -4270,6 +4325,7 @@ static struct nft_object *nft_obj_init(const struct nft_ctx *ctx,
const struct nlattr *attr) const struct nlattr *attr)
{ {
struct nlattr *tb[type->maxattr + 1]; struct nlattr *tb[type->maxattr + 1];
const struct nft_object_ops *ops;
struct nft_object *obj; struct nft_object *obj;
int err; int err;
...@@ -4282,16 +4338,27 @@ static struct nft_object *nft_obj_init(const struct nft_ctx *ctx, ...@@ -4282,16 +4338,27 @@ static struct nft_object *nft_obj_init(const struct nft_ctx *ctx,
memset(tb, 0, sizeof(tb[0]) * (type->maxattr + 1)); memset(tb, 0, sizeof(tb[0]) * (type->maxattr + 1));
} }
if (type->select_ops) {
ops = type->select_ops(ctx, (const struct nlattr * const *)tb);
if (IS_ERR(ops)) {
err = PTR_ERR(ops);
goto err1;
}
} else {
ops = type->ops;
}
err = -ENOMEM; err = -ENOMEM;
obj = kzalloc(sizeof(struct nft_object) + type->size, GFP_KERNEL); obj = kzalloc(sizeof(*obj) + ops->size, GFP_KERNEL);
if (obj == NULL) if (obj == NULL)
goto err1; goto err1;
err = type->init(ctx, (const struct nlattr * const *)tb, obj); err = ops->init(ctx, (const struct nlattr * const *)tb, obj);
if (err < 0) if (err < 0)
goto err2; goto err2;
obj->type = type; obj->ops = ops;
return obj; return obj;
err2: err2:
kfree(obj); kfree(obj);
...@@ -4307,7 +4374,7 @@ static int nft_object_dump(struct sk_buff *skb, unsigned int attr, ...@@ -4307,7 +4374,7 @@ static int nft_object_dump(struct sk_buff *skb, unsigned int attr,
nest = nla_nest_start(skb, attr); nest = nla_nest_start(skb, attr);
if (!nest) if (!nest)
goto nla_put_failure; goto nla_put_failure;
if (obj->type->dump(skb, obj, reset) < 0) if (obj->ops->dump(skb, obj, reset) < 0)
goto nla_put_failure; goto nla_put_failure;
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
return 0; return 0;
...@@ -4418,8 +4485,8 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk, ...@@ -4418,8 +4485,8 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
err3: err3:
kfree(obj->name); kfree(obj->name);
err2: err2:
if (obj->type->destroy) if (obj->ops->destroy)
obj->type->destroy(obj); obj->ops->destroy(obj);
kfree(obj); kfree(obj);
err1: err1:
module_put(type->owner); module_put(type->owner);
...@@ -4446,7 +4513,7 @@ static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net, ...@@ -4446,7 +4513,7 @@ static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net,
if (nla_put_string(skb, NFTA_OBJ_TABLE, table->name) || if (nla_put_string(skb, NFTA_OBJ_TABLE, table->name) ||
nla_put_string(skb, NFTA_OBJ_NAME, obj->name) || nla_put_string(skb, NFTA_OBJ_NAME, obj->name) ||
nla_put_be32(skb, NFTA_OBJ_TYPE, htonl(obj->type->type)) || nla_put_be32(skb, NFTA_OBJ_TYPE, htonl(obj->ops->type->type)) ||
nla_put_be32(skb, NFTA_OBJ_USE, htonl(obj->use)) || nla_put_be32(skb, NFTA_OBJ_USE, htonl(obj->use)) ||
nft_object_dump(skb, NFTA_OBJ_DATA, obj, reset)) nft_object_dump(skb, NFTA_OBJ_DATA, obj, reset))
goto nla_put_failure; goto nla_put_failure;
...@@ -4500,7 +4567,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -4500,7 +4567,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
goto cont; goto cont;
if (filter && if (filter &&
filter->type != NFT_OBJECT_UNSPEC && filter->type != NFT_OBJECT_UNSPEC &&
obj->type->type != filter->type) obj->ops->type->type != filter->type)
goto cont; goto cont;
if (nf_tables_fill_obj_info(skb, net, NETLINK_CB(cb->skb).portid, if (nf_tables_fill_obj_info(skb, net, NETLINK_CB(cb->skb).portid,
...@@ -4628,10 +4695,10 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk, ...@@ -4628,10 +4695,10 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk,
static void nft_obj_destroy(struct nft_object *obj) static void nft_obj_destroy(struct nft_object *obj)
{ {
if (obj->type->destroy) if (obj->ops->destroy)
obj->type->destroy(obj); obj->ops->destroy(obj);
module_put(obj->type->owner); module_put(obj->ops->type->owner);
kfree(obj->name); kfree(obj->name);
kfree(obj); kfree(obj);
} }
......
...@@ -175,15 +175,21 @@ static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { ...@@ -175,15 +175,21 @@ static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, [NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
}; };
static struct nft_object_type nft_counter_obj __read_mostly = { static struct nft_object_type nft_counter_obj_type;
.type = NFT_OBJECT_COUNTER, static const struct nft_object_ops nft_counter_obj_ops = {
.type = &nft_counter_obj_type,
.size = sizeof(struct nft_counter_percpu_priv), .size = sizeof(struct nft_counter_percpu_priv),
.maxattr = NFTA_COUNTER_MAX,
.policy = nft_counter_policy,
.eval = nft_counter_obj_eval, .eval = nft_counter_obj_eval,
.init = nft_counter_obj_init, .init = nft_counter_obj_init,
.destroy = nft_counter_obj_destroy, .destroy = nft_counter_obj_destroy,
.dump = nft_counter_obj_dump, .dump = nft_counter_obj_dump,
};
static struct nft_object_type nft_counter_obj_type __read_mostly = {
.type = NFT_OBJECT_COUNTER,
.ops = &nft_counter_obj_ops,
.maxattr = NFTA_COUNTER_MAX,
.policy = nft_counter_policy,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -271,7 +277,7 @@ static int __init nft_counter_module_init(void) ...@@ -271,7 +277,7 @@ static int __init nft_counter_module_init(void)
for_each_possible_cpu(cpu) for_each_possible_cpu(cpu)
seqcount_init(per_cpu_ptr(&nft_counter_seq, cpu)); seqcount_init(per_cpu_ptr(&nft_counter_seq, cpu));
err = nft_register_obj(&nft_counter_obj); err = nft_register_obj(&nft_counter_obj_type);
if (err < 0) if (err < 0)
return err; return err;
...@@ -281,14 +287,14 @@ static int __init nft_counter_module_init(void) ...@@ -281,14 +287,14 @@ static int __init nft_counter_module_init(void)
return 0; return 0;
err1: err1:
nft_unregister_obj(&nft_counter_obj); nft_unregister_obj(&nft_counter_obj_type);
return err; return err;
} }
static void __exit nft_counter_module_exit(void) static void __exit nft_counter_module_exit(void)
{ {
nft_unregister_expr(&nft_counter_type); nft_unregister_expr(&nft_counter_type);
nft_unregister_obj(&nft_counter_obj); nft_unregister_obj(&nft_counter_obj_type);
} }
module_init(nft_counter_module_init); module_init(nft_counter_module_init);
......
...@@ -904,15 +904,21 @@ static const struct nla_policy nft_ct_helper_policy[NFTA_CT_HELPER_MAX + 1] = { ...@@ -904,15 +904,21 @@ static const struct nla_policy nft_ct_helper_policy[NFTA_CT_HELPER_MAX + 1] = {
[NFTA_CT_HELPER_L4PROTO] = { .type = NLA_U8 }, [NFTA_CT_HELPER_L4PROTO] = { .type = NLA_U8 },
}; };
static struct nft_object_type nft_ct_helper_obj __read_mostly = { static struct nft_object_type nft_ct_helper_obj_type;
.type = NFT_OBJECT_CT_HELPER, static const struct nft_object_ops nft_ct_helper_obj_ops = {
.type = &nft_ct_helper_obj_type,
.size = sizeof(struct nft_ct_helper_obj), .size = sizeof(struct nft_ct_helper_obj),
.maxattr = NFTA_CT_HELPER_MAX,
.policy = nft_ct_helper_policy,
.eval = nft_ct_helper_obj_eval, .eval = nft_ct_helper_obj_eval,
.init = nft_ct_helper_obj_init, .init = nft_ct_helper_obj_init,
.destroy = nft_ct_helper_obj_destroy, .destroy = nft_ct_helper_obj_destroy,
.dump = nft_ct_helper_obj_dump, .dump = nft_ct_helper_obj_dump,
};
static struct nft_object_type nft_ct_helper_obj_type __read_mostly = {
.type = NFT_OBJECT_CT_HELPER,
.ops = &nft_ct_helper_obj_ops,
.maxattr = NFTA_CT_HELPER_MAX,
.policy = nft_ct_helper_policy,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -930,7 +936,7 @@ static int __init nft_ct_module_init(void) ...@@ -930,7 +936,7 @@ static int __init nft_ct_module_init(void)
if (err < 0) if (err < 0)
goto err1; goto err1;
err = nft_register_obj(&nft_ct_helper_obj); err = nft_register_obj(&nft_ct_helper_obj_type);
if (err < 0) if (err < 0)
goto err2; goto err2;
...@@ -945,7 +951,7 @@ static int __init nft_ct_module_init(void) ...@@ -945,7 +951,7 @@ static int __init nft_ct_module_init(void)
static void __exit nft_ct_module_exit(void) static void __exit nft_ct_module_exit(void)
{ {
nft_unregister_obj(&nft_ct_helper_obj); nft_unregister_obj(&nft_ct_helper_obj_type);
nft_unregister_expr(&nft_notrack_type); nft_unregister_expr(&nft_notrack_type);
nft_unregister_expr(&nft_ct_type); nft_unregister_expr(&nft_ct_type);
} }
......
...@@ -168,7 +168,7 @@ static const struct nft_expr_ops nft_limit_pkts_ops = { ...@@ -168,7 +168,7 @@ static const struct nft_expr_ops nft_limit_pkts_ops = {
.dump = nft_limit_pkts_dump, .dump = nft_limit_pkts_dump,
}; };
static void nft_limit_pkt_bytes_eval(const struct nft_expr *expr, static void nft_limit_bytes_eval(const struct nft_expr *expr,
struct nft_regs *regs, struct nft_regs *regs,
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt)
{ {
...@@ -179,7 +179,7 @@ static void nft_limit_pkt_bytes_eval(const struct nft_expr *expr, ...@@ -179,7 +179,7 @@ static void nft_limit_pkt_bytes_eval(const struct nft_expr *expr,
regs->verdict.code = NFT_BREAK; regs->verdict.code = NFT_BREAK;
} }
static int nft_limit_pkt_bytes_init(const struct nft_ctx *ctx, static int nft_limit_bytes_init(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
...@@ -188,7 +188,7 @@ static int nft_limit_pkt_bytes_init(const struct nft_ctx *ctx, ...@@ -188,7 +188,7 @@ static int nft_limit_pkt_bytes_init(const struct nft_ctx *ctx,
return nft_limit_init(priv, tb); return nft_limit_init(priv, tb);
} }
static int nft_limit_pkt_bytes_dump(struct sk_buff *skb, static int nft_limit_bytes_dump(struct sk_buff *skb,
const struct nft_expr *expr) const struct nft_expr *expr)
{ {
const struct nft_limit *priv = nft_expr_priv(expr); const struct nft_limit *priv = nft_expr_priv(expr);
...@@ -196,12 +196,12 @@ static int nft_limit_pkt_bytes_dump(struct sk_buff *skb, ...@@ -196,12 +196,12 @@ static int nft_limit_pkt_bytes_dump(struct sk_buff *skb,
return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES); return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES);
} }
static const struct nft_expr_ops nft_limit_pkt_bytes_ops = { static const struct nft_expr_ops nft_limit_bytes_ops = {
.type = &nft_limit_type, .type = &nft_limit_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_limit)), .size = NFT_EXPR_SIZE(sizeof(struct nft_limit)),
.eval = nft_limit_pkt_bytes_eval, .eval = nft_limit_bytes_eval,
.init = nft_limit_pkt_bytes_init, .init = nft_limit_bytes_init,
.dump = nft_limit_pkt_bytes_dump, .dump = nft_limit_bytes_dump,
}; };
static const struct nft_expr_ops * static const struct nft_expr_ops *
...@@ -215,7 +215,7 @@ nft_limit_select_ops(const struct nft_ctx *ctx, ...@@ -215,7 +215,7 @@ nft_limit_select_ops(const struct nft_ctx *ctx,
case NFT_LIMIT_PKTS: case NFT_LIMIT_PKTS:
return &nft_limit_pkts_ops; return &nft_limit_pkts_ops;
case NFT_LIMIT_PKT_BYTES: case NFT_LIMIT_PKT_BYTES:
return &nft_limit_pkt_bytes_ops; return &nft_limit_bytes_ops;
} }
return ERR_PTR(-EOPNOTSUPP); return ERR_PTR(-EOPNOTSUPP);
} }
...@@ -229,14 +229,133 @@ static struct nft_expr_type nft_limit_type __read_mostly = { ...@@ -229,14 +229,133 @@ static struct nft_expr_type nft_limit_type __read_mostly = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static void nft_limit_obj_pkts_eval(struct nft_object *obj,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_limit_pkts *priv = nft_obj_data(obj);
if (nft_limit_eval(&priv->limit, priv->cost))
regs->verdict.code = NFT_BREAK;
}
static int nft_limit_obj_pkts_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[],
struct nft_object *obj)
{
struct nft_limit_pkts *priv = nft_obj_data(obj);
int err;
err = nft_limit_init(&priv->limit, tb);
if (err < 0)
return err;
priv->cost = div64_u64(priv->limit.nsecs, priv->limit.rate);
return 0;
}
static int nft_limit_obj_pkts_dump(struct sk_buff *skb,
struct nft_object *obj,
bool reset)
{
const struct nft_limit_pkts *priv = nft_obj_data(obj);
return nft_limit_dump(skb, &priv->limit, NFT_LIMIT_PKTS);
}
static struct nft_object_type nft_limit_obj_type;
static const struct nft_object_ops nft_limit_obj_pkts_ops = {
.type = &nft_limit_obj_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_limit_pkts)),
.init = nft_limit_obj_pkts_init,
.eval = nft_limit_obj_pkts_eval,
.dump = nft_limit_obj_pkts_dump,
};
static void nft_limit_obj_bytes_eval(struct nft_object *obj,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_limit *priv = nft_obj_data(obj);
u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate);
if (nft_limit_eval(priv, cost))
regs->verdict.code = NFT_BREAK;
}
static int nft_limit_obj_bytes_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[],
struct nft_object *obj)
{
struct nft_limit *priv = nft_obj_data(obj);
return nft_limit_init(priv, tb);
}
static int nft_limit_obj_bytes_dump(struct sk_buff *skb,
struct nft_object *obj,
bool reset)
{
const struct nft_limit *priv = nft_obj_data(obj);
return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES);
}
static struct nft_object_type nft_limit_obj_type;
static const struct nft_object_ops nft_limit_obj_bytes_ops = {
.type = &nft_limit_obj_type,
.size = sizeof(struct nft_limit),
.init = nft_limit_obj_bytes_init,
.eval = nft_limit_obj_bytes_eval,
.dump = nft_limit_obj_bytes_dump,
};
static const struct nft_object_ops *
nft_limit_obj_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
if (!tb[NFTA_LIMIT_TYPE])
return &nft_limit_obj_pkts_ops;
switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) {
case NFT_LIMIT_PKTS:
return &nft_limit_obj_pkts_ops;
case NFT_LIMIT_PKT_BYTES:
return &nft_limit_obj_bytes_ops;
}
return ERR_PTR(-EOPNOTSUPP);
}
static struct nft_object_type nft_limit_obj_type __read_mostly = {
.select_ops = nft_limit_obj_select_ops,
.type = NFT_OBJECT_LIMIT,
.maxattr = NFTA_LIMIT_MAX,
.policy = nft_limit_policy,
.owner = THIS_MODULE,
};
static int __init nft_limit_module_init(void) static int __init nft_limit_module_init(void)
{ {
return nft_register_expr(&nft_limit_type); int err;
err = nft_register_obj(&nft_limit_obj_type);
if (err < 0)
return err;
err = nft_register_expr(&nft_limit_type);
if (err < 0)
goto err1;
return 0;
err1:
nft_unregister_obj(&nft_limit_obj_type);
return err;
} }
static void __exit nft_limit_module_exit(void) static void __exit nft_limit_module_exit(void)
{ {
nft_unregister_expr(&nft_limit_type); nft_unregister_expr(&nft_limit_type);
nft_unregister_obj(&nft_limit_obj_type);
} }
module_init(nft_limit_module_init); module_init(nft_limit_module_init);
...@@ -245,3 +364,4 @@ module_exit(nft_limit_module_exit); ...@@ -245,3 +364,4 @@ module_exit(nft_limit_module_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_EXPR("limit"); MODULE_ALIAS_NFT_EXPR("limit");
MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_LIMIT);
...@@ -22,7 +22,7 @@ static void nft_objref_eval(const struct nft_expr *expr, ...@@ -22,7 +22,7 @@ static void nft_objref_eval(const struct nft_expr *expr,
{ {
struct nft_object *obj = nft_objref_priv(expr); struct nft_object *obj = nft_objref_priv(expr);
obj->type->eval(obj, regs, pkt); obj->ops->eval(obj, regs, pkt);
} }
static int nft_objref_init(const struct nft_ctx *ctx, static int nft_objref_init(const struct nft_ctx *ctx,
...@@ -54,7 +54,8 @@ static int nft_objref_dump(struct sk_buff *skb, const struct nft_expr *expr) ...@@ -54,7 +54,8 @@ static int nft_objref_dump(struct sk_buff *skb, const struct nft_expr *expr)
const struct nft_object *obj = nft_objref_priv(expr); const struct nft_object *obj = nft_objref_priv(expr);
if (nla_put_string(skb, NFTA_OBJREF_IMM_NAME, obj->name) || if (nla_put_string(skb, NFTA_OBJREF_IMM_NAME, obj->name) ||
nla_put_be32(skb, NFTA_OBJREF_IMM_TYPE, htonl(obj->type->type))) nla_put_be32(skb, NFTA_OBJREF_IMM_TYPE,
htonl(obj->ops->type->type)))
goto nla_put_failure; goto nla_put_failure;
return 0; return 0;
...@@ -104,7 +105,7 @@ static void nft_objref_map_eval(const struct nft_expr *expr, ...@@ -104,7 +105,7 @@ static void nft_objref_map_eval(const struct nft_expr *expr,
return; return;
} }
obj = *nft_set_ext_obj(ext); obj = *nft_set_ext_obj(ext);
obj->type->eval(obj, regs, pkt); obj->ops->eval(obj, regs, pkt);
} }
static int nft_objref_map_init(const struct nft_ctx *ctx, static int nft_objref_map_init(const struct nft_ctx *ctx,
......
...@@ -151,14 +151,20 @@ static int nft_quota_obj_dump(struct sk_buff *skb, struct nft_object *obj, ...@@ -151,14 +151,20 @@ static int nft_quota_obj_dump(struct sk_buff *skb, struct nft_object *obj,
return nft_quota_do_dump(skb, priv, reset); return nft_quota_do_dump(skb, priv, reset);
} }
static struct nft_object_type nft_quota_obj __read_mostly = { static struct nft_object_type nft_quota_obj_type;
.type = NFT_OBJECT_QUOTA, static const struct nft_object_ops nft_quota_obj_ops = {
.type = &nft_quota_obj_type,
.size = sizeof(struct nft_quota), .size = sizeof(struct nft_quota),
.maxattr = NFTA_QUOTA_MAX,
.policy = nft_quota_policy,
.init = nft_quota_obj_init, .init = nft_quota_obj_init,
.eval = nft_quota_obj_eval, .eval = nft_quota_obj_eval,
.dump = nft_quota_obj_dump, .dump = nft_quota_obj_dump,
};
static struct nft_object_type nft_quota_obj_type __read_mostly = {
.type = NFT_OBJECT_QUOTA,
.ops = &nft_quota_obj_ops,
.maxattr = NFTA_QUOTA_MAX,
.policy = nft_quota_policy,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -209,7 +215,7 @@ static int __init nft_quota_module_init(void) ...@@ -209,7 +215,7 @@ static int __init nft_quota_module_init(void)
{ {
int err; int err;
err = nft_register_obj(&nft_quota_obj); err = nft_register_obj(&nft_quota_obj_type);
if (err < 0) if (err < 0)
return err; return err;
...@@ -219,14 +225,14 @@ static int __init nft_quota_module_init(void) ...@@ -219,14 +225,14 @@ static int __init nft_quota_module_init(void)
return 0; return 0;
err1: err1:
nft_unregister_obj(&nft_quota_obj); nft_unregister_obj(&nft_quota_obj_type);
return err; return err;
} }
static void __exit nft_quota_module_exit(void) static void __exit nft_quota_module_exit(void)
{ {
nft_unregister_expr(&nft_quota_type); nft_unregister_expr(&nft_quota_type);
nft_unregister_obj(&nft_quota_obj); nft_unregister_obj(&nft_quota_obj_type);
} }
module_init(nft_quota_module_init); module_init(nft_quota_module_init);
......
...@@ -77,10 +77,10 @@ netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -77,10 +77,10 @@ netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par)
const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
struct nf_nat_range newrange; struct nf_nat_range newrange;
NF_CT_ASSERT(xt_hooknum(par) == NF_INET_PRE_ROUTING || WARN_ON(xt_hooknum(par) != NF_INET_PRE_ROUTING &&
xt_hooknum(par) == NF_INET_POST_ROUTING || xt_hooknum(par) != NF_INET_POST_ROUTING &&
xt_hooknum(par) == NF_INET_LOCAL_OUT || xt_hooknum(par) != NF_INET_LOCAL_OUT &&
xt_hooknum(par) == NF_INET_LOCAL_IN); xt_hooknum(par) != NF_INET_LOCAL_IN);
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip);
......
...@@ -56,6 +56,7 @@ static inline struct hashlimit_net *hashlimit_pernet(struct net *net) ...@@ -56,6 +56,7 @@ static inline struct hashlimit_net *hashlimit_pernet(struct net *net)
} }
/* need to declare this at the top */ /* need to declare this at the top */
static const struct file_operations dl_file_ops_v2;
static const struct file_operations dl_file_ops_v1; static const struct file_operations dl_file_ops_v1;
static const struct file_operations dl_file_ops; static const struct file_operations dl_file_ops;
...@@ -87,8 +88,19 @@ struct dsthash_ent { ...@@ -87,8 +88,19 @@ struct dsthash_ent {
unsigned long expires; /* precalculated expiry time */ unsigned long expires; /* precalculated expiry time */
struct { struct {
unsigned long prev; /* last modification */ unsigned long prev; /* last modification */
union {
struct {
u_int64_t credit; u_int64_t credit;
u_int64_t credit_cap, cost; u_int64_t credit_cap;
u_int64_t cost;
};
struct {
u_int32_t interval, prev_window;
u_int64_t current_rate;
u_int64_t rate;
int64_t burst;
};
};
} rateinfo; } rateinfo;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -99,7 +111,7 @@ struct xt_hashlimit_htable { ...@@ -99,7 +111,7 @@ struct xt_hashlimit_htable {
u_int8_t family; u_int8_t family;
bool rnd_initialized; bool rnd_initialized;
struct hashlimit_cfg2 cfg; /* config */ struct hashlimit_cfg3 cfg; /* config */
/* used internally */ /* used internally */
spinlock_t lock; /* lock for list_head */ spinlock_t lock; /* lock for list_head */
...@@ -116,10 +128,10 @@ struct xt_hashlimit_htable { ...@@ -116,10 +128,10 @@ struct xt_hashlimit_htable {
}; };
static int static int
cfg_copy(struct hashlimit_cfg2 *to, void *from, int revision) cfg_copy(struct hashlimit_cfg3 *to, const void *from, int revision)
{ {
if (revision == 1) { if (revision == 1) {
struct hashlimit_cfg1 *cfg = from; struct hashlimit_cfg1 *cfg = (struct hashlimit_cfg1 *)from;
to->mode = cfg->mode; to->mode = cfg->mode;
to->avg = cfg->avg; to->avg = cfg->avg;
...@@ -131,7 +143,19 @@ cfg_copy(struct hashlimit_cfg2 *to, void *from, int revision) ...@@ -131,7 +143,19 @@ cfg_copy(struct hashlimit_cfg2 *to, void *from, int revision)
to->srcmask = cfg->srcmask; to->srcmask = cfg->srcmask;
to->dstmask = cfg->dstmask; to->dstmask = cfg->dstmask;
} else if (revision == 2) { } else if (revision == 2) {
memcpy(to, from, sizeof(struct hashlimit_cfg2)); struct hashlimit_cfg2 *cfg = (struct hashlimit_cfg2 *)from;
to->mode = cfg->mode;
to->avg = cfg->avg;
to->burst = cfg->burst;
to->size = cfg->size;
to->max = cfg->max;
to->gc_interval = cfg->gc_interval;
to->expire = cfg->expire;
to->srcmask = cfg->srcmask;
to->dstmask = cfg->dstmask;
} else if (revision == 3) {
memcpy(to, from, sizeof(struct hashlimit_cfg3));
} else { } else {
return -EINVAL; return -EINVAL;
} }
...@@ -240,13 +264,14 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent) ...@@ -240,13 +264,14 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent)
} }
static void htable_gc(struct work_struct *work); static void htable_gc(struct work_struct *work);
static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg, static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,
const char *name, u_int8_t family, const char *name, u_int8_t family,
struct xt_hashlimit_htable **out_hinfo, struct xt_hashlimit_htable **out_hinfo,
int revision) int revision)
{ {
struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
struct xt_hashlimit_htable *hinfo; struct xt_hashlimit_htable *hinfo;
const struct file_operations *fops;
unsigned int size, i; unsigned int size, i;
int ret; int ret;
...@@ -268,7 +293,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg, ...@@ -268,7 +293,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg,
*out_hinfo = hinfo; *out_hinfo = hinfo;
/* copy match config into hashtable config */ /* copy match config into hashtable config */
ret = cfg_copy(&hinfo->cfg, (void *)cfg, 2); ret = cfg_copy(&hinfo->cfg, (void *)cfg, 3);
if (ret) if (ret)
return ret; return ret;
...@@ -293,11 +318,21 @@ static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg, ...@@ -293,11 +318,21 @@ static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg,
} }
spin_lock_init(&hinfo->lock); spin_lock_init(&hinfo->lock);
switch (revision) {
case 1:
fops = &dl_file_ops_v1;
break;
case 2:
fops = &dl_file_ops_v2;
break;
default:
fops = &dl_file_ops;
}
hinfo->pde = proc_create_data(name, 0, hinfo->pde = proc_create_data(name, 0,
(family == NFPROTO_IPV4) ? (family == NFPROTO_IPV4) ?
hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit, hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit,
(revision == 1) ? &dl_file_ops_v1 : &dl_file_ops, fops, hinfo);
hinfo);
if (hinfo->pde == NULL) { if (hinfo->pde == NULL) {
kfree(hinfo->name); kfree(hinfo->name);
vfree(hinfo); vfree(hinfo);
...@@ -482,6 +517,25 @@ static u32 user2credits_byte(u32 user) ...@@ -482,6 +517,25 @@ static u32 user2credits_byte(u32 user)
return (u32) (us >> 32); return (u32) (us >> 32);
} }
static u64 user2rate(u64 user)
{
if (user != 0) {
return div64_u64(XT_HASHLIMIT_SCALE_v2, user);
} else {
pr_warn("invalid rate from userspace: %llu\n", user);
return 0;
}
}
static u64 user2rate_bytes(u64 user)
{
u64 r;
r = user ? 0xFFFFFFFFULL / user : 0xFFFFFFFFULL;
r = (r - 1) << 4;
return r;
}
static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now, static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now,
u32 mode, int revision) u32 mode, int revision)
{ {
...@@ -491,6 +545,21 @@ static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now, ...@@ -491,6 +545,21 @@ static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now,
if (delta == 0) if (delta == 0)
return; return;
if (revision >= 3 && mode & XT_HASHLIMIT_RATE_MATCH) {
u64 interval = dh->rateinfo.interval * HZ;
if (delta < interval)
return;
dh->rateinfo.prev = now;
dh->rateinfo.prev_window =
((dh->rateinfo.current_rate * interval) >
(delta * dh->rateinfo.rate));
dh->rateinfo.current_rate = 0;
return;
}
dh->rateinfo.prev = now; dh->rateinfo.prev = now;
if (mode & XT_HASHLIMIT_BYTES) { if (mode & XT_HASHLIMIT_BYTES) {
...@@ -515,7 +584,23 @@ static void rateinfo_init(struct dsthash_ent *dh, ...@@ -515,7 +584,23 @@ static void rateinfo_init(struct dsthash_ent *dh,
struct xt_hashlimit_htable *hinfo, int revision) struct xt_hashlimit_htable *hinfo, int revision)
{ {
dh->rateinfo.prev = jiffies; dh->rateinfo.prev = jiffies;
if (revision >= 3 && hinfo->cfg.mode & XT_HASHLIMIT_RATE_MATCH) {
dh->rateinfo.prev_window = 0;
dh->rateinfo.current_rate = 0;
if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) { if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) {
dh->rateinfo.rate = user2rate_bytes(hinfo->cfg.avg);
if (hinfo->cfg.burst)
dh->rateinfo.burst =
hinfo->cfg.burst * dh->rateinfo.rate;
else
dh->rateinfo.burst = dh->rateinfo.rate;
} else {
dh->rateinfo.rate = user2rate(hinfo->cfg.avg);
dh->rateinfo.burst =
hinfo->cfg.burst + dh->rateinfo.rate;
}
dh->rateinfo.interval = hinfo->cfg.interval;
} else if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) {
dh->rateinfo.credit = CREDITS_PER_JIFFY_BYTES * HZ; dh->rateinfo.credit = CREDITS_PER_JIFFY_BYTES * HZ;
dh->rateinfo.cost = user2credits_byte(hinfo->cfg.avg); dh->rateinfo.cost = user2credits_byte(hinfo->cfg.avg);
dh->rateinfo.credit_cap = hinfo->cfg.burst; dh->rateinfo.credit_cap = hinfo->cfg.burst;
...@@ -648,7 +733,7 @@ static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh) ...@@ -648,7 +733,7 @@ static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh)
static bool static bool
hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par, hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par,
struct xt_hashlimit_htable *hinfo, struct xt_hashlimit_htable *hinfo,
const struct hashlimit_cfg2 *cfg, int revision) const struct hashlimit_cfg3 *cfg, int revision)
{ {
unsigned long now = jiffies; unsigned long now = jiffies;
struct dsthash_ent *dh; struct dsthash_ent *dh;
...@@ -680,6 +765,20 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par, ...@@ -680,6 +765,20 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par,
rateinfo_recalc(dh, now, hinfo->cfg.mode, revision); rateinfo_recalc(dh, now, hinfo->cfg.mode, revision);
} }
if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
cost = (cfg->mode & XT_HASHLIMIT_BYTES) ? skb->len : 1;
dh->rateinfo.current_rate += cost;
if (!dh->rateinfo.prev_window &&
(dh->rateinfo.current_rate <= dh->rateinfo.burst)) {
spin_unlock(&dh->lock);
rcu_read_unlock_bh();
return !(cfg->mode & XT_HASHLIMIT_INVERT);
} else {
goto overlimit;
}
}
if (cfg->mode & XT_HASHLIMIT_BYTES) if (cfg->mode & XT_HASHLIMIT_BYTES)
cost = hashlimit_byte_cost(skb->len, dh); cost = hashlimit_byte_cost(skb->len, dh);
else else
...@@ -693,6 +792,7 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par, ...@@ -693,6 +792,7 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par,
return !(cfg->mode & XT_HASHLIMIT_INVERT); return !(cfg->mode & XT_HASHLIMIT_INVERT);
} }
overlimit:
spin_unlock(&dh->lock); spin_unlock(&dh->lock);
local_bh_enable(); local_bh_enable();
/* default match is underlimit - so over the limit, we need to invert */ /* default match is underlimit - so over the limit, we need to invert */
...@@ -708,7 +808,7 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) ...@@ -708,7 +808,7 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
{ {
const struct xt_hashlimit_mtinfo1 *info = par->matchinfo; const struct xt_hashlimit_mtinfo1 *info = par->matchinfo;
struct xt_hashlimit_htable *hinfo = info->hinfo; struct xt_hashlimit_htable *hinfo = info->hinfo;
struct hashlimit_cfg2 cfg = {}; struct hashlimit_cfg3 cfg = {};
int ret; int ret;
ret = cfg_copy(&cfg, (void *)&info->cfg, 1); ret = cfg_copy(&cfg, (void *)&info->cfg, 1);
...@@ -720,17 +820,33 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) ...@@ -720,17 +820,33 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
} }
static bool static bool
hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) hashlimit_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
{ {
const struct xt_hashlimit_mtinfo2 *info = par->matchinfo; const struct xt_hashlimit_mtinfo2 *info = par->matchinfo;
struct xt_hashlimit_htable *hinfo = info->hinfo; struct xt_hashlimit_htable *hinfo = info->hinfo;
struct hashlimit_cfg3 cfg = {};
int ret;
return hashlimit_mt_common(skb, par, hinfo, &info->cfg, 2); ret = cfg_copy(&cfg, (void *)&info->cfg, 2);
if (ret)
return ret;
return hashlimit_mt_common(skb, par, hinfo, &cfg, 2);
}
static bool
hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_hashlimit_mtinfo3 *info = par->matchinfo;
struct xt_hashlimit_htable *hinfo = info->hinfo;
return hashlimit_mt_common(skb, par, hinfo, &info->cfg, 3);
} }
static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
struct xt_hashlimit_htable **hinfo, struct xt_hashlimit_htable **hinfo,
struct hashlimit_cfg2 *cfg, struct hashlimit_cfg3 *cfg,
const char *name, int revision) const char *name, int revision)
{ {
struct net *net = par->net; struct net *net = par->net;
...@@ -753,7 +869,17 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, ...@@ -753,7 +869,17 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
} }
/* Check for overflow. */ /* Check for overflow. */
if (cfg->mode & XT_HASHLIMIT_BYTES) { if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {
if (cfg->avg == 0) {
pr_info("hashlimit invalid rate\n");
return -ERANGE;
}
if (cfg->interval == 0) {
pr_info("hashlimit invalid interval\n");
return -EINVAL;
}
} else if (cfg->mode & XT_HASHLIMIT_BYTES) {
if (user2credits_byte(cfg->avg) == 0) { if (user2credits_byte(cfg->avg) == 0) {
pr_info("overflow, rate too high: %llu\n", cfg->avg); pr_info("overflow, rate too high: %llu\n", cfg->avg);
return -EINVAL; return -EINVAL;
...@@ -784,7 +910,7 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, ...@@ -784,7 +910,7 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,
static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par) static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par)
{ {
struct xt_hashlimit_mtinfo1 *info = par->matchinfo; struct xt_hashlimit_mtinfo1 *info = par->matchinfo;
struct hashlimit_cfg2 cfg = {}; struct hashlimit_cfg3 cfg = {};
int ret; int ret;
if (info->name[sizeof(info->name) - 1] != '\0') if (info->name[sizeof(info->name) - 1] != '\0')
...@@ -799,15 +925,40 @@ static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par) ...@@ -799,15 +925,40 @@ static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par)
&cfg, info->name, 1); &cfg, info->name, 1);
} }
static int hashlimit_mt_check(const struct xt_mtchk_param *par) static int hashlimit_mt_check_v2(const struct xt_mtchk_param *par)
{ {
struct xt_hashlimit_mtinfo2 *info = par->matchinfo; struct xt_hashlimit_mtinfo2 *info = par->matchinfo;
struct hashlimit_cfg3 cfg = {};
int ret;
if (info->name[sizeof(info->name) - 1] != '\0')
return -EINVAL;
ret = cfg_copy(&cfg, (void *)&info->cfg, 2);
if (ret)
return ret;
return hashlimit_mt_check_common(par, &info->hinfo,
&cfg, info->name, 2);
}
static int hashlimit_mt_check(const struct xt_mtchk_param *par)
{
struct xt_hashlimit_mtinfo3 *info = par->matchinfo;
if (info->name[sizeof(info->name) - 1] != '\0') if (info->name[sizeof(info->name) - 1] != '\0')
return -EINVAL; return -EINVAL;
return hashlimit_mt_check_common(par, &info->hinfo, &info->cfg, return hashlimit_mt_check_common(par, &info->hinfo, &info->cfg,
info->name, 2); info->name, 3);
}
static void hashlimit_mt_destroy_v2(const struct xt_mtdtor_param *par)
{
const struct xt_hashlimit_mtinfo2 *info = par->matchinfo;
htable_put(info->hinfo);
} }
static void hashlimit_mt_destroy_v1(const struct xt_mtdtor_param *par) static void hashlimit_mt_destroy_v1(const struct xt_mtdtor_param *par)
...@@ -819,7 +970,7 @@ static void hashlimit_mt_destroy_v1(const struct xt_mtdtor_param *par) ...@@ -819,7 +970,7 @@ static void hashlimit_mt_destroy_v1(const struct xt_mtdtor_param *par)
static void hashlimit_mt_destroy(const struct xt_mtdtor_param *par) static void hashlimit_mt_destroy(const struct xt_mtdtor_param *par)
{ {
const struct xt_hashlimit_mtinfo2 *info = par->matchinfo; const struct xt_hashlimit_mtinfo3 *info = par->matchinfo;
htable_put(info->hinfo); htable_put(info->hinfo);
} }
...@@ -840,9 +991,20 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = { ...@@ -840,9 +991,20 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = {
.name = "hashlimit", .name = "hashlimit",
.revision = 2, .revision = 2,
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.match = hashlimit_mt, .match = hashlimit_mt_v2,
.matchsize = sizeof(struct xt_hashlimit_mtinfo2), .matchsize = sizeof(struct xt_hashlimit_mtinfo2),
.usersize = offsetof(struct xt_hashlimit_mtinfo2, hinfo), .usersize = offsetof(struct xt_hashlimit_mtinfo2, hinfo),
.checkentry = hashlimit_mt_check_v2,
.destroy = hashlimit_mt_destroy_v2,
.me = THIS_MODULE,
},
{
.name = "hashlimit",
.revision = 3,
.family = NFPROTO_IPV4,
.match = hashlimit_mt,
.matchsize = sizeof(struct xt_hashlimit_mtinfo3),
.usersize = offsetof(struct xt_hashlimit_mtinfo3, hinfo),
.checkentry = hashlimit_mt_check, .checkentry = hashlimit_mt_check,
.destroy = hashlimit_mt_destroy, .destroy = hashlimit_mt_destroy,
.me = THIS_MODULE, .me = THIS_MODULE,
...@@ -863,9 +1025,20 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = { ...@@ -863,9 +1025,20 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = {
.name = "hashlimit", .name = "hashlimit",
.revision = 2, .revision = 2,
.family = NFPROTO_IPV6, .family = NFPROTO_IPV6,
.match = hashlimit_mt, .match = hashlimit_mt_v2,
.matchsize = sizeof(struct xt_hashlimit_mtinfo2), .matchsize = sizeof(struct xt_hashlimit_mtinfo2),
.usersize = offsetof(struct xt_hashlimit_mtinfo2, hinfo), .usersize = offsetof(struct xt_hashlimit_mtinfo2, hinfo),
.checkentry = hashlimit_mt_check_v2,
.destroy = hashlimit_mt_destroy_v2,
.me = THIS_MODULE,
},
{
.name = "hashlimit",
.revision = 3,
.family = NFPROTO_IPV6,
.match = hashlimit_mt,
.matchsize = sizeof(struct xt_hashlimit_mtinfo3),
.usersize = offsetof(struct xt_hashlimit_mtinfo3, hinfo),
.checkentry = hashlimit_mt_check, .checkentry = hashlimit_mt_check,
.destroy = hashlimit_mt_destroy, .destroy = hashlimit_mt_destroy,
.me = THIS_MODULE, .me = THIS_MODULE,
...@@ -947,6 +1120,21 @@ static void dl_seq_print(struct dsthash_ent *ent, u_int8_t family, ...@@ -947,6 +1120,21 @@ static void dl_seq_print(struct dsthash_ent *ent, u_int8_t family,
} }
} }
static int dl_seq_real_show_v2(struct dsthash_ent *ent, u_int8_t family,
struct seq_file *s)
{
const struct xt_hashlimit_htable *ht = s->private;
spin_lock(&ent->lock);
/* recalculate to show accurate numbers */
rateinfo_recalc(ent, jiffies, ht->cfg.mode, 2);
dl_seq_print(ent, family, s);
spin_unlock(&ent->lock);
return seq_has_overflowed(s);
}
static int dl_seq_real_show_v1(struct dsthash_ent *ent, u_int8_t family, static int dl_seq_real_show_v1(struct dsthash_ent *ent, u_int8_t family,
struct seq_file *s) struct seq_file *s)
{ {
...@@ -969,7 +1157,7 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, ...@@ -969,7 +1157,7 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,
spin_lock(&ent->lock); spin_lock(&ent->lock);
/* recalculate to show accurate numbers */ /* recalculate to show accurate numbers */
rateinfo_recalc(ent, jiffies, ht->cfg.mode, 2); rateinfo_recalc(ent, jiffies, ht->cfg.mode, 3);
dl_seq_print(ent, family, s); dl_seq_print(ent, family, s);
...@@ -977,6 +1165,20 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, ...@@ -977,6 +1165,20 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,
return seq_has_overflowed(s); return seq_has_overflowed(s);
} }
static int dl_seq_show_v2(struct seq_file *s, void *v)
{
struct xt_hashlimit_htable *htable = s->private;
unsigned int *bucket = (unsigned int *)v;
struct dsthash_ent *ent;
if (!hlist_empty(&htable->hash[*bucket])) {
hlist_for_each_entry(ent, &htable->hash[*bucket], node)
if (dl_seq_real_show_v2(ent, htable->family, s))
return -1;
}
return 0;
}
static int dl_seq_show_v1(struct seq_file *s, void *v) static int dl_seq_show_v1(struct seq_file *s, void *v)
{ {
struct xt_hashlimit_htable *htable = s->private; struct xt_hashlimit_htable *htable = s->private;
...@@ -1012,6 +1214,13 @@ static const struct seq_operations dl_seq_ops_v1 = { ...@@ -1012,6 +1214,13 @@ static const struct seq_operations dl_seq_ops_v1 = {
.show = dl_seq_show_v1 .show = dl_seq_show_v1
}; };
static const struct seq_operations dl_seq_ops_v2 = {
.start = dl_seq_start,
.next = dl_seq_next,
.stop = dl_seq_stop,
.show = dl_seq_show_v2
};
static const struct seq_operations dl_seq_ops = { static const struct seq_operations dl_seq_ops = {
.start = dl_seq_start, .start = dl_seq_start,
.next = dl_seq_next, .next = dl_seq_next,
...@@ -1019,6 +1228,18 @@ static const struct seq_operations dl_seq_ops = { ...@@ -1019,6 +1228,18 @@ static const struct seq_operations dl_seq_ops = {
.show = dl_seq_show .show = dl_seq_show
}; };
static int dl_proc_open_v2(struct inode *inode, struct file *file)
{
int ret = seq_open(file, &dl_seq_ops_v2);
if (!ret) {
struct seq_file *sf = file->private_data;
sf->private = PDE_DATA(inode);
}
return ret;
}
static int dl_proc_open_v1(struct inode *inode, struct file *file) static int dl_proc_open_v1(struct inode *inode, struct file *file)
{ {
int ret = seq_open(file, &dl_seq_ops_v1); int ret = seq_open(file, &dl_seq_ops_v1);
...@@ -1042,6 +1263,14 @@ static int dl_proc_open(struct inode *inode, struct file *file) ...@@ -1042,6 +1263,14 @@ static int dl_proc_open(struct inode *inode, struct file *file)
return ret; return ret;
} }
static const struct file_operations dl_file_ops_v2 = {
.owner = THIS_MODULE,
.open = dl_proc_open_v2,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
static const struct file_operations dl_file_ops_v1 = { static const struct file_operations dl_file_ops_v1 = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = dl_proc_open_v1, .open = dl_proc_open_v1,
......
...@@ -58,9 +58,9 @@ xt_snat_target_v0(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -58,9 +58,9 @@ xt_snat_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
struct nf_conn *ct; struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct != NULL && WARN_ON(!(ct != NULL &&
(ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
ctinfo == IP_CT_RELATED_REPLY)); ctinfo == IP_CT_RELATED_REPLY)));
xt_nat_convert_range(&range, &mr->range[0]); xt_nat_convert_range(&range, &mr->range[0]);
return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
...@@ -75,8 +75,8 @@ xt_dnat_target_v0(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -75,8 +75,8 @@ xt_dnat_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
struct nf_conn *ct; struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct != NULL && WARN_ON(!(ct != NULL &&
(ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)));
xt_nat_convert_range(&range, &mr->range[0]); xt_nat_convert_range(&range, &mr->range[0]);
return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
...@@ -90,9 +90,9 @@ xt_snat_target_v1(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -90,9 +90,9 @@ xt_snat_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
struct nf_conn *ct; struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct != NULL && WARN_ON(!(ct != NULL &&
(ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
ctinfo == IP_CT_RELATED_REPLY)); ctinfo == IP_CT_RELATED_REPLY)));
return nf_nat_setup_info(ct, range, NF_NAT_MANIP_SRC); return nf_nat_setup_info(ct, range, NF_NAT_MANIP_SRC);
} }
...@@ -105,8 +105,8 @@ xt_dnat_target_v1(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -105,8 +105,8 @@ xt_dnat_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
struct nf_conn *ct; struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct != NULL && WARN_ON(!(ct != NULL &&
(ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)));
return nf_nat_setup_info(ct, range, NF_NAT_MANIP_DST); return nf_nat_setup_info(ct, range, NF_NAT_MANIP_DST);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment