1/* 2 * Copyright (c) 2013 Patrick McHardy <kaber@trash.net> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9#include <stdlib.h> 10#include <stdbool.h> 11#include <unistd.h> 12#include <string.h> 13#include <errno.h> 14#include <getopt.h> 15#include <sys/types.h> 16#include <sys/socket.h> 17#include <netinet/in.h> 18#include <arpa/inet.h> 19#include <pcap/pcap.h> 20#include <netinet/ip.h> 21#include <netinet/tcp.h> 22 23static const char *iface = "lo"; 24static uint16_t port; 25static const char *chain = "SYNPROXY"; 26 27static int parse_packet(const char *host, const uint8_t *data) 28{ 29 const struct iphdr *iph = (void *)data + 14; 30 const struct tcphdr *th = (void *)iph + iph->ihl * 4; 31 int length; 32 uint8_t *ptr; 33 34 if (!th->syn || !th->ack) 35 return 0; 36 37 printf("-A %s -d %s -p tcp --dport %u " 38 "-m state --state UNTRACKED,INVALID " 39 "-j SYNPROXY ", chain, host, port); 40 41 /* ECE && !CWR */ 42 if (th->res2 == 0x1) 43 printf("--ecn "); 44 45 length = th->doff * 4 - sizeof(*th); 46 ptr = (uint8_t *)(th + 1); 47 while (length > 0) { 48 int opcode = *ptr++; 49 int opsize; 50 51 switch (opcode) { 52 case TCPOPT_EOL: 53 return 1; 54 case TCPOPT_NOP: 55 length--; 56 continue; 57 default: 58 opsize = *ptr++; 59 if (opsize < 2) 60 return 1; 61 if (opsize > length) 62 return 1; 63 64 switch (opcode) { 65 case TCPOPT_MAXSEG: 66 if (opsize == TCPOLEN_MAXSEG) 67 printf("--mss %u ", ntohs(*(uint16_t *)ptr)); 68 break; 69 case TCPOPT_WINDOW: 70 if (opsize == TCPOLEN_WINDOW) 71 printf("--wscale %u ", *ptr); 72 break; 73 case TCPOPT_TIMESTAMP: 74 if (opsize == TCPOLEN_TIMESTAMP) 75 printf("--timestamp "); 76 break; 77 case TCPOPT_SACK_PERMITTED: 78 if (opsize == TCPOLEN_SACK_PERMITTED) 79 printf("--sack-perm "); 80 break; 81 } 82 83 ptr += opsize - 2; 84 length -= opsize; 85 } 86 } 87 printf("\n"); 88 return 1; 89} 90 91static void probe_host(const char *host) 92{ 93 struct sockaddr_in sin; 94 char pcap_errbuf[PCAP_ERRBUF_SIZE]; 95 struct pcap_pkthdr pkthdr; 96 const uint8_t *data; 97 struct bpf_program fp; 98 pcap_t *ph; 99 int fd; 100 101 ph = pcap_create(iface, pcap_errbuf); 102 if (ph == NULL) { 103 perror("pcap_create"); 104 goto err1; 105 } 106 107 if (pcap_setnonblock(ph, 1, pcap_errbuf) == -1) { 108 perror("pcap_setnonblock"); 109 goto err2; 110 } 111 112 if (pcap_setfilter(ph, &fp) == -1) { 113 pcap_perror(ph, "pcap_setfilter"); 114 goto err2; 115 } 116 117 if (pcap_activate(ph) != 0) { 118 pcap_perror(ph, "pcap_activate"); 119 goto err2; 120 } 121 122 if (pcap_compile(ph, &fp, "src host 127.0.0.1 and tcp and src port 80", 123 1, PCAP_NETMASK_UNKNOWN) == -1) { 124 pcap_perror(ph, "pcap_compile"); 125 goto err2; 126 } 127 128 fd = socket(AF_INET, SOCK_STREAM, 0); 129 if (fd < 0) { 130 perror("socket"); 131 goto err3; 132 } 133 134 memset(&sin, 0, sizeof(sin)); 135 sin.sin_family = AF_INET; 136 sin.sin_port = htons(port); 137 sin.sin_addr.s_addr = inet_addr(host); 138 139 if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 140 perror("connect"); 141 goto err4; 142 } 143 144 for (;;) { 145 data = pcap_next(ph, &pkthdr); 146 if (data == NULL) 147 break; 148 if (parse_packet(host, data)) 149 break; 150 } 151 152 close(fd); 153 154err4: 155 close(fd); 156err3: 157 pcap_freecode(&fp); 158err2: 159 pcap_close(ph); 160err1: 161 return; 162} 163 164enum { 165 OPT_HELP = 'h', 166 OPT_IFACE = 'i', 167 OPT_PORT = 'p', 168 OPT_CHAIN = 'c', 169}; 170 171static const struct option options[] = { 172 { .name = "help", .has_arg = false, .val = OPT_HELP }, 173 { .name = "iface", .has_arg = true, .val = OPT_IFACE }, 174 { .name = "port" , .has_arg = true, .val = OPT_PORT }, 175 { .name = "chain", .has_arg = true, .val = OPT_CHAIN }, 176 { } 177}; 178 179static void print_help(const char *name) 180{ 181 printf("%s [ options ] address...\n" 182 "\n" 183 "Options:\n" 184 " -i/--iface Outbound interface\n" 185 " -p/--port Port number to probe\n" 186 " -c/--chain Chain name to use for rules\n" 187 " -h/--help Show this help\n", 188 name); 189} 190 191int main(int argc, char **argv) 192{ 193 int optidx = 0, c; 194 195 for (;;) { 196 c = getopt_long(argc, argv, "hi:p:c:", options, &optidx); 197 if (c == -1) 198 break; 199 200 switch (c) { 201 case OPT_IFACE: 202 iface = optarg; 203 break; 204 case OPT_PORT: 205 port = atoi(optarg); 206 break; 207 case OPT_CHAIN: 208 chain = optarg; 209 break; 210 case OPT_HELP: 211 print_help(argv[0]); 212 exit(0); 213 case '?': 214 print_help(argv[0]); 215 exit(1); 216 } 217 } 218 219 argc -= optind; 220 argv += optind; 221 222 while (argc > 0) { 223 probe_host(*argv); 224 argc--; 225 argv++; 226 } 227 return 0; 228} 229