1/*
2 * Copyright (c) 2006-2013 Patrick McHardy <kaber@trash.net>
3 */
4
5#include <math.h>
6#include <stdio.h>
7#include <string.h>
8#include <xtables.h>
9#include <linux/netfilter/xt_statistic.h>
10
11enum {
12	O_MODE = 0,
13	O_PROBABILITY,
14	O_EVERY,
15	O_PACKET,
16	F_PROBABILITY = 1 << O_PROBABILITY,
17	F_EVERY       = 1 << O_EVERY,
18	F_PACKET      = 1 << O_PACKET,
19};
20
21static void statistic_help(void)
22{
23	printf(
24"statistic match options:\n"
25" --mode mode                    Match mode (random, nth)\n"
26" random mode:\n"
27"[!] --probability p		 Probability\n"
28" nth mode:\n"
29"[!] --every n			 Match every nth packet\n"
30" --packet p			 Initial counter value (0 <= p <= n-1, default 0)\n");
31}
32
33#define s struct xt_statistic_info
34static const struct xt_option_entry statistic_opts[] = {
35	{.name = "mode", .id = O_MODE, .type = XTTYPE_STRING,
36	 .flags = XTOPT_MAND},
37	{.name = "probability", .id = O_PROBABILITY, .type = XTTYPE_DOUBLE,
38	 .flags = XTOPT_INVERT, .min = 0, .max = 1,
39	 .excl = F_EVERY | F_PACKET},
40	{.name = "every", .id = O_EVERY, .type = XTTYPE_UINT32, .min = 1,
41	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, u.nth.every),
42	 .excl = F_PROBABILITY, .also = F_PACKET},
43	{.name = "packet", .id = O_PACKET, .type = XTTYPE_UINT32,
44	 .flags = XTOPT_PUT, XTOPT_POINTER(s, u.nth.packet),
45	 .excl = F_PROBABILITY, .also = F_EVERY},
46	XTOPT_TABLEEND,
47};
48#undef s
49
50static void statistic_parse(struct xt_option_call *cb)
51{
52	struct xt_statistic_info *info = cb->data;
53
54	if (cb->invert)
55		info->flags |= XT_STATISTIC_INVERT;
56
57	xtables_option_parse(cb);
58	switch (cb->entry->id) {
59	case O_MODE:
60		if (strcmp(cb->arg, "random") == 0)
61			info->mode = XT_STATISTIC_MODE_RANDOM;
62		else if (strcmp(cb->arg, "nth") == 0)
63			info->mode = XT_STATISTIC_MODE_NTH;
64		else
65			xtables_error(PARAMETER_PROBLEM, "Bad mode \"%s\"",
66				cb->arg);
67		break;
68	case O_PROBABILITY:
69		info->u.random.probability = lround(0x80000000 * cb->val.dbl);
70		break;
71	case O_EVERY:
72		--info->u.nth.every;
73		break;
74	}
75}
76
77static void statistic_check(struct xt_fcheck_call *cb)
78{
79	struct xt_statistic_info *info = cb->data;
80
81	if (info->mode == XT_STATISTIC_MODE_RANDOM &&
82	    !(cb->xflags & F_PROBABILITY))
83		xtables_error(PARAMETER_PROBLEM,
84			"--probability must be specified when using "
85			"random mode");
86	if (info->mode == XT_STATISTIC_MODE_NTH &&
87	    !(cb->xflags & (F_EVERY | F_PACKET)))
88		xtables_error(PARAMETER_PROBLEM,
89			"--every and --packet must be specified when "
90			"using nth mode");
91
92	/* at this point, info->u.nth.every have been decreased. */
93	if (info->u.nth.packet > info->u.nth.every)
94		xtables_error(PARAMETER_PROBLEM,
95			  "the --packet p must be 0 <= p <= n-1");
96
97	info->u.nth.count = info->u.nth.every - info->u.nth.packet;
98}
99
100static void print_match(const struct xt_statistic_info *info, char *prefix)
101{
102	switch (info->mode) {
103	case XT_STATISTIC_MODE_RANDOM:
104		printf(" %smode random%s %sprobability %.11f", prefix,
105		       (info->flags & XT_STATISTIC_INVERT) ? " !" : "",
106		       prefix,
107		       1.0 * info->u.random.probability / 0x80000000);
108		break;
109	case XT_STATISTIC_MODE_NTH:
110		printf(" %smode nth%s %severy %u", prefix,
111		       (info->flags & XT_STATISTIC_INVERT) ? " !" : "",
112		       prefix,
113		       info->u.nth.every + 1);
114		if (info->u.nth.packet || *prefix)
115			printf(" %spacket %u", prefix, info->u.nth.packet);
116		break;
117	}
118}
119
120static void
121statistic_print(const void *ip, const struct xt_entry_match *match, int numeric)
122{
123	const struct xt_statistic_info *info = (const void *)match->data;
124
125	printf(" statistic");
126	print_match(info, "");
127}
128
129static void statistic_save(const void *ip, const struct xt_entry_match *match)
130{
131	const struct xt_statistic_info *info = (const void *)match->data;
132
133	print_match(info, "--");
134}
135
136static struct xtables_match statistic_match = {
137	.family		= NFPROTO_UNSPEC,
138	.name		= "statistic",
139	.version	= XTABLES_VERSION,
140	.size		= XT_ALIGN(sizeof(struct xt_statistic_info)),
141	.userspacesize	= offsetof(struct xt_statistic_info, u.nth.count),
142	.help		= statistic_help,
143	.x6_parse	= statistic_parse,
144	.x6_fcheck	= statistic_check,
145	.print		= statistic_print,
146	.save		= statistic_save,
147	.x6_options	= statistic_opts,
148};
149
150void _init(void)
151{
152	xtables_register_match(&statistic_match);
153}
154