/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** 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 for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : Bandwidth Tester * * * * $Archive:: /Commando/Code/BandTest/BandTest.cpp $* * * * $Author:: Tom_s $* * * * $Modtime:: 3/04/02 5:50p $* * * * $Revision:: 17 $* * * * * *---------------------------------------------------------------------------------------------* * * * * *---------------------------------------------------------------------------------------------* * * * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #define WIN32_LEAN_AND_MEAN #include #include #pragma warning(disable:4201) #include #pragma warning(default:4201) #include #include #include #include #include #include "BandTest.h" #include "..\combat\specialbuilds.h" // warning C4711: function 'xxx' selected for automatic inline expansion #pragma warning(disable:4711) /*********************************************************************************************** ** Data structures */ #pragma pack(4) /* ** IP header definition. */ typedef struct tIPHeaderType { unsigned char Length : 4; // length of the header unsigned char Version : 4; // Version of IP (must be 4 for IP4) unsigned char TOS; // Type of service (usually 0) unsigned short PacketLen; // total length of the packet unsigned short ID; // unique identifier unsigned short Flags; // flags unsigned char TTL; // Time to live unsigned char Protocol; // protocol (TCP, UDP etc) unsigned short Checksum; // IP checksum unsigned int SourceIP; // IP this packet came from unsigned int DestIP; // IP this packet is going to } IPHeaderType; /* ** ICMP packet header. Sits after the IP header when used. */ typedef struct tICMPHeaderType { char Type; char Code; unsigned short Checksum; unsigned short ID; unsigned short Sequence; } ICMPHeaderType; /* ** UDP header. Sits after the IP header when used. */ typedef struct tUDPHeaderType { unsigned short SourcePort; unsigned short DestPort; unsigned short Length; unsigned short Checksum; } UDPHeaderType; /*********************************************************************************************** ** Defines */ /* ** Total size of ICMP echo packet. 20 + 8 */ #define ICMP_ECHO_SIZE 28 /* ** ICMP message numbers. */ #define ICMP_ECHO_REPLY 0 #define ICMP_ECHO 8 #define ICMP_TIME_EXCEEDED 11 /* ** IP protocol numbers. */ #define PROTOCOL_ICMP 1 #define PROTOCOL_UDP 17 /* ** Timer. */ #ifndef TIMER_SECOND #define TIMER_SECOND 1000 #endif //TIMER_SECOND /*********************************************************************************************** ** Global data. */ /* ** Sockets. */ SOCKET RawSocket = INVALID_SOCKET; SOCKET ICMPRawSocket = INVALID_SOCKET; /* ** Registry. */ static HKEY RegistryKey; //static char BandTestRegistryLocation[64] = {"Software\\Westwood\\Renegade\\BandTest\\"}; #if defined(FREEDEDICATEDSERVER) static char BandTestRegistryLocation[64] = {"Software\\Westwood\\RenegadeFDS\\BandTest\\"}; #elif defined(MULTIPLAYERDEMO) static char BandTestRegistryLocation[64] = {"Software\\Westwood\\RenegadeMPDemo\\BandTest\\"}; #elif defined(BETACLIENT) static char BandTestRegistryLocation[64] = {"Software\\Westwood\\RenegadeBeta\\BandTest\\"}; #else static char BandTestRegistryLocation[64] = {"Software\\Westwood\\Renegade\\BandTest\\"}; #endif static char RegistryPath[1024]; /* ** Packet loss. */ int PingsSent = 0; int PingsLost = 0; /* ** Connection quality. */ int NumConsistentPings = 0; int NumPingsCheckedForConsistency = 0; /* ** State. */ bool StatsValid = false; #ifdef _DEBUG HANDLE DebugFile = INVALID_HANDLE_VALUE; char DebugFileName[256]; #endif //_DEBUG /*********************************************************************************************** ** Function prototypes. */ static bool Open_Raw_Sockets(int &failure_code); static void Close_Raw_Sockets(void); static bool Send_Ping(char *payload, int payload_size, SOCKET socket, struct sockaddr *address, int sequence_id); static bool Get_Ping_Response(SOCKET socket, int &seq_id, struct sockaddr *address, unsigned long validate_addr, unsigned long &my_address); static unsigned short Get_IP_Checksum(unsigned short *buffer, int size); static bool Send_Raw_UDP(char *payload, int payload_size, SOCKET socket, struct sockaddr *address, unsigned short source_port, unsigned short dest_port); static unsigned long Upstream_Detect(unsigned long server_ip, unsigned long my_ip, int &failure_code, unsigned long &downstream, BandtestSettingsStruct *settings); static int Ping_Host(unsigned long host_ip, unsigned long my_ip, int times, int payload_size, unsigned long *ping_times, unsigned long timeout); static float Average_Ping(int num_pings, unsigned long *ping_times, bool ignore_low_high); static float Lowest_Ping(int num_pings, unsigned long *ping_times); static int Get_Path_To_Server(unsigned long *path, unsigned long my_ip, unsigned long server_ip); static void Ping_Profile(SOCKADDR_IN *router_addr, unsigned long my_ip); static bool Set_Registry_Int(const char *name, int value); static int Get_Registry_Int(const char *name, int def_value); static bool Open_Registry(void); static void Close_Registry(void); #ifdef _DEBUG void DebugString (char const * string, ...); char * Addr_As_String2(struct sockaddr_in *addr); char * Addr_As_String(unsigned char *addr); #else //_DEBUG #define DebugString //_forceinline void DebugString(char const *, ...) {}; //inline char * Addr_As_String(sockaddr_in *) {}; inline char * Addr_As_String(unsigned char *) {return NULL;}; inline char * Addr_As_String2(struct sockaddr_in *){return NULL;}; #endif //_DEBUG /* ** Default settings. */ BandtestSettingsStruct DefaultSettings = { 0, //AlwaysICMP 0, //TTLScatter 50, //FastPingPackets 20, //SlowPingPackets 15, //FastPingThreshold 0, //PingProfile }; /*********************************************************************************************** ** Your actual Code. No, really. */ /*********************************************************************************************** * DllMain -- Dll entry point. Not used for much * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/3/2001 11:20AM ST : Created * *=============================================================================================*/ bool APIENTRY DllMain(HANDLE, DWORD, void *) { return(true); } /*********************************************************************************************** * Detect_Upstream_Bandwidth -- Try and figure out what our upstream bandwidth is * * * * * * * * INPUT: IP address of server to use as destination IP in tests.Server receives no packets * * My IP. * * Number of times to retry on error * * (out) Extended error code * * * * OUTPUT: Bandwidth in bits per second. 0 = couldn't detect. 0xffffffff means **BIG** * * * * WARNINGS: All IPs in host order * * * * * * HISTORY: * * 10/3/2001 11:21AM ST : Created * *=============================================================================================*/ BANDTEST_API unsigned long Detect_Bandwidth(unsigned long server_ip, unsigned long my_ip, int retries, int &failure_code, unsigned long &downstream, unsigned long api_version, BandtestSettingsStruct *settings, char *regpath) { if (api_version != BANDTEST_API_VERSION) { return(BANDTEST_WRONG_API_VERSION); } if (regpath == NULL) { strcpy(RegistryPath, BandTestRegistryLocation); } else { strcpy(RegistryPath, regpath); } if (!Open_Registry()) { failure_code = BANDTEST_UNKNOWN_ERROR; return(0); } timeBeginPeriod(1); PingsSent = 0; PingsLost = 0; NumConsistentPings = 0; NumPingsCheckedForConsistency = 0; StatsValid = false; int tried = 0; int bps = 0; if (settings == NULL) { settings = &DefaultSettings; } while (tried < retries + 1) { tried++; bps = Upstream_Detect(server_ip, my_ip, failure_code, downstream, settings); if (bps != 0) { StatsValid = true; if (PingsSent) { DebugString("Packet loss: %d percent\n", (100 * PingsLost) / PingsSent); Set_Registry_Int("PingLoss", (100 * PingsLost) / PingsSent); if (NumPingsCheckedForConsistency) { DebugString("Connection quality: %d percent\n", (100 * NumConsistentPings) / NumPingsCheckedForConsistency); Set_Registry_Int("Quality", (100 * NumConsistentPings) / NumPingsCheckedForConsistency); } } break; } /* ** Error. See what the error code tells us. */ switch (failure_code) { /* ** Fatal errors. */ case BANDTEST_NO_WINSOCK2: case BANDTEST_NO_RAW_SOCKET_PERMISSION: case BANDTEST_NO_RAW_SOCKET_CREATE: case BANDTEST_NO_UDP_SOCKET_BIND: tried = retries + 1; break; /* ** Erros where retrying might help. */ case BANDTEST_NO_TTL_SET: case BANDTEST_NO_PING_RESPONSE: case BANDTEST_NO_FINAL_PING_TIME: case BANDTEST_NO_EXTERNAL_ROUTER: case BANDTEST_NO_IP_DETECT: break; /* ** Errors we should never get. */ case BANDTEST_OK: case BANDTEST_UNKNOWN_ERROR: case BANDTEST_BAD_PARAM: default: DebugString("Failed with error code %d\n", failure_code); //assert(false); break; } if (tried < retries + 1) { DebugString("Retry %d\n", tried); } } timeEndPeriod(1); Close_Registry(); return(bps); } /*********************************************************************************************** * Upstream_Detect-- Try and figure out what our upstream bandwidth is * * * * * * * * INPUT: IP address of server to use as destination IP in tests.Server receives no packets * * My IP. * * (out) Extended error code * * * * OUTPUT: Bandwidth in bits per second. 0 = couldn't detect. 0xffffffff means **BIG** * * * * WARNINGS: None * * * * HISTORY: * * 10/3/2001 11:21AM ST : Created * *=============================================================================================*/ unsigned long Upstream_Detect(unsigned long server_ip, unsigned long my_ip, int &failure_code, unsigned long &downstream, BandtestSettingsStruct *settings) { struct sockaddr_in host_address; struct sockaddr_in address; char temp_buffer[640]; int seq_id; int router_ttl = 0; struct sockaddr_in router_addr; unsigned short source_port = 1230; int ttl; unsigned short packet_sequencer = 0; float average_ping = 0.0f; //float lowest_ping = 0.0f; unsigned long ping_dest_address = 0; unsigned long path_to_server[256]; int hops_to_server = 0; unsigned long upstream_bandwidth; unsigned long ping_times[100]; unsigned long performance_timer = timeGetTime(); unsigned long detect_start_time = performance_timer; int i; /* ** Fill the temp buffer with garbage so it doesn't compress so well on modems. */ srand(timeGetTime()); for (int b=0 ; b 2500) { DebugString("Failed to get response to IP detect ping\n"); failure_code = BANDTEST_NO_IP_DETECT; Close_Raw_Sockets(); return(0); } }; /* ** See if the router is on the same network as me. */ if (seq_id == 50) { my_ip = ping_dest_address; #ifdef _DEBUG ping_dest_address = htonl(my_ip); DebugString("Detected my IP as %s\n", Addr_As_String((unsigned char*)&ping_dest_address)); #endif //_DEBUG } else { DebugString("Unexpected response to IP detect ping\n"); failure_code = BANDTEST_NO_IP_DETECT; Close_Raw_Sockets(); return(0); } DebugString("Took %d ms to discover my IP address\n", timeGetTime() - performance_timer); } /* ** Get the path to the server. ** */ performance_timer = timeGetTime(); hops_to_server = Get_Path_To_Server(&path_to_server[0], my_ip, server_ip); DebugString("Took %d ms to find path to server\n", timeGetTime() - performance_timer); if (hops_to_server == 0) { DebugString("Failed to get path to server\n"); failure_code = BANDTEST_NO_EXTERNAL_ROUTER; Close_Raw_Sockets(); return(0); } /* ** Dump out the whole path to the server. */ #ifdef _DEBUG DebugString("Found path to server...\n"); for (i=0 ; i 3) { first_router = 2; } for (i=first_router ; i 100) { num_pings = 6; } } pings = Ping_Host(path_to_server[i], my_ip, num_pings, 0, ping_times, timeout); if (pings) { //lowest_ping = Lowest_Ping(pings, ping_times); average_ping = Average_Ping(pings, ping_times, true); //DebugString("Lowest ping time to external router is %.2f ms\n", lowest_ping); DebugString("Average ping time to external router is %.2f ms\n", average_ping); DebugString("Took %d ms to find average ping\n", timeGetTime() - performance_timer); performance_timer = timeGetTime(); break; } else { DebugString("Failed to ping external router\n"); failure_code = BANDTEST_NO_PING_RESPONSE; Close_Raw_Sockets(); return(0); } } } } if (router_ttl == 0) { DebugString("Failed to find external router\n"); failure_code = BANDTEST_NO_EXTERNAL_ROUTER; Close_Raw_Sockets(); return(0); } assert(router_ttl != 0); /* ** If the ping time is low, it's *probably* a high bandwidth connection so we can get a more accurate test by sending more ** packets without taking tooooo long. */ int num_udp_packets = settings->SlowPingPackets; unsigned long timeout = 8*TIMER_SECOND; if (average_ping < (float)(settings->FastPingThreshold)) { num_udp_packets = settings->FastPingPackets; timeout = 4*TIMER_SECOND; } /* ** Set the UDP TTL to the next router down the list after the one we just pinged. */ SOCKET test_socket = RawSocket; if (settings->AlwaysICMP) { test_socket = ICMPRawSocket; } ttl = router_ttl + 1; int result = setsockopt(test_socket, IPPROTO_IP, IP_TTL, (char*)&ttl, sizeof(ttl)); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set IP_TTL = %d on test socket - error code %d\n", ttl, WSAGetLastError()); failure_code = BANDTEST_NO_TTL_SET; Close_Raw_Sockets(); return(0); } /* ** Make the UDP socket buffer a bit bigger. Not really important... */ int socket_transmit_buffer_size = 128000; result = setsockopt(test_socket, SOL_SOCKET, SO_RCVBUF, (char*)&socket_transmit_buffer_size, 4); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set SO_RECVBUF with error code %d\n", WSAGetLastError()); } /* ** Make a note of the current time so we can see how long this whole process takes. */ unsigned long start_time = timeGetTime(); int base_ttl = ttl; int max_ttl = hops_to_server - 1; if (max_ttl < base_ttl) { max_ttl = base_ttl; } performance_timer = timeGetTime(); /* ** Send a shitload of UDP packets to the next router down the list after the one we just pinged. */ DebugString("Sending %d 500 byte UDP packets\n", num_udp_packets); for (i=0 ; iTTLScatter) { ttl++; if (ttl > max_ttl) { ttl = base_ttl; } int result = setsockopt(test_socket, IPPROTO_IP, IP_TTL, (char*)&ttl, sizeof(ttl)); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set IP_TTL = %d on test socket - error code %d\n", ttl, WSAGetLastError()); failure_code = BANDTEST_NO_TTL_SET; } } /* ** We have to fill the packet with different garbage every time we send it otherwise a modem will be able to ** do delta compression on the packets. */ for (int b=0 ; bAlwaysICMP) { Send_Ping(temp_buffer, 466, ICMPRawSocket, (struct sockaddr *) &host_address, 0); } else { Send_Raw_UDP(temp_buffer, 466, RawSocket, (struct sockaddr *) &host_address, 1234, 4321); } } DebugString("Took %d ms to send bulk packets\n", timeGetTime() - performance_timer); performance_timer = timeGetTime(); /* ** Set the TTL to max on the ICMP socket. This shouldn't be needed but I'm doing it just in case there are any bugs in ** windoze that might confuse TTL settings between sockets. */ ttl = 255; result = setsockopt(ICMPRawSocket, IPPROTO_IP, IP_TTL, (char*)&ttl, sizeof(ttl)); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set IP_TTL = %d - error code %d\n", ttl, WSAGetLastError()); } /* ** Now see what the ping time to the router is. Since there are n UDP packets ahead of this ping before it goes out, the ** ping time will include the time taken to send the UDP packets. */ unsigned long new_router_ping_time = 0xffffffff; packet_sequencer = 5; unsigned long second_start_time = timeGetTime(); float total_time = 0.0f; /* ** Send a couple of pings to reduce the possibility of packet loss being a factor. */ Send_Ping((char*)temp_buffer, 0, ICMPRawSocket, (struct sockaddr *) &router_addr, packet_sequencer); Send_Ping((char*)temp_buffer, 0, ICMPRawSocket, (struct sockaddr *) &router_addr, packet_sequencer + 1); seq_id = -1; while (seq_id != packet_sequencer && seq_id != packet_sequencer+1) { Get_Ping_Response(ICMPRawSocket, seq_id, (struct sockaddr *) &address, 0, ping_dest_address); if (timeGetTime() - second_start_time > timeout) { break; } }; if (seq_id == packet_sequencer || seq_id == packet_sequencer + 1) { unsigned long time_now = timeGetTime(); new_router_ping_time = time_now - second_start_time; total_time = (float)(time_now - start_time); DebugString("Ping time to external router is now %d ms\n", new_router_ping_time); DebugString("Total time to send %d bytes plus send and receive ping is %d ms\n", num_udp_packets * 500, total_time); } else { DebugString("Failed to get final ping response in %d seconds\n", timeout / 1000); failure_code = BANDTEST_NO_FINAL_PING_TIME; Close_Raw_Sockets(); return(0); } if (new_router_ping_time == 0xffffffff) { DebugString("Failed to get final ping response\n"); failure_code = BANDTEST_NO_FINAL_PING_TIME; return(0); } else { total_time -= average_ping; /* ** Work out the bandwidth. ** Approx bps up = ((10000 + 28) * 8) / (time2 - time1). */ if (((unsigned long) total_time) == 0 || (total_time > ((float)0x10000000))) { DebugString("Upstream bandwidth is huge :-)\n"); failure_code = BANDTEST_OK; upstream_bandwidth = 0xffffffff; } else { unsigned long bw = (((num_udp_packets * 500) * 8) * 1000) / (unsigned long)total_time; if (bw > 100000) { float floater = (float)bw / 1024; DebugString("Upstream bandwidth to external router is %.1f kilobits per second\n", floater); } else { DebugString("Upstream bandwidth to external router is %d bits per second\n", bw); } failure_code = BANDTEST_OK; upstream_bandwidth = bw; //return(bw); } } DebugString("Took %d ms to get new ping time\n", timeGetTime() - performance_timer); /* ** If the bandwidth in the registry is close to what we just calculated then use the old downstream calculation from the ** registry. */ unsigned long downstream_bandwidth = upstream_bandwidth; int old_band = Get_Registry_Int("Up", 0); unsigned long diff = abs(upstream_bandwidth - old_band); bool calc_down = true; if (diff < upstream_bandwidth / 10) { downstream_bandwidth = Get_Registry_Int("Down", upstream_bandwidth); if (downstream_bandwidth) { calc_down = false; } } /* ** Store the calculated bandwidth into the registry. */ Set_Registry_Int("Up", upstream_bandwidth); /* ** ** Well, I suppose, since we are here, we might as well have a stab at downstream bandwidth too. ** ** ** Try sending max size pings to our friendly router and subtracting how long we think the upstream should have taken ** from the average ping time. Assume the routers latency is 0 (which of course it isn't). */ bool method_one = false; //float average_time_exceeded = 0.0f; performance_timer = timeGetTime(); if (calc_down && upstream_bandwidth < 576 * 1000 && upstream_bandwidth > 8) { int new_ping_timeout = max((int)(average_ping * 5), 200); #if (0) if (upstream_bandwidth > 80000) { method_one = true; } if (method_one) { DebugString("Detecting downstream bandwidth - method 1\n"); ttl = router_ttl + 1; int result = setsockopt(RawSocket, IPPROTO_IP, IP_TTL, (char*)&ttl, sizeof(ttl)); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set IP_TTL = %d on test socket - error code %d\n", ttl, WSAGetLastError()); failure_code = BANDTEST_NO_TTL_SET; Close_Raw_Sockets(); return(0); } /* ** First we had better wait for all those time exceeded packets to come back. */ seq_id = -1; start_time = timeGetTime(); unsigned long last_icmp_in_time = start_time; for (;;) { Get_Ping_Response(ICMPRawSocket, seq_id, (struct sockaddr *) &address, ntohl(host_address.sin_addr.s_addr), ping_dest_address); if (seq_id != -1) { seq_id = -1; last_icmp_in_time = timeGetTime(); } if (timeGetTime() - start_time > 3*TIMER_SECOND) { break; } if (timeGetTime() - last_icmp_in_time > TIMER_SECOND / 2) { break; } } /* ** Send a shitload of UDP packets to the next router down the list after the one we just pinged. ** Wait for a response for each packet before sending the next. */ num_udp_packets = 50; DebugString("Sending %d 500 byte UDP packets\n", num_udp_packets); int num_pings = 0; for (i=0 ; i (unsigned long)new_ping_timeout) { break; } } } /* ** This is the average time taken to get the TIME EXCEEDED message back. */ DebugString("Quickest time exceeded came in after %.2f ms\n", Lowest_Ping(num_pings, ping_times)); average_time_exceeded = Average_Ping(num_pings, ping_times, true); DebugString("Average time exceeded came in after %.2f ms\n", average_time_exceeded); } #endif //(0) DebugString("Detecting downstream bandwidth - method 2\n"); /* ** First we had better wait for all those time exceeded packets to come back. */ seq_id = -1; start_time = timeGetTime(); unsigned long last_icmp_in_time = start_time; for (;;) { Get_Ping_Response(ICMPRawSocket, seq_id, (struct sockaddr *) &address, ntohl(host_address.sin_addr.s_addr), ping_dest_address); if (seq_id != -1) { seq_id = -1; last_icmp_in_time = timeGetTime(); } if (timeGetTime() - start_time > 3*TIMER_SECOND) { break; } if (timeGetTime() - last_icmp_in_time > TIMER_SECOND / 2) { break; } } //int new_ping_timeout = max((int)(average_ping * 5), 200); float old_average_ping = average_ping; //float old_lowest_ping = lowest_ping; average_ping = 0.0f; /* ** Set the TTL back to max. */ int new_ttl = 255; int result = setsockopt(ICMPRawSocket, IPPROTO_IP, IP_TTL, (char*)&new_ttl, sizeof(new_ttl)); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set IP_TTL = %d - error code %d\n", new_ttl, WSAGetLastError()); failure_code = BANDTEST_NO_TTL_SET; Close_Raw_Sockets(); return(upstream_bandwidth); } /* ** Ping the router once to get a ballpark trip time. */ int pings = Ping_Host(ntohl(router_addr.sin_addr.s_addr), my_ip, 1, 540, ping_times, new_ping_timeout); if (pings == 0) { pings = Ping_Host(ntohl(router_addr.sin_addr.s_addr), my_ip, 1, 540, ping_times, 2 * TIMER_SECOND); if (pings == 0) { pings = Ping_Host(ntohl(router_addr.sin_addr.s_addr), my_ip, 1, 540, ping_times, 2 * TIMER_SECOND); } } if (pings) { /* ** Do more pings if the ping time is low. User a smaller timeout too. */ int num_pings = 15; unsigned long timeout = ping_times[0] * 3; if (ping_times[0] < 100) { num_pings = 50; timeout = 200; } else { if (ping_times[0] > 250) { num_pings = 6; } } if (method_one) { num_pings = 50; } DebugString("Sending large pings\n"); pings = Ping_Host(ntohl(router_addr.sin_addr.s_addr), my_ip, num_pings, 540, ping_times, timeout); if (pings) { //lowest_ping = Lowest_Ping(pings, ping_times); DebugString("Quickest ping came in after %.2f ms\n", Lowest_Ping(num_pings, ping_times)); average_ping = Average_Ping(pings, ping_times, true); //DebugString("Lowest ping time to external router is now %.2f ms\n", lowest_ping); DebugString("Average ping time to external router is now %.2f ms\n", average_ping); } else { DebugString("Failed to ping external router\n"); failure_code = BANDTEST_NO_PING_RESPONSE; Close_Raw_Sockets(); return(upstream_bandwidth); } /* ** Just to make sure we can never get a divide by 0. */ if (upstream_bandwidth / 8 == 0) { upstream_bandwidth = 56000; } float time_upstream = (1000.0f * 540.0f) / ((float)(upstream_bandwidth / 8)); DebugString("Time upstream is %.1f ms\n", time_upstream); float time_downstream = (float)((average_ping - time_upstream) - old_average_ping); /* ** 576 bytes took 'time_downstream' ms to come downstream. */ if (time_downstream > 0.0) { float dbw = ((1000.0f / time_downstream) * 4320.0f); // 540*8 = 4320 //downstream_bandwidth = ((1000.0f / time_downstream) * 576 * 8); downstream_bandwidth = (int)dbw; } DebugString("Took %d ms to calculate downstream bandwidth\n", timeGetTime() - performance_timer); Set_Registry_Int("Down", downstream_bandwidth); } } if (settings->PingProfile) { Ping_Profile(&router_addr, my_ip); } Close_Raw_Sockets(); if (downstream_bandwidth > 100000) { float floater = (float)downstream_bandwidth / 1024; DebugString("Downstream bandwidth from external router is %.1f kilobits per second\n", floater); } else { DebugString("Downstream bandwidth from external router is %d bits per second\n", downstream_bandwidth); } /* ** Method 1 just uses the difference between then time exceeded pings and the echo pings. */ #if (0) if (method_one) { float time_down = average_ping - average_time_exceeded; if (time_down > 0) { float bps_down = (1000.0f / time_down) * (float)(540); bps_down = bps_down * 8.0f; if (bps_down > 100000) { float floater = (float)bps_down / 1024; DebugString("Downstream bandwidth from external router by alt method is %.1f kilobits per second\n", floater); } else { DebugString("Downstream bandwidth from external router by alt method is %f bits per second\n", bps_down); } } } #endif //(0) /* ** Assume down is at least as much as up. */ if (downstream_bandwidth < upstream_bandwidth) { downstream_bandwidth = upstream_bandwidth; } downstream = downstream_bandwidth; DebugString("Total time to detect bandwidth = %d ms\n", timeGetTime() - detect_start_time); failure_code = BANDTEST_OK; return(upstream_bandwidth); } void Ping_Profile(SOCKADDR_IN *router_addr, unsigned long my_ip) { float ping_averages[1000]; unsigned long ping_times[100]; char temp_buffer[128]; char temp_graph[30][80]; static char _ping_graph[30][80] = { " ms| \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " Ping | \n", " Time | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " ms| \n", "________|_______________________________________________________\n", " 0 Payload Size 540\n", " \n", " \n", }; DebugString("Profiling ping responses\n"); memcpy(temp_graph, _ping_graph, sizeof(temp_graph)); /* ** Set the TTL back to max. */ int new_ttl = 255; int result = setsockopt(ICMPRawSocket, IPPROTO_IP, IP_TTL, (char*)&new_ttl, sizeof(new_ttl)); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set IP_TTL = %d - error code %d\n", new_ttl, WSAGetLastError()); } int ping_number = 0; for (int packet_size = 0 ; packet_size < 541 ; packet_size += 10) { /* ** Ping the router once to get a ballpark trip time. */ int pings = Ping_Host(ntohl(router_addr->sin_addr.s_addr), my_ip, 1, packet_size, ping_times, 1000); if (pings == 0) { pings = Ping_Host(ntohl(router_addr->sin_addr.s_addr), my_ip, 1, packet_size, ping_times, 2 * TIMER_SECOND); if (pings == 0) { pings = Ping_Host(ntohl(router_addr->sin_addr.s_addr), my_ip, 1, packet_size, ping_times, 2 * TIMER_SECOND); } } if (pings) { /* ** Do more pings if the ping time is low. User a smaller timeout too. */ int num_pings = 15; unsigned long timeout = ping_times[0] * 3; if (ping_times[0] < 100) { num_pings = 30; timeout = 200; } else { if (ping_times[0] > 250) { num_pings = 6; } } DebugString("Sending %d byte pings\n", packet_size); pings = Ping_Host(ntohl(router_addr->sin_addr.s_addr), my_ip, num_pings, packet_size, ping_times, timeout); if (pings) { float average_ping = Average_Ping(pings, ping_times, true); DebugString("Average ping time to external router is now %.2f ms\n", average_ping); ping_averages[ping_number] = average_ping; } else { DebugString("Failed to ping external router\n"); ping_averages[ping_number] = 0.0; } } ping_number++; } /* ** Scale the ping graph. */ float min_ping = 10000.0f; float max_ping = -1.0f; for (int i=0 ; i 1500) { DebugString("Failed to get any response to ping with TTL = %d\n", ttl); break; } }; if (seq_id != -1) { unsigned long long_router_addr = ntohl(address.sin_addr.s_addr); path[hops_to_server++] = long_router_addr; /* ** See if this router is the target. If so, we are at the end of the path. */ if (long_router_addr == server_ip || seq_id == packet_sequencer) { break; } } packet_sequencer++; } /* ** If we got a good path then store it in the registry. */ if (hops_to_server > 0 && path[hops_to_server - 1] == server_ip) { Set_Registry_Int("MyIP", my_ip); Set_Registry_Int("ServerIP", server_ip); Set_Registry_Int("PathLength", hops_to_server); Set_Registry_Int("PathValid", (int)timeGetTime()); for (int i=0 ; i timeout) { DebugString("Failed to get response to reference ping %d\n", i); PingsLost++; break; } }; /* ** Check the time now and record it as a ping time. */ if (seq_id == _packet_sequencer) { unsigned long router_ping_time = timeGetTime() - start_time; DebugString("Ping time %d to external router %s is %d ms\n", num_pings, Addr_As_String2(&host_addr), router_ping_time); ping_times[num_pings++] = router_ping_time; } _packet_sequencer++; } /* ** Try and get a measure of how far off center some of the pings are. */ if (num_pings > 5) { NumPingsCheckedForConsistency += num_pings; unsigned long *ping_copies = (unsigned long*) _alloca(num_pings * 4); memcpy(ping_copies, ping_times, num_pings * 4); float average_ping = Average_Ping(num_pings, ping_copies, true); float error_permit = 0.25f; if (average_ping < 100.0f) { error_permit = 0.35f; } if (average_ping < 30.0f) { error_permit = 0.5f; } for (int p=0 ; p 2) { Sort_Pings(num_pings, ping_times); memmove((char*)ping_times, ((char*)ping_times) + 4, 4 * (num_pings - 2)); num_pings -= 2; } float average_ping = 0.0; for (int i=0 ; i 5) { float filter_percent = 0.25f; if (average_ping < 10.0) { filter_percent = 0.30f; } float new_average = 0; int num_considered = 0; for (int i=0 ; i 3) { average_ping = new_average / (float) num_considered; } } return(average_ping); } /*********************************************************************************************** * Lowest_Ping -- Get the lowest ping time from a list of pings. * * * * * * * * INPUT: Number of pings * * Ptr to ping times array * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 5:03PM ST : Created * *=============================================================================================*/ float Lowest_Ping(int num_pings, unsigned long *ping_times) { float lowest_ping = 1000000.0; for (int i=0 ; i= 1024); assert(dest_port >= 1024); assert(address != NULL); if (address == NULL) { return(false); } if (payload_size > 550) { return(false); } /* ** Build the header on the stack for convenience. */ unsigned char packetbuf[1024]; UDPHeaderType *header = (UDPHeaderType*) packetbuf; /* ** Fill in the header fields. */ header->SourcePort = source_port; header->DestPort = htons(dest_port); header->Length = (unsigned short) (sizeof(UDPHeaderType) + payload_size); header->Checksum = 0; /* ** Copy the payload into place. */ char *payload_ptr = ((char*)header) + sizeof(UDPHeaderType); memcpy(payload_ptr, payload, payload_size); /* ** Fix up the UDP header checksum. */ int packet_size = payload_size + sizeof(*header); header->Checksum = Get_IP_Checksum((unsigned short *)header, packet_size); /* ** Send it. */ ((struct sockaddr_in*)address)->sin_port = htons(dest_port); int result = sendto(socket, (char*)header, packet_size, 0, address, sizeof(*address)); if (result == SOCKET_ERROR) { DebugString("Send_Raw_UDP - sendto failed with error code %d\n", WSAGetLastError()); return(false); } if (result != packet_size) { return(false); } return(true); } /*********************************************************************************************** * Send_Ping -- Use raw ICMP socket to send a ping message. * * * * * * * * INPUT: Ptr to buffer to copy packet payload from * * Size of packet payload * * Socket to use * * Address to ping * * * * OUTPUT: True if ping sent OK * * * * WARNINGS: None * * * * HISTORY: * * 10/3/2001 11:51AM ST : Created * *=============================================================================================*/ bool Send_Ping(char *payload, int payload_size, SOCKET socket, struct sockaddr *address, int sequence_id) { //DebugString("Send ping. payload=%08X, size=%d, socket=%d, address=%s, seq_id=%d\n", payload, payload_size, socket, Addr_As_String2((struct sockaddr_in *)address), sequence_id); /* ** Asserts. */ assert(payload_size < 550); assert(socket != INVALID_SOCKET); assert(address != NULL); if (address == NULL) { return(false); } if (payload_size > 550) { return(false); } /* ** Build the header on the stack for convenience. */ unsigned char packetbuf[1024]; ICMPHeaderType *header = (ICMPHeaderType*) packetbuf; /* ** Set up the ICMP header fields. */ header->Type = ICMP_ECHO; header->Code = 0; header->ID = (unsigned short) (GetCurrentProcessId() & 0xffff); header->Checksum = 0; header->Sequence = (unsigned short) sequence_id; /* ** Copy the payload into position. */ char *payload_ptr = ((char*)header) + sizeof(ICMPHeaderType); memcpy(payload_ptr, payload, payload_size); /* ** Fix up the checksum in the ICMP header. */ int packet_size = payload_size + sizeof(*header); header->Checksum = Get_IP_Checksum((unsigned short *)header, packet_size); /* ** Send it. */ //DebugString("sendto\n"); int result = sendto(socket, (char*)header, packet_size, 0, address, sizeof(*address)); //DebugString("returned from sendto\n"); if (result == SOCKET_ERROR) { DebugString("sendto failed with error code %d\n", WSAGetLastError()); return(false); } if (result != packet_size) { return(false); } return(true); } /*********************************************************************************************** * Get_Ping_Response -- Wait for an ICMP echo reply or time exceeded message * * * * * * * * INPUT: Socket * * Packet sequence id to look for * * (out) Ptr to buffer to put sender address in * * Address to use to validate pings (i.e. address ping was sent to) * * (out) My address - filled in on ping responses * * * * OUTPUT: True if we got a ping response * * * * WARNINGS: None * * * * HISTORY: * * 10/3/2001 12:57PM ST : Created * *=============================================================================================*/ bool Get_Ping_Response(SOCKET socket, int &seq_id, struct sockaddr *address, unsigned long validate_address, unsigned long &my_address) { struct sockaddr_in addr; int addr_len; char recv_buffer[1024]; unsigned long bytes; int result = ioctlsocket(socket, FIONREAD, &bytes); /* ** Result of 0 is success. */ if (result != 0) { Sleep(0); return(false); } else { /* ** If there is outstanding data, 'bytes' will contain the size of the next queued datagram. */ if (bytes == 0) { Sleep(0); return(false); } else { /* ** Call recvfrom function to get the outstanding packet. */ addr_len = sizeof(addr); result = recvfrom(socket, recv_buffer, sizeof(recv_buffer), 0, (LPSOCKADDR)&addr, &addr_len); if (result == SOCKET_ERROR) { DebugString("recvfrom failed with error code %d\n", WSAGetLastError()); return(false); } //DebugString("Get_Ping_Response - received packet %d bytes long\n", result); //DebugString("Get_Ping_Response - recvfrom %s\n", Addr_As_String2(&addr)); if (result < (sizeof(IPHeaderType) + sizeof(ICMPHeaderType))) { DebugString("Get_Ping_Response - packet header too small\n"); return(false); } /* ** Decode the ping response. We get the IP header and all. */ IPHeaderType *ip_header = (IPHeaderType*) recv_buffer; /* ** Get the header length. Length specified in the header is in longs so multiply it by 4 to get bytes. */ int ip_header_size = ip_header->Length * 4; if (result < (ip_header_size + (int)sizeof(ICMPHeaderType))) { DebugString("Get_Ping_Response - packet header reported size too big\n"); return(false); } if (ip_header->Protocol == PROTOCOL_ICMP) { //DebugString("Protocol is ICMP\n"); /* ** Figure out where the ICMP header is. */ //IPHeaderType *ip_header = (IPHeaderType*) recv_buffer; ICMPHeaderType *icmp_header = (ICMPHeaderType*) (recv_buffer + ip_header_size); my_address = ntohl(ip_header->DestIP); switch (icmp_header->Type) { /* ** An echo reply is basically a ping response. */ case ICMP_ECHO_REPLY: //DebugString("Type is ICMP_ECHO_REPLY\n"); if (icmp_header->ID == (unsigned short)(GetCurrentProcessId() & 0xffff)) { memcpy(address, &addr, addr_len); seq_id = icmp_header->Sequence; return(true); } break; /* ** A time exceeded is sent when a router discards a packet due to a TTL of 0. */ case ICMP_TIME_EXCEEDED: //DebugString("Type is ICMP_TIME_EXCEEDED\n"); { /* ** Find the packets original IP header. ICMP_TIME_EXCEEDED is 8 bytes followed by a copy of the original IP ** header followed by the original ICMP header followed by 64 bits of the original payload. Phew. (the original ** payload seems to be frequently lost). */ IPHeaderType *original_ip_header = (IPHeaderType*)(((char*)icmp_header) + 8); if (ntohl(original_ip_header->DestIP) == validate_address) { DebugString("Received ICMP_TIME_EXCEEDED from %s\n", Addr_As_String((unsigned char*)&ip_header->SourceIP)); memcpy(address, &addr, addr_len); seq_id = 100000; return(true); } } break; default: DebugString("Type is %d\n", icmp_header->Type); break; } } else { DebugString("Protocol is %d\n", ip_header->Protocol); } } } return(false); } /*********************************************************************************************** * Open_Raw_Sockets -- Initialize winsock and create the raw sockets we need * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/3/2001 11:31AM ST : Created * *=============================================================================================*/ bool Open_Raw_Sockets(int &failure_code) { WSADATA wsa_data; bool use_group = false; //true; /* ** We need Winsocl 2 for raw sockets. */ if (WSAStartup(MAKEWORD(2,1), &wsa_data) != 0) { DebugString("WSAStartup failed: error code %d\n", GetLastError()); failure_code = BANDTEST_NO_WINSOCK2; return(false); } /* ** Create a socket for UDP packets. */ if (use_group) { RawSocket = WSASocket(AF_INET, SOCK_RAW, IPPROTO_UDP, NULL, SG_UNCONSTRAINED_GROUP, 0); if (RawSocket == INVALID_SOCKET) { DebugString("Unable to create raw UDP socket with SG_UNCONSTRAINED_GROUP - error code \n", WSAGetLastError()); use_group = false; } } if (!use_group) { RawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); } if (RawSocket == INVALID_SOCKET) { DebugString("Unable to create raw UDP socket - error code \n", WSAGetLastError()); WSACleanup(); if (WSAGetLastError() == WSAEACCES) { failure_code = BANDTEST_NO_RAW_SOCKET_PERMISSION; } else { failure_code = BANDTEST_NO_RAW_SOCKET_CREATE; } return(false); } /* ** Get the group number. */ unsigned long group = 0; int length = 4; if (use_group) { if (getsockopt (RawSocket, SOL_SOCKET, SO_GROUP_ID, (char*)&group, &length) == SOCKET_ERROR) { DebugString("Unable to get group for raw socket - error code \n", WSAGetLastError()); use_group = false; } } /* ** Create a socket for ICMP packets. */ if (use_group) { ICMPRawSocket = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, group, 0); } else { ICMPRawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); } if (ICMPRawSocket == INVALID_SOCKET) { DebugString("Unable to create raw ICMP socket - error code \n", WSAGetLastError()); closesocket(RawSocket); WSACleanup(); if (WSAGetLastError() == WSAEACCES) { failure_code = BANDTEST_NO_RAW_SOCKET_PERMISSION; } else { failure_code = BANDTEST_NO_RAW_SOCKET_CREATE; } return(false); } /* ** Set the priority for the sockets. */ //unsigned long priority; //getsockopt (RawSocket, SOL_SOCKET, SO_GROUP_PRIORITY, (char*)&priority, &length); //getsockopt (ICMPRawSocket, SOL_SOCKET, SO_GROUP_PRIORITY, (char*)&priority, &length); unsigned long new_priority = 50; int result = setsockopt(RawSocket, SOL_SOCKET, SO_GROUP_PRIORITY, (char*)&new_priority, sizeof(new_priority)); if (result != 0) { DebugString("Unable to set priority on UDP socket - error code %d\n", WSAGetLastError()); } new_priority = 1; result = setsockopt(ICMPRawSocket, SOL_SOCKET, SO_GROUP_PRIORITY, (char*)&new_priority, sizeof(new_priority)); if (result != 0) { DebugString("Unable to set priority on ICMP socket - error code %d\n", WSAGetLastError()); } return(true); } /*********************************************************************************************** * Close_Raw_Sockets -- Initialize winsock and create the raw sockets we need * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/3/2001 11:31AM ST : Created * *=============================================================================================*/ void Close_Raw_Sockets(void) { if (RawSocket != INVALID_SOCKET) { closesocket(RawSocket); } if (RawSocket != INVALID_SOCKET) { closesocket(ICMPRawSocket); } WSACleanup(); } /*********************************************************************************************** * Get_IP_Checksum -- Create a checksum value for an IP packets * * * * * * * * INPUT: Buffer * * Buffer size * * * * OUTPUT: Checksum * * * * WARNINGS: None * * * * HISTORY: * * 10/3/2001 11:44AM ST : Created * *=============================================================================================*/ unsigned short Get_IP_Checksum(unsigned short *buffer, int size) { unsigned long checksum = 0; int new_size = size; unsigned short *bufptr = buffer; while(new_size >1) { checksum += *bufptr++; new_size -= sizeof(unsigned short); } if (new_size) { checksum += *(unsigned char*) bufptr; } checksum = (checksum >> 16) + (checksum & 0xffff); checksum += (checksum >> 16); checksum = ~checksum; return ((unsigned short) checksum); } bool Set_Registry_Int(const char *name, int value) { int result = RegSetValueEx(RegistryKey, name, 0, REG_DWORD, (unsigned char*)&value, sizeof(value)); return((result == ERROR_SUCCESS) ? true : false); } int Get_Registry_Int(const char *name, int def_value) { unsigned long type; unsigned long data; unsigned long data_size = sizeof(data); if (RegQueryValueEx(RegistryKey, name, NULL, &type, (unsigned char*)&data, &data_size) == ERROR_SUCCESS) { return(data); } return(def_value); } bool Open_Registry(void) { HKEY key; unsigned long disposition; long result = RegCreateKeyEx(HKEY_LOCAL_MACHINE, RegistryPath, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &key, &disposition); if (result == ERROR_SUCCESS) { RegistryKey = key; return(true); } return(false); } void Close_Registry(void) { RegCloseKey(RegistryKey); } #ifdef _DEBUG /*********************************************************************************************** * DebugString -- Debug output * * * * * * * * INPUT: Printf format * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/3/2001 11:36AM ST : Created * *=============================================================================================*/ void DebugString (char const * string, ...) { static char buffer[1024]; static char filebuf[1024]; static char path_to_exe[512]; static char drive[_MAX_DRIVE]; static char dir[_MAX_DIR]; va_list va; strcpy(buffer, "BandTest: "); va_start(va, string); vsprintf(&buffer[10], string, va); va_end(va); DWORD actual; if (DebugFile == INVALID_HANDLE_VALUE) { GetModuleFileName (GetModuleHandle(NULL), &path_to_exe[0], 512); _splitpath(path_to_exe, drive, dir, NULL, NULL); _makepath(DebugFileName, drive, dir, "bandtest", "txt"); DebugFile = CreateFile(DebugFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } else { DebugFile = CreateFile(DebugFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } OutputDebugString (buffer); if (DebugFile != INVALID_HANDLE_VALUE) { SetFilePointer (DebugFile, 0, NULL, FILE_END); char *srcbuf = buffer; char *destbuf = filebuf; char c; while (*srcbuf != 0) { c = *srcbuf++; if (c == '\n') { *destbuf++ = '\r'; } *destbuf++ = c; } *destbuf = 0; WriteFile(DebugFile, filebuf, strlen(filebuf), &actual, NULL); CloseHandle (DebugFile); } } /*********************************************************************************************** * Addr_As_String -- Get a human readable internet address * * * * * * * * INPUT: Address ptr * * * * OUTPUT: String representation * * * * WARNINGS: None * * * * HISTORY: * * 8/31/2001 3:48PM ST : Created * *=============================================================================================*/ char * Addr_As_String2(struct sockaddr_in *addr) { static char _string[128]; sprintf(_string, "%d.%d.%d.%d ; %d", (int)(addr->sin_addr.S_un.S_un_b.s_b1), (int)(addr->sin_addr.S_un.S_un_b.s_b2), (int)(addr->sin_addr.S_un.S_un_b.s_b3), (int)(addr->sin_addr.S_un.S_un_b.s_b4), (int)(addr->sin_port)); return(_string); } /*********************************************************************************************** * Addr_As_String -- Get a human readable internet address * * * * * * * * INPUT: Address ptr * * * * OUTPUT: String representation * * * * WARNINGS: None * * * * HISTORY: * * 8/31/2001 3:48PM ST : Created * *=============================================================================================*/ char * Addr_As_String(unsigned char *addr) { static char _string[128]; sprintf(_string, "%d.%d.%d.%d", (int)(addr[0]), (int)(addr[1]), (int)(addr[2]), (int)(addr[3])); return(_string); } #endif //_DEBUG #if (0) /* ** Try to find a router with a different address class than ours. Start at TTL = 1 and increase until we find what we ** are looking for. */ ttl = 1; hops_to_server = 0; found_whole_path = false; do { performance_timer = timeGetTime(); router_ttl = 0; for (ttl ; ttl < 100 ; ttl++) { memcpy(&address, &host_address, sizeof(address)); /* ** Set the TTL. */ int result = setsockopt(ICMPRawSocket, IPPROTO_IP, IP_TTL, (char*)&ttl, sizeof(ttl)); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set IP_TTL = %d - error code %d\n", ttl, WSAGetLastError()); failure_code = BANDTEST_NO_TTL_SET; Close_Raw_Sockets(); return(0); } unsigned long start_time = timeGetTime(); /* ** Send a ping with the previously set TTL. */ DebugString("Sending ping with TTL = %d\n", ttl); Send_Ping(temp_buffer, 0, ICMPRawSocket, (struct sockaddr *) &address, packet_sequencer); /* ** Wait for a ping response. */ seq_id = -1; while (seq_id == -1) { Get_Ping_Response(ICMPRawSocket, seq_id, (struct sockaddr *) &address, ntohl(host_address.sin_addr.s_addr), ping_dest_address); if (timeGetTime() - start_time > 5000) { DebugString("Failed to get response to ping with TTL = %d\n", ttl); break; //failure_code = BANDTEST_NO_PING_RESPONSE; //Close_Raw_Sockets(); //return(0); } }; if (seq_id != -1) { unsigned long long_router_addr = ntohl(address.sin_addr.s_addr); if (!found_whole_path) { path_to_server[hops_to_server++] = long_router_addr; } /* ** See if this router is the target. If so, we are at the end of the path. */ if (long_router_addr == server_ip || seq_id == packet_sequencer) { /* ** We better have found an external router by now... */ assert(router_ttl != 0); found_whole_path = true; break; } /* ** See if the router is on the same network as me. */ if (router_ttl == 0 && (long_router_addr & 0xffff0000) != (my_ip & 0xffff0000)) { memcpy(&router_addr, &address, sizeof(router_addr)); router_ttl = ttl; DebugString("Found external router at %s - ttl = %d\n", Addr_As_String((unsigned char*)&address.sin_addr.s_addr), router_ttl); if (found_whole_path || settings->TTLScatter == 0) { break; } } } packet_sequencer++; } //if (router_ttl == 0) { // DebugString("Failed to find external router\n"); // failure_code = BANDTEST_NO_EXTERNAL_ROUTER; // Close_Raw_Sockets(); // return(0); //} //assert(router_ttl != 0); if (router_ttl == 0) { continue; } DebugString("Took %d ms to find external router\n", timeGetTime() - performance_timer); performance_timer = timeGetTime(); average_ping = 0; /* ** Set the TTL back to max. */ int new_ttl = 255; int result = setsockopt(ICMPRawSocket, IPPROTO_IP, IP_TTL, (char*)&new_ttl, sizeof(new_ttl)); if (result == SOCKET_ERROR) { DebugString("setsockopt failed to set IP_TTL = %d - error code %d\n", new_ttl, WSAGetLastError()); failure_code = BANDTEST_NO_TTL_SET; Close_Raw_Sockets(); return(0); } /* ** Ping the router directly to get a reference round trip time. ** Try 3 pings and take an average. */ packet_sequencer = 0; memset(ping_times, 0xff, sizeof(ping_times)); num_pings = 0; for (i=0 ; i<3 ; i++) { unsigned long start_time = timeGetTime(); Send_Ping((char*)temp_buffer, 0, ICMPRawSocket, (struct sockaddr *) &router_addr, packet_sequencer); seq_id = -1; while (seq_id != packet_sequencer) { Get_Ping_Response(ICMPRawSocket, seq_id, (struct sockaddr *) &address, ntohl(host_address.sin_addr.s_addr), ping_dest_address); if (timeGetTime() - start_time > 2000) { DebugString("Failed to get response to reference ping %d\n", i); failure_code = BANDTEST_NO_PING_RESPONSE; break; //Close_Raw_Sockets(); //return(0); } }; //assert(seq_id == packet_sequencer); //assert(seq_id < 3); if (seq_id == packet_sequencer) { unsigned long router_ping_time = timeGetTime() - start_time; DebugString("Ping time %d to external router %s is %d ms\n", num_pings, Addr_As_String2(&router_addr), router_ping_time); ping_times[num_pings++] = router_ping_time; } packet_sequencer++; } //assert(num_pings == 3); average_ping = 0; if (num_pings > 0) { for (int p=0 ; p