1/* Shared library add-on to ip6tables to add packet length matching support. */
2
3#include <stdio.h>
4#include <netdb.h>
5#include <string.h>
6#include <stdlib.h>
7#include <getopt.h>
8
9#include <ip6tables.h>
10#include <linux/netfilter_ipv6/ip6t_length.h>
11
12/* Function which prints out usage message. */
13static void
14help(void)
15{
16	printf(
17"length v%s options:\n"
18"[!] --length length[:length]    Match packet length against value or range\n"
19"                                of values (inclusive)\n",
20IPTABLES_VERSION);
21
22}
23
24static struct option opts[] = {
25	{ "length", 1, 0, '1' },
26	{0}
27};
28
29/* Initialize the match. */
30static void
31init(struct ip6t_entry_match *m, unsigned int *nfcache)
32{
33	*nfcache |= NFC_UNKNOWN;
34}
35
36static u_int16_t
37parse_length(const char *s)
38{
39
40	int len;
41
42	if (string_to_number(s, 0, 0xFFFF, &len) == -1)
43		exit_error(PARAMETER_PROBLEM, "length invalid: `%s'\n", s);
44	else
45		return (u_int16_t )len;
46}
47
48/* If a single value is provided, min and max are both set to the value */
49static void
50parse_lengths(const char *s, struct ip6t_length_info *info)
51{
52	char *buffer;
53	char *cp;
54
55	buffer = strdup(s);
56	if ((cp = strchr(buffer, ':')) == NULL)
57		info->min = info->max = parse_length(buffer);
58	else {
59		*cp = '\0';
60		cp++;
61
62		info->min = buffer[0] ? parse_length(buffer) : 0;
63		info->max = cp[0] ? parse_length(cp) : 0xFFFF;
64	}
65	free(buffer);
66
67	if (info->min > info->max)
68		exit_error(PARAMETER_PROBLEM,
69		           "length min. range value `%u' greater than max. "
70		           "range value `%u'", info->min, info->max);
71
72}
73
74/* Function which parses command options; returns true if it
75   ate an option */
76static int
77parse(int c, char **argv, int invert, unsigned int *flags,
78      const struct ip6t_entry *entry,
79      unsigned int *nfcache,
80      struct ip6t_entry_match **match)
81{
82	struct ip6t_length_info *info = (struct ip6t_length_info *)(*match)->data;
83
84	switch (c) {
85		case '1':
86			if (*flags)
87				exit_error(PARAMETER_PROBLEM,
88				           "length: `--length' may only be "
89				           "specified once");
90			check_inverse(optarg, &invert, &optind, 0);
91			parse_lengths(argv[optind-1], info);
92			if (invert)
93				info->invert = 1;
94			*flags = 1;
95			break;
96
97		default:
98			return 0;
99	}
100	return 1;
101}
102
103/* Final check; must have specified --length. */
104static void
105final_check(unsigned int flags)
106{
107	if (!flags)
108		exit_error(PARAMETER_PROBLEM,
109			   "length: You must specify `--length'");
110}
111
112/* Common match printing code. */
113static void
114print_length(struct ip6t_length_info *info)
115{
116	if (info->invert)
117		fputc('!', stdout);
118
119	if (info->max == info->min)
120		printf("%u ", info->min);
121	else
122		printf("%u:%u ", info->min, info->max);
123}
124
125/* Prints out the matchinfo. */
126static void
127print(const struct ip6t_ip6 *ip,
128      const struct ip6t_entry_match *match,
129      int numeric)
130{
131	printf("length ");
132	print_length((struct ip6t_length_info *)match->data);
133}
134
135/* Saves the union ip6t_matchinfo in parsable form to stdout. */
136static void
137save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
138{
139	printf("--length ");
140	print_length((struct ip6t_length_info *)match->data);
141}
142
143struct ip6tables_match length
144= { NULL,
145    "length",
146    IPTABLES_VERSION,
147    IP6T_ALIGN(sizeof(struct ip6t_length_info)),
148    IP6T_ALIGN(sizeof(struct ip6t_length_info)),
149    &help,
150    &init,
151    &parse,
152    &final_check,
153    &print,
154    &save,
155    opts
156};
157
158void _init(void)
159{
160	register_match6(&length);
161}
162