Jan 28 2013
Remote Monitoring of Network Connections with Arduino and LEDs
Using cerealbox to create a colorful visualization of your TCP/UDP connections.
Inspiration
At Defcon in 2011 I attended a talk by Steve Ocepek over at Spider Labs introducing a neat little project he was working on. I recommend you watch the talk, but here’s the jist of it. Basically, he was using the Arduino with an 8×8 LED board (each coordinate having an RGB value) to visualize the current established connections on a given network interface. Each coordinate of the matrix was color-coded based on country code, so you could differentiate connections based on region. As you made an outbound connection, lights would suddenly appear on the board, indicating where you are connecting to. And when those connections dropped or you disconnected, the lights disappeared. Network monitoring was done with libpcap, and a list of the active connections were sent to the Arduino using the serial interface and a perl script he coded. He called it cerealbox. I thought it was really neat to have a display of network connections always visible, without exhausting valuable screen real estate. Not only was it really useful for network admins, it was also really pretty! I mean, who doesn’t like shiny bright LEDs? And he provided the source for it, so you could buy the boards and set this up yourself.
I’d never worked with Arduino before, but I was inspired enough by this proof of concept to at least get his demo working for myself, and maybe make a few modifications. But suiting the project to my needs required a few additional considerations.
Requirements
Continent Codes, not Country Codes
Getting the demo set up was the easy part. But the color-coding was randomized based on country code. I wanted something a bit more useful – something that I could clearly see what region a connection was coming from, rather than a randomized color. The problem is that there’s a lot of countries in the world. Like, almost 200. With so many countries, the variations of color would be too slight for me to figure out at a glance which region a connection is coming from. So I wanted to code it based on continent code instead, with higher contrast between indicators. Here’s my schema:
- Blue = Europe
- Orange = Asia
- Purple = Oceania
- Yellow = Africa
- White = South America
- Teal = North America
- Pink = Antarctica & Local
- Green = United States
- Red = Special IPs (My VPSes)
Okay, so I cheated – not each one of those is a continent. US is it’s own color, and it seems a little weird to have Antarctica grouped with local connections. But connections coming from Antarctica seemed like such an extreme edge case, and I didn’t want to give up an entire color for a continent with no permanent r. So there are those exceptions, and then red for my own VPSes.
Client-Server Architecture
One of the things I really wanted to do was be able to visualize the connections of any machine, not just the one connected to the Arduino. The original project bundled the packet sniffing and serial connection to the Arduino in one neat little perl script, but I wanted to separate the part that monitors the network connection and the part that sends it to the arduino. The former will belong to the client, and the latter to the server.
I originally considered just using the Arduino standalone, with the color shield chained directly on top of a WiFly shield, and sending network connections to a server hosted directly on the Arduino. I even modified the Colorduino library to use different pins from the WiFly. But in the end I wanted to ensure that the connection to the Arduino was secure. This would be difficult to implement with the 32k space limitations of the Uno I was working with.
Secure Transport Layer
Between the client & server, I wanted to add a secure network layer. I decided on having the client/server negotiate an SSL connection with a simple password authentication.
Python, not Perl
Not to start a religious war, but I’m more comfortable in Python, so I’d have to rewrite the network monitoring and serial communication components of the script Steve had written.
RasPi
I wanted the server to run on the Raspberry Pi. Actually this was the easiest part – with Raspbian, it was just a matter of installing pip with python 2.7 and installing everything else with pip. No assembly required!
Implementation
Client
Since the geolocation lookup happens immediately as a connection is read on the client-side, I needed to use a few geoip libraries: pygeoip to perform a lookup on the country code, and incf.countryutils to then fetch the continent code. Additionally, a packet sniffer was needed. I considered using scapy for this task, which I’ve had a lot of fun with in the past and I highly recommend as a versatile tool for python packet-slicing. However, scapy seemed a bit heavyweight for the task at hand, so I decided on pcapy for sniffing and imapacket for dissecting packets.
As packets come across the wire, we keep track of them, keeping a hash (in python parlance, a dict) of udp and tcp connections. For the TCP connections, we look for the syn + ack flags, indicating a connection is established. Conversely, a fin or rst flag indicates the connection has been severed. Since UDP packets are stateless, we immediately record that a connection is established when we see any UDP traffic. Periodically, we have a sweeper to handle timed out connections.
Once a connection is established or severed, perform a geoip lookup on the remote IP, and immediately send that data to the server via an ssl socket.
Server
The server is super simple. It just receives signals coming in over the ssl bind socket, and forwards that signal directly to the arduino. Serial communications are handled by the pyserial module. Several other standard library modules are used by both the client and the server, such as ssl and socket to instantiate a secure communication.
By the time we write to the serial interface, the message we send contains the following information: [Connection Closed or Opened],[Remote Mac Address],[Remote ipv4 IP],[Remote Port],[Country Code],[Continent Code]
Arduino
I didn’t change what Steve wrote a whole lot here, except for reading the continent code and setting the colors appropriately. I did separate out a file for IP addresses I wanted to specially highlight.
Challenges
One of the challenges I’ve had is when to determine when a timeout on a TCP connection has occurred. Some connections (for example, SSH), can stand for several hours before timing out. Others (HTTP, for example) time out very quickly. From my understanding, it’s impossible to tell from the transport layer if a connection has timed out in a given period of time. We can infer the timeout from the application layer, but it seems a bit inelegant of a solution. I have yet to find a good way of dealing with this problem. If you know of a solution, please contact me.
On the client, the sniffing of packets by pcapy is a blocking call. Sending a ctrl-c doesn’t throw a KeyboardInterrupt exception until a new packet is actually read. In order to ensure that users can kill the client immediately, I had to use the multiprocessing module and actually use a separate process to determine if a KeyboardInterrupt was issued, which killed both itself and the sniffing loop process. This seems a bit silly to me, but I’m not sure if there’s any better way to do this. Again, if you have any suggestions, let me know.
Left: cerealbox visualizing http(s) connections. Right: bittorrent traffic
Outcome
There’s still a few bugs to squash. But it works, it’s pretty, and it’s useful! Check out colorduino on my github and let me know what you think!