1 /* $NetBSD: l4check.c,v 1.1.1.2 2012/07/22 13:44:38 darrenr Exp $ */
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
copystr(dst,src)69 char *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
addnat(l4)98 void 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
delnat(l4)113 void 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
connectl4(l4)128 void 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
closel4(l4,dead)142 void 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
connectfd(l4)156 void 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
writefd(l4)178 void 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
readfd(l4)210 void 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
runconfig()274 int 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
gethostport(str,lnum,ipp,portp)404 int 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
mapfile(file,sizep)455 char *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
readconfig(filename)487 int 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
usage(prog)755 void usage(prog)
756 char *prog;
757 {
758 fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
759 exit(1);
760 }
761
762
main(argc,argv)763 int 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