libip6t_dst.c revision 4008138e2b5248940265b160fae001d8954fae21
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#ifdef HOPBYHOP
15#define UNAME "HBH"
16#define LNAME "hbh"
17#else
18#define UNAME "DST"
19#define LNAME "dst"
20#endif
21
22/* Function which prints out usage message. */
23static void
24help(void)
25{
26	printf(
27UNAME " v%s options:\n"
28" --" LNAME "-len [!] length           total length of this header\n"
29" --" LNAME "-opts TYPE[:LEN][,TYPE[:LEN]...] \n"
30"                               Options and its length (list, max: %d)\n",
31IPTABLES_VERSION, IP6T_OPTS_OPTSNR);
32}
33
34static struct option opts[] = {
35	{ .name = LNAME "-len",        .has_arg = 1, .flag = 0, .val = '1' },
36	{ .name = LNAME "-opts",       .has_arg = 1, .flag = 0, .val = '2' },
37	{ .name = LNAME "-not-strict", .has_arg = 1, .flag = 0, .val = '3' },
38	{ .name = 0 }
39};
40
41static u_int32_t
42parse_opts_num(const char *idstr, const char *typestr)
43{
44	unsigned long int id;
45	char* ep;
46
47	id = strtoul(idstr, &ep, 0);
48
49	if ( idstr == ep ) {
50		exit_error(PARAMETER_PROBLEM,
51			   UNAME " no valid digits in %s `%s'", typestr, idstr);
52	}
53	if ( id == ULONG_MAX  && errno == ERANGE ) {
54		exit_error(PARAMETER_PROBLEM,
55			   "%s `%s' specified too big: would overflow",
56			   typestr, idstr);
57	}
58	if ( *idstr != '\0'  && *ep != '\0' ) {
59		exit_error(PARAMETER_PROBLEM,
60			   UNAME " error parsing %s `%s'", typestr, idstr);
61	}
62	return (u_int32_t) id;
63}
64
65static int
66parse_options(const char *optsstr, u_int16_t *opts)
67{
68        char *buffer, *cp, *next, *range;
69        unsigned int i;
70
71	buffer = strdup(optsstr);
72        if (!buffer)
73		exit_error(OTHER_PROBLEM, "strdup failed");
74
75        for (cp = buffer, i = 0; cp && i < IP6T_OPTS_OPTSNR; cp = next, i++)
76        {
77                next = strchr(cp, ',');
78
79                if (next)
80			*next++='\0';
81
82                range = strchr(cp, ':');
83
84                if (range) {
85                        if (i == IP6T_OPTS_OPTSNR-1)
86                                exit_error(PARAMETER_PROBLEM,
87                                           "too many ports specified");
88                        *range++ = '\0';
89                }
90
91                opts[i] = (u_int16_t)((parse_opts_num(cp,"opt") & 0x000000FF)<<8);
92                if (range) {
93			if (opts[i] == 0)
94        			exit_error(PARAMETER_PROBLEM,
95					"PAD0 hasn't got length");
96                        opts[i] |= (u_int16_t)(parse_opts_num(range,"length") &
97					0x000000FF);
98                } else
99                        opts[i] |= (0x00FF);
100
101#ifdef DEBUG
102		printf("opts str: %s %s\n", cp, range);
103		printf("opts opt: %04X\n", opts[i]);
104#endif
105	}
106
107        if (cp)
108		exit_error(PARAMETER_PROBLEM, "too many addresses specified");
109
110	free(buffer);
111
112#ifdef DEBUG
113	printf("addr nr: %d\n", i);
114#endif
115
116	return i;
117}
118
119/* Initialize the match. */
120static void
121init(struct ip6t_entry_match *m, unsigned int *nfcache)
122{
123	struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
124
125	optinfo->hdrlen = 0;
126	optinfo->flags = 0;
127	optinfo->invflags = 0;
128	optinfo->optsnr = 0;
129}
130
131/* Function which parses command options; returns true if it
132   ate an option */
133static int
134parse(int c, char **argv, int invert, unsigned int *flags,
135      const struct ip6t_entry *entry,
136      unsigned int *nfcache,
137      struct ip6t_entry_match **match)
138{
139	struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
140
141	switch (c) {
142	case '1':
143		if (*flags & IP6T_OPTS_LEN)
144			exit_error(PARAMETER_PROBLEM,
145				   "Only one `--" LNAME "-len' allowed");
146		check_inverse(optarg, &invert, &optind, 0);
147		optinfo->hdrlen = parse_opts_num(argv[optind-1], "length");
148		if (invert)
149			optinfo->invflags |= IP6T_OPTS_INV_LEN;
150		optinfo->flags |= IP6T_OPTS_LEN;
151		*flags |= IP6T_OPTS_LEN;
152		break;
153	case '2':
154		if (*flags & IP6T_OPTS_OPTS)
155			exit_error(PARAMETER_PROBLEM,
156				   "Only one `--" LNAME "-opts' allowed");
157                check_inverse(optarg, &invert, &optind, 0);
158                if (invert)
159                        exit_error(PARAMETER_PROBLEM,
160				" '!' not allowed with `--" LNAME "-opts'");
161		optinfo->optsnr = parse_options(argv[optind-1], optinfo->opts);
162		optinfo->flags |= IP6T_OPTS_OPTS;
163		*flags |= IP6T_OPTS_OPTS;
164		break;
165	case '3':
166		if (*flags & IP6T_OPTS_NSTRICT)
167			exit_error(PARAMETER_PROBLEM,
168				   "Only one `--" LNAME "-not-strict' allowed");
169		if ( !(*flags & IP6T_OPTS_OPTS) )
170			exit_error(PARAMETER_PROBLEM,
171				   "`--" LNAME "-opts ...' required before `--"
172				   LNAME "-not-strict'");
173		optinfo->flags |= IP6T_OPTS_NSTRICT;
174		*flags |= IP6T_OPTS_NSTRICT;
175		break;
176	default:
177		return 0;
178	}
179
180	return 1;
181}
182
183/* Final check; we don't care. */
184static void
185final_check(unsigned int flags)
186{
187}
188
189static void
190print_options(int optsnr, u_int16_t *optsp)
191{
192	unsigned int i;
193
194	for(i = 0; i < optsnr; i++) {
195		printf("%d", (optsp[i] & 0xFF00) >> 8);
196
197		if ((optsp[i] & 0x00FF) != 0x00FF)
198			printf(":%d", (optsp[i] & 0x00FF));
199
200		printf("%c", (i != optsnr - 1) ? ',' : ' ');
201	}
202}
203
204/* Prints out the union ip6t_matchinfo. */
205static void
206print(const struct ip6t_ip6 *ip,
207      const struct ip6t_entry_match *match, int numeric)
208{
209	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
210
211	printf(LNAME " ");
212	if (optinfo->flags & IP6T_OPTS_LEN)
213		printf("length:%s%u ",
214			optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
215			optinfo->hdrlen);
216
217	if (optinfo->flags & IP6T_OPTS_OPTS)
218		printf("opts ");
219
220	print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
221
222	if (optinfo->flags & IP6T_OPTS_NSTRICT)
223		printf("not-strict ");
224
225	if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
226		printf("Unknown invflags: 0x%X ",
227		       optinfo->invflags & ~IP6T_OPTS_INV_MASK);
228}
229
230/* Saves the union ip6t_matchinfo in parsable form to stdout. */
231static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
232{
233	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
234
235	if (optinfo->flags & IP6T_OPTS_LEN) {
236		printf("--" LNAME "-len %s%u ",
237			(optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "",
238			optinfo->hdrlen);
239	}
240
241	if (optinfo->flags & IP6T_OPTS_OPTS)
242		printf("--" LNAME "-opts ");
243
244	print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
245
246	if (optinfo->flags & IP6T_OPTS_NSTRICT)
247		printf("--" LNAME "-not-strict ");
248}
249
250static
251struct ip6tables_match optstruct = {
252	.name          = LNAME,
253	.version       = IPTABLES_VERSION,
254	.size          = IP6T_ALIGN(sizeof(struct ip6t_opts)),
255	.userspacesize = IP6T_ALIGN(sizeof(struct ip6t_opts)),
256	.help          = &help,
257	.init          = &init,
258	.parse         = &parse,
259	.final_check   = &final_check,
260	.print         = &print,
261	.save          = &save,
262	.extra_opts    = opts
263};
264
265void
266_init(void)
267{
268	register_match6(&optstruct);
269}
270