├── .gitignore
├── BUGS
├── FEATURES
├── LICENSE
├── Makefile
├── README.md
├── tcpping.1.xml
└── tcpping.c
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 |
--------------------------------------------------------------------------------
/BUGS:
--------------------------------------------------------------------------------
1 | I'm able to get this thing to respond to duplicate packets. I'll resume
2 | ripping out libnet and see if that sticks around, but I don't like it.
3 |
4 | Things I tried:
5 |
6 | - Seeing if there were rogue sending processes that weren't killed by the
7 | interrupt (there didn't seem to be, either by ps or tcpdump)
8 | - Adding and removing options (didn't seem to matter)
9 |
10 | What to try next:
11 |
12 | - tcpdump while doing a tcpping. That should sort all of this out.
13 |
14 | Hacks:
15 |
16 | - Throw out any sequence number that we don't remember giving
17 |
18 | ** UPDATE **
19 |
20 | After re-running, I can't manage to get this to do it again. Not sure what's
21 | up. This obviously bothers me, but if I can't duplicate, there's not much I
22 | can do. Let me know if you encounter this!
23 |
24 | -----------------------------------------------------------------------------
25 |
26 | There's still one warning on Mac machines, but it's perfectly harmless AFAICT.
27 |
28 | -----------------------------------------------------------------------------
29 |
30 | I should try to drop privileges to the one needed for Linux and/or Mac OS X in
31 | both the client and server. It's a pretty limited set, after all.
32 |
--------------------------------------------------------------------------------
/FEATURES:
--------------------------------------------------------------------------------
1 | - Package for various Linux OSes (Debian, Fedora, etc)
2 |
3 | I'm trying to work out a portable way to properly drop all but the needed
4 | privileges (in Linux, that's CAP_NET_RAW). However, I can't find a good piece
5 | of code that shows how to drop privileges besides resetting the user IDs.
6 |
7 | A program with "added safety" would detect Linux and drop all privileges except
8 | CAP_NET_RAW, then maintain the privilege through a dropping of root privileges.
9 | I just haven't done it yet.
10 |
11 | As it is, I believe there are sufficient checks and balances on the program so
12 | as to permit setuid use.
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: tcpping manpage
2 |
3 | uninstall: remove
4 |
5 | tcpping: tcpping.c
6 | $(CC) -Wall -g tcpping.c -o tcpping $(CFLAGS) -lnet -lpcap
7 |
8 | manpage: tcpping.1.xml
9 | xmltoman tcpping.1.xml | gzip > tcpping.1.gz
10 |
11 | install:
12 | install -m 4755 ./tcpping /usr/bin/tcpping
13 | mkdir -p /usr/share/man/man1
14 | install -m 644 ./tcpping.1.gz /usr/share/man/man1/tcpping.1.gz
15 |
16 | remove:
17 | rm -f /usr/bin/tcpping
18 |
19 | clean:
20 | rm -f ./tcpping ./tcpping.1.gz
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tcping
2 | Ping look-alike that uses TCP SYN packets to get around firewalls and ICMP blackholes
3 |
4 | --- Overview ----------------------------------------------------------------
5 |
6 | tcpping is a quick utility designed to emulate standard 'ping' in nearly every
7 | meaningful way and only diverge when necessary. It sends out forged TCP SYN
8 | packets and listens for a SYN/ACK or RST from the server or intermediary. It
9 | counts and reports on these results using an interface that is nearly identical
10 | to standard UNIX ping.
11 |
12 | --- Why Use Instead of Ping -------------------------------------------------
13 |
14 | On the global Internet, some routers or systems will firewall ICMP messages
15 | while allowing TCP packets. Furthermore, some routers or hosts will
16 | 'deprioritize' ICMP ping (echo) messages destined for itself or others -- when
17 | the network gets busy, these get dropped.
18 |
19 | However, virtually all public servers and the majority of private systems have
20 | at least one TCP port open and will respond to requests on it quickly and
21 | reliably. This provides greater accuracy (or any accuracy at all) for
22 | determining if a host is available. It also yields more reliable timing for
23 | sensitive latency and loss measurements as deprioritized packets will not be a
24 | true measure of latency for normal traffic (better simulated by TCP packets).
25 |
26 | It was originally written by Steven Kehlet (blog at kehlet.cx); it was taken
27 | over, bugfixed, and now maintained (with the original author's blessing) by Jim
28 | Wyllie.
29 |
30 | --- Building ----------------------------------------------------------------
31 |
32 | Building tcpping requires that you have a stable build environment as well as
33 | development versions of libnet1 and pcap. If you're on a Debian-based system
34 | (including Ubuntu) you can install those libraries with the following:
35 |
36 | sudo apt-get install build-essential
37 | sudo apt-get install libnet1-dev
38 | sudo apt-get install libpcap-dev
39 | sudo apt-get install xmltoman
40 |
41 | Or, alternatively, libnet1 is conveniently hosted on GitHub:
42 | http://github.com/sam-github/libnet
43 |
44 | While libpcap is hosted at http://www.tcpdump.org/
45 |
46 | Build with the following:
47 |
48 | make
49 |
50 | --- Setuid and tcpping ------------------------------------------------------
51 |
52 | If you don't want to use root access to use it every time, you can setuid the
53 | program. Keep in mind that any security vulnerabilities in tcpping could
54 | allow someone to execute arbitrary root-level code, so do this at your own
55 | risk.
56 |
57 | sudo chown root:root tcpping
58 | sudo chmod a+s tcpping
59 |
60 | --- Compatibility Issues ----------------------------------------------------
61 |
62 | libnet1 is a retooling of the old libnet hosted on SourceForge at
63 | http://packetfactory.net/ by Peter Wang. The note from Sam at GitHub is that
64 | the upstream maintainer is unresponsive and the project is unmaintained. Older
65 | versions of libnet based on Peter Wang's implementation will no longer work.
66 | You can find Sam's implementation at https://github.com/sam-github/libnet
67 |
68 | --- Related Tools -----------------------------------------------------------
69 |
70 | Some tools that have similar functionality that may suit your needs better:
71 |
72 | hping
73 | http://www.hping.org/ -- (officially) supports more operating systems. Has
74 | many more features and is more complicated.
75 |
76 | nmap
77 | http://nmap.org/ -- Full-service security standard compiled as a package with
78 | nearly all UNIX-like distributions.
79 |
80 | mtr
81 | http://www.bitwizard.nl/mtr/ -- Traceroute combined with ping for full-route
82 | loss statistics. Newer versions support TCP pings.
83 |
--------------------------------------------------------------------------------
/tcpping.1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | tcpping
9 | [-v]
10 | [-c count]
11 | [-p port]
12 | [-i interval]
13 | [-I interface]
14 | [-t ttl]
15 | [-S srcaddress]
16 | remote_host
17 |
18 |
19 |
20 |
21 |
is a utility designed to emulate
22 | standard in nearly every meaningful way and
23 | only diverge when necessary. It sends out forged TCP SYN packets and listens
24 | for a SYN/ACK, RST, or ICMP Time Exceeded message. It counts and reports on
25 | these results using an interface that is nearly identical to standard UNIX
26 | ping.
27 |
28 |
works well in situations where ICMP
29 | messages are either thought to be less responsive (through ICMP rate-limiting)
30 | or filtered entirely via firewalls.
requires the CAP_NET_RAW capability
58 | and is therefore installed as set-uid root. Though numerous steps are taken to
59 | ensure safety here (clearing the environment, safe input checks) there is
60 | always some inherent risk.
61 |
62 |
It should also be noted that TCP SYN packets can overwhelm and crash some
63 | servers as TCP SYN packets yielding a SYN/ACK will typically allocate resources
64 | on the server. Issuing this command with a very short interval to a server
65 | listening on that port is effectively a SYN flood which the server may or may
66 | not handle gracefully.
67 |
68 |
More information about SYN floods can be found here:
70 |
71 |
72 |
73 |
tcpping was originally written by Steven Kehlet <steven@kehlet.cx>.
74 | It is now maintained by Jim Wyllie <jwyllie83@gmail.com> and has received
75 | contributions from numerous individuals. See LICENSE for details.
76 |
77 |
78 |
79 |
The canonical version of this code is available on github at
81 |
82 |
83 |
84 |
85 |
This man page was compiled from XML using , found at . You will
87 | need that program to edit this manpage.
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/tcpping.c:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2010, 2011, 2012, 2014 Jim Wyllie
3 | Copyright (c) 2004, Steven Kehlet
4 | Copyright (c) 2011, Ethan Blanton
5 | Copyright (c) 2011, Mateusz Viste
6 | All rights reserved.
7 |
8 | Redistribution and use in source and binary forms, with or without
9 | modification, are permitted provided that the following conditions are
10 | met:
11 | 1. Redistributions of source code must retain the above copyright
12 | notice, this list of conditions and the following disclaimer.
13 | 2. Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in the
15 | documentation and/or other materials provided with the
16 | distribution.
17 | 3. The names of all tcpping copyright holders may not be used to endorse
18 | or promote products derived from this software without specific prior
19 | written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 | */
33 |
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #ifdef linux
45 | #define __FAVOR_BSD
46 | #endif
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include
58 | #include
59 | #include
60 |
61 | #define tcp_flag_isset(tcpptr, flag) (((tcpptr->th_flags) & (flag)) == (flag))
62 |
63 | /* Define the types of packets that we're sniffing for on the wire */
64 | #define UNINTERESTING 0
65 | #define SYN_FROM_US 1
66 | #define SYNACK_FROM_THEM 2
67 | #define ICMP_TIMEEXCEEDED 3
68 |
69 | struct in_addr src_ip;
70 | int ttl = 64;
71 | char *myname;
72 | pid_t child_pid;
73 | int keep_going = 1;
74 | int verbose = 0;
75 | int notify_fd;
76 | struct timeval tv_timxceed;
77 | int sequence_offset = 0;
78 | char *dest_name;
79 | in_addr_t dest_ip = 0;
80 | u_short dest_port = 80;
81 |
82 | float min_ping = -1;
83 | float avg_ping = 0;
84 | float max_ping = 0;
85 | int total_syns = 0;
86 | int total_synacks = 0;
87 | int total_rsts = 0;
88 | int successful_pings = 0;
89 |
90 | /* Global handle to libnet -- libnet1 requires only one instantiation per process */
91 | libnet_t *l;
92 | libnet_ptag_t tcp_pkt;
93 | libnet_ptag_t ip_pkt;
94 |
95 | /* There are problems with duplicate SYN/ACK packets and other oddities with
96 | * firewalls and established connections. Rather than solve all of them, I'm
97 | * just going to count the first sequence-number response and ignore all
98 | * others.
99 | *
100 | * Rather than store state for all results, we'll just have rolling state for a
101 | * 32-bit bitmask. I guess something can go wrong but it will definitely be
102 | * more accurate than what we have today with negative loss rates :)
103 | */
104 | int32_t seen_response_bitflags = 0;
105 |
106 | /* Keep track of a recent history of packet send times to accurately calculate
107 | * when packets were received
108 | */
109 |
110 | #define PACKET_HISTORY 1024
111 | struct timeval sent_times[PACKET_HISTORY];
112 |
113 | void handle_sigalrm(int junk)
114 | {
115 | /* do nothing */
116 | }
117 |
118 | /* wait for child to exit so the user's prompt doesn't
119 | come back before the stats */
120 | void handle_sigint(int junk)
121 | {
122 | waitpid(child_pid, NULL, 0);
123 | libnet_destroy(l);
124 | exit(0);
125 | }
126 |
127 | /* Some functions relating to keeping track of sequence state */
128 |
129 | unsigned int tcpseq_to_orderseq(unsigned int tcpseq)
130 | {
131 | return (unsigned int)((tcpseq - sequence_offset) / 100);
132 | }
133 |
134 | int get_seenflag(unsigned int tcpseq)
135 | {
136 | unsigned int orderseq = tcpseq_to_orderseq(tcpseq);
137 | return ((seen_response_bitflags >> (orderseq % 32)) & 1);
138 | }
139 |
140 | void set_seenflag(unsigned int tcpseq, int flag)
141 | {
142 | unsigned int orderseq = tcpseq_to_orderseq(tcpseq);
143 | unsigned int shift = orderseq % 32;
144 |
145 | if (flag > 0) {
146 | seen_response_bitflags = seen_response_bitflags | (1 << shift);
147 | } else {
148 | if (get_seenflag(tcpseq) == 1) {
149 | seen_response_bitflags = seen_response_bitflags ^ (1 << shift);
150 | }
151 | }
152 | }
153 |
154 | /* Sleep for a given number of milliseconds */
155 | int msleep(long duration)
156 | {
157 | struct timespec wait_time;
158 | struct timespec remainder;
159 |
160 | wait_time.tv_sec = duration / 1000;
161 | wait_time.tv_nsec = (long)(duration % 1000) * 1000000;
162 |
163 | return nanosleep(&wait_time, &remainder);
164 | }
165 |
166 | /* Function to determine the millisecond-difference between two timestamps */
167 | long timestamp_difference(const struct timeval *one, const struct timeval *two)
168 | {
169 | long difference = 0;
170 | difference += ((one->tv_sec - two->tv_sec) * 1000);
171 | difference += ((one->tv_usec - two->tv_usec) / 1000);
172 | return difference;
173 | }
174 |
175 | /* Function to validate that the given device is a valid one according to pcap;
176 | * used for setuid safety to validate the device name. device_name is
177 | * untrusted here.
178 | */
179 | int check_device_name(char *device_name)
180 | {
181 | pcap_if_t *interface_list = NULL;
182 | pcap_if_t *current_interface = NULL;
183 | char errbuf[PCAP_ERRBUF_SIZE];
184 | int r;
185 |
186 | /* Use pcap to fetch all of the devices for capturing */
187 | r = pcap_findalldevs(&interface_list, errbuf);
188 | if (r == -1) {
189 | fprintf(stderr, "pcap_findalldevs returned -1: %s\n", errbuf);
190 | exit(1);
191 | }
192 |
193 | /* No devices? Guess this isn't a valid one */
194 | if (interface_list == NULL) {
195 | return 0;
196 | }
197 |
198 | /* Check the list of interfaces */
199 | for (
200 | current_interface = interface_list;
201 | current_interface != NULL;
202 | current_interface = current_interface -> next ) {
203 |
204 | if (strncmp(current_interface->name, device_name, strlen(current_interface->name)) == 0
205 | && device_name[strlen(current_interface->name)] == '\0') {
206 | pcap_freealldevs(interface_list);
207 | return 1;
208 | }
209 | }
210 |
211 | /* No matches? Fail out */
212 | pcap_freealldevs(interface_list);
213 | return 0;
214 | }
215 |
216 | void sanitize_environment()
217 | {
218 | #ifdef _SVID_SOURCE
219 | clearenv();
220 | #elif _XOPEN_SOURCE
221 | clearenv();
222 | #else
223 | extern char **environ;
224 | environ = NULL;
225 | #endif
226 | }
227 |
228 | void print_stats(int junk)
229 | {
230 | printf("\n");
231 |
232 | printf("--- %s TCP ping statistics ---\n", dest_name);
233 | total_syns = (total_syns != 0 ? total_syns : 1);
234 | printf("%d SYN packets transmitted, %d SYN/ACKs and %d RSTs received, %.1f%% packet loss\n",
235 | total_syns, total_synacks, total_rsts,
236 | (1 - (successful_pings*1.0/total_syns))*100);
237 | printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",
238 | min_ping, avg_ping, max_ping);
239 |
240 | exit(0);
241 | }
242 |
243 | char *inet_ntoa2(in_addr_t addr)
244 | {
245 | struct in_addr iaddr;
246 | iaddr.s_addr = addr;
247 | return inet_ntoa(iaddr);
248 | }
249 |
250 | void show_packet(struct ip *ip, struct tcphdr *tcp, const struct pcap_pkthdr *header, const u_char *packet)
251 | {
252 | int r;
253 | struct timeval tv;
254 | char flags[32];
255 |
256 | r = gettimeofday(&tv, NULL);
257 |
258 | if (r < 0) {
259 | perror("gettimeofday");
260 | exit(1);
261 | }
262 |
263 | if (tcp) {
264 | snprintf(flags, sizeof(flags), "[%s%s%s%s%s%s]",
265 | (tcp_flag_isset(tcp, TH_FIN) ? "F" : ""),
266 | (tcp_flag_isset(tcp, TH_SYN) ? "S" : ""),
267 | (tcp_flag_isset(tcp, TH_RST) ? "R" : ""),
268 | (tcp_flag_isset(tcp, TH_PUSH) ? "P" : ""),
269 | (tcp_flag_isset(tcp, TH_ACK) ? "A" : ""),
270 | (tcp_flag_isset(tcp, TH_URG) ? "U" : "")
271 | );
272 | }
273 |
274 | printf("%ld.%ld", tv.tv_sec, tv.tv_usec);
275 | printf(" %s", inet_ntoa(ip->ip_src));
276 |
277 | if (tcp) {
278 | printf(":%d", ntohs(tcp->th_sport));
279 | }
280 |
281 | printf(" -> %s", inet_ntoa(ip->ip_dst));
282 |
283 | if (tcp) {
284 | printf(":%d %s", ntohs(tcp->th_dport), flags);
285 | }
286 |
287 | printf(" Length: %u", header->caplen);
288 | printf("\n");
289 |
290 | /* If we *really* want to be verbose, give us the packet and our delineations */
291 | if (verbose >= 2) {
292 | int i;
293 | printf("\tPacket:");
294 | for (i = 0; i < header->caplen; ++i) {
295 | printf(" %02X", packet[i]);
296 | }
297 | printf("\n");
298 | }
299 | }
300 |
301 | /* Determine if this is a valid packet that we care about */
302 | int get_packet_type(struct ip *ip, struct tcphdr *tcp, struct icmp *icmp)
303 | {
304 | /* In English: "SYN packet that we sent out" */
305 | if (ip->ip_dst.s_addr == dest_ip && ip->ip_p == IPPROTO_TCP && tcp_flag_isset(tcp, TH_SYN)) {
306 | return SYN_FROM_US;
307 | }
308 |
309 | /* In English: "Response packet we're interested in, from the other host" */
310 | else if (ip->ip_src.s_addr == dest_ip && ip->ip_p == IPPROTO_TCP &&
311 | (
312 | (tcp_flag_isset(tcp, TH_SYN) && tcp_flag_isset(tcp, TH_ACK)) ||
313 | tcp_flag_isset(tcp, TH_RST)
314 | )
315 | ) {
316 | return SYNACK_FROM_THEM;
317 | }
318 |
319 | /* In English: "Response packet we're interested in, but it's a Time Exceeded from some other host */
320 | else if (ip->ip_dst.s_addr == src_ip.s_addr && ip->ip_p == IPPROTO_ICMP && icmp->icmp_type == ICMP_TIMXCEED) {
321 | return ICMP_TIMEEXCEEDED;
322 | }
323 |
324 | return 0;
325 | }
326 |
327 | /* callback to pcap_loop() */
328 | void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
329 | {
330 | int r, i;
331 | int seqno, packetno;
332 | struct ip *ip = NULL;
333 | struct tcphdr *tcp = NULL;
334 | struct icmp *icmp = NULL;
335 | float ms;
336 | char *units = "ms";
337 | char *flags;
338 | struct timeval tv_synack;
339 | struct timeval *tv_syn;
340 |
341 | int frame_offset = 0;
342 | int size_ip = sizeof(struct ip);
343 | int size_tcp = sizeof(struct tcphdr);
344 | int packet_type = 0;
345 |
346 | /* It looks like there's a "feature" somewhere where you don't
347 | * necessarily get Ethernet headers, or can't count on the underlying device to
348 | * give you anything consistent (or anything at all). For example, a Mac OS X
349 | * loopback will give you 4 bytes of header. Loopback on Linux won't give you
350 | * any frame headers. Internet on some will give you Ethernet, but not
351 | * necessarily. I kicked around some solutions (none worked great). The best
352 | * was to simply iterate and move the header around and determine if any of the
353 | * moves make this a packet I care about. If so, parse it like that. If not,
354 | * move on. This seems "good enough" to work in virtually all cases with few
355 | * false positives. */
356 | for (frame_offset = 0; (frame_offset + size_ip + size_tcp) <= header->caplen; ++frame_offset) {
357 | ip = (struct ip*)(packet + frame_offset);
358 | tcp = (struct tcphdr*)(packet + frame_offset + size_ip);
359 | icmp = (struct icmp*)(packet + frame_offset + size_ip);
360 |
361 | packet_type = get_packet_type(ip, tcp, icmp);
362 | if (packet_type != UNINTERESTING) {
363 | break;
364 | }
365 | }
366 |
367 | if (verbose) {
368 | show_packet(ip, ip->ip_p == IPPROTO_TCP ? tcp : NULL, header, packet);
369 | printf("\tSeen flags: ");
370 | for (i = 0; i < 32; ++i) {
371 | printf("%d", (seen_response_bitflags >> i) & 1);
372 | }
373 | printf("\n");
374 | }
375 |
376 | if (packet_type == UNINTERESTING) {
377 | if (verbose > 1) {
378 | printf("\tHeader probing didn't find a valid packet, dropping...\n");
379 | return;
380 | }
381 | }
382 |
383 | /* SYN packet that we sent out? */
384 | if (packet_type == SYN_FROM_US) {
385 | /* Store the send time of the packet */
386 |
387 | seqno = ntohl(tcp->th_seq);
388 | packetno = tcpseq_to_orderseq(ntohl(tcp->th_seq));
389 | memcpy(&(sent_times[packetno % PACKET_HISTORY]), &(header->ts), sizeof(struct timeval));
390 |
391 | total_syns++;
392 | }
393 |
394 | /* SYN/ACK returned from them? */
395 | else if (packet_type == SYNACK_FROM_THEM) {
396 | r = gettimeofday(&tv_synack, NULL);
397 | if (r < 0) {
398 | perror("gettimeofday");
399 | exit(1);
400 | }
401 |
402 | /* Clear some of the rolling buffer. This isn't perfect, but
403 | * it's not bad. */
404 | set_seenflag(ntohl(tcp->th_ack) + 1800, 0);
405 | set_seenflag(ntohl(tcp->th_ack) + 1700, 0);
406 | set_seenflag(ntohl(tcp->th_ack) + 1600, 0);
407 | set_seenflag(ntohl(tcp->th_ack) + 1500, 0);
408 |
409 | /* If we've seen this particular packet, back out of the room slowly
410 | * and close the door */
411 | if ((ip->ip_p == IPPROTO_TCP) && get_seenflag(ntohl(tcp->th_ack))) {
412 | if (verbose) {
413 | printf("Ignored packet; already seen one with seq=%d\n",
414 | tcpseq_to_orderseq(ntohl(tcp->th_ack) - 1));
415 | }
416 |
417 | return;
418 | }
419 |
420 | /* Mark that we saw this packet */
421 | set_seenflag(ntohl(tcp->th_ack), 1);
422 |
423 | /* Figure out when this particular packet was sent */
424 | seqno = tcpseq_to_orderseq(ntohl(tcp->th_ack) - 1);
425 | tv_syn = &(sent_times[seqno % PACKET_HISTORY]);
426 | ms = (tv_synack.tv_sec - tv_syn->tv_sec) * 1000;
427 | ms += (tv_synack.tv_usec - tv_syn->tv_usec)*1.0/1000;
428 |
429 | /* Do some analysis on the returned packet... */
430 | if (ms > 1000) {
431 | units = "s";
432 | ms /= 1000;
433 | }
434 |
435 | if (tcp_flag_isset(tcp, TH_SYN)) {
436 | flags = "SYN/ACK";
437 | total_synacks++;
438 | }
439 |
440 | else {
441 | flags = "RST";
442 | total_rsts++;
443 | }
444 |
445 | /* Raise the flag to the user that we saw it... */
446 | printf("%s from %s: seq=%u ttl=%d time=%.3f%s\n",
447 | flags,
448 | inet_ntoa(ip->ip_src),
449 | tcpseq_to_orderseq(ntohl(tcp->th_ack) - 1),
450 | ip->ip_ttl,
451 | ms, units
452 | );
453 |
454 | if (ms < min_ping || min_ping == -1) {
455 | min_ping = ms;
456 | }
457 |
458 | if (ms > max_ping) {
459 | max_ping = ms;
460 | }
461 |
462 | avg_ping = ((avg_ping * successful_pings) + ms)/(successful_pings+1);
463 | successful_pings++;
464 |
465 | /* tell parent to continue */
466 | write(notify_fd, "foo", 3);
467 |
468 | }
469 |
470 | /* In English: "Response packet we're interested in, but it's a Time Exceeded from some other host */
471 | else if (packet_type == ICMP_TIMEEXCEEDED) {
472 |
473 | struct ip *retip;
474 | struct tcphdr *rettcp;
475 |
476 | retip = (struct ip*)(packet + frame_offset + size_ip + 8);
477 | rettcp = (struct tcphdr *)(packet + frame_offset + size_ip + 8 + size_ip);
478 |
479 | /* After we build the headers for ICMP, check the hosts / protocol / etc. */
480 | if (retip->ip_dst.s_addr == dest_ip && retip->ip_p == IPPROTO_TCP &&
481 | tcp_flag_isset(rettcp, TH_SYN)) {
482 |
483 | r = gettimeofday(&tv_timxceed, NULL);
484 | if (r < 0) {
485 | perror("gettimeofday");
486 | exit(1);
487 | }
488 | /* Figure out when this particular packet was sent */
489 | seqno = tcpseq_to_orderseq(ntohl(tcp->th_ack) - 1);
490 | tv_syn = &(sent_times[seqno % PACKET_HISTORY]);
491 | ms = (tv_synack.tv_sec - tv_syn->tv_sec) * 1000;
492 | ms += (tv_synack.tv_usec - tv_syn->tv_usec)*1.0/1000;
493 |
494 | if (ms > 1000) {
495 | units = "s";
496 | ms /= 1000;
497 | }
498 |
499 | /* Extracting the sequence number would be unreliable as only
500 | * 64 bits of the TCP header are required to be present. */
501 | printf("Time to live exceeded from %s: ttl=%d time=%.3f%s\n",
502 | inet_ntoa(ip->ip_src),
503 | ip->ip_ttl,
504 | ms, units
505 | );
506 |
507 | /* tell parent to continue */
508 | write(notify_fd, "foo", 3);
509 | }
510 | }
511 | }
512 |
513 | void sniff_packets(char *device_name)
514 | {
515 | int r;
516 | pcap_t *handle;
517 | char errbuf[PCAP_ERRBUF_SIZE];
518 | char filter_expression[1024];
519 | struct bpf_program filter;
520 | bpf_u_int32 mask;
521 | bpf_u_int32 net;
522 |
523 | r = pcap_lookupnet(device_name, &net, &mask, errbuf);
524 | if (r < 0) {
525 | fprintf(stderr, "pcap_lookupnet: %s\n", errbuf);
526 | exit(1);
527 | }
528 |
529 | handle = pcap_open_live(device_name, BUFSIZ, 0, 0, errbuf);
530 | if (!handle) {
531 | fprintf(stderr, "pcap_open_live: %s\n", errbuf);
532 | exit(1);
533 | }
534 |
535 | /* set non-blocking */
536 | #ifdef BIOCIMMEDIATE
537 | r = 1;
538 | ioctl(pcap_fileno(handle), BIOCIMMEDIATE, &r);
539 | #else
540 | r = pcap_setnonblock(handle, 1, errbuf);
541 | #endif
542 | if (r < 0) {
543 | fprintf(stderr, "pcap_lookupnet: %s\n", errbuf);
544 | exit(1);
545 | }
546 |
547 | /* compile and apply the filter_expression */
548 | snprintf(filter_expression, sizeof(filter_expression),
549 | "(host %s and port %u) or icmp[icmptype] == icmp-timxceed",
550 | inet_ntoa2(dest_ip), dest_port
551 | );
552 |
553 | if (verbose) {
554 | printf("pcap filter expression: %s\n", filter_expression);
555 | }
556 |
557 | r = pcap_compile(handle, &filter, filter_expression, 0, mask);
558 | if (r < 0) {
559 | fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(handle));
560 | exit(1);
561 | }
562 |
563 | r = pcap_setfilter(handle, &filter);
564 | if (r < 0) {
565 | fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(handle));
566 | exit(1);
567 | }
568 |
569 | /* wake up parent, we're ready */
570 | write(notify_fd, "foo", 3);
571 |
572 | /* begin sniffing */
573 | r = pcap_loop(handle, -1, got_packet, NULL);
574 |
575 | pcap_close(handle);
576 | }
577 |
578 | /*
579 | * Find a device name given an IPv4 dotted-quad
580 | * Could be made to work with IPv6 without much effort
581 | * It doesn't matter right now, but this uses inet_ntoa, which isn't
582 | * re-entrant. So, don't start with the threading. I doubt this is the only
583 | * thing here that isn't thread-safe, but there ya go.
584 | *
585 | * Will allocate memory for the return device name, so be sure to free it
586 | */
587 | char* find_device(char *dq)
588 | {
589 | /* Get all of the if headers... */
590 | int errs = 0;
591 | unsigned short family = 0;
592 | struct ifaddrs* alladdrs = NULL;
593 | struct ifaddrs* currentaddr = NULL;
594 | struct sockaddr_in *iaddr = NULL;
595 | char *test_dq = NULL;
596 | char *found_name = NULL;
597 |
598 | errs = getifaddrs(&alladdrs);
599 | if (errs != 0) {
600 | return NULL;
601 | }
602 |
603 | /*
604 | * Loop through the returned device-families
605 | * (unit of iteration is device-family, so you can get a device many times)
606 | */
607 | for (currentaddr = alladdrs; currentaddr != NULL; currentaddr = currentaddr -> ifa_next) {
608 | if (currentaddr->ifa_addr == NULL) continue;
609 |
610 | family = currentaddr->ifa_addr->sa_family;
611 | if (family == AF_INET) {
612 | iaddr = (struct sockaddr_in *)currentaddr->ifa_addr;
613 | test_dq = inet_ntoa(iaddr->sin_addr);
614 | if (strncmp(dq, test_dq, strlen(test_dq)) == 0) {
615 | found_name = strdup(currentaddr->ifa_name);
616 | break;
617 | }
618 | }
619 | }
620 |
621 | freeifaddrs(alladdrs);
622 | return found_name;
623 | }
624 |
625 | /*
626 | * Given a destination, will return the source IP on the system used to route
627 | * there. Makes use of non-reentrant functions. Initializes the memory used
628 | * in the return variable, so you'll want to free it later.
629 | */
630 | char *find_source_ip(char *dq)
631 | {
632 | char *source_dq;
633 |
634 | /* Basically you just make the OS do it with a dummy socket... */
635 | int test_fd = socket(AF_INET, SOCK_DGRAM, 0);
636 | struct sockaddr_in dest, source;
637 | socklen_t socket_length = sizeof(source);
638 |
639 | /* Build the destination... */
640 | bzero(&dest, sizeof(struct sockaddr_in));
641 | dest.sin_family = AF_INET;
642 | dest.sin_port = htons(2942); /* "random" port */
643 | if (inet_pton(AF_INET, dq, &(dest.sin_addr)) != 1) return NULL;
644 |
645 | /* Try to "connect" to the UDP socket and retrieve the source socket... */
646 | if(connect(test_fd, (struct sockaddr *)&dest, sizeof(dest)) != 0) return NULL;
647 | if(getsockname(test_fd, (struct sockaddr *)&source, &socket_length) != 0) return NULL;
648 | close(test_fd);
649 |
650 | /* ... and convert that source socket to a dotted-quad */
651 | source_dq = strdup(inet_ntoa(source.sin_addr));
652 | return source_dq;
653 | }
654 |
655 |
656 | void inject_syn_packet(int sequence)
657 | {
658 | int c;
659 | int r;
660 |
661 | /* Build the custom TCP header. We have a weird hack here:
662 | * We use the sequence number to define the packet order
663 | */
664 |
665 | struct timeval tv;
666 | r = gettimeofday(&tv, NULL);
667 | if (r < 0) {
668 | perror("gettimeofday");
669 | exit(1);
670 | }
671 |
672 | r = libnet_build_tcp(
673 | random() % 65536, /* source port */
674 | dest_port, /* destination port */
675 | sequence_offset + (sequence*100), /* sequence number */
676 | 0, /* acknowledgement num */
677 | TH_SYN, /* control flags */
678 | 32768, /* window size */
679 | 0, /* checksum */
680 | 0, /* urgent pointer */
681 | LIBNET_TCP_H, /* TCP packet size */
682 | NULL, /* payload */
683 | 0, /* payload size */
684 | l, /* libnet handle */
685 | tcp_pkt /* libnet packet ref */
686 | );
687 |
688 | if (r == -1) {
689 | fprintf(stderr, "libnet_build_tcp: %s\n", libnet_geterror(l));
690 | exit(1);
691 | }
692 |
693 | /* custom IP header; I couldn't get autobuild_ipv4 to work */
694 | r = libnet_build_ipv4(
695 | LIBNET_IPV4_H + LIBNET_TCP_H, /* packet length */
696 | 0, /* tos */
697 | htons((l->ptag_state) & 0x0000ffff), /* IP id */
698 | 0, /* fragmentation */
699 | ttl, /* TTL */
700 | IPPROTO_TCP, /* encap protocol */
701 | 0, /* checksum */
702 | src_ip.s_addr, /* source IP */
703 | dest_ip, /* destination IP */
704 | NULL, /* payload */
705 | 0, /* payload size */
706 | l, /* libnet pointer */
707 | ip_pkt /* libnet packet ref */
708 | );
709 |
710 | if (r == -1) {
711 | fprintf(stderr, "libnet_autobuild_ipv4: %s\n", libnet_geterror(l));
712 | exit(1);
713 | }
714 |
715 | /* send it */
716 | c = libnet_write(l);
717 | if (c == -1) {
718 | fprintf(stderr, "libnet_write: %s\n", libnet_geterror(l));
719 | exit(1);
720 | }
721 |
722 | /* Get ready for the next packet */
723 | libnet_clear_packet(l);
724 | }
725 |
726 | void usage()
727 | {
728 | fprintf(stderr, "%s: [-v] [-c count] [-p port] [-i interval] [-I interface] [-t ttl] [-S srcaddress] remote_host\n", myname);
729 | exit(0);
730 | }
731 |
732 | int main(int argc, char *argv[])
733 | {
734 | /* Create a safe environment for setuid safety */
735 | sanitize_environment();
736 |
737 | int r;
738 | int c;
739 | char *device_name = NULL;
740 | int count = -1;
741 | long interval = 1000;
742 | struct hostent *he;
743 | int pipefds[2];
744 | char junk[256];
745 | int sequence = 1;
746 | char *src_quad = NULL;
747 |
748 | myname = argv[0];
749 | char *dest_quad = NULL;
750 | char *dest_host = NULL;
751 |
752 | bzero(&src_ip, sizeof(struct in_addr));
753 |
754 | while ((c = getopt(argc, argv, "c:p:i:vI:t:S:")) != -1) {
755 | switch (c) {
756 | case 'c':
757 | count = atoi(optarg);
758 | break;
759 | case 'p':
760 | dest_port = atoi(optarg);
761 | break;
762 | case 'i':
763 | interval = (long)(atof(optarg) * 1000.0);
764 | if (interval <= 0) {
765 | fprintf(stderr, "Invalid interval\n");
766 | exit(1);
767 | }
768 | break;
769 | case 'I':
770 | device_name = optarg;
771 | if (check_device_name(device_name) == 0) {
772 | fprintf(stderr, "Invalid capture device\n");
773 | exit(1);
774 | }
775 | break;
776 | case 'v':
777 | ++verbose;
778 | break;
779 | case 't':
780 | ttl = atoi(optarg);
781 | if (ttl < 1 || ttl > 255) {
782 | fprintf(stderr, "Invalid TTL\n");
783 | }
784 | break;
785 | case 'S':
786 | src_quad = optarg;
787 | if (inet_aton(src_quad, &src_ip) == 0) {
788 | fprintf(stderr, "Invalid source address\n");
789 | }
790 | break;
791 | default:
792 | usage();
793 | }
794 | }
795 |
796 | argc -= optind;
797 | argv += optind;
798 |
799 | if (argc != 1) {
800 | usage();
801 | }
802 |
803 | if (geteuid() != 0) {
804 | fprintf(stderr, "You must run %s as root.\n", myname);
805 | exit(1);
806 | }
807 |
808 | dest_host = argv[0];
809 | he = gethostbyname(dest_host);
810 | if (!he) {
811 | herror("gethostbyname");
812 | exit(1);
813 | }
814 |
815 | if (!he->h_addr) {
816 | fprintf(stderr, "No address associated with name: %s\n", argv[0]);
817 | exit(1);
818 | }
819 |
820 | bcopy(he->h_addr, &dest_ip, sizeof(dest_ip));
821 | if (dest_ip == INADDR_NONE) {
822 | perror("bad address");
823 | exit(1);
824 | }
825 |
826 | /* Get the dotted-quad from the resolved address as we need it later */
827 | struct in_addr dest_addr;
828 | bzero(&dest_addr, sizeof(struct in_addr));
829 | dest_addr.s_addr = dest_ip;
830 | dest_quad = inet_ntoa(dest_addr);
831 | if (dest_quad == NULL) {
832 | perror("Unable to convert returned address to dotted-quad; try pinging by IP address");
833 | exit(1);
834 | }
835 | dest_quad = strdup(dest_quad);
836 |
837 | /* Figure out the source IP if we didn't specify one */
838 | if (src_ip.s_addr == 0) {
839 | src_quad = find_source_ip(dest_quad);
840 | if (src_quad == NULL) {
841 | fprintf(stderr, "Unable to calculate source IP for tcp pings (needed for device capture). Try specifying a source IP address with -S\n");
842 | exit(1);
843 | }
844 |
845 | if (inet_aton(src_quad, &src_ip) == 0) {
846 | fprintf(stderr, "Unable to compute source IP from calculated source dotted quad: %s\n", src_quad);
847 | exit(1);
848 | }
849 | }
850 |
851 | /* Figure out the source device name if we didn't specify one */
852 | if (device_name == NULL) {
853 | device_name = find_device(src_quad);
854 | if (device_name == NULL) {
855 | fprintf(stderr, "Unable to calculate if device from source IP (%s). Is the source IP you specified bound to a device?\n", src_quad);
856 | exit(1);
857 | }
858 | }
859 |
860 | /* set up the libnet pointer and stack */
861 | char errbuf[LIBNET_ERRBUF_SIZE];
862 |
863 | l = libnet_init(LIBNET_RAW4, device_name, errbuf);
864 | if (l == NULL) {
865 | fprintf(stderr, "libnet_init: %s", errbuf);
866 | exit(1);
867 | }
868 |
869 | dest_name = he->h_name;
870 | printf("TCP PING %s (%s:%u)\n", dest_name,
871 | dest_quad, dest_port
872 | );
873 |
874 | /* start seq# somewhere random so we're not SO obvious */
875 | srandom(time(NULL));
876 | sequence_offset = random();
877 |
878 | /* pipe is to synchronize with our child */
879 | r = pipe(pipefds);
880 | if (r < 0) {
881 | perror("pipe");
882 | exit(1);
883 | }
884 |
885 | r = fcntl(pipefds[0], F_SETFL, O_NONBLOCK);
886 | if (r < 0) {
887 | perror("fcntl (nonblock)");
888 | exit(1);
889 | }
890 |
891 | child_pid = fork();
892 | if (child_pid < 0) {
893 | perror("fork");
894 | exit(1);
895 | }
896 |
897 | /* The parent is to send packets until an alarm, cnt, or Ctrl+C */
898 | if (child_pid) {
899 | close(pipefds[1]);
900 |
901 | /* wait for child sniffer to be ready */
902 | for (;;) {
903 | r = read(pipefds[0], junk, sizeof(junk));
904 | if (r > 0) {
905 | break;
906 | }
907 |
908 | msleep(200);
909 | }
910 |
911 | signal(SIGINT, handle_sigint);
912 |
913 | /* Event loop: either send, or whatever */
914 | for (;;) {
915 | inject_syn_packet(sequence++);
916 | msleep(interval);
917 |
918 | /* See if we sent too many packets */
919 | if (--count == 0) {
920 | /* tell child to display stats */
921 | kill(child_pid, SIGINT);
922 | /* make sure we wait until it died and closed */
923 | kill(getpid(), SIGINT);
924 | break;
925 | }
926 |
927 | /* If we got here, we got a different errval than a non-block. Fail out */
928 | r = read(pipefds[0], junk, sizeof(junk));
929 | if (r == -1 && (errno != EAGAIN && errno != EWOULDBLOCK)) {
930 | /* child died */
931 | fprintf(stderr, "child exited.\n");
932 | exit(1);
933 | }
934 | }
935 | }
936 |
937 | /* The child is to receive packets until terminated by the parent */
938 | else {
939 | close(pipefds[0]);
940 | notify_fd = pipefds[1];
941 | signal(SIGINT, print_stats);
942 |
943 | sniff_packets(device_name);
944 |
945 | free(device_name);
946 | }
947 |
948 | return(0);
949 | }
950 |
--------------------------------------------------------------------------------