/* * 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; }