]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
utils: add nfsynproxy tool
authorPatrick McHardy <kaber@trash.net>
Wed, 28 Aug 2013 07:32:44 +0000 (09:32 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 18 Nov 2013 17:47:04 +0000 (18:47 +0100)
[ 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 <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
configure.ac
utils/Makefile.am
utils/nfsynproxy.c [new file with mode: 0644]

index 76d0b37bc960724063f83baa2656e33ba0e726b7..7750a3d7ed465e9c83615ccf1019322f1c30b45c 100644 (file)
@@ -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}
index c26aa6405e0e35d760025d2220a4b172e2cf6475..c4192a9e73688ee7cc659e9d719a484c04fd1132 100644 (file)
@@ -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 (file)
index 0000000..9b6de93
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
+ *
+ * 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 <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pcap/pcap.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+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;
+}