summaryrefslogtreecommitdiff
path: root/utils/nfsynproxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/nfsynproxy.c')
-rw-r--r--utils/nfsynproxy.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/utils/nfsynproxy.c b/utils/nfsynproxy.c
new file mode 100644
index 0000000..baedc92
--- /dev/null
+++ b/utils/nfsynproxy.c
@@ -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_setfilter(ph, &fp) == -1) {
+ pcap_perror(ph, "pcap_setfilter");
+ goto err2;
+ }
+
+ if (pcap_activate(ph) != 0) {
+ pcap_perror(ph, "pcap_activate");
+ 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;
+ }
+
+ 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;
+}