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 (at) 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.h> 19 #include <linux/netfilter_ipv4/ip_tables.h> 20 #include "utils.h" 21 #include "tc_util.h" 22 #include <linux/tc_act/tc_ipt.h> 23 #include <stdio.h> 24 #include <dlfcn.h> 25 #include <getopt.h> 26 #include <errno.h> 27 #include <string.h> 28 #include <netdb.h> 29 #include <stdlib.h> 30 #include <ctype.h> 31 #include <stdarg.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 xtables_target *t_list; 54 static struct option *opts = original_opts; 55 static unsigned int global_option_offset; 56 #define OPTION_OFFSET 256 57 58 char *lib_dir; 59 60 void 61 xtables_register_target(struct xtables_target *me) 62 { 63 me->next = t_list; 64 t_list = me; 65 66 } 67 68 static void exit_tryhelp(int status) 69 { 70 fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", 71 pname, pname); 72 exit(status); 73 } 74 75 static void exit_error(enum xtables_exittype status, char *msg, ...) 76 { 77 va_list args; 78 79 va_start(args, msg); 80 fprintf(stderr, "%s v%s: ", pname, pversion); 81 vfprintf(stderr, msg, args); 82 va_end(args); 83 fprintf(stderr, "\n"); 84 if (status == PARAMETER_PROBLEM) 85 exit_tryhelp(status); 86 if (status == VERSION_PROBLEM) 87 fprintf(stderr, 88 "Perhaps iptables or your kernel needs to be upgraded.\n"); 89 exit(status); 90 } 91 92 /* stolen from iptables 1.2.11 93 They should really have them as a library so i can link to them 94 Email them next time i remember 95 */ 96 97 static void free_opts(struct option *local_opts) 98 { 99 if (local_opts != original_opts) { 100 free(local_opts); 101 opts = original_opts; 102 global_option_offset = 0; 103 } 104 } 105 106 static struct option * 107 merge_options(struct option *oldopts, const struct option *newopts, 108 unsigned int *option_offset) 109 { 110 struct option *merge; 111 unsigned int num_old, num_new, i; 112 113 for (num_old = 0; oldopts[num_old].name; num_old++); 114 for (num_new = 0; newopts[num_new].name; num_new++); 115 116 *option_offset = global_option_offset + OPTION_OFFSET; 117 118 merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); 119 memcpy(merge, oldopts, num_old * sizeof(struct option)); 120 for (i = 0; i < num_new; i++) { 121 merge[num_old + i] = newopts[i]; 122 merge[num_old + i].val += *option_offset; 123 } 124 memset(merge + num_old + num_new, 0, sizeof(struct option)); 125 126 return merge; 127 } 128 129 static void * 130 fw_calloc(size_t count, size_t size) 131 { 132 void *p; 133 134 if ((p = (void *) calloc(count, size)) == NULL) { 135 perror("iptables: calloc failed"); 136 exit(1); 137 } 138 return p; 139 } 140 141 static struct xtables_target * 142 find_t(char *name) 143 { 144 struct xtables_target *m; 145 146 for (m = t_list; m; m = m->next) { 147 if (strcmp(m->name, name) == 0) 148 return m; 149 } 150 151 return NULL; 152 } 153 154 static struct xtables_target * 155 get_target_name(const char *name) 156 { 157 void *handle; 158 char *error; 159 char *new_name, *lname; 160 struct xtables_target *m; 161 char path[strlen(lib_dir) + sizeof("/libipt_.so") + strlen(name)]; 162 163 #ifdef NO_SHARED_LIBS 164 return NULL; 165 #endif 166 167 new_name = calloc(1, strlen(name) + 1); 168 lname = calloc(1, strlen(name) + 1); 169 if (!new_name) 170 exit_error(PARAMETER_PROBLEM, "get_target_name"); 171 if (!lname) 172 exit_error(PARAMETER_PROBLEM, "get_target_name"); 173 174 strcpy(new_name, name); 175 strcpy(lname, name); 176 177 if (isupper(lname[0])) { 178 int i; 179 180 for (i = 0; i < strlen(name); i++) { 181 lname[i] = tolower(lname[i]); 182 } 183 } 184 185 if (islower(new_name[0])) { 186 int i; 187 188 for (i = 0; i < strlen(new_name); i++) { 189 new_name[i] = toupper(new_name[i]); 190 } 191 } 192 193 /* try libxt_xx first */ 194 sprintf(path, "%s/libxt_%s.so", lib_dir, new_name); 195 handle = dlopen(path, RTLD_LAZY); 196 if (!handle) { 197 /* try libipt_xx next */ 198 sprintf(path, "%s/libipt_%s.so", lib_dir, new_name); 199 handle = dlopen(path, RTLD_LAZY); 200 201 if (!handle) { 202 sprintf(path, "%s/libxt_%s.so", lib_dir, lname); 203 handle = dlopen(path, RTLD_LAZY); 204 } 205 206 if (!handle) { 207 sprintf(path, "%s/libipt_%s.so", lib_dir, lname); 208 handle = dlopen(path, RTLD_LAZY); 209 } 210 /* ok, lets give up .. */ 211 if (!handle) { 212 fputs(dlerror(), stderr); 213 printf("\n"); 214 free(new_name); 215 free(lname); 216 return NULL; 217 } 218 } 219 220 m = dlsym(handle, new_name); 221 if ((error = dlerror()) != NULL) { 222 m = (struct xtables_target *) dlsym(handle, lname); 223 if ((error = dlerror()) != NULL) { 224 m = find_t(new_name); 225 if (m == NULL) { 226 m = find_t(lname); 227 if (m == NULL) { 228 fputs(error, stderr); 229 fprintf(stderr, "\n"); 230 dlclose(handle); 231 free(new_name); 232 free(lname); 233 return NULL; 234 } 235 } 236 } 237 } 238 239 free(new_name); 240 free(lname); 241 return m; 242 } 243 244 static void set_revision(char *name, u_int8_t revision) 245 { 246 /* Old kernel sources don't have ".revision" field, 247 * but we stole a byte from name. */ 248 name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0'; 249 name[IPT_FUNCTION_MAXNAMELEN - 1] = revision; 250 } 251 252 /* 253 * we may need to check for version mismatch 254 */ 255 static int build_st(struct xtables_target *target, struct ipt_entry_target *t) 256 { 257 if (target) { 258 size_t size; 259 260 size = 261 XT_ALIGN(sizeof(struct ipt_entry_target)) + target->size; 262 263 if (t == NULL) { 264 target->t = fw_calloc(1, size); 265 target->t->u.target_size = size; 266 267 if (target->init != NULL) 268 target->init(target->t); 269 set_revision(target->t->u.user.name, target->revision); 270 } else { 271 target->t = t; 272 } 273 strcpy(target->t->u.user.name, target->name); 274 return 0; 275 } 276 277 return -1; 278 } 279 280 static int parse_ipt(struct action_util *a, int *argc_p, 281 char ***argv_p, int tca_id, struct nlmsghdr *n) 282 { 283 struct xtables_target *m = NULL; 284 struct ipt_entry fw; 285 struct rtattr *tail; 286 int c; 287 int rargc = *argc_p; 288 char **argv = *argv_p; 289 int argc = 0, iargc = 0; 290 char k[16]; 291 int size = 0; 292 int iok = 0, ok = 0; 293 __u32 hook = 0, index = 0; 294 295 lib_dir = getenv("IPTABLES_LIB_DIR"); 296 if (!lib_dir) 297 lib_dir = IPT_LIB_DIR; 298 299 { 300 int i; 301 302 for (i = 0; i < rargc; i++) { 303 if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { 304 break; 305 } 306 } 307 iargc = argc = i; 308 } 309 310 if (argc <= 2) { 311 fprintf(stderr, "bad arguments to ipt %d vs %d\n", argc, rargc); 312 return -1; 313 } 314 315 while (1) { 316 c = getopt_long(argc, argv, "j:", opts, NULL); 317 if (c == -1) 318 break; 319 switch (c) { 320 case 'j': 321 m = get_target_name(optarg); 322 if (m != NULL) { 323 324 if (build_st(m, NULL) < 0) { 325 printf(" %s error\n", m->name); 326 return -1; 327 } 328 opts = 329 merge_options(opts, m->extra_opts, 330 &m->option_offset); 331 } else { 332 fprintf(stderr, " failed to find target %s\n\n", optarg); 333 return -1; 334 } 335 ok++; 336 break; 337 338 default: 339 memset(&fw, 0, sizeof(fw)); 340 if (m) { 341 m->parse(c - m->option_offset, argv, 0, 342 &m->tflags, NULL, &m->t); 343 } else { 344 fprintf(stderr, " failed to find target %s\n\n", optarg); 345 return -1; 346 347 } 348 ok++; 349 break; 350 351 } 352 } 353 354 if (iargc > optind) { 355 if (matches(argv[optind], "index") == 0) { 356 if (get_u32(&index, argv[optind + 1], 10)) { 357 fprintf(stderr, "Illegal \"index\"\n"); 358 free_opts(opts); 359 return -1; 360 } 361 iok++; 362 363 optind += 2; 364 } 365 } 366 367 if (!ok && !iok) { 368 fprintf(stderr, " ipt Parser BAD!! (%s)\n", *argv); 369 return -1; 370 } 371 372 /* check that we passed the correct parameters to the target */ 373 if (m) 374 m->final_check(m->tflags); 375 376 { 377 struct tcmsg *t = NLMSG_DATA(n); 378 379 if (t->tcm_parent != TC_H_ROOT 380 && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { 381 hook = NF_IP_PRE_ROUTING; 382 } else { 383 hook = NF_IP_POST_ROUTING; 384 } 385 } 386 387 tail = NLMSG_TAIL(n); 388 addattr_l(n, MAX_MSG, tca_id, NULL, 0); 389 fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]); 390 fprintf(stdout, "\ttarget: "); 391 392 if (m) 393 m->print(NULL, m->t, 0); 394 fprintf(stdout, " index %d\n", index); 395 396 if (strlen(tname) > 16) { 397 size = 16; 398 k[15] = 0; 399 } else { 400 size = 1 + strlen(tname); 401 } 402 strncpy(k, tname, size); 403 404 addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size); 405 addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4); 406 addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4); 407 if (m) 408 addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size); 409 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 410 411 argc -= optind; 412 argv += optind; 413 *argc_p = rargc - iargc; 414 *argv_p = argv; 415 416 optind = 0; 417 free_opts(opts); 418 /* Clear flags if target will be used again */ 419 m->tflags = 0; 420 m->used = 0; 421 /* Free allocated memory */ 422 if (m->t) 423 free(m->t); 424 425 426 return 0; 427 428 } 429 430 static int 431 print_ipt(struct action_util *au, FILE * f, struct rtattr *arg) 432 { 433 struct rtattr *tb[TCA_IPT_MAX + 1]; 434 struct ipt_entry_target *t = NULL; 435 436 if (arg == NULL) 437 return -1; 438 439 lib_dir = getenv("IPTABLES_LIB_DIR"); 440 if (!lib_dir) 441 lib_dir = IPT_LIB_DIR; 442 443 parse_rtattr_nested(tb, TCA_IPT_MAX, arg); 444 445 if (tb[TCA_IPT_TABLE] == NULL) { 446 fprintf(f, "[NULL ipt table name ] assuming mangle "); 447 } else { 448 fprintf(f, "tablename: %s ", 449 rta_getattr_str(tb[TCA_IPT_TABLE])); 450 } 451 452 if (tb[TCA_IPT_HOOK] == NULL) { 453 fprintf(f, "[NULL ipt hook name ]\n "); 454 return -1; 455 } else { 456 __u32 hook; 457 458 hook = rta_getattr_u32(tb[TCA_IPT_HOOK]); 459 fprintf(f, " hook: %s\n", ipthooks[hook]); 460 } 461 462 if (tb[TCA_IPT_TARG] == NULL) { 463 fprintf(f, "\t[NULL ipt target parameters ]\n"); 464 return -1; 465 } else { 466 struct xtables_target *m = NULL; 467 468 t = RTA_DATA(tb[TCA_IPT_TARG]); 469 m = get_target_name(t->u.user.name); 470 if (m != NULL) { 471 if (build_st(m, t) < 0) { 472 fprintf(stderr, " %s error\n", m->name); 473 return -1; 474 } 475 476 opts = 477 merge_options(opts, m->extra_opts, 478 &m->option_offset); 479 } else { 480 fprintf(stderr, " failed to find target %s\n\n", 481 t->u.user.name); 482 return -1; 483 } 484 fprintf(f, "\ttarget "); 485 m->print(NULL, m->t, 0); 486 if (tb[TCA_IPT_INDEX] == NULL) { 487 fprintf(f, " [NULL ipt target index ]\n"); 488 } else { 489 __u32 index; 490 491 index = rta_getattr_u32(tb[TCA_IPT_INDEX]); 492 fprintf(f, "\n\tindex %u", index); 493 } 494 495 if (tb[TCA_IPT_CNT]) { 496 struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]); 497 498 fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); 499 } 500 if (show_stats) { 501 if (tb[TCA_IPT_TM]) { 502 struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); 503 504 print_tm(f, tm); 505 } 506 } 507 fprintf(f, "\n"); 508 509 } 510 free_opts(opts); 511 512 return 0; 513 } 514 515 struct action_util ipt_action_util = { 516 .id = "ipt", 517 .parse_aopt = parse_ipt, 518 .print_aopt = print_ipt, 519 }; 520