]> git.saurik.com Git - apple/network_cmds.git/blame - ecnprobe/ecn_probe.c
network_cmds-606.40.2.tar.gz
[apple/network_cmds.git] / ecnprobe / ecn_probe.c
CommitLineData
89c4ed63
A
1/*
2 Copyright (c) 2000
3 International Computer Science Institute
4 All rights reserved.
5
6 This file may contain software code originally developed for the
7 Sting project. The Sting software carries the following copyright:
8
9 Copyright (c) 1998, 1999
10 Stefan Savage and the University of Washington.
11 All rights reserved.
12
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions
15 are met:
16 1. Redistributions of source code must retain the above copyright
17 notice, this list of conditions and the following disclaimer.
18 2. Redistributions in binary form must reproduce the above copyright
19 notice, this list of conditions and the following disclaimer in the
20 documentation and/or other materials provided with the distribution.
21 3. All advertising materials mentioning features or use of this software
22 must display the following acknowledgment:
23 This product includes software developed by ACIRI, the AT&T
24 Center for Internet Research at ICSI (the International Computer
25 Science Institute). This product may also include software developed
26 by Stefan Savage at the University of Washington.
27 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington
28 may not be used to endorse or promote products derived from this software
29 without specific prior written permission.
30
31 THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND
32 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 ARE DISCLAIMED. IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE
35 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 SUCH DAMAGE.
42*/
43
44#include <sys/types.h>
45#include <sys/param.h>
46#include <sys/time.h>
47#include <string.h>
48#include <stdio.h>
49#include <unistd.h>
50#include <sys/socket.h>
51#include <stdlib.h>
52#include <netdb.h>
53#include <fcntl.h>
54#include <arpa/inet.h>
55#include <spawn.h>
56#include <ifaddrs.h>
57#include "inet.h"
58#include "capture.h"
59#include "support.h"
60#include "session.h"
61#include "ecn.h"
62#include "history.h"
63
64extern struct TcpSession session;
65
66void usage (char *name);
e0b07f2d 67int GetCannonicalInfo(char *string, size_t str_size, u_int32_t *address);
89c4ed63
A
68int BindTcpPort(int sockfd) ;
69
70void usage(char *name)
71{
72 printf("%s [options]\n", name);
73 printf("\t-n <target hostname | ipaddress>\n");
74 printf("\t-p <target port>\n");
75 printf("\t-m <mss>\n");
76 printf("\t-M <mtu>\n");
77 printf("\t-w <sourcePort>\n");
78 printf("\t-s <source hostname or ip address>\n");
79 printf("\t-f <file-name to get>\n");
80 printf("\t-d <interface name>\n");
fa34b6f5 81 printf("\t-C for CE path check\n");
755a8d69
A
82 printf("\t-S [A|R|X] SYN followed by ACK or RST or nothing\n");
83 printf("\t-F [set|clear|skip] how to handle firewall rules\n");
89c4ed63
A
84 return;
85}
86
87void SetupFirewall(u_int32_t targetIP, u_int16_t port, char *dev)
88{
89 char pfcmd[512];
90 char *pf_file_name = "/tmp/pf.conf";
755a8d69 91 int pf_fd = 0, rc;
89c4ed63 92 ssize_t bytes;
755a8d69 93 char *args[4];
89c4ed63
A
94
95 bzero(pfcmd, sizeof(pfcmd));
96
755a8d69 97 bzero(args, sizeof(args));
89c4ed63 98 sprintf(pfcmd, "block in quick on %s inet proto tcp from %s port %u\n",
755a8d69 99 dev, InetAddress(targetIP), port);
89c4ed63 100 if (session.debug >= SESSION_DEBUG_LOW)
755a8d69 101 printf("PF rule: %s\n", pfcmd);
89c4ed63
A
102
103 pf_fd = open(pf_file_name, O_RDWR|O_TRUNC|O_CREAT);
104 if (pf_fd < 0) {
105 perror("failed to open pf file");
106 exit(1);
107 }
108 bytes = write(pf_fd, pfcmd, strlen(pfcmd) + 1);
109 close(pf_fd);
755a8d69
A
110 args[0] = "pfctl";
111 args[1] = "-d";
112 args[2] = NULL;
113 rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL);
114 if (rc != 0) {
115 printf("Failed to exec: pfctl -d: %d\n", rc);
116 Quit(FAIL);
117 }
118
119 args[1] = "-f";
120 args[2] = pf_file_name;
121 args[3] = NULL;
122 rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL);
123 if (rc != 0) {
124 printf("Failed to exec: pfctl -f /tmp/pf.conf: %d\n", rc);
125 Quit(FAIL);
126 }
127
128 args[1] = "-e";
129 args[2] = NULL;
130 rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL);
131 if (rc != 0) {
132 printf("Failed to exec: pfctl -e: %d\n", rc);
133 Quit(FAIL);
134 }
135}
136
137void CleanupFirewall()
138{
139 char * args[3];
140 int rc;
141
142 args[0] = "pfctl";
143 args[1] = "-d";
144 args[2] = NULL;
145 rc = posix_spawn(NULL, "/sbin/pfctl", NULL, NULL, args, NULL);
146 if (rc != 0) {
147 printf("Failed to exec: pfctl -d: %d\n", rc);
148 Quit(FAIL);
149 }
89c4ed63
A
150}
151
152void Cleanup()
153{
89c4ed63
A
154 if (session.initSession > 0) {
155 shutdown(session.socket, 2);
156 }
89c4ed63
A
157 if (session.initCapture > 0) {
158 CaptureEnd();
159 }
755a8d69
A
160 if (session.initFirewall > 0) {
161 CleanupFirewall();
162 }
89c4ed63
A
163}
164
165void Quit(int how)
166{
167 SendReset();
168 Cleanup();
169 fflush(stdout);
170 fflush(stderr);
171 exit(how);
172}
173
174void SigHandle(int signo)
175{
176 Cleanup();
177 fflush(stdout);
178 fflush(stderr);
179 exit(-1);
180}
181
e0b07f2d 182int GetCannonicalInfo(char *string, size_t str_size, u_int32_t *address)
89c4ed63
A
183{
184 struct hostent *hp;
185 /* Is string in dotted decimal format? */
186 if ((*address = inet_addr(string)) == INADDR_NONE) {
187 /* No, then lookup IP address */
188 if ((hp = gethostbyname(string)) == NULL) {
189 /* Can't find IP address */
190 printf("ERROR: Couldn't obtain address for %s\n"
191 "RETURN CODE: %d\n", string, FAIL);
192 return(-1);
193 } else {
e0b07f2d 194 strlcpy(string, hp->h_name, str_size);
89c4ed63
A
195 memcpy((void *)address, (void *)hp->h_addr,
196 hp->h_length);
197 }
198 } else {
199 if ((hp = gethostbyaddr((char *)address, sizeof(*address),
200 AF_INET)) == NULL) {
201 /*
202 * Can't get cannonical hostname, so just use
203 * input string
204 */
205 if (session.debug) {
206 printf("WARNING: Couldn't obtain cannonical"
207 " name for %s\nRETURN CODE: %d",
208 string, NO_SRC_CANON_INFO);
209 }
e0b07f2d 210 /* strlcpy(name, string, MAXHOSTNAMELEN);*/
89c4ed63 211 } else {
e0b07f2d 212 /* strlcpy(name, hp->h_name, MAXHOSTNAMELEN);*/
89c4ed63
A
213 }
214 }
215 return(0);
216}
217
218int BindTcpPort(int sockfd)
219{
220 struct sockaddr_in sockName;
221 int port, result;
222 int randomOffset;
223
224#define START_PORT (50*1024)
225#define END_PORT (0xFFFF)
226
227 /*
228 * Choose random offset to reduce likelihood of
229 * collision with last run
230 */
231 randomOffset = (int)(1000.0*drand48());
232
233 /* Try to find a free port in the range START_PORT+1..END_PORT */
234 port = START_PORT+randomOffset;
235 do {
236 ++port;
237 sockName.sin_addr.s_addr = INADDR_ANY;
238 sockName.sin_family = AF_INET;
755a8d69 239 sockName.sin_port = 0; //htons(port);
89c4ed63
A
240 result = bind(sockfd, (struct sockaddr *)&sockName,
241 sizeof(sockName));
242 } while ((result < 0) && (port < END_PORT));
243
755a8d69 244
89c4ed63
A
245 if (result < 0) {
246 /* No free ports */
247 perror("bind");
248 port = 0;
755a8d69
A
249 } else {
250 socklen_t len = sizeof(sockName);
251 result = getsockname(sockfd, (struct sockaddr *)&sockName, &len);
252 if (result < 0) {
253 perror("getsockname");
254 port = 0;
255 } else {
256 port = ntohs(sockName.sin_port);
257 }
258 }
89c4ed63
A
259 return port;
260
261}
262
755a8d69
A
263#define FIREWALL_DEFAULT 0
264#define FIREWALL_SET_ONLY 1
265#define FIREWALL_CLEAR_ONLY 2
266#define FIREWALL_SKIP 3
89c4ed63
A
267
268int main(int argc, char **argv)
269{
270 u_int32_t targetIpAddress = 0;
271 u_int16_t targetPort = DEFAULT_TARGETPORT;
272 u_int16_t sourcePort = 0;
273 u_int32_t sourceIpAddress = 0;
274 int mss = DEFAULT_MSS;
275 int mtu = DEFAULT_MTU;
fa34b6f5 276 int fd, opt, usedev = 0, rc = 0, path_check = 0;
755a8d69 277 int syn_test = 0, syn_reply = 0;
89c4ed63
A
278 struct sockaddr_in saddr;
279 char dev[11]; /* device name for pcap init */
280 struct ifaddrs *ifap, *tmp;
755a8d69 281 int firewall_mode = FIREWALL_DEFAULT;
89c4ed63
A
282
283 bzero(&session, sizeof(session));
755a8d69 284 while ((opt = getopt(argc, argv, "n:p:w:m:M:s:d:f:-CS:vF:")) != -1) {
89c4ed63 285 switch (opt) {
755a8d69
A
286 case 'n':
287 if (strlen(optarg) > (MAXHOSTNAMELEN - 1)) {
288 printf("Target host name too long, max %u chars\n", MAXHOSTNAMELEN);
289 Quit(FAIL);
290 }
e0b07f2d
A
291 strlcpy(session.targetHostName, optarg,
292 sizeof(session.targetHostName));
293 strlcpy(session.targetName, session.targetHostName,
294 sizeof(session.targetName));
755a8d69
A
295 break;
296 case 'p':
297 targetPort = atoi(optarg);
298 break;
299 case 'm':
300 mss = atoi(optarg);
301 break;
302 case 'M':
303 mtu = atoi(optarg);
304 break;
305 case 'w':
306 sourcePort = atoi(optarg);
307 break;
308 case 's':
309 if (strlen(optarg) > (MAXHOSTNAMELEN - 1)) {
310 printf("Source host name too long, max %u chars\n", MAXHOSTNAMELEN);
311 Quit(FAIL);
312 }
e0b07f2d 313 strlcpy(session.sourceHostName, optarg,
755a8d69
A
314 MAXHOSTNAMELEN);
315 break;
316 case 'd':
317 if (strlen(optarg) > (sizeof(dev) - 1)) {
318 printf("Interface nae is too large, max %lu chars\n", (sizeof(dev) - 1));
319 Quit(FAIL);
320 }
321 bzero(dev, sizeof(dev));
e0b07f2d 322 strlcpy(dev, optarg, sizeof(dev));
755a8d69
A
323 usedev = 1;
324 break;
325 case 'f':
326 if (strlen(optarg) > 0) {
327 session.filename = strndup(optarg, strlen(optarg) + 1);
328 } else {
329 printf("Invalid file name \n");
330 }
331 break;
332 case 'F':
333 if (strcasecmp(optarg, "default") == 0)
334 firewall_mode = FIREWALL_DEFAULT;
335 else if (strcasecmp(optarg, "set") == 0)
336 firewall_mode = FIREWALL_SET_ONLY;
337 else if (strcasecmp(optarg, "clear") == 0)
338 firewall_mode = FIREWALL_CLEAR_ONLY;
339 else if (strcasecmp(optarg, "skip") == 0)
340 firewall_mode = FIREWALL_SKIP;
341 else
342 printf("firewall mode\n");
343 break;
344 case 'C':
345 path_check = 1;
346 break;
347 case 'S':
348 syn_test = 1;
349 if (strcasecmp(optarg, "A") == 0)
350 syn_reply = TCPFLAGS_ACK;
351 else if (strcasecmp(optarg, "R") == 0)
352 syn_reply = TCPFLAGS_RST;
353 else if (strcasecmp(optarg, "X") == 0)
354 syn_reply = 0;
355 else
356 printf("Invalid SYN reply \n");
357 break;
358 case 'v':
359 session.debug++;
360 break;
361 default:
362 usage(argv[0]);
363 exit(1);
89c4ed63
A
364 }
365 }
366 signal(SIGTERM, SigHandle);
367 signal(SIGINT, SigHandle);
368 signal(SIGHUP, SigHandle);
755a8d69 369
e0b07f2d
A
370 if (GetCannonicalInfo(session.targetHostName, sizeof(session.targetHostName),
371 &targetIpAddress) < 0)
89c4ed63
A
372 {
373 printf("Failed to convert targetIP address\n");
374 Quit(NO_TARGET_CANON_INFO);
755a8d69 375 }
89c4ed63
A
376 rc = getifaddrs(&ifap);
377 if (rc != 0 || ifap == NULL) {
378 printf("Failed to get source addresswith getifaddrs: %d\n", rc);
379 Quit(FAIL);
380 }
381 tmp = ifap;
382 sourceIpAddress = 0;
383 bzero(session.sourceHostName, MAXHOSTNAMELEN);
384 for (tmp = ifap; tmp != NULL; tmp = tmp->ifa_next) {
385 struct sockaddr_in *sin;
386 if (tmp->ifa_addr == NULL)
387 continue;
388 if (tmp->ifa_addr->sa_family != PF_INET)
389 continue;
390 if (usedev == 1) {
391 /* we know which interface to use */
392 if (strcmp(dev, tmp->ifa_name) == 0) {
393 sin = (struct sockaddr_in *)tmp->ifa_addr;
394 sourceIpAddress = sin->sin_addr.s_addr;
e0b07f2d 395 strlcpy(session.sourceHostName,
755a8d69 396 inet_ntoa(sin->sin_addr),
e0b07f2d 397 sizeof(session.sourceHostName));
89c4ed63
A
398 } else {
399 continue;
400 }
401 } else {
402 /* pick the first address */
403 bzero(dev, sizeof(dev));
404 sin = (struct sockaddr_in *)tmp->ifa_addr;
405 sourceIpAddress = sin->sin_addr.s_addr;
e0b07f2d 406 strlcpy(session.sourceHostName,
755a8d69 407 inet_ntoa(sin->sin_addr),
e0b07f2d
A
408 sizeof(session.sourceHostName));
409 strlcpy(dev, tmp->ifa_name, sizeof(dev));
89c4ed63
A
410 }
411 }
412 freeifaddrs(ifap);
413 if (sourceIpAddress == 0) {
414 printf("Failed to get source Ip address\n");
415 Quit(FAIL);
416 }
417
418 if (sourcePort == 0) {
419 bzero(&saddr, sizeof(saddr));
420 saddr.sin_family = AF_INET;
421 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
422 printf("Can't open socket\n");
423 return (-1);
424 }
425 if ((sourcePort = BindTcpPort(fd)) == 0) {
426 printf("Can't bind to port\n");
427 return (-1);
428 }
429 }
89c4ed63
A
430 printf("Source: %s:%d\n", session.sourceHostName, sourcePort);
431 printf("Destination: %s:%d\n", session.targetHostName, targetPort);
432
755a8d69
A
433 switch (firewall_mode) {
434 case FIREWALL_DEFAULT:
435 SetupFirewall(targetIpAddress, targetPort, dev);
436 session.initFirewall = 1;
437 break;
438 case FIREWALL_SET_ONLY:
439 SetupFirewall(targetIpAddress, targetPort, dev);
440 goto done;
441 case FIREWALL_CLEAR_ONLY:
442 session.initFirewall = 1;
443 goto done;
444 case FIREWALL_SKIP:
445 break;
446 }
447
448 CaptureInit(sourceIpAddress, sourcePort, targetIpAddress,
449 targetPort, dev);
450 session.initCapture = 1;
451
89c4ed63
A
452
453 printf("Starting ECN test\n");
755a8d69
A
454 if (syn_test) {
455 session.dont_send_reset = 1;
456 SynTest(sourceIpAddress, sourcePort, targetIpAddress,
457 targetPort, mss, syn_reply);
458 } else if (path_check) {
459 ECNPathCheckTest(sourceIpAddress, sourcePort, targetIpAddress,
460 targetPort, mss);
fa34b6f5 461 } else {
755a8d69
A
462 ECNTest(sourceIpAddress, sourcePort, targetIpAddress,
463 targetPort, mss);
fa34b6f5 464 }
755a8d69 465done:
fa34b6f5 466 Quit(SUCCESS);
89c4ed63
A
467 close(session.socket);
468 return (0);
469}