Workaround netfilter header brokeness.
[iproute2.git] / tc / m_ipt.c
1 /*
2  * m_ipt.c      iptables based targets 
3  *              utilities mostly ripped from iptables <duh, its the linux way>
4  *
5  *              This program is free software; you can distribute it and/or
6  *              modify it under the terms of the GNU General Public License
7  *              as published by the Free Software Foundation; either version
8  *              2 of the License, or (at your option) any later version.
9  *
10  * Authors:  J Hadi Salim (hadi@cyberus.ca) 
11  * 
12  * TODO: bad bad hardcoding IPT_LIB_DIR and PROC_SYS_MODPROBE
13  *
14 */
15
16 #include <syslog.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <linux/types.h>
21 #include <iptables.h>
22 #include <linux/netfilter_ipv4/ip_tables.h>
23 #include "utils.h"
24 #include "tc_util.h"
25 #include <linux/tc_act/tc_ipt.h>
26 #include <stdio.h>
27 #include <dlfcn.h>
28 #include <getopt.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <netdb.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <stdarg.h>
35 #include <limits.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <sys/wait.h>
39
40 const char *pname = "tc-ipt";
41 const char *tname = "mangle";
42 const char *pversion = "0.1";
43
44 #ifndef TRUE
45 #define TRUE 1
46 #endif
47 #ifndef FALSE
48 #define FALSE 0
49 #endif
50
51 #ifndef IPT_LIB_DIR
52 #define IPT_LIB_DIR "/usr/local/lib/iptables"
53 #endif
54
55 #ifndef PROC_SYS_MODPROBE
56 #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
57 #endif
58
59 static const char *ipthooks[] = {
60         "NF_IP_PRE_ROUTING",
61         "NF_IP_LOCAL_IN",
62         "NF_IP_FORWARD",
63         "NF_IP_LOCAL_OUT",
64         "NF_IP_POST_ROUTING",
65 };
66
67 static struct option original_opts[] = {
68         {"jump", 1, 0, 'j'},
69         {0, 0, 0, 0}
70 };
71
72 static struct iptables_target *t_list = NULL;
73 static struct option *opts = original_opts;
74 static unsigned int global_option_offset = 0;
75 #define OPTION_OFFSET 256
76
77
78 void
79 register_target(struct iptables_target *me)
80 {
81 /*      fprintf(stderr, "\nDummy register_target %s \n", me->name);
82 */
83         me->next = t_list;
84         t_list = me;
85
86 }
87
88 void
89 exit_tryhelp(int status)
90 {
91         fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
92                 pname, pname);
93         exit(status);
94 }
95
96 void
97 exit_error(enum exittype status, char *msg, ...)
98 {
99         va_list args;
100
101         va_start(args, msg);
102         fprintf(stderr, "%s v%s: ", pname, pversion);
103         vfprintf(stderr, msg, args);
104         va_end(args);
105         fprintf(stderr, "\n");
106         if (status == PARAMETER_PROBLEM)
107                 exit_tryhelp(status);
108         if (status == VERSION_PROBLEM)
109                 fprintf(stderr,
110                         "Perhaps iptables or your kernel needs to be upgraded.\n");
111         exit(status);
112 }
113
114 /* stolen from iptables 1.2.11
115 They should really have them as a library so i can link to them
116 Email them next time i remember
117 */
118
119 char *
120 addr_to_dotted(const struct in_addr *addrp)
121 {
122         static char buf[20];
123         const unsigned char *bytep;
124
125         bytep = (const unsigned char *) &(addrp->s_addr);
126         sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
127         return buf;
128 }
129
130 int string_to_number_ll(const char *s, unsigned long long min, 
131                         unsigned long long max,
132                  unsigned long long *ret)
133 {
134         unsigned long long number;
135         char *end;
136
137         /* Handle hex, octal, etc. */
138         errno = 0;
139         number = strtoull(s, &end, 0);
140         if (*end == '\0' && end != s) {
141                 /* we parsed a number, let's see if we want this */
142                 if (errno != ERANGE && min <= number && (!max || number <= max)) {
143                         *ret = number;
144                         return 0;
145                 }
146         }
147         return -1;
148 }
149
150 int string_to_number_l(const char *s, unsigned long min, unsigned long max,
151                        unsigned long *ret)
152 {
153         int result;
154         unsigned long long number;
155
156         result = string_to_number_ll(s, min, max, &number);
157         *ret = (unsigned long)number;
158
159         return result;
160 }
161
162 int string_to_number(const char *s, unsigned int min, unsigned int max,
163                 unsigned int *ret)
164 {
165         int result;
166         unsigned long number;
167
168         result = string_to_number_l(s, min, max, &number);
169         *ret = (unsigned int)number;
170
171         return result;
172 }
173
174 static void free_opts(struct option *opts)
175 {
176         if (opts != original_opts) {
177                 free(opts);
178                 opts = original_opts;
179                 global_option_offset = 0;
180         }
181 }
182
183 static struct option *
184 merge_options(struct option *oldopts, const struct option *newopts,
185               unsigned int *option_offset)
186 {
187         struct option *merge;
188         unsigned int num_old, num_new, i;
189
190         for (num_old = 0; oldopts[num_old].name; num_old++) ;
191         for (num_new = 0; newopts[num_new].name; num_new++) ;
192
193         *option_offset = global_option_offset + OPTION_OFFSET;
194
195         merge = malloc(sizeof (struct option) * (num_new + num_old + 1));
196         memcpy(merge, oldopts, num_old * sizeof (struct option));
197         for (i = 0; i < num_new; i++) {
198                 merge[num_old + i] = newopts[i];
199                 merge[num_old + i].val += *option_offset;
200         }
201         memset(merge + num_old + num_new, 0, sizeof (struct option));
202
203         return merge;
204 }
205
206 static void *
207 fw_calloc(size_t count, size_t size)
208 {
209         void *p;
210
211         if ((p = (void *) calloc(count, size)) == NULL) {
212                 perror("iptables: calloc failed");
213                 exit(1);
214         }
215         return p;
216 }
217
218 static struct iptables_target *
219 find_t(char *name)
220 {
221         struct iptables_target *m;
222         for (m = t_list; m; m = m->next) {
223                 if (strcmp(m->name, name) == 0)
224                         return m;
225         }
226
227         return NULL;
228 }
229
230 static struct iptables_target *
231 get_target_name(char *name)
232 {
233         void *handle;
234         char *error;
235         char *new_name, *lname;
236         struct iptables_target *m;
237
238         char path[sizeof (IPT_LIB_DIR) + sizeof ("/libipt_.so") + strlen(name)];
239
240         new_name = malloc(strlen(name) + 1);
241         lname = malloc(strlen(name) + 1);
242         if (new_name)
243                 memset(new_name, '\0', strlen(name) + 1);
244         else
245                 exit_error(PARAMETER_PROBLEM, "get_target_name");
246
247         if (lname)
248                 memset(lname, '\0', strlen(name) + 1);
249         else
250                 exit_error(PARAMETER_PROBLEM, "get_target_name");
251
252         strcpy(new_name, name);
253         strcpy(lname, name);
254
255         if (isupper(lname[0])) {
256                 int i;
257                 for (i = 0; i < strlen(name); i++) {
258                         lname[i] = tolower(lname[i]);
259                 }
260         }
261
262         if (islower(new_name[0])) {
263                 int i;
264                 for (i = 0; i < strlen(new_name); i++) {
265                         new_name[i] = toupper(new_name[i]);
266                 }
267         }
268
269         sprintf(path, IPT_LIB_DIR "/libipt_%s.so", new_name);
270         handle = dlopen(path, RTLD_LAZY);
271         if (!handle) {
272                 sprintf(path, IPT_LIB_DIR "/libipt_%s.so", lname);
273                 handle = dlopen(path, RTLD_LAZY);
274                 if (!handle) {
275                         fputs(dlerror(), stderr);
276                         printf("\n");
277                         return NULL;
278                 }
279         }
280
281         m = dlsym(handle, new_name);
282         if ((error = dlerror()) != NULL) {
283                 m = (struct iptables_target *) dlsym(handle, lname);
284                 if ((error = dlerror()) != NULL) {
285                         m = find_t(new_name);
286                         if (NULL == m) {
287                                 m = find_t(lname);
288                                 if (NULL == m) {
289                                         fputs(error, stderr);
290                                         fprintf(stderr, "\n");
291                                         dlclose(handle);
292                                         return NULL;
293                                 }
294                         }
295                 }
296         }
297
298         return m;
299 }
300
301
302 struct in_addr *dotted_to_addr(const char *dotted)
303 {
304         static struct in_addr addr;
305         unsigned char *addrp;
306         char *p, *q;
307         unsigned int onebyte;
308         int i;
309         char buf[20];
310
311         /* copy dotted string, because we need to modify it */
312         strncpy(buf, dotted, sizeof (buf) - 1);
313         addrp = (unsigned char *) &(addr.s_addr);
314
315         p = buf;
316         for (i = 0; i < 3; i++) {
317                 if ((q = strchr(p, '.')) == NULL)
318                         return (struct in_addr *) NULL;
319
320                 *q = '\0';
321                 if (string_to_number(p, 0, 255, &onebyte) == -1)
322                         return (struct in_addr *) NULL;
323
324                 addrp[i] = (unsigned char) onebyte;
325                 p = q + 1;
326         }
327
328         /* we've checked 3 bytes, now we check the last one */
329         if (string_to_number(p, 0, 255, &onebyte) == -1)
330                 return (struct in_addr *) NULL;
331
332         addrp[3] = (unsigned char) onebyte;
333
334         return &addr;
335 }
336
337 static void set_revision(char *name, u_int8_t revision)
338 {
339         /* Old kernel sources don't have ".revision" field,
340         *  but we stole a byte from name. */
341         name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
342         name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
343 }
344
345 /* 
346  * we may need to check for version mismatch
347 */
348 int
349 build_st(struct iptables_target *target, struct ipt_entry_target *t)
350 {
351         unsigned int nfcache = 0;
352
353         if (target) {
354                 size_t size;
355
356                 size =
357                     IPT_ALIGN(sizeof (struct ipt_entry_target)) + target->size;
358
359                 if (NULL == t) {
360                         target->t = fw_calloc(1, size);
361                         target->t->u.target_size = size;
362
363                         if (target->init != NULL)
364                                 target->init(target->t, &nfcache);
365                         set_revision(target->t->u.user.name, target->revision);
366                 } else {
367                         target->t = t;
368                 }
369                 strcpy(target->t->u.user.name, target->name);
370                 return 0;
371         }
372
373         return -1;
374 }
375
376 static int parse_ipt(struct action_util *a,int *argc_p, 
377                      char ***argv_p, int tca_id, struct nlmsghdr *n)
378 {
379         struct iptables_target *m = NULL;
380         struct ipt_entry fw;
381         struct rtattr *tail;
382         int c;
383         int rargc = *argc_p;
384         char **argv = *argv_p;
385         int argc = 0, iargc = 0;
386         char k[16];
387         int res = -1;
388         int size = 0;
389         int iok = 0, ok = 0;
390         __u32 hook = 0, index = 0;
391         res = 0;
392
393         {
394                 int i;
395                 for (i = 0; i < rargc; i++) {
396                         if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) {
397                                 break;
398                         }
399                 }
400                 iargc = argc = i;
401         }
402
403         if (argc <= 2) {
404                 fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc);
405                 return -1;
406         }
407
408         while (1) {
409                 c = getopt_long(argc, argv, "j:", opts, NULL);
410                 if (c == -1)
411                         break;
412                 switch (c) {
413                 case 'j':
414                         m = get_target_name(optarg);
415                         if (NULL != m) {
416
417                                 if (0 > build_st(m, NULL)) {
418                                         printf(" %s error \n", m->name);
419                                         return -1;
420                                 }
421                                 opts =
422                                     merge_options(opts, m->extra_opts,
423                                                   &m->option_offset);
424                         } else {
425                                 fprintf(stderr," failed to find target %s\n\n", optarg);
426                                 return -1;
427                         }
428                         ok++;
429                         break;
430
431                 default:
432                         memset(&fw, 0, sizeof (fw));
433                         if (m) {
434                                 m->parse(c - m->option_offset, argv, 0,
435                                          &m->tflags, NULL, &m->t);
436                         } else {
437                                 fprintf(stderr," failed to find target %s\n\n", optarg);
438                                 return -1;
439
440                         }
441                         ok++;
442                         break;
443
444                 }
445         }
446
447         if (iargc > optind) {
448                 if (matches(argv[optind], "index") == 0) {
449                         if (get_u32(&index, argv[optind + 1], 10)) {
450                                 fprintf(stderr, "Illegal \"index\"\n");
451                                 free_opts(opts);
452                                 return -1;
453                         }
454                         iok++;
455
456                         optind += 2;
457                 }
458         }
459
460         if (!ok && !iok) {
461                 fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv);
462                 return -1;
463         }
464
465         /* check that we passed the correct parameters to the target */
466         if (m)
467                 m->final_check(m->tflags);
468
469         {
470                 struct tcmsg *t = NLMSG_DATA(n);
471                 if (t->tcm_parent != TC_H_ROOT
472                     && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) {
473                         hook = NF_IP_PRE_ROUTING;
474                 } else {
475                         hook = NF_IP_POST_ROUTING;
476                 }
477         }
478
479         tail = NLMSG_TAIL(n);
480         addattr_l(n, MAX_MSG, tca_id, NULL, 0);
481         fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]);
482         fprintf(stdout, "\ttarget: ");
483
484         if (m)
485                 m->print(NULL, m->t, 0);
486         fprintf(stdout, " index %d\n", index);
487
488         if (strlen(tname) > 16) {
489                 size = 16;
490                 k[15] = 0;
491         } else {
492                 size = 1 + strlen(tname);
493         }
494         strncpy(k, tname, size);
495
496         addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size);
497         addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4);
498         addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4);
499         if (m)
500                 addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size);
501         tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
502
503         argc -= optind;
504         argv += optind;
505         *argc_p = rargc - iargc;
506         *argv_p = argv;
507         
508         optind = 1;
509         free_opts(opts);
510
511         return 0;
512
513 }
514
515 static int
516 print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
517 {
518         struct rtattr *tb[TCA_IPT_MAX + 1];
519         struct ipt_entry_target *t = NULL;
520
521         if (arg == NULL)
522                 return -1;
523
524         parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
525
526         if (tb[TCA_IPT_TABLE] == NULL) {
527                 fprintf(f, "[NULL ipt table name ] assuming mangle ");
528         } else {
529                 fprintf(f, "tablename: %s ",
530                         (char *) RTA_DATA(tb[TCA_IPT_TABLE]));
531         }
532
533         if (tb[TCA_IPT_HOOK] == NULL) {
534                 fprintf(f, "[NULL ipt hook name ]\n ");
535                 return -1;
536         } else {
537                 __u32 hook;
538                 hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
539                 fprintf(f, " hook: %s \n", ipthooks[hook]);
540         }
541
542         if (tb[TCA_IPT_TARG] == NULL) {
543                 fprintf(f, "\t[NULL ipt target parameters ] \n");
544                 return -1;
545         } else {
546                 struct iptables_target *m = NULL;
547                 t = RTA_DATA(tb[TCA_IPT_TARG]);
548                 m = get_target_name(t->u.user.name);
549                 if (NULL != m) {
550                         if (0 > build_st(m, t)) {
551                                 fprintf(stderr, " %s error \n", m->name);
552                                 return -1;
553                         }
554
555                         opts =
556                             merge_options(opts, m->extra_opts,
557                                           &m->option_offset);
558                 } else {
559                         fprintf(stderr, " failed to find target %s\n\n",
560                                 t->u.user.name);
561                         return -1;
562                 }
563                 fprintf(f, "\ttarget ");
564                 m->print(NULL, m->t, 0);
565                 if (tb[TCA_IPT_INDEX] == NULL) {
566                         fprintf(f, " [NULL ipt target index ]\n");
567                 } else {
568                         __u32 index;
569                         index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
570                         fprintf(f, " \n\tindex %d", index);
571                 }
572
573                 if (tb[TCA_IPT_CNT]) {
574                         struct tc_cnt *c  = RTA_DATA(tb[TCA_IPT_CNT]);;
575                         fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
576                 }
577                 if (show_stats) {
578                         if (tb[TCA_IPT_TM]) {
579                                 struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
580                                 print_tm(f,tm);
581                         }
582                 } 
583                 fprintf(f, " \n");
584
585         }
586         free_opts(opts);
587
588         return 0;
589 }
590
591 struct action_util ipt_action_util = {
592         .id = "ipt",
593         .parse_aopt = parse_ipt,
594         .print_aopt = print_ipt,
595 };
596