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