Pulled packet libraries into the ONOS namespace

Change-Id: Id16d1a8f4cae5de7b6aaa4536f2ba37a878d8d48
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
index a1da185..dc6f075 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -24,8 +24,6 @@
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.core.util.SingletonTask;
-import net.floodlightcontroller.packet.Ethernet;
-import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.ofcontroller.bgproute.RibUpdate.Operation;
@@ -48,6 +46,8 @@
 import net.onrc.onos.ofcontroller.util.IPv4Net;
 import net.onrc.onos.ofcontroller.util.Port;
 import net.onrc.onos.ofcontroller.util.SwitchPort;
+import net.onrc.onos.packet.Ethernet;
+import net.onrc.onos.packet.IPv4;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 import net.sf.json.JSONSerializer;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
index ed4dd7d..546ec64 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
@@ -1,5 +1,5 @@
 /**
-*    Copyright 2011,2012, Big Switch Networks, Inc. 
+l*    Copyright 2011,2012, Big Switch Networks, Inc. 
 *    Originally created by David Erickson, Stanford University
 * 
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -20,8 +20,8 @@
 import java.io.Serializable;
 import java.util.Date;
 
-import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.packet.IPv4;
 
 /**
  * An entity on the network is a visible trace of a device that corresponds
@@ -38,10 +38,7 @@
  *
  */
 public class OnosDevice implements Serializable { //implements Comparable<OnosDevice> {
-    /**
-     * Timeout for computing {@link Entity#activeSince}.
-     * @see {@link Entity#activeSince}
-     */
+
     private static int ACTIVITY_TIMEOUT = 30000;
     
     /**
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
index ef02577..bb2e387 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
@@ -22,13 +22,13 @@
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.packet.ARP;
-import net.floodlightcontroller.packet.DHCP;
-import net.floodlightcontroller.packet.Ethernet;
-import net.floodlightcontroller.packet.IPv4;
-import net.floodlightcontroller.packet.UDP;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.datagrid.IDatagridService;
+import net.onrc.onos.packet.ARP;
+import net.onrc.onos.packet.DHCP;
+import net.onrc.onos.packet.Ethernet;
+import net.onrc.onos.packet.IPv4;
+import net.onrc.onos.packet.UDP;
 
 import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFPacketIn;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/forwarding/Forwarding.java b/src/main/java/net/onrc/onos/ofcontroller/forwarding/Forwarding.java
index 8dfd3e3..0ee4466 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/forwarding/Forwarding.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/forwarding/Forwarding.java
@@ -17,7 +17,6 @@
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.datagrid.IDatagridService;
 import net.onrc.onos.ofcontroller.devicemanager.IOnosDeviceService;
@@ -35,6 +34,7 @@
 import net.onrc.onos.ofcontroller.util.FlowPathUserState;
 import net.onrc.onos.ofcontroller.util.Port;
 import net.onrc.onos.ofcontroller.util.SwitchPort;
+import net.onrc.onos.packet.Ethernet;
 import net.onrc.onos.registry.controller.IControllerRegistryService;
 
 import org.openflow.protocol.OFMessage;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index a035867..39444b9 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -52,11 +52,6 @@
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.core.util.SingletonTask;
-import net.floodlightcontroller.packet.BSN;
-import net.floodlightcontroller.packet.Ethernet;
-import net.floodlightcontroller.packet.IPv4;
-import net.floodlightcontroller.packet.LLDP;
-import net.floodlightcontroller.packet.LLDPTLV;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
 import net.floodlightcontroller.util.EventHistory;
@@ -71,6 +66,11 @@
 import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
 import net.onrc.onos.ofcontroller.linkdiscovery.NodePortTuple;
 import net.onrc.onos.ofcontroller.linkdiscovery.web.LinkDiscoveryWebRoutable;
+import net.onrc.onos.packet.BSN;
+import net.onrc.onos.packet.Ethernet;
+import net.onrc.onos.packet.IPv4;
+import net.onrc.onos.packet.LLDP;
+import net.onrc.onos.packet.LLDPTLV;
 import net.onrc.onos.registry.controller.IControllerRegistryService;
 
 import org.openflow.protocol.OFMessage;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
index 9e2ae4c..2294a60 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -19,9 +19,6 @@
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.packet.ARP;
-import net.floodlightcontroller.packet.Ethernet;
-import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.datagrid.IDatagridService;
@@ -29,6 +26,9 @@
 import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
 import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
 import net.onrc.onos.ofcontroller.util.SwitchPort;
+import net.onrc.onos.packet.ARP;
+import net.onrc.onos.packet.Ethernet;
+import net.onrc.onos.packet.IPv4;
 
 import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFPacketIn;
diff --git a/src/main/java/net/onrc/onos/packet/ARP.java b/src/main/java/net/onrc/onos/packet/ARP.java
new file mode 100644
index 0000000..2765f7d
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/ARP.java
@@ -0,0 +1,316 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class ARP extends BasePacket {
+    public static short HW_TYPE_ETHERNET = 0x1;
+
+    public static short PROTO_TYPE_IP = 0x800;
+
+    public static short OP_REQUEST = 0x1;
+    public static short OP_REPLY = 0x2;
+    public static short OP_RARP_REQUEST = 0x3;
+    public static short OP_RARP_REPLY = 0x4;
+
+    protected short hardwareType;
+    protected short protocolType;
+    protected byte hardwareAddressLength;
+    protected byte protocolAddressLength;
+    protected short opCode;
+    protected byte[] senderHardwareAddress;
+    protected byte[] senderProtocolAddress;
+    protected byte[] targetHardwareAddress;
+    protected byte[] targetProtocolAddress;
+
+    /**
+     * @return the hardwareType
+     */
+    public short getHardwareType() {
+        return hardwareType;
+    }
+
+    /**
+     * @param hardwareType the hardwareType to set
+     */
+    public ARP setHardwareType(short hardwareType) {
+        this.hardwareType = hardwareType;
+        return this;
+    }
+
+    /**
+     * @return the protocolType
+     */
+    public short getProtocolType() {
+        return protocolType;
+    }
+
+    /**
+     * @param protocolType the protocolType to set
+     */
+    public ARP setProtocolType(short protocolType) {
+        this.protocolType = protocolType;
+        return this;
+    }
+
+    /**
+     * @return the hardwareAddressLength
+     */
+    public byte getHardwareAddressLength() {
+        return hardwareAddressLength;
+    }
+
+    /**
+     * @param hardwareAddressLength the hardwareAddressLength to set
+     */
+    public ARP setHardwareAddressLength(byte hardwareAddressLength) {
+        this.hardwareAddressLength = hardwareAddressLength;
+        return this;
+    }
+
+    /**
+     * @return the protocolAddressLength
+     */
+    public byte getProtocolAddressLength() {
+        return protocolAddressLength;
+    }
+
+    /**
+     * @param protocolAddressLength the protocolAddressLength to set
+     */
+    public ARP setProtocolAddressLength(byte protocolAddressLength) {
+        this.protocolAddressLength = protocolAddressLength;
+        return this;
+    }
+
+    /**
+     * @return the opCode
+     */
+    public short getOpCode() {
+        return opCode;
+    }
+
+    /**
+     * @param opCode the opCode to set
+     */
+    public ARP setOpCode(short opCode) {
+        this.opCode = opCode;
+        return this;
+    }
+
+    /**
+     * @return the senderHardwareAddress
+     */
+    public byte[] getSenderHardwareAddress() {
+        return senderHardwareAddress;
+    }
+
+    /**
+     * @param senderHardwareAddress the senderHardwareAddress to set
+     */
+    public ARP setSenderHardwareAddress(byte[] senderHardwareAddress) {
+        this.senderHardwareAddress = senderHardwareAddress;
+        return this;
+    }
+
+    /**
+     * @return the senderProtocolAddress
+     */
+    public byte[] getSenderProtocolAddress() {
+        return senderProtocolAddress;
+    }
+
+    /**
+     * @param senderProtocolAddress the senderProtocolAddress to set
+     */
+    public ARP setSenderProtocolAddress(byte[] senderProtocolAddress) {
+        this.senderProtocolAddress = senderProtocolAddress;
+        return this;
+    }
+    
+    public ARP setSenderProtocolAddress(int address) {
+        this.senderProtocolAddress = ByteBuffer.allocate(4).putInt(address).array();
+        return this;
+    }
+
+    /**
+     * @return the targetHardwareAddress
+     */
+    public byte[] getTargetHardwareAddress() {
+        return targetHardwareAddress;
+    }
+
+    /**
+     * @param targetHardwareAddress the targetHardwareAddress to set
+     */
+    public ARP setTargetHardwareAddress(byte[] targetHardwareAddress) {
+        this.targetHardwareAddress = targetHardwareAddress;
+        return this;
+    }
+
+    /**
+     * @return the targetProtocolAddress
+     */
+    public byte[] getTargetProtocolAddress() {
+        return targetProtocolAddress;
+    }
+
+    /**
+     * @return True if gratuitous ARP (SPA = TPA), false otherwise
+     */
+    public boolean isGratuitous() {        
+        assert(senderProtocolAddress.length == targetProtocolAddress.length);
+        
+        int indx = 0;
+        while (indx < senderProtocolAddress.length) {
+            if (senderProtocolAddress[indx] != targetProtocolAddress[indx]) {
+                return false;
+            }
+            indx++;
+        }
+        
+        return true;
+    }
+    
+    /**
+     * @param targetProtocolAddress the targetProtocolAddress to set
+     */
+    public ARP setTargetProtocolAddress(byte[] targetProtocolAddress) {
+        this.targetProtocolAddress = targetProtocolAddress;
+        return this;
+    }
+    
+    public ARP setTargetProtocolAddress(int address) {
+        this.targetProtocolAddress = ByteBuffer.allocate(4).putInt(address).array();
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        int length = 8 + (2 * (0xff & this.hardwareAddressLength))
+                + (2 * (0xff & this.protocolAddressLength));
+        byte[] data = new byte[length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.putShort(this.hardwareType);
+        bb.putShort(this.protocolType);
+        bb.put(this.hardwareAddressLength);
+        bb.put(this.protocolAddressLength);
+        bb.putShort(this.opCode);
+        bb.put(this.senderHardwareAddress, 0, 0xff & this.hardwareAddressLength);
+        bb.put(this.senderProtocolAddress, 0, 0xff & this.protocolAddressLength);
+        bb.put(this.targetHardwareAddress, 0, 0xff & this.hardwareAddressLength);
+        bb.put(this.targetProtocolAddress, 0, 0xff & this.protocolAddressLength);
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        this.hardwareType = bb.getShort();
+        this.protocolType = bb.getShort();
+        this.hardwareAddressLength = bb.get();
+        this.protocolAddressLength = bb.get();
+        this.opCode = bb.getShort();
+        this.senderHardwareAddress = new byte[0xff & this.hardwareAddressLength];
+        bb.get(this.senderHardwareAddress, 0, this.senderHardwareAddress.length);
+        this.senderProtocolAddress = new byte[0xff & this.protocolAddressLength];
+        bb.get(this.senderProtocolAddress, 0, this.senderProtocolAddress.length);
+        this.targetHardwareAddress = new byte[0xff & this.hardwareAddressLength];
+        bb.get(this.targetHardwareAddress, 0, this.targetHardwareAddress.length);
+        this.targetProtocolAddress = new byte[0xff & this.protocolAddressLength];
+        bb.get(this.targetProtocolAddress, 0, this.targetProtocolAddress.length);
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 13121;
+        int result = super.hashCode();
+        result = prime * result + hardwareAddressLength;
+        result = prime * result + hardwareType;
+        result = prime * result + opCode;
+        result = prime * result + protocolAddressLength;
+        result = prime * result + protocolType;
+        result = prime * result + Arrays.hashCode(senderHardwareAddress);
+        result = prime * result + Arrays.hashCode(senderProtocolAddress);
+        result = prime * result + Arrays.hashCode(targetHardwareAddress);
+        result = prime * result + Arrays.hashCode(targetProtocolAddress);
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof ARP))
+            return false;
+        ARP other = (ARP) obj;
+        if (hardwareAddressLength != other.hardwareAddressLength)
+            return false;
+        if (hardwareType != other.hardwareType)
+            return false;
+        if (opCode != other.opCode)
+            return false;
+        if (protocolAddressLength != other.protocolAddressLength)
+            return false;
+        if (protocolType != other.protocolType)
+            return false;
+        if (!Arrays.equals(senderHardwareAddress, other.senderHardwareAddress))
+            return false;
+        if (!Arrays.equals(senderProtocolAddress, other.senderProtocolAddress))
+            return false;
+        if (!Arrays.equals(targetHardwareAddress, other.targetHardwareAddress))
+            return false;
+        if (!Arrays.equals(targetProtocolAddress, other.targetProtocolAddress))
+            return false;
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "ARP [hardwareType=" + hardwareType + ", protocolType="
+                + protocolType + ", hardwareAddressLength="
+                + hardwareAddressLength + ", protocolAddressLength="
+                + protocolAddressLength + ", opCode=" + opCode
+                + ", senderHardwareAddress="
+                + Arrays.toString(senderHardwareAddress)
+                + ", senderProtocolAddress="
+                + Arrays.toString(senderProtocolAddress)
+                + ", targetHardwareAddress="
+                + Arrays.toString(targetHardwareAddress)
+                + ", targetProtocolAddress="
+                + Arrays.toString(targetProtocolAddress) + "]";
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/BPDU.java b/src/main/java/net/onrc/onos/packet/BPDU.java
new file mode 100644
index 0000000..908411b
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/BPDU.java
@@ -0,0 +1,138 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This class is a Rapid Spanning Tree Protocol
+ * Bridge Protocol Data Unit
+ * @author alexreimers
+ */
+public class BPDU extends BasePacket {
+    public enum BPDUType {
+        CONFIG,
+        TOPOLOGY_CHANGE;
+    }
+    
+    private final long destMac = 0x0180c2000000L; // 01-80-c2-00-00-00
+    
+    // TODO - check this for RSTP
+    private LLC llcHeader;
+    private short protocolId = 0;
+    private byte version = 0;
+    private byte type;
+    private byte flags;
+    private byte[] rootBridgeId;
+    private int rootPathCost;
+    private byte[] senderBridgeId; // switch cluster MAC
+    private short portId; // port it was transmitted from
+    private short messageAge; // 256ths of a second
+    private short maxAge; // 256ths of a second
+    private short helloTime; // 256ths of a second
+    private short forwardDelay; // 256ths of a second
+    
+    public BPDU(BPDUType type) {
+        rootBridgeId = new byte[8];
+        senderBridgeId = new byte[8];
+        
+        llcHeader = new LLC();
+        llcHeader.setDsap((byte) 0x42);
+        llcHeader.setSsap((byte) 0x42);
+        llcHeader.setCtrl((byte) 0x03);
+        
+        switch(type) {
+            case CONFIG:
+                this.type = 0x0;
+                break;
+            case TOPOLOGY_CHANGE:
+                this.type = (byte) 0x80; // 1000 0000
+                break;
+            default:
+                this.type = 0;
+                break;
+        }
+    }
+    
+    @Override
+    public byte[] serialize() {
+        byte[] data;
+        // TODO check these
+        if (type == 0x0) { 
+            // config
+            data = new byte[38];
+        } else {
+            // topology change
+            data = new byte[7]; // LLC + TC notification
+        }
+        
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        // Serialize the LLC header
+        byte[] llc = llcHeader.serialize();
+        bb.put(llc, 0, llc.length);
+        bb.putShort(protocolId);
+        bb.put(version);
+        bb.put(type);
+        
+        if (type == 0x0) {
+            bb.put(flags);
+            bb.put(rootBridgeId, 0, rootBridgeId.length);
+            bb.putInt(rootPathCost);
+            bb.put(senderBridgeId, 0, senderBridgeId.length);
+            bb.putShort(portId);
+            bb.putShort(messageAge);
+            bb.putShort(maxAge);
+            bb.putShort(helloTime);
+            bb.putShort(forwardDelay);
+        }
+        
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        
+        // LLC header
+        llcHeader.deserialize(data, offset, 3);
+        
+        this.protocolId = bb.getShort();
+        this.version = bb.get();
+        this.type = bb.get();
+        
+        // These fields only exist if it's a configuration BPDU
+        if (this.type == 0x0) {
+            this.flags = bb.get();
+            bb.get(rootBridgeId, 0, 6);
+            this.rootPathCost = bb.getInt();
+            bb.get(this.senderBridgeId, 0, 6);
+            this.portId = bb.getShort();
+            this.messageAge = bb.getShort();
+            this.maxAge = bb.getShort();
+            this.helloTime = bb.getShort();
+            this.forwardDelay = bb.getShort();
+        }
+        // TODO should we set other fields to 0?
+        
+        return this;
+    }
+
+    public long getDestMac() {
+        return destMac;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/BSN.java b/src/main/java/net/onrc/onos/packet/BSN.java
new file mode 100644
index 0000000..a8c7dc0
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/BSN.java
@@ -0,0 +1,172 @@
+/**
+*    Copyright 2012, Big Switch Networks, Inc. 
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+/**
+ * 
+ */
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Shudong Zhou (shudong.zhou@bigswitch.com)
+ *
+ */
+public class BSN extends BasePacket {
+	public static final int BSN_MAGIC = 0x20000604;
+	public static final short BSN_VERSION_CURRENT = 0x0;
+	public static final short BSN_TYPE_PROBE = 0x1;
+	public static final short BSN_TYPE_BDDP  = 0x2;
+	public static Map<Short, Class<? extends IPacket>> typeClassMap;
+	
+    static {
+        typeClassMap = new HashMap<Short, Class<? extends IPacket>>();
+        typeClassMap.put(BSN_TYPE_PROBE, BSNPROBE.class);
+        typeClassMap.put(BSN_TYPE_BDDP, LLDP.class);
+    }
+
+	protected short type;
+	protected short version;
+
+	public BSN() {
+    	version = BSN_VERSION_CURRENT;
+	}
+	
+    public BSN(short type) {
+    	this.type = type;
+    	version = BSN_VERSION_CURRENT;
+    }
+
+    public short getType() {
+		return type;
+	}
+
+	public BSN setType(short type) {
+		this.type = type;
+		return this;
+	}
+	
+    public short getVersion() {
+		return version;
+	}
+
+	public BSN setVersion(short version) {
+		this.version = version;
+		return this;
+	}
+
+    @Override
+    public byte[] serialize() {
+    	short length = 4 /* magic */ + 2 /* type */ + 2 /* version */;
+    	
+    	byte[] payloadData = null;
+    	if (this.payload != null) {
+            payload.setParent(this);
+            payloadData = payload.serialize();
+            length += payloadData.length;
+        }
+    
+        byte[] data = new byte[length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.putInt(BSN_MAGIC);
+        bb.putShort(this.type);
+        bb.putShort(this.version);
+        if (payloadData != null)
+        	bb.put(payloadData);
+
+        if (this.parent != null && this.parent instanceof Ethernet)
+            ((Ethernet)this.parent).setEtherType(Ethernet.TYPE_BSN);
+
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        
+        int magic = bb.getInt();
+        if (magic != BSN_MAGIC) {
+        	throw new RuntimeException("Invalid BSN magic " + magic);
+        }
+        
+        this.type = bb.getShort();
+        this.version = bb.getShort();
+        if (this.version != BSN_VERSION_CURRENT) {
+        	throw new RuntimeException(
+        			"Invalid BSN packet version " + this.version + ", should be "
+        	        + BSN_VERSION_CURRENT);
+        }
+        
+        IPacket payload;
+        if (typeClassMap.containsKey(this.type)) {
+            Class<? extends IPacket> clazz = typeClassMap.get(this.type);
+            try {
+                payload = clazz.newInstance();
+            } catch (Exception e) {
+                throw new RuntimeException("Error parsing payload for BSN packet" + e);
+            }
+        } else {
+            payload = new Data();
+        }
+        
+        this.payload = new Data();
+        this.payload = payload.deserialize(data, bb.position(), bb.limit() - bb.position());
+        this.payload.setParent(this);
+        
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 883;
+        int result = super.hashCode();
+        result = prime * result + version;
+        result = prime * result + type;
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof BSN))
+            return false;
+        BSN other = (BSN) obj;
+        return (type == other.type &&
+        		version == other.version);
+    }
+    
+    public String toString() {
+    	StringBuffer sb = new StringBuffer("\n");
+    	sb.append("BSN packet");
+        if (typeClassMap.containsKey(this.type))
+        	sb.append(" type: " + typeClassMap.get(this.type).getCanonicalName());
+        else
+        	sb.append(" type: " + this.type);
+        
+    	return sb.toString();
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/BSNPROBE.java b/src/main/java/net/onrc/onos/packet/BSNPROBE.java
new file mode 100644
index 0000000..0519208
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/BSNPROBE.java
@@ -0,0 +1,197 @@
+/**
+*    Copyright 2012, Big Switch Networks, Inc. 
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+/**
+ * 
+ */
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import org.openflow.util.HexString;
+
+/**
+ * @author Shudong Zhou (shudong.zhou@bigswitch.com)
+ *
+ */
+public class BSNPROBE extends BasePacket {	
+	protected long controllerId;
+	protected int sequenceId;
+	protected byte[] srcMac;
+	protected byte[] dstMac;
+	protected long srcSwDpid;
+	protected int srcPortNo;
+
+    public BSNPROBE() {
+        srcMac = new byte[6];
+        dstMac = new byte[6];
+    }
+
+
+	public long getControllerId() {
+		return this.controllerId;
+	}
+
+	public BSNPROBE setControllerId(long controllerId) {
+		this.controllerId = controllerId;
+		return this;
+	}
+
+	public int getSequenceId() {
+		return sequenceId;
+	}
+
+	public BSNPROBE setSequenceId(int sequenceId) {
+		this.sequenceId = sequenceId;
+		return this;
+	}
+	
+    public byte[] getSrcMac() {
+        return this.srcMac;
+    }
+
+    public BSNPROBE setSrcMac(byte[] srcMac) {
+        this.srcMac = srcMac;
+        return this;
+    }
+    
+	public byte[] getDstMac() {
+		return dstMac;
+	}
+
+	public BSNPROBE setDstMac(byte[] dstMac) {
+		this.dstMac = dstMac;
+		return this;
+	}
+
+	public long getSrcSwDpid() {
+		return srcSwDpid;
+	}
+
+	public BSNPROBE setSrcSwDpid(long srcSwDpid) {
+		this.srcSwDpid = srcSwDpid;
+		return this;
+	}
+
+	public int getSrcPortNo() {
+		return srcPortNo;
+	}
+
+	public BSNPROBE setSrcPortNo(int srcPortNo) {
+		this.srcPortNo = srcPortNo;
+		return this;
+	}
+
+    @Override
+    public byte[] serialize() {
+    	short length = 8 /* controllerId */ + 4 /* seqId */
+    			+ 12 /* srcMac dstMac */ + 8 /* srcSwDpid */ + 4 /* srcPortNo */;
+    	
+    	byte[] payloadData = null;
+    	if (this.payload != null) {
+            payload.setParent(this);
+            payloadData = payload.serialize();
+            length += payloadData.length;
+        }
+    
+        byte[] data = new byte[length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.putLong(this.controllerId);
+        bb.putInt(this.sequenceId);
+        bb.put(this.srcMac);
+        bb.put(this.dstMac);
+        bb.putLong(this.srcSwDpid);
+        bb.putInt(this.srcPortNo);
+        if (payloadData != null)
+        	bb.put(payloadData);
+
+        if (this.parent != null && this.parent instanceof BSN)
+            ((BSN)this.parent).setType(BSN.BSN_TYPE_PROBE);
+
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        
+        controllerId = bb.getLong();
+        sequenceId = bb.getInt();
+        bb.get(this.srcMac, 0, 6);
+        bb.get(this.dstMac, 0, 6);
+        this.srcSwDpid = bb.getLong();
+        this.srcPortNo = bb.getInt();
+        
+        if (bb.hasRemaining()) {
+        	this.payload = new Data();
+	        this.payload = payload.deserialize(data, bb.position(), bb.limit() - bb.position());
+	        this.payload.setParent(this);
+        }
+        
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 883;
+        int result = super.hashCode();
+        result = prime * result + srcMac.hashCode();
+        result = prime * result + dstMac.hashCode();
+        result = prime * result + (int) (srcSwDpid >> 32) + (int) srcSwDpid;
+        result = prime * result + srcPortNo;
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof BSNPROBE))
+            return false;
+        BSNPROBE other = (BSNPROBE) obj;
+        if (!Arrays.equals(srcMac, other.srcMac))
+            return false;
+        if (!Arrays.equals(dstMac, other.dstMac))
+        	return false;
+        return (sequenceId == other.sequenceId &&
+        	    srcSwDpid == other.srcSwDpid &&
+        	    srcPortNo == other.srcPortNo
+        	    );
+    }
+    
+    public String toString() {
+    	StringBuffer sb = new StringBuffer("\n");
+    	sb.append("BSN Probe packet");
+    	sb.append("\nSource Mac: ");
+    	sb.append(HexString.toHexString(srcMac));
+    	sb.append("\nDestination Mac: ");
+    	sb.append(HexString.toHexString(dstMac));
+    	sb.append("\nSource Switch: ");
+    	sb.append(HexString.toHexString(srcSwDpid));
+    	sb.append(" port: " + srcPortNo);
+    	sb.append("\nSequence No.:" + sequenceId);
+    	
+    	return sb.toString();
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/BasePacket.java b/src/main/java/net/onrc/onos/packet/BasePacket.java
new file mode 100644
index 0000000..6d91640
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/BasePacket.java
@@ -0,0 +1,116 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+
+/**
+*
+* @author David Erickson (daviderickson@cs.stanford.edu)
+*/
+public abstract class BasePacket implements IPacket {
+    protected IPacket parent;
+    protected IPacket payload;
+
+    /**
+     * @return the parent
+     */
+    @Override
+    public IPacket getParent() {
+        return parent;
+    }
+
+    /**
+     * @param parent the parent to set
+     */
+    @Override
+    public IPacket setParent(IPacket parent) {
+        this.parent = parent;
+        return this;
+    }
+
+    /**
+     * @return the payload
+     */
+    @Override
+    public IPacket getPayload() {
+        return payload;
+    }
+
+    /**
+     * @param payload the payload to set
+     */
+    @Override
+    public IPacket setPayload(IPacket payload) {
+        this.payload = payload;
+        return this;
+    }
+    
+    @Override
+    public void resetChecksum() {
+        if (this.parent != null)
+            this.parent.resetChecksum();
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 6733;
+        int result = 1;
+        result = prime * result + ((payload == null) ? 0 : payload.hashCode());
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof BasePacket))
+            return false;
+        BasePacket other = (BasePacket) obj;
+        if (payload == null) {
+            if (other.payload != null)
+                return false;
+        } else if (!payload.equals(other.payload))
+            return false;
+        return true;
+    }
+    
+    @Override
+    public Object clone() {
+        IPacket pkt;
+        try {
+            pkt = this.getClass().newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not clone packet");
+        }
+        // TODO: we are using serialize()/deserialize() to perform the 
+        // cloning. Not the most efficient way but simple. We can revisit
+        // if we hit performance problems.
+        byte[] data = this.serialize();
+        pkt.deserialize(this.serialize(), 0, data.length);
+        pkt.setParent(this.parent);
+        return pkt;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/packet/DHCP.java b/src/main/java/net/onrc/onos/packet/DHCP.java
new file mode 100644
index 0000000..2e71ec8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/DHCP.java
@@ -0,0 +1,517 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class DHCP extends BasePacket {
+    /**
+     * ------------------------------------------
+     * |op (1)  | htype(1) | hlen(1) | hops(1)  |
+     * ------------------------------------------
+     * |        xid (4)                         |
+     * ------------------------------------------
+     * |  secs (2)          |   flags (2)       |
+     * ------------------------------------------
+     * |            ciaddr (4)                  |
+     * ------------------------------------------
+     * |            yiaddr (4)                  |
+     * ------------------------------------------
+     * |            siaddr (4)                  |
+     * ------------------------------------------
+     * |            giaddr (4)                  |
+     * ------------------------------------------
+     * |            chaddr (16)                  |
+     * ------------------------------------------
+     * |            sname (64)                  |
+     * ------------------------------------------
+     * |            file (128)                  |
+     * ------------------------------------------
+     * |            options (312)               |
+     * ------------------------------------------
+     * 
+     */
+    // Header + magic without options
+    public static int MIN_HEADER_LENGTH = 240;
+    public static byte OPCODE_REQUEST = 0x1;
+    public static byte OPCODE_REPLY = 0x2;
+
+    public static byte HWTYPE_ETHERNET = 0x1;
+    
+    public enum DHCPOptionCode {
+        OptionCode_SubnetMask           ((byte)1),
+        OptionCode_RequestedIP          ((byte)50),
+        OptionCode_LeaseTime            ((byte)51),
+        OptionCode_MessageType          ((byte)53),
+        OptionCode_DHCPServerIp         ((byte)54),
+        OptionCode_RequestedParameters  ((byte)55),
+        OptionCode_RenewalTime          ((byte)58),
+        OPtionCode_RebindingTime        ((byte)59),
+        OptionCode_ClientID             ((byte)61),
+        OptionCode_END                  ((byte)255);
+    
+        protected byte value;
+        
+        private DHCPOptionCode(byte value) {
+            this.value = value;
+        }
+        
+        public byte getValue() {
+            return value;
+        }
+    }
+    
+    protected byte opCode;
+    protected byte hardwareType;
+    protected byte hardwareAddressLength;
+    protected byte hops;
+    protected int transactionId;
+    protected short seconds;
+    protected short flags;
+    protected int clientIPAddress;
+    protected int yourIPAddress;
+    protected int serverIPAddress;
+    protected int gatewayIPAddress;
+    protected byte[] clientHardwareAddress;
+    protected String serverName;
+    protected String bootFileName;
+    protected List<DHCPOption> options = new ArrayList<DHCPOption>();
+
+    /**
+     * @return the opCode
+     */
+    public byte getOpCode() {
+        return opCode;
+    }
+
+    /**
+     * @param opCode the opCode to set
+     */
+    public DHCP setOpCode(byte opCode) {
+        this.opCode = opCode;
+        return this;
+    }
+
+    /**
+     * @return the hardwareType
+     */
+    public byte getHardwareType() {
+        return hardwareType;
+    }
+
+    /**
+     * @param hardwareType the hardwareType to set
+     */
+    public DHCP setHardwareType(byte hardwareType) {
+        this.hardwareType = hardwareType;
+        return this;
+    }
+
+    /**
+     * @return the hardwareAddressLength
+     */
+    public byte getHardwareAddressLength() {
+        return hardwareAddressLength;
+    }
+
+    /**
+     * @param hardwareAddressLength the hardwareAddressLength to set
+     */
+    public DHCP setHardwareAddressLength(byte hardwareAddressLength) {
+        this.hardwareAddressLength = hardwareAddressLength;
+        return this;
+    }
+
+    /**
+     * @return the hops
+     */
+    public byte getHops() {
+        return hops;
+    }
+
+    /**
+     * @param hops the hops to set
+     */
+    public DHCP setHops(byte hops) {
+        this.hops = hops;
+        return this;
+    }
+
+    /**
+     * @return the transactionId
+     */
+    public int getTransactionId() {
+        return transactionId;
+    }
+
+    /**
+     * @param transactionId the transactionId to set
+     */
+    public DHCP setTransactionId(int transactionId) {
+        this.transactionId = transactionId;
+        return this;
+    }
+
+    /**
+     * @return the seconds
+     */
+    public short getSeconds() {
+        return seconds;
+    }
+
+    /**
+     * @param seconds the seconds to set
+     */
+    public DHCP setSeconds(short seconds) {
+        this.seconds = seconds;
+        return this;
+    }
+
+    /**
+     * @return the flags
+     */
+    public short getFlags() {
+        return flags;
+    }
+
+    /**
+     * @param flags the flags to set
+     */
+    public DHCP setFlags(short flags) {
+        this.flags = flags;
+        return this;
+    }
+
+    /**
+     * @return the clientIPAddress
+     */
+    public int getClientIPAddress() {
+        return clientIPAddress;
+    }
+
+    /**
+     * @param clientIPAddress the clientIPAddress to set
+     */
+    public DHCP setClientIPAddress(int clientIPAddress) {
+        this.clientIPAddress = clientIPAddress;
+        return this;
+    }
+
+    /**
+     * @return the yourIPAddress
+     */
+    public int getYourIPAddress() {
+        return yourIPAddress;
+    }
+
+    /**
+     * @param yourIPAddress the yourIPAddress to set
+     */
+    public DHCP setYourIPAddress(int yourIPAddress) {
+        this.yourIPAddress = yourIPAddress;
+        return this;
+    }
+
+    /**
+     * @return the serverIPAddress
+     */
+    public int getServerIPAddress() {
+        return serverIPAddress;
+    }
+
+    /**
+     * @param serverIPAddress the serverIPAddress to set
+     */
+    public DHCP setServerIPAddress(int serverIPAddress) {
+        this.serverIPAddress = serverIPAddress;
+        return this;
+    }
+
+    /**
+     * @return the gatewayIPAddress
+     */
+    public int getGatewayIPAddress() {
+        return gatewayIPAddress;
+    }
+
+    /**
+     * @param gatewayIPAddress the gatewayIPAddress to set
+     */
+    public DHCP setGatewayIPAddress(int gatewayIPAddress) {
+        this.gatewayIPAddress = gatewayIPAddress;
+        return this;
+    }
+
+    /**
+     * @return the clientHardwareAddress
+     */
+    public byte[] getClientHardwareAddress() {
+        return clientHardwareAddress;
+    }
+
+    /**
+     * @param clientHardwareAddress the clientHardwareAddress to set
+     */
+    public DHCP setClientHardwareAddress(byte[] clientHardwareAddress) {
+        this.clientHardwareAddress = clientHardwareAddress;
+        return this;
+    }
+    
+    /**
+     * Gets a specific DHCP option parameter
+     * @param opetionCode The option code to get
+     * @return The value of the option if it exists, null otherwise
+     */
+    public DHCPOption getOption(DHCPOptionCode optionCode) {
+        for (DHCPOption opt : options) {
+            if (opt.code == optionCode.value)
+                return opt;
+        }
+        return null;
+    }
+
+    /**
+     * @return the options
+     */
+    public List<DHCPOption> getOptions() {
+        return options;
+    }
+
+    /**
+     * @param options the options to set
+     */
+    public DHCP setOptions(List<DHCPOption> options) {
+        this.options = options;
+        return this;
+    }
+
+    /**
+     * @return the packetType base on option 53
+     */
+    public DHCPPacketType getPacketType() {
+        ListIterator<DHCPOption> lit = options.listIterator();
+        while (lit.hasNext()) {
+            DHCPOption option = lit.next();
+            // only care option 53
+            if (option.getCode() == 53) {
+                return DHCPPacketType.getType(option.getData()[0]);
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * @return the serverName
+     */
+    public String getServerName() {
+        return serverName;
+    }
+
+    /**
+     * @param serverName the serverName to set
+     */
+    public DHCP setServerName(String serverName) {
+        this.serverName = serverName;
+        return this;
+    }
+
+    /**
+     * @return the bootFileName
+     */
+    public String getBootFileName() {
+        return bootFileName;
+    }
+
+    /**
+     * @param bootFileName the bootFileName to set
+     */
+    public DHCP setBootFileName(String bootFileName) {
+        this.bootFileName = bootFileName;
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        // not guaranteed to retain length/exact format
+        resetChecksum();
+
+        // minimum size 240 including magic cookie, options generally padded to 300
+        int optionsLength = 0;
+        for (DHCPOption option : this.options) {
+            if (option.getCode() == 0 || option.getCode() == 255) {
+                optionsLength += 1;
+            } else {
+                optionsLength += 2 + (int)(0xff & option.getLength());
+            }
+        }
+        int optionsPadLength = 0;
+        if (optionsLength < 60)
+            optionsPadLength = 60 - optionsLength;
+
+        byte[] data = new byte[240+optionsLength+optionsPadLength];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.put(this.opCode);
+        bb.put(this.hardwareType);
+        bb.put(this.hardwareAddressLength);
+        bb.put(this.hops);
+        bb.putInt(this.transactionId);
+        bb.putShort(this.seconds);
+        bb.putShort(this.flags);
+        bb.putInt(this.clientIPAddress);
+        bb.putInt(this.yourIPAddress);
+        bb.putInt(this.serverIPAddress);
+        bb.putInt(this.gatewayIPAddress);
+        bb.put(this.clientHardwareAddress);
+        if (this.clientHardwareAddress.length < 16) {
+            for (int i = 0; i < (16 - this.clientHardwareAddress.length); ++i) {
+                bb.put((byte) 0x0);
+            }
+        }
+        writeString(this.serverName, bb, 64);
+        writeString(this.bootFileName, bb, 128);
+        // magic cookie
+        bb.put((byte) 0x63);
+        bb.put((byte) 0x82);
+        bb.put((byte) 0x53);
+        bb.put((byte) 0x63);
+        for (DHCPOption option : this.options) {
+            int code = option.getCode() & 0xff;
+            bb.put((byte) code);
+            if ((code != 0) && (code != 255)) {
+                bb.put(option.getLength());
+                bb.put(option.getData());
+            }
+        }
+        // assume the rest is padded out with zeroes
+        return data;
+    }
+
+    protected void writeString(String string, ByteBuffer bb, int maxLength) {
+        if (string == null) {
+            for (int i = 0; i < maxLength; ++i) {
+                bb.put((byte) 0x0);
+            }
+        } else {
+            byte[] bytes = null;
+            try {
+                 bytes = string.getBytes("ascii");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException("Failure encoding server name", e);
+            }
+            int writeLength = bytes.length;
+            if (writeLength > maxLength) {
+                writeLength = maxLength;
+            }
+            bb.put(bytes, 0, writeLength);
+            for (int i = writeLength; i < maxLength; ++i) {
+                bb.put((byte) 0x0);
+            }
+        }
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        if (bb.remaining() < MIN_HEADER_LENGTH) {
+            return this;
+        }
+        
+        this.opCode = bb.get();
+        this.hardwareType = bb.get();
+        this.hardwareAddressLength = bb.get();
+        this.hops = bb.get();
+        this.transactionId = bb.getInt();
+        this.seconds = bb.getShort();
+        this.flags = bb.getShort();
+        this.clientIPAddress = bb.getInt();
+        this.yourIPAddress = bb.getInt();
+        this.serverIPAddress = bb.getInt();
+        this.gatewayIPAddress = bb.getInt();
+        int hardwareAddressLength = 0xff & this.hardwareAddressLength;
+        this.clientHardwareAddress = new byte[hardwareAddressLength];
+
+        bb.get(this.clientHardwareAddress);
+        for (int i = hardwareAddressLength; i < 16; ++i)
+            bb.get();
+        this.serverName = readString(bb, 64);
+        this.bootFileName = readString(bb, 128);
+        // read the magic cookie
+        // magic cookie
+        bb.get();
+        bb.get();
+        bb.get();
+        bb.get();
+        // read options
+        while (bb.hasRemaining()) {
+            DHCPOption option = new DHCPOption();
+            int code = 0xff & bb.get(); // convert signed byte to int in range [0,255]
+            option.setCode((byte) code);
+            if (code == 0) {
+                // skip these
+                continue;
+            } else if (code != 255) {
+                if (bb.hasRemaining()) {
+                    int l = 0xff & bb.get(); // convert signed byte to int in range [0,255]
+                    option.setLength((byte) l);
+                    if (bb.remaining() >= l) {
+                        byte[] optionData = new byte[l];
+                        bb.get(optionData);
+                        option.setData(optionData);
+                    } else {
+                        // Skip the invalid option and set the END option
+                        code = 0xff;
+                        option.setCode((byte)code);
+                        option.setLength((byte) 0);
+                    }
+                } else {
+                    // Skip the invalid option and set the END option
+                    code = 0xff;
+                    option.setCode((byte)code);
+                    option.setLength((byte) 0);
+                }
+            }
+            this.options.add(option);
+            if (code == 255) {
+                // remaining bytes are supposed to be 0, but ignore them just in case
+                break;
+            }
+        }
+
+        return this;
+    }
+
+    protected String readString(ByteBuffer bb, int maxLength) {
+        byte[] bytes = new byte[maxLength];
+        bb.get(bytes);
+        String result = null;
+        try {
+            result = new String(bytes, "ascii").trim();
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("Failure decoding string", e);
+        }
+        return result;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/DHCPOption.java b/src/main/java/net/onrc/onos/packet/DHCPOption.java
new file mode 100644
index 0000000..539fa99
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/DHCPOption.java
@@ -0,0 +1,118 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.util.Arrays;
+
+/**
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class DHCPOption {
+    protected byte code;
+    protected byte length;
+    protected byte[] data;
+
+    /**
+     * @return the code
+     */
+    public byte getCode() {
+        return code;
+    }
+
+    /**
+     * @param code the code to set
+     */
+    public DHCPOption setCode(byte code) {
+        this.code = code;
+        return this;
+    }
+
+    /**
+     * @return the length
+     */
+    public byte getLength() {
+        return length;
+    }
+
+    /**
+     * @param length the length to set
+     */
+    public DHCPOption setLength(byte length) {
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * @return the data
+     */
+    public byte[] getData() {
+        return data;
+    }
+
+    /**
+     * @param data the data to set
+     */
+    public DHCPOption setData(byte[] data) {
+        this.data = data;
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + code;
+        result = prime * result + Arrays.hashCode(data);
+        result = prime * result + length;
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof DHCPOption))
+            return false;
+        DHCPOption other = (DHCPOption) obj;
+        if (code != other.code)
+            return false;
+        if (!Arrays.equals(data, other.data))
+            return false;
+        if (length != other.length)
+            return false;
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "DHCPOption [code=" + code + ", length=" + length + ", data="
+                + Arrays.toString(data) + "]";
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/DHCPPacketType.java b/src/main/java/net/onrc/onos/packet/DHCPPacketType.java
new file mode 100644
index 0000000..a01a2a2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/DHCPPacketType.java
@@ -0,0 +1,116 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+public enum DHCPPacketType {
+    // From RFC 1533
+    DHCPDISCOVER        (1),
+    DHCPOFFER           (2),
+    DHCPREQUEST         (3),
+    DHCPDECLINE         (4),
+    DHCPACK             (5),
+    DHCPNAK             (6),
+    DHCPRELEASE         (7),
+    
+    // From RFC2132
+    DHCPINFORM          (8),
+    
+    // From RFC3203
+    DHCPFORCERENEW      (9),
+    
+    // From RFC4388
+    DHCPLEASEQUERY      (10),
+    DHCPLEASEUNASSIGNED (11),
+    DHCPLEASEUNKNOWN    (12),
+    DHCPLEASEACTIVE     (13);
+    
+    protected int value;
+    
+    private DHCPPacketType(int value) {
+        this.value = value;
+    }
+    
+    public int getValue() {
+        return value;
+    }
+    
+    public String toString(){
+        switch (value) {
+            case 1:
+                return "DHCPDISCOVER";
+            case 2:
+                return "DHCPOFFER";
+            case 3:
+                return "DHCPREQUEST";
+            case 4:
+                return "DHCPDECLINE";
+            case 5:
+                return "DHCPACK";
+            case 6:
+                return "DHCPNAK";
+            case 7:
+                return "DHCPRELEASE";
+            case 8:
+                return "DHCPINFORM";
+            case 9:
+                return "DHCPFORCERENEW";
+            case 10:
+                return "DHCPLEASEQUERY";
+            case 11:
+                return "DHCPLEASEUNASSIGNED";
+            case 12:
+                return "DHCPLEASEUNKNOWN";
+            case 13:
+                return "DHCPLEASEACTIVE";
+        }
+        
+        return null;
+    }
+    public static DHCPPacketType getType(int value) {
+        switch (value) {
+            case 1:
+                return DHCPDISCOVER;
+            case 2:
+                return DHCPOFFER;
+            case 3:
+                return DHCPREQUEST;
+            case 4:
+                return DHCPDECLINE;
+            case 5:
+                return DHCPACK;
+            case 6:
+                return DHCPNAK;
+            case 7:
+                return DHCPRELEASE;
+            case 8:
+                return DHCPINFORM;
+            case 9:
+                return DHCPFORCERENEW;
+            case 10:
+                return DHCPLEASEQUERY;
+            case 11:
+                return DHCPLEASEUNASSIGNED;
+            case 12:
+                return DHCPLEASEUNKNOWN;
+            case 13:
+                return DHCPLEASEACTIVE;
+        }
+        
+        return null;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/Data.java b/src/main/java/net/onrc/onos/packet/Data.java
new file mode 100644
index 0000000..ac1aa42
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/Data.java
@@ -0,0 +1,94 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.util.Arrays;
+
+/**
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class Data extends BasePacket {
+    protected byte[] data;
+
+    /**
+     * 
+     */
+    public Data() {
+    }
+
+    /**
+     * @param data
+     */
+    public Data(byte[] data) {
+        this.data = data;
+    }
+
+    /**
+     * @return the data
+     */
+    public byte[] getData() {
+        return data;
+    }
+
+    /**
+     * @param data the data to set
+     */
+    public Data setData(byte[] data) {
+        this.data = data;
+        return this;
+    }
+
+    public byte[] serialize() {
+        return this.data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        this.data = Arrays.copyOfRange(data, offset, data.length);
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 1571;
+        int result = super.hashCode();
+        result = prime * result + Arrays.hashCode(data);
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof Data))
+            return false;
+        Data other = (Data) obj;
+        if (!Arrays.equals(data, other.data))
+            return false;
+        return true;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/Ethernet.java b/src/main/java/net/onrc/onos/packet/Ethernet.java
new file mode 100644
index 0000000..c884129
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/Ethernet.java
@@ -0,0 +1,468 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.floodlightcontroller.util.MACAddress;
+import org.openflow.util.HexString;
+
+/**
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class Ethernet extends BasePacket {
+    private static String HEXES = "0123456789ABCDEF";
+    public static final short TYPE_ARP = 0x0806;
+    public static final short TYPE_RARP = (short) 0x8035;
+    public static final short TYPE_IPv4 = 0x0800;
+    public static final short TYPE_LLDP = (short) 0x88cc;
+    public static final short TYPE_BSN = (short) 0x8942;
+    public static final short VLAN_UNTAGGED = (short)0xffff;
+    public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
+    public static Map<Short, Class<? extends IPacket>> etherTypeClassMap;
+
+    static {
+        etherTypeClassMap = new HashMap<Short, Class<? extends IPacket>>();
+        etherTypeClassMap.put(TYPE_ARP, ARP.class);
+        etherTypeClassMap.put(TYPE_RARP, ARP.class);
+        etherTypeClassMap.put(TYPE_IPv4, IPv4.class);
+        etherTypeClassMap.put(TYPE_LLDP, LLDP.class);
+        etherTypeClassMap.put(TYPE_BSN, BSN.class);
+    }
+
+    protected MACAddress destinationMACAddress;
+    protected MACAddress sourceMACAddress;
+    protected byte priorityCode;
+    protected short vlanID;
+    protected short etherType;
+    protected boolean pad = false;
+
+    /**
+     * By default, set Ethernet to untagged
+     */
+    public Ethernet() {
+        super();
+        this.vlanID = VLAN_UNTAGGED;
+    }
+    
+    /**
+     * @return the destination MAC as a byte array
+     */
+    public byte[] getDestinationMACAddress() {
+        return destinationMACAddress.toBytes();
+    }
+    
+    /**
+     * @return the destination MAC
+     */
+    public MACAddress getDestinationMAC() {
+        return destinationMACAddress;
+    }
+
+    /**
+     * @param destinationMACAddress the destination MAC to set
+     */
+    public Ethernet setDestinationMACAddress(byte[] destinationMACAddress) {
+        this.destinationMACAddress = MACAddress.valueOf(destinationMACAddress);
+        return this;
+    }
+
+    /**
+     * @param destinationMACAddress the destination MAC to set
+     */
+    public Ethernet setDestinationMACAddress(String destinationMACAddress) {
+        this.destinationMACAddress = MACAddress.valueOf(destinationMACAddress);
+        return this;
+    }
+
+    /**
+     * @return the source MACAddress as a byte array
+     */
+    public byte[] getSourceMACAddress() {
+        return sourceMACAddress.toBytes();
+    }
+    
+    /**
+     * @return the source MACAddress
+     */
+    public MACAddress getSourceMAC() {
+        return sourceMACAddress;
+    }
+
+    /**
+     * @param sourceMACAddress the source MAC to set
+     */
+    public Ethernet setSourceMACAddress(byte[] sourceMACAddress) {
+        this.sourceMACAddress = MACAddress.valueOf(sourceMACAddress);
+        return this;
+    }
+
+    /**
+     * @param sourceMACAddress the source MAC to set
+     */
+    public Ethernet setSourceMACAddress(String sourceMACAddress) {
+        this.sourceMACAddress = MACAddress.valueOf(sourceMACAddress);
+        return this;
+    }
+
+    /**
+     * @return the priorityCode
+     */
+    public byte getPriorityCode() {
+        return priorityCode;
+    }
+
+    /**
+     * @param priorityCode the priorityCode to set
+     */
+    public Ethernet setPriorityCode(byte priorityCode) {
+        this.priorityCode = priorityCode;
+        return this;
+    }
+
+    /**
+     * @return the vlanID
+     */
+    public short getVlanID() {
+        return vlanID;
+    }
+
+    /**
+     * @param vlanID the vlanID to set
+     */
+    public Ethernet setVlanID(short vlanID) {
+        this.vlanID = vlanID;
+        return this;
+    }
+
+    /**
+     * @return the etherType
+     */
+    public short getEtherType() {
+        return etherType;
+    }
+
+    /**
+     * @param etherType the etherType to set
+     */
+    public Ethernet setEtherType(short etherType) {
+        this.etherType = etherType;
+        return this;
+    }
+    
+    /**
+     * @return True if the Ethernet frame is broadcast, false otherwise
+     */
+    public boolean isBroadcast() {
+        assert(destinationMACAddress.length() == 6);
+        return destinationMACAddress.isBroadcast();
+    }
+    
+    /**
+     * @return True is the Ethernet frame is multicast, False otherwise
+     */
+    public boolean isMulticast() {
+        return destinationMACAddress.isMulticast();
+    }
+    /**
+     * Pad this packet to 60 bytes minimum, filling with zeros?
+     * @return the pad
+     */
+    public boolean isPad() {
+        return pad;
+    }
+
+    /**
+     * Pad this packet to 60 bytes minimum, filling with zeros?
+     * @param pad the pad to set
+     */
+    public Ethernet setPad(boolean pad) {
+        this.pad = pad;
+        return this;
+    }
+
+    public byte[] serialize() {
+        byte[] payloadData = null;
+        if (payload != null) {
+            payload.setParent(this);
+            payloadData = payload.serialize();
+        }
+        int length = 14 + ((vlanID == VLAN_UNTAGGED) ? 0 : 4) +
+                          ((payloadData == null) ? 0 : payloadData.length);
+        if (pad && length < 60) {
+            length = 60;
+        }
+        byte[] data = new byte[length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.put(destinationMACAddress.toBytes());
+        bb.put(sourceMACAddress.toBytes());
+        if (vlanID != VLAN_UNTAGGED) {
+            bb.putShort((short) 0x8100);
+            bb.putShort((short) ((priorityCode << 13) | (vlanID & 0x0fff)));
+        }
+        bb.putShort(etherType);
+        if (payloadData != null)
+            bb.put(payloadData);
+        if (pad) {
+            Arrays.fill(data, bb.position(), data.length, (byte)0x0);
+        }
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        if (length <= 0)
+            return null;
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        if (this.destinationMACAddress == null)
+            this.destinationMACAddress = MACAddress.valueOf(new byte[6]);
+        byte[] dstAddr = new byte[MACAddress.MAC_ADDRESS_LENGTH];
+        bb.get(dstAddr);
+        this.destinationMACAddress = MACAddress.valueOf(dstAddr);
+
+        if (this.sourceMACAddress == null)
+            this.sourceMACAddress = MACAddress.valueOf(new byte[6]);
+        byte[] srcAddr = new byte[MACAddress.MAC_ADDRESS_LENGTH];
+        bb.get(srcAddr);
+        this.sourceMACAddress = MACAddress.valueOf(srcAddr);
+
+        short etherType = bb.getShort();
+        if (etherType == (short) 0x8100) {
+            short tci = bb.getShort();
+            this.priorityCode = (byte) ((tci >> 13) & 0x07);
+            this.vlanID = (short) (tci & 0x0fff);
+            etherType = bb.getShort();
+        } else {
+            this.vlanID = VLAN_UNTAGGED;
+        }
+        this.etherType = etherType;
+        
+        IPacket payload;
+        if (Ethernet.etherTypeClassMap.containsKey(this.etherType)) {
+            Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap.get(this.etherType);
+            try {
+                payload = clazz.newInstance();
+            } catch (Exception e) {
+                throw new RuntimeException("Error parsing payload for Ethernet packet", e);
+            }
+        } else {
+            payload = new Data();
+        }
+        this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
+        this.payload.setParent(this);
+        return this;
+    }
+
+    /**
+     * Checks to see if a string is a valid MAC address.
+     * @param macAddress
+     * @return True if macAddress is a valid MAC, False otherwise
+     */
+    public static boolean isMACAddress(String macAddress) {
+        String[] macBytes = macAddress.split(":");
+        if (macBytes.length != 6)
+            return false;
+        for (int i = 0; i < 6; ++i) {
+            if (HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1 || 
+                HEXES.indexOf(macBytes[i].toUpperCase().charAt(1)) == -1) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not
+     * matter, and returns a corresponding byte[].
+     * @param macAddress The MAC address to convert into a bye array
+     * @return The macAddress as a byte array 
+     */
+    public static byte[] toMACAddress(String macAddress) {
+        return MACAddress.valueOf(macAddress).toBytes();
+    }
+
+
+    /**
+     * Accepts a MAC address and returns the corresponding long, where the
+     * MAC bytes are set on the lower order bytes of the long.
+     * @param macAddress
+     * @return a long containing the mac address bytes
+     */
+    public static long toLong(byte[] macAddress) {
+        return MACAddress.valueOf(macAddress).toLong();
+    }
+
+    /**
+     * Convert a long MAC address to a byte array
+     * @param macAddress
+     * @return the bytes of the mac address
+     */
+    public static byte[] toByteArray(long macAddress) {
+        return MACAddress.valueOf(macAddress).toBytes();
+    }
+    
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 7867;
+        int result = super.hashCode();
+        result = prime * result + destinationMACAddress.hashCode();
+        result = prime * result + etherType;
+        result = prime * result + vlanID;
+        result = prime * result + priorityCode;
+        result = prime * result + (pad ? 1231 : 1237);
+        result = prime * result + sourceMACAddress.hashCode();
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof Ethernet))
+            return false;
+        Ethernet other = (Ethernet) obj;
+        if (!destinationMACAddress.equals(other.destinationMACAddress))
+            return false;
+        if (priorityCode != other.priorityCode)
+            return false;
+        if (vlanID != other.vlanID)
+            return false;
+        if (etherType != other.etherType)
+            return false;
+        if (pad != other.pad)
+            return false;
+        if (!sourceMACAddress.equals(other.sourceMACAddress))
+            return false;
+        return true;
+    }
+    
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString(java.lang.Object)
+     */
+    @Override
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("\n");
+
+        IPacket pkt = (IPacket) this.getPayload();
+
+        if (pkt instanceof ARP)
+            sb.append("arp");
+        else if (pkt instanceof LLDP)
+            sb.append("lldp");
+        else if (pkt instanceof ICMP)
+            sb.append("icmp");
+        else if (pkt instanceof IPv4)
+            sb.append("ip");
+        else if (pkt instanceof DHCP)
+            sb.append("dhcp");
+        else  sb.append(this.getEtherType());
+
+        sb.append("\ndl_vlan: ");
+        if (this.getVlanID() == Ethernet.VLAN_UNTAGGED)
+            sb.append("untagged");
+        else
+            sb.append(this.getVlanID());
+        sb.append("\ndl_vlan_pcp: ");
+        sb.append(this.getPriorityCode());
+        sb.append("\ndl_src: ");
+        sb.append(HexString.toHexString(this.getSourceMACAddress()));
+        sb.append("\ndl_dst: ");
+        sb.append(HexString.toHexString(this.getDestinationMACAddress()));
+
+
+        if (pkt instanceof ARP) {
+            ARP p = (ARP) pkt;
+            sb.append("\nnw_src: ");
+            sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p.getSenderProtocolAddress())));
+            sb.append("\nnw_dst: ");
+            sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p.getTargetProtocolAddress())));
+        }
+        else if (pkt instanceof LLDP) {
+            sb.append("lldp packet");
+        }
+        else if (pkt instanceof ICMP) {
+            ICMP icmp = (ICMP) pkt;
+            sb.append("\nicmp_type: ");
+            sb.append(icmp.getIcmpType());
+            sb.append("\nicmp_code: ");
+            sb.append(icmp.getIcmpCode());
+        }
+        else if (pkt instanceof IPv4) {
+            IPv4 p = (IPv4) pkt;
+            sb.append("\nnw_src: ");
+            sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
+            sb.append("\nnw_dst: ");
+            sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
+            sb.append("\nnw_tos: ");
+            sb.append(p.getDiffServ());
+            sb.append("\nnw_proto: ");
+            sb.append(p.getProtocol());
+
+            if (pkt instanceof TCP) {
+                sb.append("\ntp_src: ");
+                sb.append(((TCP) pkt).getSourcePort());
+                sb.append("\ntp_dst: ");
+                sb.append(((TCP) pkt).getDestinationPort());
+
+            } else if (pkt instanceof UDP) {
+                sb.append("\ntp_src: ");
+                sb.append(((UDP) pkt).getSourcePort());
+                sb.append("\ntp_dst: ");
+                sb.append(((UDP) pkt).getDestinationPort());
+            }
+
+            if (pkt instanceof ICMP) {
+                ICMP icmp = (ICMP) pkt;
+                sb.append("\nicmp_type: ");
+                sb.append(icmp.getIcmpType());
+                sb.append("\nicmp_code: ");
+                sb.append(icmp.getIcmpCode());
+            }
+
+        }
+        else if (pkt instanceof DHCP) {
+            sb.append("\ndhcp packet");
+        }
+        else if (pkt instanceof Data) {
+            sb.append("\ndata packet");
+        }
+        else if (pkt instanceof LLC) {
+            sb.append("\nllc packet");
+        }
+        else if (pkt instanceof BPDU) {
+            sb.append("\nbpdu packet");
+        }
+        else sb.append("\nunknwon packet");
+
+        return sb.toString();
+    }
+
+}
diff --git a/src/main/java/net/onrc/onos/packet/ICMP.java b/src/main/java/net/onrc/onos/packet/ICMP.java
new file mode 100644
index 0000000..f66b163
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/ICMP.java
@@ -0,0 +1,170 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implements ICMP packet format
+ * @author shudong.zhou@bigswitch.com
+ */
+public class ICMP extends BasePacket {
+    protected byte icmpType;
+    protected byte icmpCode;
+    protected short checksum;
+
+    /**
+     * @return the icmpType
+     */
+    public byte getIcmpType() {
+        return icmpType;
+    }
+
+    /**
+     * @param icmpType to set
+     */
+    public ICMP setIcmpType(byte icmpType) {
+        this.icmpType = icmpType;
+        return this;
+    }
+
+    /**
+     * @return the icmp code
+     */
+    public byte getIcmpCode() {
+        return icmpCode;
+    }
+
+    /**
+     * @param icmpCode code to set
+     */
+    public ICMP setIcmpCode(byte icmpCode) {
+        this.icmpCode = icmpCode;
+        return this;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return checksum;
+    }
+
+    /**
+     * @param checksum the checksum to set
+     */
+    public ICMP setChecksum(short checksum) {
+        this.checksum = checksum;
+        return this;
+    }
+
+    /**
+     * Serializes the packet. Will compute and set the following fields if they
+     * are set to specific values at the time serialize is called:
+     *      -checksum : 0
+     *      -length : 0
+     */
+    public byte[] serialize() {
+        int length = 4;
+        byte[] payloadData = null;
+        if (payload != null) {
+            payload.setParent(this);
+            payloadData = payload.serialize();
+            length += payloadData.length;
+        }
+
+        byte[] data = new byte[length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+
+        bb.put(this.icmpType);
+        bb.put(this.icmpCode);
+        bb.putShort(this.checksum);
+        if (payloadData != null)
+            bb.put(payloadData);
+
+        if (this.parent != null && this.parent instanceof IPv4)
+            ((IPv4)this.parent).setProtocol(IPv4.PROTOCOL_ICMP);
+
+        // compute checksum if needed
+        if (this.checksum == 0) {
+            bb.rewind();
+            int accumulation = 0;
+
+            for (int i = 0; i < length / 2; ++i) {
+                accumulation += 0xffff & bb.getShort();
+            }
+            // pad to an even number of shorts
+            if (length % 2 > 0) {
+                accumulation += (bb.get() & 0xff) << 8;
+            }
+
+            accumulation = ((accumulation >> 16) & 0xffff)
+                    + (accumulation & 0xffff);
+            this.checksum = (short) (~accumulation & 0xffff);
+            bb.putShort(2, this.checksum);
+        }
+        return data;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 5807;
+        int result = super.hashCode();
+        result = prime * result + icmpType;
+        result = prime * result + icmpCode;
+        result = prime * result + checksum;
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof ICMP))
+            return false;
+        ICMP other = (ICMP) obj;
+        if (icmpType != other.icmpType)
+            return false;
+        if (icmpCode != other.icmpCode)
+            return false;
+        if (checksum != other.checksum)
+            return false;
+        return true;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        this.icmpType = bb.get();
+        this.icmpCode = bb.get();
+        this.checksum = bb.getShort();
+        
+        this.payload = new Data();
+        this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
+        this.payload.setParent(this);
+        return this;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/IPacket.java b/src/main/java/net/onrc/onos/packet/IPacket.java
new file mode 100644
index 0000000..cb48f88
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/IPacket.java
@@ -0,0 +1,77 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+/**
+*
+* @author David Erickson (daviderickson@cs.stanford.edu)
+*/
+public interface IPacket {
+    /**
+     * 
+     * @return
+     */
+    public IPacket getPayload();
+
+    /**
+     * 
+     * @param packet
+     * @return
+     */
+    public IPacket setPayload(IPacket packet);
+
+    /**
+     * 
+     * @return
+     */
+    public IPacket getParent();
+
+    /**
+     * 
+     * @param packet
+     * @return
+     */
+    public IPacket setParent(IPacket packet);
+
+    /**
+     * Reset any checksums as needed, and call resetChecksum on all parents
+     */
+    public void resetChecksum();
+    
+    /**
+     * Sets all payloads parent packet if applicable, then serializes this 
+     * packet and all payloads
+     * @return a byte[] containing this packet and payloads
+     */
+    public byte[] serialize();
+
+    /**
+     * Deserializes this packet layer and all possible payloads
+     * @param data
+     * @param offset offset to start deserializing from
+     * @param length length of the data to deserialize
+     * @return the deserialized data
+     */
+    public IPacket deserialize(byte[] data, int offset, int length);
+    
+    /** Clone this packet and its payload packet but not its parent. 
+     * 
+     * @return
+     */
+    public Object clone();
+}
diff --git a/src/main/java/net/onrc/onos/packet/IPv4.java b/src/main/java/net/onrc/onos/packet/IPv4.java
new file mode 100644
index 0000000..9dc5ec2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/IPv4.java
@@ -0,0 +1,559 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+/**
+ * 
+ */
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ *
+ */
+public class IPv4 extends BasePacket {
+	public static final int ADDRESS_LENGTH = 4;
+    public static final byte PROTOCOL_ICMP = 0x1;
+    public static final byte PROTOCOL_TCP = 0x6;
+    public static final byte PROTOCOL_UDP = 0x11;
+    public static Map<Byte, Class<? extends IPacket>> protocolClassMap;
+
+    static {
+        protocolClassMap = new HashMap<Byte, Class<? extends IPacket>>();
+        protocolClassMap.put(PROTOCOL_ICMP, ICMP.class);
+        protocolClassMap.put(PROTOCOL_TCP, TCP.class);
+        protocolClassMap.put(PROTOCOL_UDP, UDP.class);
+    }
+
+    protected byte version;
+    protected byte headerLength;
+    protected byte diffServ;
+    protected short totalLength;
+    protected short identification;
+    protected byte flags;
+    protected short fragmentOffset;
+    protected byte ttl;
+    protected byte protocol;
+    protected short checksum;
+    protected int sourceAddress;
+    protected int destinationAddress;
+    protected byte[] options;
+
+    protected boolean isTruncated;
+
+    /**
+     * Default constructor that sets the version to 4.
+     */
+    public IPv4() {
+        super();
+        this.version = 4;
+        isTruncated = false;
+    }
+
+    /**
+     * @return the version
+     */
+    public byte getVersion() {
+        return version;
+    }
+
+    /**
+     * @param version the version to set
+     */
+    public IPv4 setVersion(byte version) {
+        this.version = version;
+        return this;
+    }
+
+    /**
+     * @return the headerLength
+     */
+    public byte getHeaderLength() {
+        return headerLength;
+    }
+
+    /**
+     * @return the diffServ
+     */
+    public byte getDiffServ() {
+        return diffServ;
+    }
+
+    /**
+     * @param diffServ the diffServ to set
+     */
+    public IPv4 setDiffServ(byte diffServ) {
+        this.diffServ = diffServ;
+        return this;
+    }
+
+    /**
+     * @return the totalLength
+     */
+    public short getTotalLength() {
+        return totalLength;
+    }
+
+    /**
+     * @return the identification
+     */
+    public short getIdentification() {
+        return identification;
+    }
+
+    public boolean isTruncated() {
+        return isTruncated;
+    }
+
+    public void setTruncated(boolean isTruncated) {
+        this.isTruncated = isTruncated;
+    }
+
+    /**
+     * @param identification the identification to set
+     */
+    public IPv4 setIdentification(short identification) {
+        this.identification = identification;
+        return this;
+    }
+
+    /**
+     * @return the flags
+     */
+    public byte getFlags() {
+        return flags;
+    }
+
+    /**
+     * @param flags the flags to set
+     */
+    public IPv4 setFlags(byte flags) {
+        this.flags = flags;
+        return this;
+    }
+
+    /**
+     * @return the fragmentOffset
+     */
+    public short getFragmentOffset() {
+        return fragmentOffset;
+    }
+
+    /**
+     * @param fragmentOffset the fragmentOffset to set
+     */
+    public IPv4 setFragmentOffset(short fragmentOffset) {
+        this.fragmentOffset = fragmentOffset;
+        return this;
+    }
+
+    /**
+     * @return the ttl
+     */
+    public byte getTtl() {
+        return ttl;
+    }
+
+    /**
+     * @param ttl the ttl to set
+     */
+    public IPv4 setTtl(byte ttl) {
+        this.ttl = ttl;
+        return this;
+    }
+
+    /**
+     * @return the protocol
+     */
+    public byte getProtocol() {
+        return protocol;
+    }
+
+    /**
+     * @param protocol the protocol to set
+     */
+    public IPv4 setProtocol(byte protocol) {
+        this.protocol = protocol;
+        return this;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return checksum;
+    }
+
+    /**
+     * @param checksum the checksum to set
+     */
+    public IPv4 setChecksum(short checksum) {
+        this.checksum = checksum;
+        return this;
+    }
+    @Override
+    public void resetChecksum() {
+        this.checksum = 0;
+        super.resetChecksum();
+    }
+
+    /**
+     * @return the sourceAddress
+     */
+    public int getSourceAddress() {
+        return sourceAddress;
+    }
+
+    /**
+     * @param sourceAddress the sourceAddress to set
+     */
+    public IPv4 setSourceAddress(int sourceAddress) {
+        this.sourceAddress = sourceAddress;
+        return this;
+    }
+
+    /**
+     * @param sourceAddress the sourceAddress to set
+     */
+    public IPv4 setSourceAddress(String sourceAddress) {
+        this.sourceAddress = IPv4.toIPv4Address(sourceAddress);
+        return this;
+    }
+
+    /**
+     * @return the destinationAddress
+     */
+    public int getDestinationAddress() {
+        return destinationAddress;
+    }
+
+    /**
+     * @param destinationAddress the destinationAddress to set
+     */
+    public IPv4 setDestinationAddress(int destinationAddress) {
+        this.destinationAddress = destinationAddress;
+        return this;
+    }
+
+    /**
+     * @param destinationAddress the destinationAddress to set
+     */
+    public IPv4 setDestinationAddress(String destinationAddress) {
+        this.destinationAddress = IPv4.toIPv4Address(destinationAddress);
+        return this;
+    }
+
+    /**
+     * @return the options
+     */
+    public byte[] getOptions() {
+        return options;
+    }
+
+    /**
+     * @param options the options to set
+     */
+    public IPv4 setOptions(byte[] options) {
+        if (options != null && (options.length % 4) > 0)
+            throw new IllegalArgumentException(
+                    "Options length must be a multiple of 4");
+        this.options = options;
+        return this;
+    }
+
+    /**
+     * Serializes the packet. Will compute and set the following fields if they
+     * are set to specific values at the time serialize is called:
+     *      -checksum : 0
+     *      -headerLength : 0
+     *      -totalLength : 0
+     */
+    public byte[] serialize() {
+        byte[] payloadData = null;
+        if (payload != null) {
+            payload.setParent(this);
+            payloadData = payload.serialize();
+        }
+
+        int optionsLength = 0;
+        if (this.options != null)
+            optionsLength = this.options.length / 4;
+        this.headerLength = (byte) (5 + optionsLength);
+
+        this.totalLength = (short) (this.headerLength * 4 + ((payloadData == null) ? 0
+                : payloadData.length));
+
+        byte[] data = new byte[this.totalLength];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+
+        bb.put((byte) (((this.version & 0xf) << 4) | (this.headerLength & 0xf)));
+        bb.put(this.diffServ);
+        bb.putShort(this.totalLength);
+        bb.putShort(this.identification);
+        bb.putShort((short) (((this.flags & 0x7) << 13) | (this.fragmentOffset & 0x1fff)));
+        bb.put(this.ttl);
+        bb.put(this.protocol);
+        bb.putShort(this.checksum);
+        bb.putInt(this.sourceAddress);
+        bb.putInt(this.destinationAddress);
+        if (this.options != null)
+            bb.put(this.options);
+        if (payloadData != null)
+            bb.put(payloadData);
+
+        // compute checksum if needed
+        if (this.checksum == 0) {
+            bb.rewind();
+            int accumulation = 0;
+            for (int i = 0; i < this.headerLength * 2; ++i) {
+                accumulation += 0xffff & bb.getShort();
+            }
+            accumulation = ((accumulation >> 16) & 0xffff)
+                    + (accumulation & 0xffff);
+            this.checksum = (short) (~accumulation & 0xffff);
+            bb.putShort(10, this.checksum);
+        }
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        short sscratch;
+
+        this.version = bb.get();
+        this.headerLength = (byte) (this.version & 0xf);
+        this.version = (byte) ((this.version >> 4) & 0xf);
+        this.diffServ = bb.get();
+        this.totalLength = bb.getShort();
+        this.identification = bb.getShort();
+        sscratch = bb.getShort();
+        this.flags = (byte) ((sscratch >> 13) & 0x7);
+        this.fragmentOffset = (short) (sscratch & 0x1fff);
+        this.ttl = bb.get();
+        this.protocol = bb.get();
+        this.checksum = bb.getShort();
+        this.sourceAddress = bb.getInt();
+        this.destinationAddress = bb.getInt();
+
+        if (this.headerLength > 5) {
+            int optionsLength = (this.headerLength - 5) * 4;
+            this.options = new byte[optionsLength];
+            bb.get(this.options);
+        }
+
+        IPacket payload;
+        if (IPv4.protocolClassMap.containsKey(this.protocol)) {
+            Class<? extends IPacket> clazz = IPv4.protocolClassMap.get(this.protocol);
+            try {
+                payload = clazz.newInstance();
+            } catch (Exception e) {
+                throw new RuntimeException("Error parsing payload for IPv4 packet", e);
+            }
+        } else {
+            payload = new Data();
+        }
+        this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
+        this.payload.setParent(this);
+
+        if (this.totalLength != length)
+            this.isTruncated = true;
+        else
+            this.isTruncated = false;
+
+        return this;
+    }
+
+    /**
+     * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
+     * returns the corresponding 32 bit integer.
+     * @param ipAddress
+     * @return
+     */
+    public static int toIPv4Address(String ipAddress) {
+        if (ipAddress == null)
+            throw new IllegalArgumentException("Specified IPv4 address must" +
+                "contain 4 sets of numerical digits separated by periods");
+        String[] octets = ipAddress.split("\\.");
+        if (octets.length != 4) 
+            throw new IllegalArgumentException("Specified IPv4 address must" +
+                "contain 4 sets of numerical digits separated by periods");
+
+        int result = 0;
+        for (int i = 0; i < 4; ++i) {
+            result |= Integer.valueOf(octets[i]) << ((3-i)*8);
+        }
+        return result;
+    }
+
+    /**
+     * Accepts an IPv4 address in a byte array and returns the corresponding
+     * 32-bit integer value.
+     * @param ipAddress
+     * @return
+     */
+    public static int toIPv4Address(byte[] ipAddress) {
+        int ip = 0;
+        for (int i = 0; i < 4; i++) {
+          int t = (ipAddress[i] & 0xff) << ((3-i)*8);
+          ip |= t;
+        }
+        return ip;
+    }
+
+    /**
+     * Accepts an IPv4 address and returns of string of the form xxx.xxx.xxx.xxx
+     * ie 192.168.0.1
+     * 
+     * @param ipAddress
+     * @return
+     */
+    public static String fromIPv4Address(int ipAddress) {
+        StringBuffer sb = new StringBuffer();
+        int result = 0;
+        for (int i = 0; i < 4; ++i) {
+            result = (ipAddress >> ((3-i)*8)) & 0xff;
+            sb.append(Integer.valueOf(result).toString());
+            if (i != 3)
+                sb.append(".");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Accepts a collection of IPv4 addresses as integers and returns a single
+     * String useful in toString method's containing collections of IP
+     * addresses.
+     * 
+     * @param ipAddresses collection
+     * @return
+     */
+    public static String fromIPv4AddressCollection(Collection<Integer> ipAddresses) {
+        if (ipAddresses == null)
+            return "null";
+        StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        for (Integer ip : ipAddresses) {
+            sb.append(fromIPv4Address(ip));
+            sb.append(",");
+        }
+        sb.replace(sb.length()-1, sb.length(), "]");
+        return sb.toString();
+    }
+
+    /**
+     * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
+     * returns the corresponding byte array.
+     * @param ipAddress The IP address in the form xx.xxx.xxx.xxx.
+     * @return The IP address separated into bytes
+     */
+    public static byte[] toIPv4AddressBytes(String ipAddress) {
+        String[] octets = ipAddress.split("\\.");
+        if (octets.length != 4) 
+            throw new IllegalArgumentException("Specified IPv4 address must" +
+                "contain 4 sets of numerical digits separated by periods");
+
+        byte[] result = new byte[4];
+        for (int i = 0; i < 4; ++i) {
+            result[i] = Integer.valueOf(octets[i]).byteValue();
+        }
+        return result;
+    }
+    
+    /**
+     * Accepts an IPv4 address in the form of an integer and
+     * returns the corresponding byte array.
+     * @param ipAddress The IP address as an integer.
+     * @return The IP address separated into bytes.
+     */
+    public static byte[] toIPv4AddressBytes(int ipAddress) {
+    	return new byte[] {
+                (byte)(ipAddress >>> 24),
+                (byte)(ipAddress >>> 16),
+                (byte)(ipAddress >>> 8),
+                (byte)ipAddress};
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 2521;
+        int result = super.hashCode();
+        result = prime * result + checksum;
+        result = prime * result + destinationAddress;
+        result = prime * result + diffServ;
+        result = prime * result + flags;
+        result = prime * result + fragmentOffset;
+        result = prime * result + headerLength;
+        result = prime * result + identification;
+        result = prime * result + Arrays.hashCode(options);
+        result = prime * result + protocol;
+        result = prime * result + sourceAddress;
+        result = prime * result + totalLength;
+        result = prime * result + ttl;
+        result = prime * result + version;
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof IPv4))
+            return false;
+        IPv4 other = (IPv4) obj;
+        if (checksum != other.checksum)
+            return false;
+        if (destinationAddress != other.destinationAddress)
+            return false;
+        if (diffServ != other.diffServ)
+            return false;
+        if (flags != other.flags)
+            return false;
+        if (fragmentOffset != other.fragmentOffset)
+            return false;
+        if (headerLength != other.headerLength)
+            return false;
+        if (identification != other.identification)
+            return false;
+        if (!Arrays.equals(options, other.options))
+            return false;
+        if (protocol != other.protocol)
+            return false;
+        if (sourceAddress != other.sourceAddress)
+            return false;
+        if (totalLength != other.totalLength)
+            return false;
+        if (ttl != other.ttl)
+            return false;
+        if (version != other.version)
+            return false;
+        return true;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/LLC.java b/src/main/java/net/onrc/onos/packet/LLC.java
new file mode 100644
index 0000000..7ff4f56
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/LLC.java
@@ -0,0 +1,75 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This class represents an Link Local Control
+ * header that is used in Ethernet 802.3.
+ * @author alexreimers
+ *
+ */
+public class LLC extends BasePacket {
+    private byte dsap = 0;
+    private byte ssap = 0;
+    private byte ctrl = 0;
+    
+    public byte getDsap() {
+        return dsap;
+    }
+
+    public void setDsap(byte dsap) {
+        this.dsap = dsap;
+    }
+
+    public byte getSsap() {
+        return ssap;
+    }
+
+    public void setSsap(byte ssap) {
+        this.ssap = ssap;
+    }
+
+    public byte getCtrl() {
+        return ctrl;
+    }
+
+    public void setCtrl(byte ctrl) {
+        this.ctrl = ctrl;
+    }
+
+    @Override
+    public byte[] serialize() {
+        byte[] data = new byte[3];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.put(dsap);
+        bb.put(ssap);
+        bb.put(ctrl);
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        dsap = bb.get();
+        ssap = bb.get();
+        ctrl = bb.get();
+        return this;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/LLDP.java b/src/main/java/net/onrc/onos/packet/LLDP.java
new file mode 100644
index 0000000..0e3cb2f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/LLDP.java
@@ -0,0 +1,204 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+/**
+ * 
+ */
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ *
+ */
+public class LLDP extends BasePacket {
+    protected LLDPTLV chassisId;
+    protected LLDPTLV portId;
+    protected LLDPTLV ttl;
+    protected List<LLDPTLV> optionalTLVList;
+    protected short ethType;
+
+    public LLDP() {
+        this.optionalTLVList = new ArrayList<LLDPTLV>();
+        this.ethType = Ethernet.TYPE_LLDP;
+    }
+
+    /**
+     * @return the chassisId
+     */
+    public LLDPTLV getChassisId() {
+        return chassisId;
+    }
+
+    /**
+     * @param chassisId the chassisId to set
+     */
+    public LLDP setChassisId(LLDPTLV chassisId) {
+        this.chassisId = chassisId;
+        return this;
+    }
+
+    /**
+     * @return the portId
+     */
+    public LLDPTLV getPortId() {
+        return portId;
+    }
+
+    /**
+     * @param portId the portId to set
+     */
+    public LLDP setPortId(LLDPTLV portId) {
+        this.portId = portId;
+        return this;
+    }
+
+    /**
+     * @return the ttl
+     */
+    public LLDPTLV getTtl() {
+        return ttl;
+    }
+
+    /**
+     * @param ttl the ttl to set
+     */
+    public LLDP setTtl(LLDPTLV ttl) {
+        this.ttl = ttl;
+        return this;
+    }
+
+    /**
+     * @return the optionalTLVList
+     */
+    public List<LLDPTLV> getOptionalTLVList() {
+        return optionalTLVList;
+    }
+
+    /**
+     * @param optionalTLVList the optionalTLVList to set
+     */
+    public LLDP setOptionalTLVList(List<LLDPTLV> optionalTLVList) {
+        this.optionalTLVList = optionalTLVList;
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        int length = 2+this.chassisId.getLength() + 2+this.portId.getLength() +
+            2+this.ttl.getLength() + 2;
+        for (LLDPTLV tlv : this.optionalTLVList) {
+            length += 2 + tlv.getLength();
+        }
+
+        byte[] data = new byte[length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.put(this.chassisId.serialize());
+        bb.put(this.portId.serialize());
+        bb.put(this.ttl.serialize());
+        for (LLDPTLV tlv : this.optionalTLVList) {
+            bb.put(tlv.serialize());
+        }
+        bb.putShort((short) 0); // End of LLDPDU
+
+        if (this.parent != null && this.parent instanceof Ethernet)
+            ((Ethernet)this.parent).setEtherType(ethType);
+
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        LLDPTLV tlv;
+        do {
+            tlv = new LLDPTLV().deserialize(bb);
+
+            // if there was a failure to deserialize stop processing TLVs
+            if (tlv == null)
+                break;
+            switch (tlv.getType()) {
+                case 0x0:
+                    // can throw this one away, its just an end delimiter
+                    break;
+                case 0x1:
+                    this.chassisId = tlv;
+                    break;
+                case 0x2:
+                    this.portId = tlv;
+                    break;
+                case 0x3:
+                    this.ttl = tlv;
+                    break;
+                default:
+                    this.optionalTLVList.add(tlv);
+                    break;
+            }
+        } while (tlv.getType() != 0 && bb.hasRemaining());
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 883;
+        int result = super.hashCode();
+        result = prime * result
+                + ((chassisId == null) ? 0 : chassisId.hashCode());
+        result = prime * result + (optionalTLVList.hashCode());
+        result = prime * result + ((portId == null) ? 0 : portId.hashCode());
+        result = prime * result + ((ttl == null) ? 0 : ttl.hashCode());
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof LLDP))
+            return false;
+        LLDP other = (LLDP) obj;
+        if (chassisId == null) {
+            if (other.chassisId != null)
+                return false;
+        } else if (!chassisId.equals(other.chassisId))
+            return false;
+        if (!optionalTLVList.equals(other.optionalTLVList))
+            return false;
+        if (portId == null) {
+            if (other.portId != null)
+                return false;
+        } else if (!portId.equals(other.portId))
+            return false;
+        if (ttl == null) {
+            if (other.ttl != null)
+                return false;
+        } else if (!ttl.equals(other.ttl))
+            return false;
+        return true;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/LLDPOrganizationalTLV.java b/src/main/java/net/onrc/onos/packet/LLDPOrganizationalTLV.java
new file mode 100644
index 0000000..754875c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/LLDPOrganizationalTLV.java
@@ -0,0 +1,181 @@
+/**
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/**
+ * The class representing LLDP Organizationally Specific TLV.
+ *
+ * @author Sho Shimizu (sho.shimizu@gmail.com)
+ */
+public class LLDPOrganizationalTLV extends LLDPTLV {
+    public static final int OUI_LENGTH = 3;
+    public static final int SUBTYPE_LENGTH = 1;
+    public static final byte ORGANIZATIONAL_TLV_TYPE = 127;
+    public static final int MAX_INFOSTRING_LENGTH = 507;
+
+    protected byte[] oui;
+    protected byte subType;
+    private byte[] infoString;
+
+    public LLDPOrganizationalTLV() {
+        type = ORGANIZATIONAL_TLV_TYPE;
+    }
+
+    /**
+     * Set the value of OUI.
+     * @param oui The value of OUI to be set.
+     * @return This LLDP Organizationally Specific TLV.
+     */
+    public LLDPOrganizationalTLV setOUI(byte[] oui) {
+        if (oui.length != OUI_LENGTH) {
+            throw new IllegalArgumentException("The length of OUI must be " + OUI_LENGTH +
+                ", but it is " + oui.length);
+        }
+        this.oui = Arrays.copyOf(oui, oui.length);
+        return this;
+    }
+
+    /**
+     * Returns the value of the OUI.
+     * @return The value of the OUI .
+     */
+    public byte[] getOUI() {
+        return Arrays.copyOf(oui, oui.length);
+    }
+
+    /**
+     * Set the value of sub type.
+     * @param subType The value of sub type to be set.
+     * @return This LLDP Organizationally Specific TLV.
+     */
+    public LLDPOrganizationalTLV setSubType(byte subType) {
+        this.subType = subType;
+        return this;
+    }
+
+    /**
+     * Returns the value of the sub type.
+     * @return The value of the sub type.
+     */
+    public byte getSubType() {
+        return subType;
+    }
+
+    /**
+     * Set the value of information string.
+     * @param infoString the byte array of the value of information string.
+     * @return This LLDP Organizationally Specific TLV.
+     */
+    public LLDPOrganizationalTLV setInfoString(byte[] infoString) {
+        if (infoString.length > MAX_INFOSTRING_LENGTH) {
+            throw new IllegalArgumentException("The length of infoString cannot exceed " + MAX_INFOSTRING_LENGTH);
+        }
+        this.infoString = Arrays.copyOf(infoString, infoString.length);
+        return this;
+    }
+
+    /**
+     * Set the value of information string.
+     * The String value is automatically converted into byte array with UTF-8 encoding.
+     * @param infoString the String value of information string.
+     * @return This LLDP Organizationally Specific TLV.
+     */
+    public LLDPOrganizationalTLV setInfoString(String infoString) {
+        byte[] infoStringBytes = infoString.getBytes(Charset.forName("UTF-8"));
+        return setInfoString(infoStringBytes);
+    }
+
+    /**
+     * Returns the value of information string.
+     * @return the value of information string.
+     */
+    public byte[] getInfoString() {
+        return Arrays.copyOf(infoString, infoString.length);
+    }
+
+    @Override
+    public byte[] serialize() {
+        int valueLength = OUI_LENGTH + SUBTYPE_LENGTH + infoString.length;
+        value = new byte[valueLength];
+        ByteBuffer bb = ByteBuffer.wrap(value);
+        bb.put(oui);
+        bb.put(subType);
+        bb.put(infoString);
+        return super.serialize();
+    }
+
+    @Override
+    public LLDPTLV deserialize(ByteBuffer bb) {
+        super.deserialize(bb);
+        ByteBuffer optionalField = ByteBuffer.wrap(value);
+
+        byte[] oui = new byte[OUI_LENGTH];
+        optionalField.get(oui);
+        setOUI(oui);
+
+        setSubType(optionalField.get());
+
+        byte[] infoString = new byte[getLength() - OUI_LENGTH - SUBTYPE_LENGTH];
+        optionalField.get(infoString);
+        setInfoString(infoString);
+        return this;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 1423;
+        int result = 1;
+        result = prime * result + type;
+        result = prime * result + length;
+        result = prime * result + Arrays.hashCode(oui);
+        result = prime * result + subType;
+        result = prime * result + Arrays.hashCode(infoString);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof LLDPOrganizationalTLV)) {
+            return false;
+        }
+
+        LLDPOrganizationalTLV other = (LLDPOrganizationalTLV)o;
+        if (this.type != other.type) {
+            return false;
+        }
+        if (this.length != other.length) {
+            return false;
+        }
+        if (!Arrays.equals(this.oui, other.oui)) {
+            return false;
+        }
+        if (this.subType != other.subType) {
+            return false;
+        }
+        if (!Arrays.equals(this.infoString, other.infoString)) {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/LLDPTLV.java b/src/main/java/net/onrc/onos/packet/LLDPTLV.java
new file mode 100644
index 0000000..fe361cb
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/LLDPTLV.java
@@ -0,0 +1,140 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ *
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class LLDPTLV {
+    protected byte type;
+    protected short length;
+    protected byte[] value;
+
+    /**
+     * @return the type
+     */
+    public byte getType() {
+        return type;
+    }
+
+    /**
+     * @param type the type to set
+     */
+    public LLDPTLV setType(byte type) {
+        this.type = type;
+        return this;
+    }
+
+    /**
+     * @return the length
+     */
+    public short getLength() {
+        return length;
+    }
+
+    /**
+     * @param length the length to set
+     */
+    public LLDPTLV setLength(short length) {
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * @return the value
+     */
+    public byte[] getValue() {
+        return value;
+    }
+
+    /**
+     * @param value the value to set
+     */
+    public LLDPTLV setValue(byte[] value) {
+        this.value = value;
+        return this;
+    }
+
+    public byte[] serialize() {
+        // type = 7 bits
+        // info string length 9 bits, each value == byte
+        // info string
+        short scratch = (short) (((0x7f & this.type) << 9) | (0x1ff & this.length));
+        byte[] data = new byte[2+this.length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.putShort(scratch);
+        if (this.value != null)
+            bb.put(this.value);
+        return data;
+    }
+
+    public LLDPTLV deserialize(ByteBuffer bb) {
+        short sscratch;
+        sscratch = bb.getShort();
+        this.type = (byte) ((sscratch >> 9) & 0x7f);
+        this.length = (short) (sscratch & 0x1ff);
+        if (this.length > 0) {
+            this.value = new byte[this.length];
+
+            // if there is an underrun just toss the TLV
+            if (bb.remaining() < this.length)
+                return null;
+            bb.get(this.value);
+        }
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 1423;
+        int result = 1;
+        result = prime * result + length;
+        result = prime * result + type;
+        result = prime * result + Arrays.hashCode(value);
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof LLDPTLV))
+            return false;
+        LLDPTLV other = (LLDPTLV) obj;
+        if (length != other.length)
+            return false;
+        if (type != other.type)
+            return false;
+        if (!Arrays.equals(value, other.value))
+            return false;
+        return true;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/TCP.java b/src/main/java/net/onrc/onos/packet/TCP.java
new file mode 100644
index 0000000..505ada2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/TCP.java
@@ -0,0 +1,290 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+
+/**
+ *
+ * @author shudong.zhou@bigswitch.com
+ */
+public class TCP extends BasePacket {
+    protected short sourcePort;
+    protected short destinationPort;
+    protected int sequence;
+    protected int acknowledge;
+    protected byte dataOffset;
+    protected short flags;
+    protected short windowSize;
+    protected short checksum;
+    protected short urgentPointer;
+    protected byte[] options;
+
+    /**
+     * @return the sourcePort
+     */
+    public short getSourcePort() {
+        return sourcePort;
+    }
+
+    /**
+     * @param sourcePort the sourcePort to set
+     */
+    public TCP setSourcePort(short sourcePort) {
+        this.sourcePort = sourcePort;
+        return this;
+    }
+
+    /**
+     * @return the destinationPort
+     */
+    public short getDestinationPort() {
+        return destinationPort;
+    }
+
+    /**
+     * @param destinationPort the destinationPort to set
+     */
+    public TCP setDestinationPort(short destinationPort) {
+        this.destinationPort = destinationPort;
+        return this;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return checksum;
+    }
+    
+    public int getSequence() {
+        return this.sequence;
+    }
+    public TCP setSequence(int seq) {
+        this.sequence = seq;
+        return this;
+    }
+    public int getAcknowledge() {
+        return this.acknowledge;
+    }
+    public TCP setAcknowledge(int ack) {
+        this.acknowledge = ack;
+        return this;
+    }
+    public byte getDataOffset() {
+        return this.dataOffset;
+    }
+    public TCP setDataOffset(byte offset) {
+        this.dataOffset = offset;
+        return this;
+    }
+    public short getFlags() {
+        return this.flags;
+    }
+    public TCP setFlags(short flags) {
+        this.flags = flags;
+        return this;
+    }
+    public short getWindowSize() {
+        return this.windowSize;
+    }
+    public TCP setWindowSize(short windowSize) {
+        this.windowSize = windowSize;
+        return this;
+    }
+    public short getTcpChecksum() {
+        return this.checksum;
+    }
+    public TCP setTcpChecksum(short checksum) {
+        this.checksum = checksum;
+        return this;
+    }
+    
+    @Override
+    public void resetChecksum() {
+        this.checksum = 0;
+        super.resetChecksum();
+    }
+    
+    public short getUrgentPointer(short urgentPointer) {
+        return this.urgentPointer;
+    }
+    public TCP setUrgentPointer(short urgentPointer) {
+        this.urgentPointer= urgentPointer;
+        return this;
+    }
+    public byte[] getOptions() {
+        return this.options;
+    }
+    public TCP setOptions(byte[] options) {
+        this.options = options;
+        this.dataOffset = (byte) ((20 + options.length + 3) >> 2);
+        return this;
+    }
+    /**
+     * @param checksum the checksum to set
+     */
+    public TCP setChecksum(short checksum) {
+        this.checksum = checksum;
+        return this;
+    }
+
+    /**
+     * Serializes the packet. Will compute and set the following fields if they
+     * are set to specific values at the time serialize is called:
+     *      -checksum : 0
+     *      -length : 0
+     */
+    public byte[] serialize() {
+        int length;
+        if (dataOffset == 0)
+            dataOffset = 5;  // default header length
+        length = dataOffset << 2;
+        byte[] payloadData = null;
+        if (payload != null) {
+            payload.setParent(this);
+            payloadData = payload.serialize();
+            length += payloadData.length;
+        }
+
+        byte[] data = new byte[length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+
+        bb.putShort(this.sourcePort);
+        bb.putShort(this.destinationPort);
+        bb.putInt(this.sequence);
+        bb.putInt(this.acknowledge);
+        bb.putShort((short) (this.flags | (dataOffset << 12)));
+        bb.putShort(this.windowSize);
+        bb.putShort(this.checksum);
+        bb.putShort(this.urgentPointer);
+        if (dataOffset > 5) {
+            int padding;
+            bb.put(options);
+            padding = (dataOffset << 2) - 20 - options.length;
+            for (int i = 0; i < padding; i++)
+                bb.put((byte) 0);
+        }
+        if (payloadData != null)
+            bb.put(payloadData);
+
+        if (this.parent != null && this.parent instanceof IPv4)
+            ((IPv4)this.parent).setProtocol(IPv4.PROTOCOL_TCP);
+
+        // compute checksum if needed
+        if (this.checksum == 0) {
+            bb.rewind();
+            int accumulation = 0;
+
+            // compute pseudo header mac
+            if (this.parent != null && this.parent instanceof IPv4) {
+                IPv4 ipv4 = (IPv4) this.parent;
+                accumulation += ((ipv4.getSourceAddress() >> 16) & 0xffff)
+                        + (ipv4.getSourceAddress() & 0xffff);
+                accumulation += ((ipv4.getDestinationAddress() >> 16) & 0xffff)
+                        + (ipv4.getDestinationAddress() & 0xffff);
+                accumulation += ipv4.getProtocol() & 0xff;
+                accumulation += length & 0xffff;
+            }
+
+            for (int i = 0; i < length / 2; ++i) {
+                accumulation += 0xffff & bb.getShort();
+            }
+            // pad to an even number of shorts
+            if (length % 2 > 0) {
+                accumulation += (bb.get() & 0xff) << 8;
+            }
+
+            accumulation = ((accumulation >> 16) & 0xffff)
+                    + (accumulation & 0xffff);
+            this.checksum = (short) (~accumulation & 0xffff);
+            bb.putShort(16, this.checksum);
+        }
+        return data;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 5807;
+        int result = super.hashCode();
+        result = prime * result + checksum;
+        result = prime * result + destinationPort;
+        result = prime * result + sourcePort;
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof TCP))
+            return false;
+        TCP other = (TCP) obj;
+        // May want to compare fields based on the flags set
+        return (checksum == other.checksum) &&
+               (destinationPort == other.destinationPort) &&
+               (sourcePort == other.sourcePort) &&
+               (sequence == other.sequence) &&
+               (acknowledge == other.acknowledge) &&
+               (dataOffset == other.dataOffset) &&
+               (flags == other.flags) &&
+               (windowSize == other.windowSize) &&
+               (urgentPointer == other.urgentPointer) &&
+               (dataOffset == 5 || options.equals(other.options));
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        this.sourcePort = bb.getShort();
+        this.destinationPort = bb.getShort();
+        this.sequence = bb.getInt();
+        this.acknowledge = bb.getInt();
+        this.flags = bb.getShort();
+        this.dataOffset = (byte) ((this.flags >> 12) & 0xf);
+        this.flags = (short) (this.flags & 0x1ff);
+        this.windowSize = bb.getShort();
+        this.checksum = bb.getShort();
+        this.urgentPointer = bb.getShort();
+        if (this.dataOffset > 5) {
+            int optLength = (dataOffset << 2) - 20;
+            if (bb.limit() < bb.position()+optLength) {
+                optLength = bb.limit() - bb.position();
+            }
+            try {
+                this.options = new byte[optLength];
+                bb.get(this.options, 0, optLength);
+            } catch (IndexOutOfBoundsException e) {
+                this.options = null;
+            }
+        }
+        
+        this.payload = new Data();
+        this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
+        this.payload.setParent(this);
+        return this;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/packet/UDP.java b/src/main/java/net/onrc/onos/packet/UDP.java
new file mode 100644
index 0000000..88ef883
--- /dev/null
+++ b/src/main/java/net/onrc/onos/packet/UDP.java
@@ -0,0 +1,231 @@
+/**
+*    Copyright 2011, Big Switch Networks, Inc. 
+*    Originally created by David Erickson, Stanford University
+* 
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package net.onrc.onos.packet;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class UDP extends BasePacket {
+    public static Map<Short, Class<? extends IPacket>> decodeMap;
+    public static short DHCP_SERVER_PORT = (short)67;
+    public static short DHCP_CLIENT_PORT = (short)68;
+
+    static {
+        decodeMap = new HashMap<Short, Class<? extends IPacket>>();
+        /*
+         * Disable DHCP until the deserialize code is hardened to deal with garbage input
+         */
+        UDP.decodeMap.put(DHCP_SERVER_PORT, DHCP.class);
+        UDP.decodeMap.put(DHCP_CLIENT_PORT, DHCP.class);
+        
+    }
+
+    protected short sourcePort;
+    protected short destinationPort;
+    protected short length;
+    protected short checksum;
+
+    /**
+     * @return the sourcePort
+     */
+    public short getSourcePort() {
+        return sourcePort;
+    }
+
+    /**
+     * @param sourcePort the sourcePort to set
+     */
+    public UDP setSourcePort(short sourcePort) {
+        this.sourcePort = sourcePort;
+        return this;
+    }
+
+    /**
+     * @return the destinationPort
+     */
+    public short getDestinationPort() {
+        return destinationPort;
+    }
+
+    /**
+     * @param destinationPort the destinationPort to set
+     */
+    public UDP setDestinationPort(short destinationPort) {
+        this.destinationPort = destinationPort;
+        return this;
+    }
+
+    /**
+     * @return the length
+     */
+    public short getLength() {
+        return length;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return checksum;
+    }
+
+    /**
+     * @param checksum the checksum to set
+     */
+    public UDP setChecksum(short checksum) {
+        this.checksum = checksum;
+        return this;
+    }
+
+    @Override
+    public void resetChecksum() {
+        this.checksum = 0;
+        super.resetChecksum();
+    }
+
+    /**
+     * Serializes the packet. Will compute and set the following fields if they
+     * are set to specific values at the time serialize is called:
+     *      -checksum : 0
+     *      -length : 0
+     */
+    public byte[] serialize() {
+        byte[] payloadData = null;
+        if (payload != null) {
+            payload.setParent(this);
+            payloadData = payload.serialize();
+        }
+
+        this.length = (short) (8 + ((payloadData == null) ? 0
+                : payloadData.length));
+
+        byte[] data = new byte[this.length];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+
+        bb.putShort(this.sourcePort);
+        bb.putShort(this.destinationPort);
+        bb.putShort(this.length);
+        bb.putShort(this.checksum);
+        if (payloadData != null)
+            bb.put(payloadData);
+
+        if (this.parent != null && this.parent instanceof IPv4)
+            ((IPv4)this.parent).setProtocol(IPv4.PROTOCOL_UDP);
+
+        // compute checksum if needed
+        if (this.checksum == 0) {
+            bb.rewind();
+            int accumulation = 0;
+
+            // compute pseudo header mac
+            if (this.parent != null && this.parent instanceof IPv4) {
+                IPv4 ipv4 = (IPv4) this.parent;
+                accumulation += ((ipv4.getSourceAddress() >> 16) & 0xffff)
+                        + (ipv4.getSourceAddress() & 0xffff);
+                accumulation += ((ipv4.getDestinationAddress() >> 16) & 0xffff)
+                        + (ipv4.getDestinationAddress() & 0xffff);
+                accumulation += ipv4.getProtocol() & 0xff;
+                accumulation += this.length & 0xffff;
+            }
+
+            for (int i = 0; i < this.length / 2; ++i) {
+                accumulation += 0xffff & bb.getShort();
+            }
+            // pad to an even number of shorts
+            if (this.length % 2 > 0) {
+                accumulation += (bb.get() & 0xff) << 8;
+            }
+
+            accumulation = ((accumulation >> 16) & 0xffff)
+                    + (accumulation & 0xffff);
+            this.checksum = (short) (~accumulation & 0xffff);
+            bb.putShort(6, this.checksum);
+        }
+        return data;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 5807;
+        int result = super.hashCode();
+        result = prime * result + checksum;
+        result = prime * result + destinationPort;
+        result = prime * result + length;
+        result = prime * result + sourcePort;
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof UDP))
+            return false;
+        UDP other = (UDP) obj;
+        if (checksum != other.checksum)
+            return false;
+        if (destinationPort != other.destinationPort)
+            return false;
+        if (length != other.length)
+            return false;
+        if (sourcePort != other.sourcePort)
+            return false;
+        return true;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length) {
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        this.sourcePort = bb.getShort();
+        this.destinationPort = bb.getShort();
+        this.length = bb.getShort();
+        this.checksum = bb.getShort();
+
+        if (UDP.decodeMap.containsKey(this.destinationPort)) {
+            try {
+                this.payload = UDP.decodeMap.get(this.destinationPort).getConstructor().newInstance();
+            } catch (Exception e) {
+                throw new RuntimeException("Failure instantiating class", e);
+            }
+        } else if (UDP.decodeMap.containsKey(this.sourcePort)) {
+            try {
+                this.payload = UDP.decodeMap.get(this.sourcePort).getConstructor().newInstance();
+            } catch (Exception e) {
+                throw new RuntimeException("Failure instantiating class", e);
+            }
+        } else {
+            this.payload = new Data();
+        }
+        this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
+        this.payload.setParent(this);
+        return this;
+    }
+}