m_ipt.c revision 65840e27ba0ad201fbcb143d38f94197963645d5
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 76void 77register_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 86void 87exit_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 94void 95exit_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 113They should really have them as a library so i can link to them 114Email them next time i remember 115*/ 116 117char * 118addr_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 128int 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 148int 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 160int 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 172static struct option * 173copy_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 186static struct option * 187merge_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 209static void * 210fw_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 221static struct iptables_target * 222find_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 233static struct iptables_target * 234get_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 305struct 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 340int 341build_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 365static 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 513static int 514print_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 594struct action_util ipt_action_util = { 595 .id = "ipt", 596 .parse_aopt = parse_ipt, 597 .print_aopt = print_ipt, 598}; 599 600