From 2204df45cf6060388ffe027855e80e6bfa1c74f7 Mon Sep 17 00:00:00 2001
From: Jacob Chappell <chappellind@gmail.com>
Date: Tue, 2 Jun 2015 18:58:20 -0400
Subject: [PATCH] Add "essentials" for IPv6 packet structure. Modify Ethernet
 packet to deserialize IPv6 packets.

---
 .../floodlightcontroller/packet/Ethernet.java |  30 ++-
 .../net/floodlightcontroller/packet/IPv6.java | 242 ++++++++++++++++++
 2 files changed, 258 insertions(+), 14 deletions(-)
 create mode 100644 src/main/java/net/floodlightcontroller/packet/IPv6.java

diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
index 645cb306e..d9feae28a 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 000000000..91f7565e1
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/packet/IPv6.java
@@ -0,0 +1,242 @@
+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 (payload != null) {
+            payload.setParent(this);
+            payloadData = 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[8];
+        bb.get(sourceAddress, 0, 8);
+        byte[] destinationAddress = new byte[8];
+        bb.get(destinationAddress, 0, 8);
+        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() {
+        // TODO: Change prime and the order of results?
+        final int prime = 2521;
+        int result = super.hashCode();
+        result = prime * result + this.destinationAddress.getBytes().hashCode();
+        result = prime * result + this.payloadLength;
+        result = prime * result + this.hopLimit;
+        result = prime * result + this.nextHeader.hashCode();
+        result = prime * result + this.sourceAddress.getBytes().hashCode();
+        result = prime * result + this.trafficClass;
+        result = prime * result + this.flowLabel;
+        result = prime * result + this.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 (!this.destinationAddress.equals(other.destinationAddress))
+            return false;
+        if (this.payloadLength != other.payloadLength)
+            return false;
+        if (this.hopLimit != other.hopLimit)
+            return false;
+        if (!this.nextHeader.equals(other.nextHeader))
+            return false;
+        if (!this.sourceAddress.equals(other.sourceAddress))
+            return false;
+        if (this.trafficClass != other.trafficClass)
+            return false;
+        if (this.flowLabel != other.flowLabel)
+            return false;
+        if (this.version != other.version)
+            return false;
+        return true;
+    }
+}
-- 
GitLab