[PATCH] Extension of nth match

Eike Frost Eike.Frost@gmx.de
Fri, 5 Oct 2001 20:01:48 +0200


--------------Boundary-00=_0FVQTJ8VRLUG2LGEVJH0
Content-Type: text/plain;
  charset="iso-8859-1"
Content-Transfer-Encoding: 8bit

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hello,

Please bear with me if I'm doing something utterly wrong, this is the first 
patch I submit to any kind of kernel source ...

It takes the nth match from p-o-m and localizes the counter of packets to 
every rule/match (previously if you had more than one nth rule in your chain, 
the counter would be shared between all of them, which is undesirable if, for 
example, you want to match every 3rd http and every 4th smtp packet, for 
whatever reason ;). This is done in the same way that ipt_limit.c preserves 
state (basically a copy'n'paste from there).
It also adds another option to the nth match, namely --of ... So if, for 
example, you want to match every 3rd of 7 packets, you can do that as well 
(--every 3 --of 7). Default behavior should be the same as if --of wasn't 
there at all.
What's the use of all this ? Well, one could add a mangle rule to mark every 
1st packet with 1, every second with 2, etc. and then use those markings to 
later base SNAT or DNAT decisions on that (provided --state NEW is used).

The patches are against current CVS for the userspace so and Linux 2.4.10 
patched by p-o-m for the module itself.

The Help message in p-o-m has one addition after this :

  [--of X]		Match every nth packet of X packets

I hope this code is somewhat acceptable; please tell me what I'm doing wrong 
though, since it can't all be right ;)

ttyl,
  Eike

- --- netfilter/userspace/extensions/libipt_nth.c	Mon Jul 23 04:14:22 2001
+++ netfilter-new/userspace/extensions/libipt_nth.c	Fri Oct  5 18:23:45 2001
@@ -26,6 +26,7 @@
 	printf(
 "nth v%s options:\n"
 "  --every     Nth            Match every Nth packet.\n\n",
+" [--of]       X              Match every Nth of X packets.\n"
 " [--start]    counter        Initialize the counter at the number 
'counter'\n"
 "                             instead of 0. Must be of the form :\n"
 "                             0 <= counter <= (nth-1)"
@@ -36,6 +37,7 @@
 static struct option opts[] = {
 	{ "every", 1, 0, '1' },
 	{ "start", 1, 0, '2' },
+	{ "of", 1, 0, '3' },
 	{ 0 }
 };
 
@@ -49,6 +51,7 @@
 #define IPT_NTH_OPT_EVERY	0x01
 #define IPT_NTH_OPT_NOT_EVERY	0x02
 #define IPT_NTH_OPT_START	0x04
+#define IPT_NTH_OPT_OF		0x08
 
 /* Function which parses command options; returns true if it
    ate an option */
@@ -79,13 +82,14 @@
 
 		/* Remember, this function will interpret a leading 0 to be 
 		   Octal, a leading 0x to be hexdecimal... */
- -                if (string_to_number(optarg, 2, 100, &num) == -1 || num < 2)
+                if (string_to_number(optarg, 1, 100, &num) == -1 || num < 1)
                         exit_error(PARAMETER_PROBLEM,
- -                                   "bad --every `%s', must be between 2 and 
100", optarg);
+                                   "bad --every `%s', must be between 1 and 
100", optarg);
 
 		/* assign the values */
 		nthinfo->every = num-1;
 		nthinfo->startat = 0;
+		nthinfo->of = num-1;
 		if (invert)
 		{
 			*flags |= IPT_NTH_OPT_NOT_EVERY;
@@ -115,15 +119,40 @@
 		*flags |= IPT_NTH_OPT_START;
 		nthinfo->startat = num;
 		break;
+        case '3':  
+                /* check for common mistakes... */
+                if (!((*flags & IPT_NTH_OPT_EVERY) ||
+                   (*flags & IPT_NTH_OPT_NOT_EVERY)))
+                   exit_error(PARAMETER_PROBLEM,
+                   "Can't specify --of before --every");
+                if (invert)
+                   exit_error(PARAMETER_PROBLEM,
+                       "Can't specify with ! --of");
+                if (*flags & IPT_NTH_OPT_OF)
+                   exit_error(PARAMETER_PROBLEM,
+                   "Can't specify --of twice");
+                if (string_to_number(optarg, 1, 100, &num) == -1 || num < 1)
+                   exit_error(PARAMETER_PROBLEM,
+                   "bad --of `%s', must between 1 and 100", optarg);
+                if ((num-1) < nthinfo->every)
+                   exit_error(PARAMETER_PROBLEM,
+                   "bad --of `%s', must be between %u and 100", optarg, 
nthinfo->every+1);
+           
+	        *flags |= IPT_NTH_OPT_OF;
+                nthinfo->of = num-1;
+                break;
 	default:
 		return 0;
 	}
 	return 1;
 }
 
- -/* Final check; nothing. */
+/* Final check; needs more than 0 options */
 static void final_check(unsigned int flags)
 {
+    if (!flags)
+      exit_error(PARAMETER_PROBLEM,
+      "nth match: You must specify one or more options");
 }
 
 /* Prints out the targinfo. */
@@ -137,7 +166,7 @@
 
 	if (nthinfo->not == 1)
 		printf(" !");
- -	printf("every %uth ", (nthinfo->every +1));
+	printf("every %uth of %u ", (nthinfo->every +1), (nthinfo->of+1));
 	if (nthinfo->startat != 0)
 		printf(" start at %u ", nthinfo->startat);
 }
