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