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