libip6t_dst.c revision 32b8e61e4e5bd405d9ad07bf9468498dfbb19f9e
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 u_int32_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, u_int16_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	default:
160		return 0;
161	}
162
163	return 1;
164}
165
166static void
167print_options(unsigned int optsnr, u_int16_t *optsp)
168{
169	unsigned int i;
170
171	for(i = 0; i < optsnr; i++) {
172		printf("%d", (optsp[i] & 0xFF00) >> 8);
173
174		if ((optsp[i] & 0x00FF) != 0x00FF)
175			printf(":%d", (optsp[i] & 0x00FF));
176
177		printf("%c", (i != optsnr - 1) ? ',' : ' ');
178	}
179}
180
181static void dst_print(const void *ip, const struct xt_entry_match *match,
182                      int numeric)
183{
184	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
185
186	printf("dst ");
187	if (optinfo->flags & IP6T_OPTS_LEN)
188		printf("length:%s%u ",
189			optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
190			optinfo->hdrlen);
191
192	if (optinfo->flags & IP6T_OPTS_OPTS)
193		printf("opts ");
194
195	print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
196
197	if (optinfo->flags & IP6T_OPTS_NSTRICT)
198		printf("not-strict ");
199
200	if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
201		printf("Unknown invflags: 0x%X ",
202		       optinfo->invflags & ~IP6T_OPTS_INV_MASK);
203}
204
205static void dst_save(const void *ip, const struct xt_entry_match *match)
206{
207	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
208
209	if (optinfo->flags & IP6T_OPTS_LEN) {
210		printf("%s--dst-len %u ",
211			(optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "",
212			optinfo->hdrlen);
213	}
214
215	if (optinfo->flags & IP6T_OPTS_OPTS)
216		printf("--dst-opts ");
217
218	print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
219
220	if (optinfo->flags & IP6T_OPTS_NSTRICT)
221		printf("--dst-not-strict ");
222}
223
224static struct xtables_match dst_mt6_reg = {
225	.name          = "dst",
226	.version       = XTABLES_VERSION,
227	.family        = NFPROTO_IPV6,
228	.size          = XT_ALIGN(sizeof(struct ip6t_opts)),
229	.userspacesize = XT_ALIGN(sizeof(struct ip6t_opts)),
230	.help          = dst_help,
231	.init          = dst_init,
232	.parse         = dst_parse,
233	.print         = dst_print,
234	.save          = dst_save,
235	.extra_opts    = dst_opts,
236};
237
238void
239_init(void)
240{
241	xtables_register_match(&dst_mt6_reg);
242}
243