Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/803421/?format=api
{ "id": 803421, "url": "http://patchwork.ozlabs.org/api/1.2/patches/803421/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netfilter-devel/patch/1503089939-15938-1-git-send-email-vpai@akamai.com/", "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": "<1503089939-15938-1-git-send-email-vpai@akamai.com>", "list_archive_url": null, "date": "2017-08-18T20:58:59", "name": "[1/2] netfilter/xt_hashlimit: new feature/algorithm for xt_hashlimit", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "2b0f0c6bdbd085d66af9c11ba12e2eaadaa38b09", "submitter": { "id": 66296, "url": "http://patchwork.ozlabs.org/api/1.2/people/66296/?format=api", "name": "Vishwanath Pai", "email": "vpai@akamai.com" }, "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/1503089939-15938-1-git-send-email-vpai@akamai.com/mbox/", "series": [], "comments": "http://patchwork.ozlabs.org/api/patches/803421/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/803421/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>)", "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tsecure) header.d=akamai.com header.i=@akamai.com header.b=\"CdfZhWq+\";\n\tdkim-atps=neutral" ], "Received": [ "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xYwSP74MBz9t2y\n\tfor <incoming@patchwork.ozlabs.org>;\n\tSat, 19 Aug 2017 06:59:33 +1000 (AEST)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751572AbdHRU7W (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tFri, 18 Aug 2017 16:59:22 -0400", "from mx0a-00190b01.pphosted.com ([67.231.149.131]:50406 \"EHLO\n\tmx0a-00190b01.pphosted.com\" rhost-flags-OK-OK-OK-OK)\n\tby vger.kernel.org with ESMTP id S1751126AbdHRU7V (ORCPT\n\t<rfc822;netfilter-devel@vger.kernel.org>);\n\tFri, 18 Aug 2017 16:59:21 -0400", "from pps.filterd (m0050095.ppops.net [127.0.0.1])\n\tby m0050095.ppops.net-00190b01. (8.16.0.21/8.16.0.21) with SMTP id\n\tv7IKvHAp031857; Fri, 18 Aug 2017 21:59:01 +0100", "from prod-mail-ppoint1 (prod-mail-ppoint1.akamai.com\n\t[184.51.33.18])\n\tby m0050095.ppops.net-00190b01. with ESMTP id 2cc6dwbmgr-1\n\t(version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256\n\tverify=NOT); Fri, 18 Aug 2017 21:59:01 +0100", "from pps.filterd (prod-mail-ppoint1.akamai.com [127.0.0.1])\n\tby prod-mail-ppoint1.akamai.com (8.16.0.17/8.16.0.17) with SMTP id\n\tv7IKtvhP010023; Fri, 18 Aug 2017 16:59:00 -0400", "from prod-mail-relay11.akamai.com ([172.27.118.250])\n\tby prod-mail-ppoint1.akamai.com with ESMTP id 2cc6cvn9u6-1;\n\tFri, 18 Aug 2017 16:59:00 -0400", "from bos-lpqrs.kendall.corp.akamai.com\n\t(bos-lpqrs.kendall.corp.akamai.com [172.28.13.81])\n\tby prod-mail-relay11.akamai.com (Postfix) with ESMTP id F06561FC7B;\n\tFri, 18 Aug 2017 20:58:59 +0000 (GMT)", "from vpai by bos-lpqrs.kendall.corp.akamai.com with local (Exim\n\t4.82) (envelope-from <vpai@akamai.com>)\n\tid 1dioM7-00049d-TQ; Fri, 18 Aug 2017 16:58:59 -0400" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=akamai.com;\n\th=from : to : cc :\n\tsubject : date : message-id; s=jan2016.eng;\n\tbh=UCKM+DNIrGOoXSxpbPc0j6pYzvfQ3wW053/6FvD0Y5M=;\n\tb=CdfZhWq+VYMRoZAUeKRyenIgcQAic4vVDfHBGZm8IU9MRGBOmoBgWrWB9HGGJckRYMbF\n\tCMfq5hlqahXq//73errSTsNofk9jNh3aJq7G7Atp5oEm5TzCu749ITax6ZkwyVWmCdks\n\tKLDWkpJHeX3iOnGKGTAyu41XUjx/vTXCN6wJBdw5NJghf88waI8uNm5bSMj8vYbU3MFX\n\tEJZWYBy9DzKhynEjqF+cqBNGxP46OSU6PPrUiIy67O4nkYR/sFTPH0KtUXxjtVbBbH0y\n\tVEd0tmcDoYfYRw3IKq5/5raQ9KGEHZ9arFiVWC3KYZTkZJGmRZ3xLcA0CdERrfyv4uq0\n\tcA== ", "From": "Vishwanath Pai <vpai@akamai.com>", "To": "pablo@netfilter.org, kadlec@blackhole.kfki.hu,\n\tnetfilter-devel@vger.kernel.org", "Cc": "johunt@akamai.com, fw@strlen.de, netdev@vger.kernel.org,\n\tpai.vishwain@gmail.com", "Subject": "[PATCH 1/2] netfilter/xt_hashlimit: new feature/algorithm for\n\txt_hashlimit", "Date": "Fri, 18 Aug 2017 16:58:59 -0400", "Message-Id": "<1503089939-15938-1-git-send-email-vpai@akamai.com>", "X-Mailer": "git-send-email 1.9.1", "X-Proofpoint-Virus-Version": [ "vendor=fsecure engine=2.50.10432:, ,\n\tdefinitions=2017-08-18_11:, , signatures=0", "vendor=fsecure engine=2.50.10432:, ,\n\tdefinitions=2017-08-18_11:, , signatures=0" ], "X-Proofpoint-Spam-Details": [ "rule=notspam policy=default score=0 spamscore=0\n\tsuspectscore=2\n\tmalwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam\n\tadjust=0 reason=mlx scancount=1 engine=8.0.1-1707230000\n\tdefinitions=main-1708180335", "rule=notspam policy=default score=0\n\tpriorityscore=1501 malwarescore=0\n\tsuspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1011\n\tlowpriorityscore=0 impostorscore=0 adultscore=0 classifier=spam\n\tadjust=0\n\treason=mlx scancount=1 engine=8.0.1-1707230000\n\tdefinitions=main-1708180336" ], "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": "This 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>\n---\n include/linux/netfilter/xt_hashlimit.h | 3 +-\n include/uapi/linux/netfilter/xt_hashlimit.h | 36 +++-\n net/netfilter/xt_hashlimit.c | 275 +++++++++++++++++++++++++---\n 3 files changed, 284 insertions(+), 30 deletions(-)", "diff": "diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h\nindex 074790c..0fc458b 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 79da349..aa98573 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 762e187..df57989 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,7 +128,7 @@ 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@@ -131,7 +143,19 @@ struct xt_hashlimit_htable {\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 @@ static void dsthash_free_rcu(struct rcu_head *head)\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 @@ static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh)\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 @@ static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh)\n \t\treturn !(cfg->mode & XT_HASHLIMIT_INVERT);\n \t}\n \n+overlimit:\n \tspin_unlock(&dh->lock);\n \trcu_read_unlock_bh();\n \t/* default match is underlimit - so over the limit, we need to invert */\n@@ -708,7 +808,7 @@ static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh)\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 @@ static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh)\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 void hashlimit_mt_destroy(const struct xt_mtdtor_param *par)\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 void hashlimit_mt_destroy(const struct xt_mtdtor_param *par)\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 int dl_seq_show(struct seq_file *s, void *v)\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 int dl_seq_show(struct seq_file *s, void *v)\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": [ "1/2" ] }