1#include <stdio.h>
2#include <netdb.h>
3#include <string.h>
4#include <stdlib.h>
5#include <stddef.h>
6#include <getopt.h>
7
8#include <iptables.h>
9#include <linux/netfilter/xt_statistic.h>
10
11static void
12help(void)
13{
14	printf(
15"statistic match v%s options:\n"
16" --mode mode                    Match mode (random, nth)\n"
17" random mode:\n"
18" --probability p		 Probability\n"
19" nth mode:\n"
20" --every n			 Match every nth packet\n"
21" --packet p			 Initial counter value (0 <= p <= n-1, default 0)\n"
22"\n",
23IPTABLES_VERSION);
24}
25
26static struct option opts[] = {
27	{ "mode", 1, 0, '1' },
28	{ "probability", 1, 0, '2' },
29	{ "every", 1, 0, '3' },
30	{ "packet", 1, 0, '4' },
31	{ 0 }
32};
33
34static struct xt_statistic_info *info;
35
36static int
37parse(int c, char **argv, int invert, unsigned int *flags,
38      const struct ipt_entry *entry,
39      unsigned int *nfcache,
40      struct ipt_entry_match **match)
41{
42	double prob;
43
44	info = (void *)(*match)->data;
45
46	if (invert)
47		info->flags |= XT_STATISTIC_INVERT;
48
49	switch (c) {
50	case '1':
51		if (*flags & 0x1)
52			exit_error(PARAMETER_PROBLEM, "double --mode");
53		if (!strcmp(optarg, "random"))
54			info->mode = XT_STATISTIC_MODE_RANDOM;
55		else if (!strcmp(optarg, "nth"))
56			info->mode = XT_STATISTIC_MODE_NTH;
57		else
58			exit_error(PARAMETER_PROBLEM, "Bad mode `%s'", optarg);
59		*flags |= 0x1;
60		break;
61	case '2':
62		if (*flags & 0x2)
63			exit_error(PARAMETER_PROBLEM, "double --probability");
64		prob = atof(optarg);
65		if (prob < 0 || prob > 1)
66			exit_error(PARAMETER_PROBLEM,
67				   "--probability must be between 0 and 1");
68		info->u.random.probability = 0x80000000 * prob;
69		*flags |= 0x2;
70		break;
71	case '3':
72		if (*flags & 0x4)
73			exit_error(PARAMETER_PROBLEM, "double --every");
74		if (string_to_number(optarg, 0, 0xFFFFFFFF,
75				     &info->u.nth.every) == -1)
76			exit_error(PARAMETER_PROBLEM,
77				   "cannot parse --every `%s'", optarg);
78		if (info->u.nth.every == 0)
79			exit_error(PARAMETER_PROBLEM, "--every cannot be 0");
80		info->u.nth.every--;
81		*flags |= 0x4;
82		break;
83	case '4':
84		if (*flags & 0x8)
85			exit_error(PARAMETER_PROBLEM, "double --packet");
86		if (string_to_number(optarg, 0, 0xFFFFFFFF,
87				     &info->u.nth.packet) == -1)
88			exit_error(PARAMETER_PROBLEM,
89				   "cannot parse --packet `%s'", optarg);
90		*flags |= 0x8;
91		break;
92	default:
93		return 0;
94	}
95	return 1;
96}
97
98/* Final check; must have specified --mark. */
99static void
100final_check(unsigned int flags)
101{
102	if (!(flags & 0x1))
103		exit_error(PARAMETER_PROBLEM, "no mode specified");
104	if ((flags & 0x2) && (flags & (0x4 | 0x8)))
105		exit_error(PARAMETER_PROBLEM,
106			   "both nth and random parameters given");
107	if (flags & 0x2 && info->mode != XT_STATISTIC_MODE_RANDOM)
108		exit_error(PARAMETER_PROBLEM,
109			   "--probability can only be used in random mode");
110	if (flags & 0x4 && info->mode != XT_STATISTIC_MODE_NTH)
111		exit_error(PARAMETER_PROBLEM,
112			   "--every can only be used in nth mode");
113	if (flags & 0x8 && info->mode != XT_STATISTIC_MODE_NTH)
114		exit_error(PARAMETER_PROBLEM,
115			   "--packet can only be used in nth mode");
116	info->u.nth.count = info->u.nth.every - info->u.nth.packet;
117}
118
119/* Prints out the matchinfo. */
120static void print_match(const struct xt_statistic_info *info, char *prefix)
121{
122	if (info->flags & XT_STATISTIC_INVERT)
123		printf("! ");
124
125	switch (info->mode) {
126	case XT_STATISTIC_MODE_RANDOM:
127		printf("%smode random %sprobability %f ", prefix, prefix,
128		       1.0 * info->u.random.probability / 0x80000000);
129		break;
130	case XT_STATISTIC_MODE_NTH:
131		printf("%smode nth %severy %u ", prefix, prefix,
132		       info->u.nth.every + 1);
133		if (info->u.nth.packet)
134			printf("%spacket %u ", prefix, info->u.nth.packet);
135		break;
136	}
137}
138
139static void
140print(const struct ipt_ip *ip,
141      const struct ipt_entry_match *match,
142      int numeric)
143{
144	struct xt_statistic_info *info = (struct xt_statistic_info *)match->data;
145
146	printf("statistic ");
147	print_match(info, "");
148}
149
150/* Saves the union ipt_matchinfo in parsable form to stdout. */
151static void
152save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
153{
154	struct xt_statistic_info *info = (struct xt_statistic_info *)match->data;
155
156	print_match(info, "--");
157}
158
159static struct iptables_match statistic = {
160	.name		= "statistic",
161	.version	= IPTABLES_VERSION,
162	.size		= IPT_ALIGN(sizeof(struct xt_statistic_info)),
163	.userspacesize	= offsetof(struct xt_statistic_info, u.nth.count),
164	.help		= help,
165	.parse		= parse,
166	.final_check	= final_check,
167	.print		= print,
168	.save		= save,
169	.extra_opts	= opts
170};
171
172void _init(void)
173{
174	register_match(&statistic);
175}
176