This is a low-level network debugging utility that utilizes raw packet i/o to construct and deconstruct tcp, udp, ipv4, arp, and icmp packets over ethernet.

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
etherealflaim
Date:
Tue Oct 12 05:32:59 2010 +0000
Child:
1:63d4ff534d65
Commit message:
Initial Publish - Slightly unfinished, but usable

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
main.h Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
net/arp.h Show annotated file Show diff for this revision Revisions of this file
net/ethernet.h Show annotated file Show diff for this revision Revisions of this file
net/icmp.h Show annotated file Show diff for this revision Revisions of this file
net/ip.h Show annotated file Show diff for this revision Revisions of this file
net/net.h Show annotated file Show diff for this revision Revisions of this file
net/tcp.h Show annotated file Show diff for this revision Revisions of this file
net/udp.h Show annotated file Show diff for this revision Revisions of this file
scanner.h Show annotated file Show diff for this revision Revisions of this file
sniffer.h Show annotated file Show diff for this revision Revisions of this file
util/log.h Show annotated file Show diff for this revision Revisions of this file
util/types.h Show annotated file Show diff for this revision Revisions of this file
util/util.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,232 @@
+#include "mbed.h"
+#include "string.h"
+
+#include "util/types.h"
+#include "util/log.h"
+#include "util/util.h"
+#include "sniffer.h"
+#include "scanner.h"
+
+#include <string>
+#include <queue>
+
+// The main logger (global scope)
+Sniffer sniffer;
+Log main_log;
+bool main_running = true;
+
+Ethernet_MAC BROADCAST_MAC = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+Ethernet_MAC your_mac;
+
+IP_Address my_ip = {0x00, 0x00, 0x00, 0x00};
+IP_Address your_ip;
+
+bool portscan_started = false;
+bool ip_determined = false;
+
+LocalFileSystem local("local");
+queue<string> commands;
+
+inline void loadCommands()
+{
+  FILE *fp = fopen("/local/ntcmd.txt", "r");
+  if (fp)
+  {
+    char buf[256];
+    while (NULL != fgets(buf, 256, fp))
+    {
+      if (buf[0] == '#') continue;
+      else if (strlen(buf) == 0) continue;
+      else if (!strncmp(buf, "ping", 4)) commands.push("ping");
+      else if (!strncmp(buf, "portscan", 8)) commands.push("portscan");
+      else if (!strncmp(buf, "identify", 8)) commands.push("identify");
+      else main_log.printf("Unknown command: %s", buf);
+    }
+  }
+  else
+  {
+    // Write the ntcmd file with some help
+    main_log.printf("No net tool command file found!");
+    fp = fopen("/local/ntcmd.txt", "w");
+    fprintf(fp, "# Net Tool Command File Help:\n");
+    fprintf(fp, "# 1. Comments start with the pound sign\n");
+    fprintf(fp, "# 2. Commands are on a line by themselves\n");
+    fprintf(fp, "# Known commands:\n");
+    fprintf(fp, "# 1. ping - Send a ping to the host computer every 30 seconds and write results to PING.TXT\n");
+    fprintf(fp, "# 2. portscan - Scan the host computer and write open TCP ports to PORTSCAN.TXT\n");
+    fprintf(fp, "# 3. identify - Write host computer information to IDENTITY.TXT\n");
+  }
+  fclose(fp);
+}
+
+Ticker ping_timer;
+void ping()
+{
+#define PING_BUFEFERSIZE (sizeof(IP_PacketHeader) + sizeof(ICMP_Packet))
+    u8 buffer[PING_BUFEFERSIZE];
+    IP_PacketHeader *ip_packet = (IP_PacketHeader*)buffer;
+    ICMP_Packet *ping_packet = (ICMP_Packet*)ip_packet->data;
+    
+    memset(buffer, '\0', PING_BUFEFERSIZE);
+    
+    *ip_packet = (IP_PacketHeader){0x04, 5, 0, sizeof(IP_PacketHeader)+sizeof(ICMP_Packet), 0, 0, 0, 0, 0, 32, IPPROTO_ICMP, 0x00, my_ip, your_ip};
+    *ping_packet = (ICMP_Packet){ICMP_ECHO_REQUEST, 0x00, 0x00, 0x00, 0x00};
+    
+    fix_endian_icmp(ping_packet);
+    fix_endian_ip(ip_packet);
+    ip_packet->header_checksum = checksum(ip_packet, sizeof(IP_PacketHeader), &ip_packet->header_checksum, 2);
+    ping_packet->checksum = checksum(ping_packet, sizeof(ICMP_Packet), &ping_packet->checksum, 2);
+    
+    
+    FILE *fp = fopen("/local/ping.txt", "w");
+    fprintf(fp, "PING sent...\n");
+    fclose(fp);
+    
+    sniffer.inject(your_mac, ETHERTYPE_IPV4, buffer, PING_BUFEFERSIZE);
+}
+
+void identify()
+{
+    FILE *fp = fopen("/local/identity.txt", "w");
+    fprintf(fp, "Connected host identity:\n");
+    
+    u8 *octets = your_mac.octet;
+    fprintf(fp, "  MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", octets[0],octets[1],octets[2],octets[3],octets[4],octets[5]);
+    
+    octets = your_ip.octet;
+    fprintf(fp, "  IP  Address: %d.%d.%d.%d\n", octets[0],octets[1],octets[2],octets[3]);
+    
+    fclose(fp);
+}
+
+int main() {
+    // Enable logging via USB
+    main_log.enable(LOG_USB);
+    main_log.printf("");
+    
+    // Startup sequence
+    main_log.printf("Booting...");
+    Scanner scanner(&sniffer); // Hooks into TCP handler
+    
+    main_log.printf("Loading commands...");
+    loadCommands();
+
+    main_log.printf("Starting...");
+    while(main_running) {
+        // Get the next frame
+        sniffer.next();
+        
+        // Handle ICMP packet
+        if (sniffer.icmp_packet)
+        {
+            // If the packet is an ICMP ECHO (Ping) request
+            if (sniffer.icmp_packet->type == ICMP_ECHO_REQUEST)
+            {   
+                // Build the ICMP packet
+                sniffer.icmp_packet->type = ICMP_ECHO_REPLY;
+                
+                // Build the IP packet
+                sniffer.ip_packet->destination = sniffer.ip_packet->source;
+                sniffer.ip_packet->source = my_ip;
+                
+                // Inject the packet
+                fix_endian_icmp(sniffer.icmp_packet);
+                fix_endian_ip(sniffer.ip_packet);
+                sniffer.icmp_packet->checksum = checksum(sniffer.icmp_packet, sizeof(ICMP_Packet) + sniffer.data_bytes, &sniffer.icmp_packet->checksum, 2);
+                sniffer.ip_packet->header_checksum = checksum(sniffer.ip_packet, sizeof(IP_PacketHeader), &sniffer.ip_packet->header_checksum, 2);
+                sniffer.inject(sniffer.frame_header->source, ETHERTYPE_IPV4, sniffer.ip_packet, sizeof(IP_PacketHeader) + sizeof(ICMP_Packet) + sniffer.data_bytes);
+            }
+            else if (sniffer.icmp_packet->type == ICMP_ECHO_REPLY)
+            {
+                FILE *fp = fopen("/local/ping.txt", "a");
+                fprintf(fp, "PING reply received\n");
+                fclose(fp);
+            }
+        }
+        // Handle ARP packet
+        else if (sniffer.arp_packet)
+        {
+            // These conditions can be optimized a lot
+            // Check for an ARP request
+            if (is_nonzero_mem(sniffer.arp_packet->sender_hardware_address, 6) &&
+                is_nonzero_mem(sniffer.arp_packet->sender_protocol_address, 4) &&
+                is_nonzero_mem(my_ip.octet, 4) &&
+                is_equal_mem(sniffer.arp_packet->target_protocol_address, my_ip.octet, 4) &&
+                sniffer.arp_packet->operation == 1)
+            {
+                //main_log.printf("ARP Requested:");
+                //main_log.printf("  Responding with IP - %03d.%03d.%03d.%03d", my_ip.octet[0],my_ip.octet[1],my_ip.octet[2],my_ip.octet[3]);
+                Ethernet_MAC *targmac = (Ethernet_MAC*)sniffer.arp_packet->target_hardware_address;
+                IP_Address   *targip  = (IP_Address*)sniffer.arp_packet->target_protocol_address;
+                Ethernet_MAC *srcmac  = (Ethernet_MAC*)sniffer.arp_packet->sender_hardware_address;
+                IP_Address   *srcip   = (IP_Address*)sniffer.arp_packet->sender_protocol_address;
+                
+                // Set the ARP options
+                *targmac = *srcmac;
+                *srcmac  = sniffer.mac;
+                *targip  = *srcip;
+                *srcip   = my_ip; 
+                sniffer.arp_packet->operation = 2;
+                
+                fix_endian_arp(sniffer.arp_packet);
+                sniffer.inject(BROADCAST_MAC, ETHERTYPE_ARP, sniffer.arp_packet, sizeof(ARP_Packet));
+                
+                if (!portscan_started) {
+                    portscan_started = true;
+                }
+            }
+            // Check for an ARP announce
+            if (is_nonzero_mem(sniffer.arp_packet->sender_hardware_address, 6) &&
+                is_zero_mem   (sniffer.arp_packet->sender_protocol_address, 4) &&
+                is_zero_mem   (sniffer.arp_packet->target_hardware_address, 6) &&
+                sniffer.arp_packet->operation == 1)
+            {
+                if (ip_determined) continue;
+                ip_determined = true;
+                
+                //main_log.printf("ARP Announce Received:");
+                my_ip = your_ip = *(IP_Address*)sniffer.arp_packet->target_protocol_address;
+                main_log.printf("Host IP: %03d.%03d.%03d.%03d", my_ip.octet[0],my_ip.octet[1],my_ip.octet[2],my_ip.octet[3]);
+                do { my_ip.octet[3]++; } while (!my_ip.octet[3]);
+                main_log.printf("Tool IP: %03d.%03d.%03d.%03d", my_ip.octet[0],my_ip.octet[1],my_ip.octet[2],my_ip.octet[3]);
+                
+                Ethernet_MAC *targmac = (Ethernet_MAC*)sniffer.arp_packet->target_hardware_address;
+                IP_Address   *targip  = (IP_Address*)sniffer.arp_packet->target_protocol_address;
+                Ethernet_MAC *srcmac  = (Ethernet_MAC*)sniffer.arp_packet->sender_hardware_address;
+                IP_Address   *srcip   = (IP_Address*)sniffer.arp_packet->sender_protocol_address;
+                
+                your_mac = *srcmac;
+                
+                // Set the ARP options
+                *targmac = sniffer.mac;
+                *srcmac  = sniffer.mac;
+                *targip  = my_ip;
+                *srcip   = my_ip; 
+                sniffer.arp_packet->operation = 2;
+                
+                fix_endian_arp(sniffer.arp_packet);
+                sniffer.inject(BROADCAST_MAC, ETHERTYPE_ARP, sniffer.arp_packet, sizeof(ARP_Packet));
+                
+                while (!commands.empty())
+                {
+                    string command = commands.front();
+                    main_log.printf("NetTool> %s", command.c_str());
+                    if (command == "ping")
+                        ping_timer.attach(&ping, 30);
+                    else if (command == "portscan")
+                        scanner.start(sniffer.mac, your_mac, my_ip, your_ip);
+                    else if (command == "identify")
+                        identify();
+                    commands.pop();
+                }
+            }
+        }
+        else if (sniffer.tcp_packet)
+        {
+        }
+    }
+    
+    // This shouldn't really happen...
+    main_log.printf("Terminating...");
+    return 0;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,9 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+#include "util/log.h"
+
+extern Log main_log;
+extern bool main_running;
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/e2ac27c8e93e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net/arp.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,46 @@
+#ifndef ARP_H
+#define ARP_H
+
+#include "net.h"
+
+#define ETHERTYPE_ARP 0x0806
+typedef struct {
+  u16 hardware_type; // 0x0001 for ethernet
+  u16 protocol_type; // 0x0800 for IPv4
+  u8  hardware_length; // Bytes.  Ethernet is 6
+  u8  protocol_length; // Bytes.  IPv4 is 4
+  u16 operation; // Operation.  1 for request, 2 for reply or announce
+  // The following are only valid for IPv4 over Ethernet
+  u8  sender_hardware_address[6]; // Generator of the request or reply
+  u8  sender_protocol_address[4]; // All zeroes for an ARP probe
+  u8  target_hardware_address[6]; // Announce - same as SHA
+  u8  target_protocol_address[4]; // Announce - Same as TPA
+} ARP_Packet;
+
+inline void fix_endian_arp(ARP_Packet *packet)
+{
+  fix_endian_u16(&packet->hardware_type);
+  fix_endian_u16(&packet->protocol_type);
+  fix_endian_u16(&packet->operation);
+}
+
+inline void print_arp(ARP_Packet *packet)
+{
+    main_log.printf("ARP Packet:");
+    main_log.printf("  Hardware:  0x%04X", packet->hardware_type);
+    main_log.printf("  Protocol:  0x%04X", packet->protocol_type);
+    main_log.printf("  Type:      %d", packet->operation);
+    if (packet->hardware_type != 0x0001 || packet->protocol_type != 0x0800) return;
+    
+    u8 *bytes;
+    bytes = packet->sender_hardware_address;
+    main_log.printf("  Source:    MAC - %02X:%02X:%02X:%02X:%02X:%02X", bytes[0],bytes[1],bytes[2],bytes[3],bytes[4],bytes[5]);
+    bytes = packet->target_hardware_address;
+    main_log.printf("  Target:    MAC - %02X:%02X:%02X:%02X:%02X:%02X", bytes[0],bytes[1],bytes[2],bytes[3],bytes[4],bytes[5]);
+    bytes = packet->sender_protocol_address;
+    main_log.printf("  Source:    IP  - %03d.%03d.%03d.%03d", bytes[0], bytes[1], bytes[2], bytes[3]);
+    bytes = packet->target_protocol_address;
+    main_log.printf("  Target:    IP  - %03d.%03d.%03d.%03d", bytes[0], bytes[1], bytes[2], bytes[3]);
+}
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net/ethernet.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,37 @@
+#ifndef ETHERNET_H
+#define ETHERNET_H
+
+#include "net.h"
+
+typedef struct {
+  unsigned char octet[6];
+} Ethernet_MAC;
+
+typedef struct {
+  // In the Ethernet II Framing Standard:
+  // Destination MAC address (6 octets)
+  Ethernet_MAC destination;
+  // Source MAC address (6 octets)
+  Ethernet_MAC source;
+  // (optional) VLAN Tag (unsupported)
+  // Ethernet type or length (only <0x600 or 0x0800 IPv4 supported)
+  u16 ethertype;
+  unsigned char payload[];
+} Ethernet_FrameHeader;
+
+inline void fix_endian_ethernet(Ethernet_FrameHeader *header)
+{
+  fix_endian_u16(&header->ethertype);
+}
+
+inline void print_ethernet(Ethernet_FrameHeader *frame)
+{
+  main_log.printf("Ethernet frame:");
+  u8 *src = frame->source.octet;
+  u8 *dst = frame->destination.octet;
+  main_log.printf("  Source:    MAC - %02X:%02X:%02X:%02X:%02X:%02X", src[6], src[7], src[8], src[9], src[10], src[11]);
+  main_log.printf("  Dest:      MAC - %02X:%02X:%02X:%02X:%02X:%02X", dst[0], dst[1], dst[2], dst[3], dst[4], dst[5]);
+  main_log.printf("  Ethertype: 0x%04X", frame->ethertype);
+}
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net/icmp.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,35 @@
+#ifndef ICMP_H
+#define ICMP_H
+
+#include "net.h"
+
+#define ICMP_ECHO_REPLY   0x00
+#define ICMP_ECHO_REQUEST 0x08
+#define IPPROTO_ICMP      0x01
+typedef struct {
+    u8 type;        // type of ICMP message
+    u8 code;        // code number associated with certain message types
+    u16 checksum; 
+    u16 id;         // ID value, returned in ECHO REPLY
+    u16 sequence;   // Sequence value to be returned with ECHO REPLY
+    u8 data[];      
+} ICMP_Packet;
+
+inline void fix_endian_icmp(ICMP_Packet *segment)
+{
+  fix_endian_u16(&segment->checksum);
+  fix_endian_u16(&segment->id);
+  fix_endian_u16(&segment->sequence);
+}
+
+inline void print_icmp(ICMP_Packet *segment)
+{
+  main_log.printf("ICMP Packet:");
+  main_log.printf("  Type:      0x%02X", segment->type);
+  main_log.printf("  Code:      0x%02X", segment->code);
+  main_log.printf("  Checksum:  0x%04X", segment->checksum);
+  main_log.printf("  ID:        0x%04X", segment->id);
+  main_log.printf("  Sequence:  0x%04X", segment->sequence);
+}
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net/ip.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,105 @@
+#ifndef IP_H
+#define IP_H
+
+#include "net.h"
+
+#define ETHERTYPE_IPV4 0x0800
+#define ETHERTYPE_IPV6 0x86DD
+
+/************** IPv4 ***************/
+
+typedef struct {
+  unsigned char octet[4];
+} IP_Address;
+
+// 0x0800
+typedef struct {
+  // IP packets are composed of a header and payload. The IPv4 packet header consists of:
+  // 4 bits that contain the version, that specifies if it's an IPv4 or IPv6 packet,
+  unsigned version:4; // Only 0x4 supported
+  // 4 bits that contain the Internet Header Length which is the length of the header in multiples of 4 bytes (eg. 5 means 20 bytes).
+  unsigned header_bytes_div4:4;
+  // 8 bits that contain the Type of Service, also referred to as Quality of Service (QoS), which describes what priority the packet should have,
+  unsigned tos:8;
+  // 16 bits that contain the total length of the IP packet (datagram) in bytes,
+  u16 packet_bytes;
+  // 16 bits that contain an identification tag to help reconstruct the packet from several fragments,
+  u16 fragment_id;
+  // 3 bits that contain a zero, a flag that says whether the packet is allowed to be fragmented or not (DF: Don't fragment), and a flag to state whether more fragments of a packet follow (MF: More Fragments)
+  unsigned unused_0:1;
+  unsigned dont_fragment:1;
+  unsigned more_follow:1;
+  // 13 bits that contain the fragment offset, a field to identify position of fragment within original packet
+  unsigned fragment_offset:13; ////// This and the ones above may not work properly due to endianness
+  // 8 bits that contain the Time to live (TTL) which is the number of hops (router, computer or device along a network) the packet is allowed to pass before it dies (for example, a packet with a TTL of 16 will be allowed to go across 16 routers to get to its destination before it is discarded),
+  unsigned ttl:8;
+  // 8 bits that contain the protocol (TCP, UDP, ICMP, etc...)
+  //   0x01 ICMP
+  //   0x06 TCP
+  //   0x11 UDP
+  unsigned protocol:8;
+  // 16 bits that contain the Header Checksum, a number used in error detection,
+  u16 header_checksum;
+  // 32 bits that contain the source IP address,
+  IP_Address source;
+  // 32 bits that contain the destination address.
+  IP_Address destination;
+  // Other flags are unsupported
+  unsigned char data[];
+} IP_PacketHeader;
+
+inline void fix_endian_ip(IP_PacketHeader *packet)
+{
+  packet->version ^= packet->header_bytes_div4;
+  packet->header_bytes_div4 ^= packet->version;
+  packet->version ^= packet->header_bytes_div4;
+  fix_endian_u16(&packet->packet_bytes);
+  fix_endian_u16(&packet->fragment_id);
+  // Don't fix checksums; they are done bitwise
+}
+
+inline const char *ipproto2name(u8 proto)
+{
+  switch (proto)
+  {
+    case 0x01: return "ICMP";
+    case 0x02: return "IGMP";
+    case 0x06: return "TCP";
+    case 0x11: return "UDP";
+  }
+  return "<UNKNOWN>";
+}
+
+inline void print_ip(IP_PacketHeader *packet)
+{
+  main_log.printf("IPv%d Packet:", packet->version);
+  if (packet->version != 4)
+  {
+    main_log.printf("Not an IPv4 packet (skipping)");
+    return;
+  }
+    
+  u8 *dst = packet->destination.octet;
+  u8 *src = packet->source.octet;
+  main_log.printf("  Source:    IP - %03d.%03d.%03d.%03d", src[0], src[1], src[2], src[3]);
+  main_log.printf("  Dest:      IP - %03d.%03d.%03d.%03d", dst[0], dst[1], dst[2], dst[3]);
+  main_log.printf("  TTL:       %d", packet->ttl);
+  main_log.printf("  Protocol:  0x%02X (%s)", packet->protocol, ipproto2name(packet->protocol));
+  main_log.printf("  ToS:       0x%02X", packet->tos);
+  main_log.printf("  Header:    %d bytes", packet->header_bytes_div4*4);
+  main_log.printf("  Packet:    %d bytes", packet->packet_bytes);
+  main_log.printf("  Fragment:  0x%04X DF=%d MF=%d OFFSET=0x04X", packet->fragment_id, packet->dont_fragment, packet->more_follow, packet->fragment_offset);
+  main_log.printf("  Checksum:  0x%04X", packet->header_checksum);
+}
+
+inline void str2ipaddr(const char *ip, IP_Address *addr)
+{
+  static short a,b,c,d;
+  sscanf(ip, "%3hd.%3hd.%3hd.%3hd", &a, &b, &c, &d);
+  addr->octet[0] = a;
+  addr->octet[1] = b;
+  addr->octet[2] = c;
+  addr->octet[3] = d;
+}
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net/net.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,93 @@
+#ifndef NETWORK_H
+#define NETWORK_H
+
+#include "mbed.h"
+#include "main.h"
+
+#include "ctype.h"
+
+// General networking checksum
+inline u16 checksum(void *_mem, unsigned int bytes, void *skip_byte = NULL, unsigned int skip_count = 0, u16 last = 0)
+{
+  u32 sum = 0;
+  u16 *mem = (u16*)_mem;
+  unsigned int skip_start = (u16*)skip_byte - mem;
+  
+  if (last)
+    sum = last ^ 0xFFFF;
+  
+  //main_log.printf("CK:            0x%8X", sum);
+  for (register unsigned int i = 0; i < bytes/sizeof(u16); ++i)
+  {
+    // Skip bytes we don't use (e.g. the checksum itself)
+    if (i == skip_start)
+      i += skip_count/sizeof(u16);
+    
+    // Sum them up
+    sum += mem[i];
+    
+    //main_log.printf("CK: + 0x%04X = 0x%8X", mem[i], sum);
+  }
+    
+  // One's complement of the one's complement sum
+  sum += sum >> 16;
+  sum &= 0xFFFF;
+  sum ^= 0xFFFF;
+  
+  //main_log.printf("CK:          ~ 0x%8X", sum);
+  
+  return (u16)(sum);
+}
+
+// Generic u16 endian swapping
+inline void fix_endian_u16(u16 *p)
+{
+  char *bytes = (char*)p;
+  bytes[0] ^= bytes[1];
+  bytes[1] ^= bytes[0];
+  bytes[0] ^= bytes[1];
+}
+
+// Generic u32 endian swapping
+inline void fix_endian_u32(u32 *p)
+{
+  char *bytes = (char*)p;
+  // Swap outer bytes
+  bytes[0] ^= bytes[3];
+  bytes[3] ^= bytes[0];
+  bytes[0] ^= bytes[3];
+  // Swap inner bytes
+  bytes[1] ^= bytes[2];
+  bytes[2] ^= bytes[1];
+  bytes[1] ^= bytes[2];
+}
+
+/* Printing character */
+#define PCHAR(x) (isprint(x)?(x):'.')
+
+/* Hex dump a word-aligned number of bytes (may misbehave if length is not a multiple of 32 bits */
+inline void hex_dump(void *base, unsigned int length)
+{
+  char line[/*indent*/ 2 + /*0x*/ 2 + /*addr*/ 8 + /* : */ 3 + /*4xXXXX */ 4*5 + /*: */ 2 + /*8x.*/ 8 + /*\0*/ 1];
+  for (char *p = (char*)base; p - (char*)base < length; p += 8)
+  {
+    sprintf(line, "  0x%08X : %02X%02X %02X%02X %02X%02X %02X%02X : %c%c%c%c%c%c%c%c", p,
+                  p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+                  PCHAR(p[0]), PCHAR(p[1]), PCHAR(p[2]), PCHAR(p[3]), PCHAR(p[4]), PCHAR(p[5]), PCHAR(p[6]), PCHAR(p[7]));
+    main_log.printf(line);
+  }
+}
+
+// Ethernet
+#include "ethernet.h"
+
+// ARP and IP
+#include "arp.h"
+#include "ip.h"
+
+// TCP, UDP, and ICMP
+#include "tcp.h"
+#include "udp.h"
+#include "icmp.h"
+
+#endif // NETWORK_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net/tcp.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,76 @@
+#ifndef TCP_H
+#define TCP_H
+
+#include "net.h"
+
+#define IPPROTO_TCP 0x06
+typedef struct {
+  u16 source_port;
+  u16 destination_port;
+  u32 sequence_number;
+  u32 acknowledge_number;
+  
+  unsigned data_offset_bytes_div4:4;
+  unsigned unused_0:4;
+  
+  unsigned fin:1; // connection FINished (no more data from sender)
+  unsigned syn:1; // SYNchronize sequence numbers
+  unsigned rst:1; // ReSeT the connection
+  unsigned psh:1; // PuSH to receiving application
+  unsigned ack:1; // ACKnowledge fiend is significant
+  unsigned urg:1; // URGent field is significant
+  unsigned ece:1; // ECn Echo
+  unsigned cwr:1; // Congestion Window Reduced
+  
+  u16 window_size;
+  u16 checksum;
+  u16 urgent_pointer;
+  // Options (unsupported)
+  unsigned char data[];
+} TCP_SegmentHeader;
+
+inline void fix_endian_tcp(TCP_SegmentHeader *segment)
+{
+  segment->unused_0 ^= segment->data_offset_bytes_div4;
+  segment->data_offset_bytes_div4 ^= segment->unused_0;
+  segment->unused_0 ^= segment->data_offset_bytes_div4;
+  fix_endian_u16(&segment->source_port);
+  fix_endian_u16(&segment->destination_port);
+  fix_endian_u16(&segment->window_size);
+  // No fixing checksums
+  fix_endian_u16(&segment->urgent_pointer);
+  fix_endian_u32(&segment->sequence_number);
+  fix_endian_u32(&segment->acknowledge_number);
+}
+
+inline void print_tcp(TCP_SegmentHeader *segment)
+{
+  main_log.printf("TCP Segment:");
+  main_log.printf("  Source:    PORT %d", segment->source_port);
+  main_log.printf("  Dest:      PORT %d", segment->destination_port);
+  main_log.printf("  TCP Seqno: 0x%08X", segment->sequence_number);
+  main_log.printf("  TCP Ackno: 0x%08X", segment->acknowledge_number);
+  main_log.printf("  Header:    %d bytes", segment->data_offset_bytes_div4*4);
+  if (segment->cwr) main_log.printf("  Flag:      CWR");
+  if (segment->ece) main_log.printf("  Flag:      ECE");
+  if (segment->urg) main_log.printf("  Flag:      URG");
+  if (segment->ack) main_log.printf("  Flag:      ACK");
+  if (segment->psh) main_log.printf("  Flag:      PSH");
+  if (segment->rst) main_log.printf("  Flag:      RST");
+  if (segment->syn) main_log.printf("  Flag:      SYN");
+  if (segment->fin) main_log.printf("  Flag:      FIN");
+}
+
+inline u16 pseudo_header_checksum(IP_Address source, IP_Address destination, u16 length)
+{
+  struct {
+    IP_Address src, dst;
+    u8 zeros;
+    u8 proto;
+    u16 length;
+  } pseudoheader = {source, destination, 0, IPPROTO_TCP, length};
+  fix_endian_u16(&pseudoheader.length);
+  return checksum(&pseudoheader, sizeof(pseudoheader));
+}
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net/udp.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,30 @@
+#ifndef UDP_H
+#define UDP_H
+
+#include "net.h"
+
+#define IPPROTO_UDP 0x11
+typedef struct {
+  u16 source_port;
+  u16 destination_port;
+  u16 length; // entire datagram in bytes
+  u16 checksum;
+  u8 data[];
+} UDP_Packet;
+
+inline void fix_endian_udp(UDP_Packet *segment)
+{
+  fix_endian_u16(&segment->source_port);
+  fix_endian_u16(&segment->destination_port);
+  fix_endian_u16(&segment->length);
+}
+
+inline void print_udp(UDP_Packet *segment)
+{
+  main_log.printf("UDP Packet:");
+  main_log.printf("  Source:    PORT %d", segment->source_port);
+  main_log.printf("  Dest:      PORT %d", segment->destination_port);
+  main_log.printf("  Length:    %d", segment->length);
+}
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scanner.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,168 @@
+#ifndef SCANNER_H
+#define SCANNER_H
+
+#include "main.h"
+#include "net/net.h"
+#include "util/log.h"
+#include "sniffer.h"
+
+#include <cstring>
+#include <set>
+
+#define SCANNER_PADSIZE   0
+#define SCANNER_FRAMESIZE (sizeof(Ethernet_FrameHeader) + sizeof(IP_PacketHeader) + sizeof(TCP_SegmentHeader) + SCANNER_PADSIZE)
+
+class Scanner
+{
+private:
+  Sniffer *sniffer;
+  Ticker scan_timer;
+  Timeout scan_timeout;
+  
+  u8 raw_frame[SCANNER_FRAMESIZE];
+  
+  IP_Address destip;
+  u16 port_idx;
+  u16 pseudo_checksum;
+  
+  set<u16> open_ports;
+  
+  LocalFileSystem local;
+
+public:
+  inline Scanner(Sniffer *_sniffer)
+  : sniffer(_sniffer), local("local")
+  {
+    sniffer->attach_tcp(this, &Scanner::handle_tcp);
+  }
+  
+  inline void handle_tcp(TCP_SegmentHeader *packet, u32 data_bytes)
+  {
+    if (packet->syn && packet->ack)
+    {
+      open_ports.insert(packet->source_port);
+    }
+  }
+  
+  inline void finish()
+  {
+    FILE *fp = fopen("/local/PortScan.txt", "w");
+    fprintf(fp, "Open Ports on %d.%d.%d.%d:\n", destip.octet[0], destip.octet[1], destip.octet[2], destip.octet[3]);
+    for (set<u16>::iterator iter = open_ports.begin(); iter != open_ports.end(); ++iter)
+    {
+      fprintf(fp, "  TCP:%-5d OPEN\n", *iter);
+    }
+    fclose(fp);  
+    
+    main_log.printf("Open ports:");
+    for (set<u16>::iterator iter = open_ports.begin(); iter != open_ports.end(); ++iter)
+    {
+      main_log.printf("  TCP:%-5d OPEN", *iter);
+    }
+    main_log.printf("Port scan complete.");
+  }
+  
+  inline void start(Ethernet_MAC src, Ethernet_MAC dst, IP_Address srcip, IP_Address dstip)
+  {
+    // Create the ethernet frame, IP packet, and TCP segment memory mapping
+    static Ethernet_FrameHeader *frame = (Ethernet_FrameHeader*)raw_frame;
+    static IP_PacketHeader *packet = (IP_PacketHeader*)frame->payload;
+    static TCP_SegmentHeader *segment = (TCP_SegmentHeader*)packet->data;
+    
+    destip = dstip;
+    main_log.printf("Starting TCP port scan of %d.%d.%d.%d...", dstip.octet[0], dstip.octet[1], dstip.octet[2], dstip.octet[3]);
+    
+    // Zero the frame
+    memset(raw_frame, '\0', SCANNER_FRAMESIZE);
+    
+    //sniffer->inject(dst, frame->ethertype, packet, 0); //sizeof(raw_frame)-sizeof(Ethernet_FrameHeader));
+
+    // Fill in the frame (constant for all TCP connections)
+    frame->destination = dst;
+    frame->source = src;
+    frame->ethertype = ETHERTYPE_IPV4;
+    
+    // Fill in the IP packet
+    packet->source = srcip; // Can't change with destination back-to-back? lol
+    packet->version = 0x04;
+    packet->header_bytes_div4 = 5; // *4 = 20
+    packet->packet_bytes = SCANNER_FRAMESIZE-sizeof(Ethernet_FrameHeader);
+    packet->ttl = 64;
+    packet->protocol = IPPROTO_TCP;
+    packet->destination = dstip;
+    
+    // Fill in the TCP segment
+    segment->sequence_number = 0xBADBEEF0;
+    segment->data_offset_bytes_div4 = sizeof(TCP_SegmentHeader)/4;
+    segment->syn = 1;
+    segment->window_size = 8192;
+    pseudo_checksum = pseudo_header_checksum(srcip, dstip, sizeof(TCP_SegmentHeader));
+    
+    // Initialize the scanning
+    port_idx = 0;
+    
+    open_ports.clear();
+    scan_timer.attach_us(this, &Scanner::scan, 50);
+    //scan();
+  }
+  
+  inline void scan()
+  {
+    static u16 ports[] = {1, 2, 3, 5, 7, 9, 11, 13, 17, 18, 19, 20, 21, 22, 23, 24, 25, 35, 37, 39,
+      41, 42, 43, 47, 49, 50, 51, 52, 53, 54, 56, 58, 70, 79, 80, 83, 88, 90, 101, 102,
+      104, 105, 107, 108, 109, 110, 111, 113, 113, 115, 117, 118, 119, 135, 137, 138, 139, 143, 152, 153,
+      156, 162, 170, 177, 179, 194, 199, 201, 209, 210, 213, 218, 220, 259, 264, 308, 311, 318, 350, 351,
+      366, 369, 371, 383, 384, 387, 389, 401, 427, 443, 444, 445, 464, 475, 497, 504, 512, 513, 514, 515,
+      520, 524, 530, 532, 540, 542, 543, 544, 546, 547, 548, 554, 556, 563, 587, 591, 593, 604, 631, 635,
+      636, 639, 641, 646, 647, 648, 653, 654, 657, 660, 674, 691, 692, 694, 695, 699, 700, 701, 702, 706,
+      711, 712, 749, 750, 751, 752, 753, 754, 760, 860, 873, 902, 989, 990, 991, 992, 993, 995, 1058,
+      1080, 1085, 1098, 1099, 1140, 1169, 1176, 1182, 1194, 1198, 1200, 1214, 1220, 1223, 1241, 1270, 1293, 1337, 1352, 1387,
+      1414, 1417, 1418, 1419, 1420, 1431, 1433, 1470, 1494, 1512, 1513, 1521, 1524, 1533, 1547, 1677, 1720, 1723, 1755, 1761,
+      1762, 1763, 1764, 1765, 1766, 1767, 1768, 1801, 1812, 1813, 1863, 1935, 1947, 1970, 1971, 1972, 1984, 1994, 1998, 2000,
+      2031, 2053, 2073, 2074, 2082, 2083, 2086, 2102, 2103, 2104, 2105, 2144, 2145, 2161, 2181, 2210, 2211, 2212, 2219, 2220,
+      2261, 2262, 2369, 2370, 2404, 2447, 2483, 2484, 2500, 2612, 2713, 2714, 2735, 2809, 2868, 2947, 2948, 2949, 3050, 3051,
+      3074, 3225, 3233, 3235, 3260, 3268, 3269, 3283, 3305, 3306, 3386, 3389, 3396, 3412, 3455, 3423, 3424, 3478, 3483, 3516,
+      3532, 3533, 3606, 3632, 3689, 3690, 3702, 3880, 3868, 3900, 3945, 3999, 4018, 4089, 4093, 4096, 4111, 4116, 4321, 4662,
+      4728, 4840, 4843, 4847, 4993, 4894, 4899, 4950, 5000, 5001, 5003, 5004, 5005, 5051, 5060, 5061, 5070, 5084, 5085, 5099,
+      5151, 5154, 5190, 5222, 5269, 5298, 5351, 5355, 5402, 5405, 5421, 5432, 5556, 5631, 5814, 5900, 5984, 5999, 6000, 6005,
+      6086, 6110, 6111, 6112, 6129, 6346, 6347, 6350, 6432, 6444, 6445, 6619, 6665, 6666, 6667, 6668, 6669, 6888, 6969, 7005,
+      7006, 7400, 7401, 7402, 7547, 7787, 7788, 8000, 8008, 8078, 8080, 8118, 8123, 8243, 8280, 8400, 8442, 8880, 8888, 9009,
+      9080, 9100, 9105, 9119, 9306, 9312, 9418, 9535, 9536, 9800, 9898, 9996, 10008, 10010, 10050, 10051, 10113, 10114, 10115,
+      10116, 13076, 13720, 13721, 13724, 13782, 13783, 13785, 13786, 15000, 15345, 17500, 18104, 19283, 19315, 22347, 22350,
+      24465, 24554, 26000, 31457, 33434, 40000, 43047, 43048, 47808};
+    
+    // Create the ethernet frame, IP packet, and TCP segment memory mapping
+    static Ethernet_FrameHeader *frame = (Ethernet_FrameHeader*)raw_frame;
+    static IP_PacketHeader *packet = (IP_PacketHeader*)frame->payload;
+    static TCP_SegmentHeader *segment = (TCP_SegmentHeader*)packet->data;
+    
+    segment->source_port = port_idx; //ports[port_idx];
+    segment->destination_port = port_idx; //ports[port_idx];
+    
+    fix_endian_tcp(segment);
+    segment->checksum = checksum(segment, sizeof(TCP_SegmentHeader), &segment->checksum, sizeof(segment->checksum), pseudo_checksum);
+    
+    fix_endian_ip(packet);
+    packet->header_checksum = checksum(packet, sizeof(IP_PacketHeader), &packet->header_checksum, sizeof(packet->header_checksum));
+    
+    fix_endian_ethernet(frame);
+    sniffer->inject(frame, SCANNER_FRAMESIZE);
+    
+    fix_endian_ethernet(frame);
+    fix_endian_ip(packet);
+    fix_endian_tcp(segment);
+    
+    // Update sequence number
+    segment->sequence_number++;
+    
+    //if (port_idx >= sizeof(ports)/sizeof(u16)) scan_timer.detach();
+    if (port_idx >= 65535)
+    {
+      scan_timer.detach();
+      scan_timeout.attach(this, &Scanner::finish, 7);
+    }
+    port_idx++;
+  }
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sniffer.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,253 @@
+#ifndef SNIFFER_H
+#define SNIFFER_H
+
+#include "mbed.h"
+
+#include "main.h"
+#include "util/types.h"
+#include "net/net.h"
+
+#include <cstdio>
+#include <cstring>
+#include <functional>
+
+template <class Arg1, class Arg2, class Result>
+class handler
+{
+public:
+  virtual inline Result operator() (Arg1 x, Arg2 y) const {};
+};
+
+template <class Arg1, class Arg2, class Result>
+class function_handler
+: public handler <Arg1,Arg2,Result>
+{
+protected:
+  Result (*pfunc)(Arg1,Arg2);
+public:
+  explicit inline function_handler ( Result (*f)(Arg1,Arg2) ) : pfunc (f) {}
+  virtual inline Result operator() (Arg1 x, Arg2 y) const { return pfunc(x,y); }
+};
+
+template <class Type, class Arg1, class Arg2, class Result>
+class member_handler
+: public handler <Arg1,Arg2,Result>
+{
+protected:
+  Type *inst;
+  Result (Type::*pfunc)(Arg1,Arg2);
+public:
+  explicit inline member_handler ( Type *i, Result (Type::*f)(Arg1,Arg2) ) : inst(i), pfunc (f) {}
+  virtual inline Result operator() (Arg1 x, Arg2 y) const { return (inst->*pfunc)(x,y); }
+};
+
+
+class Sniffer {
+public:
+  Ethernet_MAC mac;
+  
+private:
+  // Ethernet interface
+  Ethernet     eth;
+  IP_Address   addr;
+  
+  // Status LEDs
+  DigitalOut linked;
+  DigitalOut received;
+
+  // Frame data (big enough for largest ethernet frame)
+  int  frame_size;
+  char frame[0x600];
+  
+  // Outgoing frames
+  char outframe[0x600];
+  Ethernet_FrameHeader *outframe_header;
+
+public:  
+  // Ethernet Frame Header
+  Ethernet_FrameHeader *frame_header;
+  
+  // IP Packet Header
+  IP_PacketHeader *ip_packet;
+  
+  // ARP Packet
+  ARP_Packet *arp_packet;
+  
+  // TCP Packet
+  TCP_SegmentHeader *tcp_packet;
+  
+  // UDP Packet
+  UDP_Packet *udp_packet;
+  
+  // ICMP Packet
+  ICMP_Packet *icmp_packet;
+  
+  // Generic
+  unsigned int data_bytes;
+
+public:
+  // Constructor
+  inline Sniffer()
+  : linked(LED1), received(LED2)
+  {
+    eth.set_link(Ethernet::AutoNegotiate);
+    eth.address((char *)mac.octet);
+  }
+  
+  inline bool inject(void *data, unsigned int bytes)
+  {
+    // Send the packet
+    eth.write((char*)data, bytes);
+    int send_status = eth.send();
+
+    //decode_ethernet(data);
+    
+    return send_status;
+  }
+  
+  inline bool inject(Ethernet_MAC dest, u16 ethertype, void *packet, unsigned int bytes)
+  {
+    memset(outframe, 0x00, bytes);
+    
+    outframe_header = (Ethernet_FrameHeader*)outframe;
+    
+    // Set the ethernet frame source
+    memcpy(&outframe_header->source, mac.octet, 6);
+    
+    // Set the ethernet frame destination
+    outframe_header->destination = dest;
+    
+    // Set the ethernet ethertype
+    outframe_header->ethertype = ethertype;
+    
+    // Make sure the payload won't be too large
+    if (sizeof(Ethernet_FrameHeader) + bytes > sizeof(outframe))
+    {
+      main_log.printf("ERROR: Attempt to inject packet failed; Payload size of %d is too large", bytes);
+      return false;
+    }
+    
+    // Set the payload
+    memcpy(outframe_header->payload, packet, bytes);
+    fix_endian_ethernet(outframe_header);
+    
+    // Send the packet
+    eth.write(outframe, sizeof(Ethernet_FrameHeader) + bytes);
+    int send_status = eth.send();
+
+    //decode_ethernet(outframe);
+
+    return send_status;
+  }
+  
+  inline void wait_for_data()
+  { 
+    while (true)
+    {
+      wait(0.0001);
+      
+      if (!(linked = eth.link()))
+        continue;
+      
+      received = (frame_size = eth.receive());
+      if (!frame_size)
+        continue;
+      
+      eth.read(frame, frame_size);
+      break;
+    }
+  }
+
+  // Wait for an ethernet frame
+  inline void next()
+  {
+    wait_for_data();
+    
+    // Zero out all of the packet pointers
+    frame_header = NULL;
+    arp_packet = NULL;
+    icmp_packet = NULL;
+    tcp_packet = NULL;
+    udp_packet = NULL;
+    data_bytes = 0;
+    
+    decode_ethernet(frame);
+  }
+  
+  inline void decode_ethernet(void *frame)
+  {
+    Ethernet_FrameHeader *header = frame_header = (Ethernet_FrameHeader*)frame;
+    fix_endian_ethernet(header);
+    
+    switch (header->ethertype)
+    {
+      case ETHERTYPE_IPV4:
+      case ETHERTYPE_IPV6:
+        decode_ip((IP_PacketHeader*)header->payload);
+        break;
+      case ETHERTYPE_ARP:
+        decode_arp((ARP_Packet*)header->payload);
+        break;
+      default:
+        break; // Unknown ethertype
+    }
+  }
+  
+  inline void decode_arp(ARP_Packet *packet)
+  {
+    fix_endian_arp(packet);
+    if (packet->hardware_type != 0x0001 || packet->protocol_type != 0x0800) return;
+    arp_packet = packet;
+  }
+  
+  inline void decode_ip(IP_PacketHeader *packet)
+  {
+    u16 chk = checksum(packet, sizeof(IP_PacketHeader), &packet->header_checksum, 2);
+    fix_endian_ip(packet);
+    ip_packet = packet;
+  
+    if (packet->version != 4) return;
+    
+    data_bytes = packet->packet_bytes;
+    data_bytes -= sizeof(IP_PacketHeader);
+    
+    if (packet->protocol == IPPROTO_UDP)
+    {
+      UDP_Packet *segment = udp_packet = (UDP_Packet*)packet->data;
+      fix_endian_udp(segment);
+      data_bytes -= sizeof(UDP_Packet);
+    }
+    else if (packet->protocol == IPPROTO_ICMP)
+    {
+      ICMP_Packet *segment = icmp_packet = (ICMP_Packet *)packet->data;
+      fix_endian_icmp(segment);
+      data_bytes -= sizeof(ICMP_Packet);
+    }
+    else if (packet->protocol == IPPROTO_TCP)
+    {
+      TCP_SegmentHeader *segment = tcp_packet = (TCP_SegmentHeader*)packet->data;
+      fix_endian_tcp(segment);
+      data_bytes -= sizeof(TCP_SegmentHeader);
+      dispatch_tcp(segment,data_bytes);
+    }
+  }
+  
+  handler<TCP_SegmentHeader*,u32,void> *tcp_handler;
+  inline void dispatch_tcp(TCP_SegmentHeader *tcp_packet, u32 data_bytes)
+  {
+    if (tcp_handler) (*tcp_handler)(tcp_packet,data_bytes);
+  }
+  
+  template <class T>
+  inline void attach_tcp(T *inst, void (T::*func)(TCP_SegmentHeader *tcp_packet, u32 data_bytes))
+  {
+    tcp_handler = new member_handler<T,TCP_SegmentHeader*,u32,void>(inst, func);
+  }
+  
+  inline void attach_tcp(void (*func)(TCP_SegmentHeader *tcp_packet, u32 data_bytes))
+  {
+    tcp_handler = new function_handler<TCP_SegmentHeader*,u32,void>(func);
+  }
+};
+
+#endif // SNIFFER_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/log.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,114 @@
+#ifndef LOG_H
+#define LOG_H
+
+#include "mbed.h"
+
+#include "stdarg.h"
+#include "stdio.h"
+
+#define LOG_BUFSIZE 4096
+
+
+
+typedef enum {
+  LOG_USB = 0,
+  LOG_SER_9_10,
+  LOG_SER_13_14,
+  LOG_SER_28_27,
+  LOG_MAX
+} SerialLines;
+
+class Log {
+private:
+  // Accomodate all 3 serial ports and USB
+  Serial *m_serial[4];
+  bool    m_enable[4];
+  
+  // File log
+  LocalFileSystem local;
+
+public:
+  inline Log() : local("local")
+  {
+    // Write to file
+    FILE *logfile = fopen("/local/NetTool.log", "w");
+    fputs("NetTool - Welcome!", logfile);
+    fclose(logfile);
+  
+    // Set up the serial classes
+    m_serial[LOG_USB]       = new Serial(USBTX,USBRX);
+    m_serial[LOG_SER_9_10]  = new Serial(p9,p10);
+    m_serial[LOG_SER_13_14] = new Serial(p13,p14);
+    m_serial[LOG_SER_28_27] = new Serial(p28,p27);
+    
+    // Disable logging to all of them by default
+    for (int idx = 0; idx < LOG_MAX; ++idx)
+    {
+      m_enable[idx] = false;
+    }
+  }
+
+  // Enable logging to the given serial line
+  inline void enable(SerialLines idx)
+  {
+    m_enable[idx] = true;
+  }
+  
+  // Disable logging to the given serial line
+  inline void disable(SerialLines idx)
+  {
+    m_enable[idx] = false;
+  }
+  
+  // This can log messages up to 4095 characters and has printf semantics
+  // All log messages include an implicit \r\n at the end
+  inline bool printf(char *format, ...)
+  {
+    static char buffer[LOG_BUFSIZE];
+    static int count;
+    static va_list va;
+    
+    va_start(va, format);
+    count = vsnprintf(buffer, LOG_BUFSIZE, format, va);
+    
+    // Ensure that the log message fit
+    if (count > LOG_BUFSIZE-1)
+    {
+      // Log an error message if it didn't
+      Log::printf("Log message too long: %4d", count);
+    }
+    else
+    {
+      // Write all characters from the message
+      puts(buffer);
+      // Send the EOM (\r\n)
+      puts("\r\n");
+    }
+    va_end(va);
+    return true;
+  } // printf
+  
+  bool puts(const char *str)
+  {
+    // Write to file
+    FILE *logfile = fopen("/local/NetTool.log", "a");
+    fputs(str, logfile);
+    fclose(logfile);
+    
+    // Write all characters from the message
+    while (*str)
+    {
+      // Write to all serial devices
+      for (int s = 0; s < LOG_MAX; ++s)
+      {
+        // Only write if enabled
+        if (m_enable[s])
+          s[*m_serial].putc(*str++);
+      }
+    }
+    return true;
+  }
+
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/types.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,8 @@
+#ifndef TYPES_H
+#define TYPES_H
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/util.h	Tue Oct 12 05:32:59 2010 +0000
@@ -0,0 +1,25 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+#include "types.h"
+#include "log.h"
+
+inline bool is_nonzero_mem(u8 *start, unsigned int bytes)
+{
+  for (; bytes--; ++start) if (*start) return true;
+  return false;
+}
+
+inline bool is_zero_mem(u8 *start, unsigned int bytes)
+{
+  for (; bytes--; ++start) if (*start) return false;
+  return true;
+}
+
+inline bool is_equal_mem(u8 *a, u8 *b, unsigned int bytes)
+{
+  for (; bytes--; ++a, ++b) if (*a != *b) return false;
+  return true;
+}
+
+#endif
\ No newline at end of file