1 #include <linux/netfilter.h> 2 #include <sys/types.h> 3 #include <sys/mman.h> 4 #include <pthread.h> 5 #include <libipq.h> 6 #include <unistd.h> 7 #include <stdlib.h> 8 #include <stdint.h> 9 #include <string.h> 10 #include <assert.h> 11 #include <stdio.h> 12 #include <time.h> 13 #include <tcl.h> 14 15 #ifdef HAVE_CONFIG_H 16 #include "config.h" 17 #endif 18 19 /* 20 * Maximum size of packet to process 21 */ 22 #ifndef PNFQ_PKT_BUF_LEN 23 #define PNFQ_PKT_BUF_LEN 65535 24 #endif 25 26 /* 27 * Abort after how many consecutive LIBIPQ errors 28 */ 29 #ifndef PNFQ_MAX_READ_ERRORS 30 #define PNFQ_MAX_READ_ERRORS 20 31 #endif 32 33 /* 34 * Default number of Worker threads 35 */ 36 #ifndef PNFQ_NUM_THREADS 37 #define PNFQ_NUM_THREADS 4 38 #endif 39 40 /* 41 * Sanity warning 42 */ 43 #if defined(USE_TCL_PKT_HANDLER) && !defined(USE_THREADED_TCL) 44 #warning ************************************* 45 #warning *** THIS IS NOT AN OPTIMAL BUILD! *** 46 #warning ************************************* 47 #warning *** YOU SHOULD USE A THREADED TCL *** 48 #warning *** FOR A RELEASE BUILD. REFER TO *** 49 #warning *** <rootdir>/build/build_release *** 50 #warning *** FOR ADDITIONAL INFO AND HELP. *** 51 #warning ************************************* 52 #ifndef USE_FORCE 53 #error ADD "-DUSE_FORCE=1" TO THE CFLAGS LINE IN THE "Makefile" TO FORCE BUILD 54 #endif 55 #endif 56 57 typedef enum { 58 PNFQ_CHECK_ACCEPT = NF_ACCEPT, 59 PNFQ_CHECK_DROP = NF_DROP, 60 PNFQ_CHECK_CONTINUE = (PNFQ_CHECK_ACCEPT + PNFQ_CHECK_DROP + 1), 61 PNFQ_CHECK_DEFAULT = (PNFQ_CHECK_CONTINUE + PNFQ_CHECK_ACCEPT + PNFQ_CHECK_DROP + 1) 62 } pnfq_check_result_t; 63 64 struct pnfq_check_node; 65 struct pnfq_check_node { 66 pnfq_check_result_t action; 67 struct pnfq_check_node *_zero; 68 struct pnfq_check_node *_one; 69 }; 70 71 struct pnfq_thread_info { 72 struct ipq_handle *handle; 73 74 #ifdef USE_TCL_PKT_HANDLER 75 #ifndef USE_THREADED_TCL 76 pthread_mutex_t *tcl_mutex; 77 #endif 78 const char *tcl_scpdir; 79 #endif 80 81 int thread_id; 82 }; 83 84 #ifdef USE_IPV6 85 typedef struct { 86 uint32_t a[4]; 87 } uint128_t; 88 89 typedef enum { 90 PNFQ_ADDR_TYPE_IPV4, 91 PNFQ_ADDR_TYPE_IPV6, 92 PNFQ_ADDR_TYPE_UNKNOWN 93 } pnfq_addr_type_t; 94 95 typedef struct { 96 pnfq_addr_type_t type; 97 union { 98 uint32_t ipv4; 99 uint128_t ipv6; 100 } addr; 101 } pnfq_addr_t; 102 103 struct pnfq_check_node pnfq_check_tree_v6 = {PNFQ_CHECK_CONTINUE, NULL, NULL}; 104 #else 105 typedef uint32_t pnfq_addr_t; 106 #endif 107 108 struct pnfq_check_node pnfq_check_tree_v4 = {PNFQ_CHECK_CONTINUE, NULL, NULL}; 109 110 /* 111 * CODE 112 */ 113 void print_help(void) { 114 printf("Usage: pnfq [-Vhcl] [-T <tcl_script_dir>] [-t <num_threads>]\n"); 115 printf(" -V Print out PNFQ Version information\n"); 116 printf(" -h Print out usage information (this information)\n"); 117 printf(" -c Print out build information\n"); 118 printf(" -l Lock all memory pages into physical RAM (DANGEROUS)\n"); 119 printf(" -T <dir> Set the directory where Tcl helper scripts live\n"); 120 printf(" -t <num> Set the number of worker threads to create\n"); 121 122 return; 123 } 124 125 126 /* 127 * IP Maniuplation Functions 128 */ 129 static inline char *ipv4_to_str(uint32_t addr, char *buf, size_t buflen) { 130 snprintf(buf, buflen, "%i.%i.%i.%i", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); 131 return(buf); 132 } 133 static inline uint32_t str_to_ipv4(char *ip) { 134 uint32_t ret = 0; 135 char *p; 136 int i; 137 138 p = ip; 139 140 for (i = 0; i < 4; i++) { 141 ret <<= 8; 142 ret += strtoul(p, &p, 10); 143 144 if (i != 3) { 145 if (!p) { 146 return(0); 147 } 148 if (*p != '.') { 149 return(0); 150 } 151 } 152 p++; 153 } 154 155 return(ret); 156 } 157 #ifdef USE_IPV6 158 static inline char *ipv6_to_str(uint128_t addr, char *buf, size_t buflen) { 159 snprintf(buf, buflen, "%x:%x:%x:%x:%x:%x:%x:%x", 160 (addr.a[0] >> 16) & 0xffff, 161 addr.a[0] & 0xffff, 162 (addr.a[1] >> 16) & 0xffff, 163 addr.a[1] & 0xffff, 164 (addr.a[2] >> 16) & 0xffff, 165 addr.a[2] & 0xffff, 166 (addr.a[3] >> 16) & 0xffff, 167 addr.a[3] & 0xffff); 168 return(buf); 169 } 170 /* 171 * NOTE: Does not support "captured zeros" 172 */ 173 static inline uint128_t str_to_ipv6(char *ip) { 174 uint128_t ret = {{0}}, error_ret = {{0}}; 175 char *p; 176 int i; 177 178 p = ip; 179 180 for (i = 0; i < 8; i++) { 181 ret.a[i/2] <<= 16; 182 ret.a[i/2] += strtoul(p, &p, 16); 183 184 if (i != 7) { 185 if (!p) { 186 return(error_ret); 187 } 188 if (*p != ':') { 189 return(error_ret); 190 } 191 } 192 p++; 193 } 194 195 return(ret); 196 } 197 static inline char *ip_to_str(pnfq_addr_t addr, char *buf, size_t buflen) { 198 switch (addr.type) { 199 case PNFQ_ADDR_TYPE_IPV4: 200 return(ipv4_to_str(addr.addr.ipv4, buf, buflen)); 201 case PNFQ_ADDR_TYPE_IPV6: 202 return(ipv6_to_str(addr.addr.ipv6, buf, buflen)); 203 case PNFQ_ADDR_TYPE_UNKNOWN: 204 return(""); 205 } 206 207 return(""); 208 } 209 static inline pnfq_addr_t str_to_ip(char *ip) { 210 pnfq_addr_t ret; 211 212 if (strchr(ip, '.') != NULL) { 213 ret.type = PNFQ_ADDR_TYPE_IPV4; 214 ret.addr.ipv4 = str_to_ipv4(ip); 215 } else if (strchr(ip, ':') != NULL) { 216 ret.type = PNFQ_ADDR_TYPE_IPV6; 217 ret.addr.ipv6 = str_to_ipv6(ip); 218 } else { 219 ret.type = PNFQ_ADDR_TYPE_UNKNOWN; 220 } 221 222 return(ret); 223 } 224 #else 225 #define str_to_ip(x) str_to_ipv4(x) 226 #define ip_to_str(x, y, z) ipv4_to_str(x, y, z) 227 #endif 228 229 /* 230 * Packet Payload Inspection Routines 231 */ 232 static inline void ipv4_pkt_info(const unsigned char *payload, uint32_t *src, uint32_t *dst) { 233 /* IP Address Bytes 13 - 16 of IP header */ 234 *src = (payload[12] << 24) | (payload[13] << 16) | (payload[14] << 8) | payload[15]; 235 *dst = (payload[16] << 24) | (payload[17] << 16) | (payload[18] << 8) | payload[19]; 236 237 return; 238 } 239 #ifdef USE_IPV6 240 static inline void ipv6_pkt_info(const unsigned char *payload, uint128_t *src, uint128_t *dst) { 241 src->a[0] = (payload[8] << 24) | (payload[9] << 16) | (payload[10] << 8) | payload[11]; 242 src->a[1] = (payload[12] << 24) | (payload[13] << 16) | (payload[14] << 8) | payload[15]; 243 src->a[2] = (payload[16] << 24) | (payload[17] << 16) | (payload[18] << 8) | payload[19]; 244 src->a[3] = (payload[20] << 24) | (payload[21] << 16) | (payload[22] << 8) | payload[23]; 245 246 dst->a[0] = (payload[24] << 24) | (payload[25] << 16) | (payload[26] << 8) | payload[27]; 247 dst->a[1] = (payload[28] << 24) | (payload[29] << 16) | (payload[30] << 8) | payload[31]; 248 dst->a[2] = (payload[32] << 24) | (payload[33] << 16) | (payload[34] << 8) | payload[35]; 249 dst->a[3] = (payload[36] << 24) | (payload[37] << 16) | (payload[38] << 8) | payload[39]; 250 251 return; 252 } 253 #endif 254 static inline void pkt_info(ipq_packet_msg_t *pkt, pnfq_addr_t *src, pnfq_addr_t *dst) { 255 unsigned char *payload; 256 int ip_header_length, header_size;; 257 int version, data_len; 258 259 assert(pkt != NULL); 260 261 assert(src != NULL); 262 263 assert(dst != NULL); 264 265 data_len = pkt->data_len; 266 if (data_len <= 19) { 267 return; 268 } 269 270 /* Get IP Version Byte 1 of IP Header */ 271 payload = pkt->payload; 272 version = payload[0] & 0xF0; 273 version >>= 4; 274 275 /* Get IP Header length Byte 2 of IP Header 276 * Header length is usually 20, or 5 32-bit words */ 277 ip_header_length = payload[0] & 0x0F; 278 header_size = ip_header_length * 4; 279 280 if (data_len < header_size) { 281 return; 282 } 283 284 switch (version) { 285 #ifdef USE_IPV6 286 case 4: 287 src->type = PNFQ_ADDR_TYPE_IPV4; 288 dst->type = PNFQ_ADDR_TYPE_IPV4; 289 ipv4_pkt_info(payload, &src->addr.ipv4, &dst->addr.ipv4); 290 break; 291 case 6: 292 src->type = PNFQ_ADDR_TYPE_IPV6; 293 dst->type = PNFQ_ADDR_TYPE_IPV6; 294 ipv6_pkt_info(payload, &src->addr.ipv6, &dst->addr.ipv6); 295 break; 296 #else 297 case 4: 298 ipv4_pkt_info(payload, src, dst); 299 break; 300 #endif 301 default: 302 break; 303 } 304 } 305 306 /* 307 * Packet Filtering Binary Tree Manipulation Routines 308 */ 309 static inline void pnfq_addr_add_ipv4(uint32_t addr, uint32_t mask, pnfq_check_result_t action) { 310 struct pnfq_check_node *currnode; 311 uint32_t bitval, maskval; 312 int bit; 313 314 assert(action != PNFQ_CHECK_DEFAULT); 315 316 currnode = &pnfq_check_tree_v4; 317 318 for (bit = -1; bit < 33; bit++) { 319 if (currnode == NULL) { 320 break; 321 } 322 323 bitval = (addr >> (32 - bit - 1)) & 1; 324 325 /* 326 * Always take the same route from the HEAD (-1'th) bit. 327 */ 328 if (bit == -1) { 329 bitval = 0; 330 } 331 332 maskval = (mask >> (32 - bit - 1)) & 1; 333 if ((maskval == 0 && bit != -1) || bit == 32) { 334 currnode->action = action; 335 break; 336 } 337 338 if (bitval == 0) { 339 if (currnode->_zero == NULL) { 340 currnode->_zero = malloc(sizeof(*currnode->_zero)); 341 currnode->_zero->action = PNFQ_CHECK_CONTINUE; 342 currnode->_zero->_zero = NULL; 343 currnode->_zero->_one = NULL; 344 } 345 currnode = currnode->_zero; 346 } else { 347 if (currnode->_one == NULL) { 348 currnode->_one = malloc(sizeof(*currnode->_one)); 349 currnode->_one->action = PNFQ_CHECK_CONTINUE; 350 currnode->_one->_zero = NULL; 351 currnode->_one->_one = NULL; 352 } 353 currnode = currnode->_one; 354 } 355 } 356 357 return; 358 } 359 static inline void pnfq_addr_del_ipv4(uint32_t addr, uint32_t mask) { 360 pnfq_addr_add_ipv4(addr, mask, PNFQ_CHECK_CONTINUE); 361 } 362 static inline pnfq_check_result_t pnfq_addr_check_ipv4(uint32_t addr) { 363 pnfq_check_result_t retval = PNFQ_CHECK_DEFAULT; 364 struct pnfq_check_node *currnode; 365 uint32_t bitval; 366 int bit; 367 368 if (addr == 0) { 369 return(PNFQ_CHECK_DROP); 370 } 371 372 currnode = &pnfq_check_tree_v4; 373 374 for (bit = -1; bit < 33; bit++) { 375 if (currnode == NULL) { 376 break; 377 } 378 379 bitval = (addr >> (32 - bit - 1)) & 1; 380 381 /* 382 * Always take the same route from the HEAD (-1'th) bit. 383 */ 384 if (bit == -1) { 385 bitval = 0; 386 } 387 388 if (currnode->action != PNFQ_CHECK_CONTINUE) { 389 retval = currnode->action; 390 } 391 392 if (bitval == 0) { 393 currnode = currnode->_zero; 394 } else { 395 currnode = currnode->_one; 396 } 397 } 398 399 return(retval); 400 } 401 #ifdef USE_IPV6 402 static inline void pnfq_addr_add_ipv6(uint128_t addr, uint128_t mask, pnfq_check_result_t action) { 403 struct pnfq_check_node *currnode; 404 uint32_t bitval, maskval; 405 int bit; 406 407 assert(action != PNFQ_CHECK_DEFAULT); 408 409 currnode = &pnfq_check_tree_v6; 410 411 for (bit = -1; bit < 129; bit++) { 412 if (currnode == NULL) { 413 break; 414 } 415 416 bitval = (addr.a[bit / 32] >> (32 - (bit % 32) - 1)) & 1; 417 418 /* 419 * Always take the same route from the HEAD (-1'th) bit. 420 */ 421 if (bit == -1) { 422 bitval = 0; 423 } 424 425 maskval = (mask.a[bit / 32] >> (32 - (bit % 32) - 1)) & 1; 426 if ((maskval == 0 && bit != -1) || bit == 128) { 427 currnode->action = action; 428 break; 429 } 430 431 if (bitval == 0) { 432 if (currnode->_zero == NULL) { 433 currnode->_zero = malloc(sizeof(*currnode->_zero)); 434 currnode->_zero->action = PNFQ_CHECK_CONTINUE; 435 currnode->_zero->_zero = NULL; 436 currnode->_zero->_one = NULL; 437 } 438 currnode = currnode->_zero; 439 } else { 440 if (currnode->_one == NULL) { 441 currnode->_one = malloc(sizeof(*currnode->_one)); 442 currnode->_one->action = PNFQ_CHECK_CONTINUE; 443 currnode->_one->_zero = NULL; 444 currnode->_one->_one = NULL; 445 } 446 currnode = currnode->_one; 447 } 448 } 449 450 return; 451 } 452 static inline void pnfq_addr_del_ipv6(uint128_t addr, uint128_t mask) { 453 pnfq_addr_add_ipv6(addr, mask, PNFQ_CHECK_CONTINUE); 454 return; 455 } 456 static inline pnfq_check_result_t pnfq_addr_check_ipv6(uint128_t addr) { 457 pnfq_check_result_t retval = PNFQ_CHECK_DEFAULT; 458 struct pnfq_check_node *currnode; 459 uint32_t bitval; 460 int bit; 461 462 currnode = &pnfq_check_tree_v6; 463 464 for (bit = -1; bit < 129; bit++) { 465 if (currnode == NULL) { 466 break; 467 } 468 469 bitval = (addr.a[bit / 32] >> (32 - (bit % 32) - 1)) & 1; 470 471 /* 472 * Always take the same route from the HEAD (-1'th) bit. 473 */ 474 if (bit == -1) { 475 bitval = 0; 476 } 477 478 if (currnode->action != PNFQ_CHECK_CONTINUE) { 479 retval = currnode->action; 480 } 481 482 if (bitval == 0) { 483 currnode = currnode->_zero; 484 } else { 485 currnode = currnode->_one; 486 } 487 } 488 489 return(retval); 490 return(retval); 491 } 492 static inline void pnfq_addr_add(pnfq_addr_t addr, pnfq_addr_t mask, pnfq_check_result_t action) { 493 assert (addr.type == mask.type); 494 495 switch (addr.type) { 496 case PNFQ_ADDR_TYPE_IPV4: 497 pnfq_addr_add_ipv4(addr.addr.ipv4, mask.addr.ipv4, action); 498 break; 499 case PNFQ_ADDR_TYPE_IPV6: 500 pnfq_addr_add_ipv6(addr.addr.ipv6, mask.addr.ipv6, action); 501 break; 502 case PNFQ_ADDR_TYPE_UNKNOWN: 503 return; 504 } 505 506 return; 507 } 508 static inline void pnfq_addr_del(pnfq_addr_t addr, pnfq_addr_t mask) { 509 assert (addr.type == mask.type); 510 511 switch (addr.type) { 512 case PNFQ_ADDR_TYPE_IPV4: 513 pnfq_addr_del_ipv4(addr.addr.ipv4, mask.addr.ipv4); 514 break; 515 case PNFQ_ADDR_TYPE_IPV6: 516 pnfq_addr_del_ipv6(addr.addr.ipv6, mask.addr.ipv6); 517 break; 518 case PNFQ_ADDR_TYPE_UNKNOWN: 519 break; 520 } 521 522 return; 523 } 524 static inline pnfq_check_result_t pnfq_addr_check(pnfq_addr_t addr) { 525 switch (addr.type) { 526 case PNFQ_ADDR_TYPE_IPV4: 527 return(pnfq_addr_check_ipv4(addr.addr.ipv4)); 528 case PNFQ_ADDR_TYPE_IPV6: 529 return(pnfq_addr_check_ipv6(addr.addr.ipv6)); 530 case PNFQ_ADDR_TYPE_UNKNOWN: 531 return(NF_DROP); 532 } 533 534 return(PNFQ_CHECK_DROP); 535 } 536 #else 537 #define pnfq_addr_add(x, y, z) pnfq_addr_add_ipv4(x, y, z) 538 #define pnfq_addr_del(x, y) pnfq_addr_del_ipv4(x, y) 539 #define pnfq_addr_check(x) pnfq_addr_check_ipv4(x) 540 #endif 541 542 /* 543 * TCL Interface Routines 544 */ 545 int pnfq_addr_add_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { 546 pnfq_check_result_t action_val; 547 pnfq_addr_t addr_val, mask_val; 548 char *addr, *mask, *action; 549 550 if (argc != 4) { 551 return(TCL_ERROR); 552 } 553 554 addr = argv[1]; 555 mask = argv[2]; 556 action = argv[3]; 557 558 if (strcasecmp(action, "accept") == 0) { 559 action_val = PNFQ_CHECK_ACCEPT; 560 } else if (strcasecmp(action, "drop") == 0) { 561 action_val = PNFQ_CHECK_DROP; 562 } else { 563 return(TCL_ERROR); 564 } 565 566 addr_val = str_to_ip(addr); 567 mask_val = str_to_ip(mask); 568 569 #ifdef USE_IPV6 570 if (addr_val.type == PNFQ_ADDR_TYPE_UNKNOWN || mask_val.type == PNFQ_ADDR_TYPE_UNKNOWN) { 571 #else 572 if (addr_val == 0 || mask_val == 0) { 573 #endif 574 #ifndef NDEBUG 575 printf("Invalid address specified from Tcl: %s (%s)\n", addr, mask); 576 #endif 577 return(TCL_ERROR); 578 } 579 580 581 pnfq_addr_add(addr_val, mask_val, action_val); 582 583 return(TCL_OK); 584 } 585 int pnfq_addr_del_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { 586 pnfq_addr_t addr_val, mask_val; 587 char *addr, *mask; 588 589 if (argc != 3) { 590 return(TCL_ERROR); 591 } 592 593 addr = argv[1]; 594 mask = argv[2]; 595 596 addr_val = str_to_ip(addr); 597 mask_val = str_to_ip(mask); 598 599 #ifdef USE_IPV6 600 if (addr_val.type == PNFQ_ADDR_TYPE_UNKNOWN || mask_val.type == PNFQ_ADDR_TYPE_UNKNOWN) { 601 #else 602 if (addr_val == 0 || mask_val == 0) { 603 #endif 604 #ifndef NDEBUG 605 printf("Invalid address specified from Tcl: %s (%s)\n", addr, mask); 606 #endif 607 return(TCL_ERROR); 608 } 609 610 pnfq_addr_del(addr_val, mask_val); 611 612 return(TCL_OK); 613 } 614 void pnfq_addr_get_tcl_loop(Tcl_Interp *interp, char *var, struct pnfq_check_node *node, int bit, uint32_t bitval) { 615 #if !defined(USE_TCL_PKT_HANDLER) || !defined(USE_THREADED_TCL) 616 static 617 #endif 618 char valbuf[256], ipbuf[256], maskbuf[256]; 619 uint32_t maskval = 0; 620 int i; 621 622 if (node->action != PNFQ_CHECK_CONTINUE && bit != -1) { 623 for (i = 0; i < bit; i++) { 624 maskval |= 1; 625 maskval <<= 1; 626 } 627 if (bit == 32) { 628 maskval |= 1; 629 } else { 630 maskval <<= (32 - bit - 1); 631 } 632 633 snprintf(valbuf, sizeof(valbuf), "%s %s %s", 634 ipv4_to_str(bitval, ipbuf, sizeof(ipbuf)), 635 ipv4_to_str(maskval, maskbuf, sizeof(maskbuf)), 636 (node->action == PNFQ_CHECK_ACCEPT) ? "ACCEPT" : "DROP" 637 ); 638 639 Tcl_SetVar(interp, var, valbuf, TCL_APPEND_VALUE | TCL_LIST_ELEMENT); 640 } 641 642 if (node->_zero) { 643 pnfq_addr_get_tcl_loop(interp, var, node->_zero, bit + 1, bitval); 644 } 645 if (node->_one) { 646 bitval |= 1 << (32 - bit - 1); 647 pnfq_addr_get_tcl_loop(interp, var, node->_one, bit + 1, bitval); 648 } 649 } 650 int pnfq_addr_get_tcl(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { 651 char *var; 652 653 if (argc != 2) { 654 return(TCL_ERROR); 655 } 656 657 var = argv[1]; 658 659 Tcl_SetVar(interp, var, "", 0); 660 661 pnfq_addr_get_tcl_loop(interp, var, &pnfq_check_tree_v4, -1, 0); 662 663 return(TCL_OK); 664 } 665 666 667 /* 668 * Worker Thread Loop: Packet Processing Loop 669 */ 670 void *pnfq_handle_packets(void *arg) { 671 unsigned char pktbuf[PNFQ_PKT_BUF_LEN]; 672 pnfq_check_result_t s_verdict, d_verdict, verdict; 673 pnfq_addr_t srcaddr, dstaddr; 674 ipq_packet_msg_t *pkt; 675 ssize_t ipqr_ret; 676 struct pnfq_thread_info *tinfo; 677 struct ipq_handle *handle; 678 int ipqmt_msgtype; 679 int error_count = 0; 680 #if !defined(NDEBUG) || defined(USE_TCL_PKT_HANDLER) 681 char s_ipstrbuf[256], d_ipstrbuf[256]; 682 #endif 683 #ifdef USE_TCL_PKT_HANDLER 684 #ifndef USE_THREADED_TCL 685 pthread_mutex_t *tcl_mutex; 686 #endif 687 Tcl_Interp *interp; 688 CONST char *tcl_retstr; 689 const char *verdict_str; 690 char cmdbuf[8192]; 691 int tcl_eval_ret; 692 #endif 693 694 tinfo = arg; 695 696 handle = tinfo->handle; 697 698 #ifdef USE_TCL_PKT_HANDLER 699 if (tinfo->tcl_scpdir) { 700 #ifndef USE_THREADED_TCL 701 tcl_mutex = tinfo->tcl_mutex; 702 703 pthread_mutex_lock(tcl_mutex); 704 #endif 705 706 interp = Tcl_CreateInterp(); 707 Tcl_Init(interp); 708 709 Tcl_CreateCommand(interp, "pnfq_add", (Tcl_CmdProc *) pnfq_addr_add_tcl, NULL, NULL); 710 Tcl_CreateCommand(interp, "pnfq_delete", (Tcl_CmdProc *) pnfq_addr_del_tcl, NULL, NULL); 711 Tcl_CreateCommand(interp, "pnfq_get", (Tcl_CmdProc *) pnfq_addr_get_tcl, NULL, NULL); 712 713 Tcl_Eval(interp, "proc pnfq_handle_pkt args { return [lindex $args 0] }"); 714 715 snprintf(cmdbuf, sizeof(cmdbuf), "source %s/worker.tcl", tinfo->tcl_scpdir); 716 Tcl_Eval(interp, cmdbuf); 717 718 #ifndef USE_THREADED_TCL 719 pthread_mutex_unlock(tcl_mutex); 720 #endif 721 } else { 722 interp = NULL; 723 } 724 #endif 725 726 while (1) { 727 if (error_count > PNFQ_MAX_READ_ERRORS) { 728 break; 729 } 730 731 ipqr_ret = ipq_read(handle, pktbuf, sizeof(pktbuf), 0); 732 733 if (ipqr_ret <= 0) { 734 fprintf(stderr, "ipq_read warning: %s\n", ipq_errstr()); 735 error_count++; 736 continue; 737 } 738 739 ipqmt_msgtype = ipq_message_type(pktbuf); 740 if (ipqmt_msgtype == NLMSG_ERROR) { 741 fprintf(stderr, "Netlink transport terminal warning: %s\n", strerror(ipq_get_msgerr(pktbuf))); 742 743 break; 744 } else if (ipqmt_msgtype != IPQM_PACKET) { 745 fprintf(stderr, "Unknown message warning: %i\n", ipqmt_msgtype); 746 747 error_count++; 748 749 continue; 750 } 751 752 pkt = ipq_get_packet(pktbuf); 753 if (!pkt) { 754 continue; 755 } 756 757 error_count = 0; 758 759 pkt_info(pkt, &srcaddr, &dstaddr); 760 761 s_verdict = pnfq_addr_check(srcaddr); 762 d_verdict = pnfq_addr_check(dstaddr); 763 764 if (s_verdict == PNFQ_CHECK_DROP || d_verdict == PNFQ_CHECK_DROP) { 765 verdict = PNFQ_CHECK_DROP; 766 } else if (s_verdict == PNFQ_CHECK_ACCEPT || d_verdict == PNFQ_CHECK_ACCEPT) { 767 verdict = PNFQ_CHECK_ACCEPT; 768 } else if (s_verdict == d_verdict) { 769 verdict = s_verdict; 770 } else { 771 /* What should we do here ? */ 772 verdict = s_verdict; 773 } 774 #ifndef NDEBUG 775 switch (verdict) { 776 case PNFQ_CHECK_ACCEPT: 777 printf("[%i] Accepting packet from %s to %s\n", 778 tinfo->thread_id, 779 ip_to_str(srcaddr, s_ipstrbuf, sizeof(s_ipstrbuf)), 780 ip_to_str(dstaddr, d_ipstrbuf, sizeof(d_ipstrbuf)) 781 ); 782 break; 783 case PNFQ_CHECK_DROP: 784 printf("[%i] Dropping packet from %s to %s\n", 785 tinfo->thread_id, 786 ip_to_str(srcaddr, s_ipstrbuf, sizeof(s_ipstrbuf)), 787 ip_to_str(dstaddr, d_ipstrbuf, sizeof(d_ipstrbuf)) 788 ); 789 break; 790 case PNFQ_CHECK_DEFAULT: 791 printf("[%i] No match for packet from %s to %s\n", 792 tinfo->thread_id, 793 ip_to_str(srcaddr, s_ipstrbuf, sizeof(s_ipstrbuf)), 794 ip_to_str(dstaddr, d_ipstrbuf, sizeof(d_ipstrbuf)) 795 ); 796 break; 797 case PNFQ_CHECK_CONTINUE: 798 printf("[%i] Weird packet from %s to %s\n", 799 tinfo->thread_id, 800 ip_to_str(srcaddr, s_ipstrbuf, sizeof(s_ipstrbuf)), 801 ip_to_str(dstaddr, d_ipstrbuf, sizeof(d_ipstrbuf)) 802 ); 803 break; 804 } 805 #endif 806 807 #ifdef USE_TCL_PKT_HANDLER 808 if (interp) { 809 switch (verdict) { 810 case PNFQ_CHECK_ACCEPT: 811 verdict_str = "ACCEPT"; 812 break; 813 case PNFQ_CHECK_DROP: 814 verdict_str = "DROP"; 815 break; 816 case PNFQ_CHECK_DEFAULT: 817 verdict_str = "DEFAULT"; 818 break; 819 case PNFQ_CHECK_CONTINUE: 820 verdict_str = "ERROR"; 821 break; 822 } 823 /* 824 * Call the Tcl procedure "pnfq_handle_pkt" with three args: 825 * verdict Existing verdict: 826 * ACCEPT 827 * DROP 828 * DEFAULT 829 * src Packet SRC IP address (dotted quad format) 830 * dst Packet DST IP address (dotted quad format) 831 * Return value from the procedure should be "1" (for ACCEPT) or "0" (for DROP) 832 */ 833 snprintf(cmdbuf, sizeof(cmdbuf), "pnfq_handle_pkt %s %s %s", 834 verdict_str, 835 ip_to_str(srcaddr, s_ipstrbuf, sizeof(s_ipstrbuf)), 836 ip_to_str(dstaddr, d_ipstrbuf, sizeof(d_ipstrbuf)) 837 ); 838 839 #ifndef USE_THREADED_TCL 840 pthread_mutex_lock(tcl_mutex); 841 #endif 842 843 tcl_eval_ret = Tcl_Eval(interp, cmdbuf); 844 if (tcl_eval_ret == TCL_OK) { 845 tcl_retstr = Tcl_GetStringResult(interp); 846 } else { 847 tcl_retstr = NULL; 848 } 849 850 #ifndef USE_THREADED_TCL 851 pthread_mutex_unlock(tcl_mutex); 852 #endif 853 854 if (tcl_retstr != NULL) { 855 if (tcl_retstr[0] == 'A') { 856 #ifndef NDEBUG 857 printf("Setting Verdict from pnfq_handle_pkt: %s (ACCEPT)\n", tcl_retstr); 858 #endif 859 verdict = PNFQ_CHECK_ACCEPT; 860 } else if (tcl_retstr[0] == 'D' && tcl_retstr[1] == 'E') { 861 #ifndef NDEBUG 862 printf("Setting Verdict from pnfq_handle_pkt: %s (DEFAULT)\n", tcl_retstr); 863 #endif 864 verdict = PNFQ_CHECK_DEFAULT; 865 } else { 866 #ifndef NDEBUG 867 printf("Setting Verdict from pnfq_handle_pkt: %s (DROP)\n", tcl_retstr); 868 #endif 869 verdict = PNFQ_CHECK_DROP; 870 } 871 } 872 } 873 #endif 874 875 /* 876 * POLICY: Unmatched packets are accepted 877 */ 878 if (verdict == PNFQ_CHECK_DEFAULT) { 879 verdict = PNFQ_CHECK_ACCEPT; 880 } 881 ipq_set_verdict(handle, pkt->packet_id, verdict, 0, NULL); 882 } 883 884 return(NULL); 885 } 886 887 /* 888 * Program Entry Point and Tcl Event Loop 889 */ 890 int main(int argc, char **argv) { 891 Tcl_Interp *main_interp = NULL; 892 pthread_t *threads = NULL; 893 #ifdef USE_TCL_PKT_HANDLER 894 #ifndef USE_THREADED_TCL 895 pthread_mutex_t tcl_mutex = PTHREAD_MUTEX_INITIALIZER; 896 #endif 897 #endif 898 struct pnfq_thread_info *tinfo = NULL; 899 struct ipq_handle *handle_v4; 900 #ifdef USE_IPV6 901 struct ipq_handle *handle_v6; 902 #endif 903 #ifndef USE_THREADED_TCL 904 struct timespec waittime; 905 #endif 906 char cmdbuf[8192], *scpdir = NULL; 907 int num_threads = PNFQ_NUM_THREADS; 908 int tcl_eval_ret; 909 int ipqsm_ret; 910 int i, ch; 911 912 #ifdef PNFQ_SCRIPTDIR 913 scpdir = PNFQ_SCRIPTDIR; 914 #endif 915 916 while ((ch = getopt(argc, argv, "VhclT:t:")) != -1) { 917 switch (ch) { 918 case 'l': 919 #ifndef NDEBUG 920 printf("Locking all pages to physical RAM.\n"); 921 #endif 922 if (mlockall(MCL_FUTURE) != 0) { 923 perror("mlockall"); 924 fprintf(stderr, "Unable to lock memory pages, aborting.\n"); 925 return(EXIT_FAILURE); 926 } 927 break; 928 case 't': 929 num_threads = atoi(optarg); 930 if (num_threads <= 1 || num_threads > 128) { 931 fprintf(stderr, "Invalid number of threads (%i), min: 2, max: 128, aborting.\n", num_threads); 932 return(EXIT_FAILURE); 933 } 934 break; 935 case 'T': 936 /* TCL Script Dir */ 937 scpdir = optarg; 938 break; 939 case 'c': 940 printf("PNFQ Version %s", PACKAGE_VERSION); 941 #ifdef NDEBUG 942 printf("-rel"); 943 #else 944 printf("-dbg"); 945 #endif 946 #ifdef PNFQ_PROFILING 947 printf("-prof"); 948 #endif 949 #ifdef PNFQ_EXTRAVERS 950 printf("-%s", PNFQ_EXTRAVERS); 951 #endif 952 printf(" was built at %s on %s using:\n", __TIME__, __DATE__); 953 printf(" $ ./configure"); 954 #ifdef PNFQ_SCRIPTDIR 955 printf(" --datadir=%s/../", PNFQ_SCRIPTDIR); 956 #endif 957 #ifdef NDEBUG 958 printf(" --without-debug"); 959 #else 960 printf(" --with-debug"); 961 #endif 962 #ifdef PNFQ_PROFILING 963 printf(" --with-profile"); 964 #else 965 printf(" --without-profile"); 966 #endif 967 #ifdef TCLLIBDIR 968 printf(" --with-tcllibdir=\"%s\"", TCLLIBDIR); 969 #endif 970 #ifdef TCLINCDIR 971 printf(" --with-tclincdir=\"%s\"", TCLINCDIR); 972 #endif 973 #ifdef USE_TCL_PKT_HANDLER 974 printf(" --with-tclpkt"); 975 #else 976 printf(" --without-tclpkt"); 977 #endif 978 #ifdef USE_IPV6 979 printf(" --with-ipv6"); 980 #else 981 printf(" --without-ipv6"); 982 #endif 983 printf("\n"); 984 return(EXIT_SUCCESS); 985 case 'V': 986 printf("PNFQ Version %s", PACKAGE_VERSION); 987 #ifdef NDEBUG 988 printf("-rel"); 989 #else 990 printf("-dbg"); 991 #endif 992 #ifdef PNFQ_PROFILING 993 printf("-prof"); 994 #endif 995 #ifdef PNFQ_EXTRAVERS 996 printf("-%s", PNFQ_EXTRAVERS); 997 #endif 998 #ifdef USE_TCL_PKT_HANDLER 999 printf(" [TCLPKT]"); 1000 #endif 1001 #ifdef USE_THREADED_TCL 1002 printf(" [TCLTHREADED]"); 1003 #else 1004 printf(" [TCLUNTHREADED]"); 1005 #endif 1006 #ifdef USE_IPV6 1007 printf(" [IPV6]"); 1008 #endif 1009 printf(" [PKTBUFLEN=%i]", PNFQ_PKT_BUF_LEN); 1010 printf(" [DEFTHREADS=%i]\n", PNFQ_NUM_THREADS); 1011 return(EXIT_SUCCESS); 1012 case 'h': 1013 print_help(); 1014 return(EXIT_SUCCESS); 1015 case ':': 1016 case '?': 1017 return(EXIT_FAILURE); 1018 break; 1019 } 1020 } 1021 1022 /* 1023 * Verify that the "ip_queue" module is loaded 1024 */ 1025 if (access("/proc/net/ip_queue", F_OK) != 0) { 1026 system("/sbin/modprobe ip_queue"); 1027 } 1028 if (access("/proc/net/ip_queue", F_OK) != 0) { 1029 fprintf(stderr, "Please make sure ip_queue is installed, or that you have\n"); 1030 fprintf(stderr, "compiled a kernel with the Netfilter QUEUE target built in.\n"); 1031 fprintf(stderr, "Aborting.\n"); 1032 1033 exit(EXIT_FAILURE); 1034 } 1035 1036 #ifdef USE_IPV6 1037 if (access("/proc/net/ip6_queue", F_OK) != 0) { 1038 system("/sbin/modprobe ip6_queue"); 1039 } 1040 if (access("/proc/net/ip6_queue", F_OK) != 0) { 1041 fprintf(stderr, "Please make sure ip6_queue is installed, or that you have\n"); 1042 fprintf(stderr, "compiled a kernel with the Netfilter QUEUE target built in.\n"); 1043 fprintf(stderr, "Aborting.\n"); 1044 1045 exit(EXIT_FAILURE); 1046 } 1047 #endif 1048 1049 threads = malloc(sizeof(*threads) * num_threads); 1050 if (threads == NULL) { 1051 perror("malloc"); 1052 exit(EXIT_FAILURE); 1053 } 1054 1055 /* 1056 * Create IPQ Interface Handle 1057 */ 1058 handle_v4 = ipq_create_handle(0, PF_INET); 1059 if (handle_v4 == NULL) { 1060 fprintf(stderr, "Error creating IPQ Handler. Aborting.\n"); 1061 exit(EXIT_FAILURE); 1062 } 1063 1064 #ifdef USE_IPV6 1065 handle_v6 = ipq_create_handle(0, PF_INET6); 1066 if (handle_v6 == NULL) { 1067 fprintf(stderr, "[ipv6] Error creating IPQ Handler. Aborting.\n"); 1068 exit(EXIT_FAILURE); 1069 } 1070 #endif 1071 1072 /* 1073 * Set IPQ Interface Handle to Copy mode (so we can get the IP address 1074 * from the packets) 1075 */ 1076 ipqsm_ret = ipq_set_mode(handle_v4, IPQ_COPY_PACKET, PNFQ_PKT_BUF_LEN); 1077 if (ipqsm_ret == -1) { 1078 fprintf(stderr, "ipq_set_mode error: %s\n", ipq_errstr()); 1079 if (errno == 111) { 1080 fprintf(stderr, "Try loading the \"ip_queue\" module\n"); 1081 } 1082 1083 fprintf(stderr, "Aborting.\n"); 1084 1085 ipq_destroy_handle(handle_v4); 1086 1087 exit(EXIT_FAILURE); 1088 } 1089 1090 #ifdef USE_IPV6 1091 ipqsm_ret = ipq_set_mode(handle_v6, IPQ_COPY_PACKET, PNFQ_PKT_BUF_LEN); 1092 if (ipqsm_ret == -1) { 1093 fprintf(stderr, "[ipv6] ipq_set_mode error: %s\n", ipq_errstr()); 1094 if (errno == 111) { 1095 fprintf(stderr, "[ipv6] Try loading the \"ip6_queue\" module\n"); 1096 } 1097 1098 fprintf(stderr, "[ipv6] Aborting.\n"); 1099 1100 ipq_destroy_handle(handle_v4); 1101 ipq_destroy_handle(handle_v6); 1102 1103 exit(EXIT_FAILURE); 1104 } 1105 #endif 1106 1107 /* 1108 * Create resources for each worker thread 1109 */ 1110 tinfo = malloc(sizeof(*tinfo) * num_threads); 1111 if (tinfo == NULL) { 1112 perror("malloc"); 1113 1114 ipq_destroy_handle(handle_v4); 1115 #ifdef USE_IPV6 1116 ipq_destroy_handle(handle_v6); 1117 #endif 1118 1119 exit(EXIT_FAILURE); 1120 } 1121 1122 #ifdef USE_TCL_PKT_HANDLER 1123 #ifndef USE_THREADED_TCL 1124 pthread_mutex_init(&tcl_mutex, NULL); 1125 #endif 1126 #endif 1127 1128 /* 1129 * Initialize resources for each worker thread 1130 */ 1131 for (i = 0; i < num_threads; i++) { 1132 tinfo[i].thread_id = i; 1133 #ifndef USE_IPV6 1134 tinfo[i].handle = handle_v4; 1135 #else 1136 if ((i % 2) == 0) { 1137 tinfo[i].handle = handle_v4; 1138 } else { 1139 tinfo[i].handle = handle_v6; 1140 } 1141 #endif 1142 1143 #ifdef USE_TCL_PKT_HANDLER 1144 tinfo[i].tcl_scpdir = scpdir; 1145 #ifndef USE_THREADED_TCL 1146 tinfo[i].tcl_mutex = &tcl_mutex; 1147 #endif 1148 #endif 1149 } 1150 1151 /* 1152 * Create each worker thread 1153 */ 1154 for (i = 0; i < num_threads; i++) { 1155 pthread_create(&threads[i], NULL, pnfq_handle_packets, &tinfo[i]); 1156 } 1157 1158 /* 1159 * Create the main interface Tcl Interpreter 1160 */ 1161 if (scpdir) { 1162 #ifdef USE_TCL_PKT_HANDLER 1163 #ifndef USE_THREADED_TCL 1164 pthread_mutex_lock(&tcl_mutex); 1165 #endif 1166 #endif 1167 1168 main_interp = Tcl_CreateInterp(); 1169 Tcl_Init(main_interp); 1170 1171 Tcl_CreateCommand(main_interp, "pnfq_add", (Tcl_CmdProc *) pnfq_addr_add_tcl, NULL, NULL); 1172 Tcl_CreateCommand(main_interp, "pnfq_delete", (Tcl_CmdProc *) pnfq_addr_del_tcl, NULL, NULL); 1173 Tcl_CreateCommand(main_interp, "pnfq_get", (Tcl_CmdProc *) pnfq_addr_get_tcl, NULL, NULL); 1174 1175 snprintf(cmdbuf, sizeof(cmdbuf), "source %s/feeder.tcl", scpdir); 1176 tcl_eval_ret = Tcl_Eval(main_interp, cmdbuf); 1177 if (tcl_eval_ret != TCL_OK) { 1178 printf("Error While Loading %s/feeder.tcl: %s", scpdir, Tcl_GetVar(main_interp, "errorInfo", TCL_GLOBAL_ONLY)); 1179 } 1180 1181 #ifdef USE_TCL_PKT_HANDLER 1182 #ifndef USE_THREADED_TCL 1183 pthread_mutex_unlock(&tcl_mutex); 1184 #endif 1185 #endif 1186 } else { 1187 main_interp = NULL; 1188 } 1189 1190 while (1) { 1191 #ifdef USE_TCL_PKT_HANDLER 1192 #ifndef USE_THREADED_TCL 1193 pthread_mutex_lock(&tcl_mutex); 1194 #endif 1195 #endif 1196 1197 #ifndef USE_THREADED_TCL 1198 /* 1199 * If using an Unthreaded Tcl Build: 1200 * a. We can't wait inside the Tcl Library when using a 1201 * TCL_PKT_HANDLER because then no other threads can 1202 * invoke their handlers. 1203 * b. The Tcl_DoOneEvent() doesn't wait forever anyway, 1204 * resulting in a busy loop. 1205 */ 1206 Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT); 1207 #else 1208 Tcl_DoOneEvent(TCL_ALL_EVENTS); 1209 #endif 1210 1211 #ifdef USE_TCL_PKT_HANDLER 1212 #ifndef USE_THREADED_TCL 1213 pthread_mutex_unlock(&tcl_mutex); 1214 #endif 1215 #endif 1216 1217 #ifndef USE_THREADED_TCL 1218 waittime.tv_sec = 5; 1219 waittime.tv_nsec = 0; 1220 nanosleep(&waittime, NULL); 1221 #endif 1222 } 1223 1224 ipq_destroy_handle(handle_v4); 1225 #ifdef USE_IPV6 1226 ipq_destroy_handle(handle_v6); 1227 #endif 1228 1229 return(EXIT_FAILURE); 1230 } |