/* * Copyright 2014-2015 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 org.onlab.packet.ipv6.Authentication; import org.onlab.packet.ipv6.DestinationOptions; import org.onlab.packet.ipv6.EncapSecurityPayload; import org.onlab.packet.ipv6.Fragment; import org.onlab.packet.ipv6.HopByHopOptions; import org.onlab.packet.ipv6.IExtensionHeader; import org.onlab.packet.ipv6.Routing; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import static org.onlab.packet.PacketUtils.checkInput; /** * Implements IPv6 packet format. (RFC 2460) */ public class IPv6 extends BasePacket implements IExtensionHeader { public static final byte FIXED_HEADER_LENGTH = 40; // bytes public static final byte PROTOCOL_TCP = 0x6; public static final byte PROTOCOL_UDP = 0x11; public static final byte PROTOCOL_ICMP6 = 0x3A; public static final byte PROTOCOL_HOPOPT = 0x00; public static final byte PROTOCOL_ROUTING = 0x2B; public static final byte PROTOCOL_FRAG = 0x2C; public static final byte PROTOCOL_ESP = 0x32; public static final byte PROTOCOL_AH = 0x33; public static final byte PROTOCOL_DSTOPT = 0x3C; public static final Map> PROTOCOL_DESERIALIZER_MAP = new HashMap<>(); static { IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ICMP6, ICMP6.deserializer()); IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_TCP, TCP.deserializer()); IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_UDP, UDP.deserializer()); IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_HOPOPT, HopByHopOptions.deserializer()); IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ROUTING, Routing.deserializer()); IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_FRAG, Fragment.deserializer()); IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_ESP, EncapSecurityPayload.deserializer()); IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_AH, Authentication.deserializer()); IPv6.PROTOCOL_DESERIALIZER_MAP.put(IPv6.PROTOCOL_DSTOPT, DestinationOptions.deserializer()); } protected byte version; protected byte trafficClass; protected int flowLabel; protected short payloadLength; protected byte nextHeader; protected byte hopLimit; protected byte[] sourceAddress = new byte[Ip6Address.BYTE_LENGTH]; protected byte[] destinationAddress = new byte[Ip6Address.BYTE_LENGTH]; /** * Default constructor that sets the version to 6. */ public IPv6() { super(); this.version = 6; } /** * Gets IP version. * * @return the IP version */ public byte getVersion() { return this.version; } /** * Sets IP version. * * @param version the IP version to set * @return this */ public IPv6 setVersion(final byte version) { this.version = version; return this; } /** * Gets traffic class. * * @return the traffic class */ public byte getTrafficClass() { return this.trafficClass; } /** * Sets traffic class. * * @param trafficClass the traffic class to set * @return this */ public IPv6 setTrafficClass(final byte trafficClass) { this.trafficClass = trafficClass; return this; } /** * Gets flow label. * * @return the flow label */ public int getFlowLabel() { return this.flowLabel; } /** * Sets flow label. * * @param flowLabel the flow label to set * @return this */ public IPv6 setFlowLabel(final int flowLabel) { this.flowLabel = flowLabel; return this; } @Override public byte getNextHeader() { return this.nextHeader; } @Override public IPv6 setNextHeader(final byte nextHeader) { this.nextHeader = nextHeader; return this; } /** * Gets hop limit. * * @return the hop limit */ public byte getHopLimit() { return this.hopLimit; } /** * Sets hop limit. * * @param hopLimit the hop limit to set * @return this */ public IPv6 setHopLimit(final byte hopLimit) { this.hopLimit = hopLimit; return this; } /** * Gets source address. * * @return the IPv6 source address */ public byte[] getSourceAddress() { return this.sourceAddress; } /** * Sets source address. * * @param sourceAddress the IPv6 source address to set * @return this */ public IPv6 setSourceAddress(final byte[] sourceAddress) { this.sourceAddress = Arrays.copyOfRange(sourceAddress, 0, Ip6Address.BYTE_LENGTH); return this; } /** * Gets destination address. * * @return the IPv6 destination address */ public byte[] getDestinationAddress() { return this.destinationAddress; } /** * Sets destination address. * * @param destinationAddress the IPv6 destination address to set * @return this */ public IPv6 setDestinationAddress(final byte[] destinationAddress) { this.destinationAddress = Arrays.copyOfRange(destinationAddress, 0, Ip6Address.BYTE_LENGTH); return this; } @Override public byte[] serialize() { byte[] payloadData = null; if (this.payload != null) { this.payload.setParent(this); payloadData = this.payload.serialize(); } this.payloadLength = 0; if (payloadData != null) { this.payloadLength = (short) payloadData.length; } final byte[] data = new byte[FIXED_HEADER_LENGTH + payloadLength]; final ByteBuffer bb = ByteBuffer.wrap(data); bb.putInt((this.version & 0xf) << 28 | (this.trafficClass & 0xff) << 20 | this.flowLabel & 0xfffff); bb.putShort(this.payloadLength); bb.put(this.nextHeader); bb.put(this.hopLimit); bb.put(this.sourceAddress, 0, Ip6Address.BYTE_LENGTH); bb.put(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH); if (payloadData != null) { bb.put(payloadData); } return data; } @Override public IPacket deserialize(final byte[] data, final int offset, final int length) { final ByteBuffer bb = ByteBuffer.wrap(data, offset, length); int iscratch; iscratch = bb.getInt(); this.version = (byte) (iscratch >> 28 & 0xf); this.trafficClass = (byte) (iscratch >> 20 & 0xff); this.flowLabel = iscratch & 0xfffff; this.payloadLength = bb.getShort(); this.nextHeader = bb.get(); this.hopLimit = bb.get(); bb.get(this.sourceAddress, 0, Ip6Address.BYTE_LENGTH); bb.get(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH); Deserializer deserializer; if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) { deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader); } else { deserializer = Data.deserializer(); } try { this.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position()); this.payload.setParent(this); } catch (DeserializationException e) { return this; } return this; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 2521; int result = super.hashCode(); ByteBuffer bb; bb = ByteBuffer.wrap(this.destinationAddress); for (int i = 0; i < 4; i++) { result = prime * result + bb.getInt(); } result = prime * result + this.trafficClass; result = prime * result + this.flowLabel; result = prime * result + this.hopLimit; result = prime * result + this.nextHeader; result = prime * result + this.payloadLength; bb = ByteBuffer.wrap(this.sourceAddress); for (int i = 0; i < 4; i++) { result = prime * result + bb.getInt(); } 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 IPv6)) { return false; } final IPv6 other = (IPv6) obj; if (!Arrays.equals(this.destinationAddress, other.destinationAddress)) { return false; } if (this.trafficClass != other.trafficClass) { return false; } if (this.flowLabel != other.flowLabel) { return false; } if (this.hopLimit != other.hopLimit) { return false; } if (this.nextHeader != other.nextHeader) { return false; } if (this.payloadLength != other.payloadLength) { return false; } if (!Arrays.equals(this.sourceAddress, other.sourceAddress)) { return false; } return true; } /** * Deserializer function for IPv6 packets. * * @return deserializer function */ public static Deserializer deserializer() { return (data, offset, length) -> { checkInput(data, offset, length, FIXED_HEADER_LENGTH); IPv6 ipv6 = new IPv6(); ByteBuffer bb = ByteBuffer.wrap(data, offset, length); int iscratch = bb.getInt(); ipv6.version = (byte) (iscratch >> 28 & 0xf); ipv6.trafficClass = (byte) (iscratch >> 20 & 0xff); ipv6.flowLabel = iscratch & 0xfffff; ipv6.payloadLength = bb.getShort(); ipv6.nextHeader = bb.get(); ipv6.hopLimit = bb.get(); bb.get(ipv6.sourceAddress, 0, Ip6Address.BYTE_LENGTH); bb.get(ipv6.destinationAddress, 0, Ip6Address.BYTE_LENGTH); Deserializer deserializer; if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(ipv6.nextHeader)) { deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(ipv6.nextHeader); } else { deserializer = Data.deserializer(); } ipv6.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position()); ipv6.payload.setParent(ipv6); return ipv6; }; } }