l4check.c revision 255332
1/* $FreeBSD: head/contrib/ipfilter/l4check/l4check.c 255332 2013-09-06 23:11:19Z cy $ */ 2 3/* 4 * (C)Copyright (C) 2012 by Darren Reed. 5 */ 6#include <sys/types.h> 7#include <sys/stat.h> 8#include <sys/mman.h> 9#include <sys/socket.h> 10#include <sys/time.h> 11#include <sys/ioctl.h> 12 13#include <netinet/in.h> 14#include <netinet/in_systm.h> 15#include <netinet/ip.h> 16 17#include <net/if.h> 18 19#include <stdio.h> 20#include <netdb.h> 21#include <string.h> 22#include <ctype.h> 23#include <fcntl.h> 24#include <errno.h> 25#include <stdlib.h> 26 27#include "ip_compat.h" 28#include "ip_fil.h" 29#include "ip_nat.h" 30 31#include "ipf.h" 32 33extern char *optarg; 34 35 36typedef struct l4cfg { 37 struct l4cfg *l4_next; 38 struct ipnat l4_nat; /* NAT rule */ 39 struct sockaddr_in l4_sin; /* remote socket to connect */ 40 time_t l4_last; /* when we last connected */ 41 int l4_alive; /* 1 = remote alive */ 42 int l4_fd; 43 int l4_rw; /* 0 = reading, 1 = writing */ 44 char *l4_rbuf; /* read buffer */ 45 int l4_rsize; /* size of buffer */ 46 int l4_rlen; /* how much used */ 47 char *l4_wptr; /* next byte to write */ 48 int l4_wlen; /* length yet to be written */ 49} l4cfg_t; 50 51 52l4cfg_t *l4list = NULL; 53char *response = NULL; 54char *probe = NULL; 55l4cfg_t template; 56int frequency = 20; 57int ctimeout = 1; 58int rtimeout = 1; 59size_t plen = 0; 60size_t rlen = 0; 61int natfd = -1; 62int opts = 0; 63 64#if defined(sun) && !defined(__svr4__) && !defined(__SVR4) 65# define strerror(x) sys_errlist[x] 66#endif 67 68 69char *copystr(dst, src) 70 char *dst, *src; 71{ 72 register char *s, *t, c; 73 register int esc = 0; 74 75 for (s = src, t = dst; s && t && (c = *s++); ) 76 if (esc) { 77 esc = 0; 78 switch (c) 79 { 80 case 'n' : 81 *t++ = '\n'; 82 break; 83 case 'r' : 84 *t++ = '\r'; 85 break; 86 case 't' : 87 *t++ = '\t'; 88 break; 89 } 90 } else if (c != '\\') 91 *t++ = c; 92 else 93 esc = 1; 94 *t = '\0'; 95 return dst; 96} 97 98void addnat(l4) 99 l4cfg_t *l4; 100{ 101 ipnat_t *ipn = &l4->l4_nat; 102 103 printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]), 104 ipn->in_outmsk, ntohs(ipn->in_pmin)); 105 printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext)); 106 if (!(opts & OPT_DONOTHING)) { 107 if (ioctl(natfd, SIOCADNAT, &ipn) == -1) 108 perror("ioctl(SIOCADNAT)"); 109 } 110} 111 112 113void delnat(l4) 114 l4cfg_t *l4; 115{ 116 ipnat_t *ipn = &l4->l4_nat; 117 118 printf("Remove NAT rule for %s/%#x,%u -> ", 119 inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin); 120 printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext); 121 if (!(opts & OPT_DONOTHING)) { 122 if (ioctl(natfd, SIOCRMNAT, &ipn) == -1) 123 perror("ioctl(SIOCRMNAT)"); 124 } 125} 126 127 128void connectl4(l4) 129 l4cfg_t *l4; 130{ 131 l4->l4_rw = 1; 132 l4->l4_rlen = 0; 133 l4->l4_wlen = plen; 134 if (!l4->l4_wlen) { 135 l4->l4_alive = 1; 136 addnat(l4); 137 } else 138 l4->l4_wptr = probe; 139} 140 141 142void closel4(l4, dead) 143 l4cfg_t *l4; 144 int dead; 145{ 146 close(l4->l4_fd); 147 l4->l4_fd = -1; 148 l4->l4_rw = -1; 149 if (dead && l4->l4_alive) { 150 l4->l4_alive = 0; 151 delnat(l4); 152 } 153} 154 155 156void connectfd(l4) 157 l4cfg_t *l4; 158{ 159 if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin, 160 sizeof(l4->l4_sin)) == -1) { 161 if (errno == EISCONN) { 162 if (opts & OPT_VERBOSE) 163 fprintf(stderr, "Connected fd %d\n", 164 l4->l4_fd); 165 connectl4(l4); 166 return; 167 } 168 if (opts & OPT_VERBOSE) 169 fprintf(stderr, "Connect failed fd %d: %s\n", 170 l4->l4_fd, strerror(errno)); 171 closel4(l4, 1); 172 return; 173 } 174 l4->l4_rw = 1; 175} 176 177 178void writefd(l4) 179 l4cfg_t *l4; 180{ 181 char buf[80], *ptr; 182 int n, i, fd; 183 184 fd = l4->l4_fd; 185 186 if (l4->l4_rw == -2) { 187 connectfd(l4); 188 return; 189 } 190 191 n = l4->l4_wlen; 192 193 i = send(fd, l4->l4_wptr, n, 0); 194 if (i == 0 || i == -1) { 195 if (opts & OPT_VERBOSE) 196 fprintf(stderr, "Send on fd %d failed: %s\n", 197 fd, strerror(errno)); 198 closel4(l4, 1); 199 } else { 200 l4->l4_wptr += i; 201 l4->l4_wlen -= i; 202 if (l4->l4_wlen == 0) 203 l4->l4_rw = 0; 204 if (opts & OPT_VERBOSE) 205 fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd); 206 } 207} 208 209 210void readfd(l4) 211 l4cfg_t *l4; 212{ 213 char buf[80], *ptr; 214 int n, i, fd; 215 216 fd = l4->l4_fd; 217 218 if (l4->l4_rw == -2) { 219 connectfd(l4); 220 return; 221 } 222 223 if (l4->l4_rsize) { 224 n = l4->l4_rsize - l4->l4_rlen; 225 ptr = l4->l4_rbuf + l4->l4_rlen; 226 } else { 227 n = sizeof(buf) - 1; 228 ptr = buf; 229 } 230 231 if (opts & OPT_VERBOSE) 232 fprintf(stderr, "Read %d bytes on fd %d to %p\n", 233 n, fd, ptr); 234 i = recv(fd, ptr, n, 0); 235 if (i == 0 || i == -1) { 236 if (opts & OPT_VERBOSE) 237 fprintf(stderr, "Read error on fd %d: %s\n", 238 fd, (i == 0) ? "EOF" : strerror(errno)); 239 closel4(l4, 1); 240 } else { 241 if (ptr == buf) 242 ptr[i] = '\0'; 243 if (opts & OPT_VERBOSE) 244 fprintf(stderr, "%d: Read %d bytes [%*.*s]\n", 245 fd, i, i, i, ptr); 246 if (ptr != buf) { 247 l4->l4_rlen += i; 248 if (l4->l4_rlen >= l4->l4_rsize) { 249 if (!strncmp(response, l4->l4_rbuf, 250 l4->l4_rsize)) { 251 printf("%d: Good response\n", 252 fd); 253 if (!l4->l4_alive) { 254 l4->l4_alive = 1; 255 addnat(l4); 256 } 257 closel4(l4, 0); 258 } else { 259 if (opts & OPT_VERBOSE) 260 printf("%d: Bad response\n", 261 fd); 262 closel4(l4, 1); 263 } 264 } 265 } else if (!l4->l4_alive) { 266 l4->l4_alive = 1; 267 addnat(l4); 268 closel4(l4, 0); 269 } 270 } 271} 272 273 274int runconfig() 275{ 276 int fd, opt, res, mfd, i; 277 struct timeval tv; 278 time_t now, now1; 279 fd_set rfd, wfd; 280 l4cfg_t *l4; 281 282 mfd = 0; 283 opt = 1; 284 now = time(NULL); 285 286 /* 287 * First, initiate connections that are closed, as required. 288 */ 289 for (l4 = l4list; l4; l4 = l4->l4_next) { 290 if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) { 291 l4->l4_last = now; 292 fd = socket(AF_INET, SOCK_STREAM, 0); 293 if (fd == -1) 294 continue; 295 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, 296 sizeof(opt)); 297#ifdef O_NONBLOCK 298 if ((res = fcntl(fd, F_GETFL, 0)) != -1) 299 fcntl(fd, F_SETFL, res | O_NONBLOCK); 300#endif 301 if (opts & OPT_VERBOSE) 302 fprintf(stderr, 303 "Connecting to %s,%d (fd %d)...", 304 inet_ntoa(l4->l4_sin.sin_addr), 305 ntohs(l4->l4_sin.sin_port), fd); 306 if (connect(fd, (struct sockaddr *)&l4->l4_sin, 307 sizeof(l4->l4_sin)) == -1) { 308 if (errno != EINPROGRESS) { 309 if (opts & OPT_VERBOSE) 310 fprintf(stderr, "failed\n"); 311 perror("connect"); 312 close(fd); 313 fd = -1; 314 } else { 315 if (opts & OPT_VERBOSE) 316 fprintf(stderr, "waiting\n"); 317 l4->l4_rw = -2; 318 } 319 } else { 320 if (opts & OPT_VERBOSE) 321 fprintf(stderr, "connected\n"); 322 connectl4(l4); 323 } 324 l4->l4_fd = fd; 325 } 326 } 327 328 /* 329 * Now look for fd's which we're expecting to read/write from. 330 */ 331 FD_ZERO(&rfd); 332 FD_ZERO(&wfd); 333 tv.tv_sec = MIN(rtimeout, ctimeout); 334 tv.tv_usec = 0; 335 336 for (l4 = l4list; l4; l4 = l4->l4_next) 337 if (l4->l4_rw == 0) { 338 if (now - l4->l4_last > rtimeout) { 339 if (opts & OPT_VERBOSE) 340 fprintf(stderr, "%d: Read timeout\n", 341 l4->l4_fd); 342 closel4(l4, 1); 343 continue; 344 } 345 if (opts & OPT_VERBOSE) 346 fprintf(stderr, "Wait for read on fd %d\n", 347 l4->l4_fd); 348 FD_SET(l4->l4_fd, &rfd); 349 if (l4->l4_fd > mfd) 350 mfd = l4->l4_fd; 351 } else if ((l4->l4_rw == 1 && l4->l4_wlen) || 352 l4->l4_rw == -2) { 353 if ((l4->l4_rw == -2) && 354 (now - l4->l4_last > ctimeout)) { 355 if (opts & OPT_VERBOSE) 356 fprintf(stderr, 357 "%d: connect timeout\n", 358 l4->l4_fd); 359 closel4(l4); 360 continue; 361 } 362 if (opts & OPT_VERBOSE) 363 fprintf(stderr, "Wait for write on fd %d\n", 364 l4->l4_fd); 365 FD_SET(l4->l4_fd, &wfd); 366 if (l4->l4_fd > mfd) 367 mfd = l4->l4_fd; 368 } 369 370 if (opts & OPT_VERBOSE) 371 fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1, 372 tv.tv_sec); 373 i = select(mfd + 1, &rfd, &wfd, NULL, &tv); 374 if (i == -1) { 375 perror("select"); 376 return -1; 377 } 378 379 now1 = time(NULL); 380 381 for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) { 382 if (l4->l4_fd < 0) 383 continue; 384 if (FD_ISSET(l4->l4_fd, &rfd)) { 385 if (opts & OPT_VERBOSE) 386 fprintf(stderr, "Ready to read on fd %d\n", 387 l4->l4_fd); 388 readfd(l4); 389 i--; 390 } 391 392 if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) { 393 if (opts & OPT_VERBOSE) 394 fprintf(stderr, "Ready to write on fd %d\n", 395 l4->l4_fd); 396 writefd(l4); 397 i--; 398 } 399 } 400 return 0; 401} 402 403 404int gethostport(str, lnum, ipp, portp) 405 char *str; 406 int lnum; 407 u_32_t *ipp; 408 u_short *portp; 409{ 410 struct servent *sp; 411 struct hostent *hp; 412 char *host, *port; 413 struct in_addr ip; 414 415 host = str; 416 port = strchr(host, ','); 417 if (port) 418 *port++ = '\0'; 419 420#ifdef HAVE_INET_ATON 421 if (ISDIGIT(*host) && inet_aton(host, &ip)) 422 *ipp = ip.s_addr; 423#else 424 if (ISDIGIT(*host)) 425 *ipp = inet_addr(host); 426#endif 427 else { 428 if (!(hp = gethostbyname(host))) { 429 fprintf(stderr, "%d: can't resolve hostname: %s\n", 430 lnum, host); 431 return 0; 432 } 433 *ipp = *(u_32_t *)hp->h_addr; 434 } 435 436 if (port) { 437 if (ISDIGIT(*port)) 438 *portp = htons(atoi(port)); 439 else { 440 sp = getservbyname(port, "tcp"); 441 if (sp) 442 *portp = sp->s_port; 443 else { 444 fprintf(stderr, "%d: unknown service %s\n", 445 lnum, port); 446 return 0; 447 } 448 } 449 } else 450 *portp = 0; 451 return 1; 452} 453 454 455char *mapfile(file, sizep) 456 char *file; 457 size_t *sizep; 458{ 459 struct stat sb; 460 caddr_t addr; 461 int fd; 462 463 fd = open(file, O_RDONLY); 464 if (fd == -1) { 465 perror("open(mapfile)"); 466 return NULL; 467 } 468 469 if (fstat(fd, &sb) == -1) { 470 perror("fstat(mapfile)"); 471 close(fd); 472 return NULL; 473 } 474 475 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 476 if (addr == (caddr_t)-1) { 477 perror("mmap(mapfile)"); 478 close(fd); 479 return NULL; 480 } 481 close(fd); 482 *sizep = sb.st_size; 483 return (char *)addr; 484} 485 486 487int readconfig(filename) 488 char *filename; 489{ 490 char c, buf[512], *s, *t, *errtxt = NULL, *line; 491 int num, err = 0; 492 ipnat_t *ipn; 493 l4cfg_t *l4; 494 FILE *fp; 495 496 fp = fopen(filename, "r"); 497 if (!fp) { 498 perror("open(configfile)"); 499 return -1; 500 } 501 502 bzero((char *)&template, sizeof(template)); 503 template.l4_fd = -1; 504 template.l4_rw = -1; 505 template.l4_sin.sin_family = AF_INET; 506 ipn = &template.l4_nat; 507 ipn->in_flags = IPN_TCP|IPN_ROUNDR; 508 ipn->in_redir = NAT_REDIRECT; 509 510 for (num = 1; fgets(buf, sizeof(buf), fp); num++) { 511 s = strchr(buf, '\n'); 512 if (!s) { 513 fprintf(stderr, "%d: line too long\n", num); 514 fclose(fp); 515 return -1; 516 } 517 518 *s = '\0'; 519 520 /* 521 * lines which are comments 522 */ 523 s = strchr(buf, '#'); 524 if (s) 525 *s = '\0'; 526 527 /* 528 * Skip leading whitespace 529 */ 530 for (line = buf; (c = *line) && ISSPACE(c); line++) 531 ; 532 if (!*line) 533 continue; 534 535 if (opts & OPT_VERBOSE) 536 fprintf(stderr, "Parsing: [%s]\n", line); 537 t = strtok(line, " \t"); 538 if (!t) 539 continue; 540 if (!strcasecmp(t, "interface")) { 541 s = strtok(NULL, " \t"); 542 if (s) 543 t = strtok(NULL, "\t"); 544 if (!s || !t) { 545 errtxt = line; 546 err = -1; 547 break; 548 } 549 550 if (!strchr(t, ',')) { 551 fprintf(stderr, 552 "%d: local address,port missing\n", 553 num); 554 err = -1; 555 break; 556 } 557 558 strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname)); 559 if (!gethostport(t, num, &ipn->in_outip, 560 &ipn->in_pmin)) { 561 errtxt = line; 562 err = -1; 563 break; 564 } 565 ipn->in_outmsk = 0xffffffff; 566 ipn->in_pmax = ipn->in_pmin; 567 if (opts & OPT_VERBOSE) 568 fprintf(stderr, 569 "Interface %s %s/%#x port %u\n", 570 ipn->in_ifname, 571 inet_ntoa(ipn->in_out[0]), 572 ipn->in_outmsk, ipn->in_pmin); 573 } else if (!strcasecmp(t, "remote")) { 574 if (!*ipn->in_ifname) { 575 fprintf(stderr, 576 "%d: ifname not set prior to remote\n", 577 num); 578 err = -1; 579 break; 580 } 581 s = strtok(NULL, " \t"); 582 if (s) 583 t = strtok(NULL, ""); 584 if (!s || !t || strcasecmp(s, "server")) { 585 errtxt = line; 586 err = -1; 587 break; 588 } 589 590 ipn->in_pnext = 0; 591 if (!gethostport(t, num, &ipn->in_inip, 592 &ipn->in_pnext)) { 593 errtxt = line; 594 err = -1; 595 break; 596 } 597 ipn->in_inmsk = 0xffffffff; 598 if (ipn->in_pnext == 0) 599 ipn->in_pnext = ipn->in_pmin; 600 601 l4 = (l4cfg_t *)malloc(sizeof(*l4)); 602 if (!l4) { 603 fprintf(stderr, "%d: out of memory (%d)\n", 604 num, sizeof(*l4)); 605 err = -1; 606 break; 607 } 608 bcopy((char *)&template, (char *)l4, sizeof(*l4)); 609 l4->l4_sin.sin_addr = ipn->in_in[0]; 610 l4->l4_sin.sin_port = ipn->in_pnext; 611 l4->l4_next = l4list; 612 l4list = l4; 613 } else if (!strcasecmp(t, "connect")) { 614 s = strtok(NULL, " \t"); 615 if (s) 616 t = strtok(NULL, "\t"); 617 if (!s || !t) { 618 errtxt = line; 619 err = -1; 620 break; 621 } else if (!strcasecmp(s, "timeout")) { 622 ctimeout = atoi(t); 623 if (opts & OPT_VERBOSE) 624 fprintf(stderr, "connect timeout %d\n", 625 ctimeout); 626 } else if (!strcasecmp(s, "frequency")) { 627 frequency = atoi(t); 628 if (opts & OPT_VERBOSE) 629 fprintf(stderr, 630 "connect frequency %d\n", 631 frequency); 632 } else { 633 errtxt = line; 634 err = -1; 635 break; 636 } 637 } else if (!strcasecmp(t, "probe")) { 638 s = strtok(NULL, " \t"); 639 if (!s) { 640 errtxt = line; 641 err = -1; 642 break; 643 } else if (!strcasecmp(s, "string")) { 644 if (probe) { 645 fprintf(stderr, 646 "%d: probe already set\n", 647 num); 648 err = -1; 649 break; 650 } 651 t = strtok(NULL, ""); 652 if (!t) { 653 fprintf(stderr, 654 "%d: No probe string\n", num); 655 err = -1; 656 break; 657 } 658 659 probe = malloc(strlen(t)); 660 copystr(probe, t); 661 plen = strlen(probe); 662 if (opts & OPT_VERBOSE) 663 fprintf(stderr, "Probe string [%s]\n", 664 probe); 665 } else if (!strcasecmp(s, "file")) { 666 t = strtok(NULL, " \t"); 667 if (!t) { 668 errtxt = line; 669 err = -1; 670 break; 671 } 672 if (probe) { 673 fprintf(stderr, 674 "%d: probe already set\n", 675 num); 676 err = -1; 677 break; 678 } 679 probe = mapfile(t, &plen); 680 if (opts & OPT_VERBOSE) 681 fprintf(stderr, 682 "Probe file %s len %u@%p\n", 683 t, plen, probe); 684 } 685 } else if (!strcasecmp(t, "response")) { 686 s = strtok(NULL, " \t"); 687 if (!s) { 688 errtxt = line; 689 err = -1; 690 break; 691 } else if (!strcasecmp(s, "timeout")) { 692 t = strtok(NULL, " \t"); 693 if (!t) { 694 errtxt = line; 695 err = -1; 696 break; 697 } 698 rtimeout = atoi(t); 699 if (opts & OPT_VERBOSE) 700 fprintf(stderr, 701 "response timeout %d\n", 702 rtimeout); 703 } else if (!strcasecmp(s, "string")) { 704 if (response) { 705 fprintf(stderr, 706 "%d: response already set\n", 707 num); 708 err = -1; 709 break; 710 } 711 response = strdup(strtok(NULL, "")); 712 rlen = strlen(response); 713 template.l4_rsize = rlen; 714 template.l4_rbuf = malloc(rlen); 715 if (opts & OPT_VERBOSE) 716 fprintf(stderr, 717 "Response string [%s]\n", 718 response); 719 } else if (!strcasecmp(s, "file")) { 720 t = strtok(NULL, " \t"); 721 if (!t) { 722 errtxt = line; 723 err = -1; 724 break; 725 } 726 if (response) { 727 fprintf(stderr, 728 "%d: response already set\n", 729 num); 730 err = -1; 731 break; 732 } 733 response = mapfile(t, &rlen); 734 template.l4_rsize = rlen; 735 template.l4_rbuf = malloc(rlen); 736 if (opts & OPT_VERBOSE) 737 fprintf(stderr, 738 "Response file %s len %u@%p\n", 739 t, rlen, response); 740 } 741 } else { 742 errtxt = line; 743 err = -1; 744 break; 745 } 746 } 747 748 if (errtxt) 749 fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt); 750 fclose(fp); 751 return err; 752} 753 754 755void usage(prog) 756 char *prog; 757{ 758 fprintf(stderr, "Usage: %s -f <configfile>\n", prog); 759 exit(1); 760} 761 762 763int main(argc, argv) 764 int argc; 765 char *argv[]; 766{ 767 char *config = NULL; 768 int c; 769 770 while ((c = getopt(argc, argv, "f:nv")) != -1) 771 switch (c) 772 { 773 case 'f' : 774 config = optarg; 775 break; 776 case 'n' : 777 opts |= OPT_DONOTHING; 778 break; 779 case 'v' : 780 opts |= OPT_VERBOSE; 781 break; 782 } 783 784 if (config == NULL) 785 usage(argv[0]); 786 787 if (readconfig(config)) 788 exit(1); 789 790 if (!l4list) { 791 fprintf(stderr, "No remote servers, exiting."); 792 exit(1); 793 } 794 795 if (!(opts & OPT_DONOTHING)) { 796 natfd = open(IPL_NAT, O_RDWR); 797 if (natfd == -1) { 798 perror("open(IPL_NAT)"); 799 exit(1); 800 } 801 } 802 803 if (opts & OPT_VERBOSE) 804 fprintf(stderr, "Starting...\n"); 805 while (runconfig() == 0) 806 ; 807} 808