Coding Linux's traceroute in C#?

38 views Asked by At

I've been trying to traceroute the Server IP for the Game Planetside2 since many Players and myself have high latency issues lately and wondered at which node those issues occur. So I tried to use CMDs tracert and Ping which didn't work, it gave me the first few addresses but before it reaches the target IP it only returns * * *, so it seems like the Server has some kind of Firewall protection against it.

Instead someone suggested me to use Linux traceroute which I did and using the UDP traceroute command together with the port of the Server IP works fine "traceroute -U -p 20105 69.174.216.21".

Here is how it looks like:

> 1  Desktop (my ip)  0.340 ms
> 2  router (router ip)  3.305 ms
> 3  isp (isp ip)  8.955 ms
> 4  62.214.42.189 (62.214.42.189)  10.675 ms
> 5  62.67.18.94 (62.67.18.94)  15.491 ms
> 6  * * *
> 7  * * *
> 8  * * *
> 9  * * *
> 10  8.227.56.190 (8.227.56.190)  99.620 ms
> 11  69.174.217.5 (69.174.217.5)  98.522 ms
> 12  69.174.216.21 (69.174.216.21)  103.844 ms

Since not everyone has Linux and doesn't know how to install it on Windows, I want to code a C# version which I can give out to the community, so they can check their connections themselves.

I've been trying for a day now but no matter what I do, I fail at a certain thing and here is the issue:

If I use code to use ICMP it works for the first 5 tls (nodes) but then I only get * * * again and it doesn't find the final IP. How it looks like (ICMP):

1 Desktop IP       TtlExpired
2 Router IP        TtlExpired
3 ISP IP           TtlExpired
4 62.214.42.189    TtlExpired
5 62.67.18.94      TtlExpired
6                  failed after 3 attempts.
7                  failed after 3 attempts.
8                  failed after 3 attempts.
9                  failed after 3 attempts.
10 8.227.56.190    DestinationNetworkUnreachable
11                 failed after 3 attempts.
12                 failed after 3 attempts.

If I use code to use UDP instead for the first 6 nodes I get a "SocketException / AggregateException: The connection was terminated due to keepalive activities because an error was detected during the process" and only for the final IP I get the IP:

Traceroute[0]:  - Keepalive blocked
Traceroute[1]:  - Keepalive blocked
Traceroute[2]:  - Keepalive blocked
Traceroute[3]:  - Keepalive blocked
Traceroute[4]:  - Keepalive blocked
Traceroute[5]:  - No response received
Traceroute[6]:  - No response received
Traceroute[7]:  - No response received
Traceroute[8]:  - No response received
Traceroute[9]:  - No response received
Traceroute[10]:  - Keepalive blocked
Traceroute[11]:  - Keepalive blocked
Traceroute[11]: 69.174.216.21 - Target reached

But since it says "connection was terminated" it means there was a connection for at least 1 millisecond? So I wonder If I can get the node IP of from that terminated connection somehow then, but I don't know how to code it?

I also tried TCP but there everything failed.

So now I wonder, how does Linux's traceroute UDP code work, how is it able to get all the IPs? I already tried all of the examples here: TraceRoute and Ping in C# but none of those worked for me in this case.

I've read somewhere about "a raw-socket to listen for incoming ICMP TIME_EXCEEDED" but I'm not sure if that would give me the IP and how to do it. What am I doing wrong, maybe someone could lead me into a right direction with my code?

For ICMP:

var limit = 15;
var buffer = new byte[32];
var pingOpts = new PingOptions(1, true);
var ping = new Ping();

PingReply result = null;

do
{
    int retries = 0;
    pingOpts = new PingOptions(pingOpts.Ttl + 1, pingOpts.DontFragment);

    while (retries < 3)
    {
        result = ping.Send(ipAddress, 1000, buffer, pingOpts);

        if (result.Status != IPStatus.TimedOut)
        {
            yield return result;
            break;
        }

        retries++;
    }

    if (retries == 3)
    {
        Console.WriteLine($"TTL {pingOpts.Ttl} failed after 3 attempts.");
    }
}
while (result.Status != IPStatus.Success && pingOpts.Ttl < limit);

For UDP:

for (short ttl = 0; ttl <= 15; ttl++)
{
    string message = $"Traceroute[{ttl}]: ";

    try
    {

        using (UdpClient udpClient = new UdpClient())
        {
            udpClient.Client.ReceiveTimeout = 3000;
            udpClient.Client.Ttl = ttl;
            udpClient.DontFragment = true;

            udpClient.Send(new byte[1], 1, ipAddress, targetPort);

            Task<UdpReceiveResult> receiveTask = udpClient.ReceiveAsync();

            if (await Task.WhenAny(receiveTask, Task.Delay(3000)) == receiveTask)
            {

                UdpReceiveResult result = receiveTask.Result; //crashes here because of keep-alive smth
                IPAddress senderIp = result.RemoteEndPoint.Address;

                if (senderIp.Equals(IPAddress.Parse(ipAddress)))
                {
                    Console.WriteLine(message + ipAddress.ToString() + " - Target reached");
                }
                else
                {
                    Console.WriteLine(message + $" - Received response from {senderIp}");
                }
            }
            else
            {
                Console.WriteLine(message + $" - No response received");
            }
        }
    }
    catch (SocketException ex)
    {
        if (ex.SocketErrorCode == SocketError.TimedOut)
        {
            Console.WriteLine(message + $" - No response received");
        }
    }
    catch (System.AggregateException ex)
    {
        Console.WriteLine(message + $" - Keepalive blocked");
    }

}
0

There are 0 answers