1/*
2   Shared library add-on to iptables to add match support for every Nth packet
3
4   This file is distributed under the terms of the GNU General Public
5   License (GPL). Copies of the GPL can be obtained from:
6   ftp://prep.ai.mit.edu/pub/gnu/GPL
7
8   2001-07-17 Fabrice MARIE <fabrice@netfilter.org> : initial development.
9   2001-09-20 Richard Wagner (rwagner@cloudnet.com)
10        * added support for multiple counters
11        * added support for matching on individual packets
12          in the counter cycle
13*/
14
15#include <stdio.h>
16#include <netdb.h>
17#include <string.h>
18#include <stdlib.h>
19#include <syslog.h>
20#include <getopt.h>
21#include <iptables.h>
22#include <linux/netfilter_ipv4/ip_tables.h>
23#include <linux/netfilter_ipv4/ipt_nth.h>
24
25
26/* Function which prints out usage message. */
27static void
28help(void)
29{
30	printf(
31"nth v%s options:\n"
32"   --every     Nth              Match every Nth packet\n"
33"  [--counter   num ]            Use counter 0-%u (default:0)\n"
34"  [--start     num ]            Initialize the counter at the number 'num'\n"
35"                                instead of 0. Must be between 0 and Nth-1\n"
36"  [--packet    num ]            Match on 'num' packet. Must be between 0\n"
37"                                and Nth-1.\n\n"
38"                                If --packet is used for a counter than\n"
39"                                there must be Nth number of --packet\n"
40"                                rules, covering all values between 0 and\n"
41"                                Nth-1 inclusively.\n",
42IPTABLES_VERSION, IPT_NTH_NUM_COUNTERS-1);
43}
44
45static struct option opts[] = {
46	{ "every", 1, 0, '1' },
47	{ "start", 1, 0, '2' },
48        { "counter", 1, 0, '3' },
49        { "packet", 1, 0, '4' },
50	{ 0 }
51};
52
53#define IPT_NTH_OPT_EVERY	0x01
54#define IPT_NTH_OPT_NOT_EVERY	0x02
55#define IPT_NTH_OPT_START	0x04
56#define IPT_NTH_OPT_COUNTER     0x08
57#define IPT_NTH_OPT_PACKET      0x10
58
59/* Function which parses command options; returns true if it
60   ate an option */
61static int
62parse(int c, char **argv, int invert, unsigned int *flags,
63      const struct ipt_entry *entry,
64      unsigned int *nfcache,
65      struct ipt_entry_match **match)
66{
67	struct ipt_nth_info *nthinfo = (struct ipt_nth_info *)(*match)->data;
68	unsigned int num;
69
70	switch (c) {
71	case '1':
72		/* check for common mistakes... */
73		if ((!invert) && (*flags & IPT_NTH_OPT_EVERY))
74			exit_error(PARAMETER_PROBLEM,
75				   "Can't specify --every twice");
76		if (invert && (*flags & IPT_NTH_OPT_NOT_EVERY))
77			exit_error(PARAMETER_PROBLEM,
78				   "Can't specify ! --every twice");
79		if ((!invert) && (*flags & IPT_NTH_OPT_NOT_EVERY))
80			exit_error(PARAMETER_PROBLEM,
81				   "Can't specify --every with ! --every");
82		if (invert && (*flags & IPT_NTH_OPT_EVERY))
83			exit_error(PARAMETER_PROBLEM,
84				   "Can't specify ! --every with --every");
85
86		/* Remember, this function will interpret a leading 0 to be
87		   Octal, a leading 0x to be hexdecimal... */
88                if (string_to_number(optarg, 2, 100, &num) == -1 || num < 2)
89                        exit_error(PARAMETER_PROBLEM,
90                                   "bad --every `%s', must be between 2 and 100", optarg);
91
92		/* assign the values */
93		nthinfo->every = num-1;
94		nthinfo->startat = 0;
95                nthinfo->packet = 0xFF;
96                if(!(*flags & IPT_NTH_OPT_EVERY))
97                {
98                        nthinfo->counter = 0;
99                }
100		if (invert)
101		{
102			*flags |= IPT_NTH_OPT_NOT_EVERY;
103			nthinfo->not = 1;
104		}
105		else
106		{
107			*flags |= IPT_NTH_OPT_EVERY;
108			nthinfo->not = 0;
109		}
110		break;
111	case '2':
112		/* check for common mistakes... */
113		if (!((*flags & IPT_NTH_OPT_EVERY) ||
114		      (*flags & IPT_NTH_OPT_NOT_EVERY)))
115			exit_error(PARAMETER_PROBLEM,
116				   "Can't specify --start before --every");
117		if (invert)
118			exit_error(PARAMETER_PROBLEM,
119				   "Can't specify with ! --start");
120		if (*flags & IPT_NTH_OPT_START)
121			exit_error(PARAMETER_PROBLEM,
122				   "Can't specify --start twice");
123		if (string_to_number(optarg, 0, nthinfo->every, &num) == -1)
124                        exit_error(PARAMETER_PROBLEM,
125                                   "bad --start `%s', must between 0 and %u", optarg, nthinfo->every);
126		*flags |= IPT_NTH_OPT_START;
127		nthinfo->startat = num;
128		break;
129        case '3':
130                /* check for common mistakes... */
131                if (invert)
132                        exit_error(PARAMETER_PROBLEM,
133                                   "Can't specify with ! --counter");
134                if (*flags & IPT_NTH_OPT_COUNTER)
135                        exit_error(PARAMETER_PROBLEM,
136                                   "Can't specify --counter twice");
137                if (string_to_number(optarg, 0, IPT_NTH_NUM_COUNTERS-1, &num) == -1)
138                        exit_error(PARAMETER_PROBLEM,
139                                   "bad --counter `%s', must between 0 and %u", optarg, IPT_NTH_NUM_COUNTERS-1);
140                /* assign the values */
141                *flags |= IPT_NTH_OPT_COUNTER;
142                nthinfo->counter = num;
143                break;
144        case '4':
145                /* check for common mistakes... */
146                if (!((*flags & IPT_NTH_OPT_EVERY) ||
147                      (*flags & IPT_NTH_OPT_NOT_EVERY)))
148                        exit_error(PARAMETER_PROBLEM,
149                                   "Can't specify --packet before --every");
150                if ((*flags & IPT_NTH_OPT_NOT_EVERY))
151                        exit_error(PARAMETER_PROBLEM,
152                                   "Can't specify --packet with ! --every");
153                if (invert)
154                        exit_error(PARAMETER_PROBLEM,
155                                   "Can't specify with ! --packet");
156                if (*flags & IPT_NTH_OPT_PACKET)
157                        exit_error(PARAMETER_PROBLEM,
158                                   "Can't specify --packet twice");
159                if (string_to_number(optarg, 0, nthinfo->every, &num) == -1)
160                        exit_error(PARAMETER_PROBLEM,
161                                   "bad --packet `%s', must between 0 and %u", optarg, nthinfo->every);
162                *flags |= IPT_NTH_OPT_PACKET;
163                nthinfo->packet = num;
164                break;
165	default:
166		return 0;
167	}
168	return 1;
169}
170
171/* Final check; nothing. */
172static void final_check(unsigned int flags)
173{
174}
175
176/* Prints out the targinfo. */
177static void
178print(const struct ipt_ip *ip,
179      const struct ipt_entry_match *match,
180      int numeric)
181{
182	const struct ipt_nth_info *nthinfo
183		= (const struct ipt_nth_info *)match->data;
184
185	if (nthinfo->not == 1)
186		printf(" !");
187	printf("every %uth ", (nthinfo->every +1));
188	if (nthinfo->counter != 0)
189		printf("counter #%u ", (nthinfo->counter));
190        if (nthinfo->packet != 0xFF)
191                printf("packet #%u ", nthinfo->packet);
192	if (nthinfo->startat != 0)
193		printf("start at %u ", nthinfo->startat);
194}
195
196/* Saves the union ipt_targinfo in parsable form to stdout. */
197static void
198save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
199{
200	const struct ipt_nth_info *nthinfo
201		= (const struct ipt_nth_info *)match->data;
202
203	if (nthinfo->not == 1)
204		printf("! ");
205	printf("--every %u ", (nthinfo->every +1));
206	printf("--counter %u ", (nthinfo->counter));
207	if (nthinfo->startat != 0)
208		printf("--start %u ", nthinfo->startat );
209        if (nthinfo->packet != 0xFF)
210                printf("--packet %u ", nthinfo->packet );
211}
212
213static struct iptables_match nth = {
214	.next		= NULL,
215	.name		= "nth",
216	.version	= IPTABLES_VERSION,
217	.size		= IPT_ALIGN(sizeof(struct ipt_nth_info)),
218	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_nth_info)),
219	.help		= &help,
220	.parse		= &parse,
221	.final_check	= &final_check,
222	.print		= &print,
223	.save		= &save,
224	.extra_opts	= opts
225};
226
227void _init(void)
228{
229	register_match(&nth);
230}
231