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