Packet Painter Web App: JavaScript Network Visualizer

packet painter

This post is about a network traffic visualizer I built while working for NetBurner. The source is on BitBucket.

It is difficult to create informative and appealing visualizations of large data sets, and computer networks represent some of the largest.
Grand efforts at Internet visualization often look awesome (the original Opte Project image is in the MOMA),
but they aren’t particularly effective when it comes to conveying information. On the other hand, low-level packet visualizers like TNV
might be useful to engineers or security researchers, but they’re not exactly high art.

The Wireshark output of intern Joe’s packet sniffing project inspired me to make a network traffic visualizer that falls somewhere in between:
informative, but not a pain to look at. The result is PacketPainter, a device that serves an interactive map of connections to and from a local network.
Plug it into your router, connect to the web app, and see where your packets are going!

PacketPainter runs on a NetBurner 54417 module that operates as both a promiscuous hardware packet switch and an HTTP server.
Whenever it captures an IP packet, the NetBurner streams its source and destination addresses to the app over a WebSocket.
The rest is client-side JavaScript – the app receives IP addresses, requests their geolocations from the free Telize API, and draws them on the map in real time.
On July 29, the map of connections to and from the NetBurner office looked like this:

Green indicates more incoming connections; blue more outgoing. Mouse over lines to see IP information about each location. Powered by NetBurnerLeaflet, and Mapbox.

In my initial implementation, the web client used AJAX requests to communicate with the NetBurner server – the browser would simply request more IP addresses at a specified interval. To save some memory and bandwidth I decided to switch to WebSockets. Now, the server waits for a connection from the client and then writes addresses to the socket. I used NetBurner’s HTML processor to inject the socket’s address into the client code at compile-time:

URL = 'ws://<!--VARIABLE IPCAST(GetSocketLocalAddr(fd)) -->/ipstream';

When the server gets a connection, it sets a function to process each Ethernet frame routed through the NetBurner. This function filters out non- IP packets and
device-discovery packets destined for the NetBurner’s own MAC address:

EFRAME* eframe = ((EFRAME*)pp->pData);
InterfaceBlock *fb = GetInterFaceBlock();

// ignore non- IP packets and packets destined for this MAC address
if (memcmp(&(eframe->dest_addr), &fb->theMac, sizeof(&fb->theMac)) == 0 ||
        eframe->eType != ETHERNET_ETHERTYPE_IPv4) {
    return 0;

Then, to avoid blocking the Ethernet stack, it posts the packet’s source and destination addresses to an OSFifo. A background task pends on the FIFO and writes the addresses to the
WebSocket as JSON:

if (FD_ISSET(socket_fd, &write_fds)) {
    char src[32], dst[32], out[128];

    snShowIP(src, 32, addrs->src);
    snShowIP(dst, 32, addrs->dst);

            "{"src":"%s", "
            ""dst":"%s"}" , src, dst);

    writeall(socket_fd, out, strlen(out));

To avoid timeouts, the web app queues the addresses and requests their locations after a specified interval. I used jQuery to execute the AJAX requests:

    url: GeoipApi.URL + ip,
    type: 'GET',
    dataType: 'JSON',
    timeout: AJAX_TIMEOUT,

    success: function(data) {
        if (data.longitude != null) {

            // add address to map
            nv.AddAddr(ip, addr.count_src, addr.count_dst,
                    data.latitude, data.longitude,
          ,, data.isp);

            // remove address from queue
            delete addrs_temp[ip];

I encapsulated the functions for storing and drawing IP addresses in a “NetworkVisualizer” library, implemented in JavaScript as a self-executing anonymous function. The library’s public properties and functions are exported via a global variable nv, just like jQuery’s $. Internally, NetworkVisualizer uses the Leaflet library to render map tiles served by Mapbox. I used open-source Leaflet plugins for the heatmap, label, and fullscreen features, and wrote a custom plugin for the line-toggle button. To allow users to download maps straight from the browser, I used the FileSaver library – that’s how I was able to embed the map you see above.

It is common for individual locations to broadcast many IP addresses – just look at all the Microsoft, Amazon, and Google IPs on the map – so addresses are stored in location objects indexed by their coordinates. When a new address is added, the visualizer either updates an existing location or draws a new one on the map. Each new location is “normalized” for easier viewing: if it is farther than 180 degrees longitude from the user’s home location, it is mirrored to either the left or the right.

function GetNormalizedLoc(lat, lng) {

    var data_lng = lng;
    var difference = home_loc.lng - data_lng;

    if(Math.abs(difference) >= 180) {
        if (difference <= 0) {
            data_lng -= 360;
        } else {
            data_lng += 360
    return L.latLng(lat, data_lng);

The color, opacity, and thickness of each line is determined by the volume of traffic. The more visible the line, the more connections to its location – green indicates more incoming connections; blue indicates more outgoing.

After I got WebSockets working, this was primarily a web project – I’ve never written so much JavaScript! It’s a truly odd little language that is easy to write poorly and surprisingly difficult to write well. It wouldn’t be my choice for the backbone of the modern web, anyway.

On that note, don’t forget to check out the source!

Share this post

Subscribe to our Newsletter

Get monthly updates from our Learn Blog with the latest in IoT and Embedded technology news, trends, tutorial and best practices. Or just opt in for product change notifications.

Leave a Reply
Click to access the login or register cheese