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