@@ -151,7 +180,7 @@
 
 	if (nthinfo->not == 1)
 		printf("! ");
- -	printf("--every %u ", (nthinfo->every +1));
+	printf("--every %u --of %u ", (nthinfo->every +1), (nthinfo->of+1));
 	if (nthinfo->startat != 0)
 		printf(" --start %u", nthinfo->startat );
 }
- --- linux-vanilla/net/ipv4/netfilter/ipt_nth.c	Fri Oct  5 19:28:10 2001
+++ linux/net/ipv4/netfilter/ipt_nth.c	Fri Oct  5 19:55:49 2001
@@ -5,6 +5,7 @@
      ftp://prep.ai.mit.edu/pub/gnu/GPL
 
   2001-18-07 Fabrice MARIE <fabrice@celestix.com> : initial implementation.
+  2001-05-10 Eike Frost <Eike.Frost@gmx.de> : make counter local to every 
match, implement --of
 */
 
 #include <linux/module.h>
@@ -15,13 +16,7 @@
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_nth.h>
 
- -/*
- - * State information.
- - */
- -static struct {
- -	spinlock_t lock;
- -	u_int16_t number;
- -} state;
+static spinlock_t counter_lock = SPIN_LOCK_UNLOCKED;
 
 static int
 ipt_nth_match(const struct sk_buff *pskb,
@@ -34,45 +29,64 @@
 	      int *hotdrop)
 {
 	/* Parameters from userspace */
- -	const struct ipt_nth_info *info = matchinfo;
+        struct ipt_nth_info *info = ((struct ipt_nth_info 
*)matchinfo)->master;
 
- -	spin_lock(&state.lock);
+        spin_lock_bh(&counter_lock);
 
 	/* Do we match ? */
- -	if (info->not == 0)
- -	{
- -		if (state.number == 0)
+	
+        if (info->not == 0)
+        {
+            if (info->number == info->every)
+	    {
+	        if (info->number == info->of)
+		{
+		    info->number = 0;
+		} else
 		{
- -			++state.number;
- -			goto match;
+		    ++info->number;
 		}
- -		if (state.number == info->every)
- -			state.number = 0; /* reset the counter */
- -		else
- -			++state.number;
+		
+		goto match;
+	    } else if (info->number == info->of)
+	    {
+	        info->number = 0; /* reset the counter */
+	        goto dontmatch;
+	    } else
+	    {
+	        ++info->number;
 		goto dontmatch;
+	    }
 	}
 	else
 	{
- -		if (state.number == 0)
+            if (info->number == info->every)
+	    {
+	        if (info->number == info->of)
 		{
- -			++state.number;
- -			goto dontmatch;
- -		}
- -		if (state.number == info->every)
- -			state.number = 0;
- -		else
- -			++state.number;
+		    info->number = 0;
+		} else
+		{
+		    ++info->number;
+		}		
+		goto dontmatch;
+	    } else if (info->number == info->of)
+	    {
+	        info->number = 0; /* reset the counter */
+	        goto match;
+	    } else
+	    {
+	        ++info->number;
 		goto match;
- -	}
- -	
+	    }
+	}	
+		
  dontmatch:
