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 *
copystr(char * dst,char * src)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
addnat(l4cfg_t * l4)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
delnat(l4cfg_t * l4)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
connectl4(l4cfg_t * l4)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
closel4(l4cfg_t * l4,int dead)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
connectfd(l4cfg_t * l4)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
writefd(l4cfg_t * l4)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
readfd(l4cfg_t * l4)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
runconfig(void)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
gethostport(char * str,int lnum,u_32_t * ipp,u_short * portp)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 *
mapfile(char * file,size_t * sizep)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
readconfig(char * filename)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
usage(char * prog)751 usage(char *prog)
752 {
753 fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
754 exit(1);
755 }
756
757
758 int
main(int argc,char * argv[])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