diff options
Diffstat (limited to 'utils/nfsynproxy.c')
-rw-r--r-- | utils/nfsynproxy.c | 228 |
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; +} |