- -	/* don't match */
- -	spin_unlock(&state.lock);
+        spin_unlock_bh(&counter_lock);
 	return 0;
 
  match:
- -	spin_unlock(&state.lock);
+        spin_unlock_bh(&counter_lock);
 	return 1;
 }
 
@@ -84,15 +98,16 @@
 		   unsigned int hook_mask)
 {
 	/* Parameters from userspace */
- -	const struct ipt_nth_info *info = matchinfo;
+	struct ipt_nth_info *info = matchinfo;
 
 	if (matchsize != IPT_ALIGN(sizeof(struct ipt_nth_info))) {
 		printk("nth: matchsize %u != %u\n", matchsize,
 		       IPT_ALIGN(sizeof(struct ipt_nth_info)));
 		return 0;
 	}
- -
- -	state.number = info->startat;
+	
+	info->master = info;
+	info->number = info->startat;
 
 	return 1;
 }
@@ -107,11 +122,8 @@
 
 static int __init init(void)
 {
- -	memset(&state, 0, sizeof(state));
 	if (ipt_register_match(&ipt_nth_reg))
 		return -EINVAL;
- -
- -	spin_lock_init(&(state.lock));
 
 	printk("ipt_nth match loaded\n");
 	return 0;
- --- linux-vanilla/include/linux/netfilter_ipv4/ipt_nth.h	Fri Oct  5 19:28:10 
2001
+++ linux/include/linux/netfilter_ipv4/ipt_nth.h	Fri Oct  5 18:18:28 2001
@@ -8,6 +8,9 @@
 	u_int8_t every;
 	u_int8_t not;
 	u_int8_t startat;
+	u_int8_t of;
+	u_int8_t number;
+        struct ipt_nth_info *master;
 };
 
 #endif /*_IPT_NTH_H*/

-----BEGIN PGP SIGNATURE-----

iEYEARECAAYFAju99ZAACgkQIWUFBdAgNCrRqwCeKOHopoJF5pKLDi2/2Ylz4FeG
9xQAmwQ5arD67KwOclqjZjJQN6zr8lGT
=0BLZ
-----END PGP SIGNATURE-----

--------------Boundary-00=_0FVQTJ8VRLUG2LGEVJH0
Content-Type: text/x-diff;
  charset="iso-8859-1";
  name="nth.diff"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="nth.diff"

