adding packet types
diff --git a/net/api/pom.xml b/net/api/pom.xml
index 79737d3..d542a97 100644
--- a/net/api/pom.xml
+++ b/net/api/pom.xml
@@ -21,6 +21,10 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava-testlib</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.onlab.onos</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/of/api/pom.xml b/of/api/pom.xml
index 050fedd..7a1619e 100644
--- a/of/api/pom.xml
+++ b/of/api/pom.xml
@@ -56,7 +56,6 @@
                     </execution>
                 </executions>
             </plugin>
-
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/DefaultPacketContext.java b/of/api/src/main/java/org/onlab/onos/of/controller/DefaultPacketContext.java
new file mode 100644
index 0000000..988b809
--- /dev/null
+++ b/of/api/src/main/java/org/onlab/onos/of/controller/DefaultPacketContext.java
@@ -0,0 +1,62 @@
+package org.onlab.onos.of.controller;
+
+import org.onlab.packet.Ethernet;
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPacketOut;
+import org.projectfloodlight.openflow.types.OFPort;
+
+public class DefaultPacketContext implements PacketContext {
+
+    private boolean free = true;
+    private boolean isBuilt = false;
+    private final OpenFlowSwitch sw;
+    private final OFPacketIn pktin;
+    private final OFPacketOut pktout = null;
+
+    private DefaultPacketContext(OpenFlowSwitch s, OFPacketIn pkt) {
+        this.sw = s;
+        this.pktin = pkt;
+    }
+
+    @Override
+    public void block() {
+        free = false;
+    }
+
+    @Override
+    public void send() {
+        if (free && isBuilt) {
+            sw.sendMsg(pktout);
+        }
+
+    }
+
+    @Override
+    public void build(OFPort outPort) {
+        isBuilt = true;
+
+    }
+
+    @Override
+    public void build(Ethernet ethFrame, OFPort outPort) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public Ethernet parsed() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Dpid dpid() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public static PacketContext PacketContextFromPacketIn(OpenFlowSwitch s, OFPacketIn pkt) {
+        return new DefaultPacketContext(s, pkt);
+    }
+
+}
diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowController.java b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowController.java
index 5aec885..ab80eee 100644
--- a/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowController.java
+++ b/of/api/src/main/java/org/onlab/onos/of/controller/OpenFlowController.java
@@ -87,9 +87,10 @@
     /**
      * Process a message and notify the appropriate listeners.
      *
+     * @param dpid the dpid the message arrived on
      * @param msg the message to process.
      */
-    public void processPacket(OFMessage msg);
+    public void processPacket(Dpid dpid, OFMessage msg);
 
     /**
      * Sets the role for a given switch.
diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/PacketContext.java b/of/api/src/main/java/org/onlab/onos/of/controller/PacketContext.java
index bc06e93..f7d434d 100644
--- a/of/api/src/main/java/org/onlab/onos/of/controller/PacketContext.java
+++ b/of/api/src/main/java/org/onlab/onos/of/controller/PacketContext.java
@@ -1,5 +1,6 @@
 package org.onlab.onos.of.controller;
 
+import org.onlab.packet.Ethernet;
 import org.projectfloodlight.openflow.types.OFPort;
 
 /**
@@ -34,13 +35,13 @@
      * @param ethFrame the actual packet to send out.
      * @param outPort the out port to send to packet out of.
      */
-    public void build(Object ethFrame, OFPort outPort);
+    public void build(Ethernet ethFrame, OFPort outPort);
 
     /**
      * Provided a handle onto the parsed payload.
      * @return the parsed form of the payload.
      */
-    public Object parsed();
+    public Ethernet parsed();
 
     /**
      * Provide the dpid of the switch where the packet in arrived.
diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/driver/AbstractOpenFlowSwitch.java b/of/api/src/main/java/org/onlab/onos/of/controller/driver/AbstractOpenFlowSwitch.java
index 0a83360..3c573d3 100644
--- a/of/api/src/main/java/org/onlab/onos/of/controller/driver/AbstractOpenFlowSwitch.java
+++ b/of/api/src/main/java/org/onlab/onos/of/controller/driver/AbstractOpenFlowSwitch.java
@@ -159,7 +159,7 @@
      */
     @Override
     public final void handleMessage(OFMessage m) {
-        this.agent.processMessage(m);
+        this.agent.processMessage(dpid, m);
     }
 
     @Override
diff --git a/of/api/src/main/java/org/onlab/onos/of/controller/driver/OpenFlowAgent.java b/of/api/src/main/java/org/onlab/onos/of/controller/driver/OpenFlowAgent.java
index a0f64e2..0d37666 100644
--- a/of/api/src/main/java/org/onlab/onos/of/controller/driver/OpenFlowAgent.java
+++ b/of/api/src/main/java/org/onlab/onos/of/controller/driver/OpenFlowAgent.java
@@ -69,7 +69,9 @@
 
     /**
      * Process a message coming from a switch.
+     *
+     * @param dpid the dpid the message came on.
      * @param m the message to process
      */
-    public void processMessage(OFMessage m);
+    public void processMessage(Dpid dpid, OFMessage m);
 }
diff --git a/of/ctl/conf/checkstyle/checkstyle_maven.properties b/of/ctl/conf/checkstyle/checkstyle_maven.properties
deleted file mode 100644
index 4677e08..0000000
--- a/of/ctl/conf/checkstyle/checkstyle_maven.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-# See: http://rolf-engelhard.de/2011/04/using-the-same-suppression-filter-for-checkstyle-in-eclipse-and-maven/
-config_loc=conf/checkstyle
diff --git a/of/ctl/conf/checkstyle/sun_checks.xml b/of/ctl/conf/checkstyle/sun_checks.xml
deleted file mode 100644
index b1404c1..0000000
--- a/of/ctl/conf/checkstyle/sun_checks.xml
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE module PUBLIC
-        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
-        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
-
-
-<!--
-
-  Checkstyle configuration that checks the sun coding conventions from:
-
-    - the Java Language Specification at
-      http://java.sun.com/docs/books/jls/second_edition/html/index.html
-
-    - the Sun Code Conventions at http://java.sun.com/docs/codeconv/
-
-    - the Javadoc guidelines at
-      http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
-
-    - the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
-
-    - some best practices
-
-  Checkstyle is very configurable. Be sure to read the documentation at
-  http://checkstyle.sf.net (or in your downloaded distribution).
-
-  Most Checks are configurable, be sure to consult the documentation.
-
-  To completely disable a check, just comment it out or delete it from the file.
-
-  Finally, it is worth reading the documentation.
-
--->
-
-
-<!--
-   The default severity setting in checkstyle is 'error', so some
-   of the rules below are configured to change the severity to
-   'warning'.  Over time, these 'warning' settings should be 
-   removed as more of the ONOS source code is modified to
-   follow the recommended rules.
--->
-
-
-
-<module name="Checker">
-    <module name="SuppressionFilter">
-      <property name="file" value="${config_loc}/suppressions.xml"/>
-    </module>
-    <!--
-        If you set the basedir property below, then all reported file
-        names will be relative to the specified directory. See
-        http://checkstyle.sourceforge.net/5.x/config.html#Checker
-
-        <property name="basedir" value="${basedir}"/>
-    -->
-    <!-- Checks that a package-info.java file exists for each package.     -->
-    <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
-    <!-- ONOS does not currently supply package level Javadoc information
-         in package-info files -->
-    <!-- <module name="JavadocPackage"/> -->
-
-    <!-- Checks whether files end with a new line.                        -->
-    <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
-    <module name="NewlineAtEndOfFile"/>
-
-    <!-- Checks that property files contain the same keys.         -->
-    <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
-    <module name="Translation"/>
-
-    <!-- Checks for Size Violations.                    -->
-    <!-- See http://checkstyle.sf.net/config_sizes.html -->
-    <module name="FileLength">
-        <property name="max" value="2500"/>
-    </module>
-
-    <!-- Checks for whitespace                               -->
-    <!-- See http://checkstyle.sf.net/config_whitespace.html -->
-    <module name="FileTabCharacter"/>
-
-    <!-- Miscellaneous other checks.                   -->
-    <!-- See http://checkstyle.sf.net/config_misc.html -->
-    <module name="RegexpSingleline">
-        <property name="format" value="\s+$"/>
-        <property name="minimum" value="0"/>
-        <property name="maximum" value="0"/>
-        <property name="message" value="Line has trailing spaces."/>
-    </module>
-
-    <!-- Checks for Headers                                -->
-    <!-- See http://checkstyle.sf.net/config_header.html   -->
-    <!-- <module name="Header"> -->
-    <!--   <property name="headerFile" value="${checkstyle.header.file}"/> -->
-    <!--   <property name="fileExtensions" value="java"/> -->
-    <!-- </module> -->
-
-    <module name="SuppressionCommentFilter">
-        <property name="offCommentFormat" value="(CHECKSTYLE\:OFF|Generated by the protocol buffer compiler.)"/>
-        <property name="onCommentFormat" value="CHECKSTYLE:ON"/>
-    </module>
-
-    <module name="SuppressWithNearbyCommentFilter">
-        <property name="commentFormat" value="CHECKSTYLE IGNORE THIS LINE" />
-        <property name="checkFormat" value=".*" />
-        <property name="influenceFormat" value="0" />
-    </module>
-
-    <!-- Example: // CHECKSTYLE IGNORE FinalClass FOR NEXT 1 LINES  -->
-    <module name="SuppressWithNearbyCommentFilter">
-        <property name="commentFormat" value="CHECKSTYLE IGNORE (\w+) FOR NEXT (\d+) LINES"/>
-        <property name="checkFormat" value="$1"/>
-        <property name="influenceFormat" value="$2"/>
-    </module>
-
-    <module name="TreeWalker">
-
-        <module name="FileContentsHolder"/>
-        <!-- Checks for Javadoc comments.                     -->
-        <!-- See http://checkstyle.sf.net/config_javadoc.html -->
-        <module name="JavadocMethod">
-            <property name="severity" value="warning"/>
-            <property name="allowUndeclaredRTE" value="true"/>
-        </module>
-        <module name="JavadocType">
-            <property name="severity" value="warning"/>
-        </module>
-        <module name="JavadocVariable">
-            <!-- Suppress check for private member Javadocs.
-             Possibly revist fixing these. -->
-            <property name="scope" value="public"/>
-            <property name="severity" value="warning"/>
-        </module>
-        <module name="JavadocStyle"/>
-	<!-- @author tag should not be used -->
-        <module name="WriteTag">
-            <property name="tag" value="@author"/>
-            <property name="tagFormat" value="\S"/>
-            <property name="severity" value="ignore"/>
-            <property name="tagSeverity" value="error"/>
-        </module>
-
-
-        <!-- Checks for Naming Conventions.                  -->
-        <!-- See http://checkstyle.sf.net/config_naming.html -->
-        <module name="ConstantName">
-            <!--  ONOS allows the name "log" for static final Loggers -->
-            <property name="format"
-                      value="^log$|^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"/>
-        </module>
-        <module name="LocalFinalVariableName"/>
-
-        <module name="LocalVariableName"/>
-
-        <module name="MemberName"/>
-        <module name="MethodName"/>
-        <module name="PackageName"/>
-        <module name="ParameterName"/>
-        <module name="StaticVariableName"/>
-        <module name="TypeName"/>
-
-        <!-- Checks for imports                              -->
-        <!-- See http://checkstyle.sf.net/config_import.html -->
-        <module name="AvoidStarImport">
-          <property name="allowStaticMemberImports" value="true"/>
-        </module>
-        <module name="IllegalImport"/>
-        <!-- defaults to sun.* packages -->
-        <module name="RedundantImport"/>
-        <module name="UnusedImports"/>
-
-
-        <!-- Checks for Size Violations.                    -->
-        <!-- See http://checkstyle.sf.net/config_sizes.html -->
-        <module name="LineLength">
-            <!-- ONOS standard usage is 80 columns, but we allow up
-             to 120 to not break the build. -->
-            <property name="max" value="120"/>
-            <property name="ignorePattern" value="^import"/>
-        </module>
-        <module name="MethodLength">
-            <property name="max" value="400"/>
-        </module>
-
-        <module name="ParameterNumber"/>
-
-        <!-- Checks for whitespace                               -->
-        <!-- See http://checkstyle.sf.net/config_whitespace.html -->
-        <module name="EmptyForIteratorPad"/>
-        <module name="GenericWhitespace"/>
-        <module name="MethodParamPad"/>
-        <module name="NoWhitespaceAfter"/>
-        <module name="NoWhitespaceBefore"/>
-
-        <!-- Disabled for ONOS.  Default rules specify undesired behavior for the '?' operator -->
-        <!-- <module name="OperatorWrap"/> -->
-        <module name="ParenPad"/>
-        <module name="TypecastParenPad"/>
-        <module name="WhitespaceAfter"/>
-        <module name="WhitespaceAround">
-            <property name="allowEmptyConstructors" value="true"/>
-            <property name="allowEmptyMethods" value="true"/>
-        </module>
-
-
-
-        <!-- Modifier Checks                                    -->
-        <!-- See http://checkstyle.sf.net/config_modifiers.html -->
-        <module name="ModifierOrder"/>
-
-        <!--  Disabled for ONOS to allow use of public          -->
-        <!--  modifiers in interfaces.                          -->
-        <!-- <module name="RedundantModifier"/>                 -->
-
-
-        <!-- Checks for blocks. You know, those {}'s         -->
-        <!-- See http://checkstyle.sf.net/config_blocks.html -->
-        <module name="AvoidNestedBlocks">
-            <!-- ONOS alows declarations inside of switch case blocks -->
-            <property name="allowInSwitchCase" value="true"/>
-        </module>
-        <module name="EmptyBlock"/>
-        <module name="LeftCurly"/>
-        <module name="NeedBraces"/>
-        <module name="RightCurly"/>
-
-        <!-- Checks for common coding problems               -->
-        <!-- See http://checkstyle.sf.net/config_coding.html -->
-        <!-- ONOS allows conditional operators -->
-        <!-- <module name="AvoidInlineConditionals"/> -->
-        <module name="EmptyStatement"/>
-        <module name="EqualsHashCode"/>
-
-        <module name="HiddenField">
-            <property name="ignoreSetter" value="true"/>
-            <property name="ignoreConstructorParameter" value="true"/>
-        </module>
-
-        <module name="IllegalInstantiation"/>
-        <module name="InnerAssignment"/>
-
-        <!-- Many violations of this rule present, revist in a
-        subsequent round of cleanups -->
-        <!-- <module name="MagicNumber"/> -->
-        <module name="MissingSwitchDefault"/>
-
-        <module name="RedundantThrows">
-            <property name="allowSubclasses" value="true"/>
-            <property name="allowUnchecked" value="true"/>
-            <property name="suppressLoadErrors" value="true"/>
-        </module>
-
-        <module name="SimplifyBooleanExpression"/>
-        <module name="SimplifyBooleanReturn"/>
-
-        <!-- Checks for class design                         -->
-        <!-- See http://checkstyle.sf.net/config_design.html -->
-        <!-- ONOS produces many warnings of this type.
-        Fixing all of these is outside the scope of the current cleanup. -->
-        <!-- <module name="DesignForExtension"/> -->
-        <module name="FinalClass"/>
-
-        <module name="HideUtilityClassConstructor"/>
-
-        <module name="InterfaceIsType"/>
-
-        <module name="VisibilityModifier">
-            <property name="severity" value="warning"/>
-        </module>
-
-
-
-        <!-- Miscellaneous other checks.                   -->
-        <!-- See http://checkstyle.sf.net/config_misc.html -->
-        <module name="ArrayTypeStyle"/>
-
-        <!--  Many violations of this rule currently, too many to fix
-        in the current cleanup. -->
-        <!-- <module name="FinalParameters"/> -->
-        <!-- ONOS allows TODO markers in checked in source code -->
-        <!-- <module name="TodoComment"/> -->
-        <module name="UpperEll"/>
-
-      </module>
-
-    </module>
diff --git a/of/ctl/conf/checkstyle/suppressions.xml b/of/ctl/conf/checkstyle/suppressions.xml
deleted file mode 100644
index 41dbe2d..0000000
--- a/of/ctl/conf/checkstyle/suppressions.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN" "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
-
-<suppressions>
-     <!--
-        Note: Exclusion definition exists in multiple places.
-        - In file ${findbugs.excludeFilterFile} defined at top of pom.xml
-        - In file conf/checkstyle/onos_suppressions.xml (this file)
-        - maven-pmd-plugin configuration in pom.xml
-          (under build and reporting)
-     -->
-
-    <suppress files=".*" checks="FinalParametersCheck"/>
-    <suppress files=".*" checks="MagicNumbersCheck"/>
-    <suppress files=".*" checks="DesignForExtensionCheck"/>
-    <suppress files=".*" checks="TodoCommentCheck"/>
-    <suppress files=".*" checks="AvoidInlineConditionalsCheck"/>
-    <suppress files=".*" checks="OperatorWrapCheck"/>
-</suppressions>
-
diff --git a/of/ctl/conf/findbugs/exclude.xml b/of/ctl/conf/findbugs/exclude.xml
deleted file mode 100644
index 3a49335..0000000
--- a/of/ctl/conf/findbugs/exclude.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<FindBugsFilter>
-     <!--
-        Note: Exclusion definition exists in multiple places.
-        - In file ${findbugs.excludeFilterFile} defined at top of pom.xml (this file)
-        - In file conf/checkstyle/onos_suppressions.xml
-        - maven-pmd-plugin configuration in pom.xml
-          (under build and reporting)
-     -->
-     <Match>
-       <Class name="~net\.onrc\.onos\.core\.datastore\.serializers\..*" />
-     </Match>
-     <Match>
-       <Class name="~.*edu\.stanford\..*"/>
-     </Match>
-     <Match>
-       <Class name="~.org\.projectfloodlight\..*"/>
-     </Match>
-</FindBugsFilter>
diff --git a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/OpenFlowControllerImpl.java b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/OpenFlowControllerImpl.java
index ac10021..f7ff997 100644
--- a/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/OpenFlowControllerImpl.java
+++ b/of/ctl/src/main/java/org/onlab/onos/of/controller/impl/OpenFlowControllerImpl.java
@@ -6,9 +6,7 @@
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Service;
 import org.onlab.onos.of.controller.Dpid;
 import org.onlab.onos.of.controller.OpenFlowController;
 import org.onlab.onos.of.controller.OpenFlowSwitch;
