libip6t_rt.c revision 4008138e2b5248940265b160fae001d8954fae21
1/* Shared library add-on to ip6tables to add Routing header 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_rt.h>
11#include <sys/types.h>
12#include <sys/socket.h>
13#include <arpa/inet.h>
14
15/*#define DEBUG	1*/
16
17/* Function which prints out usage message. */
18static void
19help(void)
20{
21	printf(
22"RT v%s options:\n"
23" --rt-type [!] type            match the type\n"
24" --rt-segsleft [!] num[:num]   match the Segments Left field (range)\n"
25" --rt-len [!] length           total length of this header\n"
26" --rt-0-res                    check the reserved filed, too (type 0)\n"
27" --rt-0-addrs ADDR[,ADDR...]   Type=0 addresses (list, max: %d)\n"
28" --rt-0-not-strict             List of Type=0 addresses not a strict list\n",
29IPTABLES_VERSION, IP6T_RT_HOPS);
30}
31
32static struct option opts[] = {
33	{ "rt-type", 1, 0, '1' },
34	{ "rt-segsleft", 1, 0, '2' },
35	{ "rt-len", 1, 0, '3' },
36	{ "rt-0-res", 0, 0, '4' },
37	{ "rt-0-addrs", 1, 0, '5' },
38	{ "rt-0-not-strict", 0, 0, '6' },
39	{0}
40};
41
42static u_int32_t
43parse_rt_num(const char *idstr, const char *typestr)
44{
45	unsigned long int id;
46	char* ep;
47
48	id =  strtoul(idstr,&ep,0) ;
49
50	if ( idstr == ep ) {
51		exit_error(PARAMETER_PROBLEM,
52			   "RT no valid digits in %s `%s'", typestr, idstr);
53	}
54	if ( id == ULONG_MAX  && errno == ERANGE ) {
55		exit_error(PARAMETER_PROBLEM,
56			   "%s `%s' specified too big: would overflow",
57			   typestr, idstr);
58	}
59	if ( *idstr != '\0'  && *ep != '\0' ) {
60		exit_error(PARAMETER_PROBLEM,
61			   "RT error parsing %s `%s'", typestr, idstr);
62	}
63	return (u_int32_t) id;
64}
65
66static void
67parse_rt_segsleft(const char *idstring, u_int32_t *ids)
68{
69	char *buffer;
70	char *cp;
71
72	buffer = strdup(idstring);
73	if ((cp = strchr(buffer, ':')) == NULL)
74		ids[0] = ids[1] = parse_rt_num(buffer,"segsleft");
75	else {
76		*cp = '\0';
77		cp++;
78
79		ids[0] = buffer[0] ? parse_rt_num(buffer,"segsleft") : 0;
80		ids[1] = cp[0] ? parse_rt_num(cp,"segsleft") : 0xFFFFFFFF;
81	}
82	free(buffer);
83}
84
85static char *
86addr_to_numeric(const struct in6_addr *addrp)
87{
88	static char buf[50+1];
89	return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
90}
91
92static struct in6_addr *
93numeric_to_addr(const char *num)
94{
95	static struct in6_addr ap;
96	int err;
97
98	if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
99		return &ap;
100#ifdef DEBUG
101	fprintf(stderr, "\nnumeric2addr: %d\n", err);
102#endif
103        exit_error(PARAMETER_PROBLEM, "bad address: %s", num);
104
105	return (struct in6_addr *)NULL;
106}
107
108
109static int
110parse_addresses(const char *addrstr, struct in6_addr *addrp)
111{
112        char *buffer, *cp, *next;
113        unsigned int i;
114
115	buffer = strdup(addrstr);
116        if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
117
118        for (cp=buffer, i=0; cp && i<IP6T_RT_HOPS; cp=next,i++)
119        {
120                next=strchr(cp, ',');
121                if (next) *next++='\0';
122		memcpy(&(addrp[i]), numeric_to_addr(cp), sizeof(struct in6_addr));
123#if DEBUG
124		printf("addr str: %s\n", cp);
125		printf("addr ip6: %s\n", addr_to_numeric((numeric_to_addr(cp))));
126		printf("addr [%d]: %s\n", i, addr_to_numeric(&(addrp[i])));
127#endif
128	}
129        if (cp) exit_error(PARAMETER_PROBLEM, "too many addresses specified");
130
131	free(buffer);
132
133#if DEBUG
134	printf("addr nr: %d\n", i);
135#endif
136
137	return i;
138}
139
140/* Initialize the match. */
141static void
142init(struct ip6t_entry_match *m, unsigned int *nfcache)
143{
144	struct ip6t_rt *rtinfo = (struct ip6t_rt *)m->data;
145
146	rtinfo->rt_type = 0x0L;
147	rtinfo->segsleft[0] = 0x0L;
148	rtinfo->segsleft[1] = 0xFFFFFFFF;
149	rtinfo->hdrlen = 0;
150	rtinfo->flags = 0;
151	rtinfo->invflags = 0;
152	rtinfo->addrnr = 0;
153}
154
155/* Function which parses command options; returns true if it
156   ate an option */
157static int
158parse(int c, char **argv, int invert, unsigned int *flags,
159      const struct ip6t_entry *entry,
160      unsigned int *nfcache,
161      struct ip6t_entry_match **match)
162{
163	struct ip6t_rt *rtinfo = (struct ip6t_rt *)(*match)->data;
164
165	switch (c) {
166	case '1':
167		if (*flags & IP6T_RT_TYP)
168			exit_error(PARAMETER_PROBLEM,
169				   "Only one `--rt-type' allowed");
170		check_inverse(optarg, &invert, &optind, 0);
171		rtinfo->rt_type = parse_rt_num(argv[optind-1], "type");
172		if (invert)
173			rtinfo->invflags |= IP6T_RT_INV_TYP;
174		rtinfo->flags |= IP6T_RT_TYP;
175		*flags |= IP6T_RT_TYP;
176		break;
177	case '2':
178		if (*flags & IP6T_RT_SGS)
179			exit_error(PARAMETER_PROBLEM,
180				   "Only one `--rt-segsleft' allowed");
181		check_inverse(optarg, &invert, &optind, 0);
182		parse_rt_segsleft(argv[optind-1], rtinfo->segsleft);
183		if (invert)
184			rtinfo->invflags |= IP6T_RT_INV_SGS;
185		rtinfo->flags |= IP6T_RT_SGS;
186		*flags |= IP6T_RT_SGS;
187		break;
188	case '3':
189		if (*flags & IP6T_RT_LEN)
190			exit_error(PARAMETER_PROBLEM,
191				   "Only one `--rt-len' allowed");
192		check_inverse(optarg, &invert, &optind, 0);
193		rtinfo->hdrlen = parse_rt_num(argv[optind-1], "length");
194		if (invert)
195			rtinfo->invflags |= IP6T_RT_INV_LEN;
196		rtinfo->flags |= IP6T_RT_LEN;
197		*flags |= IP6T_RT_LEN;
198		break;
199	case '4':
200		if (*flags & IP6T_RT_RES)
201			exit_error(PARAMETER_PROBLEM,
202				   "Only one `--rt-0-res' allowed");
203		if ( !(*flags & IP6T_RT_TYP) || (rtinfo->rt_type != 0) || (rtinfo->invflags & IP6T_RT_INV_TYP) )
204			exit_error(PARAMETER_PROBLEM,
205				   "`--rt-type 0' required before `--rt-0-res'");
206		rtinfo->flags |= IP6T_RT_RES;
207		*flags |= IP6T_RT_RES;
208		break;
209	case '5':
210		if (*flags & IP6T_RT_FST)
211			exit_error(PARAMETER_PROBLEM,
212				   "Only one `--rt-0-addrs' allowed");
213		if ( !(*flags & IP6T_RT_TYP) || (rtinfo->rt_type != 0) || (rtinfo->invflags & IP6T_RT_INV_TYP) )
214			exit_error(PARAMETER_PROBLEM,
215				   "`--rt-type 0' required before `--rt-0-addrs'");
216		check_inverse(optarg, &invert, &optind, 0);
217		if (invert)
218			exit_error(PARAMETER_PROBLEM,
219				   " '!' not allowed with `--rt-0-addrs'");
220		rtinfo->addrnr = parse_addresses(argv[optind-1], rtinfo->addrs);
221		rtinfo->flags |= IP6T_RT_FST;
222		*flags |= IP6T_RT_FST;
223		break;
224	case '6':
225		if (*flags & IP6T_RT_FST_NSTRICT)
226			exit_error(PARAMETER_PROBLEM,
227				   "Only one `--rt-0-not-strict' allowed");
228		if ( !(*flags & IP6T_RT_FST) )
229			exit_error(PARAMETER_PROBLEM,
230				   "`--rt-0-addr ...' required before `--rt-0-not-strict'");
231		rtinfo->flags |= IP6T_RT_FST_NSTRICT;
232		*flags |= IP6T_RT_FST_NSTRICT;
233		break;
234	default:
235		return 0;
236	}
237
238	return 1;
239}
240
241/* Final check; we don't care. */
242static void
243final_check(unsigned int flags)
244{
245}
246
247static void
248print_nums(const char *name, u_int32_t min, u_int32_t max,
249	    int invert)
250{
251	const char *inv = invert ? "!" : "";
252
253	if (min != 0 || max != 0xFFFFFFFF || invert) {
254		printf("%s", name);
255		if (min == max) {
256			printf(":%s", inv);
257			printf("%u", min);
258		} else {
259			printf("s:%s", inv);
260			printf("%u",min);
261			printf(":");
262			printf("%u",max);
263		}
264		printf(" ");
265	}
266}
267
268static void
269print_addresses(int addrnr, struct in6_addr *addrp)
270{
271	unsigned int i;
272
273	for(i=0; i<addrnr; i++){
274		printf("%s%c", addr_to_numeric(&(addrp[i])), (i!=addrnr-1)?',':' ');
275	}
276}
277
278/* Prints out the union ip6t_matchinfo. */
279static void
280print(const struct ip6t_ip6 *ip,
281      const struct ip6t_entry_match *match, int numeric)
282{
283	const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data;
284
285	printf("rt ");
286	if (rtinfo->flags & IP6T_RT_TYP)
287	    printf("type:%s%d ", rtinfo->invflags & IP6T_RT_INV_TYP ? "!" : "",
288		    rtinfo->rt_type);
289	print_nums("segsleft", rtinfo->segsleft[0], rtinfo->segsleft[1],
290		    rtinfo->invflags & IP6T_RT_INV_SGS);
291	if (rtinfo->flags & IP6T_RT_LEN) {
292		printf("length");
293		printf(":%s", rtinfo->invflags & IP6T_RT_INV_LEN ? "!" : "");
294		printf("%u", rtinfo->hdrlen);
295		printf(" ");
296	}
297	if (rtinfo->flags & IP6T_RT_RES) printf("reserved ");
298	if (rtinfo->flags & IP6T_RT_FST) printf("0-addrs ");
299	print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs);
300	if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf("0-not-strict ");
301	if (rtinfo->invflags & ~IP6T_RT_INV_MASK)
302		printf("Unknown invflags: 0x%X ",
303		       rtinfo->invflags & ~IP6T_RT_INV_MASK);
304}
305
306/* Saves the union ip6t_matchinfo in parsable form to stdout. */
307static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
308{
309	const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data;
310
311	if (rtinfo->flags & IP6T_RT_TYP) {
312		printf("--rt-type %s%u ",
313			(rtinfo->invflags & IP6T_RT_INV_TYP) ? "! " : "",
314			rtinfo->rt_type);
315	}
316
317	if (!(rtinfo->segsleft[0] == 0
318	    && rtinfo->segsleft[1] == 0xFFFFFFFF)) {
319		printf("--rt-segsleft %s",
320			(rtinfo->invflags & IP6T_RT_INV_SGS) ? "! " : "");
321		if (rtinfo->segsleft[0]
322		    != rtinfo->segsleft[1])
323			printf("%u:%u ",
324			       rtinfo->segsleft[0],
325			       rtinfo->segsleft[1]);
326		else
327			printf("%u ",
328			       rtinfo->segsleft[0]);
329	}
330
331	if (rtinfo->flags & IP6T_RT_LEN) {
332		printf("--rt-len %s%u ",
333			(rtinfo->invflags & IP6T_RT_INV_LEN) ? "! " : "",
334			rtinfo->hdrlen);
335	}
336
337	if (rtinfo->flags & IP6T_RT_RES) printf("--rt-0-res ");
338	if (rtinfo->flags & IP6T_RT_FST) printf("--rt-0-addrs ");
339	print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs);
340	if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf("--rt-0-not-strict ");
341
342}
343
344static struct ip6tables_match rt = {
345	.name		= "rt",
346	.version	= IPTABLES_VERSION,
347	.size		= IP6T_ALIGN(sizeof(struct ip6t_rt)),
348	.userspacesize	= IP6T_ALIGN(sizeof(struct ip6t_rt)),
349	.help		= &help,
350	.init		= &init,
351	.parse		= &parse,
352	.final_check	= &final_check,
353	.print		= &print,
354	.save		= &save,
355	.extra_opts	= opts,
356};
357
358void
359_init(void)
360{
361	register_match6(&rt);
362}
363