libip6t_dst.c revision bf97128c7262f17a02fec41cdae75b472ba77f88
1/* Shared library add-on to ip6tables to add Dst 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 <xtables.h>
9#include <linux/netfilter_ipv6/ip6t_opts.h>
10#include <sys/types.h>
11#include <sys/socket.h>
12#include <arpa/inet.h>
13
14static void dst_help(void)
15{
16	printf(
17"dst match options:\n"
18"[!] --dst-len length            total length of this header\n"
19"  --dst-opts TYPE[:LEN][,TYPE[:LEN]...]\n"
20"                                Options and its length (list, max: %d)\n",
21IP6T_OPTS_OPTSNR);
22}
23
24static const struct option dst_opts[] = {
25	{ .name = "dst-len",        .has_arg = 1, .val = '1' },
26	{ .name = "dst-opts",       .has_arg = 1, .val = '2' },
27	{ .name = "dst-not-strict", .has_arg = 1, .val = '3' },
28	{ .name = NULL }
29};
30
31static u_int32_t
32parse_opts_num(const char *idstr, const char *typestr)
33{
34	unsigned long int id;
35	char* ep;
36
37	id = strtoul(idstr, &ep, 0);
38
39	if ( idstr == ep ) {
40		xtables_error(PARAMETER_PROBLEM,
41		           "dst: no valid digits in %s `%s'", typestr, idstr);
42	}
43	if ( id == ULONG_MAX  && errno == ERANGE ) {
44		xtables_error(PARAMETER_PROBLEM,
45			   "%s `%s' specified too big: would overflow",
46			   typestr, idstr);
47	}
48	if ( *idstr != '\0'  && *ep != '\0' ) {
49		xtables_error(PARAMETER_PROBLEM,
50		           "dst: error parsing %s `%s'", typestr, idstr);
51	}
52	return id;
53}
54
55static int
56parse_options(const char *optsstr, u_int16_t *opts)
57{
58        char *buffer, *cp, *next, *range;
59        unsigned int i;
60
61	buffer = strdup(optsstr);
62        if (!buffer)
63		xtables_error(OTHER_PROBLEM, "strdup failed");
64
65        for (cp = buffer, i = 0; cp && i < IP6T_OPTS_OPTSNR; cp = next, i++)
66        {
67                next = strchr(cp, ',');
68
69                if (next)
70			*next++='\0';
71
72                range = strchr(cp, ':');
73
74                if (range) {
75                        if (i == IP6T_OPTS_OPTSNR-1)
76				xtables_error(PARAMETER_PROBLEM,
77                                           "too many ports specified");
78                        *range++ = '\0';
79                }
80
81		opts[i] = (parse_opts_num(cp, "opt") & 0xFF) << 8;
82                if (range) {
83			if (opts[i] == 0)
84				xtables_error(PARAMETER_PROBLEM,
85					"PAD0 hasn't got length");
86			opts[i] |= parse_opts_num(range, "length") & 0xFF;
87                } else
88                        opts[i] |= (0x00FF);
89
90#ifdef DEBUG
91		printf("opts str: %s %s\n", cp, range);
92		printf("opts opt: %04X\n", opts[i]);
93#endif
94	}
95
96        if (cp)
97		xtables_error(PARAMETER_PROBLEM, "too many addresses specified");
98
99	free(buffer);
100
101#ifdef DEBUG
102	printf("addr nr: %d\n", i);
103#endif
104
105	return i;
106}
107
108static void dst_init(struct xt_entry_match *m)
109{
110	struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
111
112	optinfo->hdrlen = 0;
113	optinfo->flags = 0;
114	optinfo->invflags = 0;
115	optinfo->optsnr = 0;
116}
117
118static int dst_parse(int c, char **argv, int invert, unsigned int *flags,
119                     const void *entry, struct xt_entry_match **match)
120{
121	struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
122
123	switch (c) {
124	case '1':
125		if (*flags & IP6T_OPTS_LEN)
126			xtables_error(PARAMETER_PROBLEM,
127				   "Only one `--dst-len' allowed");
128		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
129		optinfo->hdrlen = parse_opts_num(argv[optind-1], "length");
130		if (invert)
131			optinfo->invflags |= IP6T_OPTS_INV_LEN;
132		optinfo->flags |= IP6T_OPTS_LEN;
133		*flags |= IP6T_OPTS_LEN;
134		break;
135	case '2':
136		if (*flags & IP6T_OPTS_OPTS)
137			xtables_error(PARAMETER_PROBLEM,
138				   "Only one `--dst-opts' allowed");
139                xtables_check_inverse(optarg, &invert, &optind, 0, argv);
140                if (invert)
141			xtables_error(PARAMETER_PROBLEM,
142				" '!' not allowed with `--dst-opts'");
143		optinfo->optsnr = parse_options(argv[optind-1], optinfo->opts);
144		optinfo->flags |= IP6T_OPTS_OPTS;
145		*flags |= IP6T_OPTS_OPTS;
146		break;
147	case '3':
148		if (*flags & IP6T_OPTS_NSTRICT)
149			xtables_error(PARAMETER_PROBLEM,
150				   "Only one `--dst-not-strict' allowed");
151		if ( !(*flags & IP6T_OPTS_OPTS) )
152			xtables_error(PARAMETER_PROBLEM,
153				   "`--dst-opts ...' required before "
154				   "`--dst-not-strict'");
155		optinfo->flags |= IP6T_OPTS_NSTRICT;
156		*flags |= IP6T_OPTS_NSTRICT;
157		break;
158	default:
159		return 0;
160	}
161
162	return 1;
163}
164
165static void
166print_options(unsigned int optsnr, u_int16_t *optsp)
167{
168	unsigned int i;
169
170	for(i = 0; i < optsnr; i++) {
171		printf("%d", (optsp[i] & 0xFF00) >> 8);
172
173		if ((optsp[i] & 0x00FF) != 0x00FF)
174			printf(":%d", (optsp[i] & 0x00FF));
175
176		printf("%c", (i != optsnr - 1) ? ',' : ' ');
177	}
178}
179
180static void dst_print(const void *ip, const struct xt_entry_match *match,
181                      int numeric)
182{
183	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
184
185	printf("dst ");
186	if (optinfo->flags & IP6T_OPTS_LEN)
187		printf("length:%s%u ",
188			optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
189			optinfo->hdrlen);
190
191	if (optinfo->flags & IP6T_OPTS_OPTS)
192		printf("opts ");
193
194	print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
195
196	if (optinfo->flags & IP6T_OPTS_NSTRICT)
197		printf("not-strict ");
198
199	if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
200		printf("Unknown invflags: 0x%X ",
201		       optinfo->invflags & ~IP6T_OPTS_INV_MASK);
202}
203
204static void dst_save(const void *ip, const struct xt_entry_match *match)
205{
206	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
207
208	if (optinfo->flags & IP6T_OPTS_LEN) {
209		printf("%s--dst-len %u ",
210			(optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "",
211			optinfo->hdrlen);
212	}
213
214	if (optinfo->flags & IP6T_OPTS_OPTS)
215		printf("--dst-opts ");
216
217	print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
218
219	if (optinfo->flags & IP6T_OPTS_NSTRICT)
220		printf("--dst-not-strict ");
221}
222
223static struct xtables_match dst_mt6_reg = {
224	.name          = "dst",
225	.version       = XTABLES_VERSION,
226	.family        = NFPROTO_IPV6,
227	.size          = XT_ALIGN(sizeof(struct ip6t_opts)),
228	.userspacesize = XT_ALIGN(sizeof(struct ip6t_opts)),
229	.help          = dst_help,
230	.init          = dst_init,
231	.parse         = dst_parse,
232	.print         = dst_print,
233	.save          = dst_save,
234	.extra_opts    = dst_opts,
235};
236
237void
238_init(void)
239{
240	xtables_register_match(&dst_mt6_reg);
241}
242