diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
index 645cb306ea18183f4e7da6f4812bdbb623edd32f..d9feae28aa1b21cc1ae35ff4585b13e010eadc8e 100644
--- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java
+++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    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
@@ -35,6 +35,7 @@ public class Ethernet extends BasePacket {
     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_IPv6 = (short) 0x86DD;
     public static final short TYPE_LLDP = (short) 0x88cc;
     public static final short TYPE_BSN = (short) 0x8942;
     public static final short VLAN_UNTAGGED = VlanVid.ZERO.getVlan(); // untagged vlan must be 0x0000 for loxi. We can use the convenient ZERO field
@@ -46,6 +47,7 @@ public class Ethernet extends BasePacket {
         etherTypeClassMap.put(TYPE_ARP, ARP.class);
         etherTypeClassMap.put(TYPE_RARP, ARP.class);
         etherTypeClassMap.put(TYPE_IPv4, IPv4.class);
+        etherTypeClassMap.put(TYPE_IPv6, IPv6.class);
         etherTypeClassMap.put(TYPE_LLDP, LLDP.class);
         etherTypeClassMap.put(TYPE_BSN, BSN.class);
     }
@@ -64,7 +66,7 @@ public class Ethernet extends BasePacket {
         super();
         this.vlanID = VLAN_UNTAGGED;
     }
-    
+
     /**
      * @return the destination MAC
      */
@@ -79,7 +81,7 @@ public class Ethernet extends BasePacket {
         this.destinationMACAddress = MacAddress.of(destinationMACAddress);
         return this;
     }
-    
+
     /**
      * @param destinationMACAddress the destination MAC to set
      */
@@ -110,7 +112,7 @@ public class Ethernet extends BasePacket {
         this.sourceMACAddress = MacAddress.of(sourceMACAddress);
         return this;
     }
-    
+
     /**
      * @param sourceMACAddress the source MAC to set
      */
@@ -171,7 +173,7 @@ public class Ethernet extends BasePacket {
         this.etherType = etherType;
         return this;
     }
-    
+
     /**
      * @return True if the Ethernet frame is broadcast, false otherwise
      */
@@ -179,7 +181,7 @@ public class Ethernet extends BasePacket {
         assert(destinationMACAddress.getLength() == 6);
         return destinationMACAddress.isBroadcast();
     }
-    
+
     /**
      * @return True is the Ethernet frame is multicast, False otherwise
      */
@@ -248,9 +250,9 @@ public class Ethernet extends BasePacket {
         bb.get(srcAddr);
         this.sourceMACAddress = MacAddress.of(srcAddr);
 
-        /* 
+        /*
          * The ethertype is represented as 2 bytes in the packet header;
-         * however, EthType can only parse an int. Negative shorts (1 in 
+         * however, EthType can only parse an int. Negative shorts (1 in
          * the most sig place b/c 2's complement) are still valid ethertypes,
          * but it doesn't appear this way unless we treat the sign bit as
          * part of an unsigned number. If we natively cast the short to an
@@ -269,7 +271,7 @@ public class Ethernet extends BasePacket {
             this.vlanID = VLAN_UNTAGGED;
         }
         this.etherType = etherType;
-        
+
         IPacket payload;
         if (Ethernet.etherTypeClassMap.containsKey((short) this.etherType.getValue())) {
             Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap.get((short) this.etherType.getValue());
@@ -321,7 +323,7 @@ public class Ethernet extends BasePacket {
         if (macBytes.length != 6)
             return false;
         for (int i = 0; i < 6; ++i) {
-            if (HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1 || 
+            if (HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1 ||
                 HEXES.indexOf(macBytes[i].toUpperCase().charAt(1)) == -1) {
                 return false;
             }
@@ -333,7 +335,7 @@ public class Ethernet extends BasePacket {
      * 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 
+     * @return The macAddress as a byte array
      */
     public static byte[] toMACAddress(String macAddress) {
         return MacAddress.of(macAddress).getBytes();
@@ -358,7 +360,7 @@ public class Ethernet extends BasePacket {
     public static byte[] toByteArray(long macAddress) {
         return MacAddress.of(macAddress).getBytes();
     }
-    
+
     @Override
 	public int hashCode() {
 		final int prime = 31;
@@ -410,7 +412,7 @@ public class Ethernet extends BasePacket {
 			return false;
 		return true;
 	}
-    
+
     /* (non-Javadoc)
      * @see java.lang.Object#toString(java.lang.Object)
      */
diff --git a/src/main/java/net/floodlightcontroller/packet/IPv6.java b/src/main/java/net/floodlightcontroller/packet/IPv6.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8af941018bd2ad2f8ae0b37f02d0cee7522d29c
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/packet/IPv6.java
@@ -0,0 +1,255 @@
+package net.floodlightcontroller.packet;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IpProtocol;
+
+/**
+ * @author Jacob Chappell (jacob.chappell@uky.edu)
+ */
+public class IPv6 extends BasePacket {
+    public static Map<IpProtocol, Class<? extends IPacket>> nextHeaderClassMap;
+
+    static {
+        nextHeaderClassMap = new HashMap<IpProtocol, Class<? extends IPacket>>();
+        // TODO: Add ICMPv6, IPv6 Options, etc..
+        nextHeaderClassMap.put(IpProtocol.TCP, TCP.class);
+        nextHeaderClassMap.put(IpProtocol.UDP, UDP.class);
+    }
+
+    public static final int HEADER_LENGTH = 40;
+
+    protected byte version;
+    protected byte trafficClass;
+    protected int flowLabel;
+    protected short payloadLength;
+    protected IpProtocol nextHeader;
+    protected byte hopLimit;
+    protected IPv6Address sourceAddress;
+    protected IPv6Address destinationAddress;
+
+    public IPv6() {
+        super();
+        this.version = 6;
+        nextHeader = IpProtocol.NONE;
+        sourceAddress = IPv6Address.NONE;
+        destinationAddress = IPv6Address.NONE;
+    }
+
+    public byte getVersion() {
+        return version;
+    }
+
+    public IPv6 setVersion(byte version) {
+        this.version = version;
+        return this;
+    }
+
+    public byte getTrafficClass() {
+        return trafficClass;
+    }
+
+    public IPv6 setTrafficClass(byte trafficClass) {
+        this.trafficClass = trafficClass;
+        return this;
+    }
+
+    public int getFlowLabel() {
+        return flowLabel;
+    }
+
+    public IPv6 setFlowLabel(int flowLabel) {
+        this.flowLabel = flowLabel;
+        return this;
+    }
+
+    public short getPayloadLength() {
+        return payloadLength;
+    }
+
+    public IPv6 setPayloadLength(short payloadLength) {
+        this.payloadLength = payloadLength;
+        return this;
+    }
+
+    public IpProtocol getNextHeader() {
+        return nextHeader;
+    }
+
+    public IPv6 setNextHeader(IpProtocol nextHeader) {
+        this.nextHeader = nextHeader;
+        return this;
+    }
+
+    public byte getHopLimit() {
+        return hopLimit;
+    }
+
+    public IPv6 setHopLimit(byte hopLimit) {
+        this.hopLimit = hopLimit;
+        return this;
+    }
+
+    public IPv6Address getSourceAddress() {
+        return sourceAddress;
+    }
+
+    public IPv6 setSourceAddress(IPv6Address sourceAddress) {
+        this.sourceAddress = sourceAddress;
+        return this;
+    }
+
+    public IPv6Address getDestinationAddress() {
+        return destinationAddress;
+    }
+
+    public IPv6 setDestinationAddress(IPv6Address destinationAddress) {
+        this.destinationAddress = destinationAddress;
+        return this;
+    }
+
+    @Override
+    public byte[] serialize() {
+        // Get the raw bytes of the payload we encapsulate.
+        byte[] payloadData = null;
+        if (this.payload != null) {
+            this.payload.setParent(this);
+            payloadData = this.payload.serialize();
+        }
+        // Update our internal payload length.
+        this.payloadLength = (short) ((payloadData != null) ? payloadData.length : 0);
+        // Create a byte buffer to hold the IPv6 packet structure.
+        byte[] data = new byte[HEADER_LENGTH + this.payloadLength];
+        ByteBuffer bb = ByteBuffer.wrap(data);
+        // Add header fields to the byte buffer in the correct order.
+        // Fear not the bit magic that must occur.
+        bb.put((byte) (((this.version & 0xF) << 4) |
+                ((this.trafficClass & 0xF0) >>> 4)));
+        bb.put((byte) (((this.trafficClass & 0xF) << 4) |
+                ((this.flowLabel & 0xF0000) >>> 16)));
+        bb.putShort((short) (this.flowLabel & 0xFFFF));
+        bb.putShort(this.payloadLength);
+        bb.put((byte) this.nextHeader.getIpProtocolNumber());
+        bb.put(this.hopLimit);
+        bb.put(this.sourceAddress.getBytes());
+        bb.put(this.destinationAddress.getBytes());
+        // Add the payload to the byte buffer, if necessary.
+        if (payloadData != null)
+            bb.put(payloadData);
+        // We're done! Return the data.
+        return data;
+    }
+
+    @Override
+    public IPacket deserialize(byte[] data, int offset, int length)
+            throws PacketParsingException {
+        // Wrap the data in a byte buffer for easier retrieval.
+        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+        // Retrieve values from IPv6 header.
+        byte firstByte = bb.get();
+        byte secondByte = bb.get();
+        this.version = (byte) ((firstByte & 0xF0) >>> 4);
+        if (this.version != 6) {
+            throw new PacketParsingException(
+                    "Invalid version for IPv6 packet: " +
+                    this.version);
+        }
+        this.trafficClass = (byte) (((firstByte & 0xF) << 4) |
+                ((secondByte & 0xF0) >>> 4));
+        this.flowLabel = ((secondByte & 0xF) << 16) |
+                (bb.getShort() & 0xFFFF);
+        this.payloadLength = bb.getShort();
+        this.nextHeader = IpProtocol.of(bb.get()); // TODO: U8.f()?
+        this.hopLimit = bb.get();
+        byte[] sourceAddress = new byte[16];
+        bb.get(sourceAddress, 0, 16);
+        byte[] destinationAddress = new byte[16];
+        bb.get(destinationAddress, 0, 16);
+        this.sourceAddress = IPv6Address.of(sourceAddress);
+        this.destinationAddress = IPv6Address.of(destinationAddress);
+        // Retrieve the payload, if possible.
+        IPacket payload;
+        if (IPv6.nextHeaderClassMap.containsKey(this.nextHeader)) {
+            Class<? extends IPacket> clazz = IPv6.nextHeaderClassMap.get(this.nextHeader);
+            try {
+                payload = clazz.newInstance();
+            } catch (Exception e) {
+                throw new RuntimeException("Error parsing payload for IPv6 packet", e);
+            }
+        } else {
+            payload = new Data();
+        }
+        // Deserialize as much of the payload as we can (hopefully all of it).
+        this.payload = payload.deserialize(data, bb.position(),
+                Math.min(this.payloadLength, bb.limit() - bb.position()));
+        this.payload.setParent(this);
+        // We're done!
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime
+                * result
+                + ((destinationAddress == null) ? 0 : destinationAddress
+                        .hashCode());
+        result = prime * result + flowLabel;
+        result = prime * result + hopLimit;
+        result = prime * result
+                + ((nextHeader == null) ? 0 : nextHeader.hashCode());
+        result = prime * result + payloadLength;
+        result = prime * result
+                + ((sourceAddress == null) ? 0 : sourceAddress.hashCode());
+        result = prime * result + trafficClass;
+        result = prime * result + version;
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (!super.equals(obj))
+            return false;
+        if (!(obj instanceof IPv6))
+            return false;
+        IPv6 other = (IPv6) obj;
+        if (destinationAddress == null) {
+            if (other.destinationAddress != null)
+                return false;
+        } else if (!destinationAddress.equals(other.destinationAddress))
+            return false;
+        if (flowLabel != other.flowLabel)
+            return false;
+        if (hopLimit != other.hopLimit)
+            return false;
+        if (nextHeader == null) {
+            if (other.nextHeader != null)
+                return false;
+        } else if (!nextHeader.equals(other.nextHeader))
+            return false;
+        if (payloadLength != other.payloadLength)
+            return false;
+        if (sourceAddress == null) {
+            if (other.sourceAddress != null)
+                return false;
+        } else if (!sourceAddress.equals(other.sourceAddress))
+            return false;
+        if (trafficClass != other.trafficClass)
+            return false;
+        if (version != other.version)
+            return false;
+        return true;
+    }
+}
diff --git a/src/test/java/net/floodlightcontroller/packet/IPv6Test.java b/src/test/java/net/floodlightcontroller/packet/IPv6Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..78607cd836de138a23de516745f2b63aea88028b
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/packet/IPv6Test.java
@@ -0,0 +1,58 @@
+package net.floodlightcontroller.packet;
+
+import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import org.junit.Test;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IpProtocol;
+
+/**
+ * @author Jacob Chappell (jacob.chappell@uky.edu)
+ */
+public class IPv6Test {
+    @Test
+    public void testSerializeWithoutPayload() {
+        byte[] expected = new byte[] {
+                0x64, 0x2B, 0x16, (byte) 0x95, 0x00, 0x00,
+                0x11, (byte) 0xE1, (byte) 0xFE, (byte) 0x80,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x7A, (byte) 0xC5, (byte) 0xFF, (byte) 0xFE,
+                0x2E, 0x77, 0x35, (byte) 0xFE, (byte) 0x80,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x77, 0x5D, (byte) 0xFF, (byte) 0xFE,
+                (byte) 0xC2, 0x30, (byte) 0xFD
+        };
+        IPv6 packet = (new IPv6())
+            .setTrafficClass((byte) 0x42)
+            .setFlowLabel(0xB1695)
+            .setPayloadLength((short) 0)
+            .setNextHeader(IpProtocol.of((short) 0x11))
+            .setHopLimit((byte) 0xE1)
+            .setSourceAddress(IPv6Address.of("fe80::7a:c5ff:fe2e:7735"))
+            .setDestinationAddress(IPv6Address.of("fe80::77:5dff:fec2:30fd"));
+        byte[] actual = packet.serialize();
+        assertTrue(Arrays.equals(expected, actual));
+    }
+
+    @Test
+    public void testDeserialize() throws PacketParsingException {
+        byte[] spudPacket = {
+                0x64, 0x2B, 0x16, (byte) 0x95, 0x00, 0x15,
+                0x11, (byte) 0xE1, (byte) 0xFE, (byte) 0x80,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x7A, (byte) 0xC5, (byte) 0xFF, (byte) 0xFE,
+                0x2E, 0x77, 0x35, (byte) 0xFE, (byte) 0x80,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x77, 0x5D, (byte) 0xFF, (byte) 0xFE,
+                (byte) 0xC2, 0x30, (byte) 0xFD, (byte) 0xD2,
+                0x01, 0x05, 0x7A, 0x00, 0x15, (byte) 0xF6,
+                (byte) 0xC8, (byte) 0xD8, 0x00, 0x00,
+                (byte) 0xD8, 0x4A, (byte) 0xC3, (byte) 0xF2,
+                0x02, 0x44, 0x75, (byte) 0x97, 0x69, 0x40
+        };
+        IPv6 packet = new IPv6();
+        packet.deserialize(spudPacket, 0, spudPacket.length);
+        byte[] packetSerialized = packet.serialize();
+        assertTrue(Arrays.equals(spudPacket, packetSerialized));
+    }
+}