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