@@ -17,11 +15,10 @@
 import org.onlab.onos.of.controller.RoleState;
 import org.onlab.onos.of.controller.driver.OpenFlowAgent;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@Component(immediate = true)
-@Service
 public class OpenFlowControllerImpl implements OpenFlowController {
 
     private static final Logger log =
@@ -38,6 +35,9 @@
     protected ArrayList<OpenFlowSwitchListener> ofEventListener =
             new ArrayList<OpenFlowSwitchListener>();
 
+    protected ArrayList<PacketListener> ofPacketListener =
+            new ArrayList<PacketListener>();
+
     private final Controller ctrl = new Controller();
 
     @Activate
@@ -94,14 +94,12 @@
 
     @Override
     public void addPacketListener(int priority, PacketListener listener) {
-        // TODO Auto-generated method stub
-
+        ofPacketListener.add(priority, listener);
     }
 
     @Override
     public void removePacketListener(PacketListener listener) {
-        // TODO Auto-generated method stub
-
+        ofPacketListener.remove(listener);
     }
 
     @Override
@@ -110,8 +108,22 @@
     }
 
     @Override
-    public void processPacket(OFMessage msg) {
-        log.info("Got message {}", msg);
+    public void processPacket(Dpid dpid, OFMessage msg) {
+        switch (msg.getType()) {
+        case PORT_STATUS:
+            for (OpenFlowSwitchListener l : ofEventListener) {
+                l.portChanged(dpid, (OFPortStatus) msg);
+            }
+            break;
+        case PACKET_IN:
+            for (PacketListener p : ofPacketListener) {
+                //TODO fix me!
+                p.handlePacket(null);
+            }
+            break;
+        default:
+            log.warn("Handling message type {} not yet implemented", msg.getType());
+        }
     }
 
     @Override
@@ -252,8 +264,8 @@
         }
 
         @Override
-        public void processMessage(OFMessage m) {
-            processPacket(m);
+        public void processMessage(Dpid dpid, OFMessage m) {
+            processPacket(dpid, m);
         }
     }
 
diff --git a/of/pom.xml b/of/pom.xml
index 5a030f4..a8323a1 100644
--- a/of/pom.xml
+++ b/of/pom.xml
@@ -22,6 +22,13 @@
         <module>drivers</module>
     </modules>
 
+    <dependencies>
+        <dependency>
+            <groupId>org.onlab.onos</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+    </dependencies>
+
     <build>
         <plugins>
             <plugin>
diff --git a/providers/of/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java b/providers/of/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
index 314564d..ed9d970 100644
--- a/providers/of/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
+++ b/providers/of/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
@@ -1,5 +1,7 @@
 package org.onlab.onos.provider.of.link.impl;
 
+import static org.slf4j.LoggerFactory.getLogger;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -11,10 +13,10 @@
 import org.onlab.onos.net.provider.AbstractProvider;
 import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.onos.of.controller.OpenFlowController;
+import org.onlab.onos.of.controller.PacketContext;
+import org.onlab.onos.of.controller.PacketListener;
 import org.slf4j.Logger;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
 /**
  * Provider which uses an OpenFlow controller to detect network
  * infrastructure links.
@@ -32,6 +34,8 @@
 
     private LinkProviderService providerService;
 
+    private final PacketListener listener = new InternalLinkProvider();
+
     /**
      * Creates an OpenFlow link provider.
      */
@@ -42,14 +46,26 @@
     @Activate
     public void activate() {
         providerService = providerRegistry.register(this);
+        controller.addPacketListener(0, listener);
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
         providerRegistry.unregister(this);
+        controller.removePacketListener(listener);
         providerService = null;
         log.info("Stopped");
     }
 
+
+    private class InternalLinkProvider implements PacketListener {
+
+        @Override
+        public void handlePacket(PacketContext pktCtx) {
+
+        }
+
+    }
+
 }
diff --git a/tools/build/conf/src/main/resources/onos/suppressions.xml b/tools/build/conf/src/main/resources/onos/suppressions.xml
index bae6bb7..ed406da 100644
--- a/tools/build/conf/src/main/resources/onos/suppressions.xml
+++ b/tools/build/conf/src/main/resources/onos/suppressions.xml
@@ -25,5 +25,7 @@
     <suppress files=".*" checks="TodoCommentCheck"/>
     <suppress files=".*" checks="AvoidInlineConditionalsCheck"/>
     <suppress files=".*" checks="OperatorWrapCheck"/>
+    <suppress files=".*" checks="HiddenField"/>
+    
 </suppressions>
 
