iptables-1.3.2-20050720
[iptables.git] / extensions / libip6t_rt.c
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. */
18 static void
19 help(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",
29 IPTABLES_VERSION, IP6T_RT_HOPS);
30 }
31
32 static 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
42 static u_int32_t
43 parse_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
66 static void
67 parse_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
85 static char *
86 addr_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
92 static struct in6_addr *
93 numeric_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
109 static int
110 parse_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. */
141 static void
142 init(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 */
157 static int
158 parse(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. */
242 static void
243 final_check(unsigned int flags)
244 {
245 }
246
247 static void
248 print_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
268 static void
269 print_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. */
279 static void
280 print(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. */
307 static 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
344 static 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
358 void
359 _init(void)
360 {
361         register_match6(&rt);
362 }