Internet2

Network Diagnostic Tool (NDT)

About    |    Downloads      |    Tutorial (PPT)     |     Cookbook (PDF)

Introduction

The open-source Network Diagnostic Toolkit (NDT) was originally developed to identify when network performance was sub-par and to give the user (or their admin) guidance on how to correct the problem, but its popularity as an Internet "speed test", typically embedded inside of a web application front-end that collects additional data, has grown in recent years. Notable examples of "early adopters" of NDT in this context include the Virginia Tech eCorridors Community Broadband Map and BroadbandCensus.com. NDT is well suited to this purpose, but it is important to note that in light of its original purpose, it collects additional data that sets it apart from other free and commercial "speed test" programs. Specifically, NDT seeks to determine why a network connection exhibits certain performance characteristics. In addition to measuring the familiar upload and download speeds of a user's connection, NDT also performs tests that can assess factors such as latency, packet loss, congestion, bad cables, and bottlenecks on the end-to-end path from client to server.

The latest release of NDT now provides the ability for web developers to use Javascript to retrieve the values of variables from within a running instance of the NDT applet (Tcpbw100) and use the values of these variables elsewhere in their web applications. In this way, the value added information beyond upload and download speed that NDT collects during a test can be made available to front-end web applications, such as mapping and data collection interfaces.

The variables are accessed by first obtaining a reference to the NDT applet itself, and then calling the appropriate functions to access the desired variables. Note that the applet must have completed its testing cycle before these variables will store meaningful values.



General Procedure for Integrating NDT into a Web Application

The section of the reference document will illustrate how to:

  • embed the NDT applet in your web application
  • obtain a reference to the NDT applet object
  • extract values for variables of interest upon completion of a network performance test

When reading through this section, please refer to the following examples:


Embedding the NDT Applet directly from MeasurementLab (recommended method):


Web developers that want to add an NDT test to their page may do so by simply copying and pasting the following code into the source of any .html, .htm, .php (...etc) page.

Note that width and height tags in all the examples that follow are shown for example only - you can change them to whatever suits your application.

<applet name="NDT" code=Tcpbw100.class codebase="http://ndt.iupui.donar.measurement-lab.org:7123" ARCHIVE="Tcpbw100.jar" width=400 height=400> </applet>

If desired, you may add a parameter to the applet to instruct it to initiate a test immediately upon load:

<applet name="NDT" code=Tcpbw100.class codebase="http://ndt.iupui.donar.measurement-lab.org:7123" ARCHIVE="Tcpbw100.jar" width=400 height=400>
    <PARAM name="autoRun" VALUE="true">
</applet>

Deploying the applet in this way guarantees that you'll always have the latest updates, and you won't have to worry about building it from source. Additionally, by using the above URL, your clients will be routed by MeasurementLab to the closest available NDT server to their location. If you want to retrieve data from the applet, even though it's being served from a remote location, you can still use the API functions below in your page's JavaScript to access the same values you would if you were running your own instance of the applet.

Building your own copy of the NDT Applet from source (old method, not recommended unless the previous approach does not meet your needs):

Prior to the efforts of MeasurementLab to make NDT more accessible to a large audience of web developers that only need the front-end of the tool, it was necessary to build one's own local instance of the NDT applet. This method will still be appropriate for a number of advanced user communities, particularly those involved in network performance research not affiliated with MeasurementLab. However, if a web developer wants to take advantage of MeasurementLab's ability to direct users to the closest server automatically, transparent upgrades over time and doesn't want the overhead of compiling, signing, and deploying a locally hosted Java applet, the above method will be preferable.