--- netfilter/userspace/extensions/libipt_nth.c	Mon Jul 23 04:14:22 2001
+++ netfilter-new/userspace/extensions/libipt_nth.c	Fri Oct  5 18:23:45 2001
@@ -26,6 +26,7 @@
 	printf(
 "nth v%s options:\n"
 "  --every     Nth            Match every Nth packet.\n\n",
+" [--of]       X              Match every Nth of X packets.\n"
 " [--start]    counter        Initialize the counter at the number 'counter'\n"
 "                             instead of 0. Must be of the form :\n"
 "                             0 <= counter <= (nth-1)"
@@ -36,6 +37,7 @@
 static struct option opts[] = {
 	{ "every", 1, 0, '1' },
 	{ "start", 1, 0, '2' },
+	{ "of", 1, 0, '3' },
 	{ 0 }
 };
 
@@ -49,6 +51,7 @@
 #define IPT_NTH_OPT_EVERY	0x01
 #define IPT_NTH_OPT_NOT_EVERY	0x02
 #define IPT_NTH_OPT_START	0x04
+#define IPT_NTH_OPT_OF		0x08
 
 /* Function which parses command options; returns true if it
    ate an option */
@@ -79,13 +82,14 @@
 
 		/* Remember, this function will interpret a leading 0 to be 
 		   Octal, a leading 0x to be hexdecimal... */
-                if (string_to_number(optarg, 2, 100, &num) == -1 || num < 2)
+                if (string_to_number(optarg, 1, 100, &num) == -1 || num < 1)
                         exit_error(PARAMETER_PROBLEM,
-                                   "bad --every `%s', must be between 2 and 100", optarg);
+                                   "bad --every `%s', must be between 1 and 100", optarg);
 
 		/* assign the values */
 		nthinfo->every = num-1;
 		nthinfo->startat = 0;
+		nthinfo->of = num-1;
 		if (invert)
 		{
 			*flags |= IPT_NTH_OPT_NOT_EVERY;
@@ -115,15 +119,40 @@
 		*flags |= IPT_NTH_OPT_START;
 		nthinfo->startat = num;
 		break;
+        case '3':  
+                /* check for common mistakes... */
+                if (!((*flags & IPT_NTH_OPT_EVERY) ||
+                   (*flags & IPT_NTH_OPT_NOT_EVERY)))
+                   exit_error(PARAMETER_PROBLEM,
+                   "Can't specify --of before --every");
+                if (invert)
+                   exit_error(PARAMETER_PROBLEM,
+                       "Can't specify with ! --of");
+                if (*flags & IPT_NTH_OPT_OF)
+                   exit_error(PARAMETER_PROBLEM,
+                   "Can't specify --of twice");
+                if (string_to_number(optarg, 1, 100, &num) == -1 || num < 1)
+                   exit_error(PARAMETER_PROBLEM,
+                   "bad --of `%s', must between 1 and 100", optarg);
+                if ((num-1) < nthinfo->every)
+                   exit_error(PARAMETER_PROBLEM,
+                   "bad --of `%s', must be between %u and 100", optarg, nthinfo->every+1);
+           
+	        *flags |= IPT_NTH_OPT_OF;
+                nthinfo->of = num-1;
+                break;
 	default:
 		return 0;
 	}
 	return 1;
 }
 
-/* Final check; nothing. */
+/* Final check; needs more than 0 options */
 static void final_check(unsigned int flags)
 {
+    if (!flags)
+      exit_error(PARAMETER_PROBLEM,
+      "nth match: You must specify one or more options");
 }
 
 /* Prints out the targinfo. */
@@ -137,7 +166,7 @@
 
 	if (nthinfo->not == 1)
 		printf(" !");
-	printf("every %uth ", (nthinfo->every +1));
+	printf("every %uth of %u ", (nthinfo->every +1), (nthinfo->of+1));
 	if (nthinfo->startat != 0)
 		printf(" start at %u ", nthinfo->startat);
 }
@@ -151,7 +180,7 @@
 
 	if (nthinfo->not == 1)
 		printf("! ");
-	printf("--every %u ", (nthinfo->every +1));
+	printf("--every %u --of %u ", (nthinfo->every +1), (nthinfo->of+1));
 	if (nthinfo->startat != 0)
 		printf(" --start %u", nthinfo->startat );
 }
--- linux-vanilla/net/ipv4/netfilter/ipt_nth.c	Fri Oct  5 19:28:10 2001
+++ linux/net/ipv4/netfilter/ipt_nth.c	Fri Oct  5 19:55:49 2001
@@ -5,6 +5,7 @@
      ftp://prep.ai.mit.edu/pub/gnu/GPL
 
   2001-18-07 Fabrice MARIE <fabrice@celestix.com> : initial implementation.
+  2001-05-10 Eike Frost <Eike.Frost@gmx.de> : make counter local to every match, implement --of
 */
 
 #include <linux/module.h>
