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
23 static const char *iface = "lo";
24 static uint16_t port;
25 static const char *chain = "SYNPROXY";
26
parse_packet(const char * host,const uint8_t * data)27 static 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
probe_host(const char * host)91 static 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
154 err4:
155 close(fd);
156 err3:
157 pcap_freecode(&fp);
158 err2:
159 pcap_close(ph);
160 err1:
161 return;
162 }
163
164 enum {
165 OPT_HELP = 'h',
166 OPT_IFACE = 'i',
167 OPT_PORT = 'p',
168 OPT_CHAIN = 'c',
169 };
170
171 static 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
print_help(const char * name)179 static 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
main(int argc,char ** argv)191 int 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