[PATCH] NETFILTER: add support for invert condition (!) in ipt_limit

Marco Innocenti m.innocenti at cineca.it
Thu Oct 20 11:01:00 CEST 2005


On Mon, Oct 17, 2005 at 05:15:07PM +0200, Patrick McHardy wrote:
> This breaks userspace compatibility, you need to use a new revision
> if you want to change this structure. Look at ipt_MARK for an example.


Signed-off-by: Marco Innocenti <m.innocenti at cineca.it>
---
diff -urpN linux-source-2.6.13/include/linux/netfilter_ipv4/ipt_limit.h linux-source-2.6.13.lavoro/include/linux/netfilter_ipv4/ipt_limit.h
--- linux-source-2.6.13/include/linux/netfilter_ipv4/ipt_limit.h	2005-08-29 01:41:01.000000000 +0200
+++ linux-source-2.6.13.lavoro/include/linux/netfilter_ipv4/ipt_limit.h	2005-10-19 16:22:57.000000000 +0200
@@ -3,9 +3,10 @@
 
 /* timings are in milliseconds. */
 #define IPT_LIMIT_SCALE 10000
-
 /* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
    seconds, or one every 59 hours. */
+
+/* Version 0 */
 struct ipt_rateinfo {
 	u_int32_t avg;    /* Average secs between packets * scale */
 	u_int32_t burst;  /* Period multiplier for upper limit. */
@@ -18,4 +19,19 @@ struct ipt_rateinfo {
 	/* Ugly, ugly fucker. */
 	struct ipt_rateinfo *master;
 };
+
+/* Version 1 */
+struct ipt_rateinfo_v1 {
+	u_int32_t avg;    /* Average secs between packets * scale */
+	u_int32_t burst;  /* Period multiplier for upper limit. */
+	u_int8_t invert;
+
+	/* Used internally by the kernel */
+	unsigned long prev;
+	u_int32_t credit;
+	u_int32_t credit_cap, cost;
+
+	/* Ugly, ugly fucker. */
+	struct ipt_rateinfo_v1 *master;
+};
 #endif /*_IPT_RATE_H*/
diff -urpN linux-source-2.6.13/net/ipv4/netfilter/ipt_limit.c linux-source-2.6.13.lavoro/net/ipv4/netfilter/ipt_limit.c
--- linux-source-2.6.13/net/ipv4/netfilter/ipt_limit.c	2005-08-29 01:41:01.000000000 +0200
+++ linux-source-2.6.13.lavoro/net/ipv4/netfilter/ipt_limit.c	2005-10-19 16:12:34.000000000 +0200
@@ -63,7 +63,7 @@ static DEFINE_SPINLOCK(limit_lock);
 #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)
 
 static int
-ipt_limit_match(const struct sk_buff *skb,
+ipt_limit_match_v0(const struct sk_buff *skb,
 		const struct net_device *in,
 		const struct net_device *out,
 		const void *matchinfo,
@@ -89,6 +89,34 @@ ipt_limit_match(const struct sk_buff *sk
 	return 0;
 }
 
+
+static int
+ipt_limit_match_v1(const struct sk_buff *skb,
+		const struct net_device *in,
+		const struct net_device *out,
+		const void *matchinfo,
+		int offset,
+		int *hotdrop)
+{
+	struct ipt_rateinfo_v1 *r = ((struct ipt_rateinfo_v1 *)matchinfo)->master;
+	unsigned long now = jiffies;
+
+	spin_lock_bh(&limit_lock);
+	r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY;
+	if (r->credit > r->credit_cap)
+		r->credit = r->credit_cap;
+
+	if (r->credit >= r->cost) {
+		/* We're under the limit. */
+		r->credit -= r->cost;
+		spin_unlock_bh(&limit_lock);
+		return !r->invert;
+	}
+
+       	spin_unlock_bh(&limit_lock);
+	return r->invert;
+}
+
 /* Precision saver. */
 static u_int32_t
 user2credits(u_int32_t user)
@@ -102,7 +130,7 @@ user2credits(u_int32_t user)
 }
 
 static int
-ipt_limit_checkentry(const char *tablename,
+ipt_limit_checkentry_v0(const char *tablename,
 		     const struct ipt_ip *ip,
 		     void *matchinfo,
 		     unsigned int matchsize,
@@ -134,23 +162,74 @@ ipt_limit_checkentry(const char *tablena
 	return 1;
 }
 
-static struct ipt_match ipt_limit_reg = {
+static int
+ipt_limit_checkentry_v1(const char *tablename,
+		     const struct ipt_ip *ip,
+		     void *matchinfo,
+		     unsigned int matchsize,
+		     unsigned int hook_mask)
+{
+	struct ipt_rateinfo_v1 *r = matchinfo;
+
+	if (matchsize != IPT_ALIGN(sizeof(struct ipt_rateinfo_v1)))
+		return 0;
+
+	/* Check for overflow. */
+	if (r->burst == 0
+	    || user2credits(r->avg * r->burst) < user2credits(r->avg)) {
+		printk("Overflow in ipt_limit, try lower: %u/%u\n",
+		       r->avg, r->burst);
+		return 0;
+	}
+
+	/* User avg in seconds * IPT_LIMIT_SCALE: convert to jiffies *
+	   128. */
+	r->prev = jiffies;
+	r->credit = user2credits(r->avg * r->burst);	 /* Credits full. */
+	r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */
+	r->cost = user2credits(r->avg);
+
+	/* For SMP, we only want to use one set of counters. */
+	r->master = r;
+
+	return 1;
+}
+
+
+static struct ipt_match ipt_limit_reg_v0 = {
 	.name		= "limit",
-	.match		= ipt_limit_match,
-	.checkentry	= ipt_limit_checkentry,
+	.match		= ipt_limit_match_v0,
+	.checkentry	= ipt_limit_checkentry_v0,
 	.me		= THIS_MODULE,
+	.revision	= 0,
 };
 
+static struct ipt_match ipt_limit_reg_v1 = {
+	.name		= "limit",
+	.match		= ipt_limit_match_v1,
+	.checkentry	= ipt_limit_checkentry_v1,
+	.me		= THIS_MODULE,
+	.revision	= 1,
+};
+
+
 static int __init init(void)
 {
-	if (ipt_register_match(&ipt_limit_reg))
-		return -EINVAL;
-	return 0;
+	int err;
+
+	err = ipt_register_match(&ipt_limit_reg_v0);
+	if (!err) {
+		err = ipt_register_match(&ipt_limit_reg_v1);
+		if (err)
+			ipt_unregister_match(&ipt_limit_reg_v0);
+	}
+	return err;
 }
 
 static void __exit fini(void)
 {
-	ipt_unregister_match(&ipt_limit_reg);
+	ipt_unregister_match(&ipt_limit_reg_v0);
+	ipt_unregister_match(&ipt_limit_reg_v1);
 }
 
 module_init(init);
diff -upr iptables-1.3.3.orig/extensions/libipt_limit.c iptables-1.3.3/extensions/libipt_limit.c
--- iptables-1.3.3.orig/extensions/libipt_limit.c	2005-02-19 20:19:17.000000000 +0100
+++ iptables-1.3.3/extensions/libipt_limit.c	2005-10-19 21:16:46.000000000 +0200
@@ -19,7 +19,7 @@
 
 /* Function which prints out usage message. */
 static void
-help(void)
+help_v0(void)
 {
 	printf(
 "limit v%s options:\n"
@@ -30,6 +30,19 @@ help(void)
 "\n", IPTABLES_VERSION, IPT_LIMIT_BURST);
 }
 
+static void
+help_v1(void)
+{
+	printf(
+"limit v%s options:\n"
+"[!] --limit avg			max average match rate: default "IPT_LIMIT_AVG"\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--limit-burst number		number to match in a burst, default %u\n"
+"\n", IPTABLES_VERSION, IPT_LIMIT_BURST);
+}
+
+
 static struct option opts[] = {
 	{ "limit", 1, 0, '%' },
 	{ "limit-burst", 1, 0, '$' },
@@ -74,7 +87,7 @@ int parse_rate(const char *rate, u_int32
 
 /* Initialize the match. */
 static void
-init(struct ipt_entry_match *m, unsigned int *nfcache)
+init_v0(struct ipt_entry_match *m, unsigned int *nfcache)
 {
 	struct ipt_rateinfo *r = (struct ipt_rateinfo *)m->data;
 
@@ -83,6 +96,18 @@ init(struct ipt_entry_match *m, unsigned
 
 }
 
+/* Initialize the match. */
+static void
+init_v1(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+	struct ipt_rateinfo_v1 *r = (struct ipt_rateinfo_v1 *)m->data;
+
+	parse_rate(IPT_LIMIT_AVG, &r->avg);
+	r->burst = IPT_LIMIT_BURST;
+
+}
+
+
 /* FIXME: handle overflow:
 	if (r->avg*r->burst/r->burst != r->avg)
 		exit_error(PARAMETER_PROBLEM,
@@ -92,7 +117,7 @@ init(struct ipt_entry_match *m, unsigned
 /* Function which parses command options; returns true if it
    ate an option */
 static int
-parse(int c, char **argv, int invert, unsigned int *flags,
+parse_v0(int c, char **argv, int invert, unsigned int *flags,
       const struct ipt_entry *entry,
       unsigned int *nfcache,
       struct ipt_entry_match **match)
@@ -122,11 +147,49 @@ parse(int c, char **argv, int invert, un
 
 	if (invert)
 		exit_error(PARAMETER_PROBLEM,
-			   "limit does not support invert");
+			   "update the kernel to use '!' with limit");
 
 	return 1;
 }
 
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse_v1(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+	struct ipt_rateinfo_v1 *r = (struct ipt_rateinfo_v1 *)(*match)->data;
+	unsigned int num;
+
+	switch(c) {
+	case '%':
+		if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+		if (!parse_rate(optarg, &r->avg))
+			exit_error(PARAMETER_PROBLEM,
+				   "bad rate `%s'", optarg);
+		break;
+
+	case '$':
+		if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
+		if (string_to_number(optarg, 0, 10000, &num) == -1)
+			exit_error(PARAMETER_PROBLEM,
+				   "bad --limit-burst `%s'", optarg);
+		r->burst = num;
+		break;
+
+	default:
+		return 0;
+	}
+
+	if (invert)
+		 r->invert = 1;
+
+	return 1;
+}
+
+
 /* Final check; nothing. */
 static void final_check(unsigned int flags)
 {
@@ -156,7 +219,7 @@ static void print_rate(u_int32_t period)
 
 /* Prints out the matchinfo. */
 static void
-print(const struct ipt_ip *ip,
+print_v0(const struct ipt_ip *ip,
       const struct ipt_entry_match *match,
       int numeric)
 {
@@ -165,8 +228,22 @@ print(const struct ipt_ip *ip,
 	printf("burst %u ", r->burst);
 }
 
+/* Prints out the matchinfo. */
+static void
+print_v1(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+	struct ipt_rateinfo_v1 *r = (struct ipt_rateinfo_v1 *)match->data;
+        if (r->invert)
+		printf("! ");
+	printf("limit: avg "); print_rate(r->avg);
+	printf("burst %u ", r->burst);
+}
+
+
 /* FIXME: Make minimalist: only print rate if not default --RR */
-static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+static void save_v0(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 {
 	struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
 
@@ -175,22 +252,52 @@ static void save(const struct ipt_ip *ip
 		printf("--limit-burst %u ", r->burst);
 }
 
-static struct iptables_match limit = { 
+/* FIXME: Make minimalist: only print rate if not default --RR */
+static void save_v1(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+	struct ipt_rateinfo_v1 *r = (struct ipt_rateinfo_v1 *)match->data;
+
+	printf("--limit "); print_rate(r->avg);
+	if (r->burst != IPT_LIMIT_BURST)
+		printf("--limit-burst %u ", r->burst);
+}
+
+
+static struct iptables_match limit_v0 = { 
 	.next		= NULL,
 	.name		= "limit",
 	.version	= IPTABLES_VERSION,
+	.revision	= 0,
 	.size		= IPT_ALIGN(sizeof(struct ipt_rateinfo)),
 	.userspacesize	= offsetof(struct ipt_rateinfo, prev),
-	.help		= &help,
-	.init		= &init,
-	.parse		= &parse,
+	.help		= &help_v0,
+	.init		= &init_v0,
+	.parse		= &parse_v0,
+	.final_check	= &final_check,
+	.print		= &print_v0,
+	.save		= &save_v0,
+	.extra_opts	= opts
+};
+
+static struct iptables_match limit_v1 = { 
+	.next		= NULL,
+	.name		= "limit",
+	.version	= IPTABLES_VERSION,
+	.revision	= 1,
+	.size		= IPT_ALIGN(sizeof(struct ipt_rateinfo_v1)),
+	.userspacesize	= offsetof(struct ipt_rateinfo_v1, prev),
+	.help		= &help_v1,
+	.init		= &init_v1,
+	.parse		= &parse_v1,
 	.final_check	= &final_check,
-	.print		= &print,
-	.save		= &save,
+	.print		= &print_v1,
+	.save		= &save_v1,
 	.extra_opts	= opts
 };
 
+
 void _init(void)
 {
-	register_match(&limit);
+	register_match(&limit_v0);
+	register_match(&limit_v1);
 }
diff -upr iptables-1.3.3.orig/include/linux/netfilter_ipv4/ipt_limit.h iptables-1.3.3/include/linux/netfilter_ipv4/ipt_limit.h
--- iptables-1.3.3.orig/include/linux/netfilter_ipv4/ipt_limit.h	2004-10-10 11:56:23.000000000 +0200
+++ iptables-1.3.3/include/linux/netfilter_ipv4/ipt_limit.h	2005-10-19 20:21:47.000000000 +0200
@@ -3,9 +3,10 @@
 
 /* timings are in milliseconds. */
 #define IPT_LIMIT_SCALE 10000
-
 /* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
    seconds, or one every 59 hours. */
+
+/* Version 0 */
 struct ipt_rateinfo {
 	u_int32_t avg;    /* Average secs between packets * scale */
 	u_int32_t burst;  /* Period multiplier for upper limit. */
@@ -23,4 +24,25 @@ struct ipt_rateinfo {
 	u_int32_t credit;
 	u_int32_t credit_cap, cost;
 };
+
+/* Version 1 */
+struct ipt_rateinfo_v1 {
+	u_int32_t avg;    /* Average secs between packets * scale */
+	u_int32_t burst;  /* Period multiplier for upper limit. */
+        u_int8_t invert;
+
+#ifdef KERNEL_64_USERSPACE_32
+	u_int64_t prev;
+	u_int64_t placeholder;
+#else
+	/* Used internally by the kernel */
+	unsigned long prev;
+	/* Ugly, ugly fucker. */
+	struct ipt_rateinfo_v1 *master;
+#endif
+
+	u_int32_t credit;
+	u_int32_t credit_cap, cost;
+};
+
 #endif /*_IPT_RATE_H*/


 

-- 
Ciao
     Marco Innocenti



More information about the netfilter-devel mailing list