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