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