NDT is open-source software, and is available as a source code download from http://software.internet2.edu/sources/ndt/. NDT is a client/server application, but this document assumes that front-end application developers will only be interested in deploying the Java applet component, and will use existing NDT servers (such as those provided by MeasurementLab). In order to prepare the applet for use in your web application, follow this procedure. You will need to install the Java Software Development Kit (JDK) to accomplish these steps.

  1. Compile the applet.
    <ndt root>/Applet/javac Tcpbw100.java
  2. Package the applet class files into a .jar file.
    <ndt root>/Applet/jar -cvf Tcpbw100.jar *.class
  3. In order to enable the applet to communicate with NDT servers other than your own (such as those provided by MeasurementLab), you will need to sign the applet. If you have a valid certificate provided by a commercial issuing authority, you will use that; otherwise, you will create a "self-signed" applet. To do this, use the JDK keytool utility to create a keystore. Then, use the jarsigner utility to sign the applet, as follows:
    <ndt root>/Applet/jarsigner -verbose -keystore ./.keystore Tcpbw100.jar <name of certificate>
    When prompted, enter the passphrase for your keystore.
  4. Once the applet is signed, copy the .jar file to your web server.

After these steps have been completed, your applet should be able to communicate with any NDT server. To deploy it, you will embed the applet in your web page using an HTML <applet> tag, as follows:

If you want to test against MeasurementLab's network of testing servers, and have your users directed to the closest web100srv server, do the following:

<applet name="NDT" code=Tcpbw100.class ARCHIVE="Tcpbw100.jar" width=580 height=300>
    <PARAM NAME="testingServer" VALUE="ndt.iupui.donar.measurement-lab.org">
</applet>

If you're running your own web100srv instance at "myserver.mydomain.com", do the following:

<applet name="NDT" code=Tcpbw100.class ARCHIVE="Tcpbw100.jar" width=580 height=300>
    <PARAM NAME="testingServer" VALUE="myserver.mydomain.com">
</applet>

Again, refer to the worked example for a demonstration of this concept.



Using the API to obtain references to variable values

Once the applet is embedded in your web page and functioning properly, you can code your application logic to access variables stored within the NDT applet. You will use JavaScript functions to access these variables by calling the appropriate methods (listed below). All variables from NDT are returned as strings, so if you want to manipulate the values mathematically, you will need to convert them to the appropriate type. Note also that the NDT applet cannot "push" data into your application when it concludes a test - you will need to either interactively extract the data, as shown in the example, or else write application logic that checks to see whether the applet has completed, and then harvests the values. The specific variables that you collect will be up to you, and will depend on the purpose and data requirements of your application.

The general procedure for extracting values from NDT is to obtain a reference to the object on your page that will receive the data, obtain a reference to the NDT applet, and then assign the target object the result of a function in the NDT API. For example, if our page contains a form called "ndtForm" which contains a field called "c2sspd" that will store the output of get_c2sspd(), the JavaScript code would be:

var theNDTForm = document.getElementById("ndtForm");
var NDT = document.applets["NDT"];
theNDTForm.tb_c2sspd.value = NDT.get_c2sspd();

Note that this assumes the applet name is "NDT", as in the example above.
Please refer to the demonstration page for more examples of this procedure for retrieving values from NDT.



List of Available Variables and their Interpretations

