Introduction
Tracert, a command line utility in Windows, displays the IP addresses of the nodes that a network packet encounters while it travels to a destination host from a source. There are some articles, here on CP, that show how to implement tracert functionality:
- CTraceRoute v1.0 by P. J. Naughter.
- Trace route using raw sockets by Babar Qaisrani
The above articles use C++ code, and I needed something in C# for .NET framework 2.0. Luckily, the .NET framework 2.0 has a Ping
component which is used by the code presented in this article to implement a tracert component.
Using the Tracert Component?
The simplest way to use the component in your project is to add the component to the VS.NET toolbox and drag and drop to the appropriate designer surface. If you add the tracert project as a reference to another project, then the Tracert
component is automatically available in the VS.NET toolbox as shown in the screenshot below. Also shown is how the component appears when it is dropped on the design surface.
Once you have added the component to the design surface, you need to set the properties of the component. The most important property you need to set is the HostNameOrAddress
property, which can either be set to a host name or an IP address.
tracert.HostNameOrAddress = "www.codeproject.com";
tracert.HostNameOrAddress = "208.90.12.1";
Next, you need to assign event handlers to the events exposed by the component. This is because the component does its work asynchronously. There are two events you can use: RouteNodeFound
and Done
. You can handle the RouteNodeFound
event to find out intermediate nodes as they are found.
tracert.RouteNodeFound +=
new EventHandler<RouteNodeFoundEventArgs>(tracert_RouteNodeFound);
In the code snippet below, the IP address of the nodes are added to a list view as they are found.
private void tracert_RouteNodeFound(object sender, RouteNodeFoundEventArgs e)
{
ListViewItem item = routeList.Items.Add(e.Node.Address.ToString());
.
.
}
Finally, you need to handle the Done
event to find whether the trace process has finished.
tracert.Done += new System.EventHandler(this.tracert_Done);
private void tracert_Done(object sender, EventArgs e)
{
.
.
IPAddress address = tracert.Nodes[i].Address;
}
Finally, the actual tracing itself can be started by calling the Trace
method.
tracert.Trace();
This is the simplest and the most common way to use the component. You can further control the behavior of the component by setting some additional properties as described in the next section.
Tracert Component Reference
This component has public properties as described below.
Property Name | Type | Description |
---|
MaxHops | int | Maximum number of network hops to take to reach the destination. The tracert process aborts after the maximum number of hops even if the destination is not reached. The default value is 30. |
Nodes | Array of TracertNode | This returns all the nodes in the path. |
HostNameOrAddress | string | The name of the host like www.codeproject.com/, or the IP address in string form like 209.12.1.99. |
Timeout | int | The is the time, in milliseconds, to wait for a particular node to respond before moving to the next node in the path. The default value is 5000. |
IsDone | bool | If true , then it indicates that the destination has been reached, or the maximum number of hops have been taken. |
The methods of this control are as follows:
Method Name | Return Value | Description |
---|
Trace | void | This method starts the actual route tracing. You must set the HostNameOrAddress property before calling this method. This method works asynchronously. |
The events are as follows:
Event Name | Description |
---|
Done | This event is fired when the route tracing is finished. This can happen either when the destination host is reached, or when the maximum hops have been taken. |
RouteNodeFound | This event is fired when an intermediate node has been found on a path. The RouteNodeFoundEventArgs argument supplied to the event handler contains additional information about the node. |
RouteNodeFoundEventArgs Reference
An argument of type RouteNodeFoundEventArgs
is passed to the event handler for the RouteNodeFound
event. The properties of this class are the following:
Property Name | Type | Description |
---|
Node | TracertNode | Indicates the node found in the iteration. |
TracertNode reference
The TracertNode
class is used to encapsulate a node in the path; it has the following properties.
Property Name | Type | Description |
---|
Address | IPAddress | Indicates the IP Address of the node. The address is equal to IPAddress.Any (0.0.0.0 ) if an error or timeout occurs. |
RoundTripTime | int | The interval in milliseconds from the moment the packet was sent to the node and an acknowledgement received by the sender. |
Status | IPStatus | The status of the ping request sent to the node. This can be either IPStatus.Success or IPStatus.Timeout . |
How Tracert Works?
When you send a Ping request to a destination, you have an option to specify the TTL (time-to-live) value. As a network packet travels from one intermediate host to another, its TTL value is reduced by one. When the TTL value becomes zero, the sender gets an error acknowledgement - TTL exceeded. The sender can find out the IP address of the host from which this acknowledgement was received. So, if you set the TTL value to 1 and send a packet to the destination, the packet will be returned by the first node in the path. If you then set the TTL value to 2 and send the packet again, then it will be returned by the second host in the path, and so on. You can continue this process of incrementing the TTL values till the destination has arrived or the maximum number of hops has been reached. The figure below shows a sample network path and the behavior of a network packet:
The arrows in the diagram show the direction of the packet. The bold line shows the path traversed by the packet. The dotted line indicates that the network packet may have traversed the path had the TTL not expired. Now that we have a brief understanding of the way Tracert
works, let's look at how it can be implemented in code.
Implementing Tracert in Code
We will be using the .NET framework's Ping
class to implement the tracert functionality. Let's start directly with a code snippet.
_ping = new Ping();
_ping.PingCompleted += new PingCompletedEventHandler(OnPingCompleted);
_options = new PingOptions(1, true);
_ping.SendAsync(_destination, _timeout, Tracert.Buffer, _options, null);
The Ping
class can send the ping request either synchronously using the Send
method, or asynchronously using the SendAsync
method. When the SendAsync
method is used, the event PingCompleted
is fired to notify the completion of the request. The SendAsync
method has many different overloads. In the overload used in the above code, the parameters are as follows:
Parameter | Description |
---|
_destination | This is the IP address of the final destination. |
_timeout | The time, in milliseconds, to wait for the network request to complete. |
Tracert.Buffer | The byte buffer to send as request. This is taken from a static variable. In our sample, the buffer is a 32 byte array filled with the ASCII code of character A. |
_options |
An object of type PingOptions which indicates the TTL values and also a boolean value that indicates whether the buffer should be fragemented or not. For the initial call, we choose a TTL value of 1. We also choose not to fragment the buffer (in my tests, I have not seen this value impacting the results in any way). |
null (the last parameter) |
The last parameter is a user defined object that is provided in the PingCompleted event handler. We will not be using this, so we pass a null reference. |
Once the request completes, either successfully or unsucessfully, the event handler for the PingCompleted
event is called. In our case, this is the OnPingCompleted
method.
void OnPingCompleted(object sender, PingCompletedEventArgs e)
{
ProcessNode(e.Reply.Address, e.Reply.Status);
if (!this.IsDone)
{
lock (this)
{
if (_ping == null)
{
ProcessNode(_destination, IPStatus.Unknown);
}
else
{
_options.Ttl += 1;
ping.SendAsync(_destination, _timeout,
Tracert.Buffer, _options, null);
}
}
}
}
The OnPingCompleted
function is called with two arguments. The first argument is the sender
object which will be the original Ping
object on which we invoked the SendAsync
method. The argument e
is of type PingCompletedEventArgs
, and provides more information about the event. The important member of the PingCompletedEventArgs
is the Reply
member. This member provides the IPAddress
of the nodes where the packet expired, and a status value. The status value is set to IPStatus.TtlExpired
when the destination is not reached by the packet; otherwise, it will be set to IPStatus.Success
. We process the intermediate node in the ProcessNode
method.
protected void ProcessNode(IPAddress address,
IPStatus status)
{
long roundTripTime = 0;
if (status == IPStatus.TtlExpired ||
status == IPStatus.Success)
{
Ping pingIntermediate = new Ping();
try
{
PingReply reply = pingIntermediate.Send(
address,
_timeout);
roundTripTime = reply.RoundtripTime;
status = reply.Status;
}
catch (PingException e)
{
System.Diagnostics.Trace.WriteLine(e);
}
finally
{
pingIntermediate.Dispose();
}
}
TracertNode node = new TracertNode(address, roundTripTime,
status);
lock (_nodes)
{
_nodes.Add(node);
}
if (RouteNodeFound != null)
RouteNodeFound(this,
new RouteNodeFoundEventArgs(node, this.IsDone));
this.IsDone = address.Equals(_destination);
lock (_nodes)
{
if (!this.IsDone && _nodes.Count >= _maxHops - 1)
ProcessNode(_destination, IPStatus.Success);
}
}
In the ProcessNode
method, we ping the node again. This is because we want to obtain the round trip time to the destination. The PingReply
class does return the round trip time, but it does only when the packet has successfully reached the destination. Once we obtain the round trip time, we fire the RouteNodeFound
event. Next, we add the destination node to the list of nodes. Finally, we check whether we have reached the final destination. If it is true, we set the IsDone
property to true
. This is, in brief, how the component works.
History
- June 25, 2006: Initial submission.