libip6t_dst.c revision 9ee386a1b6d7704b259460152c959ab0e79e02aa
1/* Shared library add-on to ip6tables to add Hop-by-Hop and Dst headers 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_opts.h>
10#include <sys/types.h>
11#include <sys/socket.h>
12#include <arpa/inet.h>
13
14/* Function which prints out usage message. */
15static void dst_help(void)
16{
17	printf(
18"dst v%s 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",
22IPTABLES_VERSION, IP6T_OPTS_OPTSNR);
23}
24
25static const struct option dst_opts[] = {
26	{ .name = "dst-len",        .has_arg = 1, .val = '1' },
27	{ .name = "dst-opts",       .has_arg = 1, .val = '2' },
28	{ .name = "dst-not-strict", .has_arg = 1, .val = '3' },
29	{ .name = NULL }
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		exit_error(PARAMETER_PROBLEM,
42		           "dst: no valid digits in %s `%s'", typestr, idstr);
43	}
44	if ( id == ULONG_MAX  && errno == ERANGE ) {
45		exit_error(PARAMETER_PROBLEM,
46			   "%s `%s' specified too big: would overflow",
47			   typestr, idstr);
48	}
49	if ( *idstr != '\0'  && *ep != '\0' ) {
50		exit_error(PARAMETER_PROBLEM,
51		           "dst: error parsing %s `%s'", typestr, idstr);
52	}
53	return (u_int32_t) 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		exit_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                                exit_error(PARAMETER_PROBLEM,
78                                           "too many ports specified");
79                        *range++ = '\0';
80                }
81
82                opts[i] = (u_int16_t)((parse_opts_num(cp,"opt") & 0x000000FF)<<8);
83                if (range) {
84			if (opts[i] == 0)
85        			exit_error(PARAMETER_PROBLEM,
86					"PAD0 hasn't got length");
87                        opts[i] |= (u_int16_t)(parse_opts_num(range,"length") &
88					0x000000FF);
89                } else
90                        opts[i] |= (0x00FF);
91
92#ifdef DEBUG
93		printf("opts str: %s %s\n", cp, range);
94		printf("opts opt: %04X\n", opts[i]);
95#endif
96	}
97
98        if (cp)
99		exit_error(PARAMETER_PROBLEM, "too many addresses specified");
100
101	free(buffer);
102
103#ifdef DEBUG
104	printf("addr nr: %d\n", i);
105#endif
106
107	return i;
108}
109
110/* Initialize the match. */
111static void dst_init(struct xt_entry_match *m)
112{
113	struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
114
115	optinfo->hdrlen = 0;
116	optinfo->flags = 0;
117	optinfo->invflags = 0;
118	optinfo->optsnr = 0;
119}
120
121/* Function which parses command options; returns true if it
122   ate an option */
123static int dst_parse(int c, char **argv, int invert, unsigned int *flags,
124                     const void *entry, struct xt_entry_match **match)
125{
126	struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
127
128	switch (c) {
129	case '1':
130		if (*flags & IP6T_OPTS_LEN)
131			exit_error(PARAMETER_PROBLEM,
132				   "Only one `--dst-len' allowed");
133		check_inverse(optarg, &invert, &optind, 0);
134		optinfo->hdrlen = parse_opts_num(argv[optind-1], "length");
135		if (invert)
136			optinfo->invflags |= IP6T_OPTS_INV_LEN;
137		optinfo->flags |= IP6T_OPTS_LEN;
138		*flags |= IP6T_OPTS_LEN;
139		break;
140	case '2':
141		if (*flags & IP6T_OPTS_OPTS)
142			exit_error(PARAMETER_PROBLEM,
143				   "Only one `--dst-opts' allowed");
144                check_inverse(optarg, &invert, &optind, 0);
145                if (invert)
146                        exit_error(PARAMETER_PROBLEM,
147				" '!' not allowed with `--dst-opts'");
148		optinfo->optsnr = parse_options(argv[optind-1], optinfo->opts);
149		optinfo->flags |= IP6T_OPTS_OPTS;
150		*flags |= IP6T_OPTS_OPTS;
151		break;
152	case '3':
153		if (*flags & IP6T_OPTS_NSTRICT)
154			exit_error(PARAMETER_PROBLEM,
155				   "Only one `--dst-not-strict' allowed");
156		if ( !(*flags & IP6T_OPTS_OPTS) )
157			exit_error(PARAMETER_PROBLEM,
158				   "`--dst-opts ...' required before "
159				   "`--dst-not-strict'");
160		optinfo->flags |= IP6T_OPTS_NSTRICT;
161		*flags |= IP6T_OPTS_NSTRICT;
162		break;
163	default:
164		return 0;
165	}
166
167	return 1;
168}
169
170static void
171print_options(unsigned optsnr, u_int16_t *optsp)
172{
173	unsigned int i;
174
175	for(i = 0; i < optsnr; i++) {
176		printf("%d", (optsp[i] & 0xFF00) >> 8);
177
178		if ((optsp[i] & 0x00FF) != 0x00FF)
179			printf(":%d", (optsp[i] & 0x00FF));
180
181		printf("%c", (i != optsnr - 1) ? ',' : ' ');
182	}
183}
184
185/* Prints out the union ip6t_matchinfo. */
186static void dst_print(const void *ip, const struct xt_entry_match *match,
187                      int numeric)
188{
189	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
190
191	printf("dst ");
192	if (optinfo->flags & IP6T_OPTS_LEN)
193		printf("length:%s%u ",
194			optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
195			optinfo->hdrlen);
196
197	if (optinfo->flags & IP6T_OPTS_OPTS)
198		printf("opts ");
199
200	print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
201
202	if (optinfo->flags & IP6T_OPTS_NSTRICT)
203		printf("not-strict ");
204
205	if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
206		printf("Unknown invflags: 0x%X ",
207		       optinfo->invflags & ~IP6T_OPTS_INV_MASK);
208}
209
210/* Saves the union ip6t_matchinfo in parsable form to stdout. */
211static void dst_save(const void *ip, const struct xt_entry_match *match)
212{
213	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
214
215	if (optinfo->flags & IP6T_OPTS_LEN) {
216		printf("--dst-len %s%u ",
217			(optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "",
218			optinfo->hdrlen);
219	}
220
221	if (optinfo->flags & IP6T_OPTS_OPTS)
222		printf("--dst-opts ");
223
224	print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
225
226	if (optinfo->flags & IP6T_OPTS_NSTRICT)
227		printf("--dst-not-strict ");
228}
229
230static struct ip6tables_match dst_match6 = {
231	.name          = "dst",
232	.version       = IPTABLES_VERSION,
233	.size          = IP6T_ALIGN(sizeof(struct ip6t_opts)),
234	.userspacesize = IP6T_ALIGN(sizeof(struct ip6t_opts)),
235	.help          = dst_help,
236	.init          = dst_init,
237	.parse         = dst_parse,
238	.print         = dst_print,
239	.save          = dst_save,
240	.extra_opts    = dst_opts,
241};
242
243void
244_init(void)
245{
246	register_match6(&dst_match6);
247}
248