libip6t_dst.c revision d09b6d591ca7d7d7575cb6aa20384c9830f777ab
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 void dst_init(struct xt_entry_match *m)
110{
111	struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
112
113	optinfo->hdrlen = 0;
114	optinfo->flags = 0;
115	optinfo->invflags = 0;
116	optinfo->optsnr = 0;
117}
118
119static int dst_parse(int c, char **argv, int invert, unsigned int *flags,
120                     const void *entry, struct xt_entry_match **match)
121{
122	struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
123
124	switch (c) {
125	case '1':
126		if (*flags & IP6T_OPTS_LEN)
127			xtables_error(PARAMETER_PROBLEM,
128				   "Only one `--dst-len' allowed");
129		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
130		optinfo->hdrlen = parse_opts_num(optarg, "length");
131		if (invert)
132			optinfo->invflags |= IP6T_OPTS_INV_LEN;
133		optinfo->flags |= IP6T_OPTS_LEN;
134		*flags |= IP6T_OPTS_LEN;
135		break;
136	case '2':
137		if (*flags & IP6T_OPTS_OPTS)
138			xtables_error(PARAMETER_PROBLEM,
139				   "Only one `--dst-opts' allowed");
140                xtables_check_inverse(optarg, &invert, &optind, 0, argv);
141                if (invert)
142			xtables_error(PARAMETER_PROBLEM,
143				" '!' not allowed with `--dst-opts'");
144		optinfo->optsnr = parse_options(optarg, optinfo->opts);
145		optinfo->flags |= IP6T_OPTS_OPTS;
146		*flags |= IP6T_OPTS_OPTS;
147		break;
148	case '3':
149		if (*flags & IP6T_OPTS_NSTRICT)
150			xtables_error(PARAMETER_PROBLEM,
151				   "Only one `--dst-not-strict' allowed");
152		if ( !(*flags & IP6T_OPTS_OPTS) )
153			xtables_error(PARAMETER_PROBLEM,
154				   "`--dst-opts ...' required before "
155				   "`--dst-not-strict'");
156		optinfo->flags |= IP6T_OPTS_NSTRICT;
157		*flags |= IP6T_OPTS_NSTRICT;
158		break;
159	}
160
161	return 1;
162}
163
164static void
165print_options(unsigned int optsnr, uint16_t *optsp)
166{
167	unsigned int i;
168
169	for(i = 0; i < optsnr; i++) {
170		printf("%d", (optsp[i] & 0xFF00) >> 8);
171
172		if ((optsp[i] & 0x00FF) != 0x00FF)
173			printf(":%d", (optsp[i] & 0x00FF));
174
175		printf("%c", (i != optsnr - 1) ? ',' : ' ');
176	}
177}
178
179static void dst_print(const void *ip, const struct xt_entry_match *match,
180                      int numeric)
181{
182	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
183
184	printf("dst ");
185	if (optinfo->flags & IP6T_OPTS_LEN)
186		printf("length:%s%u ",
187			optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
188			optinfo->hdrlen);
189
190	if (optinfo->flags & IP6T_OPTS_OPTS)
191		printf("opts ");
192
193	print_options(optinfo->optsnr, (uint16_t *)optinfo->opts);
194
195	if (optinfo->flags & IP6T_OPTS_NSTRICT)
196		printf("not-strict ");
197
198	if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
199		printf("Unknown invflags: 0x%X ",
200		       optinfo->invflags & ~IP6T_OPTS_INV_MASK);
201}
202
203static void dst_save(const void *ip, const struct xt_entry_match *match)
204{
205	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
206
207	if (optinfo->flags & IP6T_OPTS_LEN) {
208		printf("%s--dst-len %u ",
209			(optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "",
210			optinfo->hdrlen);
211	}
212
213	if (optinfo->flags & IP6T_OPTS_OPTS)
214		printf("--dst-opts ");
215
216	print_options(optinfo->optsnr, (uint16_t *)optinfo->opts);
217
218	if (optinfo->flags & IP6T_OPTS_NSTRICT)
219		printf("--dst-not-strict ");
220}
221
222static struct xtables_match dst_mt6_reg = {
223	.name          = "dst",
224	.version       = XTABLES_VERSION,
225	.family        = NFPROTO_IPV6,
226	.size          = XT_ALIGN(sizeof(struct ip6t_opts)),
227	.userspacesize = XT_ALIGN(sizeof(struct ip6t_opts)),
228	.help          = dst_help,
229	.init          = dst_init,
230	.parse         = dst_parse,
231	.print         = dst_print,
232	.save          = dst_save,
233	.extra_opts    = dst_opts,
234};
235
236void
237_init(void)
238{
239	xtables_register_match(&dst_mt6_reg);
240}
241