1/*
2   Shared library add-on to iptables to add match support for random match.
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-10-14 Fabrice MARIE <fabrice@netfilter.org> : initial development.
9*/
10
11#include <stdio.h>
12#include <netdb.h>
13#include <string.h>
14#include <stdlib.h>
15#include <syslog.h>
16#include <getopt.h>
17#include <iptables.h>
18#include <linux/netfilter_ipv4/ip_tables.h>
19#include <linux/netfilter_ipv4/ipt_random.h>
20
21/**
22 * The kernel random routing returns numbers between 0 and 255.
23 * To ease the task of the user in choosing the probability
24 * of matching, we want him to be able to use percentages.
25 * Therefore we have to accept numbers in percentage here,
26 * turn them into number between 0 and 255 for the kernel module,
27 * and turn them back to percentages when we print/save
28 * the rule.
29 */
30
31
32/* Function which prints out usage message. */
33static void
34help(void)
35{
36	printf(
37"random v%s options:\n"
38"  [--average      percent ]    The probability in percentage of the match\n"
39"                               If ommited, a probability of 50%% percent is set.\n"
40"                               Percentage must be within : 1 <= percent <= 99.\n\n",
41IPTABLES_VERSION);
42}
43
44static struct option opts[] = {
45	{ "average", 1, 0, '1' },
46	{ 0 }
47};
48
49/* Initialize the target. */
50static void
51init(struct ipt_entry_match *m, unsigned int *nfcache)
52{
53	struct ipt_rand_info *randinfo = (struct ipt_rand_info *)(m)->data;
54
55	/* We assign the average to be 50 which is our default value */
56	/* 50 * 2.55 = 128 */
57	randinfo->average = 128;
58}
59
60#define IPT_RAND_OPT_AVERAGE	0x01
61
62/* Function which parses command options; returns true if it
63   ate an option */
64static int
65parse(int c, char **argv, int invert, unsigned int *flags,
66      const struct ipt_entry *entry,
67      unsigned int *nfcache,
68      struct ipt_entry_match **match)
69{
70	struct ipt_rand_info *randinfo = (struct ipt_rand_info *)(*match)->data;
71	unsigned int num;
72
73	switch (c) {
74	case '1':
75		/* check for common mistakes... */
76		if (invert)
77			exit_error(PARAMETER_PROBLEM,
78				   "Can't specify ! --average");
79		if (*flags & IPT_RAND_OPT_AVERAGE)
80			exit_error(PARAMETER_PROBLEM,
81				   "Can't specify --average twice");
82
83		/* Remember, this function will interpret a leading 0 to be
84		   Octal, a leading 0x to be hexdecimal... */
85                if (string_to_number(optarg, 1, 99, &num) == -1 || num < 1)
86                        exit_error(PARAMETER_PROBLEM,
87                                   "bad --average `%s', must be between 1 and 99", optarg);
88
89		/* assign the values */
90		randinfo->average = (int)(num * 2.55);
91		*flags |= IPT_RAND_OPT_AVERAGE;
92		break;
93	default:
94		return 0;
95	}
96	return 1;
97}
98
99/* Final check; nothing. */
100static void final_check(unsigned int flags)
101{
102}
103
104/* Prints out the targinfo. */
105static void
106print(const struct ipt_ip *ip,
107      const struct ipt_entry_match *match,
108      int numeric)
109{
110	const struct ipt_rand_info *randinfo
111		= (const struct ipt_rand_info *)match->data;
112	div_t result = div((randinfo->average*100), 255);
113	if (result.rem > 127)  /* round up... */
114		++result.quot;
115
116	printf(" random %u%% ", result.quot);
117}
118
119/* Saves the union ipt_targinfo in parsable form to stdout. */
120static void
121save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
122{
123	const struct ipt_rand_info *randinfo
124		= (const struct ipt_rand_info *)match->data;
125	div_t result = div((randinfo->average *100), 255);
126	if (result.rem > 127)  /* round up... */
127		++result.quot;
128
129	printf("--average %u ", result.quot);
130}
131
132struct iptables_match rand_match = {
133	.next		= NULL,
134	.name		= "random",
135	.version	= IPTABLES_VERSION,
136	.size		= IPT_ALIGN(sizeof(struct ipt_rand_info)),
137	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_rand_info)),
138	.help		= &help,
139	.init		= &init,
140	.parse		= &parse,
141	.final_check	= &final_check,
142	.print		= &print,
143	.save		= &save,
144	.extra_opts	= opts
145};
146
147void _init(void)
148{
149	register_match(&rand_match);
150}
151