]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/m_ipt.c
Use NLMSG_TAIL
[mirror_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 #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 = NLMSG_TAIL(n);
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 = (void *) NLMSG_TAIL(n) - (void *) tail;
573
574 argc -= optind;
575 argv += optind;
576 *argc_p = rargc - iargc;
577 *argv_p = argv;
578
579 optind = 1;
580
581 return 0;
582
583 }
584
585 static int
586 print_ipt(struct action_util *au,FILE * f, struct rtattr *arg)
587 {
588 struct rtattr *tb[TCA_IPT_MAX + 1];
589 struct ipt_entry_target *t = NULL;
590 struct option *opts;
591
592 if (arg == NULL)
593 return -1;
594
595 opts = copy_options(original_opts);
596
597 if (NULL == opts)
598 return -1;
599 memset(tb, 0, sizeof (tb));
600 parse_rtattr(tb, TCA_IPT_MAX, RTA_DATA(arg), RTA_PAYLOAD(arg));
601
602 if (tb[TCA_IPT_TABLE] == NULL) {
603 fprintf(f, "[NULL ipt table name ] assuming mangle ");
604 } else {
605 fprintf(f, "tablename: %s ",
606 (char *) RTA_DATA(tb[TCA_IPT_TABLE]));
607 }
608
609 if (tb[TCA_IPT_HOOK] == NULL) {
610 fprintf(f, "[NULL ipt hook name ]\n ");
611 return -1;
612 } else {
613 __u32 hook;
614 hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]);
615 fprintf(f, " hook: %s \n", ipthooks[hook]);
616 }
617
618 if (tb[TCA_IPT_TARG] == NULL) {
619 fprintf(f, "\t[NULL ipt target parameters ] \n");
620 return -1;
621 } else {
622 struct iptables_target *m = NULL;
623 t = RTA_DATA(tb[TCA_IPT_TARG]);
624 m = get_target_name(t->u.user.name);
625 if (NULL != m) {
626 if (0 > build_st(m, t)) {
627 fprintf(stderr, " %s error \n", m->name);
628 return -1;
629 }
630
631 opts =
632 merge_options(opts, m->extra_opts,
633 &m->option_offset);
634 } else {
635 fprintf(stderr, " failed to find target %s\n\n",
636 t->u.user.name);
637 return -1;
638 }
639 fprintf(f, "\ttarget ");
640 m->print(NULL, m->t, 0);
641 if (tb[TCA_IPT_INDEX] == NULL) {
642 fprintf(f, " [NULL ipt target index ]\n");
643 } else {
644 __u32 index;
645 index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]);
646 fprintf(f, " \n\tindex %d", index);
647 }
648
649 if (tb[TCA_IPT_CNT]) {
650 struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);;
651 fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
652 }
653 if (show_stats) {
654 if (tb[TCA_IPT_TM]) {
655 struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
656 print_tm(f,tm);
657 }
658 }
659 fprintf(f, " \n");
660
661 }
662
663 return 0;
664 }
665
666 struct action_util ipt_action_util = {
667 .id = "ipt",
668 .parse_aopt = parse_ipt,
669 .print_aopt = print_ipt,
670 };
671