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