2487 lines
87 KiB
C++
2487 lines
87 KiB
C++
|
/*
|
||
|
** 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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** 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 <winsock2.h>
|
||
|
#include <ws2tcpip.h>
|
||
|
#pragma warning(disable:4201)
|
||
|
#include <mmsystem.h>
|
||
|
#pragma warning(default:4201)
|
||
|
|
||
|
#include <malloc.h>
|
||
|
#include <stdio.h>
|
||
|
#include <conio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#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<sizeof(temp_buffer) ; b++) {
|
||
|
temp_buffer[b] = (unsigned char) (rand() & 0xff);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Try to open the sockets we need.
|
||
|
*/
|
||
|
if (!Open_Raw_Sockets(failure_code)) {
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Bind the UDP socket to a port.
|
||
|
*/
|
||
|
struct sockaddr_in temp_addr;
|
||
|
temp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||
|
temp_addr.sin_port = htons(source_port);
|
||
|
temp_addr.sin_family = AF_INET;
|
||
|
if (bind(RawSocket, (LPSOCKADDR) &temp_addr, sizeof(temp_addr)) == SOCKET_ERROR) {
|
||
|
DebugString("bind failed with error code %d\n", WSAGetLastError());
|
||
|
failure_code = BANDTEST_NO_UDP_SOCKET_BIND;
|
||
|
Close_Raw_Sockets();
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Bind the ICMP socket to our local address.
|
||
|
*/
|
||
|
#if (0)
|
||
|
temp_addr.sin_port = 0;
|
||
|
if (bind(ICMPRawSocket, (LPSOCKADDR) &temp_addr, sizeof(temp_addr)) == SOCKET_ERROR) {
|
||
|
DebugString("bind failed with error code %d\n", WSAGetLastError());
|
||
|
failure_code = BANDTEST_NO_UDP_SOCKET_BIND;
|
||
|
Close_Raw_Sockets();
|
||
|
return(0);
|
||
|
}
|
||
|
#endif //(0)
|
||
|
|
||
|
|
||
|
address.sin_addr.s_addr = htonl(server_ip);
|
||
|
address.sin_port = 80; //www port number. We can use anything here.
|
||
|
address.sin_family = AF_INET;
|
||
|
memcpy(&host_address, &address, sizeof(host_address));
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
** If we don't know our own IP, ping someone we do know to find out.
|
||
|
*/
|
||
|
if (my_ip == 0) {
|
||
|
|
||
|
performance_timer = timeGetTime();
|
||
|
|
||
|
DebugString("Detecting my IP\n");
|
||
|
Send_Ping(temp_buffer, 0, ICMPRawSocket, (struct sockaddr *) &address, 50);
|
||
|
|
||
|
/*
|
||
|
** Wait for a ping response.
|
||
|
*/
|
||
|
unsigned long start_time = timeGetTime();
|
||
|
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 > 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<hops_to_server ; i++) {
|
||
|
unsigned long temp = htonl(path_to_server[i]);
|
||
|
DebugString(" %02d : %s\n", i, Addr_As_String((unsigned char*)(&temp)));
|
||
|
}
|
||
|
#endif //_DEBUG
|
||
|
|
||
|
int first_router = 0;
|
||
|
if (hops_to_server > 3) {
|
||
|
first_router = 2;
|
||
|
}
|
||
|
|
||
|
for (i=first_router ; i<hops_to_server ; i++) {
|
||
|
if ((path_to_server[i] & 0xffff0000) != (my_ip & 0xffff0000)) {
|
||
|
|
||
|
/*
|
||
|
** Ping the router once to see if it responds.
|
||
|
*/
|
||
|
int pings = Ping_Host(path_to_server[i], my_ip, 1, 0, ping_times, 2*TIMER_SECOND);
|
||
|
if (pings == 0) {
|
||
|
pings = Ping_Host(path_to_server[i], my_ip, 1, 0, ping_times, 2*TIMER_SECOND);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** If it responded, this is our man, ping it some more to establish a reference ping average.
|
||
|
*/
|
||
|
if (pings) {
|
||
|
performance_timer = timeGetTime();
|
||
|
router_ttl = i + 1;
|
||
|
router_addr.sin_addr.s_addr = htonl(path_to_server[i]);
|
||
|
router_addr.sin_port = 80;
|
||
|
router_addr.sin_family = AF_INET;
|
||
|
|
||
|
int num_pings = 15;
|
||
|
unsigned long timeout = ping_times[0] * 3;
|
||
|
if (ping_times[0] < 50) {
|
||
|
num_pings = 50;
|
||
|
timeout = 100;
|
||
|
} else {
|
||
|
if (ping_times[0] > 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 ; i<num_udp_packets ; i++) {
|
||
|
|
||
|
/*
|
||
|
** Use a different TTL for each packet if requested.
|
||
|
*/
|
||
|
if (settings->TTLScatter) {
|
||
|
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 ; b<sizeof(temp_buffer) ; b++) {
|
||
|
temp_buffer[b] = (unsigned char) (rand() & 0xff);
|
||
|
}
|
||
|
if (settings->AlwaysICMP) {
|
||
|
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<num_udp_packets ; i++) {
|
||
|
|
||
|
/*
|
||
|
** 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 ; b<sizeof(temp_buffer) ; b++) {
|
||
|
temp_buffer[b] = (unsigned char) (rand() & 0xff);
|
||
|
}
|
||
|
start_time = timeGetTime();
|
||
|
Send_Raw_UDP(temp_buffer, 540, RawSocket, (struct sockaddr *) &host_address, 1234, 4321);
|
||
|
|
||
|
seq_id = -1;
|
||
|
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;
|
||
|
unsigned long ping_time = timeGetTime() - start_time;
|
||
|
ping_times[num_pings++] = ping_time;
|
||
|
break;
|
||
|
}
|
||
|
if (timeGetTime() - start_time > (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<ping_number ; i++) {
|
||
|
min_ping = min(min_ping, ping_averages[i]);
|
||
|
max_ping = max(max_ping, ping_averages[i]);
|
||
|
}
|
||
|
|
||
|
sprintf(temp_buffer, "%3.1f", max_ping);
|
||
|
strncpy(&temp_graph[0][0], temp_buffer, strlen(temp_buffer));
|
||
|
sprintf(temp_buffer, "%3.1f", min_ping);
|
||
|
strncpy(&temp_graph[25][0], temp_buffer, strlen(temp_buffer));
|
||
|
|
||
|
float scale = 25.0f / (max_ping - min_ping);
|
||
|
|
||
|
/*
|
||
|
** Draw the pings onto the graph.
|
||
|
*/
|
||
|
for (i=0 ; i<ping_number ; i++) {
|
||
|
float ping = ping_averages[i];
|
||
|
int position = (int)((ping - min_ping) * scale);
|
||
|
|
||
|
position = 25 - position;
|
||
|
temp_graph[position][i+9] = '*';
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Dump it out.
|
||
|
*/
|
||
|
for (i=0 ; i<30 ; i++) {
|
||
|
DebugString(temp_graph[i]);
|
||
|
cprintf(temp_graph[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* Get_Path_To_Server -- Trace the route to the server *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Ptr to array to receive path IPs *
|
||
|
* My IP address *
|
||
|
* Servers IP address *
|
||
|
* *
|
||
|
* OUTPUT: Number of hops in path *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 10/8/2001 2:07PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
int Get_Path_To_Server(unsigned long *path, unsigned long my_ip, unsigned long server_ip)
|
||
|
{
|
||
|
char reg_name[128];
|
||
|
int path_size = 0;
|
||
|
struct sockaddr_in host_address;
|
||
|
struct sockaddr_in address;
|
||
|
char temp_buffer[640];
|
||
|
int seq_id;
|
||
|
unsigned long ping_dest_address = htonl(my_ip);
|
||
|
|
||
|
/*
|
||
|
** See if the path in the registry looks valid.
|
||
|
*/
|
||
|
int reg_my_ip = Get_Registry_Int("MyIP", 0);
|
||
|
int reg_server_ip = Get_Registry_Int("ServerIP", 0);
|
||
|
int reg_path_length = Get_Registry_Int("PathLength", 0);
|
||
|
int reg_path_time = Get_Registry_Int("PathValid", 0);
|
||
|
|
||
|
/*
|
||
|
** If the ip at either end of the route has changed then the path isn't valid anymore.
|
||
|
*/
|
||
|
if (((unsigned long)reg_my_ip) == my_ip && ((unsigned long)reg_server_ip) == server_ip) {
|
||
|
|
||
|
/*
|
||
|
** If the path is too old then we should probably not consider it valid.
|
||
|
*/
|
||
|
unsigned long time = timeGetTime();
|
||
|
unsigned long last_path_time = (unsigned long) reg_path_time;
|
||
|
|
||
|
/*
|
||
|
** Lets only use it if it's less than 2 hours old.
|
||
|
*/
|
||
|
if ((last_path_time < time) && (time - last_path_time < (TIMER_SECOND * 60 * 120))) {
|
||
|
|
||
|
/*
|
||
|
** OK, the path in the registry looks good - just return that.
|
||
|
*/
|
||
|
for (int i=0 ; i<reg_path_length ; i++) {
|
||
|
sprintf(reg_name, "Path%02d", i);
|
||
|
path[i] = (unsigned long) Get_Registry_Int(reg_name, 0);
|
||
|
if (path[i]) {
|
||
|
path_size++;
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (reg_path_length == path_size) {
|
||
|
DebugString("Using path from registry\n");
|
||
|
return(path_size);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** The path in the registry isn't any good. Discover it for ourselves.
|
||
|
*/
|
||
|
address.sin_addr.s_addr = htonl(server_ip);
|
||
|
address.sin_port = 80; //www port number. We can use anything here.
|
||
|
address.sin_family = AF_INET;
|
||
|
memcpy(&host_address, &address, sizeof(host_address));
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
** 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.
|
||
|
*/
|
||
|
int ttl = 1;
|
||
|
int hops_to_server = 0;
|
||
|
int packet_sequencer = 0;
|
||
|
|
||
|
for (ttl = 1 ; 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());
|
||
|
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);
|
||
|
//DebugString("Ping sent\n");
|
||
|
|
||
|
/*
|
||
|
** 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 > 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<hops_to_server ; i++) {
|
||
|
sprintf(reg_name, "Path%02d", i);
|
||
|
Set_Registry_Int(reg_name, path[i]);
|
||
|
}
|
||
|
return(hops_to_server);
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* Ping_Host -- Send a number of pings to a host and wait for responses *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Host IP address to ping *
|
||
|
* My IP address *
|
||
|
* Number of pings to send *
|
||
|
* Payload size of ping *
|
||
|
* Ptr to array to receive ping times *
|
||
|
* Timeout for each ping *
|
||
|
* *
|
||
|
* OUTPUT: Number of pings received *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 10/8/2001 2:09PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
int Ping_Host(unsigned long host_ip, unsigned long my_ip, int times, int payload_size, unsigned long *ping_times, unsigned long timeout)
|
||
|
{
|
||
|
static int _packet_sequencer = 0;
|
||
|
int num_pings = 0;
|
||
|
char temp_buffer[640];
|
||
|
struct sockaddr_in host_addr;
|
||
|
struct sockaddr_in address;
|
||
|
unsigned long ping_dest_address = htonl(my_ip);
|
||
|
|
||
|
/*
|
||
|
** 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());
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
host_addr.sin_addr.s_addr = htonl(host_ip);
|
||
|
host_addr.sin_port = 80; //www port number. We can use anything here.
|
||
|
host_addr.sin_family = AF_INET;
|
||
|
|
||
|
/*
|
||
|
** Ping n times.
|
||
|
*/
|
||
|
for (int i=0 ; i<times ; i++) {
|
||
|
|
||
|
for (int b=0 ; b<sizeof(temp_buffer) ; b++) {
|
||
|
temp_buffer[b] = (unsigned char) (rand() & 0xff);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Record the time before sending the ping.
|
||
|
*/
|
||
|
unsigned long start_time = timeGetTime();
|
||
|
|
||
|
/*
|
||
|
** Send the ping.
|
||
|
*/
|
||
|
Send_Ping((char*)temp_buffer, payload_size, ICMPRawSocket, (struct sockaddr *) &host_addr, _packet_sequencer);
|
||
|
PingsSent++;
|
||
|
|
||
|
int seq_id = -1;
|
||
|
|
||
|
/*
|
||
|
** Wait for the ping response.
|
||
|
*/
|
||
|
while (seq_id != _packet_sequencer) {
|
||
|
Get_Ping_Response(ICMPRawSocket, seq_id, (struct sockaddr *) &address, ntohl(host_addr.sin_addr.s_addr), ping_dest_address);
|
||
|
if (timeGetTime() - start_time > 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<num_pings ; p++) {
|
||
|
float diff = ((float)ping_times[p]) - average_ping;
|
||
|
if (diff < 0) {
|
||
|
diff = -diff;
|
||
|
}
|
||
|
if (diff < average_ping * error_permit) {
|
||
|
NumConsistentPings++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(num_pings);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* Ping_Compare -- qsort compare function for unsigned longs *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: ping 1 *
|
||
|
* ping 2 *
|
||
|
* *
|
||
|
* OUTPUT: result of comparison *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 10/8/2001 2:13PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
int __cdecl Ping_Compare(const void *ping1, const void *ping2)
|
||
|
{
|
||
|
unsigned long p1 = *((unsigned long*)ping1);
|
||
|
unsigned long p2 = *((unsigned long*)ping2);
|
||
|
|
||
|
if (p1 == p2) {
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
if (p1 < p2) {
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* Sort_Pings -- Sort the ping list into ascending order *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Number of pings in list *
|
||
|
* Ptr to ping time array *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 10/8/2001 2:14PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
void Sort_Pings(int num_pings, unsigned long *ping_times)
|
||
|
{
|
||
|
qsort(ping_times, num_pings, sizeof(unsigned long), &Ping_Compare);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* Average_Ping -- Calculate the average ping time from a list of pings. *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Number of pings *
|
||
|
* Ptr to ping times array *
|
||
|
* Ignore low & high - filter out very low and very high values from average *
|
||
|
* *
|
||
|
* OUTPUT: Nothing *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 10/8/2001 2:14PM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
float Average_Ping(int num_pings, unsigned long *ping_times, bool ignore_low_high)
|
||
|
{
|
||
|
if (ignore_low_high && num_pings > 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<num_pings ; i++) {
|
||
|
average_ping += (float)(ping_times[i]);
|
||
|
}
|
||
|
average_ping = average_ping / (float) num_pings;
|
||
|
|
||
|
/*
|
||
|
** If we have lots of pings, see if we can improve the accurracy by filtering out very small or very big pings.
|
||
|
*/
|
||
|
if (num_pings > 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<num_pings ; i++) {
|
||
|
float diff = ((float)ping_times[i]) - average_ping;
|
||
|
if (diff < 0) {
|
||
|
diff = -diff;
|
||
|
}
|
||
|
if (diff < average_ping * 0.25f) {
|
||
|
new_average += (float) ping_times[i];
|
||
|
num_considered++;
|
||
|
}
|
||
|
}
|
||
|
if (num_considered > 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<num_pings ; i++) {
|
||
|
lowest_ping = min(lowest_ping, (float)(ping_times[i]));
|
||
|
}
|
||
|
return(lowest_ping);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* Send_Raw_UDP -- Send a raw UDP packet. *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: Ptr to buffer to copy packet payload from *
|
||
|
* Size of packet payload *
|
||
|
* Raw UDP socket to use *
|
||
|
* Address to send packet to *
|
||
|
* Source port to put in the UDP header *
|
||
|
* Dest port to put in the UDP header *
|
||
|
* *
|
||
|
* OUTPUT: True if sent OK *
|
||
|
* *
|
||
|
* WARNINGS: None *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 10/3/2001 11:46AM ST : Created *
|
||
|
*=============================================================================================*/
|
||
|
bool Send_Raw_UDP(char *payload, int payload_size, SOCKET socket, struct sockaddr *address, unsigned short source_port, unsigned short dest_port)
|
||
|
{
|
||
|
/*
|
||
|
** Asserts.
|
||
|
*/
|
||
|
assert(payload_size < 550);
|
||
|
assert(socket != INVALID_SOCKET);
|
||
|
assert(source_port >= 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<num_pings ; p++) {
|
||
|
average_ping += ping_times[p];
|
||
|
}
|
||
|
average_ping = average_ping / num_pings;
|
||
|
DebugString("Average ping time to external router is %d ms\n", average_ping);
|
||
|
DebugString("Took %d ms to find average ping\n", timeGetTime() - performance_timer);
|
||
|
performance_timer = timeGetTime();
|
||
|
} else {
|
||
|
/*
|
||
|
** If we didn't get all three pings then go round again.
|
||
|
*/
|
||
|
router_ttl = 0;
|
||
|
}
|
||
|
} while (router_ttl == 0 && ttl < 8);
|
||
|
|
||
|
#endif //(0)
|