/*************************************************************************** * PacketStats.cc -- The PacketStats class handles packet statistics. It * * is intended to keep track of the number of packets and bytes sent and * * received, keep track of start and finish times, etc. * * * ***********************IMPORTANT NMAP LICENSE TERMS************************ * * * The Nmap Security Scanner is (C) 1996-2011 Insecure.Com LLC. Nmap is * * also a registered trademark of Insecure.Com LLC. This program is free * * software; you may redistribute and/or modify it under the terms of the * * GNU General Public License as published by the Free Software * * Foundation; Version 2 with the clarifications and exceptions described * * below. This guarantees your right to use, modify, and redistribute * * this software under certain conditions. If you wish to embed Nmap * * technology into proprietary software, we sell alternative licenses * * (contact sales@insecure.com). Dozens of software vendors already * * license Nmap technology such as host discovery, port scanning, OS * * detection, and version detection. * * * * Note that the GPL places important restrictions on "derived works", yet * * it does not provide a detailed definition of that term. To avoid * * misunderstandings, we consider an application to constitute a * * "derivative work" for the purpose of this license if it does any of the * * following: * * o Integrates source code from Nmap * * o Reads or includes Nmap copyrighted data files, such as * * nmap-os-db or nmap-service-probes. * * o Executes Nmap and parses the results (as opposed to typical shell or * * execution-menu apps, which simply display raw Nmap output and so are * * not derivative works.) * * o Integrates/includes/aggregates Nmap into a proprietary executable * * installer, such as those produced by InstallShield. * * o Links to a library or executes a program that does any of the above * * * * The term "Nmap" should be taken to also include any portions or derived * * works of Nmap. This list is not exclusive, but is meant to clarify our * * interpretation of derived works with some common examples. Our * * interpretation applies only to Nmap--we don't speak for other people's * * GPL works. * * * * If you have any questions about the GPL licensing restrictions on using * * Nmap in non-GPL works, we would be happy to help. As mentioned above, * * we also offer alternative license to integrate Nmap into proprietary * * applications and appliances. These contracts have been sold to dozens * * of software vendors, and generally include a perpetual license as well * * as providing for priority support and updates as well as helping to * * fund the continued development of Nmap technology. Please email * * sales@insecure.com for further information. * * * * As a special exception to the GPL terms, Insecure.Com LLC grants * * permission to link the code of this program with any version of the * * OpenSSL library which is distributed under a license identical to that * * listed in the included docs/licenses/OpenSSL.txt file, and distribute * * linked combinations including the two. You must obey the GNU GPL in all * * respects for all of the code used other than OpenSSL. If you modify * * this file, you may extend this exception to your version of the file, * * but you are not obligated to do so. * * * * If you received these files with a written license agreement or * * contract stating terms other than the terms above, then that * * alternative license agreement takes precedence over these comments. * * * * Source is provided to this software because we believe users have a * * right to know exactly what a program is going to do before they run it. * * This also allows you to audit the software for security holes (none * * have been found so far). * * * * Source code also allows you to port Nmap to new platforms, fix bugs, * * and add new features. You are highly encouraged to send your changes * * to nmap-dev@insecure.org for possible incorporation into the main * * distribution. By sending these changes to Fyodor or one of the * * Insecure.Org development mailing lists, it is assumed that you are * * offering the Nmap Project (Insecure.Com LLC) the unlimited, * * non-exclusive right to reuse, modify, and relicense the code. Nmap * * will always be available Open Source, but this is important because the * * inability to relicense code has caused devastating problems for other * * Free Software projects (such as KDE and NASM). We also occasionally * * relicense the code to third parties as discussed above. If you wish to * * specify special license conditions of your contributions, just say so * * when you send them. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License v2.0 for more details at * * http://www.gnu.org/licenses/gpl-2.0.html , or in the COPYING file * * included with Nmap. * * * ***************************************************************************/ #include "nping.h" #include "stats.h" #include "NpingOps.h" #include "output.h" /*****************************************************************************/ /* Implementation of NpingTimer class. */ /*****************************************************************************/ NpingTimer::NpingTimer(){ this->reset(); } NpingTimer::~NpingTimer(){ } void NpingTimer::reset(){ this->start_tv.tv_sec=0; this->start_tv.tv_usec=0; this->stop_tv.tv_sec=0; this->stop_tv.tv_usec=0; } /* End of reset() */ int NpingTimer::start(){ if( timeval_set(&start_tv) || timeval_set(&stop_tv) ) return OP_FAILURE; gettimeofday(&start_tv, NULL); return OP_SUCCESS; } /* End of start() */ int NpingTimer::stop(){ if( !timeval_set(&start_tv) || timeval_set(&stop_tv) ) return OP_FAILURE; gettimeofday(&stop_tv, NULL); return OP_SUCCESS; } /* End of stop() */ double NpingTimer::elapsed(struct timeval *now){ struct timeval tv; const struct timeval *end_tv=NULL; /* If for some reason the clock has not been started, * just return 0 seconds elapsed. */ if(!timeval_set(&start_tv)){ return 0.0; } /* If the caller supplied a time, use it */ if(now!=NULL){ end_tv=now; /* If the clock has been stopped already, use the stop time */ }else if(timeval_set(&stop_tv)){ end_tv = &stop_tv; }else{ gettimeofday(&tv, NULL); end_tv = &tv; } return TIMEVAL_SUBTRACT(*end_tv, start_tv) / 1000000.0; } /* End of elapsed() */ bool NpingTimer::is_started(){ return timeval_set(&this->start_tv); } /* End of is_started() */ bool NpingTimer::is_stopped(){ return timeval_set(&this->stop_tv); } /* End of is_stopped() */ /* Returns true if tv has been initialized; i.e., its members are not all zero. */ bool NpingTimer::timeval_set(const struct timeval *tv){ return (tv->tv_sec != 0 || tv->tv_usec != 0); } /* End of timeval_set() */ /*****************************************************************************/ /* Implementation of NpingStats class. */ /*****************************************************************************/ PacketStats::PacketStats(){ this->reset(); } /* End of PacketStats constructor */ PacketStats::~PacketStats(){ } /* End of PacketStats destructor */ void PacketStats::reset(){ this->packets[INDEX_SENT]=0; this->packets[INDEX_RCVD]=0; this->packets[INDEX_ECHO]=0; this->bytes[INDEX_SENT]=0; this->bytes[INDEX_RCVD]=0; this->bytes[INDEX_ECHO]=0; this->tcp[INDEX_SENT]=0; this->tcp[INDEX_RCVD]=0; this->tcp[INDEX_ECHO]=0; this->udp[INDEX_SENT]=0; this->udp[INDEX_RCVD]=0; this->udp[INDEX_ECHO]=0; this->icmp4[INDEX_SENT]=0; this->icmp4[INDEX_RCVD]=0; this->icmp4[INDEX_ECHO]=0; this->icmp6[INDEX_SENT]=0; this->icmp6[INDEX_RCVD]=0; this->icmp6[INDEX_ECHO]=0; this->arp[INDEX_SENT]=0; this->arp[INDEX_RCVD]=0; this->arp[INDEX_ECHO]=0; this->ip4[INDEX_SENT]=0; this->ip4[INDEX_RCVD]=0; this->ip4[INDEX_ECHO]=0; this->ip6[INDEX_SENT]=0; this->ip6[INDEX_RCVD]=0; this->ip6[INDEX_ECHO]=0; this->tcpconn[INDEX_CONN_ISSUED]=0; this->tcpconn[INDEX_CONN_ACCEPTED]=0; //this->sctpconn[INDEX_CONN_ISSUED]=0; //this->sctpconn[INDEX_CONN_ACCEPTED]=0; this->echo_clients_served=0; this->tx_timer.reset(); this->rx_timer.reset(); this->run_timer.reset(); this->max_rtt=-1; this->min_rtt=-1; this->avg_rtt=-1; } /* End of reset() */ /** Updates packet and byte count for sent/received/echoed packets. This * method is meant to be used internally. Use the update_sent(), update_rcvd() * and update_echo() instead. */ int PacketStats::update_packet_count(int index, int ip_version, int proto, u32 pkt_len){ assert(index==INDEX_SENT || index==INDEX_RCVD || index==INDEX_ECHO); /* General packet and byte count */ this->packets[index]++; this->bytes[index]+=pkt_len; /* IP stats */ switch(ip_version){ case AF_INET: this->ip4[index]++; break; case AF_INET6: this->ip6[index]++; break; } /* Stats for protocols above IP */ switch(proto){ case HEADER_TYPE_ICMPv4: this->icmp4[index]++; break; case HEADER_TYPE_ICMPv6: this->icmp6[index]++; break; case HEADER_TYPE_TCP: this->tcp[index]++; break; case HEADER_TYPE_UDP: this->udp[index]++; break; case HEADER_TYPE_ARP: this->arp[index]++; break; } return OP_SUCCESS; } /* End of update_packet_count() */ /* Update the stats for tranmitted packets */ int PacketStats::update_sent(int ip_version, int proto, u32 pkt_len){ return this->update_packet_count(INDEX_SENT, ip_version, proto, pkt_len); } /* End of update_sent() */ /* Update the stats for received packets */ int PacketStats::update_rcvd(int ip_version, int proto, u32 pkt_len){ return this->update_packet_count(INDEX_RCVD, ip_version, proto, pkt_len); } /* End of update_rcvd() */ /* Update the stats for echoed packets (echo mode). */ int PacketStats::update_echo(int ip_version, int proto, u32 pkt_len){ return this->update_packet_count(INDEX_ECHO, ip_version, proto, pkt_len); } /* End of update_echo() */ /** Updates count for echo clients served by the echo server. */ int PacketStats::update_clients_served(){ this->echo_clients_served++; return OP_SUCCESS; } /* End of update_clients_served() */ /* Updates connection counters (issued and accepted TCP connections). This * method is meant to be used internally. Use the update_connects() and * update_accepts() instead. */ int PacketStats::update_connection_count(int index, int ip_version, int proto){ assert(index==INDEX_CONN_ISSUED || index==INDEX_CONN_ACCEPTED); /* IP stats */ switch(ip_version){ case AF_INET: this->ip4[index]++; break; case AF_INET6: this->ip6[index]++; break; } /* TCP Connection stats */ switch(proto){ case HEADER_TYPE_TCP: this->tcpconn[index]++; break; default: assert(false); break; } return OP_SUCCESS; } /* End of update_connection_count() */ /* Update the stats for the number of connections that we have tried to * establish. In other words, the number of connect()s that we have issued. * The "proto" parameter is now redundant but it will make sense if one day * we support SCTP connections. */ int PacketStats::update_connects(int ip_version, int proto){ return this->update_connection_count(INDEX_CONN_ISSUED, ip_version, proto); } /* End of update_connects() */ /* Update the stats for the number of connections that we have successfully * established. The "proto" parameter is now redundant but it will make * sense if one day we support SCTP connections. */ int PacketStats::update_accepts(int ip_version, int proto){ return this->update_connection_count(INDEX_CONN_ACCEPTED, ip_version, proto); } /* End of update_accepts() */ /* Update the number of bytes read. Note that this method is public only because * it is used to update byte counts for TCP connections. Also, note that we * are reusing the same this->bytes variable that holds byte counts for * raw packets. However, as Nping shouldn't mix privileged and unprivileged * operation modes, this should be OK for now. */ int PacketStats::update_bytes_read(u32 count){ this->bytes[INDEX_RCVD]+=count; return OP_SUCCESS; } /* End of update_bytes_read() */ /* Update the number of bytes written. Note that this method is public only * because it is used to update byte counts for TCP connections where we had * some payload to send. Also, note that we are reusing the same this->bytes * variable that holds byte counts for raw packets. However, as Nping shouldn't * mix privileged and unprivileged operation modes, this should be OK for * now. */ int PacketStats::update_bytes_written(u32 count){ this->bytes[INDEX_SENT]+=count; return OP_SUCCESS; } /* End of update_bytes_written() */ /* Assumes that the counter for received packets has NOT been incremented yet. */ int PacketStats::update_rtt(int rtt){ /* Update Max RTT */ if(rtt > this->max_rtt || this->max_rtt<0){ this->max_rtt=rtt; } /* Update Min RTT */ if(rtt < this->min_rtt || this->min_rtt<0){ this->min_rtt=rtt; } /* Update average RTT */ if(this->packets[INDEX_RCVD]==0 || this->avg_rtt<0){ this->avg_rtt = rtt; }else{ this->avg_rtt = ((this->avg_rtt*(this->packets[INDEX_RCVD]))+rtt) / (this->packets[INDEX_RCVD]+1); } return OP_SUCCESS; } /* End of update_rtt() */ int PacketStats::start_clocks(){ this->start_tx_clock(); this->start_rx_clock(); return OP_SUCCESS; } /* End of start_clocks() */ int PacketStats::stop_clocks(){ this->stop_tx_clock(); this->stop_rx_clock(); return OP_SUCCESS; } /* End of stop_clocks() */ int PacketStats::start_tx_clock(){ this->tx_timer.start(); return OP_SUCCESS; } /* End of start_tx_clock() */ int PacketStats::stop_tx_clock(){ this->tx_timer.stop(); return OP_SUCCESS; } /* End of stop_tx_clock() */ int PacketStats::start_rx_clock(){ this->rx_timer.start(); return OP_SUCCESS; } /* End of start_rx_clock() */ int PacketStats::stop_rx_clock(){ this->rx_timer.stop(); return OP_SUCCESS; } /* End of stop_rx_clock() */ int PacketStats::start_runtime(){ this->run_timer.start(); return OP_SUCCESS; } /* End of start_runtime() */ int PacketStats::stop_runtime(){ this->run_timer.start(); return OP_SUCCESS; } /* End of stop_runtime() */ double PacketStats::get_tx_elapsed(){ return this->tx_timer.elapsed(NULL); } /* End of get_tx_elapsed() */ double PacketStats::get_rx_elapsed(){ return this->rx_timer.elapsed(NULL); } /* End of get_rx_elapsed() */ double PacketStats::get_runtime_elapsed(struct timeval *now){ return this->run_timer.elapsed(now); } /* End of get_runtime_elapsed() */ u64_t PacketStats::get_pkts_sent(){ return this->packets[INDEX_SENT]; } /* End of get_pkts_sent() */ u64_t PacketStats::get_bytes_sent(){ return this->bytes[INDEX_SENT]; } /* End of get_bytes_sent() */ u64_t PacketStats::get_pkts_rcvd(){ return this->packets[INDEX_RCVD]; } /* End of get pkts_rcvd() */ u64_t PacketStats::get_bytes_rcvd(){ return this->bytes[INDEX_RCVD]; } /* End of get_bytes_rcvd() */ u64_t PacketStats::get_pkts_echoed(){ return this->packets[INDEX_ECHO]; } /* End of get_pkts_echoed() */ u64_t PacketStats::get_bytes_echoed(){ return this->bytes[INDEX_ECHO]; } /* End of get_bytes_echoed() */ u32 PacketStats::get_clients_served(){ return this->echo_clients_served; } /* End of get_clients_served() */ u64_t PacketStats::get_connects(int proto){ /* TCP Connection stats */ switch(proto){ case HEADER_TYPE_TCP: return this->tcpconn[INDEX_CONN_ISSUED]; break; default: assert(false); break; } return 0; } /* End of get_connects() */ u64_t PacketStats::get_accepts(int proto){ /* TCP Connection stats */ switch(proto){ case HEADER_TYPE_TCP: return this->tcpconn[INDEX_CONN_ACCEPTED]; break; default: assert(false); break; } return 0; } /* End of get_accepts() */ u64_t PacketStats::get_connects_failed(int proto){ /* TCP Connection stats */ switch(proto){ case HEADER_TYPE_TCP: if(this->tcpconn[INDEX_CONN_ISSUED] <= this->tcpconn[INDEX_CONN_ACCEPTED]) return 0; else return this->tcpconn[INDEX_CONN_ISSUED] - this->tcpconn[INDEX_CONN_ACCEPTED]; break; default: assert(false); break; } return 0; } /* End of get_accepts() */ double PacketStats::get_percent_failed(int proto){ u32 pkt_rcvd=this->get_accepts(proto); u32 pkt_sent=this->get_connects(proto); u32 pkt_lost=(pkt_rcvd>=pkt_sent) ? 0 : (u32)(pkt_sent-pkt_rcvd); /* Only compute percentage if we actually sent packets, don't do divisions * by zero! (this could happen when user presses CTRL-C and we print the * stats */ double percentlost=0.0; if( pkt_lost!=0 && pkt_sent!=0) percentlost=((double)pkt_lost)/((double)pkt_sent); return percentlost*100; } /* End of get_percent_lost() */ u64_t PacketStats::get_pkts_lost(){ if(this->packets[INDEX_SENT] <= this->packets[INDEX_RCVD]) return 0; else return this->packets[INDEX_SENT] - this->packets[INDEX_RCVD]; } /* End of get_pkts_lost() */ double PacketStats::get_percent_lost(){ u32 pkt_rcvd=this->packets[INDEX_RCVD]; u32 pkt_sent=this->packets[INDEX_SENT]; u32 pkt_lost=(pkt_rcvd>=pkt_sent) ? 0 : (u32)(pkt_sent-pkt_rcvd); /* Only compute percentage if we actually sent packets, don't do divisions * by zero! (this could happen when user presses CTRL-C and we print the * stats */ double percentlost=0.0; if( pkt_lost!=0 && pkt_sent!=0) percentlost=((double)pkt_lost)/((double)pkt_sent); return percentlost*100; } /* End of get_percent_lost() */ u64_t PacketStats::get_pkts_unmatched(){ if(this->packets[INDEX_RCVD] <= this->packets[INDEX_ECHO]) return 0; else return this->packets[INDEX_RCVD] - this->packets[INDEX_ECHO]; } /* End of get_pkts_unmatched() */ double PacketStats::get_percent_unmatched(){ u32 pkt_captured=this->packets[INDEX_RCVD]; u32 pkt_echoed=this->packets[INDEX_ECHO]; u32 pkt_unmatched=(pkt_captured<=pkt_echoed) ? 0 : (u32)(pkt_captured-pkt_echoed); double percentunmatched=0.0; if( pkt_unmatched!=0 && pkt_captured!=0) percentunmatched=((double)pkt_unmatched)/((double)pkt_captured); return percentunmatched*100; } /* End of get_percent_unmatched() */ double PacketStats::get_tx_pkt_rate(){ double elapsed = this->tx_timer.elapsed(NULL); if(elapsed <= 0.0) return 0.0; else return this->packets[INDEX_SENT] / elapsed; } /* End of get_tx_pkt_rate() */ double PacketStats::get_tx_byte_rate(){ double elapsed = this->tx_timer.elapsed(NULL); if(elapsed <= 0.0) return 0.0; else return this->bytes[INDEX_SENT] / elapsed; } /* End of get_tx_byte_rate() */ double PacketStats::get_rx_pkt_rate(){ double elapsed = this->rx_timer.elapsed(NULL); if(elapsed <= 0.0) return 0.0; else return this->packets[INDEX_RCVD] / elapsed; } /* End of get_rx_pkt_rate() */ double PacketStats::get_rx_byte_rate(){ double elapsed = this->rx_timer.elapsed(NULL); if(elapsed <= 0.0) return 0.0; else return this->bytes[INDEX_RCVD] / elapsed; } /* End of get_rx_byte_rate() */ /* Returns max RTT observed for this host */ int PacketStats::get_max_rtt(){ return this->max_rtt; } /* End of get_max_rtt() */ /* Print round trip times */ int PacketStats::print_RTTs(){ /* Maximum RTT observed */ if(max_rtt>=0) nping_print(VB_0|NO_NEWLINE,"Max rtt: %.3lfms ", this->max_rtt/1000.0 ); else nping_print(VB_0|NO_NEWLINE,"Max rtt: N/A "); /* Minimum RTT observed */ if(min_rtt>=0) nping_print(VB_0|NO_NEWLINE,"| Min rtt: %.3lfms ", this->min_rtt/1000.0 ); else nping_print(VB_0|NO_NEWLINE,"| Min rtt: N/A " ); /* Average RTT */ if(avg_rtt>=0) nping_print(VB_0,"| Avg rtt: %.3lfms", this->avg_rtt/1000.0 ); else nping_print(VB_0,"| Avg rtt: N/A" ); return OP_SUCCESS; } /* End of print_RTTs() */