@@ -15,13 +16,7 @@
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_nth.h>
 
-/*
- * State information.
- */
-static struct {
-	spinlock_t lock;
-	u_int16_t number;
-} state;
+static spinlock_t counter_lock = SPIN_LOCK_UNLOCKED;
 
 static int
 ipt_nth_match(const struct sk_buff *pskb,
@@ -34,45 +29,64 @@
 	      int *hotdrop)
 {
 	/* Parameters from userspace */
-	const struct ipt_nth_info *info = matchinfo;
+        struct ipt_nth_info *info = ((struct ipt_nth_info *)matchinfo)->master;
 
-	spin_lock(&state.lock);
+        spin_lock_bh(&counter_lock);
 
 	/* Do we match ? */
-	if (info->not == 0)
-	{
-		if (state.number == 0)
+	
+        if (info->not == 0)
+        {
+            if (info->number == info->every)
+	    {
+	        if (info->number == info->of)
+		{
+		    info->number = 0;
+		} else
 		{
-			++state.number;
-			goto match;
+		    ++info->number;
 		}
-		if (state.number == info->every)
-			state.number = 0; /* reset the counter */
-		else
-			++state.number;
+		
+		goto match;
+	    } else if (info->number == info->of)
+	    {
+	        info->number = 0; /* reset the counter */
+	        goto dontmatch;
+	    } else
+	    {
+	        ++info->number;
 		goto dontmatch;
+	    }
 	}
 	else
 	{
-		if (state.number == 0)
+            if (info->number == info->every)
+	    {
+	        if (info->number == info->of)
 		{
-			++state.number;
-			goto dontmatch;
-		}
-		if (state.number == info->every)
-			state.number = 0;
-		else
-			++state.number;
+		    info->number = 0;
+		} else
+		{
+		    ++info->number;
+		}		
+		goto dontmatch;
+	    } else if (info->number == info->of)
+	    {
+	        info->number = 0; /* reset the counter */
+	        goto match;
+	    } else
+	    {
+	        ++info->number;
 		goto match;
-	}
-	
+	    }
+	}	
+		
  dontmatch:
-	/* don't match */
-	spin_unlock(&state.lock);
+        spin_unlock_bh(&counter_lock);
 	return 0;
 
  match:
-	spin_unlock(&state.lock);
+        spin_unlock_bh(&counter_lock);
 	return 1;
 }
 
@@ -84,15 +98,16 @@
 		   unsigned int hook_mask)
 {
 	/* Parameters from userspace */
-	const struct ipt_nth_info *info = matchinfo;
+	struct ipt_nth_info *info = matchinfo;
 
 	if (matchsize != IPT_ALIGN(sizeof(struct ipt_nth_info))) {
 		printk("nth: matchsize %u != %u\n", matchsize,
 		       IPT_ALIGN(sizeof(struct ipt_nth_info)));
 		return 0;
 	}
-
-	state.number = info->startat;
+	
+	info->master = info;
+	info->number = info->startat;
 
 	return 1;
 }
@@ -107,11 +122,8 @@
 
 static int __init init(void)
 {
-	memset(&state, 0, sizeof(state));
 	if (ipt_register_match(&ipt_nth_reg))
 		return -EINVAL;
-
-	spin_lock_init(&(state.lock));
 
 	printk("ipt_nth match loaded\n");
 	return 0;
--- linux-vanilla/include/linux/netfilter_ipv4/ipt_nth.h	Fri Oct  5 19:28:10 2001
+++ linux/include/linux/netfilter_ipv4/ipt_nth.h	Fri Oct  5 18:18:28 2001
@@ -8,6 +8,9 @@
 	u_int8_t every;
 	u_int8_t not;
 	u_int8_t startat;
+	u_int8_t of;
+	u_int8_t number;
+        struct ipt_nth_info *master;
 };
 
 #endif /*_IPT_NTH_H*/

--------------Boundary-00=_0FVQTJ8VRLUG2LGEVJH0--