From 9e6928f037823773a37630dec5a764455dcea6fb Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 28 Aug 2013 09:32:44 +0200 Subject: utils: add nfsynproxy tool [ Originally synconf, but Jesper D. Brouer suggested to change the name to avoid a possible filename clash. I also include nfsynproxy in the final configure report --pablo ] Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- configure.ac | 9 ++- utils/Makefile.am | 5 ++ utils/nfsynproxy.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 utils/nfsynproxy.c diff --git a/configure.ac b/configure.ac index 76d0b37b..7750a3d7 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,9 @@ AC_ARG_ENABLE([libipq], AC_ARG_ENABLE([bpf-compiler], AS_HELP_STRING([--enable-bpf-compiler], [Build bpf compiler]), [enable_bpfc="yes"], [enable_bpfc="no"]) +AC_ARG_ENABLE([nfsynproxy], + AS_HELP_STRING([--enable-nfsynproxy], [Build SYNPROXY configuration tool]), + [enable_nfsynproxy="yes"], [enable_nfsynproxy="no"]) AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH], [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]), [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig']) @@ -102,9 +105,10 @@ AM_CONDITIONAL([ENABLE_LARGEFILE], [test "$enable_largefile" = "yes"]) AM_CONDITIONAL([ENABLE_DEVEL], [test "$enable_devel" = "yes"]) AM_CONDITIONAL([ENABLE_LIBIPQ], [test "$enable_libipq" = "yes"]) AM_CONDITIONAL([ENABLE_BPFC], [test "$enable_bpfc" = "yes"]) +AM_CONDITIONAL([ENABLE_SYNCONF], [test "$enable_nfsynproxy" = "yes"]) -if test "x$enable_bpfc" = "xyes"; then - AC_CHECK_LIB(pcap, pcap_compile,, AC_MSG_ERROR(missing libpcap library required by bpf compiler)) +if test "x$enable_bpfc" = "xyes" || test "x$enable_nfsynproxy" = "xyes"; then + AC_CHECK_LIB(pcap, pcap_compile,, AC_MSG_ERROR(missing libpcap library required by bpf compiler or nfsynproxy tool)) fi PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0], @@ -177,6 +181,7 @@ Iptables Configuration: IPQ support: ${enable_libipq} Large file support: ${enable_largefile} BPF utils support: ${enable_bpfc} + nfsynproxy util support: ${enable_nfsynproxy} Build parameters: Put plugins into executable (static): ${enable_static} diff --git a/utils/Makefile.am b/utils/Makefile.am index c26aa640..c4192a9e 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -18,3 +18,8 @@ if ENABLE_BPFC sbin_PROGRAMS += nfbpf_compile nfbpf_compile_LDADD = -lpcap endif + +if ENABLE_SYNCONF +sbin_PROGRAMS += nfsynproxy +nfsynproxy_LDADD = -lpcap +endif diff --git a/utils/nfsynproxy.c b/utils/nfsynproxy.c new file mode 100644 index 00000000..9b6de93b --- /dev/null +++ b/utils/nfsynproxy.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2013 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *iface = "lo"; +static uint16_t port; +static const char *chain = "SYNPROXY"; + +static int parse_packet(const char *host, const uint8_t *data) +{ + const struct iphdr *iph = (void *)data + 14; + const struct tcphdr *th = (void *)iph + iph->ihl * 4; + int length; + uint8_t *ptr; + + if (!th->syn || !th->ack) + return 0; + + printf("-A %s -d %s -p tcp --dport %u " + "-m state --state UNTRACKED,INVALID " + "-j SYNPROXY ", chain, host, port); + + /* ECE && !CWR */ + if (th->res2 == 0x1) + printf("--ecn "); + + length = th->doff * 4 - sizeof(*th); + ptr = (uint8_t *)(th + 1); + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return 1; + case TCPOPT_NOP: + length--; + continue; + default: + opsize = *ptr++; + if (opsize < 2) + return 1; + if (opsize > length) + return 1; + + switch (opcode) { + case TCPOPT_MAXSEG: + if (opsize == TCPOLEN_MAXSEG) + printf("--mss %u ", ntohs(*(uint16_t *)ptr)); + break; + case TCPOPT_WINDOW: + if (opsize == TCPOLEN_WINDOW) + printf("--wscale %u ", *ptr); + break; + case TCPOPT_TIMESTAMP: + if (opsize == TCPOLEN_TIMESTAMP) + printf("--timestamp "); + break; + case TCPOPT_SACK_PERMITTED: + if (opsize == TCPOLEN_SACK_PERMITTED) + printf("--sack-perm "); + break; + } + + ptr += opsize - 2; + length -= opsize; + } + } + printf("\n"); + return 1; +} + +static void probe_host(const char *host) +{ + struct sockaddr_in sin; + char pcap_errbuf[PCAP_ERRBUF_SIZE]; + struct pcap_pkthdr pkthdr; + const uint8_t *data; + struct bpf_program fp; + pcap_t *ph; + int fd; + + ph = pcap_create(iface, pcap_errbuf); + if (ph == NULL) { + perror("pcap_create"); + goto err1; + } + + if (pcap_setnonblock(ph, 1, pcap_errbuf) == -1) { + perror("pcap_setnonblock"); + goto err2; + } + + if (pcap_compile(ph, &fp, "src host 127.0.0.1 and tcp and src port 80", + 1, PCAP_NETMASK_UNKNOWN) == -1) { + pcap_perror(ph, "pcap_compile"); + goto err2; + } + + if (pcap_setfilter(ph, &fp) == -1) { + pcap_perror(ph, "pcap_setfilter"); + goto err3; + } + + if (pcap_activate(ph) != 0) { + pcap_perror(ph, "pcap_activate"); + goto err3; + } + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + goto err3; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = inet_addr(host); + + if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + perror("connect"); + goto err4; + } + + for (;;) { + data = pcap_next(ph, &pkthdr); + if (data == NULL) + break; + if (parse_packet(host, data)) + break; + } + + close(fd); + +err4: + close(fd); +err3: + pcap_freecode(&fp); +err2: + pcap_close(ph); +err1: + return; +} + +enum { + OPT_HELP = 'h', + OPT_IFACE = 'i', + OPT_PORT = 'p', + OPT_CHAIN = 'c', +}; + +static const struct option options[] = { + { .name = "help", .has_arg = false, .val = OPT_HELP }, + { .name = "iface", .has_arg = true, .val = OPT_IFACE }, + { .name = "port" , .has_arg = true, .val = OPT_PORT }, + { .name = "chain", .has_arg = true, .val = OPT_CHAIN }, + { } +}; + +static void print_help(const char *name) +{ + printf("%s [ options ] address...\n" + "\n" + "Options:\n" + " -i/--iface Outbound interface\n" + " -p/--port Port number to probe\n" + " -c/--chain Chain name to use for rules\n" + " -h/--help Show this help\n", + name); +} + +int main(int argc, char **argv) +{ + int optidx = 0, c; + + for (;;) { + c = getopt_long(argc, argv, "hi:p:c:", options, &optidx); + if (c == -1) + break; + + switch (c) { + case OPT_IFACE: + iface = optarg; + break; + case OPT_PORT: + port = atoi(optarg); + break; + case OPT_CHAIN: + chain = optarg; + break; + case OPT_HELP: + print_help(argv[0]); + exit(0); + case '?': + print_help(argv[0]); + exit(1); + } + } + + argc -= optind; + argv += optind; + + while (argc > 0) { + probe_host(*argv); + argc--; + argv++; + } + return 0; +} -- cgit v1.2.3