diff options
author | ngie <ngie@FreeBSD.org> | 2015-10-05 03:26:51 +0000 |
---|---|---|
committer | ngie <ngie@FreeBSD.org> | 2015-10-05 03:26:51 +0000 |
commit | e1dd16d965b177f109afb771e59432e36f335d0a (patch) | |
tree | 15db092a5401cf329f1bff9d3bf700d1fde0f121 /contrib/ipfilter/l4check | |
parent | 115d008392113efc6f844baa7cc407e9eaae63db (diff) | |
download | FreeBSD-src-e1dd16d965b177f109afb771e59432e36f335d0a.zip FreeBSD-src-e1dd16d965b177f109afb771e59432e36f335d0a.tar.gz |
Revert r288682
I meant to do this on ^/user/ngie/more-tests
Pointyhat to: ngie (use svn info next time...)
Diffstat (limited to 'contrib/ipfilter/l4check')
-rw-r--r-- | contrib/ipfilter/l4check/Makefile | 10 | ||||
-rw-r--r-- | contrib/ipfilter/l4check/http.check | 2 | ||||
-rw-r--r-- | contrib/ipfilter/l4check/http.ok | 1 | ||||
-rw-r--r-- | contrib/ipfilter/l4check/l4check.c | 807 | ||||
-rw-r--r-- | contrib/ipfilter/l4check/l4check.conf | 31 |
5 files changed, 851 insertions, 0 deletions
diff --git a/contrib/ipfilter/l4check/Makefile b/contrib/ipfilter/l4check/Makefile new file mode 100644 index 0000000..e7366b6 --- /dev/null +++ b/contrib/ipfilter/l4check/Makefile @@ -0,0 +1,10 @@ +# For Solaris +#LIBS=-lsocket -lnsl + +all: l4check + +l4check: l4check.c + $(CC) -g -I.. $(CFLAGS) $(LIBS) l4check.c -o $@ + +clean: + /bin/rm -f l4check diff --git a/contrib/ipfilter/l4check/http.check b/contrib/ipfilter/l4check/http.check new file mode 100644 index 0000000..56d93d9 --- /dev/null +++ b/contrib/ipfilter/l4check/http.check @@ -0,0 +1,2 @@ +GET / + diff --git a/contrib/ipfilter/l4check/http.ok b/contrib/ipfilter/l4check/http.ok new file mode 100644 index 0000000..2b5d2c1 --- /dev/null +++ b/contrib/ipfilter/l4check/http.ok @@ -0,0 +1 @@ +<HTML>
\ No newline at end of file diff --git a/contrib/ipfilter/l4check/l4check.c b/contrib/ipfilter/l4check/l4check.c new file mode 100644 index 0000000..014446d --- /dev/null +++ b/contrib/ipfilter/l4check/l4check.c @@ -0,0 +1,807 @@ +/* $FreeBSD$ */ + +/* + * (C)Copyright (C) 2012 by Darren Reed. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> + +#include <net/if.h> + +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> + +#include "ip_compat.h" +#include "ip_fil.h" +#include "ip_nat.h" + +#include "ipf.h" + +extern char *optarg; + + +typedef struct l4cfg { + struct l4cfg *l4_next; + struct ipnat l4_nat; /* NAT rule */ + struct sockaddr_in l4_sin; /* remote socket to connect */ + time_t l4_last; /* when we last connected */ + int l4_alive; /* 1 = remote alive */ + int l4_fd; + int l4_rw; /* 0 = reading, 1 = writing */ + char *l4_rbuf; /* read buffer */ + int l4_rsize; /* size of buffer */ + int l4_rlen; /* how much used */ + char *l4_wptr; /* next byte to write */ + int l4_wlen; /* length yet to be written */ +} l4cfg_t; + + +l4cfg_t *l4list = NULL; +char *response = NULL; +char *probe = NULL; +l4cfg_t template; +int frequency = 20; +int ctimeout = 1; +int rtimeout = 1; +size_t plen = 0; +size_t rlen = 0; +int natfd = -1; +int opts = 0; + +#if defined(sun) && !defined(__svr4__) && !defined(__SVR4) +# define strerror(x) sys_errlist[x] +#endif + + +char *copystr(dst, src) + char *dst, *src; +{ + register char *s, *t, c; + register int esc = 0; + + for (s = src, t = dst; s && t && (c = *s++); ) + if (esc) { + esc = 0; + switch (c) + { + case 'n' : + *t++ = '\n'; + break; + case 'r' : + *t++ = '\r'; + break; + case 't' : + *t++ = '\t'; + break; + } + } else if (c != '\\') + *t++ = c; + else + esc = 1; + *t = '\0'; + return dst; +} + +void addnat(l4) + l4cfg_t *l4; +{ + ipnat_t *ipn = &l4->l4_nat; + + printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]), + ipn->in_outmsk, ntohs(ipn->in_pmin)); + printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext)); + if (!(opts & OPT_DONOTHING)) { + if (ioctl(natfd, SIOCADNAT, &ipn) == -1) + perror("ioctl(SIOCADNAT)"); + } +} + + +void delnat(l4) + l4cfg_t *l4; +{ + ipnat_t *ipn = &l4->l4_nat; + + printf("Remove NAT rule for %s/%#x,%u -> ", + inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin); + printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext); + if (!(opts & OPT_DONOTHING)) { + if (ioctl(natfd, SIOCRMNAT, &ipn) == -1) + perror("ioctl(SIOCRMNAT)"); + } +} + + +void connectl4(l4) + l4cfg_t *l4; +{ + l4->l4_rw = 1; + l4->l4_rlen = 0; + l4->l4_wlen = plen; + if (!l4->l4_wlen) { + l4->l4_alive = 1; + addnat(l4); + } else + l4->l4_wptr = probe; +} + + +void closel4(l4, dead) + l4cfg_t *l4; + int dead; +{ + close(l4->l4_fd); + l4->l4_fd = -1; + l4->l4_rw = -1; + if (dead && l4->l4_alive) { + l4->l4_alive = 0; + delnat(l4); + } +} + + +void connectfd(l4) + l4cfg_t *l4; +{ + if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin, + sizeof(l4->l4_sin)) == -1) { + if (errno == EISCONN) { + if (opts & OPT_VERBOSE) + fprintf(stderr, "Connected fd %d\n", + l4->l4_fd); + connectl4(l4); + return; + } + if (opts & OPT_VERBOSE) + fprintf(stderr, "Connect failed fd %d: %s\n", + l4->l4_fd, strerror(errno)); + closel4(l4, 1); + return; + } + l4->l4_rw = 1; +} + + +void writefd(l4) + l4cfg_t *l4; +{ + char buf[80], *ptr; + int n, i, fd; + + fd = l4->l4_fd; + + if (l4->l4_rw == -2) { + connectfd(l4); + return; + } + + n = l4->l4_wlen; + + i = send(fd, l4->l4_wptr, n, 0); + if (i == 0 || i == -1) { + if (opts & OPT_VERBOSE) + fprintf(stderr, "Send on fd %d failed: %s\n", + fd, strerror(errno)); + closel4(l4, 1); + } else { + l4->l4_wptr += i; + l4->l4_wlen -= i; + if (l4->l4_wlen == 0) + l4->l4_rw = 0; + if (opts & OPT_VERBOSE) + fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd); + } +} + + +void readfd(l4) + l4cfg_t *l4; +{ + char buf[80], *ptr; + int n, i, fd; + + fd = l4->l4_fd; + + if (l4->l4_rw == -2) { + connectfd(l4); + return; + } + + if (l4->l4_rsize) { + n = l4->l4_rsize - l4->l4_rlen; + ptr = l4->l4_rbuf + l4->l4_rlen; + } else { + n = sizeof(buf) - 1; + ptr = buf; + } + + if (opts & OPT_VERBOSE) + fprintf(stderr, "Read %d bytes on fd %d to %p\n", + n, fd, ptr); + i = recv(fd, ptr, n, 0); + if (i == 0 || i == -1) { + if (opts & OPT_VERBOSE) + fprintf(stderr, "Read error on fd %d: %s\n", + fd, (i == 0) ? "EOF" : strerror(errno)); + closel4(l4, 1); + } else { + if (ptr == buf) + ptr[i] = '\0'; + if (opts & OPT_VERBOSE) + fprintf(stderr, "%d: Read %d bytes [%*.*s]\n", + fd, i, i, i, ptr); + if (ptr != buf) { + l4->l4_rlen += i; + if (l4->l4_rlen >= l4->l4_rsize) { + if (!strncmp(response, l4->l4_rbuf, + l4->l4_rsize)) { + printf("%d: Good response\n", + fd); + if (!l4->l4_alive) { + l4->l4_alive = 1; + addnat(l4); + } + closel4(l4, 0); + } else { + if (opts & OPT_VERBOSE) + printf("%d: Bad response\n", + fd); + closel4(l4, 1); + } + } + } else if (!l4->l4_alive) { + l4->l4_alive = 1; + addnat(l4); + closel4(l4, 0); + } + } +} + + +int runconfig() +{ + int fd, opt, res, mfd, i; + struct timeval tv; + time_t now, now1; + fd_set rfd, wfd; + l4cfg_t *l4; + + mfd = 0; + opt = 1; + now = time(NULL); + + /* + * First, initiate connections that are closed, as required. + */ + for (l4 = l4list; l4; l4 = l4->l4_next) { + if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) { + l4->l4_last = now; + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) + continue; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, + sizeof(opt)); +#ifdef O_NONBLOCK + if ((res = fcntl(fd, F_GETFL, 0)) != -1) + fcntl(fd, F_SETFL, res | O_NONBLOCK); +#endif + if (opts & OPT_VERBOSE) + fprintf(stderr, + "Connecting to %s,%d (fd %d)...", + inet_ntoa(l4->l4_sin.sin_addr), + ntohs(l4->l4_sin.sin_port), fd); + if (connect(fd, (struct sockaddr *)&l4->l4_sin, + sizeof(l4->l4_sin)) == -1) { + if (errno != EINPROGRESS) { + if (opts & OPT_VERBOSE) + fprintf(stderr, "failed\n"); + perror("connect"); + close(fd); + fd = -1; + } else { + if (opts & OPT_VERBOSE) + fprintf(stderr, "waiting\n"); + l4->l4_rw = -2; + } + } else { + if (opts & OPT_VERBOSE) + fprintf(stderr, "connected\n"); + connectl4(l4); + } + l4->l4_fd = fd; + } + } + + /* + * Now look for fd's which we're expecting to read/write from. + */ + FD_ZERO(&rfd); + FD_ZERO(&wfd); + tv.tv_sec = MIN(rtimeout, ctimeout); + tv.tv_usec = 0; + + for (l4 = l4list; l4; l4 = l4->l4_next) + if (l4->l4_rw == 0) { + if (now - l4->l4_last > rtimeout) { + if (opts & OPT_VERBOSE) + fprintf(stderr, "%d: Read timeout\n", + l4->l4_fd); + closel4(l4, 1); + continue; + } + if (opts & OPT_VERBOSE) + fprintf(stderr, "Wait for read on fd %d\n", + l4->l4_fd); + FD_SET(l4->l4_fd, &rfd); + if (l4->l4_fd > mfd) + mfd = l4->l4_fd; + } else if ((l4->l4_rw == 1 && l4->l4_wlen) || + l4->l4_rw == -2) { + if ((l4->l4_rw == -2) && + (now - l4->l4_last > ctimeout)) { + if (opts & OPT_VERBOSE) + fprintf(stderr, + "%d: connect timeout\n", + l4->l4_fd); + closel4(l4); + continue; + } + if (opts & OPT_VERBOSE) + fprintf(stderr, "Wait for write on fd %d\n", + l4->l4_fd); + FD_SET(l4->l4_fd, &wfd); + if (l4->l4_fd > mfd) + mfd = l4->l4_fd; + } + + if (opts & OPT_VERBOSE) + fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1, + tv.tv_sec); + i = select(mfd + 1, &rfd, &wfd, NULL, &tv); + if (i == -1) { + perror("select"); + return -1; + } + + now1 = time(NULL); + + for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) { + if (l4->l4_fd < 0) + continue; + if (FD_ISSET(l4->l4_fd, &rfd)) { + if (opts & OPT_VERBOSE) + fprintf(stderr, "Ready to read on fd %d\n", + l4->l4_fd); + readfd(l4); + i--; + } + + if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) { + if (opts & OPT_VERBOSE) + fprintf(stderr, "Ready to write on fd %d\n", + l4->l4_fd); + writefd(l4); + i--; + } + } + return 0; +} + + +int gethostport(str, lnum, ipp, portp) + char *str; + int lnum; + u_32_t *ipp; + u_short *portp; +{ + struct servent *sp; + struct hostent *hp; + char *host, *port; + struct in_addr ip; + + host = str; + port = strchr(host, ','); + if (port) + *port++ = '\0'; + +#ifdef HAVE_INET_ATON + if (ISDIGIT(*host) && inet_aton(host, &ip)) + *ipp = ip.s_addr; +#else + if (ISDIGIT(*host)) + *ipp = inet_addr(host); +#endif + else { + if (!(hp = gethostbyname(host))) { + fprintf(stderr, "%d: can't resolve hostname: %s\n", + lnum, host); + return 0; + } + *ipp = *(u_32_t *)hp->h_addr; + } + + if (port) { + if (ISDIGIT(*port)) + *portp = htons(atoi(port)); + else { + sp = getservbyname(port, "tcp"); + if (sp) + *portp = sp->s_port; + else { + fprintf(stderr, "%d: unknown service %s\n", + lnum, port); + return 0; + } + } + } else + *portp = 0; + return 1; +} + + +char *mapfile(file, sizep) + char *file; + size_t *sizep; +{ + struct stat sb; + caddr_t addr; + int fd; + + fd = open(file, O_RDONLY); + if (fd == -1) { + perror("open(mapfile)"); + return NULL; + } + + if (fstat(fd, &sb) == -1) { + perror("fstat(mapfile)"); + close(fd); + return NULL; + } + + addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (addr == (caddr_t)-1) { + perror("mmap(mapfile)"); + close(fd); + return NULL; + } + close(fd); + *sizep = sb.st_size; + return (char *)addr; +} + + +int readconfig(filename) + char *filename; +{ + char c, buf[512], *s, *t, *errtxt = NULL, *line; + int num, err = 0; + ipnat_t *ipn; + l4cfg_t *l4; + FILE *fp; + + fp = fopen(filename, "r"); + if (!fp) { + perror("open(configfile)"); + return -1; + } + + bzero((char *)&template, sizeof(template)); + template.l4_fd = -1; + template.l4_rw = -1; + template.l4_sin.sin_family = AF_INET; + ipn = &template.l4_nat; + ipn->in_flags = IPN_TCP|IPN_ROUNDR; + ipn->in_redir = NAT_REDIRECT; + + for (num = 1; fgets(buf, sizeof(buf), fp); num++) { + s = strchr(buf, '\n'); + if (!s) { + fprintf(stderr, "%d: line too long\n", num); + fclose(fp); + return -1; + } + + *s = '\0'; + + /* + * lines which are comments + */ + s = strchr(buf, '#'); + if (s) + *s = '\0'; + + /* + * Skip leading whitespace + */ + for (line = buf; (c = *line) && ISSPACE(c); line++) + ; + if (!*line) + continue; + + if (opts & OPT_VERBOSE) + fprintf(stderr, "Parsing: [%s]\n", line); + t = strtok(line, " \t"); + if (!t) + continue; + if (!strcasecmp(t, "interface")) { + s = strtok(NULL, " \t"); + if (s) + t = strtok(NULL, "\t"); + if (!s || !t) { + errtxt = line; + err = -1; + break; + } + + if (!strchr(t, ',')) { + fprintf(stderr, + "%d: local address,port missing\n", + num); + err = -1; + break; + } + + strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname)); + if (!gethostport(t, num, &ipn->in_outip, + &ipn->in_pmin)) { + errtxt = line; + err = -1; + break; + } + ipn->in_outmsk = 0xffffffff; + ipn->in_pmax = ipn->in_pmin; + if (opts & OPT_VERBOSE) + fprintf(stderr, + "Interface %s %s/%#x port %u\n", + ipn->in_ifname, + inet_ntoa(ipn->in_out[0]), + ipn->in_outmsk, ipn->in_pmin); + } else if (!strcasecmp(t, "remote")) { + if (!*ipn->in_ifname) { + fprintf(stderr, + "%d: ifname not set prior to remote\n", + num); + err = -1; + break; + } + s = strtok(NULL, " \t"); + if (s) + t = strtok(NULL, ""); + if (!s || !t || strcasecmp(s, "server")) { + errtxt = line; + err = -1; + break; + } + + ipn->in_pnext = 0; + if (!gethostport(t, num, &ipn->in_inip, + &ipn->in_pnext)) { + errtxt = line; + err = -1; + break; + } + ipn->in_inmsk = 0xffffffff; + if (ipn->in_pnext == 0) + ipn->in_pnext = ipn->in_pmin; + + l4 = (l4cfg_t *)malloc(sizeof(*l4)); + if (!l4) { + fprintf(stderr, "%d: out of memory (%d)\n", + num, sizeof(*l4)); + err = -1; + break; + } + bcopy((char *)&template, (char *)l4, sizeof(*l4)); + l4->l4_sin.sin_addr = ipn->in_in[0]; + l4->l4_sin.sin_port = ipn->in_pnext; + l4->l4_next = l4list; + l4list = l4; + } else if (!strcasecmp(t, "connect")) { + s = strtok(NULL, " \t"); + if (s) + t = strtok(NULL, "\t"); + if (!s || !t) { + errtxt = line; + err = -1; + break; + } else if (!strcasecmp(s, "timeout")) { + ctimeout = atoi(t); + if (opts & OPT_VERBOSE) + fprintf(stderr, "connect timeout %d\n", + ctimeout); + } else if (!strcasecmp(s, "frequency")) { + frequency = atoi(t); + if (opts & OPT_VERBOSE) + fprintf(stderr, + "connect frequency %d\n", + frequency); + } else { + errtxt = line; + err = -1; + break; + } + } else if (!strcasecmp(t, "probe")) { + s = strtok(NULL, " \t"); + if (!s) { + errtxt = line; + err = -1; + break; + } else if (!strcasecmp(s, "string")) { + if (probe) { + fprintf(stderr, + "%d: probe already set\n", + num); + err = -1; + break; + } + t = strtok(NULL, ""); + if (!t) { + fprintf(stderr, + "%d: No probe string\n", num); + err = -1; + break; + } + + probe = malloc(strlen(t)); + copystr(probe, t); + plen = strlen(probe); + if (opts & OPT_VERBOSE) + fprintf(stderr, "Probe string [%s]\n", + probe); + } else if (!strcasecmp(s, "file")) { + t = strtok(NULL, " \t"); + if (!t) { + errtxt = line; + err = -1; + break; + } + if (probe) { + fprintf(stderr, + "%d: probe already set\n", + num); + err = -1; + break; + } + probe = mapfile(t, &plen); + if (opts & OPT_VERBOSE) + fprintf(stderr, + "Probe file %s len %u@%p\n", + t, plen, probe); + } + } else if (!strcasecmp(t, "response")) { + s = strtok(NULL, " \t"); + if (!s) { + errtxt = line; + err = -1; + break; + } else if (!strcasecmp(s, "timeout")) { + t = strtok(NULL, " \t"); + if (!t) { + errtxt = line; + err = -1; + break; + } + rtimeout = atoi(t); + if (opts & OPT_VERBOSE) + fprintf(stderr, + "response timeout %d\n", + rtimeout); + } else if (!strcasecmp(s, "string")) { + if (response) { + fprintf(stderr, + "%d: response already set\n", + num); + err = -1; + break; + } + response = strdup(strtok(NULL, "")); + rlen = strlen(response); + template.l4_rsize = rlen; + template.l4_rbuf = malloc(rlen); + if (opts & OPT_VERBOSE) + fprintf(stderr, + "Response string [%s]\n", + response); + } else if (!strcasecmp(s, "file")) { + t = strtok(NULL, " \t"); + if (!t) { + errtxt = line; + err = -1; + break; + } + if (response) { + fprintf(stderr, + "%d: response already set\n", + num); + err = -1; + break; + } + response = mapfile(t, &rlen); + template.l4_rsize = rlen; + template.l4_rbuf = malloc(rlen); + if (opts & OPT_VERBOSE) + fprintf(stderr, + "Response file %s len %u@%p\n", + t, rlen, response); + } + } else { + errtxt = line; + err = -1; + break; + } + } + + if (errtxt) + fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt); + fclose(fp); + return err; +} + + +void usage(prog) + char *prog; +{ + fprintf(stderr, "Usage: %s -f <configfile>\n", prog); + exit(1); +} + + +int main(argc, argv) + int argc; + char *argv[]; +{ + char *config = NULL; + int c; + + while ((c = getopt(argc, argv, "f:nv")) != -1) + switch (c) + { + case 'f' : + config = optarg; + break; + case 'n' : + opts |= OPT_DONOTHING; + break; + case 'v' : + opts |= OPT_VERBOSE; + break; + } + + if (config == NULL) + usage(argv[0]); + + if (readconfig(config)) + exit(1); + + if (!l4list) { + fprintf(stderr, "No remote servers, exiting."); + exit(1); + } + + if (!(opts & OPT_DONOTHING)) { + natfd = open(IPL_NAT, O_RDWR); + if (natfd == -1) { + perror("open(IPL_NAT)"); + exit(1); + } + } + + if (opts & OPT_VERBOSE) + fprintf(stderr, "Starting...\n"); + while (runconfig() == 0) + ; +} diff --git a/contrib/ipfilter/l4check/l4check.conf b/contrib/ipfilter/l4check/l4check.conf new file mode 100644 index 0000000..d000e9f --- /dev/null +++ b/contrib/ipfilter/l4check/l4check.conf @@ -0,0 +1,31 @@ +# +# NOTE: ORDER IS IMPORTANT IN THIS FILE +# +# Interface to do the redirections on and the IP address which will be +# targeted. +# +interface nf0 192.168.1.1,2100 +# +connect timeout 1 +connect frequency 20 +# +# If no probe string is specified, a successful connection implies the +# server is still alive. +# +probe string GET /\n\n +#probe file http.check +# +response timeout 4 +response string <HTML> +#response file http.ok +# +# Here we have multiple servers, listed because that's what happens to be +# used for testing of connect timeoutes, read timeouts, success and things +# which don't connect. +# +remote server 192.168.1.2,23 +remote server 192.168.1.2,2101 +remote server 192.168.1.3,25 +remote server 192.168.1.254,8000 +remote server 192.168.1.1,9 +# |