Function name
Data type (*)
Units
Interpretation of function results
get_c2sspd() double Mbps Upload speed (client to server). Expressed as Mbps using base-10 conversion (1 Mibit == 1000 Kibit). Clients may convert this value to other units (Kbps, bps, Gbps) as needed.
get_s2cspd() double Mbps Download speed (server to client). Expressed as Mbps using base-10 conversion (1 Mibit == 1000 Kibit). Clients may convert this value to other units (Kbps, bps, Gbps) as needed.
get_CurRwinRcvd() int The current value of the client's TCP receive window.
get_MaxRwinRcvd() int The maximum value of the client's TCP receive window.
get_loss() double ratio The fraction of packets lost during this test.
get_Ping() double msec Returns the minimum Round Trip Time of packets in this test, which approximates the Ping time. Serves as a measure of network latency.
get_MaxRTT() double msec Returns the maximum Round Trip Time of packets in this test, which approximates the longest delay. Serves as a measure of network latency.
get_avgrtt() double The average Round Trip Time of packets in this test. Serves as a measure of network latency.
get_jitter() double msec Jitter can be defined as the variance of latency. In NDT it is calculated as the difference between the maximum and minimum recorded round trip times. Note that this is an imperfect measure - variations in latency may be caused by CPU limiting conditions as well as network limiting conditions.
get_WaitSec() int The amount of time spent waiting following a timeout.
get_CurRTO() int The current value of the TCP time-out counter. If this value is exceeded TCP will consider a packet as lost and it will start the loss recovery process.
get_SACKsRcvd() int The number of Selective Acknowledgment packets received. SACK's are generated when the client detects packet loss.
get_host() String The NDT server (running web100srv) from which this test was run.
get_osVer() String The version of the client's operating system, as reported by the JRE.
get_osName() String The name of the client's operating system, as reported by the JRE.
get_osArch() String The name of the client's processor architecture, as reported by the JRE.
get_javaVer() String The version of the Java Runtime Environment (JRE) present on the client's computer.
get_mismatch() String yes/no Reports whether a duplex mismatch condition exists. A value of "no" means that a duplex-mismatch condition was not detected; a value of "yes" is returned otherwise.
get_Bad_cable() String yes/no Reports whether the test has detected a cable fault. Returns "yes" if a fault is detected, "no" otherwise.
get_congestion() String yes/no A "yes" value indicates that network congestion may be limiting the connection. Returns "no" otherwise.
get_cwndtime() double The percentage of time not spent in a receiver limited or sender limited state. This may indicate a problem or it may just indicate that the connection is not sender/receiver limited.
get_rcvrLimiting() double percentage The percentage of the time the connection is limited by the client machine's receive buffer.
get_optimalRcvrBuffer() double bytes The value to which the client's receive buffer should be set to correct a receiver limiting condition reported by get_rcvrLimiting(). This value is only meaningful if a receiver limiting condition exists.
get_AccessTech() String NDT attempts to guess the "bottleneck" link on the end-to-end path, which is assumed to be the client's immediate physical network connection.
get_clientIP() String IP address The IP address of the client's computer (the machine running the NDT applet in a web browser). This may or may not return a meaningful value depending on the client's operating system.
get_natStatus() String yes/no Reports whether the client appears to be behind a Network Address Translation (NAT) appliance. Returns "yes" if this condition is present, "no" otherwise.
get_DupAcksOut() int Returns the number of Duplicate ACKs sent. This can be used as a measure of packet loss since it reflects the receipt of out-of-order packets.
get_TimeStamp() String date/time Returns the date and time stamp of the moment when the test was initiated.
isReady() String {yes | no | failed} Reports back to the calling environment whether or not the NDT applet has finished its suite of tests. This could be useful as a test for front-end applications collecting the applet's data non-interactively - the other functions made available in this API are not "ready" to provide data until all tests have concluded. Should any of NDT's tests fail, the value of isReady() will be the string "failed". If this is the case, a call to get_errmsg() will return the detailed error message.
get_errmsg() String Returns the detailed error message that is generated if an NDT test fails. This condition may be tested for using the isReady() function, which will return the string "failed" as opposed to the usual "yes" or "no" if there are any failure conditions present in any of the NDT tests (simple firewall, middlebox, server to client, client to server). Possible failure conditions include an inabilty of the NDT applet to establish a socket connection to its testing server, which could be caused by a firewall on ports 3001, 3002 or 3003, or by a proxy server; additional conditions include the web100srv process not running on the remote server, incompatible versions between client and server, or the test being aborted manually by the user, among others.
run_test() void Initiates a test from JavaScript. This provides exactly the same functionality as clicking the "Start" button inside the NDT applet itself. This can eliminate the need for direct user interaction with the NDT applet interface. The applet must have already been instantiated for this to work.

* Data type in this table refers to the underlying (native) data type of a variable. All data is returned to the client using the functions above as type String. In many instances this is sufficient, but should calculations need to be performed on the data, it will need to be converted back to its native data type.