/**
* Copyright 2015, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
**/
package net.floodlightcontroller.packet;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.projectfloodlight.openflow.types.IPv6Address;
import org.projectfloodlight.openflow.types.IpProtocol;
/**
* @author Jacob Chappell (jacob.chappell@uky.edu)
* @edited Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.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();
/*
* If we forgot to include the IpProtocol before serializing,
* try to ascertain what it is from the payload. If it's not
* a payload type we know about, we'll throw an exception.
*/
if ((this.nextHeader == null || this.nextHeader.equals(IpProtocol.NONE))) {
if (IPv6.nextHeaderClassMap.containsValue(this.payload.getClass())) {
Set<Map.Entry<IpProtocol, Class<? extends IPacket>>> entries = IPv6.nextHeaderClassMap.entrySet();
for (Map.Entry<IpProtocol, Class<? extends IPacket>> m : entries) {
if (m.getValue().equals(this.payload.getClass())) {
this.setNextHeader(m.getKey());
log.warn("Setting previously unset IPv6 'next header' to {} as detected by payload {}", m.getKey(), this.getPayload().getClass().toString());
break;
}
}
} else if (this.payload instanceof Data) {
/* we're good -- there shouldn't be an IpProtocol set it it's just data */
} else {
throw new IllegalArgumentException("IpProtocol is unset in IPv6 packet " + this.toString() + ". Unable to determine payload type to set for payload " + this.getPayload().getClass().toString());
}
}
}
// 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 String toString() {
return "IPv6 [version=" + version + ", trafficClass=" + trafficClass
+ ", flowLabel=" + flowLabel + ", payloadLength="
+ payloadLength + ", nextHeader=" + nextHeader + ", hopLimit="
+ hopLimit + ", sourceAddress=" + sourceAddress
+ ", destinationAddress=" + destinationAddress + ", parent="
+ parent + ", payload=" + payload + "]";
}
@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());
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;
}
}