├── .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.

31 |
32 | 33 | 34 | 36 | 37 | 39 | 40 | 42 | 43 | 45 | 46 | 48 | 49 | 51 | 52 | 54 | 55 | 56 |
57 |

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 | --------------------------------------------------------------------------------