ONOS-512: Implement IPv6 Extension Headers
* Create IExtensionHeader interface
- setNextHeader, getNextHeader interface for all extension header classes
- Except EncapSecurityPayload, in which the nextHeader field is encrypted
* Create BaseOptions class
- Super class of HopByHopOptions and DestinationOptions, since these two are very similar
* Implement following classes with unit test
- HopByHopOptions
- DestinationOptions
- Fragment
- Routing
- Authentication
- EncapSecurityPayload
Change-Id: If65894eccf20ac90f04bc2b0cb42aac6dd5a9674
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPv6.java b/utils/misc/src/main/java/org/onlab/packet/IPv6.java
index 53a1ace..16c6687 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IPv6.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IPv6.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Open Networking Laboratory
+ * 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.
@@ -18,6 +18,13 @@
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.IExtensionHeader;
+import org.onlab.packet.ipv6.HopByHopOptions;
+import org.onlab.packet.ipv6.Routing;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
@@ -26,13 +33,20 @@
/**
* Implements IPv6 packet format. (RFC 2460)
*/
-public class IPv6 extends BasePacket {
+public class IPv6 extends BasePacket implements IExtensionHeader {
public static final byte FIXED_HEADER_LENGTH = 40; // bytes
- // TODO: Implement extension header.
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<Byte, Class<? extends IPacket>> PROTOCOL_CLASS_MAP =
new HashMap<>();
@@ -40,6 +54,12 @@
IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_ICMP6, ICMP6.class);
IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_TCP, TCP.class);
IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_UDP, UDP.class);
+ IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_HOPOPT, HopByHopOptions.class);
+ IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_ROUTING, Routing.class);
+ IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_FRAG, Fragment.class);
+ IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_ESP, EncapSecurityPayload.class);
+ IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_AH, Authentication.class);
+ IPv6.PROTOCOL_CLASS_MAP.put(IPv6.PROTOCOL_DSTOPT, DestinationOptions.class);
}
protected byte version;
@@ -119,21 +139,12 @@
return this;
}
- /**
- * Gets next header.
- *
- * @return the next header
- */
+ @Override
public byte getNextHeader() {
return this.nextHeader;
}
- /**
- * Sets next header.
- *
- * @param nextHeader the next header to set
- * @return this
- */
+ @Override
public IPv6 setNextHeader(final byte nextHeader) {
this.nextHeader = nextHeader;
return this;
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
new file mode 100644
index 0000000..d4c741f
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
@@ -0,0 +1,262 @@
+/*
+ * 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.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Implements IPv6 authentication extension header format. (RFC 4302)
+ */
+public class Authentication extends BasePacket implements IExtensionHeader {
+ public static final byte FIXED_HEADER_LENGTH = 12; // bytes
+ public static final byte LENGTH_UNIT = 4; // bytes per unit
+ public static final byte MINUS = 2;
+
+ protected byte nextHeader;
+ protected byte payloadLength;
+ protected int securityParamIndex;
+ protected int sequence;
+ protected byte[] integrityCheck;
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public Authentication setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets the payload length of this header.
+ *
+ * @return the payload length
+ */
+ public byte getPayloadLength() {
+ return this.payloadLength;
+ }
+
+ /**
+ * Sets the payload length of this header.
+ *
+ * @param payloadLength the payload length to set
+ * @return this
+ */
+ public Authentication setPayloadLength(final byte payloadLength) {
+ this.payloadLength = payloadLength;
+ return this;
+ }
+
+ /**
+ * Gets the security parameter index of this header.
+ *
+ * @return the security parameter index
+ */
+ public int getSecurityParamIndex() {
+ return this.securityParamIndex;
+ }
+
+ /**
+ * Sets the security parameter index of this header.
+ *
+ * @param securityParamIndex the security parameter index to set
+ * @return this
+ */
+ public Authentication setSecurityParamIndex(final int securityParamIndex) {
+ this.securityParamIndex = securityParamIndex;
+ return this;
+ }
+
+ /**
+ * Gets the sequence number of this header.
+ *
+ * @return the sequence number
+ */
+ public int getSequence() {
+ return this.sequence;
+ }
+
+ /**
+ * Sets the sequence number of this header.
+ *
+ * @param sequence the sequence number to set
+ * @return this
+ */
+ public Authentication setSequence(final int sequence) {
+ this.sequence = sequence;
+ return this;
+ }
+
+ /**
+ * Gets the integrity check value of this header.
+ *
+ * @return the integrity check value
+ */
+ public byte[] getIntegrityCheck() {
+ return this.integrityCheck;
+ }
+
+ /**
+ * Sets the integrity check value of this header.
+ *
+ * @param integrityCheck the integrity check value to set
+ * @return this
+ */
+ public Authentication setIngegrityCheck(final byte[] integrityCheck) {
+ this.integrityCheck =
+ Arrays.copyOfRange(integrityCheck, 0, integrityCheck.length);
+ return this;
+ }
+
+ /**
+ * Gets the total length of this header.
+ * According to spec, payload length should be the total length of this AH
+ * in 4-octet unit, minus 2
+ *
+ * @return the total length
+ */
+ public int getTotalLength() {
+ return (this.payloadLength + MINUS) * LENGTH_UNIT;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int headerLength = FIXED_HEADER_LENGTH + integrityCheck.length;
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[headerLength + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.nextHeader);
+ bb.put(this.payloadLength);
+ bb.putShort((short) 0);
+ bb.putInt(this.securityParamIndex);
+ bb.putInt(this.sequence);
+ bb.put(this.integrityCheck, 0, integrityCheck.length);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_AH);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.nextHeader = bb.get();
+ this.payloadLength = bb.get();
+ bb.getShort();
+ this.securityParamIndex = bb.getInt();
+ this.sequence = bb.getInt();
+ int icvLength = getTotalLength() - FIXED_HEADER_LENGTH;
+ this.integrityCheck = new byte[icvLength];
+ bb.get(this.integrityCheck, 0, icvLength);
+
+ IPacket payload;
+ if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
+ final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
+ .get(this.nextHeader);
+ try {
+ payload = clazz.newInstance();
+ } catch (final Exception e) {
+ throw new RuntimeException(
+ "Error parsing payload for Authentication packet", e);
+ }
+ } else {
+ payload = new Data();
+ }
+ this.payload = payload.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(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.nextHeader;
+ result = prime * result + this.payloadLength;
+ result = prime * result + this.securityParamIndex;
+ result = prime * result + this.sequence;
+ for (byte b : this.integrityCheck) {
+ result = prime * result + b;
+ }
+ 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 Authentication)) {
+ return false;
+ }
+ final Authentication other = (Authentication) obj;
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.payloadLength != other.payloadLength) {
+ return false;
+ }
+ if (this.securityParamIndex != other.securityParamIndex) {
+ return false;
+ }
+ if (this.sequence != other.sequence) {
+ return false;
+ }
+ if (!Arrays.equals(this.integrityCheck, other.integrityCheck)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
new file mode 100644
index 0000000..6fd81cf
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
@@ -0,0 +1,222 @@
+/*
+ * 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.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Base class for hop-by-hop options and destination options.
+ */
+public class BaseOptions extends BasePacket implements IExtensionHeader {
+ public static final byte FIXED_HEADER_LENGTH = 2; // bytes
+ public static final byte FIXED_OPTIONS_LENGTH = 6; // bytes
+ public static final byte LENGTH_UNIT = 8; // bytes per unit
+
+ protected byte nextHeader;
+ protected byte headerExtLength;
+ protected byte[] options;
+ protected byte type;
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public BaseOptions setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets the extension length of this header.
+ *
+ * @return header length
+ */
+ public byte getHeaderExtLength() {
+ return this.headerExtLength;
+ }
+
+ /**
+ * Sets the extension length of this header.
+ *
+ * @param headerExtLength the header length to set
+ * @return this
+ */
+ public BaseOptions setHeaderExtLength(final byte headerExtLength) {
+ this.headerExtLength = headerExtLength;
+ return this;
+ }
+
+ /**
+ * Gets the options.
+ *
+ * @return the options
+ */
+ public byte[] getOptions() {
+ return this.options;
+ }
+
+ /**
+ * Sets the options.
+ *
+ * @param options the options to set
+ * @return this
+ */
+ public BaseOptions setOptions(final byte[] options) {
+ this.options =
+ Arrays.copyOfRange(options, 0, options.length);
+ return this;
+ }
+
+ /**
+ * Gets the type of this option.
+ *
+ * @return the type
+ */
+ protected byte getType() {
+ return this.type;
+ }
+
+ /**
+ * Sets the type of this option.
+ * Must be either IPv6.PROTOCOL_HOPOPT or IPv6.PROTOCOL_DSTOPT
+ *
+ * @param type the type to set
+ * @return this
+ */
+ protected BaseOptions setType(final byte type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int headerLength = FIXED_HEADER_LENGTH + options.length;
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[headerLength + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.nextHeader);
+ bb.put(this.headerExtLength);
+ bb.put(this.options, 0, options.length);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(this.type);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.nextHeader = bb.get();
+ this.headerExtLength = bb.get();
+ int optionLength =
+ FIXED_OPTIONS_LENGTH + LENGTH_UNIT * this.headerExtLength;
+ this.options = new byte[optionLength];
+ bb.get(this.options, 0, optionLength);
+
+ IPacket payload;
+ if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
+ final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
+ .get(this.nextHeader);
+ try {
+ payload = clazz.newInstance();
+ } catch (final Exception e) {
+ throw new RuntimeException(
+ "Error parsing payload for BaseOptions packet", e);
+ }
+ } else {
+ payload = new Data();
+ }
+ this.payload = payload.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(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.nextHeader;
+ result = prime * result + this.headerExtLength;
+ for (byte b : this.options) {
+ result = prime * result + b;
+ }
+ 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 BaseOptions)) {
+ return false;
+ }
+ final BaseOptions other = (BaseOptions) obj;
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.headerExtLength != other.headerExtLength) {
+ return false;
+ }
+ if (!Arrays.equals(this.options, other.options)) {
+ return false;
+ }
+ if (this.type != other.type) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java
new file mode 100644
index 0000000..208bdd7
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java
@@ -0,0 +1,29 @@
+/*
+ * 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.ipv6;
+
+import org.onlab.packet.IPv6;
+
+/**
+ * Implements IPv6 Destination Options extension header format. (RFC 2460)
+ */
+public class DestinationOptions extends BaseOptions {
+ public DestinationOptions() {
+ super();
+ this.setType(IPv6.PROTOCOL_DSTOPT);
+ }
+}
\ No newline at end of file
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
new file mode 100644
index 0000000..ffe552f
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
@@ -0,0 +1,161 @@
+/*
+ * 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.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+import java.nio.ByteBuffer;
+
+/**
+ * Implements IPv6 Encapsulating Security Payload (ESP) extension header format.
+ * (RFC 4303)
+ */
+public class EncapSecurityPayload extends BasePacket {
+ public static final byte HEADER_LENGTH = 8; // bytes
+
+ protected int securityParamIndex;
+ protected int sequence;
+ //
+ // NOTE: The remaining fields including payload data, padding length and
+ // next header are encrypted and all considered as a payload of ESP.
+ //
+
+ /**
+ * Gets the security parameter index of this header.
+ *
+ * @return the security parameter index
+ */
+ public int getSecurityParamIndex() {
+ return this.securityParamIndex;
+ }
+
+ /**
+ * Sets the security parameter index of this header.
+ *
+ * @param securityParamIndex the security parameter index to set
+ * @return this
+ */
+ public EncapSecurityPayload setSecurityParamIndex(final int securityParamIndex) {
+ this.securityParamIndex = securityParamIndex;
+ return this;
+ }
+
+ /**
+ * Gets the sequence number of this header.
+ *
+ * @return the sequence number
+ */
+ public int getSequence() {
+ return this.sequence;
+ }
+
+ /**
+ * Sets the sequence number of this header.
+ *
+ * @param sequence the sequence number to set
+ * @return this
+ */
+ public EncapSecurityPayload setSequence(final int sequence) {
+ this.sequence = sequence;
+ 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 bb = ByteBuffer.wrap(data);
+
+ bb.putInt(this.securityParamIndex);
+ bb.putInt(this.sequence);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ESP);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.securityParamIndex = bb.getInt();
+ this.sequence = bb.getInt();
+
+ this.payload = new Data();
+ this.payload.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(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.securityParamIndex;
+ result = prime * result + this.sequence;
+ 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 EncapSecurityPayload)) {
+ return false;
+ }
+ final EncapSecurityPayload other = (EncapSecurityPayload) obj;
+ if (this.securityParamIndex != other.securityParamIndex) {
+ return false;
+ }
+ if (this.sequence != other.sequence) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
new file mode 100644
index 0000000..5a0d4b4
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
@@ -0,0 +1,219 @@
+/*
+ * 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.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implements IPv6 fragment extension header format. (RFC 2460)
+ */
+public class Fragment extends BasePacket implements IExtensionHeader {
+ public static final byte HEADER_LENGTH = 8; // bytes
+
+ protected byte nextHeader;
+ protected short fragmentOffset;
+ protected byte moreFragment;
+ protected int identification;
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public Fragment setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets the fragment offset of this header.
+ *
+ * @return fragment offset
+ */
+ public short getFragmentOffset() {
+ return this.fragmentOffset;
+ }
+
+ /**
+ * Sets the fragment offset of this header.
+ *
+ * @param fragmentOffset the fragment offset to set
+ * @return this
+ */
+ public Fragment setFragmentOffset(final short fragmentOffset) {
+ this.fragmentOffset = fragmentOffset;
+ return this;
+ }
+
+ /**
+ * Gets the more fragment flag of this header.
+ *
+ * @return more fragment flag
+ */
+ public byte getMoreFragment() {
+ return this.moreFragment;
+ }
+
+ /**
+ * Sets the more fragment flag of this header.
+ *
+ * @param moreFragment the more fragment flag to set
+ * @return this
+ */
+ public Fragment setMoreFragment(final byte moreFragment) {
+ this.moreFragment = moreFragment;
+ return this;
+ }
+
+ /**
+ * Gets the identification of this header.
+ *
+ * @return identification
+ */
+ public int getIdentification() {
+ return this.identification;
+ }
+
+ /**
+ * Sets the identification of this header.
+ *
+ * @param identification the identification to set
+ * @return this
+ */
+ public Fragment setIdentification(final int identification) {
+ this.identification = identification;
+ 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 bb = ByteBuffer.wrap(data);
+
+ bb.put(this.nextHeader);
+ bb.put((byte) 0);
+ bb.putShort((short) (
+ (this.fragmentOffset & 0x1fff) << 3 |
+ this.moreFragment & 0x1
+ ));
+ bb.putInt(this.identification);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_FRAG);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.nextHeader = bb.get();
+ bb.get();
+ short sscratch = bb.getShort();
+ this.fragmentOffset = (short) (sscratch >> 3 & 0x1fff);
+ this.moreFragment = (byte) (sscratch & 0x1);
+ this.identification = bb.getInt();
+
+ IPacket payload;
+ if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
+ final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
+ .get(this.nextHeader);
+ try {
+ payload = clazz.newInstance();
+ } catch (final Exception e) {
+ throw new RuntimeException(
+ "Error parsing payload for Fragment packet", e);
+ }
+ } else {
+ payload = new Data();
+ }
+ this.payload = payload.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(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.nextHeader;
+ result = prime * result + this.fragmentOffset;
+ result = prime * result + this.moreFragment;
+ result = prime * result + this.identification;
+ 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 Fragment)) {
+ return false;
+ }
+ final Fragment other = (Fragment) obj;
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.fragmentOffset != other.fragmentOffset) {
+ return false;
+ }
+ if (this.moreFragment != other.moreFragment) {
+ return false;
+ }
+ if (this.identification != other.identification) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java
new file mode 100644
index 0000000..cd8c141
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java
@@ -0,0 +1,29 @@
+/*
+ * 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.ipv6;
+
+import org.onlab.packet.IPv6;
+
+/**
+ * Implements IPv6 Hop-by-hop Options extension header format. (RFC 2460)
+ */
+public class HopByHopOptions extends BaseOptions {
+ public HopByHopOptions() {
+ super();
+ this.setType(IPv6.PROTOCOL_HOPOPT);
+ }
+}
\ No newline at end of file
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java
new file mode 100644
index 0000000..bbe5b7c
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java
@@ -0,0 +1,37 @@
+/*
+ * 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.ipv6;
+
+/**
+ * Interface for IPv6 extension header.
+ */
+public interface IExtensionHeader {
+ /**
+ * Gets the type of next header.
+ *
+ * @return next header
+ */
+ public byte getNextHeader();
+
+ /**
+ * Sets the type of next header.
+ *
+ * @param nextHeader the next header to set
+ * @return this
+ */
+ public IExtensionHeader setNextHeader(final byte nextHeader);
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
new file mode 100644
index 0000000..808d2ec
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
@@ -0,0 +1,251 @@
+/*
+ * 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.ipv6;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Implements IPv6 routing extension header format. (RFC 2460)
+ */
+public class Routing extends BasePacket implements IExtensionHeader {
+ public static final byte FIXED_HEADER_LENGTH = 4; // bytes
+ public static final byte FIXED_ROUTING_DATA_LENGTH = 4; // bytes
+ public static final byte LENGTH_UNIT = 8; // bytes per unit
+
+ protected byte nextHeader;
+ protected byte headerExtLength;
+ protected byte routingType;
+ protected byte segmentsLeft;
+ protected byte[] routingData;
+
+ @Override
+ public byte getNextHeader() {
+ return this.nextHeader;
+ }
+
+ @Override
+ public Routing setNextHeader(final byte nextHeader) {
+ this.nextHeader = nextHeader;
+ return this;
+ }
+
+ /**
+ * Gets the extension length of this header.
+ *
+ * @return header length
+ */
+ public byte getHeaderExtLength() {
+ return this.headerExtLength;
+ }
+
+ /**
+ * Sets the extension length of this header.
+ *
+ * @param headerExtLength the header length to set
+ * @return this
+ */
+ public Routing setHeaderExtLength(final byte headerExtLength) {
+ this.headerExtLength = headerExtLength;
+ return this;
+ }
+
+ /**
+ * Gets the routing type of this header.
+ *
+ * @return routing type
+ */
+ public byte getRoutingType() {
+ return this.routingType;
+ }
+
+ /**
+ * Sets the routing type of this header.
+ *
+ * @param routingType the routing type to set
+ * @return this
+ */
+ public Routing setRoutingType(final byte routingType) {
+ this.routingType = routingType;
+ return this;
+ }
+
+ /**
+ * Gets the number of remaining route segments of this header.
+ *
+ * @return number of remaining route segments
+ */
+ public byte getSegmentsLeft() {
+ return this.segmentsLeft;
+ }
+
+ /**
+ * Sets the number of remaining route segments of this header.
+ *
+ * @param segmentsLeft the number of remaining route segments to set
+ * @return this
+ */
+ public Routing setSegmntsLeft(final byte segmentsLeft) {
+ this.segmentsLeft = segmentsLeft;
+ return this;
+ }
+
+ /**
+ * Gets the routing data.
+ *
+ * @return the routing data
+ */
+ public byte[] getRoutingData() {
+ return this.routingData;
+ }
+
+ /**
+ * Sets the routing data.
+ *
+ * @param routingData the routing data to set
+ * @return this
+ */
+ public Routing setRoutingData(final byte[] routingData) {
+ this.routingData =
+ Arrays.copyOfRange(routingData, 0, routingData.length);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int headerLength = FIXED_HEADER_LENGTH + routingData.length;
+ int payloadLength = 0;
+ if (payloadData != null) {
+ payloadLength = payloadData.length;
+ }
+
+ final byte[] data = new byte[headerLength + payloadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.nextHeader);
+ bb.put(this.headerExtLength);
+ bb.put(this.routingType);
+ bb.put(this.segmentsLeft);
+ bb.put(this.routingData, 0, routingData.length);
+
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IExtensionHeader) {
+ ((IExtensionHeader) this.parent).setNextHeader(IPv6.PROTOCOL_ROUTING);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.nextHeader = bb.get();
+ this.headerExtLength = bb.get();
+ this.routingType = bb.get();
+ this.segmentsLeft = bb.get();
+ int dataLength =
+ FIXED_ROUTING_DATA_LENGTH + LENGTH_UNIT * this.headerExtLength;
+ this.routingData = new byte[dataLength];
+ bb.get(this.routingData, 0, dataLength);
+
+ IPacket payload;
+ if (IPv6.PROTOCOL_CLASS_MAP.containsKey(this.nextHeader)) {
+ final Class<? extends IPacket> clazz = IPv6.PROTOCOL_CLASS_MAP
+ .get(this.nextHeader);
+ try {
+ payload = clazz.newInstance();
+ } catch (final Exception e) {
+ throw new RuntimeException(
+ "Error parsing payload for Routing packet", e);
+ }
+ } else {
+ payload = new Data();
+ }
+ this.payload = payload.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ this.payload.setParent(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.nextHeader;
+ result = prime * result + this.headerExtLength;
+ result = prime * result + this.routingType;
+ result = prime * result + this.segmentsLeft;
+ for (byte b : this.routingData) {
+ result = prime * result + b;
+ }
+ 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 Routing)) {
+ return false;
+ }
+ final Routing other = (Routing) obj;
+ if (this.nextHeader != other.nextHeader) {
+ return false;
+ }
+ if (this.headerExtLength != other.headerExtLength) {
+ return false;
+ }
+ if (this.routingType != other.routingType) {
+ return false;
+ }
+ if (this.segmentsLeft != other.segmentsLeft) {
+ return false;
+ }
+ if (!Arrays.equals(this.routingData, other.routingData)) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java b/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java
new file mode 100644
index 0000000..e4391d5
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 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.
+ */
+
+/**
+ * Utilities for decoding and encoding IPv6 extension headers.
+ */
+package org.onlab.packet.ipv6;