m_ipt.c revision 78934000777e1f1837450ed6bfe738df1fde5a03
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 39const char *pname = "tc-ipt"; 40const char *tname = "mangle"; 41const 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 58static 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 66static struct option original_opts[] = { 67 {"jump", 1, 0, 'j'}, 68 {0, 0, 0, 0} 69}; 70 71static struct iptables_target *t_list = NULL; 72static 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*/ 79void 80register_match(struct iptables_match *me) 81{ 82/* fprintf(stderr, "\nDummy register_match\n"); */ 83} 84 85void 86register_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 96void 97exit_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 104void 105exit_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 123They should really have them as a library so i can link to them 124Email them next time i remember 125*/ 126 127char * 128addr_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 138int 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 158int 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 170int 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 183static int 184string_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 204static struct option * 205copy_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 218static struct option * 219merge_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 241static void * 242fw_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 254static void * 255fw_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 266static int 267check_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 281static struct iptables_target * 282find_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 293static struct iptables_target * 294get_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 365static char * 366addr_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 377struct 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 412int 413build_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 437static 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 585static int 586print_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 600 parse_rtattr_nested(tb, TCA_IPT_MAX, 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 666struct action_util ipt_action_util = { 667 .id = "ipt", 668 .parse_aopt = parse_ipt, 669 .print_aopt = print_ipt, 670}; 671 672