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