diff --git a/utils/misc/src/main/java/org/onlab/packet/ARP.java b/utils/misc/src/main/java/org/onlab/packet/ARP.java
new file mode 100644
index 0000000..df31142
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ARP.java
@@ -0,0 +1,365 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class ARP extends BasePacket {
+    public static final short HW_TYPE_ETHERNET = 0x1;
+
+    public static final short PROTO_TYPE_IP = 0x800;
+
+    public static final short OP_REQUEST = 0x1;
+    public static final short OP_REPLY = 0x2;
+    public static final short OP_RARP_REQUEST = 0x3;
+    public static final 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 this.hardwareType;
+    }
+
+    /**
+     * @param hardwareType
+     *            the hardwareType to set
+     */
+    public ARP setHardwareType(final short hwType) {
+        this.hardwareType = hwType;
+        return this;
+    }
+
+    /**
+     * @return the protocolType
+     */
+    public short getProtocolType() {
+        return this.protocolType;
+    }
+
+    /**
+     * @param protocolType
+     *            the protocolType to set
+     */
+    public ARP setProtocolType(final short protoType) {
+        this.protocolType = protoType;
+        return this;
+    }
+
+    /**
+     * @return the hardwareAddressLength
+     */
+    public byte getHardwareAddressLength() {
+        return this.hardwareAddressLength;
+    }
+
+    /**
+     * @param hwAddressLength
+     *            the hardwareAddressLength to set
+     */
+    public ARP setHardwareAddressLength(final byte hwAddressLength) {
+        this.hardwareAddressLength = hwAddressLength;
+        return this;
+    }
+
+    /**
+     * @return the protocolAddressLength
+     */
+    public byte getProtocolAddressLength() {
+        return this.protocolAddressLength;
+    }
+
+    /**
+     * @param protocolAddressLength
+     *            the protocolAddressLength to set
+     */
+    public ARP setProtocolAddressLength(final byte protoAddressLength) {
+        this.protocolAddressLength = protoAddressLength;
+        return this;
+    }
+
+    /**
+     * @return the opCode
+     */
+    public short getOpCode() {
+        return this.opCode;
+    }
+
+    /**
+     * @param opCode
+     *            the opCode to set
+     */
+    public ARP setOpCode(final short op) {
+        this.opCode = op;
+        return this;
+    }
+
+    /**
+     * @return the senderHardwareAddress
+     */
+    public byte[] getSenderHardwareAddress() {
+        return this.senderHardwareAddress;
+    }
+
+    /**
+     * @param senderHardwareAddress
+     *            the senderHardwareAddress to set
+     */
+    public ARP setSenderHardwareAddress(final byte[] senderHWAddress) {
+        this.senderHardwareAddress = senderHWAddress;
+        return this;
+    }
+
+    /**
+     * @return the senderProtocolAddress
+     */
+    public byte[] getSenderProtocolAddress() {
+        return this.senderProtocolAddress;
+    }
+
+    /**
+     * @param senderProtocolAddress
+     *            the senderProtocolAddress to set
+     */
+    public ARP setSenderProtocolAddress(final byte[] senderProtoAddress) {
+        this.senderProtocolAddress = senderProtoAddress;
+        return this;
+    }
+
+    public ARP setSenderProtocolAddress(final int address) {
+        this.senderProtocolAddress = ByteBuffer.allocate(4).putInt(address)
+                .array();
+        return this;
+    }
+
+    /**
+     * @return the targetHardwareAddress
+     */
+    public byte[] getTargetHardwareAddress() {
+        return this.targetHardwareAddress;
+    }
+
+    /**
+     * @param targetHardwareAddress
+     *            the targetHardwareAddress to set
+     */
+    public ARP setTargetHardwareAddress(final byte[] targetHWAddress) {
+        this.targetHardwareAddress = targetHWAddress;
+        return this;
+    }
+
+    /**
+     * @return the targetProtocolAddress
+     */
+    public byte[] getTargetProtocolAddress() {
+        return this.targetProtocolAddress;
+    }
+
+    /**
+     * @return True if gratuitous ARP (SPA = TPA), false otherwise
+     */
+    public boolean isGratuitous() {
+        assert this.senderProtocolAddress.length == this.targetProtocolAddress.length;
+
+        int indx = 0;
+        while (indx < this.senderProtocolAddress.length) {
+            if (this.senderProtocolAddress[indx] != this.targetProtocolAddress[indx]) {
+                return false;
+            }
+            indx++;
+        }
+
+        return true;
+    }
+
+    /**
+     * @param targetProtocolAddress
+     *            the targetProtocolAddress to set
+     */
+    public ARP setTargetProtocolAddress(final byte[] targetProtoAddress) {
+        this.targetProtocolAddress = targetProtoAddress;
+        return this;
+    }
+
+    public ARP setTargetProtocolAddress(final int address) {
+        this.targetProtocolAddress = ByteBuffer.allocate(4).putInt(address)
+                .array();
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        final int length = 8 + 2 * (0xff & this.hardwareAddressLength) + 2
+                * (0xff & this.protocolAddressLength);
+        final byte[] data = new byte[length];
+        final 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(final byte[] data, final int offset,
+            final int length) {
+        final 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 + this.hardwareAddressLength;
+        result = prime * result + this.hardwareType;
+        result = prime * result + this.opCode;
+        result = prime * result + this.protocolAddressLength;
+        result = prime * result + this.protocolType;
+        result = prime * result + Arrays.hashCode(this.senderHardwareAddress);
+        result = prime * result + Arrays.hashCode(this.senderProtocolAddress);
+        result = prime * result + Arrays.hashCode(this.targetHardwareAddress);
+        result = prime * result + Arrays.hashCode(this.targetProtocolAddress);
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof ARP)) {
+            return false;
+        }
+        final ARP other = (ARP) obj;
+        if (this.hardwareAddressLength != other.hardwareAddressLength) {
+            return false;
+        }
+        if (this.hardwareType != other.hardwareType) {
+            return false;
+        }
+        if (this.opCode != other.opCode) {
+            return false;
+        }
+        if (this.protocolAddressLength != other.protocolAddressLength) {
+            return false;
+        }
+        if (this.protocolType != other.protocolType) {
+            return false;
+        }
+        if (!Arrays.equals(this.senderHardwareAddress,
+                other.senderHardwareAddress)) {
+            return false;
+        }
+        if (!Arrays.equals(this.senderProtocolAddress,
+                other.senderProtocolAddress)) {
+            return false;
+        }
+        if (!Arrays.equals(this.targetHardwareAddress,
+                other.targetHardwareAddress)) {
+            return false;
+        }
+        if (!Arrays.equals(this.targetProtocolAddress,
+                other.targetProtocolAddress)) {
+            return false;
+        }
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "ARP [hardwareType=" + this.hardwareType + ", protocolType="
+                + this.protocolType + ", hardwareAddressLength="
+                + this.hardwareAddressLength + ", protocolAddressLength="
+                + this.protocolAddressLength + ", opCode=" + this.opCode
+                + ", senderHardwareAddress="
+                + Arrays.toString(this.senderHardwareAddress)
+                + ", senderProtocolAddress="
+                + Arrays.toString(this.senderProtocolAddress)
+                + ", targetHardwareAddress="
+                + Arrays.toString(this.targetHardwareAddress)
+                + ", targetProtocolAddress="
+                + Arrays.toString(this.targetProtocolAddress) + "]";
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/BasePacket.java b/utils/misc/src/main/java/org/onlab/packet/BasePacket.java
new file mode 100644
index 0000000..d6ae9b4
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/BasePacket.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 this.parent;
+    }
+
+    /**
+     * @param parent
+     *            the parent to set
+     */
+    @Override
+    public IPacket setParent(final IPacket parent) {
+        this.parent = parent;
+        return this;
+    }
+
+    /**
+     * @return the payload
+     */
+    @Override
+    public IPacket getPayload() {
+        return this.payload;
+    }
+
+    /**
+     * @param payload
+     *            the payload to set
+     */
+    @Override
+    public IPacket setPayload(final 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
+                + (this.payload == null ? 0 : this.payload.hashCode());
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof BasePacket)) {
+            return false;
+        }
+        final BasePacket other = (BasePacket) obj;
+        if (this.payload == null) {
+            if (other.payload != null) {
+                return false;
+            }
+        } else if (!this.payload.equals(other.payload)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public Object clone() {
+        IPacket pkt;
+        try {
+            pkt = this.getClass().newInstance();
+        } catch (final 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.
+        final byte[] data = this.serialize();
+        pkt.deserialize(this.serialize(), 0, data.length);
+        pkt.setParent(this.parent);
+        return pkt;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCP.java b/utils/misc/src/main/java/org/onlab/packet/DHCP.java
new file mode 100644
index 0000000..9ace649
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCP.java
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 {
+    /**
+     * Dynamic Host Configuration Protocol packet.
+     * ------------------------------------------ |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 final int MIN_HEADER_LENGTH = 240;
+    public static final byte OPCODE_REQUEST = 0x1;
+    public static final byte OPCODE_REPLY = 0x2;
+
+    public static final 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(final byte value) {
+            this.value = value;
+        }
+
+        public byte getValue() {
+            return this.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 this.opCode;
+    }
+
+    /**
+     * @param opCode
+     *            the opCode to set
+     */
+    public DHCP setOpCode(final byte opCode) {
+        this.opCode = opCode;
+        return this;
+    }
+
+    /**
+     * @return the hardwareType
+     */
+    public byte getHardwareType() {
+        return this.hardwareType;
+    }
+
+    /**
+     * @param hardwareType
+     *            the hardwareType to set
+     */
+    public DHCP setHardwareType(final byte hardwareType) {
+        this.hardwareType = hardwareType;
+        return this;
+    }
+
+    /**
+     * @return the hardwareAddressLength
+     */
+    public byte getHardwareAddressLength() {
+        return this.hardwareAddressLength;
+    }
+
+    /**
+     * @param hardwareAddressLength
+     *            the hardwareAddressLength to set
+     */
+    public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
+        this.hardwareAddressLength = hardwareAddressLength;
+        return this;
+    }
+
+    /**
+     * @return the hops
+     */
+    public byte getHops() {
+        return this.hops;
+    }
+
+    /**
+     * @param hops
+     *            the hops to set
+     */
+    public DHCP setHops(final byte hops) {
+        this.hops = hops;
+        return this;
+    }
+
+    /**
+     * @return the transactionId
+     */
+    public int getTransactionId() {
+        return this.transactionId;
+    }
+
+    /**
+     * @param transactionId
+     *            the transactionId to set
+     */
+    public DHCP setTransactionId(final int transactionId) {
+        this.transactionId = transactionId;
+        return this;
+    }
+
+    /**
+     * @return the seconds
+     */
+    public short getSeconds() {
+        return this.seconds;
+    }
+
+    /**
+     * @param seconds
+     *            the seconds to set
+     */
+    public DHCP setSeconds(final short seconds) {
+        this.seconds = seconds;
+        return this;
+    }
+
+    /**
+     * @return the flags
+     */
+    public short getFlags() {
+        return this.flags;
+    }
+
+    /**
+     * @param flags
+     *            the flags to set
+     */
+    public DHCP setFlags(final short flags) {
+        this.flags = flags;
+        return this;
+    }
+
+    /**
+     * @return the clientIPAddress
+     */
+    public int getClientIPAddress() {
+        return this.clientIPAddress;
+    }
+
+    /**
+     * @param clientIPAddress
+     *            the clientIPAddress to set
+     */
+    public DHCP setClientIPAddress(final int clientIPAddress) {
+        this.clientIPAddress = clientIPAddress;
+        return this;
+    }
+
+    /**
+     * @return the yourIPAddress
+     */
+    public int getYourIPAddress() {
+        return this.yourIPAddress;
+    }
+
+    /**
+     * @param yourIPAddress
+     *            the yourIPAddress to set
+     */
+    public DHCP setYourIPAddress(final int yourIPAddress) {
+        this.yourIPAddress = yourIPAddress;
+        return this;
+    }
+
+    /**
+     * @return the serverIPAddress
+     */
+    public int getServerIPAddress() {
+        return this.serverIPAddress;
+    }
+
+    /**
+     * @param serverIPAddress
+     *            the serverIPAddress to set
+     */
+    public DHCP setServerIPAddress(final int serverIPAddress) {
+        this.serverIPAddress = serverIPAddress;
+        return this;
+    }
+
+    /**
+     * @return the gatewayIPAddress
+     */
+    public int getGatewayIPAddress() {
+        return this.gatewayIPAddress;
+    }
+
+    /**
+     * @param gatewayIPAddress
+     *            the gatewayIPAddress to set
+     */
+    public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
+        this.gatewayIPAddress = gatewayIPAddress;
+        return this;
+    }
+
+    /**
+     * @return the clientHardwareAddress
+     */
+    public byte[] getClientHardwareAddress() {
+        return this.clientHardwareAddress;
+    }
+
+    /**
+     * @param clientHardwareAddress
+     *            the clientHardwareAddress to set
+     */
+    public DHCP setClientHardwareAddress(final 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(final DHCPOptionCode optionCode) {
+        for (final DHCPOption opt : this.options) {
+            if (opt.code == optionCode.value) {
+                return opt;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the options
+     */
+    public List<DHCPOption> getOptions() {
+        return this.options;
+    }
+
+    /**
+     * @param options
+     *            the options to set
+     */
+    public DHCP setOptions(final List<DHCPOption> options) {
+        this.options = options;
+        return this;
+    }
+
+    /**
+     * @return the packetType base on option 53
+     */
+    public DHCPPacketType getPacketType() {
+        final ListIterator<DHCPOption> lit = this.options.listIterator();
+        while (lit.hasNext()) {
+            final 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 this.serverName;
+    }
+
+    /**
+     * @param server
+     *            the serverName to set
+     */
+    public DHCP setServerName(final String server) {
+        this.serverName = server;
+        return this;
+    }
+
+    /**
+     * @return the bootFileName
+     */
+    public String getBootFileName() {
+        return this.bootFileName;
+    }
+
+    /**
+     * @param bootFile
+     *            the bootFileName to set
+     */
+    public DHCP setBootFileName(final String bootFile) {
+        this.bootFileName = bootFile;
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        // not guaranteed to retain length/exact format
+        this.resetChecksum();
+
+        // minimum size 240 including magic cookie, options generally padded to
+        // 300
+        int optionsLength = 0;
+        for (final DHCPOption option : this.options) {
+            if (option.getCode() == 0 || option.getCode() == 255) {
+                optionsLength += 1;
+            } else {
+                optionsLength += 2 + (0xff & option.getLength());
+            }
+        }
+        int optionsPadLength = 0;
+        if (optionsLength < 60) {
+            optionsPadLength = 60 - optionsLength;
+        }
+
+        final byte[] data = new byte[240 + optionsLength + optionsPadLength];
+        final 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);
+            }
+        }
+        this.writeString(this.serverName, bb, 64);
+        this.writeString(this.bootFileName, bb, 128);
+        // magic cookie
+        bb.put((byte) 0x63);
+        bb.put((byte) 0x82);
+        bb.put((byte) 0x53);
+        bb.put((byte) 0x63);
+        for (final DHCPOption option : this.options) {
+            final 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(final String string, final ByteBuffer bb,
+            final 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 (final 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(final byte[] data, final int offset,
+            final int length) {
+        final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        if (bb.remaining() < DHCP.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();
+        final 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 = this.readString(bb, 64);
+        this.bootFileName = this.readString(bb, 128);
+        // read the magic cookie
+        // magic cookie
+        bb.get();
+        bb.get();
+        bb.get();
+        bb.get();
+        // read options
+        while (bb.hasRemaining()) {
+            final 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()) {
+                    final int l = 0xff & bb.get(); // convert signed byte to
+                    // int in range [0,255]
+                    option.setLength((byte) l);
+                    if (bb.remaining() >= l) {
+                        final 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(final ByteBuffer bb, final int maxLength) {
+        final byte[] bytes = new byte[maxLength];
+        bb.get(bytes);
+        String result = null;
+        try {
+            result = new String(bytes, "ascii").trim();
+        } catch (final UnsupportedEncodingException e) {
+            throw new RuntimeException("Failure decoding string", e);
+        }
+        return result;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java b/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
new file mode 100644
index 0000000..fb6f4df
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 this.code;
+    }
+
+    /**
+     * @param code
+     *            the code to set
+     */
+    public DHCPOption setCode(final byte code) {
+        this.code = code;
+        return this;
+    }
+
+    /**
+     * @return the length
+     */
+    public byte getLength() {
+        return this.length;
+    }
+
+    /**
+     * @param length
+     *            the length to set
+     */
+    public DHCPOption setLength(final byte length) {
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * @return the data
+     */
+    public byte[] getData() {
+        return this.data;
+    }
+
+    /**
+     * @param data
+     *            the data to set
+     */
+    public DHCPOption setData(final 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 + this.code;
+        result = prime * result + Arrays.hashCode(this.data);
+        result = prime * result + this.length;
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof DHCPOption)) {
+            return false;
+        }
+        final DHCPOption other = (DHCPOption) obj;
+        if (this.code != other.code) {
+            return false;
+        }
+        if (!Arrays.equals(this.data, other.data)) {
+            return false;
+        }
+        if (this.length != other.length) {
+            return false;
+        }
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "DHCPOption [code=" + this.code + ", length=" + this.length
+                + ", data=" + Arrays.toString(this.data) + "]";
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java b/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
new file mode 100644
index 0000000..a1b5bea
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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(final int value) {
+        this.value = value;
+    }
+
+    public int getValue() {
+        return this.value;
+    }
+
+    @Override
+    public String toString() {
+        switch (this.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";
+        default:
+            break;
+        }
+
+        return null;
+    }
+
+    public static DHCPPacketType getType(final 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;
+        default:
+            break;
+        }
+
+        return null;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Data.java b/utils/misc/src/main/java/org/onlab/packet/Data.java
new file mode 100644
index 0000000..9754ceb
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/Data.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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(final byte[] data) {
+        this.data = data;
+    }
+
+    /**
+     * @return the data
+     */
+    public byte[] getData() {
+        return this.data;
+    }
+
+    /**
+     * @param data
+     *            the data to set
+     */
+    public Data setData(final byte[] data) {
+        this.data = data;
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        return this.data;
+    }
+
+    @Override
+    public IPacket deserialize(final byte[] data, final int offset,
+            final 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(this.data);
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof Data)) {
+            return false;
+        }
+        final Data other = (Data) obj;
+        if (!Arrays.equals(this.data, other.data)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ethernet.java b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
new file mode 100644
index 0000000..7f729c5
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
@@ -0,0 +1,553 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ */
+public class Ethernet extends BasePacket {
+    private static final 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 {
+        Ethernet.etherTypeClassMap = new HashMap<Short, Class<? extends IPacket>>();
+        Ethernet.etherTypeClassMap.put(Ethernet.TYPE_ARP, ARP.class);
+        Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
+        Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
+        Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.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 = Ethernet.VLAN_UNTAGGED;
+    }
+
+    /**
+     * Gets the destination MAC address.
+     *
+     * @return the destination MAC as a byte array
+     */
+    public byte[] getDestinationMACAddress() {
+        return this.destinationMACAddress.toBytes();
+    }
+
+    /**
+     * Gets the destination MAC address.
+     *
+     * @return the destination MAC
+     */
+    public MACAddress getDestinationMAC() {
+        return this.destinationMACAddress;
+    }
+
+    /**
+     * Sets the destination MAC address.
+     *
+     * @param destinationMACAddress the destination MAC to set
+     * @return the Ethernet frame
+     */
+    public Ethernet setDestinationMACAddress(final byte[] destMac) {
+        this.destinationMACAddress = MACAddress.valueOf(destMac);
+        return this;
+    }
+
+    /**
+     * Sets the destination MAC address.
+     *
+     * @param destinationMACAddress the destination MAC to set
+     * @return the Ethernet frame
+     */
+    public Ethernet setDestinationMACAddress(final String destMac) {
+        this.destinationMACAddress = MACAddress.valueOf(destMac);
+        return this;
+    }
+
+    /**
+     * Gets the source MAC address.
+     *
+     * @return the source MACAddress as a byte array
+     */
+    public byte[] getSourceMACAddress() {
+        return this.sourceMACAddress.toBytes();
+    }
+
+    /**
+     * Gets the source MAC address.
+     *
+     * @return the source MACAddress
+     */
+    public MACAddress getSourceMAC() {
+        return this.sourceMACAddress;
+    }
+
+    /**
+     * Sets the source MAC address.
+     *
+     * @param sourceMACAddress the source MAC to set
+     * @return the Ethernet frame
+     */
+    public Ethernet setSourceMACAddress(final byte[] sourceMac) {
+        this.sourceMACAddress = MACAddress.valueOf(sourceMac);
+        return this;
+    }
+
+    /**
+     * Sets the source MAC address.
+     *
+     * @param sourceMACAddress the source MAC to set
+     * @return the Ethernet frame
+     */
+    public Ethernet setSourceMACAddress(final String sourceMac) {
+        this.sourceMACAddress = MACAddress.valueOf(sourceMac);
+        return this;
+    }
+
+    /**
+     * Gets the priority code.
+     *
+     * @return the priorityCode
+     */
+    public byte getPriorityCode() {
+        return this.priorityCode;
+    }
+
+    /**
+     * Sets the priority code.
+     *
+     * @param priorityCode the priorityCode to set
+     * @return the Ethernet frame
+     */
+    public Ethernet setPriorityCode(final byte priority) {
+        this.priorityCode = priority;
+        return this;
+    }
+
+    /**
+     * Gets the VLAN ID.
+     *
+     * @return the vlanID
+     */
+    public short getVlanID() {
+        return this.vlanID;
+    }
+
+    /**
+     * Sets the VLAN ID.
+     *
+     * @param vlanID the vlanID to set
+     * @return the Ethernet frame
+     */
+    public Ethernet setVlanID(final short vlan) {
+        this.vlanID = vlan;
+        return this;
+    }
+
+    /**
+     * Gets the Ethernet type.
+     *
+     * @return the etherType
+     */
+    public short getEtherType() {
+        return this.etherType;
+    }
+
+    /**
+     * Sets the Ethernet type.
+     *
+     * @param etherType the etherType to set
+     * @return the Ethernet frame
+     */
+    public Ethernet setEtherType(final short ethType) {
+        this.etherType = ethType;
+        return this;
+    }
+
+    /**
+     * @return True if the Ethernet frame is broadcast, false otherwise
+     */
+    public boolean isBroadcast() {
+        assert this.destinationMACAddress.length() == 6;
+        return this.destinationMACAddress.isBroadcast();
+    }
+
+    /**
+     * @return True is the Ethernet frame is multicast, False otherwise
+     */
+    public boolean isMulticast() {
+        return this.destinationMACAddress.isMulticast();
+    }
+
+    /**
+     * Pad this packet to 60 bytes minimum, filling with zeros?
+     *
+     * @return the pad
+     */
+    public boolean isPad() {
+        return this.pad;
+    }
+
+    /**
+     * Pad this packet to 60 bytes minimum, filling with zeros?
+     *
+     * @param pad
+     *            the pad to set
+     */
+    public Ethernet setPad(final boolean pd) {
+        this.pad = pd;
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        byte[] payloadData = null;
+        if (this.payload != null) {
+            this.payload.setParent(this);
+            payloadData = this.payload.serialize();
+        }
+        int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
+                + (payloadData == null ? 0 : payloadData.length);
+        if (this.pad && length < 60) {
+            length = 60;
+        }
+        final byte[] data = new byte[length];
+        final ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.put(this.destinationMACAddress.toBytes());
+        bb.put(this.sourceMACAddress.toBytes());
+        if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
+            bb.putShort((short) 0x8100);
+            bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
+        }
+        bb.putShort(this.etherType);
+        if (payloadData != null) {
+            bb.put(payloadData);
+        }
+        if (this.pad) {
+            Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
+        }
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(final byte[] data, final int offset,
+            final int length) {
+        if (length <= 0) {
+            return null;
+        }
+        final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        if (this.destinationMACAddress == null) {
+            this.destinationMACAddress = MACAddress.valueOf(new byte[6]);
+        }
+        final 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]);
+        }
+        final byte[] srcAddr = new byte[MACAddress.MAC_ADDRESS_LENGTH];
+        bb.get(srcAddr);
+        this.sourceMACAddress = MACAddress.valueOf(srcAddr);
+
+        short ethType = bb.getShort();
+        if (ethType == (short) 0x8100) {
+            final short tci = bb.getShort();
+            this.priorityCode = (byte) (tci >> 13 & 0x07);
+            this.vlanID = (short) (tci & 0x0fff);
+            ethType = bb.getShort();
+        } else {
+            this.vlanID = Ethernet.VLAN_UNTAGGED;
+        }
+        this.etherType = ethType;
+
+        IPacket payload;
+        if (Ethernet.etherTypeClassMap.containsKey(this.etherType)) {
+            final Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap
+                    .get(this.etherType);
+            try {
+                payload = clazz.newInstance();
+            } catch (final 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(final String macAddress) {
+        final String[] macBytes = macAddress.split(":");
+        if (macBytes.length != 6) {
+            return false;
+        }
+        for (int i = 0; i < 6; ++i) {
+            if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
+                    || Ethernet.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(final 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(final byte[] macAddress) {
+        return MACAddress.valueOf(macAddress).toLong();
+    }
+
+    /**
+     * Converts a long MAC address to a byte array.
+     *
+     * @param macAddress
+     * @return the bytes of the mac address
+     */
+    public static byte[] toByteArray(final 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 + this.destinationMACAddress.hashCode();
+        result = prime * result + this.etherType;
+        result = prime * result + this.vlanID;
+        result = prime * result + this.priorityCode;
+        result = prime * result + (this.pad ? 1231 : 1237);
+        result = prime * result + this.sourceMACAddress.hashCode();
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof Ethernet)) {
+            return false;
+        }
+        final Ethernet other = (Ethernet) obj;
+        if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
+            return false;
+        }
+        if (this.priorityCode != other.priorityCode) {
+            return false;
+        }
+        if (this.vlanID != other.vlanID) {
+            return false;
+        }
+        if (this.etherType != other.etherType) {
+            return false;
+        }
+        if (this.pad != other.pad) {
+            return false;
+        }
+        if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
+            return false;
+        }
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString(java.lang.Object)
+     */
+    @Override
+    public String toString() {
+
+        final StringBuffer sb = new StringBuffer("\n");
+
+        final IPacket pkt = 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(bytesToHex(this.getSourceMACAddress()));
+        sb.append("\ndl_dst: ");
+        sb.append(bytesToHex(this.getDestinationMACAddress()));
+
+        if (pkt instanceof ARP) {
+            final 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) {
+            final 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) {
+            final 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) {
+                final 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 {
+            sb.append("\nunknwon packet");
+        }
+
+        return sb.toString();
+    }
+
+    public static String bytesToHex(byte[] in) {
+        final StringBuilder builder = new StringBuilder();
+        for (byte b : in) {
+            builder.append(String.format("%02x", b));
+        }
+        return builder.toString();
+    }
+
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ICMP.java b/utils/misc/src/main/java/org/onlab/packet/ICMP.java
new file mode 100644
index 0000000..0a52984
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ICMP.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 this.icmpType;
+    }
+
+    /**
+     * @param icmpType
+     *            to set
+     */
+    public ICMP setIcmpType(final byte icmpType) {
+        this.icmpType = icmpType;
+        return this;
+    }
+
+    /**
+     * @return the icmp code
+     */
+    public byte getIcmpCode() {
+        return this.icmpCode;
+    }
+
+    /**
+     * @param icmpCode
+     *            code to set
+     */
+    public ICMP setIcmpCode(final byte icmpCode) {
+        this.icmpCode = icmpCode;
+        return this;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return this.checksum;
+    }
+
+    /**
+     * @param checksum
+     *            the checksum to set
+     */
+    public ICMP setChecksum(final 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
+     */
+    @Override
+    public byte[] serialize() {
+        int length = 4;
+        byte[] payloadData = null;
+        if (this.payload != null) {
+            this.payload.setParent(this);
+            payloadData = this.payload.serialize();
+            length += payloadData.length;
+        }
+
+        final byte[] data = new byte[length];
+        final 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 + this.icmpType;
+        result = prime * result + this.icmpCode;
+        result = prime * result + this.checksum;
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof ICMP)) {
+            return false;
+        }
+        final ICMP other = (ICMP) obj;
+        if (this.icmpType != other.icmpType) {
+            return false;
+        }
+        if (this.icmpCode != other.icmpCode) {
+            return false;
+        }
+        if (this.checksum != other.checksum) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public IPacket deserialize(final byte[] data, final int offset,
+            final int length) {
+        final 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 = this.payload.deserialize(data, bb.position(), bb.limit()
+                - bb.position());
+        this.payload.setParent(this);
+        return this;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPacket.java b/utils/misc/src/main/java/org/onlab/packet/IPacket.java
new file mode 100644
index 0000000..be9e4c8
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/IPacket.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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/utils/misc/src/main/java/org/onlab/packet/IPv4.java b/utils/misc/src/main/java/org/onlab/packet/IPv4.java
new file mode 100644
index 0000000..7176f67
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/IPv4.java
@@ -0,0 +1,626 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 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 {
+        IPv4.protocolClassMap = new HashMap<Byte, Class<? extends IPacket>>();
+        IPv4.protocolClassMap.put(IPv4.PROTOCOL_ICMP, ICMP.class);
+        IPv4.protocolClassMap.put(IPv4.PROTOCOL_TCP, TCP.class);
+        IPv4.protocolClassMap.put(IPv4.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;
+        this.isTruncated = false;
+    }
+
+    /**
+     * @return the version
+     */
+    public byte getVersion() {
+        return this.version;
+    }
+
+    /**
+     * @param version
+     *            the version to set
+     */
+    public IPv4 setVersion(final byte version) {
+        this.version = version;
+        return this;
+    }
+
+    /**
+     * @return the headerLength
+     */
+    public byte getHeaderLength() {
+        return this.headerLength;
+    }
+
+    /**
+     * @return the diffServ
+     */
+    public byte getDiffServ() {
+        return this.diffServ;
+    }
+
+    /**
+     * @param diffServ
+     *            the diffServ to set
+     */
+    public IPv4 setDiffServ(final byte diffServ) {
+        this.diffServ = diffServ;
+        return this;
+    }
+
+    /**
+     * @return the totalLength
+     */
+    public short getTotalLength() {
+        return this.totalLength;
+    }
+
+    /**
+     * @return the identification
+     */
+    public short getIdentification() {
+        return this.identification;
+    }
+
+    public boolean isTruncated() {
+        return this.isTruncated;
+    }
+
+    public void setTruncated(final boolean isTruncated) {
+        this.isTruncated = isTruncated;
+    }
+
+    /**
+     * @param identification
+     *            the identification to set
+     */
+    public IPv4 setIdentification(final short identification) {
+        this.identification = identification;
+        return this;
+    }
+
+    /**
+     * @return the flags
+     */
+    public byte getFlags() {
+        return this.flags;
+    }
+
+    /**
+     * @param flags
+     *            the flags to set
+     */
+    public IPv4 setFlags(final byte flags) {
+        this.flags = flags;
+        return this;
+    }
+
+    /**
+     * @return the fragmentOffset
+     */
+    public short getFragmentOffset() {
+        return this.fragmentOffset;
+    }
+
+    /**
+     * @param fragmentOffset
+     *            the fragmentOffset to set
+     */
+    public IPv4 setFragmentOffset(final short fragmentOffset) {
+        this.fragmentOffset = fragmentOffset;
+        return this;
+    }
+
+    /**
+     * @return the ttl
+     */
+    public byte getTtl() {
+        return this.ttl;
+    }
+
+    /**
+     * @param ttl
+     *            the ttl to set
+     */
+    public IPv4 setTtl(final byte ttl) {
+        this.ttl = ttl;
+        return this;
+    }
+
+    /**
+     * @return the protocol
+     */
+    public byte getProtocol() {
+        return this.protocol;
+    }
+
+    /**
+     * @param protocol
+     *            the protocol to set
+     */
+    public IPv4 setProtocol(final byte protocol) {
+        this.protocol = protocol;
+        return this;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return this.checksum;
+    }
+
+    /**
+     * @param checksum
+     *            the checksum to set
+     */
+    public IPv4 setChecksum(final short checksum) {
+        this.checksum = checksum;
+        return this;
+    }
+
+    @Override
+    public void resetChecksum() {
+        this.checksum = 0;
+        super.resetChecksum();
+    }
+
+    /**
+     * @return the sourceAddress
+     */
+    public int getSourceAddress() {
+        return this.sourceAddress;
+    }
+
+    /**
+     * @param sourceAddress
+     *            the sourceAddress to set
+     */
+    public IPv4 setSourceAddress(final int sourceAddress) {
+        this.sourceAddress = sourceAddress;
+        return this;
+    }
+
+    /**
+     * @param sourceAddress
+     *            the sourceAddress to set
+     */
+    public IPv4 setSourceAddress(final String sourceAddress) {
+        this.sourceAddress = IPv4.toIPv4Address(sourceAddress);
+        return this;
+    }
+
+    /**
+     * @return the destinationAddress
+     */
+    public int getDestinationAddress() {
+        return this.destinationAddress;
+    }
+
+    /**
+     * @param destinationAddress
+     *            the destinationAddress to set
+     */
+    public IPv4 setDestinationAddress(final int destinationAddress) {
+        this.destinationAddress = destinationAddress;
+        return this;
+    }
+
+    /**
+     * @param destinationAddress
+     *            the destinationAddress to set
+     */
+    public IPv4 setDestinationAddress(final String destinationAddress) {
+        this.destinationAddress = IPv4.toIPv4Address(destinationAddress);
+        return this;
+    }
+
+    /**
+     * @return the options
+     */
+    public byte[] getOptions() {
+        return this.options;
+    }
+
+    /**
+     * @param options
+     *            the options to set
+     */
+    public IPv4 setOptions(final 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
+     */
+    @Override
+    public byte[] serialize() {
+        byte[] payloadData = null;
+        if (this.payload != null) {
+            this.payload.setParent(this);
+            payloadData = this.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));
+
+        final byte[] data = new byte[this.totalLength];
+        final 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(final byte[] data, final int offset,
+            final int length) {
+        final 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) {
+            final int optionsLength = (this.headerLength - 5) * 4;
+            this.options = new byte[optionsLength];
+            bb.get(this.options);
+        }
+
+        IPacket payload;
+        if (IPv4.protocolClassMap.containsKey(this.protocol)) {
+            final Class<? extends IPacket> clazz = IPv4.protocolClassMap
+                    .get(this.protocol);
+            try {
+                payload = clazz.newInstance();
+            } catch (final 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(final String ipAddress) {
+        if (ipAddress == null) {
+            throw new IllegalArgumentException("Specified IPv4 address must"
+                    + "contain 4 sets of numerical digits separated by periods");
+        }
+        final 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(final byte[] ipAddress) {
+        int ip = 0;
+        for (int i = 0; i < 4; i++) {
+            final 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,
+     * e.g., 192.168.0.1.
+     *
+     * @param ipAddress
+     * @return
+     */
+    public static String fromIPv4Address(final int ipAddress) {
+        final 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(
+            final Collection<Integer> ipAddresses) {
+        if (ipAddresses == null) {
+            return "null";
+        }
+        final StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        for (final Integer ip : ipAddresses) {
+            sb.append(IPv4.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(final String ipAddress) {
+        final String[] octets = ipAddress.split("\\.");
+        if (octets.length != 4) {
+            throw new IllegalArgumentException("Specified IPv4 address must"
+                    + "contain 4 sets of numerical digits separated by periods");
+        }
+
+        final 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(final 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 + this.checksum;
+        result = prime * result + this.destinationAddress;
+        result = prime * result + this.diffServ;
+        result = prime * result + this.flags;
+        result = prime * result + this.fragmentOffset;
+        result = prime * result + this.headerLength;
+        result = prime * result + this.identification;
+        result = prime * result + Arrays.hashCode(this.options);
+        result = prime * result + this.protocol;
+        result = prime * result + this.sourceAddress;
+        result = prime * result + this.totalLength;
+        result = prime * result + this.ttl;
+        result = prime * result + this.version;
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof IPv4)) {
+            return false;
+        }
+        final IPv4 other = (IPv4) obj;
+        if (this.checksum != other.checksum) {
+            return false;
+        }
+        if (this.destinationAddress != other.destinationAddress) {
+            return false;
+        }
+        if (this.diffServ != other.diffServ) {
+            return false;
+        }
+        if (this.flags != other.flags) {
+            return false;
+        }
+        if (this.fragmentOffset != other.fragmentOffset) {
+            return false;
+        }
+        if (this.headerLength != other.headerLength) {
+            return false;
+        }
+        if (this.identification != other.identification) {
+            return false;
+        }
+        if (!Arrays.equals(this.options, other.options)) {
+            return false;
+        }
+        if (this.protocol != other.protocol) {
+            return false;
+        }
+        if (this.sourceAddress != other.sourceAddress) {
+            return false;
+        }
+        if (this.totalLength != other.totalLength) {
+            return false;
+        }
+        if (this.ttl != other.ttl) {
+            return false;
+        }
+        if (this.version != other.version) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLC.java b/utils/misc/src/main/java/org/onlab/packet/LLC.java
new file mode 100644
index 0000000..0c64c62
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/LLC.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 this.dsap;
+    }
+
+    public void setDsap(final byte dsap) {
+        this.dsap = dsap;
+    }
+
+    public byte getSsap() {
+        return this.ssap;
+    }
+
+    public void setSsap(final byte ssap) {
+        this.ssap = ssap;
+    }
+
+    public byte getCtrl() {
+        return this.ctrl;
+    }
+
+    public void setCtrl(final byte ctrl) {
+        this.ctrl = ctrl;
+    }
+
+    @Override
+    public byte[] serialize() {
+        final byte[] data = new byte[3];
+        final ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.put(this.dsap);
+        bb.put(this.ssap);
+        bb.put(this.ctrl);
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(final byte[] data, final int offset,
+            final int length) {
+        final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        this.dsap = bb.get();
+        this.ssap = bb.get();
+        this.ctrl = bb.get();
+        return this;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDP.java b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
new file mode 100644
index 0000000..e3909ba
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 this.chassisId;
+    }
+
+    /**
+     * @param chassisId
+     *            the chassisId to set
+     */
+    public LLDP setChassisId(final LLDPTLV chassis) {
+        this.chassisId = chassis;
+        return this;
+    }
+
+    /**
+     * @return the portId
+     */
+    public LLDPTLV getPortId() {
+        return this.portId;
+    }
+
+    /**
+     * @param portId
+     *            the portId to set
+     */
+    public LLDP setPortId(final LLDPTLV portId) {
+        this.portId = portId;
+        return this;
+    }
+
+    /**
+     * @return the ttl
+     */
+    public LLDPTLV getTtl() {
+        return this.ttl;
+    }
+
+    /**
+     * @param ttl
+     *            the ttl to set
+     */
+    public LLDP setTtl(final LLDPTLV ttl) {
+        this.ttl = ttl;
+        return this;
+    }
+
+    /**
+     * @return the optionalTLVList
+     */
+    public List<LLDPTLV> getOptionalTLVList() {
+        return this.optionalTLVList;
+    }
+
+    /**
+     * @param optionalTLVList
+     *            the optionalTLVList to set
+     */
+    public LLDP setOptionalTLVList(final 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 (final LLDPTLV tlv : this.optionalTLVList) {
+            length += 2 + tlv.getLength();
+        }
+
+        final byte[] data = new byte[length];
+        final ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.put(this.chassisId.serialize());
+        bb.put(this.portId.serialize());
+        bb.put(this.ttl.serialize());
+        for (final 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(this.ethType); }
+         */
+
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(final byte[] data, final int offset,
+            final int length) {
+        final 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
+                + (this.chassisId == null ? 0 : this.chassisId.hashCode());
+        result = prime * result + this.optionalTLVList.hashCode();
+        result = prime * result
+                + (this.portId == null ? 0 : this.portId.hashCode());
+        result = prime * result + (this.ttl == null ? 0 : this.ttl.hashCode());
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof LLDP)) {
+            return false;
+        }
+        final LLDP other = (LLDP) obj;
+        if (this.chassisId == null) {
+            if (other.chassisId != null) {
+                return false;
+            }
+        } else if (!this.chassisId.equals(other.chassisId)) {
+            return false;
+        }
+        if (!this.optionalTLVList.equals(other.optionalTLVList)) {
+            return false;
+        }
+        if (this.portId == null) {
+            if (other.portId != null) {
+                return false;
+            }
+        } else if (!this.portId.equals(other.portId)) {
+            return false;
+        }
+        if (this.ttl == null) {
+            if (other.ttl != null) {
+                return false;
+            }
+        } else if (!this.ttl.equals(other.ttl)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java b/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
new file mode 100644
index 0000000..4b1f424
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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() {
+        this.type = LLDPOrganizationalTLV.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(final byte[] oui) {
+        if (oui.length != LLDPOrganizationalTLV.OUI_LENGTH) {
+            throw new IllegalArgumentException("The length of OUI must be "
+                    + LLDPOrganizationalTLV.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(this.oui, this.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(final 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 this.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(final byte[] infoString) {
+        if (infoString.length > LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH) {
+            throw new IllegalArgumentException(
+                    "The length of infoString cannot exceed "
+                            + LLDPOrganizationalTLV.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(final String infoString) {
+        final byte[] infoStringBytes = infoString.getBytes(Charset
+                .forName("UTF-8"));
+        return this.setInfoString(infoStringBytes);
+    }
+
+    /**
+     * Returns the value of information string.
+     *
+     * @return the value of information string.
+     */
+    public byte[] getInfoString() {
+        return Arrays.copyOf(this.infoString, this.infoString.length);
+    }
+
+    @Override
+    public byte[] serialize() {
+        final int valueLength = LLDPOrganizationalTLV.OUI_LENGTH
+                + LLDPOrganizationalTLV.SUBTYPE_LENGTH + this.infoString.length;
+        this.value = new byte[valueLength];
+        final ByteBuffer bb = ByteBuffer.wrap(this.value);
+        bb.put(this.oui);
+        bb.put(this.subType);
+        bb.put(this.infoString);
+        return super.serialize();
+    }
+
+    @Override
+    public LLDPTLV deserialize(final ByteBuffer bb) {
+        super.deserialize(bb);
+        final ByteBuffer optionalField = ByteBuffer.wrap(this.value);
+
+        final byte[] oui = new byte[LLDPOrganizationalTLV.OUI_LENGTH];
+        optionalField.get(oui);
+        this.setOUI(oui);
+
+        this.setSubType(optionalField.get());
+
+        final byte[] infoString = new byte[this.getLength()
+                - LLDPOrganizationalTLV.OUI_LENGTH
+                - LLDPOrganizationalTLV.SUBTYPE_LENGTH];
+        optionalField.get(infoString);
+        this.setInfoString(infoString);
+        return this;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 1423;
+        int result = 1;
+        result = prime * result + this.type;
+        result = prime * result + this.length;
+        result = prime * result + Arrays.hashCode(this.oui);
+        result = prime * result + this.subType;
+        result = prime * result + Arrays.hashCode(this.infoString);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof LLDPOrganizationalTLV)) {
+            return false;
+        }
+
+        final 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/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java b/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
new file mode 100644
index 0000000..207b928
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 this.type;
+    }
+
+    /**
+     * @param type
+     *            the type to set
+     */
+    public LLDPTLV setType(final byte type) {
+        this.type = type;
+        return this;
+    }
+
+    /**
+     * @return the length
+     */
+    public short getLength() {
+        return this.length;
+    }
+
+    /**
+     * @param length
+     *            the length to set
+     */
+    public LLDPTLV setLength(final short length) {
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * @return the value
+     */
+    public byte[] getValue() {
+        return this.value;
+    }
+
+    /**
+     * @param value
+     *            the value to set
+     */
+    public LLDPTLV setValue(final byte[] value) {
+        this.value = value;
+        return this;
+    }
+
+    public byte[] serialize() {
+        // type = 7 bits
+        // info string length 9 bits, each value == byte
+        // info string
+        final short scratch = (short) ((0x7f & this.type) << 9 | 0x1ff & this.length);
+        final byte[] data = new byte[2 + this.length];
+        final ByteBuffer bb = ByteBuffer.wrap(data);
+        bb.putShort(scratch);
+        if (this.value != null) {
+            bb.put(this.value);
+        }
+        return data;
+    }
+
+    public LLDPTLV deserialize(final 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 + this.length;
+        result = prime * result + this.type;
+        result = prime * result + Arrays.hashCode(this.value);
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof LLDPTLV)) {
+            return false;
+        }
+        final LLDPTLV other = (LLDPTLV) obj;
+        if (this.length != other.length) {
+            return false;
+        }
+        if (this.type != other.type) {
+            return false;
+        }
+        if (!Arrays.equals(this.value, other.value)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/MACAddress.java b/utils/misc/src/main/java/org/onlab/packet/MACAddress.java
new file mode 100644
index 0000000..7d6f77e
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/MACAddress.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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 org.onlab.packet;
+
+import java.util.Arrays;
+
+/**
+ * The class representing MAC address.
+ *
+ * @author Sho Shimizu (sho.shimizu@gmail.com)
+ */
+public class MACAddress {
+    public static final int MAC_ADDRESS_LENGTH = 6;
+    private byte[] address = new byte[MACAddress.MAC_ADDRESS_LENGTH];
+
+    public MACAddress(final byte[] address) {
+        this.address = Arrays.copyOf(address, MACAddress.MAC_ADDRESS_LENGTH);
+    }
+
+    /**
+     * Returns a MAC address instance representing the value of the specified
+     * {@code String}.
+     *
+     * @param address
+     *            the String representation of the MAC Address to be parsed.
+     * @return a MAC Address instance representing the value of the specified
+     *         {@code String}.
+     * @throws IllegalArgumentException
+     *             if the string cannot be parsed as a MAC address.
+     */
+    public static MACAddress valueOf(final String address) {
+        final String[] elements = address.split(":");
+        if (elements.length != MACAddress.MAC_ADDRESS_LENGTH) {
+            throw new IllegalArgumentException(
+                    "Specified MAC Address must contain 12 hex digits"
+                            + " separated pairwise by :'s.");
+        }
+
+        final byte[] addressInBytes = new byte[MACAddress.MAC_ADDRESS_LENGTH];
+        for (int i = 0; i < MACAddress.MAC_ADDRESS_LENGTH; i++) {
+            final String element = elements[i];
+            addressInBytes[i] = (byte) Integer.parseInt(element, 16);
+        }
+
+        return new MACAddress(addressInBytes);
+    }
+
+    /**
+     * Returns a MAC address instance representing the specified {@code byte}
+     * array.
+     *
+     * @param address
+     *            the byte array to be parsed.
+     * @return a MAC address instance representing the specified {@code byte}
+     *         array.
+     * @throws IllegalArgumentException
+     *             if the byte array cannot be parsed as a MAC address.
+     */
+    public static MACAddress valueOf(final byte[] address) {
+        if (address.length != MACAddress.MAC_ADDRESS_LENGTH) {
+            throw new IllegalArgumentException("the length is not "
+                    + MACAddress.MAC_ADDRESS_LENGTH);
+        }
+
+        return new MACAddress(address);
+    }
+
+    /**
+     * Returns a MAC address instance representing the specified {@code long}
+     * value. The lower 48 bits of the long value are used to parse as a MAC
+     * address.
+     *
+     * @param address
+     *            the long value to be parsed. The lower 48 bits are used for a
+     *            MAC address.
+     * @return a MAC address instance representing the specified {@code long}
+     *         value.
+     * @throws IllegalArgumentException
+     *             if the long value cannot be parsed as a MAC address.
+     */
+    public static MACAddress valueOf(final long address) {
+        final byte[] addressInBytes = new byte[] {
+                (byte) (address >> 40 & 0xff), (byte) (address >> 32 & 0xff),
+                (byte) (address >> 24 & 0xff), (byte) (address >> 16 & 0xff),
+                (byte) (address >> 8 & 0xff), (byte) (address >> 0 & 0xff) };
+
+        return new MACAddress(addressInBytes);
+    }
+
+    /**
+     * Returns the length of the {@code MACAddress}.
+     *
+     * @return the length of the {@code MACAddress}.
+     */
+    public int length() {
+        return this.address.length;
+    }
+
+    /**
+     * Returns the value of the {@code MACAddress} as a {@code byte} array.
+     *
+     * @return the numeric value represented by this object after conversion to
+     *         type {@code byte} array.
+     */
+    public byte[] toBytes() {
+        return Arrays.copyOf(this.address, this.address.length);
+    }
+
+    /**
+     * Returns the value of the {@code MACAddress} as a {@code long}.
+     *
+     * @return the numeric value represented by this object after conversion to
+     *         type {@code long}.
+     */
+    public long toLong() {
+        long mac = 0;
+        for (int i = 0; i < 6; i++) {
+            final long t = (this.address[i] & 0xffL) << (5 - i) * 8;
+            mac |= t;
+        }
+        return mac;
+    }
+
+    /**
+     * Returns {@code true} if the MAC address is the broadcast address.
+     *
+     * @return {@code true} if the MAC address is the broadcast address.
+     */
+    public boolean isBroadcast() {
+        for (final byte b : this.address) {
+            if (b != -1) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns {@code true} if the MAC address is the multicast address.
+     *
+     * @return {@code true} if the MAC address is the multicast address.
+     */
+    public boolean isMulticast() {
+        if (this.isBroadcast()) {
+            return false;
+        }
+        return (this.address[0] & 0x01) != 0;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof MACAddress)) {
+            return false;
+        }
+
+        final MACAddress other = (MACAddress) o;
+        return Arrays.equals(this.address, other.address);
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(this.address);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        for (final byte b : this.address) {
+            if (builder.length() > 0) {
+                builder.append(":");
+            }
+            builder.append(String.format("%02X", b & 0xFF));
+        }
+        return builder.toString();
+    }
+
+    /**
+     * @return MAC address in string representation without colons (useful for
+     *         radix tree storage)
+     */
+    public String toStringNoColon() {
+        final StringBuilder builder = new StringBuilder();
+        for (final byte b : this.address) {
+            builder.append(String.format("%02X", b & 0xFF));
+        }
+        return builder.toString();
+    }
+
+    public byte[] getAddress() {
+        return this.address;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/OVXLLDP.java b/utils/misc/src/main/java/org/onlab/packet/OVXLLDP.java
new file mode 100644
index 0000000..f1a4b5f
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/OVXLLDP.java
@@ -0,0 +1,354 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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 org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.lang.ArrayUtils;
+
+
+/**
+ * LLDP packets OpenVirteX uses for discovery of physical network topology.
+ * Refer to IEEE Std 802.1ABTM-2009 for more information.
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class OVXLLDP extends LLDP {
+
+    // ON.Lab OUI and OVX name for organizationally specific TLVs
+    public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
+    public static final String OVX_NAME = "OpenVirteX";
+    public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
+        0x01};
+    public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
+        (byte) 0xc2, 0x00, 0x00, 0x0e};
+    public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+    public static final short ETHERTYPE_VLAN = (short) 0x8100;
+
+    // TLV constants: type, size and subtype
+    // Organizationally specific TLV also have packet offset and contents of TLV
+    // header
+    private static final byte CHASSIS_TLV_TYPE = 1;
+    private static final byte CHASSIS_TLV_SIZE = 7;
+    private static final byte CHASSIS_TLV_SUBTYPE = 4;
+
+    private static final byte PORT_TLV_TYPE = 2;
+    private static final byte PORT_TLV_SIZE = 7;
+    private static final byte PORT_TLV_SUBTYPE = 2;
+
+    private static final byte TTL_TLV_TYPE = 3;
+    private static final byte TTL_TLV_SIZE = 2;
+
+    private static final byte NAME_TLV_TYPE = 127;
+    // 4 = OUI (3) + subtype (1)
+    private static final byte NAME_TLV_SIZE = (byte) (4 + OVXLLDP.OVX_NAME.length());
+    private static final byte NAME_TLV_SUBTYPE = 1;
+    private static final short NAME_TLV_OFFSET = 32;
+    private static final short NAME_TLV_HEADER = (short) ((NAME_TLV_TYPE << 9) | NAME_TLV_SIZE);
+    // Contents of full name TLV
+    private static final byte[] NAME_TLV = ByteBuffer.allocate(NAME_TLV_SIZE + 2)
+            .putShort(NAME_TLV_HEADER).put(ONLAB_OUI).put(NAME_TLV_SUBTYPE)
+            .put(OVX_NAME.getBytes()).array();
+
+    private static final byte DPID_TLV_TYPE = 127;
+    private static final byte DPID_TLV_SIZE = (byte) (12); // 12 = OUI (3) + subtype
+    // (1) + dpid (8)
+    private static final byte DPID_TLV_SUBTYPE = 2;
+    private static final short DPID_TLV_HEADER = (short) ((DPID_TLV_TYPE << 9) | DPID_TLV_SIZE);
+    // Contents of dpid TLV
+    // Note that this does *not* contain the actual dpid since we cannot match
+    // on it
+    private static final byte[] DPID_TLV = ByteBuffer.allocate(DPID_TLV_SIZE + 2 - 8)
+            .putShort(DPID_TLV_HEADER).put(ONLAB_OUI).put(DPID_TLV_SUBTYPE)
+            .array();
+
+    // Pre-built contents of both organizationally specific TLVs
+    private static final byte[] OUI_TLV = ArrayUtils.addAll(NAME_TLV, DPID_TLV);
+
+    // Default switch, port number and TTL
+    private static final byte[] DEFAULT_DPID = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00 };
+    private static final short DEFAULT_PORT = 0;
+    private static final short DEFAULT_TTL = 120; // in seconds
+
+    // Minimum and OVX-generated LLDP packet sizes
+    private static final short MINIMUM_LLDP_SIZE = 61;
+    // Add 12 for 2-byte header of each TLV and a single EndOfLLDPTLV
+    private static final short OVX_LLDP_SIZE = (short) (CHASSIS_TLV_SIZE
+            + PORT_TLV_SIZE + TTL_TLV_SIZE + NAME_TLV_SIZE + DPID_TLV_SIZE + 12);
+
+    // Field offsets in OVX-generated LLDP
+    private static final short ETHERTYPE_OFFSET = 12;
+    private static final short PORT_OFFSET = 26;
+    private static final short DPID_OFFSET = 54;
+
+    // Private member fields
+    // Byte arrays for TLV information string
+    private ByteBuffer bb;
+    private final byte[] chassisId = new byte[CHASSIS_TLV_SIZE];
+    private final byte[] portId = new byte[PORT_TLV_SIZE];
+    private final byte[] ttl = new byte[TTL_TLV_SIZE];
+    private final byte[] ouiName = new byte[NAME_TLV_SIZE];
+    private final byte[] ouiDpid = new byte[DPID_TLV_SIZE];
+
+    // TLVs
+    private final LLDPTLV chassisTLV;
+    private final LLDPTLV portTLV;
+    private final LLDPTLV ttlTLV;
+    private final LLDPTLV ouiNameTLV;
+    private final LLDPTLV ouiDpidTLV;
+    private final List<LLDPTLV> optionalTLVList;
+
+    /**
+     * Instantiates a new OVX LDDP message.
+     */
+    public OVXLLDP() {
+        // Create TLVs
+        this.chassisTLV = new LLDPTLV();
+        this.portTLV = new LLDPTLV();
+        this.ttlTLV = new LLDPTLV();
+        this.ouiNameTLV = new LLDPTLV();
+        this.ouiDpidTLV = new LLDPTLV();
+        this.optionalTLVList = new LinkedList<LLDPTLV>();
+        this.optionalTLVList.add(this.ouiNameTLV);
+        this.optionalTLVList.add(this.ouiDpidTLV);
+
+        // Add TLVs to LLDP packet
+        this.setChassisId(this.chassisTLV);
+        this.setPortId(this.portTLV);
+        this.setTtl(this.ttlTLV);
+        this.setOptionalTLVList(this.optionalTLVList);
+
+        // Set TLVs to default values
+        this.setChassisTLV(DEFAULT_DPID);
+        this.setPortTLV(DEFAULT_PORT);
+        this.setTTLTLV(DEFAULT_TTL);
+        this.setOUIName(OVXLLDP.OVX_NAME);
+        this.setOUIDpid(DEFAULT_DPID);
+    }
+
+    /**
+     * Sets chassis TLV. Note that we can only put 6 bytes in the chassis ID, so
+     * we use another organizationally specific TLV to put the full dpid (see
+     * setOUIDpid()).
+     *
+     * @param dpid the switch DPID
+     */
+    private void setChassisTLV(final byte[] dpid) {
+        this.bb = ByteBuffer.wrap(this.chassisId);
+        this.bb.put(CHASSIS_TLV_SUBTYPE);
+        for (int i = 2; i < 8; i++) {
+            bb.put(dpid[i]);
+        }
+        this.chassisTLV.setLength(CHASSIS_TLV_SIZE);
+        this.chassisTLV.setType(CHASSIS_TLV_TYPE);
+        this.chassisTLV.setValue(this.chassisId);
+    }
+
+    /**
+     * Sets port TLV.
+     *
+     * @param portNumber the port number
+     */
+    private void setPortTLV(final long portNumber) {
+        this.bb = ByteBuffer.wrap(this.portId);
+        this.bb.put(PORT_TLV_SUBTYPE);
+        this.bb.putLong(portNumber);
+
+        this.portTLV.setLength(PORT_TLV_SIZE);
+        this.portTLV.setType(PORT_TLV_TYPE);
+        this.portTLV.setValue(this.portId);
+    }
+
+    /**
+     * Sets Time To Live TLV.
+     *
+     * @param time the time to live
+     */
+    private void setTTLTLV(final short time) {
+        this.bb = ByteBuffer.wrap(this.ttl);
+        this.bb.putShort(time);
+
+        this.ttlTLV.setLength(TTL_TLV_SIZE);
+        this.ttlTLV.setType(TTL_TLV_TYPE);
+        this.ttlTLV.setValue(this.ttl);
+    }
+
+    /**
+     * Set. organizationally specific TLV for OVX name (subtype 1).
+     *
+     * @param name the name
+     */
+    private void setOUIName(final String name) {
+        this.bb = ByteBuffer.wrap(ouiName);
+        this.bb.put(OVXLLDP.ONLAB_OUI);
+        this.bb.put(NAME_TLV_SUBTYPE);
+        this.bb.put(name.getBytes());
+
+        this.ouiNameTLV.setLength(NAME_TLV_SIZE);
+        this.ouiNameTLV.setType(NAME_TLV_TYPE);
+        this.ouiNameTLV.setValue(ouiName);
+    }
+
+    /**
+     * Sets organizationally specific TLV for OVX full dpid (subtype 2).
+     *
+     * @param dpid the switch DPID
+     */
+    private void setOUIDpid(final byte[] dpid) {
+        this.bb = ByteBuffer.wrap(ouiDpid);
+        this.bb.put(OVXLLDP.ONLAB_OUI);
+        this.bb.put(DPID_TLV_SUBTYPE);
+        this.bb.put(dpid);
+
+        this.ouiDpidTLV.setLength(DPID_TLV_SIZE);
+        this.ouiDpidTLV.setType(DPID_TLV_TYPE);
+        this.ouiDpidTLV.setValue(ouiDpid);
+    }
+
+    /**
+     * Sets switch DPID in LLDP packet.
+     *
+     * @param sw the switch instance
+     */
+    public void setSwitch(long dp) {
+        final byte[] dpid = ByteBuffer.allocate(8).putLong(dp)
+                .array();
+        this.setChassisTLV(dpid);
+        this.setOUIDpid(dpid);
+    }
+
+    /**
+     * Sets port in LLDP packet.
+     *
+     * @param port the port instance
+     */
+    public void setPort(long port) {
+        long portNumber = port;
+        this.setPortTLV(portNumber);
+    }
+
+    /**
+     * Serializes full LLDP packet to byte array. Need to set both switch and
+     * port before you can serialize.
+     */
+    @Override
+    public byte[] serialize() {
+        return super.serialize();
+    }
+
+    /**
+     * Checks if LLDP packet has correct size, LLDP multicast address, and
+     * ethertype. Packet assumed to have Ethernet header.
+     *
+     * @param packet
+     * @return true if packet is LLDP, false otherwise
+     */
+    public static boolean isLLDP(final byte[] packet) {
+        // Does packet exist and does it have the mininum size?
+        if (packet == null || packet.length < MINIMUM_LLDP_SIZE) {
+            return false;
+        }
+
+        // Packet has LLDP multicast destination address?
+        final ByteBuffer bb = ByteBuffer.wrap(packet);
+        final byte[] dst = new byte[6];
+        bb.get(dst);
+
+        if (!(Arrays.equals(dst, OVXLLDP.LLDP_NICIRA)
+                || Arrays.equals(dst, OVXLLDP.LLDP_MULTICAST) || Arrays.equals(
+                        dst, OVXLLDP.BDDP_MULTICAST))) {
+
+            return false;
+        }
+
+        // Fetch ethertype, skip VLAN tag if it's there
+        short etherType = bb.getShort(ETHERTYPE_OFFSET);
+        if (etherType == ETHERTYPE_VLAN) {
+            etherType = bb.getShort(ETHERTYPE_OFFSET + 4);
+        }
+
+        // Check ethertype
+        if (etherType == Ethernet.TYPE_LLDP) {
+            return true;
+        }
+        if (etherType == Ethernet.TYPE_BSN) {
+            return true;
+        }
+
+        return false;
+
+    }
+
+    /**
+     * Checks if packet has size of OVX-generated LLDP, and correctness of two
+     * organizationally specific TLVs that use ON.Lab's OUI. Assumes packet is
+     * valid LLDP packet
+     *
+     * @param packet
+     * @return
+     */
+    public static boolean isOVXLLDP(byte[] packet) {
+        if (packet.length < OVX_LLDP_SIZE) {
+            return false;
+        }
+
+        // Extra offset due to VLAN tag
+        final ByteBuffer bb = ByteBuffer.wrap(packet);
+        int offset = 0;
+        if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
+                && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
+            offset = 4;
+        }
+
+        // Compare packet's organizationally specific TLVs to the expected
+        // values
+        for (int i = 0; i < OUI_TLV.length; i++) {
+            if (packet[NAME_TLV_OFFSET + offset + i] != OUI_TLV[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Extracts dpid and port from OVX-generated LLDP packet.
+     *
+     * @param packet
+     * @return Dpid and port
+     */
+    /*    public static long parseLLDP(final byte[] packet) {
+        final ByteBuffer bb = ByteBuffer.wrap(packet);
+
+        // Extra offset due to VLAN tag
+        int offset = 0;
+        if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
+                && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
+            offset = 4;
+        }
+
+        final short port = bb.getLong(PORT_OFFSET + offset);
+        final long dpid = bb.getLong(DPID_OFFSET + offset);
+
+        return dpid
+    }
+     */
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/TCP.java b/utils/misc/src/main/java/org/onlab/packet/TCP.java
new file mode 100644
index 0000000..95a8d8f
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/TCP.java
@@ -0,0 +1,337 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 this.sourcePort;
+    }
+
+    /**
+     * @param sourcePort
+     *            the sourcePort to set
+     */
+    public TCP setSourcePort(final short sourcePort) {
+        this.sourcePort = sourcePort;
+        return this;
+    }
+
+    /**
+     * @return the destinationPort
+     */
+    public short getDestinationPort() {
+        return this.destinationPort;
+    }
+
+    /**
+     * @param destinationPort
+     *            the destinationPort to set
+     */
+    public TCP setDestinationPort(final short destinationPort) {
+        this.destinationPort = destinationPort;
+        return this;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return this.checksum;
+    }
+
+    public int getSequence() {
+        return this.sequence;
+    }
+
+    public TCP setSequence(final int seq) {
+        this.sequence = seq;
+        return this;
+    }
+
+    public int getAcknowledge() {
+        return this.acknowledge;
+    }
+
+    public TCP setAcknowledge(final int ack) {
+        this.acknowledge = ack;
+        return this;
+    }
+
+    public byte getDataOffset() {
+        return this.dataOffset;
+    }
+
+    public TCP setDataOffset(final byte offset) {
+        this.dataOffset = offset;
+        return this;
+    }
+
+    public short getFlags() {
+        return this.flags;
+    }
+
+    public TCP setFlags(final short flags) {
+        this.flags = flags;
+        return this;
+    }
+
+    public short getWindowSize() {
+        return this.windowSize;
+    }
+
+    public TCP setWindowSize(final short windowSize) {
+        this.windowSize = windowSize;
+        return this;
+    }
+
+    public short getTcpChecksum() {
+        return this.checksum;
+    }
+
+    public TCP setTcpChecksum(final short checksum) {
+        this.checksum = checksum;
+        return this;
+    }
+
+    @Override
+    public void resetChecksum() {
+        this.checksum = 0;
+        super.resetChecksum();
+    }
+
+    public short getUrgentPointer(final short urgentPointer) {
+        return this.urgentPointer;
+    }
+
+    public TCP setUrgentPointer(final short urgentPointer) {
+        this.urgentPointer = urgentPointer;
+        return this;
+    }
+
+    public byte[] getOptions() {
+        return this.options;
+    }
+
+    public TCP setOptions(final byte[] options) {
+        this.options = options;
+        this.dataOffset = (byte) (20 + options.length + 3 >> 2);
+        return this;
+    }
+
+    /**
+     * @param checksum
+     *            the checksum to set
+     */
+    public TCP setChecksum(final 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
+     */
+    @Override
+    public byte[] serialize() {
+        int length;
+        if (this.dataOffset == 0) {
+            this.dataOffset = 5; // default header length
+        }
+        length = this.dataOffset << 2;
+        byte[] payloadData = null;
+        if (this.payload != null) {
+            this.payload.setParent(this);
+            payloadData = this.payload.serialize();
+            length += payloadData.length;
+        }
+
+        final byte[] data = new byte[length];
+        final 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 | this.dataOffset << 12));
+        bb.putShort(this.windowSize);
+        bb.putShort(this.checksum);
+        bb.putShort(this.urgentPointer);
+        if (this.dataOffset > 5) {
+            int padding;
+            bb.put(this.options);
+            padding = (this.dataOffset << 2) - 20 - this.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) {
+                final 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 + this.checksum;
+        result = prime * result + this.destinationPort;
+        result = prime * result + this.sourcePort;
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof TCP)) {
+            return false;
+        }
+        final TCP other = (TCP) obj;
+        // May want to compare fields based on the flags set
+        return this.checksum == other.checksum
+                && this.destinationPort == other.destinationPort
+                && this.sourcePort == other.sourcePort
+                && this.sequence == other.sequence
+                && this.acknowledge == other.acknowledge
+                && this.dataOffset == other.dataOffset
+                && this.flags == other.flags
+                && this.windowSize == other.windowSize
+                && this.urgentPointer == other.urgentPointer
+                && (this.dataOffset == 5 || this.options.equals(other.options));
+    }
+
+    @Override
+    public IPacket deserialize(final byte[] data, final int offset,
+            final int length) {
+        final 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 = (this.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 (final IndexOutOfBoundsException e) {
+                this.options = null;
+            }
+        }
+
+        this.payload = new Data();
+        this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
+                - bb.position());
+        this.payload.setParent(this);
+        return this;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/UDP.java b/utils/misc/src/main/java/org/onlab/packet/UDP.java
new file mode 100644
index 0000000..41e48d3
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/UDP.java
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ ******************************************************************************/
+/**
+ * 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 org.onlab.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 final short DHCP_SERVER_PORT = (short) 67;
+    public static final short DHCP_CLIENT_PORT = (short) 68;
+
+    static {
+        UDP.decodeMap = new HashMap<Short, Class<? extends IPacket>>();
+        /*
+         * Disable DHCP until the deserialize code is hardened to deal with
+         * garbage input
+         */
+        UDP.decodeMap.put(UDP.DHCP_SERVER_PORT, DHCP.class);
+        UDP.decodeMap.put(UDP.DHCP_CLIENT_PORT, DHCP.class);
+
+    }
+
+    protected short sourcePort;
+    protected short destinationPort;
+    protected short length;
+    protected short checksum;
+
+    /**
+     * @return the sourcePort
+     */
+    public short getSourcePort() {
+        return this.sourcePort;
+    }
+
+    /**
+     * @param sourcePort
+     *            the sourcePort to set
+     */
+    public UDP setSourcePort(final short sourcePort) {
+        this.sourcePort = sourcePort;
+        return this;
+    }
+
+    /**
+     * @return the destinationPort
+     */
+    public short getDestinationPort() {
+        return this.destinationPort;
+    }
+
+    /**
+     * @param destinationPort
+     *            the destinationPort to set
+     */
+    public UDP setDestinationPort(final short destinationPort) {
+        this.destinationPort = destinationPort;
+        return this;
+    }
+
+    /**
+     * @return the length
+     */
+    public short getLength() {
+        return this.length;
+    }
+
+    /**
+     * @return the checksum
+     */
+    public short getChecksum() {
+        return this.checksum;
+    }
+
+    /**
+     * @param checksum
+     *            the checksum to set
+     */
+    public UDP setChecksum(final 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
+     */
+    @Override
+    public byte[] serialize() {
+        byte[] payloadData = null;
+        if (this.payload != null) {
+            this.payload.setParent(this);
+            payloadData = this.payload.serialize();
+        }
+
+        this.length = (short) (8 + (payloadData == null ? 0
+                : payloadData.length));
+
+        final byte[] data = new byte[this.length];
+        final 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) {
+                final 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 + this.checksum;
+        result = prime * result + this.destinationPort;
+        result = prime * result + this.length;
+        result = prime * result + this.sourcePort;
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof UDP)) {
+            return false;
+        }
+        final UDP other = (UDP) obj;
+        if (this.checksum != other.checksum) {
+            return false;
+        }
+        if (this.destinationPort != other.destinationPort) {
+            return false;
+        }
+        if (this.length != other.length) {
+            return false;
+        }
+        if (this.sourcePort != other.sourcePort) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public IPacket deserialize(final byte[] data, final int offset,
+            final int length) {
+        final 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 (final 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 (final Exception e) {
+                throw new RuntimeException("Failure instantiating class", e);
+            }
+        } else {
+            this.payload = new Data();
+        }
+        this.payload = this.payload.deserialize(data, bb.position(), bb.limit()
+                - bb.position());
+        this.payload.setParent(this);
+        return this;
+    }
+}
diff --git a/utils/pom.xml b/utils/pom.xml
index c956abe..a34aa02 100644
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -27,6 +27,11 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.3</version>
+        </dependency>
     </dependencies>
 
     <build>