libip6t_frag.c revision a620c61d441b931bc4a52ec07f1b906318ee4069
1/* Shared library add-on to ip6tables to add Fragmentation header support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <getopt.h>
7#include <errno.h>
8#include <ip6tables.h>
9#include <linux/netfilter_ipv6/ip6t_frag.h>
10
11/* Function which prints out usage message. */
12static void
13help(void)
14{
15	printf(
16"FRAG v%s options:\n"
17" --fragid [!] id[:id]          match the id (range)\n"
18" --fraglen [!] length          total length of this header\n"
19" --fragres                     check the reserved filed, too\n"
20" --fragfirst                   matches on the first fragment\n"
21" [--fragmore|--fraglast]       there are more fragments or this\n"
22"                               is the last one\n",
23IPTABLES_VERSION);
24}
25
26static struct option opts[] = {
27	{ .name = "fragid",    .has_arg = 1, .flag = 0, .val = '1' },
28	{ .name = "fraglen",   .has_arg = 1, .flag = 0, .val = '2' },
29	{ .name = "fragres",   .has_arg = 0, .flag = 0, .val = '3' },
30	{ .name = "fragfirst", .has_arg = 0, .flag = 0, .val = '4' },
31	{ .name = "fragmore",  .has_arg = 0, .flag = 0, .val = '5' },
32	{ .name = "fraglast",  .has_arg = 0, .flag = 0, .val = '6' },
33	{ .name = 0 }
34};
35
36static u_int32_t
37parse_frag_id(const char *idstr, const char *typestr)
38{
39	unsigned long int id;
40	char* ep;
41
42	id = strtoul(idstr, &ep, 0);
43
44	if ( idstr == ep ) {
45		exit_error(PARAMETER_PROBLEM,
46			   "FRAG no valid digits in %s `%s'", typestr, idstr);
47	}
48	if ( id == ULONG_MAX  && errno == ERANGE ) {
49		exit_error(PARAMETER_PROBLEM,
50			   "%s `%s' specified too big: would overflow",
51			   typestr, idstr);
52	}
53	if ( *idstr != '\0'  && *ep != '\0' ) {
54		exit_error(PARAMETER_PROBLEM,
55			   "FRAG error parsing %s `%s'", typestr, idstr);
56	}
57	return (u_int32_t) id;
58}
59
60static void
61parse_frag_ids(const char *idstring, u_int32_t *ids)
62{
63	char *buffer;
64	char *cp;
65
66	buffer = strdup(idstring);
67	if ((cp = strchr(buffer, ':')) == NULL)
68		ids[0] = ids[1] = parse_frag_id(buffer,"id");
69	else {
70		*cp = '\0';
71		cp++;
72
73		ids[0] = buffer[0] ? parse_frag_id(buffer,"id") : 0;
74		ids[1] = cp[0] ? parse_frag_id(cp,"id") : 0xFFFFFFFF;
75	}
76	free(buffer);
77}
78
79/* Initialize the match. */
80static void
81init(struct xt_entry_match *m, unsigned int *nfcache)
82{
83	struct ip6t_frag *fraginfo = (struct ip6t_frag *)m->data;
84
85	fraginfo->ids[0] = 0x0L;
86	fraginfo->ids[1] = 0xFFFFFFFF;
87	fraginfo->hdrlen = 0;
88	fraginfo->flags = 0;
89	fraginfo->invflags = 0;
90}
91
92/* Function which parses command options; returns true if it
93   ate an option */
94static int
95parse(int c, char **argv, int invert, unsigned int *flags,
96      const void *entry,
97      unsigned int *nfcache,
98      struct xt_entry_match **match)
99{
100	struct ip6t_frag *fraginfo = (struct ip6t_frag *)(*match)->data;
101
102	switch (c) {
103	case '1':
104		if (*flags & IP6T_FRAG_IDS)
105			exit_error(PARAMETER_PROBLEM,
106				   "Only one `--fragid' allowed");
107		check_inverse(optarg, &invert, &optind, 0);
108		parse_frag_ids(argv[optind-1], fraginfo->ids);
109		if (invert)
110			fraginfo->invflags |= IP6T_FRAG_INV_IDS;
111		fraginfo->flags |= IP6T_FRAG_IDS;
112		*flags |= IP6T_FRAG_IDS;
113		break;
114	case '2':
115		if (*flags & IP6T_FRAG_LEN)
116			exit_error(PARAMETER_PROBLEM,
117				   "Only one `--fraglen' allowed");
118		check_inverse(optarg, &invert, &optind, 0);
119		fraginfo->hdrlen = parse_frag_id(argv[optind-1], "length");
120		if (invert)
121			fraginfo->invflags |= IP6T_FRAG_INV_LEN;
122		fraginfo->flags |= IP6T_FRAG_LEN;
123		*flags |= IP6T_FRAG_LEN;
124		break;
125	case '3':
126		if (*flags & IP6T_FRAG_RES)
127			exit_error(PARAMETER_PROBLEM,
128				   "Only one `--fragres' allowed");
129		fraginfo->flags |= IP6T_FRAG_RES;
130		*flags |= IP6T_FRAG_RES;
131		break;
132	case '4':
133		if (*flags & IP6T_FRAG_FST)
134			exit_error(PARAMETER_PROBLEM,
135				   "Only one `--fragfirst' allowed");
136		fraginfo->flags |= IP6T_FRAG_FST;
137		*flags |= IP6T_FRAG_FST;
138		break;
139	case '5':
140		if (*flags & (IP6T_FRAG_MF|IP6T_FRAG_NMF))
141			exit_error(PARAMETER_PROBLEM,
142			   "Only one `--fragmore' or `--fraglast' allowed");
143		fraginfo->flags |= IP6T_FRAG_MF;
144		*flags |= IP6T_FRAG_MF;
145		break;
146	case '6':
147		if (*flags & (IP6T_FRAG_MF|IP6T_FRAG_NMF))
148			exit_error(PARAMETER_PROBLEM,
149			   "Only one `--fragmore' or `--fraglast' allowed");
150		fraginfo->flags |= IP6T_FRAG_NMF;
151		*flags |= IP6T_FRAG_NMF;
152		break;
153	default:
154		return 0;
155	}
156
157	return 1;
158}
159
160/* Final check; we don't care. */
161static void
162final_check(unsigned int flags)
163{
164}
165
166static void
167print_ids(const char *name, u_int32_t min, u_int32_t max,
168	    int invert)
169{
170	const char *inv = invert ? "!" : "";
171
172	if (min != 0 || max != 0xFFFFFFFF || invert) {
173		printf("%s", name);
174		if (min == max)
175			printf(":%s%u ", inv, min);
176		else
177			printf("s:%s%u:%u ", inv, min, max);
178	}
179}
180
181/* Prints out the union ip6t_matchinfo. */
182static void
183print(const void *ip,
184      const struct xt_entry_match *match, int numeric)
185{
186	const struct ip6t_frag *frag = (struct ip6t_frag *)match->data;
187
188	printf("frag ");
189	print_ids("id", frag->ids[0], frag->ids[1],
190		    frag->invflags & IP6T_FRAG_INV_IDS);
191
192	if (frag->flags & IP6T_FRAG_LEN) {
193		printf("length:%s%u ",
194			frag->invflags & IP6T_FRAG_INV_LEN ? "!" : "",
195			frag->hdrlen);
196	}
197
198	if (frag->flags & IP6T_FRAG_RES)
199		printf("reserved ");
200
201	if (frag->flags & IP6T_FRAG_FST)
202		printf("first ");
203
204	if (frag->flags & IP6T_FRAG_MF)
205		printf("more ");
206
207	if (frag->flags & IP6T_FRAG_NMF)
208		printf("last ");
209
210	if (frag->invflags & ~IP6T_FRAG_INV_MASK)
211		printf("Unknown invflags: 0x%X ",
212		       frag->invflags & ~IP6T_FRAG_INV_MASK);
213}
214
215/* Saves the union ip6t_matchinfo in parsable form to stdout. */
216static void save(const void *ip, const struct xt_entry_match *match)
217{
218	const struct ip6t_frag *fraginfo = (struct ip6t_frag *)match->data;
219
220	if (!(fraginfo->ids[0] == 0
221	    && fraginfo->ids[1] == 0xFFFFFFFF)) {
222		printf("--fragid %s",
223			(fraginfo->invflags & IP6T_FRAG_INV_IDS) ? "! " : "");
224		if (fraginfo->ids[0]
225		    != fraginfo->ids[1])
226			printf("%u:%u ",
227			       fraginfo->ids[0],
228			       fraginfo->ids[1]);
229		else
230			printf("%u ",
231			       fraginfo->ids[0]);
232	}
233
234	if (fraginfo->flags & IP6T_FRAG_LEN) {
235		printf("--fraglen %s%u ",
236			(fraginfo->invflags & IP6T_FRAG_INV_LEN) ? "! " : "",
237			fraginfo->hdrlen);
238	}
239
240	if (fraginfo->flags & IP6T_FRAG_RES)
241		printf("--fragres ");
242
243	if (fraginfo->flags & IP6T_FRAG_FST)
244		printf("--fragfirst ");
245
246	if (fraginfo->flags & IP6T_FRAG_MF)
247		printf("--fragmore ");
248
249	if (fraginfo->flags & IP6T_FRAG_NMF)
250		printf("--fraglast ");
251}
252
253static
254struct ip6tables_match frag = {
255	.name          = "frag",
256	.version       = IPTABLES_VERSION,
257	.size          = IP6T_ALIGN(sizeof(struct ip6t_frag)),
258	.userspacesize = IP6T_ALIGN(sizeof(struct ip6t_frag)),
259	.help          = &help,
260	.init          = &init,
261	.parse         = &parse,
262	.final_check   = &final_check,
263	.print         = &print,
264	.save          = &save,
265	.extra_opts    = opts
266};
267
268void
269_init(void)
270{
271	register_match6(&frag);
272}
273