Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/809815/?format=api
{ "id": 809815, "url": "http://patchwork.ozlabs.org/api/1.2/patches/809815/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netfilter-devel/patch/1504555874-4168-2-git-send-email-pablo@netfilter.org/", "project": { "id": 26, "url": "http://patchwork.ozlabs.org/api/1.2/projects/26/?format=api", "name": "Netfilter Development", "link_name": "netfilter-devel", "list_id": "netfilter-devel.vger.kernel.org", "list_email": "netfilter-devel@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<1504555874-4168-2-git-send-email-pablo@netfilter.org>", "list_archive_url": null, "date": "2017-09-04T20:11:03", "name": "[01/12] netfilter: xt_hashlimit: add rate match mode", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "895d1ca89e45dfd60c881597b4c120cdd3c5e679", "submitter": { "id": 1315, "url": "http://patchwork.ozlabs.org/api/1.2/people/1315/?format=api", "name": "Pablo Neira Ayuso", "email": "pablo@netfilter.org" }, "delegate": { "id": 6139, "url": "http://patchwork.ozlabs.org/api/1.2/users/6139/?format=api", "username": "pablo", "first_name": "Pablo", "last_name": "Neira", "email": "pablo@netfilter.org" }, "mbox": "http://patchwork.ozlabs.org/project/netfilter-devel/patch/1504555874-4168-2-git-send-email-pablo@netfilter.org/mbox/", "series": [ { "id": 1452, "url": "http://patchwork.ozlabs.org/api/1.2/series/1452/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netfilter-devel/list/?series=1452", "date": "2017-09-04T20:11:02", "name": "[01/12] netfilter: xt_hashlimit: add rate match mode", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/1452/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/809815/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/809815/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<netfilter-devel-owner@vger.kernel.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org", "Authentication-Results": "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netfilter-devel-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)", "Received": [ "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xmLcT4YH6z9t2R\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 5 Sep 2017 06:12:41 +1000 (AEST)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1754696AbdIDULb (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tMon, 4 Sep 2017 16:11:31 -0400", "from mail.us.es ([193.147.175.20]:42126 \"EHLO mail.us.es\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1754624AbdIDUL0 (ORCPT <rfc822; netfilter-devel@vger.kernel.org>);\n\tMon, 4 Sep 2017 16:11:26 -0400", "from antivirus1-rhel7.int (unknown [192.168.2.11])\n\tby mail.us.es (Postfix) with ESMTP id 63687532F8A\n\tfor <netfilter-devel@vger.kernel.org>;\n\tMon, 4 Sep 2017 22:10:58 +0200 (CEST)", "from antivirus1-rhel7.int (localhost [127.0.0.1])\n\tby antivirus1-rhel7.int (Postfix) with ESMTP id 42C88B5059\n\tfor <netfilter-devel@vger.kernel.org>;\n\tMon, 4 Sep 2017 22:10:58 +0200 (CEST)", "by antivirus1-rhel7.int (Postfix, from userid 99)\n\tid 37FA6B5026; Mon, 4 Sep 2017 22:10:58 +0200 (CEST)", "from antivirus1-rhel7.int (localhost [127.0.0.1])\n\tby antivirus1-rhel7.int (Postfix) with ESMTP id 1CA63B5026;\n\tMon, 4 Sep 2017 22:10:55 +0200 (CEST)", "from 192.168.1.97 (192.168.1.97) by antivirus1-rhel7.int\n\t(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int); \n\tMon, 04 Sep 2017 22:10:55 +0200 (CEST)", "from salvia.here (129.166.216.87.static.jazztel.es\n\t[87.216.166.129]) (Authenticated sender: pneira@us.es)\n\tby entrada.int (Postfix) with ESMTPA id 9AD6F4265A22;\n\tMon, 4 Sep 2017 22:10:54 +0200 (CEST)" ], "X-Spam-Checker-Version": "SpamAssassin 3.4.1 (2015-04-28) on\n\tantivirus1-rhel7.int", "X-Spam-Level": "", "X-Spam-Status": "No, score=-108.2 required=7.5 tests=ALL_TRUSTED,BAYES_50,\n\tSMTPAUTH_US2,USER_IN_WHITELIST autolearn=disabled version=3.4.1", "X-Virus-Status": "clean(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int)", "X-SMTPAUTHUS": "auth mail.us.es", "From": "Pablo Neira Ayuso <pablo@netfilter.org>", "To": "netfilter-devel@vger.kernel.org", "Cc": "davem@davemloft.net, netdev@vger.kernel.org", "Subject": "[PATCH 01/12] netfilter: xt_hashlimit: add rate match mode", "Date": "Mon, 4 Sep 2017 22:11:03 +0200", "Message-Id": "<1504555874-4168-2-git-send-email-pablo@netfilter.org>", "X-Mailer": "git-send-email 2.1.4", "In-Reply-To": "<1504555874-4168-1-git-send-email-pablo@netfilter.org>", "References": "<1504555874-4168-1-git-send-email-pablo@netfilter.org>", "X-Virus-Scanned": "ClamAV using ClamSMTP", "Sender": "netfilter-devel-owner@vger.kernel.org", "Precedence": "bulk", "List-ID": "<netfilter-devel.vger.kernel.org>", "X-Mailing-List": "netfilter-devel@vger.kernel.org" }, "content": "From: Vishwanath Pai <vpai@akamai.com>\n\nThis patch adds a new feature to hashlimit that allows matching on the\ncurrent packet/byte rate without rate limiting. This can be enabled\nwith a new flag --hashlimit-rate-match. The match returns true if the\ncurrent rate of packets is above/below the user specified value.\n\nThe main difference between the existing algorithm and the new one is\nthat the existing algorithm rate-limits the flow whereas the new\nalgorithm does not. Instead it *classifies* the flow based on whether\nit is above or below a certain rate. I will demonstrate this with an\nexample below. Let us assume this rule:\n\niptables -A INPUT -m hashlimit --hashlimit-above 10/s -j new_chain\n\nIf the packet rate is 15/s, the existing algorithm would ACCEPT 10\npackets every second and send 5 packets to \"new_chain\".\n\nBut with the new algorithm, as long as the rate of 15/s is sustained,\nall packets will continue to match and every packet is sent to new_chain.\n\nThis new functionality will let us classify different flows based on\ntheir current rate, so that further decisions can be made on them based on\nwhat the current rate is.\n\nThis is how the new algorithm works:\nWe divide time into intervals of 1 (sec/min/hour) as specified by\nthe user. We keep track of the number of packets/bytes processed in the\ncurrent interval. After each interval we reset the counter to 0.\n\nWhen we receive a packet for match, we look at the packet rate\nduring the current interval and the previous interval to make a\ndecision:\n\nif [ prev_rate < user and cur_rate < user ]\n return Below\nelse\n return Above\n\nWhere cur_rate is the number of packets/bytes seen in the current\ninterval, prev is the number of packets/bytes seen in the previous\ninterval and 'user' is the rate specified by the user.\n\nWe also provide flexibility to the user for choosing the time\ninterval using the option --hashilmit-interval. For example the user can\nkeep a low rate like x/hour but still keep the interval as small as 1\nsecond.\n\nTo preserve backwards compatibility we have to add this feature in a new\nrevision, so I've created revision 3 for hashlimit. The two new options\nwe add are:\n\n--hashlimit-rate-match\n--hashlimit-rate-interval\n\nI have updated the help text to add these new options. Also added a few\ntests for the new options.\n\nSuggested-by: Igor Lubashev <ilubashe@akamai.com>\nReviewed-by: Josh Hunt <johunt@akamai.com>\nSigned-off-by: Vishwanath Pai <vpai@akamai.com>\nSigned-off-by: Pablo Neira Ayuso <pablo@netfilter.org>\n---\n include/linux/netfilter/xt_hashlimit.h | 3 +-\n include/uapi/linux/netfilter/xt_hashlimit.h | 36 +++-\n net/netfilter/xt_hashlimit.c | 277 +++++++++++++++++++++++++---\n 3 files changed, 285 insertions(+), 31 deletions(-)", "diff": "diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h\nindex 074790c0cf74..0fc458bde80b 100644\n--- a/include/linux/netfilter/xt_hashlimit.h\n+++ b/include/linux/netfilter/xt_hashlimit.h\n@@ -5,5 +5,6 @@\n \n #define XT_HASHLIMIT_ALL (XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT | \\\n \t\t\t XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | \\\n-\t\t\t XT_HASHLIMIT_INVERT | XT_HASHLIMIT_BYTES)\n+\t\t\t XT_HASHLIMIT_INVERT | XT_HASHLIMIT_BYTES |\\\n+\t\t\t XT_HASHLIMIT_RATE_MATCH)\n #endif /*_XT_HASHLIMIT_H*/\ndiff --git a/include/uapi/linux/netfilter/xt_hashlimit.h b/include/uapi/linux/netfilter/xt_hashlimit.h\nindex 79da349f1060..aa98573248b1 100644\n--- a/include/uapi/linux/netfilter/xt_hashlimit.h\n+++ b/include/uapi/linux/netfilter/xt_hashlimit.h\n@@ -19,12 +19,13 @@\n struct xt_hashlimit_htable;\n \n enum {\n-\tXT_HASHLIMIT_HASH_DIP = 1 << 0,\n-\tXT_HASHLIMIT_HASH_DPT = 1 << 1,\n-\tXT_HASHLIMIT_HASH_SIP = 1 << 2,\n-\tXT_HASHLIMIT_HASH_SPT = 1 << 3,\n-\tXT_HASHLIMIT_INVERT = 1 << 4,\n-\tXT_HASHLIMIT_BYTES = 1 << 5,\n+\tXT_HASHLIMIT_HASH_DIP\t\t= 1 << 0,\n+\tXT_HASHLIMIT_HASH_DPT\t\t= 1 << 1,\n+\tXT_HASHLIMIT_HASH_SIP\t\t= 1 << 2,\n+\tXT_HASHLIMIT_HASH_SPT\t\t= 1 << 3,\n+\tXT_HASHLIMIT_INVERT\t\t= 1 << 4,\n+\tXT_HASHLIMIT_BYTES\t\t= 1 << 5,\n+\tXT_HASHLIMIT_RATE_MATCH\t\t= 1 << 6,\n };\n \n struct hashlimit_cfg {\n@@ -79,6 +80,21 @@ struct hashlimit_cfg2 {\n \t__u8 srcmask, dstmask;\n };\n \n+struct hashlimit_cfg3 {\n+\t__u64 avg;\t\t/* Average secs between packets * scale */\n+\t__u64 burst;\t\t/* Period multiplier for upper limit. */\n+\t__u32 mode;\t\t/* bitmask of XT_HASHLIMIT_HASH_* */\n+\n+\t/* user specified */\n+\t__u32 size;\t\t/* how many buckets */\n+\t__u32 max;\t\t/* max number of entries */\n+\t__u32 gc_interval;\t/* gc interval */\n+\t__u32 expire;\t\t/* when do entries expire? */\n+\n+\t__u32 interval;\n+\t__u8 srcmask, dstmask;\n+};\n+\n struct xt_hashlimit_mtinfo1 {\n \tchar name[IFNAMSIZ];\n \tstruct hashlimit_cfg1 cfg;\n@@ -95,4 +111,12 @@ struct xt_hashlimit_mtinfo2 {\n \tstruct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));\n };\n \n+struct xt_hashlimit_mtinfo3 {\n+\tchar name[NAME_MAX];\n+\tstruct hashlimit_cfg3 cfg;\n+\n+\t/* Used internally by the kernel */\n+\tstruct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));\n+};\n+\n #endif /* _UAPI_XT_HASHLIMIT_H */\ndiff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c\nindex ffdb611e54a2..10d48234f5f4 100644\n--- a/net/netfilter/xt_hashlimit.c\n+++ b/net/netfilter/xt_hashlimit.c\n@@ -56,6 +56,7 @@ static inline struct hashlimit_net *hashlimit_pernet(struct net *net)\n }\n \n /* need to declare this at the top */\n+static const struct file_operations dl_file_ops_v2;\n static const struct file_operations dl_file_ops_v1;\n static const struct file_operations dl_file_ops;\n \n@@ -87,8 +88,19 @@ struct dsthash_ent {\n \tunsigned long expires;\t\t/* precalculated expiry time */\n \tstruct {\n \t\tunsigned long prev;\t/* last modification */\n-\t\tu_int64_t credit;\n-\t\tu_int64_t credit_cap, cost;\n+\t\tunion {\n+\t\t\tstruct {\n+\t\t\t\tu_int64_t credit;\n+\t\t\t\tu_int64_t credit_cap;\n+\t\t\t\tu_int64_t cost;\n+\t\t\t};\n+\t\t\tstruct {\n+\t\t\t\tu_int32_t interval, prev_window;\n+\t\t\t\tu_int64_t current_rate;\n+\t\t\t\tu_int64_t rate;\n+\t\t\t\tint64_t burst;\n+\t\t\t};\n+\t\t};\n \t} rateinfo;\n \tstruct rcu_head rcu;\n };\n@@ -99,7 +111,7 @@ struct xt_hashlimit_htable {\n \tu_int8_t family;\n \tbool rnd_initialized;\n \n-\tstruct hashlimit_cfg2 cfg;\t/* config */\n+\tstruct hashlimit_cfg3 cfg;\t/* config */\n \n \t/* used internally */\n \tspinlock_t lock;\t\t/* lock for list_head */\n@@ -116,10 +128,10 @@ struct xt_hashlimit_htable {\n };\n \n static int\n-cfg_copy(struct hashlimit_cfg2 *to, void *from, int revision)\n+cfg_copy(struct hashlimit_cfg3 *to, const void *from, int revision)\n {\n \tif (revision == 1) {\n-\t\tstruct hashlimit_cfg1 *cfg = from;\n+\t\tstruct hashlimit_cfg1 *cfg = (struct hashlimit_cfg1 *)from;\n \n \t\tto->mode = cfg->mode;\n \t\tto->avg = cfg->avg;\n@@ -131,7 +143,19 @@ cfg_copy(struct hashlimit_cfg2 *to, void *from, int revision)\n \t\tto->srcmask = cfg->srcmask;\n \t\tto->dstmask = cfg->dstmask;\n \t} else if (revision == 2) {\n-\t\tmemcpy(to, from, sizeof(struct hashlimit_cfg2));\n+\t\tstruct hashlimit_cfg2 *cfg = (struct hashlimit_cfg2 *)from;\n+\n+\t\tto->mode = cfg->mode;\n+\t\tto->avg = cfg->avg;\n+\t\tto->burst = cfg->burst;\n+\t\tto->size = cfg->size;\n+\t\tto->max = cfg->max;\n+\t\tto->gc_interval = cfg->gc_interval;\n+\t\tto->expire = cfg->expire;\n+\t\tto->srcmask = cfg->srcmask;\n+\t\tto->dstmask = cfg->dstmask;\n+\t} else if (revision == 3) {\n+\t\tmemcpy(to, from, sizeof(struct hashlimit_cfg3));\n \t} else {\n \t\treturn -EINVAL;\n \t}\n@@ -240,13 +264,14 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent)\n }\n static void htable_gc(struct work_struct *work);\n \n-static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg,\n+static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,\n \t\t\t const char *name, u_int8_t family,\n \t\t\t struct xt_hashlimit_htable **out_hinfo,\n \t\t\t int revision)\n {\n \tstruct hashlimit_net *hashlimit_net = hashlimit_pernet(net);\n \tstruct xt_hashlimit_htable *hinfo;\n+\tconst struct file_operations *fops;\n \tunsigned int size, i;\n \tint ret;\n \n@@ -268,7 +293,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg,\n \t*out_hinfo = hinfo;\n \n \t/* copy match config into hashtable config */\n-\tret = cfg_copy(&hinfo->cfg, (void *)cfg, 2);\n+\tret = cfg_copy(&hinfo->cfg, (void *)cfg, 3);\n \n \tif (ret)\n \t\treturn ret;\n@@ -293,11 +318,21 @@ static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg,\n \t}\n \tspin_lock_init(&hinfo->lock);\n \n+\tswitch (revision) {\n+\tcase 1:\n+\t\tfops = &dl_file_ops_v1;\n+\t\tbreak;\n+\tcase 2:\n+\t\tfops = &dl_file_ops_v2;\n+\t\tbreak;\n+\tdefault:\n+\t\tfops = &dl_file_ops;\n+\t}\n+\n \thinfo->pde = proc_create_data(name, 0,\n \t\t(family == NFPROTO_IPV4) ?\n \t\thashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit,\n-\t\t(revision == 1) ? &dl_file_ops_v1 : &dl_file_ops,\n-\t\thinfo);\n+\t\tfops, hinfo);\n \tif (hinfo->pde == NULL) {\n \t\tkfree(hinfo->name);\n \t\tvfree(hinfo);\n@@ -482,6 +517,25 @@ static u32 user2credits_byte(u32 user)\n \treturn (u32) (us >> 32);\n }\n \n+static u64 user2rate(u64 user)\n+{\n+\tif (user != 0) {\n+\t\treturn div64_u64(XT_HASHLIMIT_SCALE_v2, user);\n+\t} else {\n+\t\tpr_warn(\"invalid rate from userspace: %llu\\n\", user);\n+\t\treturn 0;\n+\t}\n+}\n+\n+static u64 user2rate_bytes(u64 user)\n+{\n+\tu64 r;\n+\n+\tr = user ? 0xFFFFFFFFULL / user : 0xFFFFFFFFULL;\n+\tr = (r - 1) << 4;\n+\treturn r;\n+}\n+\n static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now,\n \t\t\t u32 mode, int revision)\n {\n@@ -491,6 +545,21 @@ static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now,\n \tif (delta == 0)\n \t\treturn;\n \n+\tif (revision >= 3 && mode & XT_HASHLIMIT_RATE_MATCH) {\n+\t\tu64 interval = dh->rateinfo.interval * HZ;\n+\n+\t\tif (delta < interval)\n+\t\t\treturn;\n+\n+\t\tdh->rateinfo.prev = now;\n+\t\tdh->rateinfo.prev_window =\n+\t\t\t((dh->rateinfo.current_rate * interval) >\n+\t\t\t (delta * dh->rateinfo.rate));\n+\t\tdh->rateinfo.current_rate = 0;\n+\n+\t\treturn;\n+\t}\n+\n \tdh->rateinfo.prev = now;\n \n \tif (mode & XT_HASHLIMIT_BYTES) {\n@@ -515,7 +584,23 @@ static void rateinfo_init(struct dsthash_ent *dh,\n \t\t\t struct xt_hashlimit_htable *hinfo, int revision)\n {\n \tdh->rateinfo.prev = jiffies;\n-\tif (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) {\n+\tif (revision >= 3 && hinfo->cfg.mode & XT_HASHLIMIT_RATE_MATCH) {\n+\t\tdh->rateinfo.prev_window = 0;\n+\t\tdh->rateinfo.current_rate = 0;\n+\t\tif (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) {\n+\t\t\tdh->rateinfo.rate = user2rate_bytes(hinfo->cfg.avg);\n+\t\t\tif (hinfo->cfg.burst)\n+\t\t\t\tdh->rateinfo.burst =\n+\t\t\t\t\thinfo->cfg.burst * dh->rateinfo.rate;\n+\t\t\telse\n+\t\t\t\tdh->rateinfo.burst = dh->rateinfo.rate;\n+\t\t} else {\n+\t\t\tdh->rateinfo.rate = user2rate(hinfo->cfg.avg);\n+\t\t\tdh->rateinfo.burst =\n+\t\t\t\thinfo->cfg.burst + dh->rateinfo.rate;\n+\t\t}\n+\t\tdh->rateinfo.interval = hinfo->cfg.interval;\n+\t} else if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) {\n \t\tdh->rateinfo.credit = CREDITS_PER_JIFFY_BYTES * HZ;\n \t\tdh->rateinfo.cost = user2credits_byte(hinfo->cfg.avg);\n \t\tdh->rateinfo.credit_cap = hinfo->cfg.burst;\n@@ -648,7 +733,7 @@ static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh)\n static bool\n hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par,\n \t\t struct xt_hashlimit_htable *hinfo,\n-\t\t const struct hashlimit_cfg2 *cfg, int revision)\n+\t\t const struct hashlimit_cfg3 *cfg, int revision)\n {\n \tunsigned long now = jiffies;\n \tstruct dsthash_ent *dh;\n@@ -680,6 +765,20 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par,\n \t\trateinfo_recalc(dh, now, hinfo->cfg.mode, revision);\n \t}\n \n+\tif (cfg->mode & XT_HASHLIMIT_RATE_MATCH) {\n+\t\tcost = (cfg->mode & XT_HASHLIMIT_BYTES) ? skb->len : 1;\n+\t\tdh->rateinfo.current_rate += cost;\n+\n+\t\tif (!dh->rateinfo.prev_window &&\n+\t\t (dh->rateinfo.current_rate <= dh->rateinfo.burst)) {\n+\t\t\tspin_unlock(&dh->lock);\n+\t\t\trcu_read_unlock_bh();\n+\t\t\treturn !(cfg->mode & XT_HASHLIMIT_INVERT);\n+\t\t} else {\n+\t\t\tgoto overlimit;\n+\t\t}\n+\t}\n+\n \tif (cfg->mode & XT_HASHLIMIT_BYTES)\n \t\tcost = hashlimit_byte_cost(skb->len, dh);\n \telse\n@@ -693,6 +792,7 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par,\n \t\treturn !(cfg->mode & XT_HASHLIMIT_INVERT);\n \t}\n \n+overlimit:\n \tspin_unlock(&dh->lock);\n \tlocal_bh_enable();\n \t/* default match is underlimit - so over the limit, we need to invert */\n@@ -708,7 +808,7 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)\n {\n \tconst struct xt_hashlimit_mtinfo1 *info = par->matchinfo;\n \tstruct xt_hashlimit_htable *hinfo = info->hinfo;\n-\tstruct hashlimit_cfg2 cfg = {};\n+\tstruct hashlimit_cfg3 cfg = {};\n \tint ret;\n \n \tret = cfg_copy(&cfg, (void *)&info->cfg, 1);\n@@ -720,17 +820,33 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)\n }\n \n static bool\n-hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)\n+hashlimit_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)\n {\n \tconst struct xt_hashlimit_mtinfo2 *info = par->matchinfo;\n \tstruct xt_hashlimit_htable *hinfo = info->hinfo;\n+\tstruct hashlimit_cfg3 cfg = {};\n+\tint ret;\n+\n+\tret = cfg_copy(&cfg, (void *)&info->cfg, 2);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn hashlimit_mt_common(skb, par, hinfo, &cfg, 2);\n+}\n+\n+static bool\n+hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)\n+{\n+\tconst struct xt_hashlimit_mtinfo3 *info = par->matchinfo;\n+\tstruct xt_hashlimit_htable *hinfo = info->hinfo;\n \n-\treturn hashlimit_mt_common(skb, par, hinfo, &info->cfg, 2);\n+\treturn hashlimit_mt_common(skb, par, hinfo, &info->cfg, 3);\n }\n \n static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,\n \t\t\t\t struct xt_hashlimit_htable **hinfo,\n-\t\t\t\t struct hashlimit_cfg2 *cfg,\n+\t\t\t\t struct hashlimit_cfg3 *cfg,\n \t\t\t\t const char *name, int revision)\n {\n \tstruct net *net = par->net;\n@@ -753,7 +869,17 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,\n \t}\n \n \t/* Check for overflow. */\n-\tif (cfg->mode & XT_HASHLIMIT_BYTES) {\n+\tif (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) {\n+\t\tif (cfg->avg == 0) {\n+\t\t\tpr_info(\"hashlimit invalid rate\\n\");\n+\t\t\treturn -ERANGE;\n+\t\t}\n+\n+\t\tif (cfg->interval == 0) {\n+\t\t\tpr_info(\"hashlimit invalid interval\\n\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t} else if (cfg->mode & XT_HASHLIMIT_BYTES) {\n \t\tif (user2credits_byte(cfg->avg) == 0) {\n \t\t\tpr_info(\"overflow, rate too high: %llu\\n\", cfg->avg);\n \t\t\treturn -EINVAL;\n@@ -784,7 +910,7 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,\n static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par)\n {\n \tstruct xt_hashlimit_mtinfo1 *info = par->matchinfo;\n-\tstruct hashlimit_cfg2 cfg = {};\n+\tstruct hashlimit_cfg3 cfg = {};\n \tint ret;\n \n \tif (info->name[sizeof(info->name) - 1] != '\\0')\n@@ -799,15 +925,40 @@ static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par)\n \t\t\t\t\t &cfg, info->name, 1);\n }\n \n-static int hashlimit_mt_check(const struct xt_mtchk_param *par)\n+static int hashlimit_mt_check_v2(const struct xt_mtchk_param *par)\n {\n \tstruct xt_hashlimit_mtinfo2 *info = par->matchinfo;\n+\tstruct hashlimit_cfg3 cfg = {};\n+\tint ret;\n+\n+\tif (info->name[sizeof(info->name) - 1] != '\\0')\n+\t\treturn -EINVAL;\n+\n+\tret = cfg_copy(&cfg, (void *)&info->cfg, 2);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn hashlimit_mt_check_common(par, &info->hinfo,\n+\t\t\t\t\t &cfg, info->name, 2);\n+}\n+\n+static int hashlimit_mt_check(const struct xt_mtchk_param *par)\n+{\n+\tstruct xt_hashlimit_mtinfo3 *info = par->matchinfo;\n \n \tif (info->name[sizeof(info->name) - 1] != '\\0')\n \t\treturn -EINVAL;\n \n \treturn hashlimit_mt_check_common(par, &info->hinfo, &info->cfg,\n-\t\t\t\t\t info->name, 2);\n+\t\t\t\t\t info->name, 3);\n+}\n+\n+static void hashlimit_mt_destroy_v2(const struct xt_mtdtor_param *par)\n+{\n+\tconst struct xt_hashlimit_mtinfo2 *info = par->matchinfo;\n+\n+\thtable_put(info->hinfo);\n }\n \n static void hashlimit_mt_destroy_v1(const struct xt_mtdtor_param *par)\n@@ -819,7 +970,7 @@ static void hashlimit_mt_destroy_v1(const struct xt_mtdtor_param *par)\n \n static void hashlimit_mt_destroy(const struct xt_mtdtor_param *par)\n {\n-\tconst struct xt_hashlimit_mtinfo2 *info = par->matchinfo;\n+\tconst struct xt_hashlimit_mtinfo3 *info = par->matchinfo;\n \n \thtable_put(info->hinfo);\n }\n@@ -840,9 +991,20 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = {\n \t\t.name = \"hashlimit\",\n \t\t.revision = 2,\n \t\t.family = NFPROTO_IPV4,\n-\t\t.match = hashlimit_mt,\n+\t\t.match = hashlimit_mt_v2,\n \t\t.matchsize = sizeof(struct xt_hashlimit_mtinfo2),\n \t\t.usersize\t= offsetof(struct xt_hashlimit_mtinfo2, hinfo),\n+\t\t.checkentry = hashlimit_mt_check_v2,\n+\t\t.destroy = hashlimit_mt_destroy_v2,\n+\t\t.me = THIS_MODULE,\n+\t},\n+\t{\n+\t\t.name = \"hashlimit\",\n+\t\t.revision = 3,\n+\t\t.family = NFPROTO_IPV4,\n+\t\t.match = hashlimit_mt,\n+\t\t.matchsize = sizeof(struct xt_hashlimit_mtinfo3),\n+\t\t.usersize\t= offsetof(struct xt_hashlimit_mtinfo3, hinfo),\n \t\t.checkentry = hashlimit_mt_check,\n \t\t.destroy = hashlimit_mt_destroy,\n \t\t.me = THIS_MODULE,\n@@ -863,9 +1025,20 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = {\n \t\t.name = \"hashlimit\",\n \t\t.revision = 2,\n \t\t.family = NFPROTO_IPV6,\n-\t\t.match = hashlimit_mt,\n+\t\t.match = hashlimit_mt_v2,\n \t\t.matchsize = sizeof(struct xt_hashlimit_mtinfo2),\n \t\t.usersize\t= offsetof(struct xt_hashlimit_mtinfo2, hinfo),\n+\t\t.checkentry = hashlimit_mt_check_v2,\n+\t\t.destroy = hashlimit_mt_destroy_v2,\n+\t\t.me = THIS_MODULE,\n+\t},\n+\t{\n+\t\t.name = \"hashlimit\",\n+\t\t.revision = 3,\n+\t\t.family = NFPROTO_IPV6,\n+\t\t.match = hashlimit_mt,\n+\t\t.matchsize = sizeof(struct xt_hashlimit_mtinfo3),\n+\t\t.usersize\t= offsetof(struct xt_hashlimit_mtinfo3, hinfo),\n \t\t.checkentry = hashlimit_mt_check,\n \t\t.destroy = hashlimit_mt_destroy,\n \t\t.me = THIS_MODULE,\n@@ -947,6 +1120,21 @@ static void dl_seq_print(struct dsthash_ent *ent, u_int8_t family,\n \t}\n }\n \n+static int dl_seq_real_show_v2(struct dsthash_ent *ent, u_int8_t family,\n+\t\t\t struct seq_file *s)\n+{\n+\tconst struct xt_hashlimit_htable *ht = s->private;\n+\n+\tspin_lock(&ent->lock);\n+\t/* recalculate to show accurate numbers */\n+\trateinfo_recalc(ent, jiffies, ht->cfg.mode, 2);\n+\n+\tdl_seq_print(ent, family, s);\n+\n+\tspin_unlock(&ent->lock);\n+\treturn seq_has_overflowed(s);\n+}\n+\n static int dl_seq_real_show_v1(struct dsthash_ent *ent, u_int8_t family,\n \t\t\t struct seq_file *s)\n {\n@@ -969,7 +1157,7 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,\n \n \tspin_lock(&ent->lock);\n \t/* recalculate to show accurate numbers */\n-\trateinfo_recalc(ent, jiffies, ht->cfg.mode, 2);\n+\trateinfo_recalc(ent, jiffies, ht->cfg.mode, 3);\n \n \tdl_seq_print(ent, family, s);\n \n@@ -977,6 +1165,20 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,\n \treturn seq_has_overflowed(s);\n }\n \n+static int dl_seq_show_v2(struct seq_file *s, void *v)\n+{\n+\tstruct xt_hashlimit_htable *htable = s->private;\n+\tunsigned int *bucket = (unsigned int *)v;\n+\tstruct dsthash_ent *ent;\n+\n+\tif (!hlist_empty(&htable->hash[*bucket])) {\n+\t\thlist_for_each_entry(ent, &htable->hash[*bucket], node)\n+\t\t\tif (dl_seq_real_show_v2(ent, htable->family, s))\n+\t\t\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n static int dl_seq_show_v1(struct seq_file *s, void *v)\n {\n \tstruct xt_hashlimit_htable *htable = s->private;\n@@ -1012,6 +1214,13 @@ static const struct seq_operations dl_seq_ops_v1 = {\n \t.show = dl_seq_show_v1\n };\n \n+static const struct seq_operations dl_seq_ops_v2 = {\n+\t.start = dl_seq_start,\n+\t.next = dl_seq_next,\n+\t.stop = dl_seq_stop,\n+\t.show = dl_seq_show_v2\n+};\n+\n static const struct seq_operations dl_seq_ops = {\n \t.start = dl_seq_start,\n \t.next = dl_seq_next,\n@@ -1019,6 +1228,18 @@ static const struct seq_operations dl_seq_ops = {\n \t.show = dl_seq_show\n };\n \n+static int dl_proc_open_v2(struct inode *inode, struct file *file)\n+{\n+\tint ret = seq_open(file, &dl_seq_ops_v2);\n+\n+\tif (!ret) {\n+\t\tstruct seq_file *sf = file->private_data;\n+\n+\t\tsf->private = PDE_DATA(inode);\n+\t}\n+\treturn ret;\n+}\n+\n static int dl_proc_open_v1(struct inode *inode, struct file *file)\n {\n \tint ret = seq_open(file, &dl_seq_ops_v1);\n@@ -1042,6 +1263,14 @@ static int dl_proc_open(struct inode *inode, struct file *file)\n \treturn ret;\n }\n \n+static const struct file_operations dl_file_ops_v2 = {\n+\t.owner = THIS_MODULE,\n+\t.open = dl_proc_open_v2,\n+\t.read = seq_read,\n+\t.llseek = seq_lseek,\n+\t.release = seq_release\n+};\n+\n static const struct file_operations dl_file_ops_v1 = {\n \t.owner = THIS_MODULE,\n \t.open = dl_proc_open_v1,\n", "prefixes": [ "01/12" ] }