libxt_statistic.c revision d09b6d591ca7d7d7575cb6aa20384c9830f777ab
1#include <stdbool.h>
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <stddef.h>
7#include <getopt.h>
8
9#include <xtables.h>
10#include <linux/netfilter/xt_statistic.h>
11
12static void statistic_help(void)
13{
14	printf(
15"statistic match 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}
23
24static const struct option statistic_opts[] = {
25	{.name = "mode",        .has_arg = true, .val = '1'},
26	{.name = "probability", .has_arg = true, .val = '2'},
27	{.name = "every",       .has_arg = true, .val = '3'},
28	{.name = "packet",      .has_arg = true, .val = '4'},
29	XT_GETOPT_TABLEEND,
30};
31
32static struct xt_statistic_info *global_info;
33
34static void statistic_mt_init(struct xt_entry_match *match)
35{
36	global_info = (void *)match->data;
37}
38
39static int
40statistic_parse(int c, char **argv, int invert, unsigned int *flags,
41                const void *entry, struct xt_entry_match **match)
42{
43	struct xt_statistic_info *info = (void *)(*match)->data;
44	unsigned int val;
45	double prob;
46
47	if (invert)
48		info->flags |= XT_STATISTIC_INVERT;
49
50	switch (c) {
51	case '1':
52		if (*flags & 0x1)
53			xtables_error(PARAMETER_PROBLEM, "double --mode");
54		if (!strcmp(optarg, "random"))
55			info->mode = XT_STATISTIC_MODE_RANDOM;
56		else if (!strcmp(optarg, "nth"))
57			info->mode = XT_STATISTIC_MODE_NTH;
58		else
59			xtables_error(PARAMETER_PROBLEM, "Bad mode \"%s\"", optarg);
60		*flags |= 0x1;
61		break;
62	case '2':
63		if (*flags & 0x2)
64			xtables_error(PARAMETER_PROBLEM, "double --probability");
65		prob = atof(optarg);
66		if (prob < 0 || prob > 1)
67			xtables_error(PARAMETER_PROBLEM,
68				   "--probability must be between 0 and 1");
69		info->u.random.probability = 0x80000000 * prob;
70		*flags |= 0x2;
71		break;
72	case '3':
73		if (*flags & 0x4)
74			xtables_error(PARAMETER_PROBLEM, "double --every");
75		if (!xtables_strtoui(optarg, NULL, &val, 0, UINT32_MAX))
76			xtables_error(PARAMETER_PROBLEM,
77				   "cannot parse --every `%s'", optarg);
78		info->u.nth.every = val;
79		if (info->u.nth.every == 0)
80			xtables_error(PARAMETER_PROBLEM, "--every cannot be 0");
81		info->u.nth.every--;
82		*flags |= 0x4;
83		break;
84	case '4':
85		if (*flags & 0x8)
86			xtables_error(PARAMETER_PROBLEM, "double --packet");
87		if (!xtables_strtoui(optarg, NULL, &val, 0, UINT32_MAX))
88			xtables_error(PARAMETER_PROBLEM,
89				   "cannot parse --packet `%s'", optarg);
90		info->u.nth.packet = val;
91		*flags |= 0x8;
92		break;
93	}
94	return 1;
95}
96
97static void statistic_check(unsigned int flags)
98{
99	if (!(flags & 0x1))
100		xtables_error(PARAMETER_PROBLEM, "no mode specified");
101	if ((flags & 0x2) && (flags & (0x4 | 0x8)))
102		xtables_error(PARAMETER_PROBLEM,
103			   "both nth and random parameters given");
104	if (flags & 0x2 && global_info->mode != XT_STATISTIC_MODE_RANDOM)
105		xtables_error(PARAMETER_PROBLEM,
106			   "--probability can only be used in random mode");
107	if (flags & 0x4 && global_info->mode != XT_STATISTIC_MODE_NTH)
108		xtables_error(PARAMETER_PROBLEM,
109			   "--every can only be used in nth mode");
110	if (flags & 0x8 && global_info->mode != XT_STATISTIC_MODE_NTH)
111		xtables_error(PARAMETER_PROBLEM,
112			   "--packet can only be used in nth mode");
113	if ((flags & 0x8) && !(flags & 0x4))
114		xtables_error(PARAMETER_PROBLEM,
115			   "--packet can only be used with --every");
116	/* at this point, info->u.nth.every have been decreased. */
117	if (global_info->u.nth.packet > global_info->u.nth.every)
118		xtables_error(PARAMETER_PROBLEM,
119			  "the --packet p must be 0 <= p <= n-1");
120
121
122	global_info->u.nth.count = global_info->u.nth.every -
123	                           global_info->u.nth.packet;
124}
125
126static void print_match(const struct xt_statistic_info *info, char *prefix)
127{
128	if (info->flags & XT_STATISTIC_INVERT)
129		printf("! ");
130
131	switch (info->mode) {
132	case XT_STATISTIC_MODE_RANDOM:
133		printf("%smode random %sprobability %f ", prefix, prefix,
134		       1.0 * info->u.random.probability / 0x80000000);
135		break;
136	case XT_STATISTIC_MODE_NTH:
137		printf("%smode nth %severy %u ", prefix, prefix,
138		       info->u.nth.every + 1);
139		if (info->u.nth.packet)
140			printf("%spacket %u ", prefix, info->u.nth.packet);
141		break;
142	}
143}
144
145static void
146statistic_print(const void *ip, const struct xt_entry_match *match, int numeric)
147{
148	const struct xt_statistic_info *info = (const void *)match->data;
149
150	printf("statistic ");
151	print_match(info, "");
152}
153
154static void statistic_save(const void *ip, const struct xt_entry_match *match)
155{
156	const struct xt_statistic_info *info = (const void *)match->data;
157
158	print_match(info, "--");
159}
160
161static struct xtables_match statistic_match = {
162	.family		= NFPROTO_UNSPEC,
163	.name		= "statistic",
164	.version	= XTABLES_VERSION,
165	.size		= XT_ALIGN(sizeof(struct xt_statistic_info)),
166	.userspacesize	= offsetof(struct xt_statistic_info, u.nth.count),
167	.init		= statistic_mt_init,
168	.help		= statistic_help,
169	.parse		= statistic_parse,
170	.final_check	= statistic_check,
171	.print		= statistic_print,
172	.save		= statistic_save,
173	.extra_opts	= statistic_opts,
174};
175
176void _init(void)
177{
178	xtables_register_match(&statistic_match);
179}
180