diff options
Diffstat (limited to 'framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java')
-rw-r--r-- | framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java new file mode 100644 index 00000000..c8981302 --- /dev/null +++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java @@ -0,0 +1,366 @@ +/* + * 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.IExtensionHeader; +import org.onlab.packet.ndp.NeighborAdvertisement; +import org.onlab.packet.ndp.NeighborSolicitation; +import org.onlab.packet.ndp.Redirect; +import org.onlab.packet.ndp.RouterAdvertisement; +import org.onlab.packet.ndp.RouterSolicitation; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import static org.onlab.packet.PacketUtils.checkInput; + +/** + * Implements ICMPv6 packet format. (RFC 4443) + */ +public class ICMP6 extends BasePacket { + public static final byte HEADER_LENGTH = 4; // bytes + + // Type + /** Destination Unreachable. */ + public static final byte DEST_UNREACH = (byte) 0x01; + /** Packet Too Big. */ + public static final byte PKT_TOO_BIG = (byte) 0x02; + /** Time Exceeded. */ + public static final byte TIME_EXCEED = (byte) 0x03; + /** Parameter Problem. */ + public static final byte PARAM_ERR = (byte) 0x04; + /** Echo Request. */ + public static final byte ECHO_REQUEST = (byte) 0x80; + /** Echo Reply. */ + public static final byte ECHO_REPLY = (byte) 0x81; + /** Multicast Listener Query. */ + public static final byte MCAST_QUERY = (byte) 0x82; + /** Multicast Listener Report. */ + public static final byte MCAST_REPORT = (byte) 0x83; + /** Multicast Listener Done. */ + public static final byte MCAST_DONE = (byte) 0x84; + /** Router Solicitation. */ + public static final byte ROUTER_SOLICITATION = (byte) 0x85; + /** Router Advertisement. */ + public static final byte ROUTER_ADVERTISEMENT = (byte) 0x86; + /** Neighbor Solicitation. */ + public static final byte NEIGHBOR_SOLICITATION = (byte) 0x87; + /** Neighbor Advertisement. */ + public static final byte NEIGHBOR_ADVERTISEMENT = (byte) 0x88; + /** Redirect Message. */ + public static final byte REDIRECT = (byte) 0x89; + + // Code for DEST_UNREACH + /** No route to destination. */ + public static final byte NO_ROUTE = (byte) 0x00; + /** Communication with destination administratively prohibited. */ + public static final byte COMM_PROHIBIT = (byte) 0x01; + /** Beyond scope of source address. */ + public static final byte BEYOND_SCOPE = (byte) 0x02; + /** Address unreachable. */ + public static final byte ADDR_UNREACH = (byte) 0x03; + /** Port unreachable. */ + public static final byte PORT_UNREACH = (byte) 0x04; + /** Source address failed ingress/egress policy. */ + public static final byte FAIL_POLICY = (byte) 0x05; + /** Reject route to destination. */ + public static final byte REJECT_ROUTE = (byte) 0x06; + /** Error in Source Routing Header. */ + public static final byte SRC_ROUTING_HEADER_ERR = (byte) 0x07; + + // Code for TIME_EXCEED + /** Hop limit exceeded in transit. */ + public static final byte HOP_LIMIT_EXCEED = (byte) 0x00; + /** Fragment reassembly time exceeded. */ + public static final byte DEFRAG_TIME_EXCEED = (byte) 0x01; + + // Code for PARAM_ERR + /** Erroneous header field encountered. */ + public static final byte HDR_FIELD_ERR = (byte) 0x00; + /** Unrecognized Next Header type encountered. */ + public static final byte NEXT_HEADER_ERR = (byte) 0x01; + /** Unrecognized IPv6 option encountered. */ + public static final byte IPV6_OPT_ERR = (byte) 0x01; + + public static final Map<Byte, Deserializer<? extends IPacket>> TYPE_DESERIALIZER_MAP = + new HashMap<>(); + + static { + ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.ROUTER_SOLICITATION, RouterSolicitation.deserializer()); + ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.ROUTER_ADVERTISEMENT, RouterAdvertisement.deserializer()); + ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.NEIGHBOR_SOLICITATION, NeighborSolicitation.deserializer()); + ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement.deserializer()); + ICMP6.TYPE_DESERIALIZER_MAP.put(ICMP6.REDIRECT, Redirect.deserializer()); + } + + protected byte icmpType; + protected byte icmpCode; + protected short checksum; + + private static final byte[] ZERO_ADDRESS = new byte[Ip6Address.BYTE_LENGTH]; + + /** + * Gets ICMP6 type. + * + * @return the ICMP6 type + */ + public byte getIcmpType() { + return this.icmpType; + } + + /** + * Sets ICMP6 type. + * + * @param icmpType the ICMP type to set + * @return this + */ + public ICMP6 setIcmpType(final byte icmpType) { + this.icmpType = icmpType; + return this; + } + + /** + * Gets ICMP6 code. + * + * @return the ICMP6 code + */ + public byte getIcmpCode() { + return this.icmpCode; + } + + /** + * Sets ICMP6 code. + * + * @param icmpCode the ICMP6 code to set + * @return this + */ + public ICMP6 setIcmpCode(final byte icmpCode) { + this.icmpCode = icmpCode; + return this; + } + + /** + * Gets checksum. + * + * @return the checksum + */ + public short getChecksum() { + return this.checksum; + } + + /** + * Sets checksum. + * + * @param checksum the checksum to set + * @return this + */ + public ICMP6 setChecksum(final short checksum) { + this.checksum = checksum; + return this; + } + + @Override + public byte[] serialize() { + byte[] payloadData = null; + if (this.payload != null) { + this.payload.setParent(this); + payloadData = this.payload.serialize(); + } + + int payloadLength = 0; + if (payloadData != null) { + payloadLength = payloadData.length; + } + + final byte[] data = new byte[HEADER_LENGTH + payloadLength]; + final ByteBuffer bbData = ByteBuffer.wrap(data); + + // Creating ByteBuffer for checksum calculation + final byte[] checksumData = + new byte[IPv6.FIXED_HEADER_LENGTH + HEADER_LENGTH + payloadLength]; + final ByteBuffer bbChecksum = ByteBuffer.wrap(checksumData); + + // + // Creating IPv6 Pseudo Header for checksum calculation according + // to RFC 4443 and RFC 2460 + // + IPv6 ipv6Parent = null; + for (IPacket p = this.parent; p != null; p = p.getParent()) { + if (p instanceof IPv6) { + ipv6Parent = (IPv6) p; + break; + } + } + if (ipv6Parent != null) { + bbChecksum.put(ipv6Parent.getSourceAddress()); + bbChecksum.put(ipv6Parent.getDestinationAddress()); + } else { + // NOTE: IPv6 source and destination addresses unknown. Use zeroes. + bbChecksum.put(ZERO_ADDRESS); + bbChecksum.put(ZERO_ADDRESS); + } + bbChecksum.putInt(HEADER_LENGTH + payloadLength); + bbChecksum.put((byte) 0); + bbChecksum.put((byte) 0); + bbChecksum.put((byte) 0); + bbChecksum.put(IPv6.PROTOCOL_ICMP6); + bbChecksum.put(this.icmpType); + bbChecksum.put(this.icmpCode); + bbChecksum.put((byte) 0); + bbChecksum.put((byte) 0); + + bbData.put(this.icmpType); + bbData.put(this.icmpCode); + bbData.putShort(this.checksum); + if (payloadData != null) { + bbData.put(payloadData); + bbChecksum.put(payloadData); + } + + if (this.parent != null) { + if (this.parent instanceof IPv6) { + ((IPv6) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6); + } else if (this.parent instanceof IExtensionHeader) { + ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ICMP6); + } + } + + // compute checksum if needed + if (this.checksum == 0) { + bbData.rewind(); + bbChecksum.rewind(); + int accumulation = 0; + + for (int i = 0; i < checksumData.length / 2; ++i) { + accumulation += 0xffff & bbChecksum.getShort(); + } + // pad to an even number of shorts + if (checksumData.length % 2 > 0) { + accumulation += (bbChecksum.get() & 0xff) << 8; + } + + accumulation = (accumulation >> 16 & 0xffff) + + (accumulation & 0xffff); + this.checksum = (short) (~accumulation & 0xffff); + bbData.putShort(2, this.checksum); + } + return data; + } + + @Override + public IPacket deserialize(final byte[] data, final int offset, + final int length) { + final ByteBuffer bb = ByteBuffer.wrap(data, offset, length); + this.icmpType = bb.get(); + this.icmpCode = bb.get(); + this.checksum = bb.getShort(); + + Deserializer<? extends IPacket> deserializer; + if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmpType)) { + deserializer = TYPE_DESERIALIZER_MAP.get(icmpType); + } 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 = 5807; + int result = super.hashCode(); + result = prime * result + this.icmpType; + result = prime * result + this.icmpCode; + result = prime * result + this.checksum; + 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 ICMP6)) { + return false; + } + final ICMP6 other = (ICMP6) obj; + if (this.icmpType != other.icmpType) { + return false; + } + if (this.icmpCode != other.icmpCode) { + return false; + } + if (this.checksum != other.checksum) { + return false; + } + return true; + } + + /** + * Deserializer function for ICMPv6 packets. + * + * @return deserializer function + */ + public static Deserializer<ICMP6> deserializer() { + return (data, offset, length) -> { + checkInput(data, offset, length, HEADER_LENGTH); + + ICMP6 icmp6 = new ICMP6(); + + ByteBuffer bb = ByteBuffer.wrap(data, offset, length); + + icmp6.icmpType = bb.get(); + icmp6.icmpCode = bb.get(); + icmp6.checksum = bb.getShort(); + + Deserializer<? extends IPacket> deserializer; + if (ICMP6.TYPE_DESERIALIZER_MAP.containsKey(icmp6.icmpType)) { + deserializer = TYPE_DESERIALIZER_MAP.get(icmp6.icmpType); + } else { + deserializer = Data.deserializer(); + } + icmp6.payload = deserializer.deserialize(data, bb.position(), + bb.limit() - bb.position()); + icmp6.payload.setParent(icmp6); + + return icmp6; + }; + } +} |