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