aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/utils/misc/src/main/java/org/onlab/packet
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
commit13d05bc8458758ee39cb829098241e89616717ee (patch)
tree22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/utils/misc/src/main/java/org/onlab/packet
parent6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff)
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/utils/misc/src/main/java/org/onlab/packet')
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ARP.java439
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/BasePacket.java127
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ChassisId.java86
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java632
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java136
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java116
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Data.java132
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java32
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Deserializer.java36
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAP.java264
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAPOL.java199
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java159
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ethernet.java625
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP.java223
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP6.java366
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java335
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java98
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java158
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java202
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPacket.java89
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv4.java731
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv6.java384
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java174
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java104
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java152
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java93
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpAddress.java559
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java303
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLC.java102
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDP.java300
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java225
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java165
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MPLS.java147
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java217
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java74
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java185
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java84
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUS.java423
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUSAttribute.java142
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TCP.java462
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TpPort.java104
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/UDP.java306
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/VlanId.java102
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java300
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java260
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java29
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java188
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java253
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java29
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java37
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java291
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java20
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java278
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java281
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java192
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java225
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java325
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java155
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/package-info.java21
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/package-info.java21
60 files changed, 12897 insertions, 0 deletions
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ARP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ARP.java
new file mode 100644
index 00000000..dc3c07f1
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ARP.java
@@ -0,0 +1,439 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ *
+ *
+ */
+public class ARP extends BasePacket {
+ public static final short HW_TYPE_ETHERNET = 0x1;
+
+ public static final short PROTO_TYPE_IP = 0x800;
+
+ public static final short OP_REQUEST = 0x1;
+ public static final short OP_REPLY = 0x2;
+ public static final short OP_RARP_REQUEST = 0x3;
+ public static final short OP_RARP_REPLY = 0x4;
+
+ public static final short INITIAL_HEADER_LENGTH = 8;
+
+ protected short hardwareType;
+ protected short protocolType;
+ protected byte hardwareAddressLength;
+ protected byte protocolAddressLength;
+ protected short opCode;
+ protected byte[] senderHardwareAddress;
+ protected byte[] senderProtocolAddress;
+ protected byte[] targetHardwareAddress;
+ protected byte[] targetProtocolAddress;
+
+ /**
+ * @return the hardwareType
+ */
+ public short getHardwareType() {
+ return this.hardwareType;
+ }
+
+ /**
+ * @param hwType
+ * the hardwareType to set
+ * @return this
+ */
+ public ARP setHardwareType(final short hwType) {
+ this.hardwareType = hwType;
+ return this;
+ }
+
+ /**
+ * @return the protocolType
+ */
+ public short getProtocolType() {
+ return this.protocolType;
+ }
+
+ /**
+ * @param protoType
+ * the protocolType to set
+ * @return this
+ */
+ public ARP setProtocolType(final short protoType) {
+ this.protocolType = protoType;
+ return this;
+ }
+
+ /**
+ * @return the hardwareAddressLength
+ */
+ public byte getHardwareAddressLength() {
+ return this.hardwareAddressLength;
+ }
+
+ /**
+ * @param hwAddressLength
+ * the hardwareAddressLength to set
+ * @return this
+ */
+ public ARP setHardwareAddressLength(final byte hwAddressLength) {
+ this.hardwareAddressLength = hwAddressLength;
+ return this;
+ }
+
+ /**
+ * @return the protocolAddressLength
+ */
+ public byte getProtocolAddressLength() {
+ return this.protocolAddressLength;
+ }
+
+ /**
+ * @param protoAddressLength
+ * the protocolAddressLength to set
+ * @return this
+ */
+ public ARP setProtocolAddressLength(final byte protoAddressLength) {
+ this.protocolAddressLength = protoAddressLength;
+ return this;
+ }
+
+ /**
+ * @return the opCode
+ */
+ public short getOpCode() {
+ return this.opCode;
+ }
+
+ /**
+ * @param op
+ * the opCode to set
+ * @return this
+ */
+ public ARP setOpCode(final short op) {
+ this.opCode = op;
+ return this;
+ }
+
+ /**
+ * @return the senderHardwareAddress
+ */
+ public byte[] getSenderHardwareAddress() {
+ return this.senderHardwareAddress;
+ }
+
+ /**
+ * @param senderHWAddress
+ * the senderHardwareAddress to set
+ * @return this
+ */
+ public ARP setSenderHardwareAddress(final byte[] senderHWAddress) {
+ this.senderHardwareAddress = senderHWAddress;
+ return this;
+ }
+
+ /**
+ * @return the senderProtocolAddress
+ */
+ public byte[] getSenderProtocolAddress() {
+ return this.senderProtocolAddress;
+ }
+
+ /**
+ * @param senderProtoAddress
+ * the senderProtocolAddress to set
+ * @return this
+ */
+ public ARP setSenderProtocolAddress(final byte[] senderProtoAddress) {
+ this.senderProtocolAddress = senderProtoAddress;
+ return this;
+ }
+
+ public ARP setSenderProtocolAddress(final int address) {
+ this.senderProtocolAddress = ByteBuffer.allocate(4).putInt(address)
+ .array();
+ return this;
+ }
+
+ /**
+ * @return the targetHardwareAddress
+ */
+ public byte[] getTargetHardwareAddress() {
+ return this.targetHardwareAddress;
+ }
+
+ /**
+ * @param targetHWAddress
+ * the targetHardwareAddress to set
+ * @return this
+ */
+ public ARP setTargetHardwareAddress(final byte[] targetHWAddress) {
+ this.targetHardwareAddress = targetHWAddress;
+ return this;
+ }
+
+ /**
+ * @return the targetProtocolAddress
+ */
+ public byte[] getTargetProtocolAddress() {
+ return this.targetProtocolAddress;
+ }
+
+ /**
+ * @return True if gratuitous ARP (SPA = TPA), false otherwise
+ */
+ public boolean isGratuitous() {
+ assert this.senderProtocolAddress.length == this.targetProtocolAddress.length;
+
+ int indx = 0;
+ while (indx < this.senderProtocolAddress.length) {
+ if (this.senderProtocolAddress[indx] != this.targetProtocolAddress[indx]) {
+ return false;
+ }
+ indx++;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param targetProtoAddress
+ * the targetProtocolAddress to set
+ * @return this
+ */
+ public ARP setTargetProtocolAddress(final byte[] targetProtoAddress) {
+ this.targetProtocolAddress = targetProtoAddress;
+ return this;
+ }
+
+ public ARP setTargetProtocolAddress(final int address) {
+ this.targetProtocolAddress = ByteBuffer.allocate(4).putInt(address)
+ .array();
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ final int length = 8 + 2 * (0xff & this.hardwareAddressLength) + 2
+ * (0xff & this.protocolAddressLength);
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.putShort(this.hardwareType);
+ bb.putShort(this.protocolType);
+ bb.put(this.hardwareAddressLength);
+ bb.put(this.protocolAddressLength);
+ bb.putShort(this.opCode);
+ bb.put(this.senderHardwareAddress, 0, 0xff & this.hardwareAddressLength);
+ bb.put(this.senderProtocolAddress, 0, 0xff & this.protocolAddressLength);
+ bb.put(this.targetHardwareAddress, 0, 0xff & this.hardwareAddressLength);
+ bb.put(this.targetProtocolAddress, 0, 0xff & this.protocolAddressLength);
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.hardwareType = bb.getShort();
+ this.protocolType = bb.getShort();
+ this.hardwareAddressLength = bb.get();
+ this.protocolAddressLength = bb.get();
+ this.opCode = bb.getShort();
+ this.senderHardwareAddress = new byte[0xff & this.hardwareAddressLength];
+ bb.get(this.senderHardwareAddress, 0, this.senderHardwareAddress.length);
+ this.senderProtocolAddress = new byte[0xff & this.protocolAddressLength];
+ bb.get(this.senderProtocolAddress, 0, this.senderProtocolAddress.length);
+ this.targetHardwareAddress = new byte[0xff & this.hardwareAddressLength];
+ bb.get(this.targetHardwareAddress, 0, this.targetHardwareAddress.length);
+ this.targetProtocolAddress = new byte[0xff & this.protocolAddressLength];
+ bb.get(this.targetProtocolAddress, 0, this.targetProtocolAddress.length);
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 13121;
+ int result = super.hashCode();
+ result = prime * result + this.hardwareAddressLength;
+ result = prime * result + this.hardwareType;
+ result = prime * result + this.opCode;
+ result = prime * result + this.protocolAddressLength;
+ result = prime * result + this.protocolType;
+ result = prime * result + Arrays.hashCode(this.senderHardwareAddress);
+ result = prime * result + Arrays.hashCode(this.senderProtocolAddress);
+ result = prime * result + Arrays.hashCode(this.targetHardwareAddress);
+ result = prime * result + Arrays.hashCode(this.targetProtocolAddress);
+ 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 ARP)) {
+ return false;
+ }
+ final ARP other = (ARP) obj;
+ if (this.hardwareAddressLength != other.hardwareAddressLength) {
+ return false;
+ }
+ if (this.hardwareType != other.hardwareType) {
+ return false;
+ }
+ if (this.opCode != other.opCode) {
+ return false;
+ }
+ if (this.protocolAddressLength != other.protocolAddressLength) {
+ return false;
+ }
+ if (this.protocolType != other.protocolType) {
+ return false;
+ }
+ if (!Arrays.equals(this.senderHardwareAddress,
+ other.senderHardwareAddress)) {
+ return false;
+ }
+ if (!Arrays.equals(this.senderProtocolAddress,
+ other.senderProtocolAddress)) {
+ return false;
+ }
+ if (!Arrays.equals(this.targetHardwareAddress,
+ other.targetHardwareAddress)) {
+ return false;
+ }
+ if (!Arrays.equals(this.targetProtocolAddress,
+ other.targetProtocolAddress)) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "ARP [hardwareType=" + this.hardwareType + ", protocolType="
+ + this.protocolType + ", hardwareAddressLength="
+ + this.hardwareAddressLength + ", protocolAddressLength="
+ + this.protocolAddressLength + ", opCode=" + this.opCode
+ + ", senderHardwareAddress="
+ + Arrays.toString(this.senderHardwareAddress)
+ + ", senderProtocolAddress="
+ + Arrays.toString(this.senderProtocolAddress)
+ + ", targetHardwareAddress="
+ + Arrays.toString(this.targetHardwareAddress)
+ + ", targetProtocolAddress="
+ + Arrays.toString(this.targetProtocolAddress) + "]";
+ }
+
+ /**
+ * Builds an ARP reply based on a request.
+ *
+ * @param srcIp the IP address to use as the reply source
+ * @param srcMac the MAC address to use as the reply source
+ * @param request the ARP request we got
+ * @return an Ethernet frame containing the ARP reply
+ */
+ public static Ethernet buildArpReply(Ip4Address srcIp, MacAddress srcMac,
+ Ethernet request) {
+
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(request.getSourceMAC());
+ eth.setSourceMACAddress(srcMac);
+ eth.setEtherType(Ethernet.TYPE_ARP);
+ eth.setVlanID(request.getVlanID());
+
+ ARP arp = new ARP();
+ arp.setOpCode(ARP.OP_REPLY);
+ arp.setProtocolType(ARP.PROTO_TYPE_IP);
+ arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
+
+ arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
+ arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
+ arp.setSenderHardwareAddress(srcMac.toBytes());
+ arp.setTargetHardwareAddress(request.getSourceMACAddress());
+
+ arp.setTargetProtocolAddress(((ARP) request.getPayload())
+ .getSenderProtocolAddress());
+ arp.setSenderProtocolAddress(srcIp.toInt());
+
+ eth.setPayload(arp);
+ return eth;
+ }
+
+ /**
+ * Deserializer function for ARP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<ARP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, INITIAL_HEADER_LENGTH);
+
+ ARP arp = new ARP();
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ arp.setHardwareType(bb.getShort());
+ arp.setProtocolType(bb.getShort());
+
+ byte hwAddressLength = bb.get();
+ arp.setHardwareAddressLength(hwAddressLength);
+
+ byte protocolAddressLength = bb.get();
+ arp.setProtocolAddressLength(protocolAddressLength);
+ arp.setOpCode(bb.getShort());
+
+ // Check we have enough space for the addresses
+ checkHeaderLength(length, INITIAL_HEADER_LENGTH +
+ 2 * hwAddressLength +
+ 2 * protocolAddressLength);
+
+ arp.senderHardwareAddress = new byte[0xff & hwAddressLength];
+ bb.get(arp.senderHardwareAddress, 0, arp.senderHardwareAddress.length);
+ arp.senderProtocolAddress = new byte[0xff & protocolAddressLength];
+ bb.get(arp.senderProtocolAddress, 0, arp.senderProtocolAddress.length);
+ arp.targetHardwareAddress = new byte[0xff & hwAddressLength];
+ bb.get(arp.targetHardwareAddress, 0, arp.targetHardwareAddress.length);
+ arp.targetProtocolAddress = new byte[0xff & protocolAddressLength];
+ bb.get(arp.targetProtocolAddress, 0, arp.targetProtocolAddress.length);
+
+ return arp;
+ };
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/BasePacket.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/BasePacket.java
new file mode 100644
index 00000000..4aece66f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/BasePacket.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+/**
+ *
+ *
+ */
+public abstract class BasePacket implements IPacket {
+ protected IPacket parent;
+ protected IPacket payload;
+
+ /**
+ * @return the parent
+ */
+ @Override
+ public IPacket getParent() {
+ return this.parent;
+ }
+
+ /**
+ * @param parent
+ * the parent to set
+ */
+ @Override
+ public IPacket setParent(final IPacket parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ /**
+ * @return the payload
+ */
+ @Override
+ public IPacket getPayload() {
+ return this.payload;
+ }
+
+ /**
+ * @param payload
+ * the payload to set
+ */
+ @Override
+ public IPacket setPayload(final IPacket payload) {
+ this.payload = payload;
+ return this;
+ }
+
+ @Override
+ public void resetChecksum() {
+ if (this.parent != null) {
+ this.parent.resetChecksum();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 6733;
+ int result = 1;
+ result = prime * result
+ + (this.payload == null ? 0 : this.payload.hashCode());
+ 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 (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof BasePacket)) {
+ return false;
+ }
+ final BasePacket other = (BasePacket) obj;
+ if (this.payload == null) {
+ if (other.payload != null) {
+ return false;
+ }
+ } else if (!this.payload.equals(other.payload)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public Object clone() {
+ IPacket pkt;
+ try {
+ pkt = this.getClass().newInstance();
+ } catch (final Exception e) {
+ throw new RuntimeException("Could not clone packet");
+ }
+
+ final byte[] data = this.serialize();
+ pkt.deserialize(this.serialize(), 0, data.length);
+ pkt.setParent(this.parent);
+ return pkt;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ChassisId.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
new file mode 100644
index 00000000..23c9859f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+package org.onlab.packet;
+
+/**
+ * The class representing a network device chassisId.
+ * This class is immutable.
+ */
+public final class ChassisId {
+
+ private static final long UNKNOWN = 0;
+ private final long value;
+
+ /**
+ * Default constructor.
+ */
+ public ChassisId() {
+ this.value = ChassisId.UNKNOWN;
+ }
+
+ /**
+ * Constructor from a long value.
+ *
+ * @param value the value to use.
+ */
+ public ChassisId(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public ChassisId(String value) {
+ this.value = Long.parseLong(value, 16);
+ }
+
+ /**
+ * Get the value of the chassis id.
+ *
+ * @return the value of the chassis id.
+ */
+ public long value() {
+ return value;
+ }
+
+ /**
+ * Convert the Chassis Id value to a ':' separated hexadecimal string.
+ *
+ * @return the Chassis Id value as a ':' separated hexadecimal string.
+ */
+ @Override
+ public String toString() {
+ return Long.toHexString(this.value);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof ChassisId)) {
+ return false;
+ }
+
+ ChassisId otherChassisId = (ChassisId) other;
+
+ return value == otherChassisId.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(value);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java
new file mode 100644
index 00000000..de5b43fa
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java
@@ -0,0 +1,632 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ *
+ */
+public class DHCP extends BasePacket {
+ /**
+ * Dynamic Host Configuration Protocol packet.
+ * ------------------------------------------ |op (1) | htype(1) | hlen(1) |
+ * hops(1) | ------------------------------------------ | xid (4) |
+ * ------------------------------------------ | secs (2) | flags (2) |
+ * ------------------------------------------ | ciaddr (4) |
+ * ------------------------------------------ | yiaddr (4) |
+ * ------------------------------------------ | siaddr (4) |
+ * ------------------------------------------ | giaddr (4) |
+ * ------------------------------------------ | chaddr (16) |
+ * ------------------------------------------ | sname (64) |
+ * ------------------------------------------ | file (128) |
+ * ------------------------------------------ | options (312) |
+ * ------------------------------------------
+ *
+ */
+ // Header + magic without options
+ public static final int MIN_HEADER_LENGTH = 240;
+ public static final byte OPCODE_REQUEST = 0x1;
+ public static final byte OPCODE_REPLY = 0x2;
+
+ public static final byte HWTYPE_ETHERNET = 0x1;
+
+ public enum DHCPOptionCode {
+ OptionCode_SubnetMask((byte) 1), OptionCode_RouterAddress((byte) 3), OptionCode_DomainServer((byte) 6),
+ OptionCode_HostName((byte) 12), OptionCode_DomainName((byte) 15), OptionCode_BroadcastAddress((byte) 28),
+ OptionCode_RequestedIP((byte) 50), OptionCode_LeaseTime((byte) 51), OptionCode_MessageType((byte) 53),
+ OptionCode_DHCPServerIp((byte) 54), OptionCode_RequestedParameters((byte) 55),
+ OptionCode_RenewalTime((byte) 58), OPtionCode_RebindingTime((byte) 59), OptionCode_ClientID((byte) 61),
+ OptionCode_END((byte) 255);
+
+ protected byte value;
+
+ private DHCPOptionCode(final byte value) {
+ this.value = value;
+ }
+
+ public byte getValue() {
+ return this.value;
+ }
+ }
+
+ protected byte opCode;
+ protected byte hardwareType;
+ protected byte hardwareAddressLength;
+ protected byte hops;
+ protected int transactionId;
+ protected short seconds;
+ protected short flags;
+ protected int clientIPAddress;
+ protected int yourIPAddress;
+ protected int serverIPAddress;
+ protected int gatewayIPAddress;
+ protected byte[] clientHardwareAddress;
+ protected String serverName;
+ protected String bootFileName;
+ protected List<DHCPOption> options = new ArrayList<DHCPOption>();
+
+ /**
+ * @return the opCode
+ */
+ public byte getOpCode() {
+ return this.opCode;
+ }
+
+ /**
+ * @param opCode
+ * the opCode to set
+ * @return this
+ */
+ public DHCP setOpCode(final byte opCode) {
+ this.opCode = opCode;
+ return this;
+ }
+
+ /**
+ * @return the hardwareType
+ */
+ public byte getHardwareType() {
+ return this.hardwareType;
+ }
+
+ /**
+ * @param hardwareType
+ * the hardwareType to set
+ * @return this
+ */
+ public DHCP setHardwareType(final byte hardwareType) {
+ this.hardwareType = hardwareType;
+ return this;
+ }
+
+ /**
+ * @return the hardwareAddressLength
+ */
+ public byte getHardwareAddressLength() {
+ return this.hardwareAddressLength;
+ }
+
+ /**
+ * @param hardwareAddressLength
+ * the hardwareAddressLength to set
+ * @return this
+ */
+ public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
+ this.hardwareAddressLength = hardwareAddressLength;
+ return this;
+ }
+
+ /**
+ * @return the hops
+ */
+ public byte getHops() {
+ return this.hops;
+ }
+
+ /**
+ * @param hops
+ * the hops to set
+ * @return this
+ */
+ public DHCP setHops(final byte hops) {
+ this.hops = hops;
+ return this;
+ }
+
+ /**
+ * @return the transactionId
+ */
+ public int getTransactionId() {
+ return this.transactionId;
+ }
+
+ /**
+ * @param transactionId
+ * the transactionId to set
+ * @return this
+ */
+ public DHCP setTransactionId(final int transactionId) {
+ this.transactionId = transactionId;
+ return this;
+ }
+
+ /**
+ * @return the seconds
+ */
+ public short getSeconds() {
+ return this.seconds;
+ }
+
+ /**
+ * @param seconds
+ * the seconds to set
+ * @return this
+ */
+ public DHCP setSeconds(final short seconds) {
+ this.seconds = seconds;
+ return this;
+ }
+
+ /**
+ * @return the flags
+ */
+ public short getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * @param flags
+ * the flags to set
+ * @return this
+ */
+ public DHCP setFlags(final short flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * @return the clientIPAddress
+ */
+ public int getClientIPAddress() {
+ return this.clientIPAddress;
+ }
+
+ /**
+ * @param clientIPAddress
+ * the clientIPAddress to set
+ * @return this
+ */
+ public DHCP setClientIPAddress(final int clientIPAddress) {
+ this.clientIPAddress = clientIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the yourIPAddress
+ */
+ public int getYourIPAddress() {
+ return this.yourIPAddress;
+ }
+
+ /**
+ * @param yourIPAddress
+ * the yourIPAddress to set
+ * @return this
+ */
+ public DHCP setYourIPAddress(final int yourIPAddress) {
+ this.yourIPAddress = yourIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the serverIPAddress
+ */
+ public int getServerIPAddress() {
+ return this.serverIPAddress;
+ }
+
+ /**
+ * @param serverIPAddress
+ * the serverIPAddress to set
+ * @return this
+ */
+ public DHCP setServerIPAddress(final int serverIPAddress) {
+ this.serverIPAddress = serverIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the gatewayIPAddress
+ */
+ public int getGatewayIPAddress() {
+ return this.gatewayIPAddress;
+ }
+
+ /**
+ * @param gatewayIPAddress
+ * the gatewayIPAddress to set
+ * @return this
+ */
+ public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
+ this.gatewayIPAddress = gatewayIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the clientHardwareAddress
+ */
+ public byte[] getClientHardwareAddress() {
+ return this.clientHardwareAddress;
+ }
+
+ /**
+ * @param clientHardwareAddress
+ * the clientHardwareAddress to set
+ * @return this
+ */
+ public DHCP setClientHardwareAddress(final byte[] clientHardwareAddress) {
+ this.clientHardwareAddress = clientHardwareAddress;
+ return this;
+ }
+
+ /**
+ * Gets a specific DHCP option parameter.
+ *
+ * @param optionCode
+ * The option code to get
+ * @return The value of the option if it exists, null otherwise
+ */
+ public DHCPOption getOption(final DHCPOptionCode optionCode) {
+ for (final DHCPOption opt : this.options) {
+ if (opt.code == optionCode.value) {
+ return opt;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the options
+ */
+ public List<DHCPOption> getOptions() {
+ return this.options;
+ }
+
+ /**
+ * @param options
+ * the options to set
+ * @return this
+ */
+ public DHCP setOptions(final List<DHCPOption> options) {
+ this.options = options;
+ return this;
+ }
+
+ /**
+ * @return the packetType base on option 53
+ */
+ public DHCPPacketType getPacketType() {
+ final ListIterator<DHCPOption> lit = this.options.listIterator();
+ while (lit.hasNext()) {
+ final DHCPOption option = lit.next();
+ // only care option 53
+ if (option.getCode() == 53) {
+ return DHCPPacketType.getType(option.getData()[0]);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the serverName
+ */
+ public String getServerName() {
+ return this.serverName;
+ }
+
+ /**
+ * @param server
+ * the serverName to set
+ * @return this
+ */
+ public DHCP setServerName(final String server) {
+ this.serverName = server;
+ return this;
+ }
+
+ /**
+ * @return the bootFileName
+ */
+ public String getBootFileName() {
+ return this.bootFileName;
+ }
+
+ /**
+ * @param bootFile
+ * the bootFileName to set
+ * @return this
+ */
+ public DHCP setBootFileName(final String bootFile) {
+ this.bootFileName = bootFile;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ // not guaranteed to retain length/exact format
+ this.resetChecksum();
+
+ // minimum size 240 including magic cookie, options generally padded to
+ // 300
+ int optionsLength = 0;
+ for (final DHCPOption option : this.options) {
+ if (option.getCode() == 0 || option.getCode() == ((byte) 255)) {
+ optionsLength += 1;
+ } else {
+ optionsLength += 2 + (0xff & option.getLength());
+ }
+ }
+ int optionsPadLength = 0;
+ if (optionsLength < 60) {
+ optionsPadLength = 60 - optionsLength;
+ }
+
+ final byte[] data = new byte[240 + optionsLength + optionsPadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.opCode);
+ bb.put(this.hardwareType);
+ bb.put(this.hardwareAddressLength);
+ bb.put(this.hops);
+ bb.putInt(this.transactionId);
+ bb.putShort(this.seconds);
+ bb.putShort(this.flags);
+ bb.putInt(this.clientIPAddress);
+ bb.putInt(this.yourIPAddress);
+ bb.putInt(this.serverIPAddress);
+ bb.putInt(this.gatewayIPAddress);
+ checkArgument(this.clientHardwareAddress.length <= 16,
+ "Hardware address is too long (%s bytes)", this.clientHardwareAddress.length);
+ bb.put(this.clientHardwareAddress);
+ if (this.clientHardwareAddress.length < 16) {
+ for (int i = 0; i < 16 - this.clientHardwareAddress.length; ++i) {
+ bb.put((byte) 0x0);
+ }
+ }
+ this.writeString(this.serverName, bb, 64);
+ this.writeString(this.bootFileName, bb, 128);
+ // magic cookie
+ bb.put((byte) 0x63);
+ bb.put((byte) 0x82);
+ bb.put((byte) 0x53);
+ bb.put((byte) 0x63);
+ for (final DHCPOption option : this.options) {
+ final int code = option.getCode() & 0xff;
+ bb.put((byte) code);
+ if (code != 0 && code != 255) {
+ bb.put(option.getLength());
+ bb.put(option.getData());
+ }
+ }
+ // assume the rest is padded out with zeroes
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ if (bb.remaining() < DHCP.MIN_HEADER_LENGTH) {
+ return this;
+ }
+
+ this.opCode = bb.get();
+ this.hardwareType = bb.get();
+ this.hardwareAddressLength = bb.get();
+ this.hops = bb.get();
+ this.transactionId = bb.getInt();
+ this.seconds = bb.getShort();
+ this.flags = bb.getShort();
+ this.clientIPAddress = bb.getInt();
+ this.yourIPAddress = bb.getInt();
+ this.serverIPAddress = bb.getInt();
+ this.gatewayIPAddress = bb.getInt();
+ final int hardwareAddressLength = 0xff & this.hardwareAddressLength;
+ this.clientHardwareAddress = new byte[hardwareAddressLength];
+
+ bb.get(this.clientHardwareAddress);
+ for (int i = hardwareAddressLength; i < 16; ++i) {
+ bb.get();
+ }
+ this.serverName = this.readString(bb, 64);
+ this.bootFileName = this.readString(bb, 128);
+ // read the magic cookie
+ // magic cookie
+ bb.get();
+ bb.get();
+ bb.get();
+ bb.get();
+ // read options
+ while (bb.hasRemaining()) {
+ final DHCPOption option = new DHCPOption();
+ int code = 0xff & bb.get(); // convert signed byte to int in range
+ // [0,255]
+ option.setCode((byte) code);
+ if (code == 0) {
+ // skip these
+ continue;
+ } else if (code != 255) {
+ if (bb.hasRemaining()) {
+ final int l = 0xff & bb.get(); // convert signed byte to
+ // int in range [0,255]
+ option.setLength((byte) l);
+ if (bb.remaining() >= l) {
+ final byte[] optionData = new byte[l];
+ bb.get(optionData);
+ option.setData(optionData);
+ } else {
+ // Skip the invalid option and set the END option
+ code = 0xff;
+ option.setCode((byte) code);
+ option.setLength((byte) 0);
+ }
+ } else {
+ // Skip the invalid option and set the END option
+ code = 0xff;
+ option.setCode((byte) code);
+ option.setLength((byte) 0);
+ }
+ }
+ this.options.add(option);
+ if (code == 255) {
+ // remaining bytes are supposed to be 0, but ignore them just in
+ // case
+ break;
+ }
+ }
+
+ return this;
+ }
+
+ protected void writeString(final String string, final ByteBuffer bb,
+ final int maxLength) {
+ if (string == null) {
+ for (int i = 0; i < maxLength; ++i) {
+ bb.put((byte) 0x0);
+ }
+ } else {
+ byte[] bytes = null;
+ try {
+ bytes = string.getBytes("ascii");
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("Failure encoding server name", e);
+ }
+ int writeLength = bytes.length;
+ if (writeLength > maxLength) {
+ writeLength = maxLength;
+ }
+ bb.put(bytes, 0, writeLength);
+ for (int i = writeLength; i < maxLength; ++i) {
+ bb.put((byte) 0x0);
+ }
+ }
+ }
+
+ private static String readString(final ByteBuffer bb, final int maxLength) {
+ final byte[] bytes = new byte[maxLength];
+ bb.get(bytes);
+ String result = null;
+ try {
+ result = new String(bytes, "ascii").trim();
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("Failure decoding string", e);
+ }
+ return result;
+ }
+
+ /**
+ * Deserializer function for DHCP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<DHCP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, MIN_HEADER_LENGTH);
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ DHCP dhcp = new DHCP();
+
+ dhcp.opCode = bb.get();
+ dhcp.hardwareType = bb.get();
+ dhcp.hardwareAddressLength = bb.get();
+ dhcp.hops = bb.get();
+ dhcp.transactionId = bb.getInt();
+ dhcp.seconds = bb.getShort();
+ dhcp.flags = bb.getShort();
+ dhcp.clientIPAddress = bb.getInt();
+ dhcp.yourIPAddress = bb.getInt();
+ dhcp.serverIPAddress = bb.getInt();
+ dhcp.gatewayIPAddress = bb.getInt();
+ final int hardwareAddressLength = 0xff & dhcp.hardwareAddressLength;
+ dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
+
+ bb.get(dhcp.clientHardwareAddress);
+ for (int i = hardwareAddressLength; i < 16; ++i) {
+ bb.get();
+ }
+ dhcp.serverName = readString(bb, 64);
+ dhcp.bootFileName = readString(bb, 128);
+ // read the magic cookie
+ // magic cookie
+ bb.get();
+ bb.get();
+ bb.get();
+ bb.get();
+
+ // read options
+ boolean foundEndOptionsMarker = false;
+ while (bb.hasRemaining()) {
+ final DHCPOption option = new DHCPOption();
+ int code = 0xff & bb.get(); // convert signed byte to int in range
+ // [0,255]
+ option.setCode((byte) code);
+ if (code == 0) {
+ // skip these
+ continue;
+ } else if (code != 255) {
+ if (bb.hasRemaining()) {
+ final int l = 0xff & bb.get(); // convert signed byte to
+ // int in range [0,255]
+ option.setLength((byte) l);
+ if (bb.remaining() >= l) {
+ final byte[] optionData = new byte[l];
+ bb.get(optionData);
+ option.setData(optionData);
+ dhcp.options.add(option);
+ } else {
+ throw new DeserializationException(
+ "Buffer underflow while reading DHCP option");
+ }
+ }
+ } else if (code == 255) {
+ DHCPOption end = new DHCPOption();
+ end.setCode((byte) 255);
+ dhcp.options.add(end);
+ // remaining bytes are supposed to be 0, but ignore them just in
+ // case
+ foundEndOptionsMarker = true;
+ break;
+ }
+ }
+
+ if (!foundEndOptionsMarker) {
+ throw new DeserializationException("DHCP End options marker was missing");
+ }
+
+ return dhcp;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
new file mode 100644
index 00000000..1b6c670e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.util.Arrays;
+
+/**
+ *
+ */
+public class DHCPOption {
+ protected byte code;
+ protected byte length;
+ protected byte[] data;
+
+ /**
+ * @return the code
+ */
+ public byte getCode() {
+ return this.code;
+ }
+
+ /**
+ * @param code
+ * the code to set
+ * @return this
+ */
+ public DHCPOption setCode(final byte code) {
+ this.code = code;
+ return this;
+ }
+
+ /**
+ * @return the length
+ */
+ public byte getLength() {
+ return this.length;
+ }
+
+ /**
+ * @param length
+ * the length to set
+ * @return this
+ */
+ public DHCPOption setLength(final byte length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * @return the data
+ */
+ public byte[] getData() {
+ return this.data;
+ }
+
+ /**
+ * @param data
+ * the data to set
+ * @return this
+ */
+ public DHCPOption setData(final byte[] data) {
+ this.data = data;
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + this.code;
+ result = prime * result + Arrays.hashCode(this.data);
+ result = prime * result + this.length;
+ 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 (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DHCPOption)) {
+ return false;
+ }
+ final DHCPOption other = (DHCPOption) obj;
+ if (this.code != other.code) {
+ return false;
+ }
+ if (!Arrays.equals(this.data, other.data)) {
+ return false;
+ }
+ if (this.length != other.length) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "DHCPOption [code=" + this.code + ", length=" + this.length
+ + ", data=" + Arrays.toString(this.data) + "]";
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
new file mode 100644
index 00000000..8254c770
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+public enum DHCPPacketType {
+ // From RFC 1533
+ DHCPDISCOVER(1), DHCPOFFER(2), DHCPREQUEST(3), DHCPDECLINE(4), DHCPACK(5), DHCPNAK(
+ 6), DHCPRELEASE(7),
+
+ // From RFC2132
+ DHCPINFORM(8),
+
+ // From RFC3203
+ DHCPFORCERENEW(9),
+
+ // From RFC4388
+ DHCPLEASEQUERY(10), DHCPLEASEUNASSIGNED(11), DHCPLEASEUNKNOWN(12), DHCPLEASEACTIVE(
+ 13);
+
+ protected int value;
+
+ private DHCPPacketType(final int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ switch (this.value) {
+ case 1:
+ return "DHCPDISCOVER";
+ case 2:
+ return "DHCPOFFER";
+ case 3:
+ return "DHCPREQUEST";
+ case 4:
+ return "DHCPDECLINE";
+ case 5:
+ return "DHCPACK";
+ case 6:
+ return "DHCPNAK";
+ case 7:
+ return "DHCPRELEASE";
+ case 8:
+ return "DHCPINFORM";
+ case 9:
+ return "DHCPFORCERENEW";
+ case 10:
+ return "DHCPLEASEQUERY";
+ case 11:
+ return "DHCPLEASEUNASSIGNED";
+ case 12:
+ return "DHCPLEASEUNKNOWN";
+ case 13:
+ return "DHCPLEASEACTIVE";
+ default:
+ break;
+ }
+
+ return null;
+ }
+
+ public static DHCPPacketType getType(final int value) {
+ switch (value) {
+ case 1:
+ return DHCPDISCOVER;
+ case 2:
+ return DHCPOFFER;
+ case 3:
+ return DHCPREQUEST;
+ case 4:
+ return DHCPDECLINE;
+ case 5:
+ return DHCPACK;
+ case 6:
+ return DHCPNAK;
+ case 7:
+ return DHCPRELEASE;
+ case 8:
+ return DHCPINFORM;
+ case 9:
+ return DHCPFORCERENEW;
+ case 10:
+ return DHCPLEASEQUERY;
+ case 11:
+ return DHCPLEASEUNASSIGNED;
+ case 12:
+ return DHCPLEASEUNKNOWN;
+ case 13:
+ return DHCPLEASEACTIVE;
+ default:
+ break;
+ }
+
+ return null;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Data.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Data.java
new file mode 100644
index 00000000..79abcba1
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Data.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ *
+ */
+public class Data extends BasePacket {
+ protected byte[] data;
+
+ /**
+ *
+ */
+ public Data() {
+ data = new byte[0];
+ }
+
+ /**
+ * @param data the data
+ */
+ public Data(final byte[] data) {
+ this.data = data;
+ }
+
+ /**
+ * @return the data
+ */
+ public byte[] getData() {
+ return this.data;
+ }
+
+ /**
+ * @param data
+ * the data to set
+ * @return self
+ */
+ public Data setData(final byte[] data) {
+ this.data = data;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ return this.data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ this.data = Arrays.copyOfRange(data, offset, data.length);
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 1571;
+ int result = super.hashCode();
+ result = prime * result + Arrays.hashCode(this.data);
+ 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 Data)) {
+ return false;
+ }
+ final Data other = (Data) obj;
+ if (!Arrays.equals(this.data, other.data)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for generic payload data.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Data> deserializer() {
+ return (data, offset, length) -> {
+ // Allow zero-length data for now
+ if (length == 0) {
+ return new Data();
+ }
+
+ checkInput(data, offset, length, 1);
+
+ Data dataObject = new Data();
+
+ dataObject.data = Arrays.copyOfRange(data, offset, data.length);
+
+ return dataObject;
+ };
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java
new file mode 100644
index 00000000..03a8aa35
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DeserializationException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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;
+
+/**
+ * Signals that an error occurred during deserialization of a packet.
+ */
+public class DeserializationException extends Exception {
+
+ /**
+ * Creates a new deserialization exception with the given message.
+ *
+ * @param message exception message
+ */
+ public DeserializationException(String message) {
+ super(message);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Deserializer.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Deserializer.java
new file mode 100644
index 00000000..e0ef381b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Deserializer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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;
+
+/**
+ * Function to deserialize a packet from a byte-based input stream.
+ */
+@FunctionalInterface
+public interface Deserializer<U extends IPacket> {
+
+ /**
+ * Deserialize a packet object from a byte array.
+ *
+ * @param data input array to take packet bytes from
+ * @param offset index where this packet header begins in the byte array
+ * @param length length of the packet header
+ * @return a deserialized packet object
+ * @throws DeserializationException if the packet cannot be deserialized
+ * from the input
+ */
+ U deserialize(byte[] data, int offset, int length) throws DeserializationException;
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAP.java
new file mode 100644
index 00000000..516938a4
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAP.java
@@ -0,0 +1,264 @@
+/*
+ *
+ * * Copyright 2015 AT&T Foundry
+ * *
+ * * 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 java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * EAP (Extensible Authentication Protocol) packet.
+ */
+public class EAP extends BasePacket {
+ private static final int HEADER_LENGTH = 4;
+
+ public static final short MIN_LEN = 0x4;
+ public static final short EAP_HDR_LEN_REQ_RESP = 5;
+ public static final short EAP_HDR_LEN_SUC_FAIL = 4;
+
+ // EAP Code
+ public static final byte REQUEST = 0x1;
+ public static final byte RESPONSE = 0x2;
+ public static final byte SUCCESS = 0x3;
+ public static final byte FAILURE = 0x4;
+
+ // EAP Attribute Type
+ public static final byte ATTR_IDENTITY = 0x1;
+ public static final byte ATTR_NOTIFICATION = 0x2;
+ public static final byte ATTR_NAK = 0x3;
+ public static final byte ATTR_MD5 = 0x4;
+ public static final byte ATTR_OTP = 0x5;
+ public static final byte ATTR_GTC = 0x6;
+ public static final byte ATTR_TLS = 0xd;
+
+ protected byte code;
+ protected byte identifier;
+ protected short length;
+ protected byte type;
+ protected byte[] data;
+
+
+ /**
+ * Gets the EAP code.
+ *
+ * @return EAP code
+ */
+ public byte getCode() {
+ return this.code;
+ }
+
+
+ /**
+ * Sets the EAP code.
+ *
+ * @param code EAP code
+ * @return this
+ */
+ public EAP setCode(final byte code) {
+ this.code = code;
+ return this;
+ }
+
+ /**
+ * Gets the EAP identifier.
+ *
+ * @return EAP identifier
+ */
+ public byte getIdentifier() {
+ return this.identifier;
+ }
+
+ /**
+ * Sets the EAP identifier.
+ *
+ * @param identifier EAP identifier
+ * @return this
+ */
+ public EAP setIdentifier(final byte identifier) {
+ this.identifier = identifier;
+ return this;
+ }
+
+ /**
+ * Gets the get packet length.
+ *
+ * @return packet length
+ */
+ public short getLength() {
+ return this.length;
+ }
+
+ /**
+ * Sets the packet length.
+ *
+ * @param length packet length
+ * @return this
+ */
+ public EAP setLength(final short length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Gets the data type.
+ *
+ * @return data type
+ */
+ public byte getDataType() {
+ return this.type;
+ }
+
+ /**
+ * Sets the data type.
+ *
+ * @param type data type
+ * @return this
+ */
+ public EAP setDataType(final byte type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Gets the EAP data.
+ *
+ * @return EAP data
+ */
+ public byte[] getData() {
+ return this.data;
+ }
+
+ /**
+ * Sets the EAP data.
+ *
+ * @param data EAP data to be set
+ * @return this
+ */
+ public EAP setData(final byte[] data) {
+ this.data = data;
+ return this;
+ }
+
+ /**
+ * Default EAP constructor that sets the EAP code to 0.
+ */
+ public EAP() {
+ this.code = 0;
+ }
+
+ /**
+ * EAP constructor that initially sets all fields.
+ *
+ * @param code EAP code
+ * @param identifier EAP identifier
+ * @param type packet type
+ * @param data EAP data
+ */
+ public EAP(byte code, byte identifier, byte type, byte[] data) {
+ this.code = code;
+ this.identifier = identifier;
+ if (this.code == REQUEST || this.code == RESPONSE) {
+ this.length = (short) (5 + (data == null ? 0 : data.length));
+ this.type = type;
+ } else {
+ this.length = (short) (4 + (data == null ? 0 : data.length));
+ }
+ this.data = data;
+ }
+
+ /**
+ * Deserializer for EAP packets.
+ *
+ * @return deserializer
+ */
+ public static Deserializer<EAP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ EAP eap = new EAP();
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ eap.code = bb.get();
+ eap.identifier = bb.get();
+ eap.length = bb.getShort();
+
+ checkHeaderLength(length, eap.length);
+
+ int dataLength;
+ if (eap.code == REQUEST || eap.code == RESPONSE) {
+ eap.type = bb.get();
+ dataLength = eap.length - 5;
+ } else {
+ dataLength = eap.length - 4;
+ }
+
+ eap.data = new byte[dataLength];
+ bb.get(eap.data);
+ return eap;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ final byte[] data = new byte[this.length];
+
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.code);
+ bb.put(this.identifier);
+ bb.putShort(this.length);
+ if (this.code == REQUEST || this.code == RESPONSE) {
+ bb.put(this.type);
+ }
+ if (this.data != null) {
+ bb.put(this.data);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.code = bb.get();
+ this.identifier = bb.get();
+ this.length = bb.getShort();
+
+ int dataLength;
+ if (this.code == REQUEST || this.code == RESPONSE) {
+ this.type = bb.get();
+ dataLength = this.length - 5;
+ } else {
+ dataLength = this.length - 4;
+ }
+ this.data = new byte[dataLength];
+ bb.get(this.data);
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 3889;
+ int result = super.hashCode();
+ result = prime * result + this.code;
+ result = prime * result + this.identifier;
+ result = prime * result + this.length;
+ result = prime * result + this.type;
+ return result;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAPOL.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAPOL.java
new file mode 100644
index 00000000..1820cc31
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EAPOL.java
@@ -0,0 +1,199 @@
+/*
+ *
+ * * Copyright 2015 AT&T Foundry
+ * *
+ * * 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 java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * EAPOL (Extensible Authentication Protocol over LAN) header.
+ */
+public class EAPOL extends BasePacket {
+
+ private byte version = 0x01;
+ private byte eapolType;
+ private short packetLength;
+
+ private static final int HEADER_LENGTH = 4;
+
+ // EAPOL Packet Type
+ public static final byte EAPOL_PACKET = 0x0;
+ public static final byte EAPOL_START = 0x1;
+ public static final byte EAPOL_LOGOFF = 0x2;
+ public static final byte EAPOL_KEY = 0x3;
+ public static final byte EAPOL_ASF = 0x4;
+
+ public static final MacAddress PAE_GROUP_ADDR = MacAddress.valueOf(new byte[] {
+ (byte) 0x01, (byte) 0x80, (byte) 0xc2, (byte) 0x00, (byte) 0x00, (byte) 0x03
+ });
+
+ /**
+ * Gets the version.
+ *
+ * @return version
+ */
+ public byte getVersion() {
+ return this.version;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version EAPOL version
+ * @return this
+ */
+ public EAPOL setVersion(final byte version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Gets the type.
+ *
+ * @return EAPOL type
+ */
+ public byte getEapolType() {
+ return this.eapolType;
+ }
+
+ /**
+ * Sets the EAPOL type.
+ *
+ * @param eapolType EAPOL type
+ * @return this
+ */
+ public EAPOL setEapolType(final byte eapolType) {
+ this.eapolType = eapolType;
+ return this;
+ }
+
+ /**
+ * Gets the packet length.
+ *
+ * @return packet length
+ */
+ public short getPacketLength() {
+ return this.packetLength;
+ }
+
+ /**
+ * Sets the packet length.
+ *
+ * @param packetLen packet length
+ * @return this
+ */
+ public EAPOL setPacketLength(final short packetLen) {
+ this.packetLength = packetLen;
+ return this;
+ }
+
+ /**
+ * Serializes the packet, based on the code/type using the payload
+ * to compute its length.
+ *
+ * @return this
+ */
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ // prepare the buffer to hold the version (1), packet type (1),
+ // packet length (2) and the eap payload.
+ // if there is no payload, packet length is 0
+ byte[] data = new byte[4 + this.packetLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.version);
+ bb.put(this.eapolType);
+ bb.putShort(this.packetLength);
+
+ // put the EAP payload
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 3889;
+ int result = super.hashCode();
+ result = prime * result + this.version;
+ result = prime * result + this.eapolType;
+ result = prime * result + this.packetLength;
+ return result;
+ }
+
+ /**
+ * Deserializer for EAPOL packets.
+ *
+ * @return deserializer
+ */
+ public static Deserializer<EAPOL> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ EAPOL eapol = new EAPOL();
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ eapol.setVersion(bb.get());
+ eapol.setEapolType(bb.get());
+ eapol.setPacketLength(bb.getShort());
+
+ if (eapol.packetLength > 0) {
+ checkHeaderLength(length, HEADER_LENGTH + eapol.packetLength);
+ // deserialize the EAP Payload
+ eapol.payload = EAP.deserializer().deserialize(data,
+ bb.position(), bb.limit() - bb.position());
+
+ eapol.payload.setParent(eapol);
+ }
+
+ return eapol;
+ };
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ // deserialize the EAPOL header
+ this.version = bb.get();
+ this.eapolType = bb.get();
+ this.packetLength = bb.getShort();
+
+ if (this.packetLength > 0) {
+ // deserialize the EAP Payload
+ this.payload = new EAP();
+
+ this.payload = this.payload.deserialize(data, bb.position(), length - 4);
+ this.payload.setParent(this);
+ }
+
+ return this;
+ }
+}
+
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java
new file mode 100644
index 00000000..561c9307
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 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;
+
+/**
+ * Representation of an Ethertype.
+ */
+public class EthType {
+
+ /**
+ * A list of known ethertypes. Adding a fully defined enum here will
+ * associated the ethertype with a textual representation and a parsing
+ * class.
+ */
+ public enum EtherType {
+
+ ARP(0x806, "arp", org.onlab.packet.ARP.deserializer()),
+ RARP(0x8035, "rarp", org.onlab.packet.ARP.deserializer()),
+ IPV4(0x800, "ipv4", org.onlab.packet.IPv4.deserializer()),
+ IPV6(0x86dd, "ipv6", org.onlab.packet.IPv6.deserializer()),
+ LLDP(0x88cc, "lldp", org.onlab.packet.LLDP.deserializer()),
+ VLAN(0x8100, "vlan", null),
+ BDDP(0x8942, "bddp", org.onlab.packet.LLDP.deserializer()),
+ MPLS_UNICAST(0x8847, "mpls_unicast", org.onlab.packet.MPLS.deserializer()),
+ MPLS_MULTICAST(0x8848, "mpls_unicast", org.onlab.packet.MPLS.deserializer()),
+ EAPOL(0x888e, "eapol", org.onlab.packet.EAPOL.deserializer()),
+ UNKNOWN(0, "unknown", null);
+
+
+ private final EthType etherType;
+ private final String type;
+ private final Deserializer<?> deserializer;
+
+ /**
+ * Constucts a new ethertype.
+ *
+ * @param ethType The actual ethertype
+ * @param type it's textual representation
+ * @param deserializer a parser for this ethertype
+ */
+ EtherType(int ethType, String type, Deserializer<?> deserializer) {
+ this.etherType = new EthType(ethType);
+ this.type = type;
+ this.deserializer = deserializer;
+ }
+
+ public EthType ethType() {
+ return etherType;
+ }
+
+ @Override
+ public String toString() {
+ return type;
+ }
+
+ public Deserializer<?> deserializer() {
+ return deserializer;
+ }
+
+ public static EtherType lookup(short etherType) {
+ for (EtherType ethType : EtherType.values()) {
+ if (ethType.ethType().toShort() == etherType) {
+ return ethType;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ }
+
+
+ private final short etherType;
+
+ /**
+ * Builds the EthType.
+ *
+ * @param etherType an integer representing an ethtype
+ */
+ public EthType(int etherType) {
+ this.etherType = (short) (etherType & 0xFFFF);
+ }
+
+ /**
+ * Builds the EthType.
+ *
+ * @param etherType a short representing the ethtype
+ */
+ public EthType(short etherType) {
+ this.etherType = etherType;
+ }
+
+ /**
+ * Returns the short value for this ethtype.
+ *
+ * @return a short value
+ */
+ public short toShort() {
+ return etherType;
+ }
+
+ /**
+ * Looks up the ethertype by it's numerical representation
+ * and returns it's textual format.
+ *
+ * @param etherType the short value of the ethertype
+ * @return a textual representation
+ */
+ public EtherType lookup(short etherType) {
+ for (EtherType ethType : EtherType.values()) {
+ if (ethType.ethType().toShort() == etherType) {
+ return ethType;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ EthType ethType = (EthType) o;
+
+ if (etherType != ethType.etherType) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) etherType;
+ }
+
+ public String toString() {
+ EtherType ethType = lookup(this.etherType);
+ return (ethType == null ? String.format("0x%04x", etherType) :
+ ethType.toString());
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ethernet.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
new file mode 100644
index 00000000..003c1772
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
@@ -0,0 +1,625 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ *
+ */
+public class Ethernet extends BasePacket {
+ private static final String HEXES = "0123456789ABCDEF";
+
+ public static final short TYPE_ARP = EthType.EtherType.ARP.ethType().toShort();
+ public static final short TYPE_RARP = EthType.EtherType.RARP.ethType().toShort();
+ public static final short TYPE_IPV4 = EthType.EtherType.IPV4.ethType().toShort();
+ public static final short TYPE_IPV6 = EthType.EtherType.IPV6.ethType().toShort();
+ public static final short TYPE_LLDP = EthType.EtherType.LLDP.ethType().toShort();
+ public static final short TYPE_VLAN = EthType.EtherType.VLAN.ethType().toShort();
+ public static final short TYPE_BSN = EthType.EtherType.BDDP.ethType().toShort();
+
+ public static final short MPLS_UNICAST = EthType.EtherType.MPLS_UNICAST.ethType().toShort();;
+ public static final short MPLS_MULTICAST = EthType.EtherType.MPLS_MULTICAST.ethType().toShort();
+
+
+ public static final short VLAN_UNTAGGED = (short) 0xffff;
+
+ public static final short ETHERNET_HEADER_LENGTH = 14; // bytes
+ public static final short VLAN_HEADER_LENGTH = 4; // bytes
+
+ public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
+
+ private static final Map<Short, Deserializer<? extends IPacket>> ETHERTYPE_DESERIALIZER_MAP =
+ new HashMap<>();
+
+ static {
+ for (EthType.EtherType ethType : EthType.EtherType.values()) {
+ if (ethType.deserializer() != null) {
+ ETHERTYPE_DESERIALIZER_MAP.put(ethType.ethType().toShort(), ethType.deserializer());
+ }
+ }
+ }
+
+ protected MacAddress destinationMACAddress;
+ protected MacAddress sourceMACAddress;
+ protected byte priorityCode;
+ protected short vlanID;
+ protected short etherType;
+ protected boolean pad = false;
+
+ /**
+ * By default, set Ethernet to untagged.
+ */
+ public Ethernet() {
+ super();
+ this.vlanID = Ethernet.VLAN_UNTAGGED;
+ }
+
+ /**
+ * Gets the destination MAC address.
+ *
+ * @return the destination MAC as a byte array
+ */
+ public byte[] getDestinationMACAddress() {
+ return this.destinationMACAddress.toBytes();
+ }
+
+ /**
+ * Gets the destination MAC address.
+ *
+ * @return the destination MAC
+ */
+ public MacAddress getDestinationMAC() {
+ return this.destinationMACAddress;
+ }
+
+ /**
+ * Sets the destination MAC address.
+ *
+ * @param destMac the destination MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setDestinationMACAddress(final MacAddress destMac) {
+ this.destinationMACAddress = checkNotNull(destMac);
+ return this;
+ }
+
+ /**
+ * Sets the destination MAC address.
+ *
+ * @param destMac the destination MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setDestinationMACAddress(final byte[] destMac) {
+ this.destinationMACAddress = MacAddress.valueOf(destMac);
+ return this;
+ }
+
+ /**
+ * Sets the destination MAC address.
+ *
+ * @param destMac the destination MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setDestinationMACAddress(final String destMac) {
+ this.destinationMACAddress = MacAddress.valueOf(destMac);
+ return this;
+ }
+
+ /**
+ * Gets the source MAC address.
+ *
+ * @return the source MACAddress as a byte array
+ */
+ public byte[] getSourceMACAddress() {
+ return this.sourceMACAddress.toBytes();
+ }
+
+ /**
+ * Gets the source MAC address.
+ *
+ * @return the source MACAddress
+ */
+ public MacAddress getSourceMAC() {
+ return this.sourceMACAddress;
+ }
+
+ /**
+ * Sets the source MAC address.
+ *
+ * @param sourceMac the source MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setSourceMACAddress(final MacAddress sourceMac) {
+ this.sourceMACAddress = checkNotNull(sourceMac);
+ return this;
+ }
+
+ /**
+ * Sets the source MAC address.
+ *
+ * @param sourceMac the source MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setSourceMACAddress(final byte[] sourceMac) {
+ this.sourceMACAddress = MacAddress.valueOf(sourceMac);
+ return this;
+ }
+
+ /**
+ * Sets the source MAC address.
+ *
+ * @param sourceMac the source MAC to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setSourceMACAddress(final String sourceMac) {
+ this.sourceMACAddress = MacAddress.valueOf(sourceMac);
+ return this;
+ }
+
+ /**
+ * Gets the priority code.
+ *
+ * @return the priorityCode
+ */
+ public byte getPriorityCode() {
+ return this.priorityCode;
+ }
+
+ /**
+ * Sets the priority code.
+ *
+ * @param priority the priorityCode to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setPriorityCode(final byte priority) {
+ this.priorityCode = priority;
+ return this;
+ }
+
+ /**
+ * Gets the VLAN ID.
+ *
+ * @return the vlanID
+ */
+ public short getVlanID() {
+ return this.vlanID;
+ }
+
+ /**
+ * Sets the VLAN ID.
+ *
+ * @param vlan the vlanID to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setVlanID(final short vlan) {
+ this.vlanID = vlan;
+ return this;
+ }
+
+ /**
+ * Gets the Ethernet type.
+ *
+ * @return the etherType
+ */
+ public short getEtherType() {
+ return this.etherType;
+ }
+
+ /**
+ * Sets the Ethernet type.
+ *
+ * @param ethType the etherType to set
+ * @return the Ethernet frame
+ */
+ public Ethernet setEtherType(final short ethType) {
+ this.etherType = ethType;
+ return this;
+ }
+
+ /**
+ * @return True if the Ethernet frame is broadcast, false otherwise
+ */
+ public boolean isBroadcast() {
+ assert this.destinationMACAddress.length() == 6;
+ return this.destinationMACAddress.isBroadcast();
+ }
+
+ /**
+ * @return True is the Ethernet frame is multicast, False otherwise
+ */
+ public boolean isMulticast() {
+ return this.destinationMACAddress.isMulticast();
+ }
+
+ /**
+ * Pad this packet to 60 bytes minimum, filling with zeros?
+ *
+ * @return the pad
+ */
+ public boolean isPad() {
+ return this.pad;
+ }
+
+ /**
+ * Pad this packet to 60 bytes minimum, filling with zeros?
+ *
+ * @param pd
+ * the pad to set
+ * @return this
+ */
+ public Ethernet setPad(final boolean pd) {
+ this.pad = pd;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+ int length = 14 + (this.vlanID == Ethernet.VLAN_UNTAGGED ? 0 : 4)
+ + (payloadData == null ? 0 : payloadData.length);
+ if (this.pad && length < 60) {
+ length = 60;
+ }
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.destinationMACAddress.toBytes());
+ bb.put(this.sourceMACAddress.toBytes());
+ if (this.vlanID != Ethernet.VLAN_UNTAGGED) {
+ bb.putShort(TYPE_VLAN);
+ bb.putShort((short) (this.priorityCode << 13 | this.vlanID & 0x0fff));
+ }
+ bb.putShort(this.etherType);
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+ if (this.pad) {
+ Arrays.fill(data, bb.position(), data.length, (byte) 0x0);
+ }
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ if (length <= 0) {
+ return null;
+ }
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ if (this.destinationMACAddress == null) {
+ this.destinationMACAddress = MacAddress.valueOf(new byte[6]);
+ }
+ final byte[] dstAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ bb.get(dstAddr);
+ this.destinationMACAddress = MacAddress.valueOf(dstAddr);
+
+ if (this.sourceMACAddress == null) {
+ this.sourceMACAddress = MacAddress.valueOf(new byte[6]);
+ }
+ final byte[] srcAddr = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ bb.get(srcAddr);
+ this.sourceMACAddress = MacAddress.valueOf(srcAddr);
+
+ short ethType = bb.getShort();
+ if (ethType == TYPE_VLAN) {
+ final short tci = bb.getShort();
+ this.priorityCode = (byte) (tci >> 13 & 0x07);
+ this.vlanID = (short) (tci & 0x0fff);
+ ethType = bb.getShort();
+ } else {
+ this.vlanID = Ethernet.VLAN_UNTAGGED;
+ }
+ this.etherType = ethType;
+
+ IPacket payload;
+ Deserializer<? extends IPacket> deserializer;
+ if (Ethernet.ETHERTYPE_DESERIALIZER_MAP.containsKey(ethType)) {
+ deserializer = Ethernet.ETHERTYPE_DESERIALIZER_MAP.get(ethType);
+ } 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;
+ }
+
+ /**
+ * Checks to see if a string is a valid MAC address.
+ *
+ * @param macAddress string to test if it is a valid MAC
+ * @return True if macAddress is a valid MAC, False otherwise
+ */
+ public static boolean isMACAddress(final String macAddress) {
+ final String[] macBytes = macAddress.split(":");
+ if (macBytes.length != 6) {
+ return false;
+ }
+ for (int i = 0; i < 6; ++i) {
+ if (Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1
+ || Ethernet.HEXES.indexOf(macBytes[i].toUpperCase().charAt(
+ 1)) == -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 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 byte array
+ * @return The macAddress as a byte array
+ */
+ public static byte[] toMACAddress(final String macAddress) {
+ return MacAddress.valueOf(macAddress).toBytes();
+ }
+
+ /**
+ * Accepts a MAC address and returns the corresponding long, where the MAC
+ * bytes are set on the lower order bytes of the long.
+ *
+ * @param macAddress MAC address as a byte array
+ * @return a long containing the mac address bytes
+ */
+ public static long toLong(final byte[] macAddress) {
+ return MacAddress.valueOf(macAddress).toLong();
+ }
+
+ /**
+ * Converts a long MAC address to a byte array.
+ *
+ * @param macAddress MAC address set on the lower order bytes of the long
+ * @return the bytes of the mac address
+ */
+ public static byte[] toByteArray(final long macAddress) {
+ return MacAddress.valueOf(macAddress).toBytes();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 7867;
+ int result = super.hashCode();
+ result = prime * result + this.destinationMACAddress.hashCode();
+ result = prime * result + this.etherType;
+ result = prime * result + this.vlanID;
+ result = prime * result + this.priorityCode;
+ result = prime * result + (this.pad ? 1231 : 1237);
+ result = prime * result + this.sourceMACAddress.hashCode();
+ 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 Ethernet)) {
+ return false;
+ }
+ final Ethernet other = (Ethernet) obj;
+ if (!this.destinationMACAddress.equals(other.destinationMACAddress)) {
+ return false;
+ }
+ if (this.priorityCode != other.priorityCode) {
+ return false;
+ }
+ if (this.vlanID != other.vlanID) {
+ return false;
+ }
+ if (this.etherType != other.etherType) {
+ return false;
+ }
+ if (this.pad != other.pad) {
+ return false;
+ }
+ if (!this.sourceMACAddress.equals(other.sourceMACAddress)) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString(java.lang.Object)
+ */
+ @Override
+ public String toString() {
+
+ final StringBuffer sb = new StringBuffer("\n");
+
+ final IPacket pkt = this.getPayload();
+
+ if (pkt instanceof ARP) {
+ sb.append("arp");
+ } else if (pkt instanceof LLDP) {
+ sb.append("lldp");
+ } else if (pkt instanceof ICMP) {
+ sb.append("icmp");
+ } else if (pkt instanceof IPv4) {
+ sb.append("ip");
+ } else if (pkt instanceof DHCP) {
+ sb.append("dhcp");
+ } else {
+ sb.append(this.getEtherType());
+ }
+
+ sb.append("\ndl_vlan: ");
+ if (this.getVlanID() == Ethernet.VLAN_UNTAGGED) {
+ sb.append("untagged");
+ } else {
+ sb.append(this.getVlanID());
+ }
+ sb.append("\ndl_vlan_pcp: ");
+ sb.append(this.getPriorityCode());
+ sb.append("\ndl_src: ");
+ sb.append(bytesToHex(this.getSourceMACAddress()));
+ sb.append("\ndl_dst: ");
+ sb.append(bytesToHex(this.getDestinationMACAddress()));
+
+ if (pkt instanceof ARP) {
+ final ARP p = (ARP) pkt;
+ sb.append("\nnw_src: ");
+ sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
+ .getSenderProtocolAddress())));
+ sb.append("\nnw_dst: ");
+ sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p
+ .getTargetProtocolAddress())));
+ } else if (pkt instanceof LLDP) {
+ sb.append("lldp packet");
+ } else if (pkt instanceof ICMP) {
+ final ICMP icmp = (ICMP) pkt;
+ sb.append("\nicmp_type: ");
+ sb.append(icmp.getIcmpType());
+ sb.append("\nicmp_code: ");
+ sb.append(icmp.getIcmpCode());
+ } else if (pkt instanceof IPv4) {
+ final IPv4 p = (IPv4) pkt;
+ sb.append("\nnw_src: ");
+ sb.append(IPv4.fromIPv4Address(p.getSourceAddress()));
+ sb.append("\nnw_dst: ");
+ sb.append(IPv4.fromIPv4Address(p.getDestinationAddress()));
+ sb.append("\nnw_tos: ");
+ sb.append(p.getDiffServ());
+ sb.append("\nnw_proto: ");
+ sb.append(p.getProtocol());
+
+ if (pkt instanceof TCP) {
+ sb.append("\ntp_src: ");
+ sb.append(((TCP) pkt).getSourcePort());
+ sb.append("\ntp_dst: ");
+ sb.append(((TCP) pkt).getDestinationPort());
+
+ } else if (pkt instanceof UDP) {
+ sb.append("\ntp_src: ");
+ sb.append(((UDP) pkt).getSourcePort());
+ sb.append("\ntp_dst: ");
+ sb.append(((UDP) pkt).getDestinationPort());
+ }
+
+ if (pkt instanceof ICMP) {
+ final ICMP icmp = (ICMP) pkt;
+ sb.append("\nicmp_type: ");
+ sb.append(icmp.getIcmpType());
+ sb.append("\nicmp_code: ");
+ sb.append(icmp.getIcmpCode());
+ }
+
+ } else if (pkt instanceof DHCP) {
+ sb.append("\ndhcp packet");
+ } else if (pkt instanceof Data) {
+ sb.append("\ndata packet");
+ } else if (pkt instanceof LLC) {
+ sb.append("\nllc packet");
+ } else {
+ sb.append("\nunknown packet");
+ }
+
+ return sb.toString();
+ }
+
+ public static String bytesToHex(byte[] in) {
+ final StringBuilder builder = new StringBuilder();
+ for (byte b : in) {
+ builder.append(String.format("%02x", b));
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Deserializer function for Ethernet packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Ethernet> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, ETHERNET_HEADER_LENGTH);
+
+ byte[] addressBuffer = new byte[DATALAYER_ADDRESS_LENGTH];
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ Ethernet eth = new Ethernet();
+ // Read destination MAC address into buffer
+ bb.get(addressBuffer);
+ eth.setDestinationMACAddress(addressBuffer);
+
+ // Read source MAC address into buffer
+ bb.get(addressBuffer);
+ eth.setSourceMACAddress(addressBuffer);
+
+ short ethType = bb.getShort();
+ if (ethType == TYPE_VLAN) {
+ checkHeaderLength(length, ETHERNET_HEADER_LENGTH + VLAN_HEADER_LENGTH);
+ final short tci = bb.getShort();
+ eth.setPriorityCode((byte) (tci >> 13 & 0x07));
+ eth.setVlanID((short) (tci & 0x0fff));
+ ethType = bb.getShort();
+ } else {
+ eth.setVlanID(Ethernet.VLAN_UNTAGGED);
+ }
+ eth.setEtherType(ethType);
+
+ IPacket payload;
+ Deserializer<? extends IPacket> deserializer;
+ if (Ethernet.ETHERTYPE_DESERIALIZER_MAP.containsKey(ethType)) {
+ deserializer = Ethernet.ETHERTYPE_DESERIALIZER_MAP.get(ethType);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ payload.setParent(eth);
+ eth.setPayload(payload);
+
+ return eth;
+ };
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP.java
new file mode 100644
index 00000000..d07a9ba4
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ICMP.java
@@ -0,0 +1,223 @@
+/*
+ * 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 java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ * Implements ICMP packet format.
+ *
+ */
+public class ICMP extends BasePacket {
+ protected byte icmpType;
+ protected byte icmpCode;
+ protected short checksum;
+
+ public static final byte TYPE_ECHO_REQUEST = 0x08;
+ public static final byte TYPE_ECHO_REPLY = 0x00;
+ public static final byte SUBTYPE_ECHO_REPLY = 0x00;
+
+ public static final short ICMP_HEADER_LENGTH = 4;
+
+ /**
+ * @return the icmpType
+ */
+ public byte getIcmpType() {
+ return this.icmpType;
+ }
+
+ /**
+ * @param icmpType
+ * to set
+ * @return this
+ */
+ public ICMP setIcmpType(final byte icmpType) {
+ this.icmpType = icmpType;
+ return this;
+ }
+
+ /**
+ * @return the icmp code
+ */
+ public byte getIcmpCode() {
+ return this.icmpCode;
+ }
+
+ /**
+ * @param icmpCode
+ * code to set
+ * @return this
+ */
+ public ICMP setIcmpCode(final byte icmpCode) {
+ this.icmpCode = icmpCode;
+ return this;
+ }
+
+ /**
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * @param checksum
+ * the checksum to set
+ * @return this
+ */
+ public ICMP setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ /**
+ * Serializes the packet. Will compute and set the following fields if they
+ * are set to specific values at the time serialize is called: -checksum : 0
+ * -length : 0
+ */
+ @Override
+ public byte[] serialize() {
+ int length = 4;
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ length += payloadData.length;
+ }
+
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.icmpType);
+ bb.put(this.icmpCode);
+ bb.putShort(this.checksum);
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IPv4) {
+ ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_ICMP);
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bb.rewind();
+ int accumulation = 0;
+
+ for (int i = 0; i < length / 2; ++i) {
+ accumulation += 0xffff & bb.getShort();
+ }
+ // pad to an even number of shorts
+ if (length % 2 > 0) {
+ accumulation += (bb.get() & 0xff) << 8;
+ }
+
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bb.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();
+
+ this.payload = new Data();
+ this.payload = 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.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 ICMP)) {
+ return false;
+ }
+ final ICMP other = (ICMP) 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 ICMP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<ICMP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, ICMP_HEADER_LENGTH);
+
+ ICMP icmp = new ICMP();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ icmp.icmpType = bb.get();
+ icmp.icmpCode = bb.get();
+ icmp.checksum = bb.getShort();
+
+ icmp.payload = Data.deserializer()
+ .deserialize(data, bb.position(), bb.limit()
+ - bb.position());
+ icmp.payload.setParent(icmp);
+ return icmp;
+ };
+ }
+}
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;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java
new file mode 100644
index 00000000..e7abbd6a
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 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 java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+
+/**
+ * Implements IGMP control packet format.
+ */
+public class IGMP extends BasePacket {
+ private final Logger log = getLogger(getClass());
+
+ public static final byte TYPE_IGMPV3_MEMBERSHIP_QUERY = 0x11;
+ public static final byte TYPE_IGMPV1_MEMBERSHIP_REPORT = 0x12;
+ public static final byte TYPE_IGMPV2_MEMBERSHIP_REPORT = 0x16;
+ public static final byte TYPE_IGMPV2_LEAVE_GROUP = 0x17;
+ public static final byte TYPE_IGMPV3_MEMBERSHIP_REPORT = 0x22;
+ public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP = new HashMap<>();
+
+ public static final int MINIMUM_HEADER_LEN = 12;
+
+ List<IGMPGroup> groups = new ArrayList<>();
+
+ // Fields contained in the IGMP header
+ private byte igmpType;
+ private byte resField = 0;
+ private short checksum = 0;
+
+ private byte[] unsupportTypeData;
+
+ public IGMP() {
+ }
+
+ /**
+ * Get the IGMP message type.
+ *
+ * @return the IGMP message type
+ */
+ public byte getIgmpType() {
+ return igmpType;
+ }
+
+ /**
+ * Set the IGMP message type.
+ *
+ * @param msgType IGMP message type
+ */
+ public void setIgmpType(byte msgType) {
+ igmpType = msgType;
+ }
+
+ /**
+ * Get the checksum of this message.
+ *
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return checksum;
+ }
+
+ /**
+ * get the Max Resp Code.
+ *
+ * @return The Maximum Time allowed before before sending a responding report.
+ */
+ public byte getMaxRespField() {
+ return resField;
+ }
+
+ /**
+ * Set the Max Resp Code.
+ *
+ * @param respCode the Maximum Response Code.
+ */
+ public void setMaxRespCode(byte respCode) {
+ if (igmpType != IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY) {
+ log.debug("Requesting the max response code for an incorrect field: ");
+ }
+ this.resField = respCode;
+ }
+
+ /**
+ * Get the list of IGMPGroups. The group objects will be either IGMPQuery or IGMPMembership
+ * depending on the IGMP message type. For IGMP Query, the groups list should only be
+ * one group.
+ *
+ * @return The list of IGMP groups.
+ */
+ public List<IGMPGroup> getGroups() {
+ return groups;
+ }
+
+ /**
+ * Add a multicast group to this IGMP message.
+ *
+ * @param group the IGMPGroup will be IGMPQuery or IGMPMembership depending on the message type.
+ * @return true if group was valid and added, false otherwise.
+ */
+ public boolean addGroup(IGMPGroup group) {
+ checkNotNull(group);
+ switch (this.igmpType) {
+ case TYPE_IGMPV3_MEMBERSHIP_QUERY:
+ if (group instanceof IGMPMembership) {
+ return false;
+ }
+
+ if (group.sources.size() > 1) {
+ return false;
+ }
+ break;
+
+ case TYPE_IGMPV3_MEMBERSHIP_REPORT:
+ if (group instanceof IGMPMembership) {
+ return false;
+ }
+ break;
+
+ default:
+ log.debug("Warning no IGMP message type has been set");
+ }
+
+ this.groups.add(group);
+ return true;
+ }
+
+ /**
+ * Serialize this IGMP packet. This will take care
+ * of serializing IGMPv3 Queries and IGMPv3 Membership
+ * Reports.
+ *
+ * @return the serialized IGMP message
+ */
+ @Override
+ public byte[] serialize() {
+ byte [] data = new byte[8915];
+
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.getIgmpType());
+
+ // reserved or max resp code depending on type.
+ bb.put(this.resField);
+
+ // Must calculate checksum
+ bb.putShort((short) 0);
+
+ switch (this.igmpType) {
+
+ case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
+ // reserved
+ bb.putShort((short) 0);
+ // Number of groups
+ bb.putShort((short) groups.size());
+ // Fall through
+
+ case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
+
+ for (IGMPGroup grp : groups) {
+ grp.serialize(bb);
+ }
+ break;
+
+ default:
+ bb.put(this.unsupportTypeData);
+ break;
+ }
+
+ int size = bb.position();
+ bb.position(0);
+ byte [] rdata = new byte[size];
+ bb.get(rdata, 0, size);
+ return rdata;
+ }
+
+ /**
+ * Deserialize an IGMP message.
+ *
+ * @param data bytes to deserialize
+ * @param offset offset to start deserializing from
+ * @param length length of the data to deserialize
+ * @return populated IGMP object
+ */
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+
+ IGMP igmp = new IGMP();
+ try {
+ igmp = IGMP.deserializer().deserialize(data, offset, length);
+ } catch (DeserializationException e) {
+ log.error(e.getStackTrace().toString());
+ return this;
+ }
+ this.igmpType = igmp.igmpType;
+ this.resField = igmp.resField;
+ this.checksum = igmp.checksum;
+ this.groups = igmp.groups;
+ return this;
+ }
+
+ /**
+ * Deserializer function for IPv4 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<IGMP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, MINIMUM_HEADER_LEN);
+
+ IGMP igmp = new IGMP();
+
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ igmp.igmpType = bb.get();
+ igmp.resField = bb.get();
+ igmp.checksum = bb.getShort();
+ int len = MINIMUM_HEADER_LEN;
+ String msg;
+
+ switch (igmp.igmpType) {
+
+ case TYPE_IGMPV3_MEMBERSHIP_QUERY:
+ IGMPQuery qgroup = new IGMPQuery();
+ qgroup.deserialize(bb);
+ igmp.groups.add(qgroup);
+ break;
+
+ case TYPE_IGMPV3_MEMBERSHIP_REPORT:
+ bb.getShort(); // Ignore resvd
+ int ngrps = bb.getShort();
+
+ for (; ngrps > 0; ngrps--) {
+ IGMPMembership mgroup = new IGMPMembership();
+ mgroup.deserialize(bb);
+ igmp.groups.add(mgroup);
+ }
+ break;
+
+ /*
+ * NOTE: according to the IGMPv3 spec. These previous IGMP type fields
+ * must be supported. At this time we are going to <b>assume</b> we run
+ * in a modern network where all devices are IGMPv3 capable.
+ */
+ case TYPE_IGMPV1_MEMBERSHIP_REPORT:
+ case TYPE_IGMPV2_MEMBERSHIP_REPORT:
+ case TYPE_IGMPV2_LEAVE_GROUP:
+ igmp.unsupportTypeData = bb.array(); // Is this the entire array?
+ msg = "IGMP message type: " + igmp.igmpType + " is not supported";
+ igmp.log.debug(msg);
+ break;
+
+ default:
+ msg = "IGMP message type: " + igmp.igmpType + " is not recognized";
+ igmp.unsupportTypeData = bb.array();
+ igmp.log.debug(msg);
+ break;
+ }
+ return igmp;
+ };
+ }
+
+ /*
+ * (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 IGMP)) {
+ return false;
+ }
+ final IGMP other = (IGMP) obj;
+ if (this.igmpType != other.igmpType) {
+ return false;
+ }
+ if (this.resField != other.resField) {
+ return false;
+ }
+ if (this.checksum != other.checksum) {
+ return false;
+ }
+ if (this.groups.size() != other.groups.size()) {
+ return false;
+ }
+ // TODO: equals should be true regardless of order.
+ if (!groups.equals(other.groups)) {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ result = prime * result + this.igmpType;
+ result = prime * result + this.groups.size();
+ result = prime * result + this.resField;
+ result = prime * result + this.checksum;
+ result = prime * result + this.groups.hashCode();
+ return result;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java
new file mode 100644
index 00000000..70ff5563
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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 java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class to represent Groups for membership query and reports.
+ */
+public abstract class IGMPGroup {
+
+ protected int auxInfo;
+ protected IpAddress gaddr;
+ protected List<IpAddress> sources = new ArrayList<>();
+
+ public IGMPGroup() {
+ }
+
+ /**
+ * Initialize this object with a multicast group address and additional info.
+ *
+ * @param gaddr: the multicast group address for this message type.
+ * @param auxInfo: additional info potentially used by IGMPQuery
+ */
+ public IGMPGroup(IpAddress gaddr, int auxInfo) {
+ this.gaddr = gaddr;
+ this.auxInfo = auxInfo;
+ }
+
+ /**
+ * Get the multicast group address.
+ *
+ * @return the group address
+ */
+ public IpAddress getGaddr() {
+ return this.gaddr;
+ }
+
+ /**
+ * Get the auxillary info.
+ *
+ * @return the auxillary info
+ */
+ public int getAuxInfo() {
+ return this.auxInfo;
+ }
+
+ /**
+ * Add a unicast source address to this message.
+ *
+ * @param saddr IPv4 unicast source address
+ */
+ public void addSource(IpAddress saddr) {
+ sources.add(saddr);
+ }
+
+ /**
+ * Return the list of source addresses.
+ *
+ * @return list of source addresses
+ */
+ public List<IpAddress> getSources() {
+ return sources;
+ }
+
+ /**
+ * Deserialize an IGMPQuery or IGMPMembership message.
+ *
+ * @param bb the ByteBuffer wrapping the serialized message. The position of the
+ * ByteBuffer should be pointing at the head of either message type.
+ * @return An object populated with the respective IGMPGroup subclass
+ * @throws DeserializationException in case deserialization goes wrong
+ */
+ public abstract IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException;
+
+ /**
+ * Serialize the IGMPGroup subclass.
+ *
+ * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
+ * @return The serialized message
+ */
+ public abstract byte[] serialize(ByteBuffer bb);
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java
new file mode 100644
index 00000000..495e283c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 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 java.nio.ByteBuffer;
+import static org.onlab.packet.PacketUtils.checkBufferLength;
+
+public class IGMPMembership extends IGMPGroup {
+
+ public static final byte MODE_IS_INCLUDE = 0x1;
+ public static final byte MODE_IS_EXCLUDE = 0x2;
+ public static final byte CHANGE_TO_INCLUDE_MODE = 0x3;
+ public static final byte CHANGE_TO_EXCLUDE_MODE = 0x4;
+ public static final byte ALLOW_NEW_SOURCES = 0x5;
+ public static final byte BLOCK_OLD_SOURCES = 0x6;
+
+ private final int minGroupRecordLen = Ip4Address.BYTE_LENGTH + 4;
+
+ protected byte recordType;
+ protected byte auxDataLength = 0;
+ protected byte[] auxData;
+
+ /**
+ * Constructor initialized with a multicast group address.
+ *
+ * @param gaddr A multicast group address.
+ */
+ public IGMPMembership(Ip4Address gaddr) {
+ super(gaddr, 0);
+ }
+
+ /**
+ * Default constructor.
+ */
+ public IGMPMembership() {
+ super();
+ }
+
+ /**
+ * Serialize this Membership Report.
+ *
+ * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
+ * @return serialized IGMP message.
+ */
+ @Override
+ public byte[] serialize(ByteBuffer bb) {
+
+ bb.put(recordType);
+ bb.put(auxDataLength); // reserved
+ bb.putShort((short) sources.size());
+ bb.put(gaddr.toOctets());
+ for (IpAddress ipaddr : sources) {
+ bb.put(ipaddr.toOctets());
+ }
+
+ if (auxDataLength > 0) {
+ bb.put(auxData);
+ }
+
+ return bb.array();
+ }
+
+ /**
+ * Deserialize the IGMP Membership report packet.
+ *
+ * @param bb the ByteBuffer wrapping the serialized message. The position of the
+ * ByteBuffer should be pointing at the head of either message type.
+ * @return IGMP Group
+ * @throws DeserializationException if deserialization fails
+ */
+ public IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException {
+
+ // Make sure there is enough buffer to read the header,
+ // including the number of sources
+ checkBufferLength(bb.remaining(), 0, minGroupRecordLen);
+ recordType = bb.get();
+ auxDataLength = bb.get();
+ int nsrcs = bb.getShort();
+
+ gaddr = Ip4Address.valueOf(bb.getInt());
+
+ // Make sure we have enough buffer to hold all of these sources
+ checkBufferLength(bb.remaining(), 0, Ip4Address.BYTE_LENGTH * nsrcs);
+ for (; nsrcs > 0; nsrcs--) {
+ Ip4Address src = Ip4Address.valueOf(bb.getInt());
+ this.sources.add(src);
+ }
+
+ if (auxDataLength > 0) {
+ auxData = new byte[auxDataLength];
+ bb.get(auxData, 0, auxDataLength);
+ }
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals()
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof IGMPMembership)) {
+ return false;
+ }
+ IGMPMembership other = (IGMPMembership) obj;
+
+ if (!this.gaddr.equals(other.gaddr)) {
+ return false;
+ }
+ if (this.recordType != other.recordType) {
+ return false;
+ }
+ if (this.auxDataLength != other.auxDataLength) {
+ return false;
+ }
+ if (this.sources.size() != other.sources.size()) {
+ return false;
+ }
+ // TODO: make these tolerant of order
+ if (!this.sources.equals(other.sources)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ result = prime * result + this.gaddr.hashCode();
+ result = prime * result + this.recordType;
+ result = prime * result + this.auxDataLength;
+ result = prime * result + this.sources.hashCode();
+ return result;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java
new file mode 100644
index 00000000..bb53eba5
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 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 java.nio.ByteBuffer;
+
+public class IGMPQuery extends IGMPGroup {
+
+ // Bits and bytes after the group address
+ private byte resv = 0;
+ private boolean sbit = false;
+ private byte qrv = 2;
+ private byte qqic = 0x7d;
+
+ /**
+ * Create IGMP Query message.
+ *
+ * @param gaddr initiaze with a group address.
+ * @param auxInfo auxillary info.
+ */
+ public IGMPQuery(IpAddress gaddr, int auxInfo) {
+ super(gaddr, auxInfo);
+ }
+
+ /**
+ * Create IGMP Query message.
+ */
+ public IGMPQuery() {
+ super();
+ }
+
+ /**
+ * Is the S flag set? Telling adjacent routers to suppress normal timer updates.
+ *
+ * @return true if the flag is set, false otherwise
+ */
+ public boolean isSbit() {
+ return sbit;
+ }
+
+ /**
+ * Set the S flag. Default is false.
+ *
+ * @param sbit true or false
+ */
+ public void setSbit(boolean sbit) {
+ this.sbit = sbit;
+ }
+
+ /**
+ * Get the Querier Robustness Variable.
+ *
+ * @return querier robustness value
+ */
+ public byte getQrv() {
+ return qrv;
+ }
+
+ /**
+ * Set the Querier Robustness Variable. Default is 2.
+ *
+ * @param qrv new querier robustness value
+ */
+ public void setQrv(byte qrv) {
+ this.qrv = qrv;
+ }
+
+ /**
+ * Get the reserved field. Should be zero, but ignored regardless of it's value.
+ *
+ * @return the reserved field.
+ */
+ public byte getResv() {
+ return resv;
+ }
+
+ /**
+ * Set the reserved field. Should be 0 and ignored by receivers.
+ *
+ * @param resv the reserved field.
+ */
+ public void setResv(byte resv) {
+ this.resv = resv;
+ }
+
+ /**
+ * Serialize this IGMPQuery.
+ *
+ * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
+ * @return the serialized message
+ */
+ @Override
+ public byte[] serialize(ByteBuffer bb) {
+
+ bb.put(gaddr.toOctets());
+ byte fld = (byte) (0x7 & qrv);
+ bb.put(fld);
+ bb.put(qqic);
+ bb.putShort((short) sources.size());
+ for (IpAddress ipaddr : sources) {
+ bb.put(ipaddr.toOctets());
+ }
+ return bb.array();
+ }
+
+ /**
+ * Deserialize the IGMP Query group structure.
+ *
+ * @param bb ByteBuffer pointing at the IGMP Query group address
+ * @return the IGMP Group object
+ */
+ public IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException {
+
+ gaddr = Ip4Address.valueOf(bb.getInt());
+ byte fld = bb.get();
+
+ // Just ignore the reserved bits
+ resv = 0;
+ this.sbit = ((fld & 0x8) == 0x8);
+ qrv = (byte) (fld & 0x7);
+
+ // QQIC field
+ qqic = bb.get();
+
+ // Get the number of sources.
+ short nsrcs = bb.getShort();
+
+ // Do a sanity check on the amount of space we have in our buffer.
+ int lengthNeeded = (Ip4Address.BYTE_LENGTH * nsrcs);
+ PacketUtils.checkHeaderLength(bb.remaining(), lengthNeeded);
+
+ for (; nsrcs > 0; nsrcs--) {
+ Ip4Address ipaddr = Ip4Address.valueOf(bb.getInt());
+ this.sources.add(ipaddr);
+ }
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals()
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof IGMPQuery)) {
+ return false;
+ }
+ IGMPQuery other = (IGMPQuery) obj;
+
+ if (this.sbit != other.sbit) {
+ return false;
+ }
+ if (this.qrv != other.qrv) {
+ return false;
+ }
+ if (this.qqic != other.qqic) {
+ return false;
+ }
+ if (this.sources.size() != other.sources.size()) {
+ return false;
+ }
+
+ // TODO: make these tolerant of order
+ if (!this.sources.equals(other.sources)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ result = prime * result + this.gaddr.hashCode();
+ result = prime * result + this.qqic;
+ result = prime * result + this.qrv;
+ result = prime * result + this.sources.hashCode();
+ return result;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPacket.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPacket.java
new file mode 100644
index 00000000..64e6ac36
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPacket.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+/**
+ *
+ */
+public interface IPacket {
+ /**
+ *
+ * @return the payload
+ */
+ IPacket getPayload();
+
+ /**
+ *
+ * @param packet new payload
+ * @return self
+ */
+ IPacket setPayload(IPacket packet);
+
+ /**
+ *
+ * @return parent packet
+ */
+ IPacket getParent();
+
+ /**
+ *
+ * @param packet new parent
+ * @return self
+ */
+ IPacket setParent(IPacket packet);
+
+ /**
+ * Reset any checksums as needed, and call resetChecksum on all parents.
+ */
+ void resetChecksum();
+
+ /**
+ * Sets all payloads parent packet if applicable, then serializes this
+ * packet and all payloads.
+ *
+ * @return a byte[] containing this packet and payloads
+ */
+ byte[] serialize();
+
+ /**
+ * Deserializes this packet layer and all possible payloads.
+ *
+ * NOTE: This method has been deprecated and will be removed in a future
+ * release. It is now recommended to use the Deserializer function provided
+ * by the deserialize() method on each packet to deserialize them. The
+ * Deserializer functions are robust to malformed input.
+ *
+ * @param data bytes to deserialize
+ * @param offset
+ * offset to start deserializing from
+ * @param length
+ * length of the data to deserialize
+ * @return the deserialized data
+ * @deprecated in Cardinal Release
+ */
+ @Deprecated
+ IPacket deserialize(byte[] data, int offset, int length);
+
+ /**
+ * Clone this packet and its payload packet but not its parent.
+ *
+ * @return the clone
+ */
+ Object clone();
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv4.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv4.java
new file mode 100644
index 00000000..d75b50a2
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv4.java
@@ -0,0 +1,731 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ *
+ */
+public class IPv4 extends BasePacket {
+ public static final byte PROTOCOL_ICMP = 0x1;
+ public static final byte PROTOCOL_IGMP = 0x2;
+ public static final byte PROTOCOL_TCP = 0x6;
+ public static final byte PROTOCOL_UDP = 0x11;
+ public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
+ new HashMap<>();
+
+ static {
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_ICMP, ICMP.deserializer());
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_IGMP, IGMP.deserializer());
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_TCP, TCP.deserializer());
+ IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_UDP, UDP.deserializer());
+ }
+
+ private static final byte DSCP_MASK = 0x3f;
+ private static final byte DSCP_OFFSET = 2;
+ private static final byte ECN_MASK = 0x3;
+
+ private static final short HEADER_LENGTH = 20;
+
+ protected byte version;
+ protected byte headerLength;
+ protected byte diffServ;
+ protected short totalLength;
+ protected short identification;
+ protected byte flags;
+ protected short fragmentOffset;
+ protected byte ttl;
+ protected byte protocol;
+ protected short checksum;
+ protected int sourceAddress;
+ protected int destinationAddress;
+ protected byte[] options;
+
+ protected boolean isTruncated;
+
+ /**
+ * Default constructor that sets the version to 4.
+ */
+ public IPv4() {
+ super();
+ this.version = 4;
+ this.isTruncated = false;
+ }
+
+ /**
+ * @return the version
+ */
+ public byte getVersion() {
+ return this.version;
+ }
+
+ /**
+ * @param version
+ * the version to set
+ * @return this
+ */
+ public IPv4 setVersion(final byte version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * @return the headerLength
+ */
+ public byte getHeaderLength() {
+ return this.headerLength;
+ }
+
+ /**
+ * Gets the DSCP value (6 bits).
+ *
+ * @return the DSCP value (6 bits)
+ */
+ public byte getDscp() {
+ return (byte) ((this.diffServ >>> DSCP_OFFSET) & DSCP_MASK);
+ }
+
+ /**
+ * Sets the DSCP value (6 bits).
+ *
+ * @param dscp the DSCP value (6 bits)
+ * @return this
+ */
+ public IPv4 setDscp(byte dscp) {
+ this.diffServ &= ~(DSCP_MASK << DSCP_OFFSET);
+ this.diffServ |= (dscp & DSCP_MASK) << DSCP_OFFSET;
+ return this;
+ }
+
+ /**
+ * Gets the ECN value (2 bits).
+ *
+ * @return the ECN value (2 bits)
+ */
+ public byte getEcn() {
+ return (byte) (this.diffServ & ECN_MASK);
+ }
+
+ /**
+ * Sets the ECN value (2 bits).
+ *
+ * @param ecn the ECN value (2 bits)
+ * @return this
+ */
+ public IPv4 setEcn(byte ecn) {
+ this.diffServ &= ~ECN_MASK;
+ this.diffServ |= (ecn & ECN_MASK);
+ return this;
+ }
+
+ /**
+ * Gets the DiffServ octet (including the DSCP and ECN bits).
+ *
+ * @return the diffServ octet (including the DSCP and ECN bits)
+ */
+ public byte getDiffServ() {
+ return this.diffServ;
+ }
+
+ /**
+ * Sets the DiffServ octet (including the DSCP and ECN bits).
+ *
+ * @param diffServ the diffServ octet to set (including the DSCP and ECN
+ * bits)
+ * @return this
+ */
+ public IPv4 setDiffServ(final byte diffServ) {
+ this.diffServ = diffServ;
+ return this;
+ }
+
+ /**
+ * @return the totalLength
+ */
+ public short getTotalLength() {
+ return this.totalLength;
+ }
+
+ /**
+ * @return the identification
+ */
+ public short getIdentification() {
+ return this.identification;
+ }
+
+ public boolean isTruncated() {
+ return this.isTruncated;
+ }
+
+ public void setTruncated(final boolean isTruncated) {
+ this.isTruncated = isTruncated;
+ }
+
+ /**
+ * @param identification
+ * the identification to set
+ * @return this
+ */
+ public IPv4 setIdentification(final short identification) {
+ this.identification = identification;
+ return this;
+ }
+
+ /**
+ * @return the flags
+ */
+ public byte getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * @param flags
+ * the flags to set
+ * @return this
+s */
+ public IPv4 setFlags(final byte flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * @return the fragmentOffset
+ */
+ public short getFragmentOffset() {
+ return this.fragmentOffset;
+ }
+
+ /**
+ * @param fragmentOffset
+ * the fragmentOffset to set
+ * @return this
+ */
+ public IPv4 setFragmentOffset(final short fragmentOffset) {
+ this.fragmentOffset = fragmentOffset;
+ return this;
+ }
+
+ /**
+ * @return the ttl
+ */
+ public byte getTtl() {
+ return this.ttl;
+ }
+
+ /**
+ * @param ttl
+ * the ttl to set
+ * @return this
+ */
+ public IPv4 setTtl(final byte ttl) {
+ this.ttl = ttl;
+ return this;
+ }
+
+ /**
+ * @return the protocol
+ */
+ public byte getProtocol() {
+ return this.protocol;
+ }
+
+ /**
+ * @param protocol
+ * the protocol to set
+ * @return this
+ */
+ public IPv4 setProtocol(final byte protocol) {
+ this.protocol = protocol;
+ return this;
+ }
+
+ /**
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * @param checksum
+ * the checksum to set
+ * @return this
+ */
+ public IPv4 setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ @Override
+ public void resetChecksum() {
+ this.checksum = 0;
+ super.resetChecksum();
+ }
+
+ /**
+ * @return the sourceAddress
+ */
+ public int getSourceAddress() {
+ return this.sourceAddress;
+ }
+
+ /**
+ * @param sourceAddress
+ * the sourceAddress to set
+ * @return this
+ */
+ public IPv4 setSourceAddress(final int sourceAddress) {
+ this.sourceAddress = sourceAddress;
+ return this;
+ }
+
+ /**
+ * @param sourceAddress
+ * the sourceAddress to set
+ * @return this
+ */
+ public IPv4 setSourceAddress(final String sourceAddress) {
+ this.sourceAddress = IPv4.toIPv4Address(sourceAddress);
+ return this;
+ }
+
+ /**
+ * @return the destinationAddress
+ */
+ public int getDestinationAddress() {
+ return this.destinationAddress;
+ }
+
+ /**
+ * @param destinationAddress
+ * the destinationAddress to set
+ * @return this
+ */
+ public IPv4 setDestinationAddress(final int destinationAddress) {
+ this.destinationAddress = destinationAddress;
+ return this;
+ }
+
+ /**
+ * @param destinationAddress
+ * the destinationAddress to set
+ * @return this
+ */
+ public IPv4 setDestinationAddress(final String destinationAddress) {
+ this.destinationAddress = IPv4.toIPv4Address(destinationAddress);
+ return this;
+ }
+
+ /**
+ * @return the options
+ */
+ public byte[] getOptions() {
+ return this.options;
+ }
+
+ /**
+ * @param options
+ * the options to set
+ * @return this
+ */
+ public IPv4 setOptions(final byte[] options) {
+ if (options != null && options.length % 4 > 0) {
+ throw new IllegalArgumentException(
+ "Options length must be a multiple of 4");
+ }
+ this.options = options;
+ return this;
+ }
+
+ /**
+ * Serializes the packet. Will compute and set the following fields if they
+ * are set to specific values at the time serialize is called: -checksum : 0
+ * -headerLength : 0 -totalLength : 0
+ */
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ int optionsLength = 0;
+ if (this.options != null) {
+ optionsLength = this.options.length / 4;
+ }
+ this.headerLength = (byte) (5 + optionsLength);
+
+ this.totalLength = (short) (this.headerLength * 4 + (payloadData == null ? 0
+ : payloadData.length));
+
+ final byte[] data = new byte[this.totalLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put((byte) ((this.version & 0xf) << 4 | this.headerLength & 0xf));
+ bb.put(this.diffServ);
+ bb.putShort(this.totalLength);
+ bb.putShort(this.identification);
+ bb.putShort((short) ((this.flags & 0x7) << 13 | this.fragmentOffset & 0x1fff));
+ bb.put(this.ttl);
+ bb.put(this.protocol);
+ bb.putShort(this.checksum);
+ bb.putInt(this.sourceAddress);
+ bb.putInt(this.destinationAddress);
+ if (this.options != null) {
+ bb.put(this.options);
+ }
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bb.rewind();
+ int accumulation = 0;
+ for (int i = 0; i < this.headerLength * 2; ++i) {
+ accumulation += 0xffff & bb.getShort();
+ }
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bb.putShort(10, 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);
+ short sscratch;
+
+ this.version = bb.get();
+ this.headerLength = (byte) (this.version & 0xf);
+ this.version = (byte) (this.version >> 4 & 0xf);
+ this.diffServ = bb.get();
+ this.totalLength = bb.getShort();
+ this.identification = bb.getShort();
+ sscratch = bb.getShort();
+ this.flags = (byte) (sscratch >> 13 & 0x7);
+ this.fragmentOffset = (short) (sscratch & 0x1fff);
+ this.ttl = bb.get();
+ this.protocol = bb.get();
+ this.checksum = bb.getShort();
+ this.sourceAddress = bb.getInt();
+ this.destinationAddress = bb.getInt();
+
+ if (this.headerLength > 5) {
+ final int optionsLength = (this.headerLength - 5) * 4;
+ this.options = new byte[optionsLength];
+ bb.get(this.options);
+ }
+
+ if (this.totalLength != length) {
+ this.isTruncated = true;
+ } else {
+ this.isTruncated = false;
+ }
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv4.PROTOCOL_DESERIALIZER_MAP.containsKey(this.protocol)) {
+ deserializer = IPv4.PROTOCOL_DESERIALIZER_MAP.get(this.protocol);
+ } 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;
+ }
+
+ /**
+ * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
+ * returns the corresponding 32 bit integer.
+ *
+ * @param ipAddress ip address in string form
+ * @return int ip address value
+ */
+ public static int toIPv4Address(final String ipAddress) {
+ if (ipAddress == null) {
+ throw new IllegalArgumentException("Specified IPv4 address must"
+ + "contain 4 sets of numerical digits separated by periods");
+ }
+ final String[] octets = ipAddress.split("\\.");
+ if (octets.length != 4) {
+ throw new IllegalArgumentException("Specified IPv4 address must"
+ + "contain 4 sets of numerical digits separated by periods");
+ }
+
+ int result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result |= Integer.parseInt(octets[i]) << (3 - i) * 8;
+ }
+ return result;
+ }
+
+ /**
+ * Accepts an IPv4 address in a byte array and returns the corresponding
+ * 32-bit integer value.
+ *
+ * @param ipAddress ip address in byte form
+ * @return int ip address value
+ */
+ public static int toIPv4Address(final byte[] ipAddress) {
+ int ip = 0;
+ for (int i = 0; i < 4; i++) {
+ final int t = (ipAddress[i] & 0xff) << (3 - i) * 8;
+ ip |= t;
+ }
+ return ip;
+ }
+
+ /**
+ * Accepts an IPv4 address and returns of string of the form xxx.xxx.xxx.xxx,
+ * e.g., 192.168.0.1.
+ *
+ * @param ipAddress ip address in form
+ * @return string form of ip address
+ */
+ public static String fromIPv4Address(final int ipAddress) {
+ final StringBuffer sb = new StringBuffer();
+ int result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result = ipAddress >> (3 - i) * 8 & 0xff;
+ sb.append(result);
+ if (i != 3) {
+ sb.append(".");
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Accepts a collection of IPv4 addresses as integers and returns a single
+ * String useful in toString method's containing collections of IP
+ * addresses.
+ *
+ * @param ipAddresses
+ * collection
+ * @return ip addresses in comma-separated string form
+ */
+ public static String fromIPv4AddressCollection(
+ final Collection<Integer> ipAddresses) {
+ if (ipAddresses == null) {
+ return "null";
+ }
+ final StringBuffer sb = new StringBuffer();
+ sb.append("[");
+ for (final Integer ip : ipAddresses) {
+ sb.append(IPv4.fromIPv4Address(ip));
+ sb.append(",");
+ }
+ sb.replace(sb.length() - 1, sb.length(), "]");
+ return sb.toString();
+ }
+
+ /**
+ * Accepts an IPv4 address of the form xxx.xxx.xxx.xxx, ie 192.168.0.1 and
+ * returns the corresponding byte array.
+ *
+ * @param ipAddress
+ * The IP address in the form xx.xxx.xxx.xxx.
+ * @return The IP address separated into bytes
+ */
+ public static byte[] toIPv4AddressBytes(final String ipAddress) {
+ final String[] octets = ipAddress.split("\\.");
+ if (octets.length != 4) {
+ throw new IllegalArgumentException("Specified IPv4 address must"
+ + "contain 4 sets of numerical digits separated by periods");
+ }
+
+ final byte[] result = new byte[4];
+ for (int i = 0; i < 4; ++i) {
+ result[i] = Integer.valueOf(octets[i]).byteValue();
+ }
+ return result;
+ }
+
+ /**
+ * Accepts an IPv4 address in the form of an integer and returns the
+ * corresponding byte array.
+ *
+ * @param ipAddress
+ * The IP address as an integer.
+ * @return The IP address separated into bytes.
+ */
+ public static byte[] toIPv4AddressBytes(final int ipAddress) {
+ return new byte[] {(byte) (ipAddress >>> 24),
+ (byte) (ipAddress >>> 16), (byte) (ipAddress >>> 8),
+ (byte) ipAddress};
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 2521;
+ int result = super.hashCode();
+ result = prime * result + this.checksum;
+ result = prime * result + this.destinationAddress;
+ result = prime * result + this.diffServ;
+ result = prime * result + this.flags;
+ result = prime * result + this.fragmentOffset;
+ result = prime * result + this.headerLength;
+ result = prime * result + this.identification;
+ result = prime * result + Arrays.hashCode(this.options);
+ result = prime * result + this.protocol;
+ result = prime * result + this.sourceAddress;
+ result = prime * result + this.totalLength;
+ result = prime * result + this.ttl;
+ 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 IPv4)) {
+ return false;
+ }
+ final IPv4 other = (IPv4) obj;
+ if (this.checksum != other.checksum) {
+ return false;
+ }
+ if (this.destinationAddress != other.destinationAddress) {
+ return false;
+ }
+ if (this.diffServ != other.diffServ) {
+ return false;
+ }
+ if (this.flags != other.flags) {
+ return false;
+ }
+ if (this.fragmentOffset != other.fragmentOffset) {
+ return false;
+ }
+ if (this.headerLength != other.headerLength) {
+ return false;
+ }
+ if (this.identification != other.identification) {
+ return false;
+ }
+ if (!Arrays.equals(this.options, other.options)) {
+ return false;
+ }
+ if (this.protocol != other.protocol) {
+ return false;
+ }
+ if (this.sourceAddress != other.sourceAddress) {
+ return false;
+ }
+ if (this.totalLength != other.totalLength) {
+ return false;
+ }
+ if (this.ttl != other.ttl) {
+ return false;
+ }
+ if (this.version != other.version) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for IPv4 packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<IPv4> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ IPv4 ipv4 = new IPv4();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ byte versionByte = bb.get();
+ ipv4.headerLength = (byte) (versionByte & 0xf);
+ ipv4.setVersion((byte) (versionByte >> 4 & 0xf));
+ ipv4.setDiffServ(bb.get());
+ ipv4.totalLength = bb.getShort();
+ ipv4.identification = bb.getShort();
+ short flagsFragment = bb.getShort();
+ ipv4.flags = (byte) (flagsFragment >> 13 & 0x7);
+ ipv4.fragmentOffset = (short) (flagsFragment & 0x1fff);
+ ipv4.ttl = bb.get();
+ ipv4.protocol = bb.get();
+ ipv4.checksum = bb.getShort();
+ ipv4.sourceAddress = bb.getInt();
+ ipv4.destinationAddress = bb.getInt();
+
+ if (ipv4.headerLength > 5) {
+ checkHeaderLength(length, ipv4.headerLength * 4);
+
+ int optionsLength = (ipv4.headerLength - 5) * 4;
+ ipv4.options = new byte[optionsLength];
+ bb.get(ipv4.options);
+ }
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv4.PROTOCOL_DESERIALIZER_MAP.containsKey(ipv4.protocol)) {
+ deserializer = IPv4.PROTOCOL_DESERIALIZER_MAP.get(ipv4.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ ipv4.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ ipv4.payload.setParent(ipv4);
+
+ if (ipv4.totalLength != length) {
+ ipv4.isTruncated = true;
+ } else {
+ ipv4.isTruncated = false;
+ }
+
+ return ipv4;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv6.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv6.java
new file mode 100644
index 00000000..2e59632a
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IPv6.java
@@ -0,0 +1,384 @@
+/*
+ * 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<Byte, Deserializer<? extends IPacket>> 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<? extends IPacket> 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<IPv6> 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<? extends IPacket> 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;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
new file mode 100644
index 00000000..114f126c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Address.java
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+package org.onlab.packet;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import com.google.common.net.InetAddresses;
+
+/**
+ * A class representing an IPv4 address.
+ * This class is immutable.
+ */
+public final class Ip4Address extends IpAddress {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET;
+ public static final int BYTE_LENGTH = IpAddress.INET_BYTE_LENGTH;
+ public static final int BIT_LENGTH = IpAddress.INET_BIT_LENGTH;
+
+ /**
+ * Constructor for given IP address version and address octets.
+ *
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ private Ip4Address(byte[] value) {
+ super(VERSION, value);
+ }
+
+ /**
+ * Returns the integer value of this IPv4 address.
+ *
+ * @return the IPv4 address's value as an integer
+ */
+ public int toInt() {
+ ByteBuffer bb = ByteBuffer.wrap(super.toOctets());
+ return bb.getInt();
+ }
+
+ /**
+ * Converts an integer into an IPv4 address.
+ *
+ * @param value an integer representing an IPv4 address value
+ * @return an IPv4 address
+ */
+ public static Ip4Address valueOf(int value) {
+ byte[] bytes =
+ ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
+ return new Ip4Address(bytes);
+ }
+
+ /**
+ * Converts a byte array into an IPv4 address.
+ *
+ * @param value the IPv4 address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip4Address valueOf(byte[] value) {
+ return new Ip4Address(value);
+ }
+
+ /**
+ * Converts a byte array and a given offset from the beginning of the
+ * array into an IPv4 address.
+ * <p>
+ * The IP address is stored in network byte order (i.e., the most
+ * significant byte first).
+ * </p>
+ * @param value the value to use
+ * @param offset the offset in bytes from the beginning of the byte array
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip4Address valueOf(byte[] value, int offset) {
+ IpAddress.checkArguments(VERSION, value, offset);
+ byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+ return Ip4Address.valueOf(bc);
+ }
+
+ /**
+ * Converts an InetAddress into an IPv4 address.
+ *
+ * @param inetAddress the InetAddress value to use. It must contain an IPv4
+ * address
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip4Address valueOf(InetAddress inetAddress) {
+ byte[] bytes = inetAddress.getAddress();
+ if (inetAddress instanceof Inet4Address) {
+ return new Ip4Address(bytes);
+ }
+ if ((inetAddress instanceof Inet6Address) ||
+ (bytes.length == INET6_BYTE_LENGTH)) {
+ final String msg = "Invalid IPv4 version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+ // Use the number of bytes as a hint
+ if (bytes.length == INET_BYTE_LENGTH) {
+ return new Ip4Address(bytes);
+ }
+ final String msg = "Unrecognized IP version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+
+ /**
+ * Converts an IPv4 string literal (e.g., "10.2.3.4") into an IP address.
+ *
+ * @param value an IPv4 address value in string form
+ * @return an IPv4 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip4Address valueOf(String value) {
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = InetAddresses.forString(value);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Invalid IP address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+ return valueOf(inetAddress);
+ }
+
+ /**
+ * Creates an IPv4 network mask prefix.
+ *
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32]
+ * @return a new IPv4 address that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip4Address makeMaskPrefix(int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(VERSION, prefixLength);
+ return new Ip4Address(mask);
+ }
+
+ /**
+ * Creates an IPv4 address by masking it with a network mask of given
+ * mask length.
+ *
+ * @param address the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32]
+ * @return a new IPv4 address that is masked with a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ public static Ip4Address makeMaskedAddress(final Ip4Address address,
+ int prefixLength) {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return Ip4Address.valueOf(net);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
new file mode 100644
index 00000000..fc442c3e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip4Prefix.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+/**
+ * The class representing an IPv4 network address.
+ * This class is immutable.
+ */
+public final class Ip4Prefix extends IpPrefix {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET;
+ // Maximum network mask length
+ public static final int MAX_MASK_LENGTH = IpPrefix.MAX_INET_MASK_LENGTH;
+
+ /**
+ * Constructor for given IPv4 address, and a prefix length.
+ *
+ * @param address the IPv4 address
+ * @param prefixLength the prefix length
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ private Ip4Prefix(Ip4Address address, int prefixLength) {
+ super(address, prefixLength);
+ }
+
+ /**
+ * Returns the IPv4 address value of the prefix.
+ *
+ * @return the IPv4 address value of the prefix
+ */
+ public Ip4Address address() {
+ IpAddress a = super.address();
+ return (Ip4Address) a;
+ }
+
+ /**
+ * Converts an integer and a prefix length into an IPv4 prefix.
+ *
+ * @param address an integer representing the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip4Prefix valueOf(int address, int prefixLength) {
+ return new Ip4Prefix(Ip4Address.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts a byte array and a prefix length into an IPv4 prefix.
+ *
+ * @param address the IPv4 address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip4Prefix valueOf(byte[] address, int prefixLength) {
+ return new Ip4Prefix(Ip4Address.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts an IPv4 address and a prefix length into an IPv4 prefix.
+ *
+ * @param address the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip4Prefix valueOf(Ip4Address address, int prefixLength) {
+ return new Ip4Prefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16")
+ * into an IPv4 prefix.
+ *
+ * @param address an IP prefix in string form (e.g., "10.1.0.0/16")
+ * @return an IPv4 prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip4Prefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IPv4 prefix string: " + address + ". " +
+ "Address must take form \"x.x.x.x/y\"";
+ throw new IllegalArgumentException(msg);
+ }
+ Ip4Address ipAddress = Ip4Address.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
+
+ return new Ip4Prefix(ipAddress, prefixLength);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
new file mode 100644
index 00000000..d353422b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+package org.onlab.packet;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.util.Arrays;
+
+import com.google.common.net.InetAddresses;
+
+/**
+ * A class representing an IPv6 address.
+ * This class is immutable.
+ */
+public final class Ip6Address extends IpAddress {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET6;
+ public static final int BYTE_LENGTH = IpAddress.INET6_BYTE_LENGTH;
+ public static final int BIT_LENGTH = IpAddress.INET6_BIT_LENGTH;
+
+ /**
+ * Constructor for given IP address version and address octets.
+ *
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ private Ip6Address(byte[] value) {
+ super(VERSION, value);
+ }
+
+ /**
+ * Converts a byte array into an IPv6 address.
+ *
+ * @param value the IPv6 address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip6Address valueOf(byte[] value) {
+ return new Ip6Address(value);
+ }
+
+ /**
+ * Converts a byte array and a given offset from the beginning of the
+ * array into an IPv6 address.
+ * <p>
+ * The IP address is stored in network byte order (i.e., the most
+ * significant byte first).
+ * </p>
+ * @param value the value to use
+ * @param offset the offset in bytes from the beginning of the byte array
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip6Address valueOf(byte[] value, int offset) {
+ IpAddress.checkArguments(VERSION, value, offset);
+ byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+ return Ip6Address.valueOf(bc);
+ }
+
+ /**
+ * Converts an InetAddress into an IPv6 address.
+ *
+ * @param inetAddress the InetAddress value to use. It must contain an IPv6
+ * address
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip6Address valueOf(InetAddress inetAddress) {
+ byte[] bytes = inetAddress.getAddress();
+ if (inetAddress instanceof Inet6Address) {
+ return new Ip6Address(bytes);
+ }
+ if ((inetAddress instanceof Inet4Address) ||
+ (bytes.length == INET_BYTE_LENGTH)) {
+ final String msg = "Invalid IPv6 version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+ // Use the number of bytes as a hint
+ if (bytes.length == INET6_BYTE_LENGTH) {
+ return new Ip6Address(bytes);
+ }
+ final String msg = "Unrecognized IP version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+
+ /**
+ * Converts an IPv6 string literal (e.g., "1111:2222::8888") into an IP
+ * address.
+ *
+ * @param value an IPv6 address value in string form
+ * @return an IPv6 address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static Ip6Address valueOf(String value) {
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = InetAddresses.forString(value);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Invalid IP address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+ return valueOf(inetAddress);
+ }
+
+ /**
+ * Creates an IPv6 network mask prefix.
+ *
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 128]
+ * @return a new IPv6 address that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip6Address makeMaskPrefix(int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(VERSION, prefixLength);
+ return new Ip6Address(mask);
+ }
+
+ /**
+ * Creates an IPv6 address by masking it with a network mask of given
+ * mask length.
+ *
+ * @param address the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 128]
+ * @return a new IPv6 address that is masked with a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ public static Ip6Address makeMaskedAddress(final Ip6Address address,
+ int prefixLength) {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return Ip6Address.valueOf(net);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
new file mode 100644
index 00000000..4ea9099b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/Ip6Prefix.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+/**
+ * The class representing an IPv6 network address.
+ * This class is immutable.
+ */
+public final class Ip6Prefix extends IpPrefix {
+ public static final IpAddress.Version VERSION = IpAddress.Version.INET6;
+ // Maximum network mask length
+ public static final int MAX_MASK_LENGTH = IpPrefix.MAX_INET6_MASK_LENGTH;
+
+ /**
+ * Constructor for given IPv6 address, and a prefix length.
+ *
+ * @param address the IPv6 address
+ * @param prefixLength the prefix length
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ private Ip6Prefix(Ip6Address address, int prefixLength) {
+ super(address, prefixLength);
+ }
+
+ /**
+ * Returns the IPv6 address value of the prefix.
+ *
+ * @return the IPv6 address value of the prefix
+ */
+ public Ip6Address address() {
+ IpAddress a = super.address();
+ return (Ip6Address) a;
+ }
+
+ /**
+ * Converts a byte array and a prefix length into an IPv6 prefix.
+ *
+ * @param address the IPv6 address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip6Prefix valueOf(byte[] address, int prefixLength) {
+ return new Ip6Prefix(Ip6Address.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts an IPv6 address and a prefix length into an IPv6 prefix.
+ *
+ * @param address the IPv6 address
+ * @param prefixLength the prefix length
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static Ip6Prefix valueOf(Ip6Address address, int prefixLength) {
+ return new Ip6Prefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "1111:2222::/64")
+ * into an IPv6 prefix.
+ *
+ * @param address an IP prefix in string form (e.g.,"1111:2222::/64")
+ * @return an IPv6 prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static Ip6Prefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IPv6 prefix string: " + address + ". " +
+ "Address must take form " +
+ "\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
+ throw new IllegalArgumentException(msg);
+ }
+ Ip6Address ipAddress = Ip6Address.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
+
+ return new Ip6Prefix(ipAddress, prefixLength);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpAddress.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
new file mode 100644
index 00000000..5fdd3276
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpAddress.java
@@ -0,0 +1,559 @@
+/*
+ * 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 java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Objects;
+import com.google.common.net.InetAddresses;
+import com.google.common.primitives.UnsignedBytes;
+
+
+/**
+ * A class representing an IP address.
+ * This class is immutable.
+ */
+public class IpAddress implements Comparable<IpAddress> {
+ private static final int BIT_MASK = 0x000000ff;
+
+ // IP Versions
+ public enum Version { INET, INET6 };
+
+ // lengths of address, in bytes
+ public static final int INET_BYTE_LENGTH = 4;
+ public static final int INET_BIT_LENGTH = INET_BYTE_LENGTH * Byte.SIZE;
+ public static final int INET6_BYTE_LENGTH = 16;
+ public static final int INET6_BIT_LENGTH = INET6_BYTE_LENGTH * Byte.SIZE;
+
+ private final Version version;
+ private final byte[] octets;
+
+ /**
+ * Constructor for given IP address version and address octets.
+ *
+ * @param version the IP address version
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ protected IpAddress(Version version, byte[] value) {
+ checkArguments(version, value, 0);
+ this.version = version;
+ switch (version) {
+ case INET:
+ this.octets = Arrays.copyOf(value, INET_BYTE_LENGTH);
+ break;
+ case INET6:
+ this.octets = Arrays.copyOf(value, INET6_BYTE_LENGTH);
+ break;
+ default:
+ // Should not be reached
+ this.octets = null;
+ break;
+ }
+ }
+
+ /**
+ * Returns the IP version of this address.
+ *
+ * @return the version
+ */
+ public Version version() {
+ return this.version;
+ }
+
+ /**
+ * Tests whether the IP version of this address is IPv4.
+ *
+ * @return true if the IP version of this address is IPv4, otherwise false.
+ */
+ public boolean isIp4() {
+ return (version() == Ip4Address.VERSION);
+ }
+
+ /**
+ * Tests whether the IP version of this address is IPv6.
+ *
+ * @return true if the IP version of this address is IPv6, otherwise false.
+ */
+ public boolean isIp6() {
+ return (version() == Ip6Address.VERSION);
+ }
+
+ /**
+ * Gets the {@link Ip4Address} view of the IP address.
+ *
+ * @return the {@link Ip4Address} view of the IP address if it is IPv4,
+ * otherwise null
+ */
+ public Ip4Address getIp4Address() {
+ if (!isIp4()) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip4Address
+ if (this instanceof Ip4Address) {
+ return (Ip4Address) this;
+ }
+ return Ip4Address.valueOf(octets);
+ }
+
+ /**
+ * Gets the {@link Ip6Address} view of the IP address.
+ *
+ * @return the {@link Ip6Address} view of the IP address if it is IPv6,
+ * otherwise null
+ */
+ public Ip6Address getIp6Address() {
+ if (!isIp6()) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip6Address
+ if (this instanceof Ip6Address) {
+ return (Ip6Address) this;
+ }
+ return Ip6Address.valueOf(octets);
+ }
+
+ /**
+ * Returns the IP address as a byte array.
+ *
+ * @return a byte array
+ */
+ public byte[] toOctets() {
+ return Arrays.copyOf(octets, octets.length);
+ }
+
+ /**
+ * Computes the IP address byte length for a given IP version.
+ *
+ * @param version the IP version
+ * @return the IP address byte length for the IP version
+ * @throws IllegalArgumentException if the IP version is invalid
+ */
+ public static int byteLength(Version version) {
+ switch (version) {
+ case INET:
+ return INET_BYTE_LENGTH;
+ case INET6:
+ return INET6_BYTE_LENGTH;
+ default:
+ String msg = "Invalid IP version " + version;
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ /**
+ * Converts an integer into an IPv4 address.
+ *
+ * @param value an integer representing an IPv4 address value
+ * @return an IP address
+ */
+ public static IpAddress valueOf(int value) {
+ byte[] bytes =
+ ByteBuffer.allocate(INET_BYTE_LENGTH).putInt(value).array();
+ return new IpAddress(Version.INET, bytes);
+ }
+
+ /**
+ * Converts a byte array into an IP address.
+ *
+ * @param version the IP address version
+ * @param value the IP address value stored in network byte order
+ * (i.e., the most significant byte first)
+ * @return an IP address
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpAddress valueOf(Version version, byte[] value) {
+ return new IpAddress(version, value);
+ }
+
+ /**
+ * Converts a byte array and a given offset from the beginning of the
+ * array into an IP address.
+ * <p>
+ * The IP address is stored in network byte order (i.e., the most
+ * significant byte first).
+ * </p>
+ * @param version the IP address version
+ * @param value the value to use
+ * @param offset the offset in bytes from the beginning of the byte array
+ * @return an IP address
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpAddress valueOf(Version version, byte[] value,
+ int offset) {
+ checkArguments(version, value, offset);
+ byte[] bc = Arrays.copyOfRange(value, offset, value.length);
+ return IpAddress.valueOf(version, bc);
+ }
+
+ /**
+ * Converts an InetAddress into an IP address.
+ *
+ * @param inetAddress the InetAddress value to use
+ * @return an IP address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static IpAddress valueOf(InetAddress inetAddress) {
+ byte[] bytes = inetAddress.getAddress();
+ if (inetAddress instanceof Inet4Address) {
+ return new IpAddress(Version.INET, bytes);
+ }
+ if (inetAddress instanceof Inet6Address) {
+ return new IpAddress(Version.INET6, bytes);
+ }
+ // Use the number of bytes as a hint
+ if (bytes.length == INET_BYTE_LENGTH) {
+ return new IpAddress(Version.INET, bytes);
+ }
+ if (bytes.length == INET6_BYTE_LENGTH) {
+ return new IpAddress(Version.INET6, bytes);
+ }
+ final String msg = "Unrecognized IP version address string: " +
+ inetAddress.toString();
+ throw new IllegalArgumentException(msg);
+ }
+
+ /**
+ * Converts an IPv4 or IPv6 string literal (e.g., "10.2.3.4" or
+ * "1111:2222::8888") into an IP address.
+ *
+ * @param value an IP address value in string form
+ * @return an IP address
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public static IpAddress valueOf(String value) {
+ InetAddress inetAddress = null;
+ try {
+ inetAddress = InetAddresses.forString(value);
+ } catch (IllegalArgumentException e) {
+ final String msg = "Invalid IP address string: " + value;
+ throw new IllegalArgumentException(msg);
+ }
+ return valueOf(inetAddress);
+ }
+
+ /**
+ * Creates an IP network mask prefix.
+ *
+ * @param version the IP address version
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a new IP address that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpAddress makeMaskPrefix(Version version, int prefixLength) {
+ byte[] mask = makeMaskPrefixArray(version, prefixLength);
+ return new IpAddress(version, mask);
+ }
+
+ /**
+ * Creates an IP address by masking it with a network mask of given
+ * mask length.
+ *
+ * @param address the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a new IP address that is masked with a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ public static IpAddress makeMaskedAddress(final IpAddress address,
+ int prefixLength) {
+ if (address instanceof Ip4Address) {
+ Ip4Address ip4a = (Ip4Address) address;
+ return Ip4Address.makeMaskedAddress(ip4a, prefixLength);
+ } else if (address instanceof Ip6Address) {
+ Ip6Address ip6a = (Ip6Address) address;
+ return Ip6Address.makeMaskedAddress(ip6a, prefixLength);
+ } else {
+ byte[] net = makeMaskedAddressArray(address, prefixLength);
+ return IpAddress.valueOf(address.version(), net);
+ }
+ }
+
+ /**
+ * Check if this IP address is zero.
+ *
+ * @return true if this address is zero
+ */
+ public boolean isZero() {
+ for (byte b : octets) {
+ if (b != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check if this IP address is self-assigned.
+ *
+ * @return true if this address is self-assigned
+ */
+ public boolean isSelfAssigned() {
+ return isIp4() && octets[0] == (byte) 169;
+ }
+
+ @Override
+ public int compareTo(IpAddress o) {
+ // Compare first the version
+ if (this.version != o.version) {
+ return this.version.compareTo(o.version);
+ }
+
+ // Compare the bytes, one-by-one
+ for (int i = 0; i < this.octets.length; i++) {
+ if (this.octets[i] != o.octets[i]) {
+ return UnsignedBytes.compare(this.octets[i], o.octets[i]);
+ }
+ }
+ return 0; // Equal
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(version, Arrays.hashCode(octets));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if ((obj == null) || (!(obj instanceof IpAddress))) {
+ return false;
+ }
+ IpAddress other = (IpAddress) obj;
+ return (version == other.version) &&
+ Arrays.equals(octets, other.octets);
+ }
+
+ @Override
+ /*
+ * (non-Javadoc)
+ * The string representation of the IP address: "x.x.x.x" for IPv4
+ * addresses, or ':' separated string for IPv6 addresses.
+ *
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ // FIXME InetAddress is super slow
+ switch (version) {
+ case INET:
+ return String.format("%d.%d.%d.%d", octets[0] & 0xff,
+ octets[1] & 0xff,
+ octets[2] & 0xff,
+ octets[3] & 0xff);
+ case INET6:
+ default:
+ return ipv6ToStringHelper();
+ }
+ }
+
+ /**
+ * Generates an IP prefix.
+ *
+ * @return the IP prefix of the IP address
+ */
+ public IpPrefix toIpPrefix() {
+
+ if (isIp4()) {
+ return IpPrefix.valueOf(new IpAddress(Version.INET, octets),
+ Ip4Address.BIT_LENGTH);
+ } else {
+ return IpPrefix.valueOf(new IpAddress(Version.INET6, octets),
+ Ip6Address.BIT_LENGTH);
+ }
+ }
+
+ /**
+ * Gets the IP address name for the IP address version.
+ *
+ * @param version the IP address version
+ * @return the IP address name for the IP address version
+ */
+ private static String addressName(Version version) {
+ switch (version) {
+ case INET:
+ return "IPv4";
+ case INET6:
+ return "IPv6";
+ default:
+ break;
+ }
+ return "UnknownIP(" + version + ")";
+ }
+
+ /**
+ * Checks whether the arguments are valid.
+ *
+ * @param version the IP address version
+ * @param value the IP address value stored in a byte array
+ * @param offset the offset in bytes from the beginning of the byte
+ * array with the address
+ * @throws IllegalArgumentException if any of the arguments is invalid
+ */
+ static void checkArguments(Version version, byte[] value, int offset) {
+ // Check the offset and byte array length
+ int addrByteLength = byteLength(version);
+ if ((offset < 0) || (offset + addrByteLength > value.length)) {
+ String msg;
+ if (value.length < addrByteLength) {
+ msg = "Invalid " + addressName(version) +
+ " address array: array length: " + value.length +
+ ". Must be at least " + addrByteLength;
+ } else {
+ msg = "Invalid " + addressName(version) +
+ " address array: array offset: " + offset +
+ ". Must be in the interval [0, " +
+ (value.length - addrByteLength) + "]";
+ }
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ /**
+ * Creates a byte array for IP network mask prefix.
+ *
+ * @param version the IP address version
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a byte array that contains a mask prefix of the
+ * specified length
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ static byte[] makeMaskPrefixArray(Version version, int prefixLength) {
+ int addrByteLength = byteLength(version);
+ int addrBitLength = addrByteLength * Byte.SIZE;
+
+ // Verify the prefix length
+ if ((prefixLength < 0) || (prefixLength > addrBitLength)) {
+ final String msg = "Invalid IP prefix length: " + prefixLength +
+ ". Must be in the interval [0, " + addrBitLength + "].";
+ throw new IllegalArgumentException(msg);
+ }
+
+ // Number of bytes and extra bits that should be all 1s
+ int maskBytes = prefixLength / Byte.SIZE;
+ int maskBits = prefixLength % Byte.SIZE;
+ byte[] mask = new byte[addrByteLength];
+
+ // Set the bytes and extra bits to 1s
+ for (int i = 0; i < maskBytes; i++) {
+ mask[i] = (byte) 0xff; // Set mask bytes to 1s
+ }
+ for (int i = maskBytes; i < addrByteLength; i++) {
+ mask[i] = 0; // Set remaining bytes to 0s
+ }
+ if (maskBits > 0) {
+ mask[maskBytes] = (byte) (0xff << (Byte.SIZE - maskBits));
+ }
+ return mask;
+ }
+
+ /**
+ * Creates a byte array that represents an IP address masked with
+ * a network mask of given mask length.
+ *
+ * @param addr the address to mask
+ * @param prefixLength the length of the mask prefix. Must be in the
+ * interval [0, 32] for IPv4, or [0, 128] for IPv6
+ * @return a byte array that represents the IP address masked with
+ * a mask prefix of the specified length
+ * @throws IllegalArgumentException if the prefix length is invalid
+ */
+ static byte[] makeMaskedAddressArray(final IpAddress addr,
+ int prefixLength) {
+ byte[] mask = IpAddress.makeMaskPrefixArray(addr.version(),
+ prefixLength);
+ byte[] net = new byte[mask.length];
+
+ // Mask each byte
+ for (int i = 0; i < net.length; i++) {
+ net[i] = (byte) (addr.octets[i] & mask[i]);
+ }
+ return net;
+ }
+
+ /**
+ * Creates a string based on the IPv6 recommendations for canonical representations found here:
+ * https://tools.ietf.org/html/rfc5952#section-1.
+ * @return A properly formatted IPv6 canonical representation.
+ */
+ private String ipv6ToStringHelper() {
+ //Populate a buffer with the string of the full address with leading zeros stripped
+ StringBuffer buff = new StringBuffer();
+ buff.append(String.format("%x:%x:%x:%x:%x:%x:%x:%x",
+ (((octets[0] & BIT_MASK) << 8) | (octets[1] & BIT_MASK)),
+ (((octets[2] & BIT_MASK) << 8) | (octets[3] & BIT_MASK)),
+ (((octets[4] & BIT_MASK) << 8) | (octets[5] & BIT_MASK)),
+ (((octets[6] & BIT_MASK) << 8) | (octets[7] & BIT_MASK)),
+ (((octets[8] & BIT_MASK) << 8) | (octets[9] & BIT_MASK)),
+ (((octets[10] & BIT_MASK) << 8) | (octets[11] & BIT_MASK)),
+ (((octets[12] & BIT_MASK) << 8) | (octets[13] & BIT_MASK)),
+ (((octets[14] & BIT_MASK) << 8) | (octets[15] & BIT_MASK))));
+ //Initialize variables for tracking longest zero subsequence, tiebreaking by first occurence
+ int longestSeqStart, longestSeqLen, currSeqStart, currSeqLen;
+ longestSeqStart = 0;
+ longestSeqLen = 0;
+ currSeqStart = 0;
+ currSeqLen = 0;
+
+ for (int index = 0; index < buff.length(); index++) {
+ if (buff.charAt(index) == ':') {
+ if (currSeqLen != 0 && buff.charAt(index + 1) == '0') {
+ currSeqLen += 1;
+ }
+ } else if (buff.charAt(index) == '0' && ((index == 0) || (buff.charAt(index - 1) == ':'))) {
+ if (currSeqLen == 0) {
+ currSeqStart = index;
+ }
+ currSeqLen += 1;
+ } else {
+ if (currSeqLen > longestSeqLen) {
+ longestSeqStart = currSeqStart;
+ longestSeqLen = currSeqLen;
+ }
+ currSeqLen = 0;
+ }
+ }
+
+ if (currSeqLen > longestSeqLen) {
+ longestSeqLen = currSeqLen;
+ longestSeqStart = currSeqStart;
+ }
+ if (longestSeqLen > 1) {
+ if (buff.length() == (longestSeqStart + longestSeqLen)) {
+ buff.append(':');
+ }
+
+ buff.delete(longestSeqStart, longestSeqStart + longestSeqLen);
+
+ if (longestSeqStart == 0) {
+ buff.insert(0, ':');
+ }
+ }
+
+ return buff.toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
new file mode 100644
index 00000000..14d07fed
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IpPrefix.java
@@ -0,0 +1,303 @@
+/*
+ * 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 java.util.Objects;
+
+/**
+ * A class representing an IP prefix. A prefix consists of an IP address and
+ * a subnet mask.
+ * This class is immutable.
+ * <p>
+ * NOTE: The stored IP address in the result IP prefix is masked to
+ * contain zeroes in all bits after the prefix length.
+ * </p>
+ */
+public class IpPrefix {
+ // Maximum network mask length
+ public static final int MAX_INET_MASK_LENGTH = IpAddress.INET_BIT_LENGTH;
+ public static final int MAX_INET6_MASK_LENGTH = IpAddress.INET6_BIT_LENGTH;
+
+ private final IpAddress address;
+ private final short prefixLength;
+
+ /**
+ * Constructor for given IP address, and a prefix length.
+ *
+ * @param address the IP address
+ * @param prefixLength the prefix length
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ protected IpPrefix(IpAddress address, int prefixLength) {
+ checkPrefixLength(address.version(), prefixLength);
+ this.address = IpAddress.makeMaskedAddress(address, prefixLength);
+ this.prefixLength = (short) prefixLength;
+ }
+
+ /**
+ * Returns the IP version of the prefix.
+ *
+ * @return the IP version of the prefix
+ */
+ public IpAddress.Version version() {
+ return address.version();
+ }
+
+ /**
+ * Tests whether the IP version of this prefix is IPv4.
+ *
+ * @return true if the IP version of this prefix is IPv4, otherwise false.
+ */
+ public boolean isIp4() {
+ return address.isIp4();
+ }
+
+ /**
+ * Tests whether the IP version of this prefix is IPv6.
+ *
+ * @return true if the IP version of this prefix is IPv6, otherwise false.
+ */
+ public boolean isIp6() {
+ return address.isIp6();
+ }
+
+ /**
+ * Returns the IP address value of the prefix.
+ *
+ * @return the IP address value of the prefix
+ */
+ public IpAddress address() {
+ return address;
+ }
+
+ /**
+ * Returns the IP address prefix length.
+ *
+ * @return the IP address prefix length
+ */
+ public int prefixLength() {
+ return prefixLength;
+ }
+
+ /**
+ * Gets the {@link Ip4Prefix} view of the IP prefix.
+ *
+ * @return the {@link Ip4Prefix} view of the IP prefix if it is IPv4,
+ * otherwise null
+ */
+ public Ip4Prefix getIp4Prefix() {
+ if (!isIp4()) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip4Prefix
+ if (this instanceof Ip4Prefix) {
+ return (Ip4Prefix) this;
+ }
+ return Ip4Prefix.valueOf(address.getIp4Address(), prefixLength);
+ }
+
+ /**
+ * Gets the {@link Ip6Prefix} view of the IP prefix.
+ *
+ * @return the {@link Ip6Prefix} view of the IP prefix if it is IPv6,
+ * otherwise null
+ */
+ public Ip6Prefix getIp6Prefix() {
+ if (!isIp6()) {
+ return null;
+ }
+
+ // Return this object itself if it is already instance of Ip6Prefix
+ if (this instanceof Ip6Prefix) {
+ return (Ip6Prefix) this;
+ }
+ return Ip6Prefix.valueOf(address.getIp6Address(), prefixLength);
+ }
+
+ /**
+ * Converts an integer and a prefix length into an IPv4 prefix.
+ *
+ * @param address an integer representing the IPv4 address
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(int address, int prefixLength) {
+ return new IpPrefix(IpAddress.valueOf(address), prefixLength);
+ }
+
+ /**
+ * Converts a byte array and a prefix length into an IP prefix.
+ *
+ * @param version the IP address version
+ * @param address the IP address value stored in network byte order
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(IpAddress.Version version, byte[] address,
+ int prefixLength) {
+ return new IpPrefix(IpAddress.valueOf(version, address), prefixLength);
+ }
+
+ /**
+ * Converts an IP address and a prefix length into an IP prefix.
+ *
+ * @param address the IP address
+ * @param prefixLength the prefix length
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ public static IpPrefix valueOf(IpAddress address, int prefixLength) {
+ return new IpPrefix(address, prefixLength);
+ }
+
+ /**
+ * Converts a CIDR (slash) notation string (e.g., "10.1.0.0/16" or
+ * "1111:2222::/64") into an IP prefix.
+ *
+ * @param address an IP prefix in string form (e.g. "10.1.0.0/16" or
+ * "1111:2222::/64")
+ * @return an IP prefix
+ * @throws IllegalArgumentException if the arguments are invalid
+ */
+ public static IpPrefix valueOf(String address) {
+ final String[] parts = address.split("/");
+ if (parts.length != 2) {
+ String msg = "Malformed IP prefix string: " + address + ". " +
+ "Address must take form \"x.x.x.x/y\" or " +
+ "\"xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/y\"";
+ throw new IllegalArgumentException(msg);
+ }
+ IpAddress ipAddress = IpAddress.valueOf(parts[0]);
+ int prefixLength = Integer.parseInt(parts[1]);
+
+ return new IpPrefix(ipAddress, prefixLength);
+ }
+
+ /**
+ * Determines whether a given IP prefix is contained within this prefix.
+ *
+ * @param other the IP prefix to test
+ * @return true if the other IP prefix is contained in this prefix,
+ * otherwise false
+ */
+ public boolean contains(IpPrefix other) {
+ if (version() != other.version()) {
+ return false;
+ }
+
+ if (this.prefixLength > other.prefixLength) {
+ return false; // This prefix has smaller prefix size
+ }
+
+ //
+ // Mask the other address with my prefix length.
+ // If the other prefix is within this prefix, the masked address must
+ // be same as the address of this prefix.
+ //
+ IpAddress maskedAddr =
+ IpAddress.makeMaskedAddress(other.address, this.prefixLength);
+ return this.address.equals(maskedAddr);
+ }
+
+ /**
+ * Determines whether a given IP address is contained within this prefix.
+ *
+ * @param other the IP address to test
+ * @return true if the IP address is contained in this prefix, otherwise
+ * false
+ */
+ public boolean contains(IpAddress other) {
+ if (version() != other.version()) {
+ return false;
+ }
+
+ //
+ // Mask the other address with my prefix length.
+ // If the other prefix is within this prefix, the masked address must
+ // be same as the address of this prefix.
+ //
+ IpAddress maskedAddr =
+ IpAddress.makeMaskedAddress(other, this.prefixLength);
+ return this.address.equals(maskedAddr);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, prefixLength);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if ((obj == null) || (!(obj instanceof IpPrefix))) {
+ return false;
+ }
+ IpPrefix other = (IpPrefix) obj;
+ return ((prefixLength == other.prefixLength) &&
+ address.equals(other.address));
+ }
+
+ @Override
+ /*
+ * (non-Javadoc)
+ * The format is "x.x.x.x/y" for IPv4 prefixes.
+ *
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(address.toString());
+ builder.append("/");
+ builder.append(String.format("%d", prefixLength));
+ return builder.toString();
+ }
+
+ /**
+ * Checks whether the prefix length is valid.
+ *
+ * @param version the IP address version
+ * @param prefixLength the prefix length value to check
+ * @throws IllegalArgumentException if the prefix length value is invalid
+ */
+ private static void checkPrefixLength(IpAddress.Version version,
+ int prefixLength) {
+ int maxPrefixLen = 0;
+
+ switch (version) {
+ case INET:
+ maxPrefixLen = MAX_INET_MASK_LENGTH;
+ break;
+ case INET6:
+ maxPrefixLen = MAX_INET6_MASK_LENGTH;
+ break;
+ default:
+ String msg = "Invalid IP version " + version;
+ throw new IllegalArgumentException(msg);
+ }
+
+ if ((prefixLength < 0) || (prefixLength > maxPrefixLen)) {
+ String msg = "Invalid prefix length " + prefixLength + ". " +
+ "The value must be in the interval [0, " +
+ maxPrefixLen + "]";
+ throw new IllegalArgumentException(msg);
+ }
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLC.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLC.java
new file mode 100644
index 00000000..78b4f3fa
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLC.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ * This class represents an Link Local Control header that is used in Ethernet
+ * 802.3.
+ *
+ *
+ */
+public class LLC extends BasePacket {
+
+ public static final byte LLC_HEADER_LENGTH = 3;
+
+ private byte dsap = 0;
+ private byte ssap = 0;
+ private byte ctrl = 0;
+
+ public byte getDsap() {
+ return this.dsap;
+ }
+
+ public void setDsap(final byte dsap) {
+ this.dsap = dsap;
+ }
+
+ public byte getSsap() {
+ return this.ssap;
+ }
+
+ public void setSsap(final byte ssap) {
+ this.ssap = ssap;
+ }
+
+ public byte getCtrl() {
+ return this.ctrl;
+ }
+
+ public void setCtrl(final byte ctrl) {
+ this.ctrl = ctrl;
+ }
+
+ @Override
+ public byte[] serialize() {
+ final byte[] data = new byte[3];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.dsap);
+ bb.put(this.ssap);
+ bb.put(this.ctrl);
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.dsap = bb.get();
+ this.ssap = bb.get();
+ this.ctrl = bb.get();
+ return this;
+ }
+
+ /**
+ * Deserializer function for LLC packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<LLC> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, LLC_HEADER_LENGTH);
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ LLC llc = new LLC();
+
+ llc.dsap = bb.get();
+ llc.ssap = bb.get();
+ llc.ctrl = bb.get();
+
+ return llc;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDP.java
new file mode 100644
index 00000000..ae9d7173
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDP.java
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+/**
+ *
+ */
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ *
+ */
+public class LLDP extends BasePacket {
+ public static final byte CHASSIS_TLV_TYPE = 1;
+ public static final short CHASSIS_TLV_SIZE = 7;
+ public static final byte CHASSIS_TLV_SUBTYPE = 4;
+
+ public static final byte PORT_TLV_TYPE = 2;
+ public static final short PORT_TLV_SIZE = 5;
+ public static final byte PORT_TLV_SUBTYPE = 2;
+
+ public static final byte TTL_TLV_TYPE = 3;
+ public static final short TTL_TLV_SIZE = 2;
+
+ protected LLDPTLV chassisId;
+ protected LLDPTLV portId;
+ protected LLDPTLV ttl;
+ protected List<LLDPTLV> optionalTLVList;
+ protected short ethType;
+
+ public LLDP() {
+ this.optionalTLVList = new LinkedList<>();
+ this.ethType = Ethernet.TYPE_LLDP;
+ }
+
+ /**
+ * @return the chassisId
+ */
+ public LLDPTLV getChassisId() {
+ return this.chassisId;
+ }
+
+ /**
+ * @param chassis
+ * the chassisId to set
+ * @return this
+ */
+ public LLDP setChassisId(final LLDPTLV chassis) {
+ this.chassisId = chassis;
+ return this;
+ }
+
+ /**
+ * @return the portId
+ */
+ public LLDPTLV getPortId() {
+ return this.portId;
+ }
+
+ /**
+ * @param portId
+ * the portId to set
+ * @return this
+ */
+ public LLDP setPortId(final LLDPTLV portId) {
+ this.portId = portId;
+ return this;
+ }
+
+ /**
+ * @return the ttl
+ */
+ public LLDPTLV getTtl() {
+ return this.ttl;
+ }
+
+ /**
+ * @param ttl
+ * the ttl to set
+ * @return this
+ */
+ public LLDP setTtl(final LLDPTLV ttl) {
+ this.ttl = ttl;
+ return this;
+ }
+
+ /**
+ * @return the optionalTLVList
+ */
+ public List<LLDPTLV> getOptionalTLVList() {
+ return this.optionalTLVList;
+ }
+
+ /**
+ * @param optionalTLVList
+ * the optionalTLVList to set
+ * @return this
+ */
+ public LLDP setOptionalTLVList(final List<LLDPTLV> optionalTLVList) {
+ this.optionalTLVList = optionalTLVList;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ int length = 2 + this.chassisId.getLength() + 2
+ + this.portId.getLength() + 2 + this.ttl.getLength() + 2;
+ for (final LLDPTLV tlv : this.optionalTLVList) {
+ length += 2 + tlv.getLength();
+ }
+
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.chassisId.serialize());
+ bb.put(this.portId.serialize());
+ bb.put(this.ttl.serialize());
+ for (final LLDPTLV tlv : this.optionalTLVList) {
+ bb.put(tlv.serialize());
+ }
+ bb.putShort((short) 0); // End of LLDPDU
+
+ /*
+ * if (this.parent != null && this.parent instanceof Ethernet) {
+ * ((Ethernet) this.parent).setEtherType(this.ethType); }
+ */
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ LLDPTLV tlv;
+ do {
+ try {
+ tlv = new LLDPOrganizationalTLV().deserialize(bb);
+ } catch (DeserializationException e) {
+ break;
+ }
+
+ // if there was a failure to deserialize stop processing TLVs
+ if (tlv == null) {
+ break;
+ }
+ switch (tlv.getType()) {
+ case 0x0:
+ // can throw this one away, its just an end delimiter
+ break;
+ case 0x1:
+ this.chassisId = tlv;
+ break;
+ case 0x2:
+ this.portId = tlv;
+ break;
+ case 0x3:
+ this.ttl = tlv;
+ break;
+
+ default:
+ this.optionalTLVList.add(tlv);
+ break;
+ }
+ } while (tlv.getType() != 0 && bb.hasRemaining());
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 883;
+ int result = super.hashCode();
+ result = prime * result
+ + (this.chassisId == null ? 0 : this.chassisId.hashCode());
+ result = prime * result + this.optionalTLVList.hashCode();
+ result = prime * result
+ + (this.portId == null ? 0 : this.portId.hashCode());
+ result = prime * result + (this.ttl == null ? 0 : this.ttl.hashCode());
+ 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 LLDP)) {
+ return false;
+ }
+ final LLDP other = (LLDP) obj;
+ if (this.chassisId == null) {
+ if (other.chassisId != null) {
+ return false;
+ }
+ } else if (!this.chassisId.equals(other.chassisId)) {
+ return false;
+ }
+ if (!this.optionalTLVList.equals(other.optionalTLVList)) {
+ return false;
+ }
+ if (this.portId == null) {
+ if (other.portId != null) {
+ return false;
+ }
+ } else if (!this.portId.equals(other.portId)) {
+ return false;
+ }
+ if (this.ttl == null) {
+ if (other.ttl != null) {
+ return false;
+ }
+ } else if (!this.ttl.equals(other.ttl)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for LLDP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<LLDP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, 0);
+
+ LLDP lldp = new LLDP();
+
+ int currentIndex = 0;
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ LLDPTLV tlv;
+ do {
+ // Each new TLV must be a minimum of 2 bytes
+ // (containing the type and length fields).
+ currentIndex += 2;
+ checkHeaderLength(length, currentIndex);
+
+ tlv = new LLDPOrganizationalTLV().deserialize(bb);
+
+ // if there was a failure to deserialize stop processing TLVs
+ if (tlv == null) {
+ break;
+ }
+ switch (tlv.getType()) {
+ case 0x0:
+ // can throw this one away, it's just an end delimiter
+ break;
+ case 0x1:
+ lldp.chassisId = tlv;
+ break;
+ case 0x2:
+ lldp.portId = tlv;
+ break;
+ case 0x3:
+ lldp.ttl = tlv;
+ break;
+ default:
+ lldp.optionalTLVList.add(tlv);
+ break;
+ }
+
+ currentIndex += tlv.getLength();
+ } while (tlv.getType() != 0);
+
+ return lldp;
+ };
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
new file mode 100644
index 00000000..bedf439f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ */
+/**
+ * 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 java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+/**
+ * The class representing LLDP Organizationally Specific TLV.
+ *
+ */
+public class LLDPOrganizationalTLV extends LLDPTLV {
+ public static final int OUI_LENGTH = 3;
+ public static final int SUBTYPE_LENGTH = 1;
+ public static final byte ORGANIZATIONAL_TLV_TYPE = 127;
+ public static final int MAX_INFOSTRING_LENGTH = 507;
+
+ protected byte[] oui;
+ protected byte subType;
+ private byte[] infoString;
+
+ public LLDPOrganizationalTLV() {
+ this.type = LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE;
+ }
+
+ /**
+ * Set the value of OUI.
+ *
+ * @param oui
+ * The value of OUI to be set.
+ * @return This LLDP Organizationally Specific TLV.
+ */
+ public LLDPOrganizationalTLV setOUI(final byte[] oui) {
+ if (oui.length != LLDPOrganizationalTLV.OUI_LENGTH) {
+ throw new IllegalArgumentException("The length of OUI must be "
+ + LLDPOrganizationalTLV.OUI_LENGTH + ", but it is "
+ + oui.length);
+ }
+ this.oui = Arrays.copyOf(oui, oui.length);
+ return this;
+ }
+
+ /**
+ * Returns the value of the OUI.
+ *
+ * @return The value of the OUI .
+ */
+ public byte[] getOUI() {
+ return Arrays.copyOf(this.oui, this.oui.length);
+ }
+
+ /**
+ * Set the value of sub type.
+ *
+ * @param subType
+ * The value of sub type to be set.
+ * @return This LLDP Organizationally Specific TLV.
+ */
+ public LLDPOrganizationalTLV setSubType(final byte subType) {
+ this.subType = subType;
+ return this;
+ }
+
+ /**
+ * Returns the value of the sub type.
+ *
+ * @return The value of the sub type.
+ */
+ public byte getSubType() {
+ return this.subType;
+ }
+
+ /**
+ * Set the value of information string.
+ *
+ * @param infoString
+ * the byte array of the value of information string.
+ * @return This LLDP Organizationally Specific TLV.
+ */
+ public LLDPOrganizationalTLV setInfoString(final byte[] infoString) {
+ if (infoString.length > LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH) {
+ throw new IllegalArgumentException(
+ "The length of infoString cannot exceed "
+ + LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH);
+ }
+ this.infoString = Arrays.copyOf(infoString, infoString.length);
+ return this;
+ }
+
+ /**
+ * Set the value of information string. The String value is automatically
+ * converted into byte array with UTF-8 encoding.
+ *
+ * @param infoString
+ * the String value of information string.
+ * @return This LLDP Organizationally Specific TLV.
+ */
+ public LLDPOrganizationalTLV setInfoString(final String infoString) {
+ final byte[] infoStringBytes = infoString.getBytes(Charset
+ .forName("UTF-8"));
+ return this.setInfoString(infoStringBytes);
+ }
+
+ /**
+ * Returns the value of information string.
+ *
+ * @return the value of information string.
+ */
+ public byte[] getInfoString() {
+ return Arrays.copyOf(this.infoString, this.infoString.length);
+ }
+
+ @Override
+ public byte[] serialize() {
+ if (this.type != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ return super.serialize();
+ }
+ final int valueLength = LLDPOrganizationalTLV.OUI_LENGTH
+ + LLDPOrganizationalTLV.SUBTYPE_LENGTH + this.infoString.length;
+ this.value = new byte[valueLength];
+ final ByteBuffer bb = ByteBuffer.wrap(this.value);
+ bb.put(this.oui);
+ bb.put(this.subType);
+ bb.put(this.infoString);
+ return super.serialize();
+ }
+
+ @Override
+ public LLDPTLV deserialize(final ByteBuffer bb) throws DeserializationException {
+ super.deserialize(bb);
+ if (this.getType() != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ return this;
+ }
+
+ if (this.getLength() <= OUI_LENGTH + SUBTYPE_LENGTH) {
+ throw new DeserializationException(
+ "TLV length is less than required for organizational TLV");
+ }
+
+ final ByteBuffer optionalField = ByteBuffer.wrap(this.value);
+
+ final byte[] oui = new byte[LLDPOrganizationalTLV.OUI_LENGTH];
+ optionalField.get(oui);
+ this.setOUI(oui);
+
+ this.setSubType(optionalField.get());
+
+ final byte[] infoString = new byte[this.getLength()
+ - LLDPOrganizationalTLV.OUI_LENGTH
+ - LLDPOrganizationalTLV.SUBTYPE_LENGTH];
+ optionalField.get(infoString);
+ this.setInfoString(infoString);
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 1423;
+ int result = 1;
+ result = prime * result + this.type;
+ result = prime * result + this.length;
+ result = prime * result + Arrays.hashCode(this.oui);
+ result = prime * result + this.subType;
+ result = prime * result + Arrays.hashCode(this.infoString);
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof LLDPOrganizationalTLV)) {
+ return false;
+ }
+
+ final LLDPOrganizationalTLV other = (LLDPOrganizationalTLV) o;
+ if (this.type != other.type) {
+ return false;
+ }
+ if (this.length != other.length) {
+ return false;
+ }
+ if (!Arrays.equals(this.oui, other.oui)) {
+ return false;
+ }
+ if (this.subType != other.subType) {
+ return false;
+ }
+ if (!Arrays.equals(this.infoString, other.infoString)) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
new file mode 100644
index 00000000..77efe1b7
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+
+
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ *
+ *
+ */
+public class LLDPTLV {
+ protected byte type;
+ protected short length;
+ protected byte[] value;
+
+ /**
+ * @return the type
+ */
+ public byte getType() {
+ return this.type;
+ }
+
+ /**
+ * @param type
+ * the type to set
+ * @return this
+ */
+ public LLDPTLV setType(final byte type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return the length
+ */
+ public short getLength() {
+ return this.length;
+ }
+
+ /**
+ * @param length
+ * the length to set
+ * @return this
+ */
+ public LLDPTLV setLength(final short length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * @return the value
+ */
+ public byte[] getValue() {
+ return this.value;
+ }
+
+ /**
+ * @param value
+ * the value to set
+ * @return this
+ */
+ public LLDPTLV setValue(final byte[] value) {
+ this.value = value;
+ return this;
+ }
+
+ public byte[] serialize() {
+ // type = 7 bits
+ // info string length 9 bits, each value == byte
+ // info string
+ final short scratch = (short) ((0x7f & this.type) << 9 | 0x1ff & this.length);
+ final byte[] data = new byte[2 + this.length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.putShort(scratch);
+ if (this.value != null) {
+ bb.put(this.value);
+ }
+ return data;
+ }
+
+ public LLDPTLV deserialize(final ByteBuffer bb) throws DeserializationException {
+ if (bb.remaining() < 2) {
+ throw new DeserializationException(
+ "Not enough bytes to deserialize TLV type and length");
+ }
+ short typeLength;
+ typeLength = bb.getShort();
+ this.type = (byte) (typeLength >> 9 & 0x7f);
+ this.length = (short) (typeLength & 0x1ff);
+
+ if (this.length > 0) {
+ this.value = new byte[this.length];
+
+ // if there is an underrun just toss the TLV
+ if (bb.remaining() < this.length) {
+ throw new DeserializationException(
+ "Remaining bytes are less then the length of the TLV");
+ }
+ bb.get(this.value);
+ }
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 1423;
+ int result = 1;
+ result = prime * result + this.length;
+ result = prime * result + this.type;
+ result = prime * result + Arrays.hashCode(this.value);
+ 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 (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof LLDPTLV)) {
+ return false;
+ }
+ final LLDPTLV other = (LLDPTLV) obj;
+ if (this.length != other.length) {
+ return false;
+ }
+ if (this.type != other.type) {
+ return false;
+ }
+ if (!Arrays.equals(this.value, other.value)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MPLS.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MPLS.java
new file mode 100644
index 00000000..47dbeed2
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MPLS.java
@@ -0,0 +1,147 @@
+package org.onlab.packet;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+public class MPLS extends BasePacket {
+ public static final int HEADER_LENGTH = 4;
+
+ public static final byte PROTOCOL_IPV4 = 0x1;
+ public static final byte PROTOCOL_MPLS = 0x6;
+ static Map<Byte, Deserializer<? extends IPacket>> protocolDeserializerMap
+ = new HashMap<>();
+
+ static {
+ protocolDeserializerMap.put(PROTOCOL_IPV4, IPv4.deserializer());
+ protocolDeserializerMap.put(PROTOCOL_MPLS, MPLS.deserializer());
+ }
+
+ protected int label; //20bits
+ protected byte bos; //1bit
+ protected byte ttl; //8bits
+ protected byte protocol;
+
+ /**
+ * Default constructor that sets the version to 4.
+ */
+ public MPLS() {
+ super();
+ this.bos = 1;
+ this.protocol = PROTOCOL_IPV4;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (payload != null) {
+ payload.setParent(this);
+ payloadData = payload.serialize();
+ }
+
+ byte[] data = new byte[(4 + ((payloadData != null) ? payloadData.length : 0)) ];
+ ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(((this.label & 0x000fffff) << 12) | ((this.bos & 0x1) << 8 | (this.ttl & 0xff)));
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int mplsheader = bb.getInt();
+ this.label = ((mplsheader & 0xfffff000) >> 12);
+ this.bos = (byte) ((mplsheader & 0x00000100) >> 8);
+ this.bos = (byte) (mplsheader & 0x000000ff);
+ this.protocol = (this.bos == 1) ? PROTOCOL_IPV4 : PROTOCOL_MPLS;
+
+ Deserializer<? extends IPacket> deserializer;
+ if (protocolDeserializerMap.containsKey(this.protocol)) {
+ deserializer = protocolDeserializerMap.get(this.protocol);
+ } 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;
+ }
+
+ /**
+ * Returns the MPLS label.
+ *
+ * @return MPLS label
+ */
+ public int getLabel() {
+ return label;
+ }
+
+ /**
+ * Sets the MPLS label.
+ *
+ * @param label MPLS label
+ */
+ public void setLabel(int label) {
+ this.label = label;
+ }
+
+ /**
+ * Returns the MPLS TTL of the packet.
+ *
+ * @return MPLS TTL of the packet
+ */
+ public byte getTtl() {
+ return ttl;
+ }
+
+ /**
+ * Sets the MPLS TTL of the packet.
+ *
+ * @param ttl MPLS TTL
+ */
+ public void setTtl(byte ttl) {
+ this.ttl = ttl;
+ }
+
+ /**
+ * Deserializer function for MPLS packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<MPLS> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ MPLS mpls = new MPLS();
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int mplsheader = bb.getInt();
+ mpls.label = ((mplsheader & 0xfffff000) >>> 12);
+ mpls.bos = (byte) ((mplsheader & 0x00000100) >> 8);
+ mpls.ttl = (byte) (mplsheader & 0x000000ff);
+ mpls.protocol = (mpls.bos == 1) ? PROTOCOL_IPV4 : PROTOCOL_MPLS;
+
+ Deserializer<? extends IPacket> deserializer;
+ if (protocolDeserializerMap.containsKey(mpls.protocol)) {
+ deserializer = protocolDeserializerMap.get(mpls.protocol);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ mpls.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position());
+ mpls.payload.setParent(mpls);
+
+ return mpls;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
new file mode 100644
index 00000000..89cddbae
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
@@ -0,0 +1,217 @@
+/*
+ * 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 java.util.Arrays;
+
+/**
+ * The class representing MAC address.
+ */
+public class MacAddress {
+
+ public static final MacAddress ZERO = valueOf("00:00:00:00:00:00");
+ public static final MacAddress BROADCAST = valueOf("ff:ff:ff:ff:ff:ff");
+
+ private static final byte[] LL = new byte[]{
+ 0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00,
+ 0x00, 0x0e, 0x03
+ };
+
+ public static final int MAC_ADDRESS_LENGTH = 6;
+ private byte[] address = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+
+ public MacAddress(final byte[] address) {
+ this.address = Arrays.copyOf(address, MacAddress.MAC_ADDRESS_LENGTH);
+ }
+
+ /**
+ * Returns a MAC address instance representing the value of the specified
+ * {@code String}.
+ *
+ * @param address the String representation of the MAC Address to be parsed.
+ * @return a MAC Address instance representing the value of the specified
+ * {@code String}.
+ * @throws IllegalArgumentException if the string cannot be parsed as a MAC address.
+ */
+ public static MacAddress valueOf(final String address) {
+ final String[] elements = address.split(":");
+ if (elements.length != MacAddress.MAC_ADDRESS_LENGTH) {
+ throw new IllegalArgumentException(
+ "Specified MAC Address must contain 12 hex digits"
+ + " separated pairwise by :'s.");
+ }
+
+ final byte[] addressInBytes = new byte[MacAddress.MAC_ADDRESS_LENGTH];
+ for (int i = 0; i < MacAddress.MAC_ADDRESS_LENGTH; i++) {
+ final String element = elements[i];
+ addressInBytes[i] = (byte) Integer.parseInt(element, 16);
+ }
+
+ return new MacAddress(addressInBytes);
+ }
+
+ /**
+ * Returns a MAC address instance representing the specified {@code byte}
+ * array.
+ *
+ * @param address the byte array to be parsed.
+ * @return a MAC address instance representing the specified {@code byte}
+ * array.
+ * @throws IllegalArgumentException if the byte array cannot be parsed as a MAC address.
+ */
+ public static MacAddress valueOf(final byte[] address) {
+ if (address.length != MacAddress.MAC_ADDRESS_LENGTH) {
+ throw new IllegalArgumentException("the length is not "
+ + MacAddress.MAC_ADDRESS_LENGTH);
+ }
+
+ return new MacAddress(address);
+ }
+
+ /**
+ * Returns a MAC address instance representing the specified {@code long}
+ * value. The lower 48 bits of the long value are used to parse as a MAC
+ * address.
+ *
+ * @param address the long value to be parsed. The lower 48 bits are used for a
+ * MAC address.
+ * @return a MAC address instance representing the specified {@code long}
+ * value.
+ * @throws IllegalArgumentException if the long value cannot be parsed as a MAC address.
+ */
+ public static MacAddress valueOf(final long address) {
+ final byte[] addressInBytes = new byte[]{
+ (byte) (address >> 40 & 0xff), (byte) (address >> 32 & 0xff),
+ (byte) (address >> 24 & 0xff), (byte) (address >> 16 & 0xff),
+ (byte) (address >> 8 & 0xff), (byte) (address >> 0 & 0xff)};
+
+ return new MacAddress(addressInBytes);
+ }
+
+ /**
+ * Returns the length of the {@code MACAddress}.
+ *
+ * @return the length of the {@code MACAddress}.
+ */
+ public int length() {
+ return this.address.length;
+ }
+
+ /**
+ * Returns the value of the {@code MACAddress} as a {@code byte} array.
+ *
+ * @return the numeric value represented by this object after conversion to
+ * type {@code byte} array.
+ */
+ public byte[] toBytes() {
+ return Arrays.copyOf(this.address, this.address.length);
+ }
+
+ /**
+ * Returns the value of the {@code MACAddress} as a {@code long}.
+ *
+ * @return the numeric value represented by this object after conversion to
+ * type {@code long}.
+ */
+ public long toLong() {
+ long mac = 0;
+ for (int i = 0; i < 6; i++) {
+ final long t = (this.address[i] & 0xffL) << (5 - i) * 8;
+ mac |= t;
+ }
+ return mac;
+ }
+
+ /**
+ * Returns {@code true} if the MAC address is the broadcast address.
+ *
+ * @return {@code true} if the MAC address is the broadcast address.
+ */
+ public boolean isBroadcast() {
+ for (final byte b : this.address) {
+ if (b != -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@code true} if the MAC address is the multicast address.
+ *
+ * @return {@code true} if the MAC address is the multicast address.
+ */
+ public boolean isMulticast() {
+ if (this.isBroadcast()) {
+ return false;
+ }
+ return (this.address[0] & 0x01) != 0;
+ }
+
+ /**
+ * Returns true if this MAC address is link local.
+ *
+ * @return true if link local
+ */
+ public boolean isLinkLocal() {
+ return LL[0] == address[0] && LL[1] == address[1] && LL[2] == address[2] &&
+ LL[3] == address[3] && LL[4] == address[4] &&
+ (LL[5] == address[5] || LL[6] == address[5] || LL[7] == address[5]);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof MacAddress)) {
+ return false;
+ }
+
+ final MacAddress other = (MacAddress) o;
+ return Arrays.equals(this.address, other.address);
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(toLong());
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ for (final byte b : this.address) {
+ if (builder.length() > 0) {
+ builder.append(":");
+ }
+ builder.append(String.format("%02X", b & 0xFF));
+ }
+ return builder.toString();
+ }
+
+ /**
+ * @return MAC address in string representation without colons (useful for
+ * radix tree storage)
+ */
+ public String toStringNoColon() {
+ final StringBuilder builder = new StringBuilder();
+ for (final byte b : this.address) {
+ builder.append(String.format("%02X", b & 0xFF));
+ }
+ return builder.toString();
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java
new file mode 100644
index 00000000..09a939fc
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MplsLabel.java
@@ -0,0 +1,74 @@
+package org.onlab.packet;
+
+/*
+ * 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.
+ */
+
+/**
+ * Representation of a MPLS label.
+ */
+public class MplsLabel {
+
+ private final int mplsLabel;
+
+ // An MPLS Label maximum 20 bits.
+ public static final int MAX_MPLS = 0xFFFFF;
+
+ protected MplsLabel(int value) {
+ this.mplsLabel = value;
+ }
+
+ public static MplsLabel mplsLabel(int value) {
+
+ if (value < 0 || value > MAX_MPLS) {
+ String errorMsg = "MPLS label value " + value +
+ " is not in the interval [0, 0xFFFFF]";
+ throw new IllegalArgumentException(errorMsg);
+ }
+ return new MplsLabel(value);
+ }
+
+ public int toInt() {
+ return this.mplsLabel;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof MplsLabel) {
+
+ MplsLabel other = (MplsLabel) obj;
+
+ if (this.mplsLabel == other.mplsLabel) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.mplsLabel;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(this.mplsLabel);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
new file mode 100644
index 00000000..5b3902a8
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+package org.onlab.packet;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.ArrayUtils;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * ONOS LLDP containing organizational TLV for ONOS device dicovery.
+ */
+public class ONOSLLDP extends LLDP {
+
+ public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
+ public static final String DEFAULT_DEVICE = "INVALID";
+ public static final String DEFAULT_NAME = "ONOS Discovery";
+
+ public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
+ 0x01};
+ public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
+ (byte) 0xc2, 0x00, 0x00, 0x0e};
+ public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+
+ private static final byte NAME_SUBTYPE = 1;
+ private static final byte DEVICE_SUBTYPE = 2;
+ private static final short NAME_LENGTH = 4; //1 for subtype + 3 for OUI
+ private static final short DEVICE_LENGTH = 4; //1 for subtype + 3 for OUI
+ private final LLDPOrganizationalTLV nameTLV = new LLDPOrganizationalTLV();
+ private final LLDPOrganizationalTLV deviceTLV = new LLDPOrganizationalTLV();
+
+ // TLV constants: type, size and subtype
+ // Organizationally specific TLV also have packet offset and contents of TLV
+ // header
+ private static final byte CHASSIS_TLV_TYPE = 1;
+ private static final byte CHASSIS_TLV_SIZE = 7;
+ private static final byte CHASSIS_TLV_SUBTYPE = 4;
+
+ private static final byte PORT_TLV_TYPE = 2;
+ private static final byte PORT_TLV_SIZE = 5;
+ private static final byte PORT_TLV_SUBTYPE = 2;
+
+ private static final byte TTL_TLV_TYPE = 3;
+
+
+ private final byte[] ttlValue = new byte[] {0, 0x78};
+
+ public ONOSLLDP() {
+ super();
+ setName(DEFAULT_NAME);
+ setDevice(DEFAULT_DEVICE);
+ setOptionalTLVList(Lists.<LLDPTLV>newArrayList(nameTLV, deviceTLV));
+ setTtl(new LLDPTLV().setType(TTL_TLV_TYPE)
+ .setLength((short) ttlValue.length)
+ .setValue(ttlValue));
+
+ }
+
+ private ONOSLLDP(LLDP lldp) {
+ this.portId = lldp.getPortId();
+ this.chassisId = lldp.getChassisId();
+ this.ttl = lldp.getTtl();
+ this.optionalTLVList = lldp.getOptionalTLVList();
+ }
+
+ public void setName(String name) {
+ nameTLV.setLength((short) (name.length() + NAME_LENGTH));
+ nameTLV.setInfoString(name);
+ nameTLV.setSubType(NAME_SUBTYPE);
+ nameTLV.setOUI(ONLAB_OUI);
+ }
+
+ public void setDevice(String device) {
+ deviceTLV.setInfoString(device);
+ deviceTLV.setLength((short) (device.length() + DEVICE_LENGTH));
+ deviceTLV.setSubType(DEVICE_SUBTYPE);
+ deviceTLV.setOUI(ONLAB_OUI);
+ }
+
+ public void setChassisId(final ChassisId chassisId) {
+ MacAddress chassisMac = MacAddress.valueOf(chassisId.value());
+ byte[] chassis = ArrayUtils.addAll(new byte[] {CHASSIS_TLV_SUBTYPE},
+ chassisMac.toBytes());
+
+ LLDPTLV chassisTLV = new LLDPTLV();
+ chassisTLV.setLength(CHASSIS_TLV_SIZE);
+ chassisTLV.setType(CHASSIS_TLV_TYPE);
+ chassisTLV.setValue(chassis);
+ this.setChassisId(chassisTLV);
+ }
+
+ public void setPortId(final int portNumber) {
+ byte[] port = ArrayUtils.addAll(new byte[] {PORT_TLV_SUBTYPE},
+ ByteBuffer.allocate(4).putInt(portNumber).array());
+
+ LLDPTLV portTLV = new LLDPTLV();
+ portTLV.setLength(PORT_TLV_SIZE);
+ portTLV.setType(PORT_TLV_TYPE);
+ portTLV.setValue(port);
+ this.setPortId(portTLV);
+ }
+
+ public LLDPOrganizationalTLV getNameTLV() {
+ for (LLDPTLV tlv : this.getOptionalTLVList()) {
+ if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
+ if (orgTLV.getSubType() == NAME_SUBTYPE) {
+ return orgTLV;
+ }
+ }
+ }
+ return null;
+ }
+
+ public LLDPOrganizationalTLV getDeviceTLV() {
+ for (LLDPTLV tlv : this.getOptionalTLVList()) {
+ if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
+ if (orgTLV.getSubType() == DEVICE_SUBTYPE) {
+ return orgTLV;
+ }
+ }
+ }
+ return null;
+ }
+
+ public String getNameString() {
+ LLDPOrganizationalTLV tlv = getNameTLV();
+ if (tlv != null) {
+ return new String(tlv.getInfoString(), StandardCharsets.UTF_8);
+ }
+ return null;
+ }
+
+ public String getDeviceString() {
+ LLDPOrganizationalTLV tlv = getDeviceTLV();
+ if (tlv != null) {
+ return new String(tlv.getInfoString(), StandardCharsets.UTF_8);
+ }
+ return null;
+ }
+
+ public Integer getPort() {
+ ByteBuffer portBB = ByteBuffer.wrap(this.getPortId().getValue());
+ portBB.position(1);
+ return portBB.getInt();
+ }
+
+ /**
+ * Given an ethernet packet, determines if this is an LLDP from
+ * ONOS and returns the device the LLDP came from.
+ * @param eth an ethernet packet
+ * @return a the lldp packet or null
+ */
+ public static ONOSLLDP parseONOSLLDP(Ethernet eth) {
+ if (eth.getEtherType() == Ethernet.TYPE_LLDP ||
+ eth.getEtherType() == Ethernet.TYPE_BSN) {
+ ONOSLLDP onosLldp = new ONOSLLDP((LLDP) eth.getPayload()); //(ONOSLLDP) eth.getPayload();
+ if (ONOSLLDP.DEFAULT_NAME.equals(onosLldp.getNameString())) {
+ return onosLldp;
+ }
+ }
+ return null;
+ }
+
+
+
+
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java
new file mode 100644
index 00000000..c3bede2f
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PacketUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 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 static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Utilities for working with packet headers.
+ */
+public final class PacketUtils {
+
+ private PacketUtils() {
+ }
+
+ /**
+ * Check the length of the input buffer is appropriate given the offset and
+ * length parameters.
+ *
+ * @param byteLength length of the input buffer array
+ * @param offset offset given to begin reading bytes from
+ * @param length length given to read up until
+ * @throws DeserializationException if the input parameters don't match up (i.e
+ * we can't read that many bytes from the buffer at the given offest)
+ */
+ public static void checkBufferLength(int byteLength, int offset, int length)
+ throws DeserializationException {
+ boolean ok = (offset >= 0 && offset < byteLength);
+ ok = ok & (length >= 0 && offset + length <= byteLength);
+
+ if (!ok) {
+ throw new DeserializationException("Unable to read " + length + " bytes from a "
+ + byteLength + " byte array starting at offset " + offset);
+ }
+ }
+
+ /**
+ * Check that there are enough bytes in the buffer to read some number of
+ * bytes that we need to read a full header.
+ *
+ * @param givenLength given size of the buffer
+ * @param requiredLength number of bytes we need to read some header fully
+ * @throws DeserializationException if there aren't enough bytes
+ */
+ public static void checkHeaderLength(int givenLength, int requiredLength)
+ throws DeserializationException {
+ if (requiredLength > givenLength) {
+ throw new DeserializationException(requiredLength
+ + " bytes are needed to continue deserialization, however only "
+ + givenLength + " remain in buffer");
+ }
+ }
+
+ /**
+ * Check the input parameters are sane and there's enough bytes to read
+ * the required length.
+ *
+ * @param data input byte buffer
+ * @param offset offset of the start of the header
+ * @param length length given to deserialize the header
+ * @param requiredLength length needed to deserialize header
+ * @throws DeserializationException if we're unable to deserialize the
+ * packet based on the input parameters
+ */
+ public static void checkInput(byte[] data, int offset, int length, int requiredLength)
+ throws DeserializationException {
+ checkNotNull(data);
+ checkBufferLength(data.length, offset, length);
+ checkHeaderLength(length, requiredLength);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUS.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUS.java
new file mode 100644
index 00000000..297fee7c
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUS.java
@@ -0,0 +1,423 @@
+/*
+ *
+ * * Copyright 2015 AT&T Foundry
+ * *
+ * * 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.slf4j.Logger;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * RADIUS packet.
+ */
+public class RADIUS extends BasePacket {
+ protected byte code;
+ protected byte identifier;
+ protected short length = RADIUS_MIN_LENGTH;
+ protected byte[] authenticator = new byte[16];
+ protected List<RADIUSAttribute> attributes = new ArrayList<>();
+
+ // RADIUS parameters
+ public static final short RADIUS_MIN_LENGTH = 20;
+ public static final short MAX_ATTR_VALUE_LENGTH = 253;
+ public static final short RADIUS_MAX_LENGTH = 4096;
+
+ // RADIUS packet types
+ public static final byte RADIUS_CODE_ACCESS_REQUEST = 0x01;
+ public static final byte RADIUS_CODE_ACCESS_ACCEPT = 0x02;
+ public static final byte RADIUS_CODE_ACCESS_REJECT = 0x03;
+ public static final byte RADIUS_CODE_ACCOUNTING_REQUEST = 0x04;
+ public static final byte RADIUS_CODE_ACCOUNTING_RESPONSE = 0x05;
+ public static final byte RADIUS_CODE_ACCESS_CHALLENGE = 0x0b;
+
+ private final Logger log = getLogger(getClass());
+
+ /**
+ * Default constructor.
+ */
+ public RADIUS() {
+ }
+
+ /**
+ * Constructs a RADIUS packet with the given code and identifier.
+ *
+ * @param code code
+ * @param identifier identifier
+ */
+ public RADIUS(byte code, byte identifier) {
+ this.code = code;
+ this.identifier = identifier;
+ }
+
+ /**
+ * Gets the code.
+ *
+ * @return code
+ */
+ public byte getCode() {
+ return this.code;
+ }
+
+ /**
+ * Sets the code.
+ *
+ * @param code code
+ */
+ public void setCode(byte code) {
+ this.code = code;
+ }
+
+ /**
+ * Gets the identifier.
+ *
+ * @return identifier
+ */
+ public byte getIdentifier() {
+ return this.identifier;
+ }
+
+ /**
+ * Sets the identifier.
+ *
+ * @param identifier identifier
+ */
+ public void setIdentifier(byte identifier) {
+ this.identifier = identifier;
+ }
+
+ /**
+ * Gets the authenticator.
+ *
+ * @return authenticator
+ */
+ public byte[] getAuthenticator() {
+ return this.authenticator;
+ }
+
+ /**
+ * Sets the authenticator.
+ *
+ * @param authenticator authenticator
+ */
+ public void setAuthenticator(byte[] authenticator) {
+ this.authenticator = authenticator;
+ }
+
+ /**
+ * Generates an authenticator code.
+ *
+ * @return the authenticator
+ */
+ public byte[] generateAuthCode() {
+ new SecureRandom().nextBytes(this.authenticator);
+ return this.authenticator;
+ }
+
+ /**
+ * Checks if the packet's code field is valid.
+ *
+ * @return whether the code is valid
+ */
+ public boolean isValidCode() {
+ return this.code == RADIUS_CODE_ACCESS_REQUEST ||
+ this.code == RADIUS_CODE_ACCESS_ACCEPT ||
+ this.code == RADIUS_CODE_ACCESS_REJECT ||
+ this.code == RADIUS_CODE_ACCOUNTING_REQUEST ||
+ this.code == RADIUS_CODE_ACCOUNTING_RESPONSE ||
+ this.code == RADIUS_CODE_ACCESS_CHALLENGE;
+ }
+
+ /**
+ * Adds a message authenticator to the packet based on the given key.
+ *
+ * @param key key to generate message authenticator
+ * @return the messgae authenticator RADIUS attribute
+ */
+ public RADIUSAttribute addMessageAuthenticator(String key) {
+ // Message-Authenticator = HMAC-MD5 (Type, Identifier, Length,
+ // Request Authenticator, Attributes)
+ // When the message integrity check is calculated the signature string
+ // should be considered to be sixteen octets of zero.
+ byte[] hashOutput = new byte[16];
+ Arrays.fill(hashOutput, (byte) 0);
+
+ RADIUSAttribute authAttribute = this.getAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH);
+ if (authAttribute != null) {
+ // If Message-Authenticator was already present, override it
+ this.log.warn("Attempted to add duplicate Message-Authenticator");
+ authAttribute = this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
+ } else {
+ // Else generate a new attribute padded with zeroes
+ authAttribute = this.setAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
+ }
+ // Calculate the MD5 HMAC based on the message
+ try {
+ SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacMD5");
+ Mac mac = Mac.getInstance("HmacMD5");
+ mac.init(keySpec);
+ hashOutput = mac.doFinal(this.serialize());
+ // Update HMAC in Message-Authenticator
+ authAttribute = this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, hashOutput);
+ } catch (Exception e) {
+ this.log.error("Failed to generate message authenticator: {}", e.getMessage());
+ }
+
+ return authAttribute;
+ }
+
+ /**
+ * Checks the message authenticator in the packet with one generated from
+ * the given key.
+ *
+ * @param key key to generate message authenticator
+ * @return whether the message authenticators match or not
+ */
+ public boolean checkMessageAuthenticator(String key) {
+ byte[] newHash = new byte[16];
+ Arrays.fill(newHash, (byte) 0);
+ byte[] messageAuthenticator = this.getAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH).getValue();
+ this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, newHash);
+ // Calculate the MD5 HMAC based on the message
+ try {
+ SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacMD5");
+ Mac mac = Mac.getInstance("HmacMD5");
+ mac.init(keySpec);
+ newHash = mac.doFinal(this.serialize());
+ } catch (Exception e) {
+ log.error("Failed to generate message authenticator: {}", e.getMessage());
+ }
+ this.updateAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, messageAuthenticator);
+ // Compare the calculated Message-Authenticator with the one in the message
+ return Arrays.equals(newHash, messageAuthenticator);
+ }
+
+ /**
+ * Encapsulates an EAP packet in this RADIUS packet.
+ *
+ * @param message EAP message object to be embedded in the RADIUS
+ * EAP-Message attributed
+ */
+ public void encapsulateMessage(EAP message) {
+ if (message.length <= MAX_ATTR_VALUE_LENGTH) {
+ // Use the regular serialization method as it fits into one EAP-Message attribute
+ this.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
+ message.serialize());
+ } else {
+ // Segment the message into chucks and embed them in several EAP-Message attributes
+ short remainingLength = message.length;
+ byte[] messageBuffer = message.serialize();
+ final ByteBuffer bb = ByteBuffer.wrap(messageBuffer);
+ while (bb.hasRemaining()) {
+ byte[] messageAttributeData;
+ if (remainingLength > MAX_ATTR_VALUE_LENGTH) {
+ // The remaining data is still too long to fit into one attribute, keep going
+ messageAttributeData = new byte[MAX_ATTR_VALUE_LENGTH];
+ bb.get(messageAttributeData, 0, MAX_ATTR_VALUE_LENGTH);
+ remainingLength -= MAX_ATTR_VALUE_LENGTH;
+ } else {
+ // The remaining data fits, this will be the last chunk
+ messageAttributeData = new byte[remainingLength];
+ bb.get(messageAttributeData, 0, remainingLength);
+ }
+ this.attributes.add(new RADIUSAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
+ (byte) (messageAttributeData.length + 2), messageAttributeData));
+
+ // Adding the size of the data to the total RADIUS length
+ this.length += (short) (messageAttributeData.length & 0xFF);
+ // Adding the size of the overhead attribute type and length
+ this.length += 2;
+ }
+ }
+ }
+
+ /**
+ * Decapsulates an EAP packet from the RADIUS packet.
+ *
+ * @return An EAP object containing the reassembled EAP message
+ */
+ public EAP decapsulateMessage() {
+ EAP message = new EAP();
+ ByteArrayOutputStream messageStream = new ByteArrayOutputStream();
+ // Iterating through EAP-Message attributes to concatenate their value
+ for (RADIUSAttribute ra : this.getAttributeList(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE)) {
+ try {
+ messageStream.write(ra.getValue());
+ } catch (IOException e) {
+ log.error("Error while reassembling EAP message: {}", e.getMessage());
+ }
+ }
+ // Assembling EAP object from the concatenated stream
+ message.deserialize(messageStream.toByteArray(), 0, messageStream.size());
+ return message;
+ }
+
+ /**
+ * Gets a list of attributes from the RADIUS packet.
+ *
+ * @param attrType the type field of the required attributes
+ * @return List of the attributes that matches the type or an empty list if there is none
+ */
+ public ArrayList<RADIUSAttribute> getAttributeList(byte attrType) {
+ ArrayList<RADIUSAttribute> attrList = new ArrayList<>();
+ for (int i = 0; i < this.attributes.size(); i++) {
+ if (this.attributes.get(i).getType() == attrType) {
+ attrList.add(this.attributes.get(i));
+ }
+ }
+ return attrList;
+ }
+
+ /**
+ * Gets an attribute from the RADIUS packet.
+ *
+ * @param attrType the type field of the required attribute
+ * @return the first attribute that matches the type or null if does not exist
+ */
+ public RADIUSAttribute getAttribute(byte attrType) {
+ for (int i = 0; i < this.attributes.size(); i++) {
+ if (this.attributes.get(i).getType() == attrType) {
+ return this.attributes.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets an attribute in the RADIUS packet.
+ *
+ * @param attrType the type field of the attribute to set
+ * @param value value to be set
+ * @return reference to the attribute object
+ */
+ public RADIUSAttribute setAttribute(byte attrType, byte[] value) {
+ byte attrLength = (byte) (value.length + 2);
+ RADIUSAttribute newAttribute = new RADIUSAttribute(attrType, attrLength, value);
+ this.attributes.add(newAttribute);
+ this.length += (short) (attrLength & 0xFF);
+ return newAttribute;
+ }
+
+ /**
+ * Updates an attribute in the RADIUS packet.
+ *
+ * @param attrType the type field of the attribute to update
+ * @param value the value to update to
+ * @return reference to the attribute object
+ */
+ public RADIUSAttribute updateAttribute(byte attrType, byte[] value) {
+ for (int i = 0; i < this.attributes.size(); i++) {
+ if (this.attributes.get(i).getType() == attrType) {
+ this.length -= (short) (this.attributes.get(i).getLength() & 0xFF);
+ RADIUSAttribute newAttr = new RADIUSAttribute(attrType, (byte) (value.length + 2), value);
+ this.attributes.set(i, newAttr);
+ this.length += (short) (newAttr.getLength() & 0xFF);
+ return newAttr;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Deserializer for RADIUS packets.
+ *
+ * @return deserializer
+ */
+ public static Deserializer<RADIUS> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, RADIUS_MIN_LENGTH);
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ RADIUS radius = new RADIUS();
+ radius.code = bb.get();
+ radius.identifier = bb.get();
+ radius.length = bb.getShort();
+ bb.get(radius.authenticator, 0, 16);
+
+ checkHeaderLength(length, radius.length);
+
+ int remainingLength = radius.length - RADIUS_MIN_LENGTH;
+ while (remainingLength > 0 && bb.hasRemaining()) {
+
+ RADIUSAttribute attr = new RADIUSAttribute();
+ attr.setType(bb.get());
+ attr.setLength(bb.get());
+ short attrLength = (short) (attr.length & 0xff);
+ attr.value = new byte[attrLength - 2];
+ bb.get(attr.value, 0, attrLength - 2);
+ radius.attributes.add(attr);
+ remainingLength -= attr.length;
+ }
+ return radius;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ final byte[] data = new byte[this.length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.code);
+ bb.put(this.identifier);
+ bb.putShort(this.length);
+ bb.put(this.authenticator);
+ for (int i = 0; i < this.attributes.size(); i++) {
+ RADIUSAttribute attr = this.attributes.get(i);
+ bb.put(attr.getType());
+ bb.put(attr.getLength());
+ bb.put(attr.getValue());
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ this.code = bb.get();
+ this.identifier = bb.get();
+ this.length = bb.getShort();
+ bb.get(this.authenticator, 0, 16);
+
+ int remainingLength = this.length - RADIUS_MIN_LENGTH;
+ while (remainingLength > 0 && bb.hasRemaining()) {
+ RADIUSAttribute attr = new RADIUSAttribute();
+ attr.setType(bb.get());
+ attr.setLength(bb.get());
+ short attrLength = (short) (attr.length & 0xff);
+ attr.value = new byte[attrLength - 2];
+ bb.get(attr.value, 0, attrLength - 2);
+ this.attributes.add(attr);
+ remainingLength -= attr.length;
+ }
+ return this;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUSAttribute.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUSAttribute.java
new file mode 100644
index 00000000..9687e377
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/RADIUSAttribute.java
@@ -0,0 +1,142 @@
+/*
+ *
+ * * Copyright 2015 AT&T Foundry
+ * *
+ * * 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;
+
+/**
+ * An attribute in a RADIUS packet.
+ */
+public class RADIUSAttribute {
+ protected byte type;
+ protected byte length;
+ protected byte[] value;
+
+ // RADIUS attribute types
+ public static final byte RADIUS_ATTR_USERNAME = 1;
+ public static final byte RADIUS_ATTR_NAS_IP = 4;
+ public static final byte RADIUS_ATTR_NAS_PORT = 5;
+ public static final byte RADIUS_ATTR_FRAMED_MTU = 12;
+ public static final byte RADIUS_ATTR_STATE = 24;
+ public static final byte RADIUS_ATTR_VENDOR_SPECIFIC = 26;
+ public static final byte RADIUS_ATTR_CALLING_STATION_ID = 31;
+ public static final byte RADIUS_ATTR_NAS_ID = 32;
+ public static final byte RADIUS_ATTR_ACCT_SESSION_ID = 44;
+ public static final byte RADIUS_ATTR_NAS_PORT_TYPE = 61;
+ public static final byte RADIUS_ATTR_EAP_MESSAGE = 79;
+ public static final byte RADIUS_ATTR_MESSAGE_AUTH = 80;
+ public static final byte RADIUS_ATTR_NAS_PORT_ID = 87;
+
+ /**
+ * Default constructor.
+ */
+ public RADIUSAttribute() {
+ }
+
+ /**
+ * Constructs a RADIUS attribute with the give type, length and value.
+ *
+ * @param type type
+ * @param length length
+ * @param value value
+ */
+ public RADIUSAttribute(final byte type, final byte length, final byte[] value) {
+ this.type = type;
+ this.length = length;
+ this.value = value;
+ }
+
+ /**
+ * Checks if the attribute type is valid.
+ *
+ * @return whether the type is valid or not
+ */
+ public boolean isValidType() {
+ return this.type == RADIUS_ATTR_USERNAME ||
+ this.type == RADIUS_ATTR_NAS_IP ||
+ this.type == RADIUS_ATTR_NAS_PORT ||
+ this.type == RADIUS_ATTR_VENDOR_SPECIFIC ||
+ this.type == RADIUS_ATTR_CALLING_STATION_ID ||
+ this.type == RADIUS_ATTR_NAS_ID ||
+ this.type == RADIUS_ATTR_ACCT_SESSION_ID ||
+ this.type == RADIUS_ATTR_NAS_PORT_TYPE ||
+ this.type == RADIUS_ATTR_EAP_MESSAGE ||
+ this.type == RADIUS_ATTR_MESSAGE_AUTH ||
+ this.type == RADIUS_ATTR_NAS_PORT_ID;
+ }
+
+ /**
+ * Gets the attribute type.
+ *
+ * @return the type
+ */
+ public byte getType() {
+ return this.type;
+ }
+
+ /**
+ * Sets the attribute type.
+ *
+ * @param type the code to set
+ * @return this
+ */
+ public RADIUSAttribute setType(final byte type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Gets the attribute length.
+ *
+ * @return the length
+ */
+ public byte getLength() {
+ return this.length;
+ }
+
+ /**
+ * Sets the attribute length.
+ *
+ * @param length the length to set
+ * @return this
+ */
+ public RADIUSAttribute setLength(final byte length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * Gets the attribute value.
+ *
+ * @return the value
+ */
+ public byte[] getValue() {
+ return this.value;
+ }
+
+ /**
+ * Sets the attribute value.
+ *
+ * @param value the data to set
+ * @return this
+ */
+ public RADIUSAttribute setValue(final byte[] value) {
+ this.value = value;
+ return this;
+ }
+
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TCP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TCP.java
new file mode 100644
index 00000000..e089f272
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TCP.java
@@ -0,0 +1,462 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ * Implements TCP packet format.
+ */
+
+public class TCP extends BasePacket {
+
+ private static final short TCP_HEADER_LENGTH = 20;
+
+ protected int sourcePort;
+ protected int destinationPort;
+ protected int sequence;
+ protected int acknowledge;
+ protected byte dataOffset;
+ protected short flags;
+ protected short windowSize;
+ protected short checksum;
+ protected short urgentPointer;
+ protected byte[] options;
+
+ /**
+ * Gets TCP source port.
+ *
+ * @return TCP source port
+ */
+ public int getSourcePort() {
+ return this.sourcePort;
+ }
+
+ /**
+ * Sets TCP source port.
+ *
+ * @param sourcePort the sourcePort to set (unsigned 16 bits integer)
+ * @return this
+ */
+ public TCP setSourcePort(final int sourcePort) {
+ this.sourcePort = sourcePort;
+ return this;
+ }
+
+ /**
+ * Gets TCP destination port.
+ *
+ * @return the destinationPort
+ */
+ public int getDestinationPort() {
+ return this.destinationPort;
+ }
+
+ /**
+ * Sets TCP destination port.
+ *
+ * @param destinationPort the destinationPort to set (unsigned 16 bits integer)
+ * @return this
+ */
+ public TCP setDestinationPort(final int destinationPort) {
+ this.destinationPort = destinationPort;
+ return this;
+ }
+
+ /**
+ * Gets checksum.
+ *
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * Sets checksum.
+ *
+ * @param checksum the checksum to set
+ * @return this
+ */
+ public TCP setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ /**
+ * Gets sequence number.
+ *
+ * @return the sequence number
+ */
+ public int getSequence() {
+ return this.sequence;
+ }
+
+ /**
+ * Sets sequence number.
+ *
+ * @param seq the sequence number to set
+ * @return this
+ */
+ public TCP setSequence(final int seq) {
+ this.sequence = seq;
+ return this;
+ }
+
+ /**
+ * Gets acknowledge number.
+ *
+ * @return the acknowledge number
+ */
+ public int getAcknowledge() {
+ return this.acknowledge;
+ }
+
+ /**
+ * Sets acknowledge number.
+ *
+ * @param ack the acknowledge number to set
+ * @return this
+ */
+ public TCP setAcknowledge(final int ack) {
+ this.acknowledge = ack;
+ return this;
+ }
+
+ /**
+ * Gets offset.
+ *
+ * @return the offset
+ */
+ public byte getDataOffset() {
+ return this.dataOffset;
+ }
+
+ /**
+ * Sets offset.
+ *
+ * @param offset the offset to set
+ * @return this
+ */
+ public TCP setDataOffset(final byte offset) {
+ this.dataOffset = offset;
+ return this;
+ }
+
+ /**
+ * Gets TCP flags.
+ *
+ * @return the TCP flags
+ */
+ public short getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * Sets TCP flags.
+ *
+ * @param flags the TCP flags to set
+ * @return this
+ */
+ public TCP setFlags(final short flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * Gets TCP window size.
+ *
+ * @return the TCP window size
+ */
+ public short getWindowSize() {
+ return this.windowSize;
+ }
+
+ /**
+ * Sets TCP window size.
+ *
+ * @param windowSize the TCP window size to set
+ * @return this
+ */
+ public TCP setWindowSize(final short windowSize) {
+ this.windowSize = windowSize;
+ return this;
+ }
+
+ @Override
+ public void resetChecksum() {
+ this.checksum = 0;
+ super.resetChecksum();
+ }
+
+ /**
+ * Gets urgent pointer.
+ *
+ * @return the urgent pointer
+ */
+ public short getUrgentPointer() {
+ return this.urgentPointer;
+ }
+
+ /**
+ * Sets urgent pointer.
+ *
+ * @param urgentPointer the urgent pointer to set
+ * @return this
+ */
+ public TCP setUrgentPointer(final short urgentPointer) {
+ this.urgentPointer = urgentPointer;
+ return this;
+ }
+
+ /**
+ * Gets TCP options.
+ *
+ * @return the TCP options
+ */
+ public byte[] getOptions() {
+ return this.options;
+ }
+
+ /**
+ * Sets TCP options.
+ *
+ * @param options the options to set
+ * @return this
+ */
+ public TCP setOptions(final byte[] options) {
+ this.options = options;
+ this.dataOffset = (byte) (20 + options.length + 3 >> 2);
+ return this;
+ }
+
+ /**
+ * Serializes the packet. Will compute and set the following fields if they
+ * are set to specific values at the time serialize is called: -checksum : 0
+ * -length : 0
+ */
+ @Override
+ public byte[] serialize() {
+ int length;
+ if (this.dataOffset == 0) {
+ this.dataOffset = 5; // default header length
+ }
+ length = this.dataOffset << 2;
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ length += payloadData.length;
+ }
+
+ final byte[] data = new byte[length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putShort((short) (this.sourcePort & 0xffff));
+ bb.putShort((short) (this.destinationPort & 0xffff));
+ bb.putInt(this.sequence);
+ bb.putInt(this.acknowledge);
+ bb.putShort((short) (this.flags | this.dataOffset << 12));
+ bb.putShort(this.windowSize);
+ bb.putShort(this.checksum);
+ bb.putShort(this.urgentPointer);
+ if (this.dataOffset > 5) {
+ int padding;
+ bb.put(this.options);
+ padding = (this.dataOffset << 2) - 20 - this.options.length;
+ for (int i = 0; i < padding; i++) {
+ bb.put((byte) 0);
+ }
+ }
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IPv4) {
+ ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_TCP);
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bb.rewind();
+ int accumulation = 0;
+
+ // compute pseudo header mac
+ if (this.parent != null) {
+ if (this.parent instanceof IPv4) {
+ final IPv4 ipv4 = (IPv4) this.parent;
+ accumulation += (ipv4.getSourceAddress() >> 16 & 0xffff)
+ + (ipv4.getSourceAddress() & 0xffff);
+ accumulation += (ipv4.getDestinationAddress() >> 16 & 0xffff)
+ + (ipv4.getDestinationAddress() & 0xffff);
+ accumulation += ipv4.getProtocol() & 0xff;
+ accumulation += length & 0xffff;
+ } else if (this.parent instanceof IPv6) {
+ final IPv6 ipv6 = (IPv6) this.parent;
+ final int bbLength =
+ Ip6Address.BYTE_LENGTH * 2 // IPv6 src, dst
+ + 2 // nextHeader (with padding)
+ + 4; // length
+ final ByteBuffer bbChecksum = ByteBuffer.allocate(bbLength);
+ bbChecksum.put(ipv6.getSourceAddress());
+ bbChecksum.put(ipv6.getDestinationAddress());
+ bbChecksum.put((byte) 0); // padding
+ bbChecksum.put(ipv6.getNextHeader());
+ bbChecksum.putInt(length);
+ bbChecksum.rewind();
+ for (int i = 0; i < bbLength / 2; ++i) {
+ accumulation += 0xffff & bbChecksum.getShort();
+ }
+ }
+ }
+
+ for (int i = 0; i < length / 2; ++i) {
+ accumulation += 0xffff & bb.getShort();
+ }
+ // pad to an even number of shorts
+ if (length % 2 > 0) {
+ accumulation += (bb.get() & 0xff) << 8;
+ }
+
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bb.putShort(16, 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.sourcePort = (bb.getShort() & 0xffff);
+ this.destinationPort = (bb.getShort() & 0xffff);
+ this.sequence = bb.getInt();
+ this.acknowledge = bb.getInt();
+ this.flags = bb.getShort();
+ this.dataOffset = (byte) (this.flags >> 12 & 0xf);
+ this.flags = (short) (this.flags & 0x1ff);
+ this.windowSize = bb.getShort();
+ this.checksum = bb.getShort();
+ this.urgentPointer = bb.getShort();
+ if (this.dataOffset > 5) {
+ int optLength = (this.dataOffset << 2) - 20;
+ if (bb.limit() < bb.position() + optLength) {
+ optLength = bb.limit() - bb.position();
+ }
+ try {
+ this.options = new byte[optLength];
+ bb.get(this.options, 0, optLength);
+ } catch (final IndexOutOfBoundsException e) {
+ this.options = null;
+ }
+ }
+
+ this.payload = new Data();
+ this.payload = 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.checksum;
+ result = prime * result + this.destinationPort;
+ result = prime * result + this.sourcePort;
+ 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 TCP)) {
+ return false;
+ }
+ final TCP other = (TCP) obj;
+ // May want to compare fields based on the flags set
+ return this.checksum == other.checksum
+ && this.destinationPort == other.destinationPort
+ && this.sourcePort == other.sourcePort
+ && this.sequence == other.sequence
+ && this.acknowledge == other.acknowledge
+ && this.dataOffset == other.dataOffset
+ && this.flags == other.flags
+ && this.windowSize == other.windowSize
+ && this.urgentPointer == other.urgentPointer
+ && (this.dataOffset == 5 || Arrays.equals(this.options, other.options));
+ }
+
+ /**
+ * Deserializer function for TCP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<TCP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, TCP_HEADER_LENGTH);
+
+ TCP tcp = new TCP();
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ tcp.sourcePort = (bb.getShort() & 0xffff);
+ tcp.destinationPort = (bb.getShort() & 0xffff);
+ tcp.sequence = bb.getInt();
+ tcp.acknowledge = bb.getInt();
+ tcp.flags = bb.getShort();
+ tcp.dataOffset = (byte) (tcp.flags >> 12 & 0xf);
+ tcp.flags = (short) (tcp.flags & 0x1ff);
+ tcp.windowSize = bb.getShort();
+ tcp.checksum = bb.getShort();
+ tcp.urgentPointer = bb.getShort();
+ if (tcp.dataOffset > 5) {
+ int optLength = (tcp.dataOffset << 2) - 20;
+ checkHeaderLength(length, TCP_HEADER_LENGTH + tcp.dataOffset);
+ tcp.options = new byte[optLength];
+ bb.get(tcp.options, 0, optLength);
+ }
+
+ tcp.payload = Data.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+ tcp.payload.setParent(tcp);
+ return tcp;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TpPort.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TpPort.java
new file mode 100644
index 00000000..9b86a816
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/TpPort.java
@@ -0,0 +1,104 @@
+package org.onlab.packet;
+
+/*
+ * 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.
+ */
+
+/**
+ * Representation of a transport layer port.
+ */
+public class TpPort {
+
+ private final int port;
+
+ // Transport layer port is unsigned 16 bits integer.
+ public static final int MAX_PORT = 0xFFFF;
+ public static final int MIN_PORT = 0;
+
+ /**
+ * Constructs a new TpPort.
+ *
+ * @param value the transport layer port
+ */
+ protected TpPort(int value) {
+ this.port = value;
+ }
+
+ /**
+ * Converts an integer into a TpPort.
+ *
+ * @param value an integer representing the transport layer port
+ * @return a TpPort
+ * @throws IllegalArgumentException if the value is invalid
+ */
+ public static TpPort tpPort(int value) {
+ if (value < MIN_PORT || value > MAX_PORT) {
+ throw new IllegalArgumentException(
+ "Transport layer port value " + value + "is not in the interval [0, 0xFFFF]");
+ }
+ return new TpPort(value);
+ }
+
+ /**
+ * Returns the integer value for this transport port.
+ *
+ * @return an integer value
+ */
+ public int toInt() {
+ return this.port;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof TpPort) {
+
+ TpPort other = (TpPort) obj;
+
+ if (this.port == other.port) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return this.port;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return String.valueOf(this.port);
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/UDP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/UDP.java
new file mode 100644
index 00000000..a30c9a92
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/UDP.java
@@ -0,0 +1,306 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.onlab.packet.PacketUtils.*;
+
+/**
+ * Representation of a UDP packet.
+ */
+public class UDP extends BasePacket {
+ public static final Map<Integer, Deserializer<? extends IPacket>> PORT_DESERIALIZER_MAP =
+ new HashMap<>();
+ public static final int DHCP_SERVER_PORT = 67;
+ public static final int DHCP_CLIENT_PORT = 68;
+
+ private static final short UDP_HEADER_LENGTH = 8;
+
+ static {
+ /*
+ * Disable DHCP until the deserialize code is hardened to deal with
+ * garbage input
+ */
+ UDP.PORT_DESERIALIZER_MAP.put(UDP.DHCP_SERVER_PORT, DHCP.deserializer());
+ UDP.PORT_DESERIALIZER_MAP.put(UDP.DHCP_CLIENT_PORT, DHCP.deserializer());
+
+ }
+
+ protected int sourcePort;
+ protected int destinationPort;
+ protected short length;
+ protected short checksum;
+
+ /**
+ * @return the sourcePort
+ */
+ public int getSourcePort() {
+ return this.sourcePort;
+ }
+
+ /**
+ * @param sourcePort
+ * the sourcePort to set (16 bits unsigned integer)
+ * @return this
+ */
+ public UDP setSourcePort(final int sourcePort) {
+ this.sourcePort = sourcePort;
+ return this;
+ }
+
+ /**
+ * @return the destinationPort
+ */
+ public int getDestinationPort() {
+ return this.destinationPort;
+ }
+
+ /**
+ * @param destinationPort
+ * the destinationPort to set (16 bits unsigned integer)
+ * @return this
+ */
+ public UDP setDestinationPort(final int destinationPort) {
+ this.destinationPort = destinationPort;
+ return this;
+ }
+
+ /**
+ * @return the length
+ */
+ public short getLength() {
+ return this.length;
+ }
+
+ /**
+ * @return the checksum
+ */
+ public short getChecksum() {
+ return this.checksum;
+ }
+
+ /**
+ * @param checksum
+ * the checksum to set
+ * @return this
+ */
+ public UDP setChecksum(final short checksum) {
+ this.checksum = checksum;
+ return this;
+ }
+
+ @Override
+ public void resetChecksum() {
+ this.checksum = 0;
+ super.resetChecksum();
+ }
+
+ /**
+ * Serializes the packet. Will compute and set the following fields if they
+ * are set to specific values at the time serialize is called: -checksum : 0
+ * -length : 0
+ */
+ @Override
+ public byte[] serialize() {
+ byte[] payloadData = null;
+ if (this.payload != null) {
+ this.payload.setParent(this);
+ payloadData = this.payload.serialize();
+ }
+
+ this.length = (short) (8 + (payloadData == null ? 0
+ : payloadData.length));
+
+ final byte[] data = new byte[this.length];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putShort((short) (this.sourcePort & 0xffff));
+ bb.putShort((short) (this.destinationPort & 0xffff));
+ bb.putShort(this.length);
+ bb.putShort(this.checksum);
+ if (payloadData != null) {
+ bb.put(payloadData);
+ }
+
+ if (this.parent != null && this.parent instanceof IPv4) {
+ ((IPv4) this.parent).setProtocol(IPv4.PROTOCOL_UDP);
+ }
+
+ // compute checksum if needed
+ if (this.checksum == 0) {
+ bb.rewind();
+ int accumulation = 0;
+
+ // compute pseudo header mac
+ if (this.parent != null) {
+ if (this.parent instanceof IPv4) {
+ final IPv4 ipv4 = (IPv4) this.parent;
+ accumulation += (ipv4.getSourceAddress() >> 16 & 0xffff)
+ + (ipv4.getSourceAddress() & 0xffff);
+ accumulation += (ipv4.getDestinationAddress() >> 16 & 0xffff)
+ + (ipv4.getDestinationAddress() & 0xffff);
+ accumulation += ipv4.getProtocol() & 0xff;
+ accumulation += length & 0xffff;
+ } else if (this.parent instanceof IPv6) {
+ final IPv6 ipv6 = (IPv6) this.parent;
+ final int bbLength =
+ Ip6Address.BYTE_LENGTH * 2 // IPv6 src, dst
+ + 2 // nextHeader (with padding)
+ + 4; // length
+ final ByteBuffer bbChecksum = ByteBuffer.allocate(bbLength);
+ bbChecksum.put(ipv6.getSourceAddress());
+ bbChecksum.put(ipv6.getDestinationAddress());
+ bbChecksum.put((byte) 0); // padding
+ bbChecksum.put(ipv6.getNextHeader());
+ bbChecksum.putInt(length);
+ bbChecksum.rewind();
+ for (int i = 0; i < bbLength / 2; ++i) {
+ accumulation += 0xffff & bbChecksum.getShort();
+ }
+ }
+ }
+
+ for (int i = 0; i < this.length / 2; ++i) {
+ accumulation += 0xffff & bb.getShort();
+ }
+ // pad to an even number of shorts
+ if (this.length % 2 > 0) {
+ accumulation += (bb.get() & 0xff) << 8;
+ }
+
+ accumulation = (accumulation >> 16 & 0xffff)
+ + (accumulation & 0xffff);
+ this.checksum = (short) (~accumulation & 0xffff);
+ bb.putShort(6, 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.sourcePort = (bb.getShort() & 0xffff);
+ this.destinationPort = (bb.getShort() & 0xffff);
+ this.length = bb.getShort();
+ this.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (UDP.PORT_DESERIALIZER_MAP.containsKey(this.destinationPort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(this.destinationPort);
+ } else if (UDP.PORT_DESERIALIZER_MAP.containsKey(this.sourcePort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(this.sourcePort);
+ } 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.checksum;
+ result = prime * result + this.destinationPort;
+ result = prime * result + this.length;
+ result = prime * result + this.sourcePort;
+ 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 UDP)) {
+ return false;
+ }
+ final UDP other = (UDP) obj;
+ if (this.checksum != other.checksum) {
+ return false;
+ }
+ if (this.destinationPort != other.destinationPort) {
+ return false;
+ }
+ if (this.length != other.length) {
+ return false;
+ }
+ if (this.sourcePort != other.sourcePort) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for UDP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<UDP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, UDP_HEADER_LENGTH);
+
+ UDP udp = new UDP();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ udp.sourcePort = (bb.getShort() & 0xffff);
+ udp.destinationPort = (bb.getShort() & 0xffff);
+ udp.length = bb.getShort();
+ udp.checksum = bb.getShort();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (UDP.PORT_DESERIALIZER_MAP.containsKey(udp.destinationPort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(udp.destinationPort);
+ } else if (UDP.PORT_DESERIALIZER_MAP.containsKey(udp.sourcePort)) {
+ deserializer = UDP.PORT_DESERIALIZER_MAP.get(udp.sourcePort);
+ } else {
+ deserializer = Data.deserializer();
+ }
+
+ udp.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ udp.payload.setParent(udp);
+ return udp;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/VlanId.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/VlanId.java
new file mode 100644
index 00000000..4b38308b
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/VlanId.java
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+/**
+ * Representation of a VLAN ID.
+ */
+public class VlanId {
+
+ private final short value;
+
+ // Based on convention used elsewhere? Check and change if needed
+ public static final short UNTAGGED = (short) 0xffff;
+
+ // In a traffic selector, this means that a VLAN ID must be present, but
+ // can have any value. We use the same value as OpenFlow, but this is not
+ // required.
+ public static final short ANY_VALUE = (short) 0x1000;
+
+ public static final VlanId NONE = VlanId.vlanId(UNTAGGED);
+ public static final VlanId ANY = VlanId.vlanId(ANY_VALUE);
+
+ // A VLAN ID is actually 12 bits of a VLAN tag.
+ public static final short MAX_VLAN = 4095;
+
+ protected VlanId() {
+ this.value = UNTAGGED;
+ }
+
+ protected VlanId(short value) {
+ this.value = value;
+ }
+
+ public static VlanId vlanId() {
+ return new VlanId(UNTAGGED);
+ }
+
+ public static VlanId vlanId(short value) {
+ if (value == UNTAGGED) {
+ return new VlanId();
+ }
+
+ if (value == ANY_VALUE) {
+ return new VlanId(ANY_VALUE);
+ }
+
+ if (value > MAX_VLAN) {
+ throw new IllegalArgumentException(
+ "value exceeds allowed maximum VLAN ID value (4095)");
+ }
+ return new VlanId(value);
+ }
+
+ public short toShort() {
+ return this.value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof VlanId) {
+
+ VlanId other = (VlanId) obj;
+
+ if (this.value == other.value) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ if (this.value == ANY_VALUE) {
+ return "Any";
+ }
+ return String.valueOf(this.value);
+ }
+}
+
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
new file mode 100644
index 00000000..ec04a812
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Authentication.java
@@ -0,0 +1,300 @@
+/*
+ * 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.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * 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);
+
+ Deserializer<? extends IPacket> 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 = 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;
+ }
+
+ /**
+ * Deserializer function for authentication headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Authentication> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ Authentication authentication = new Authentication();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ authentication.nextHeader = bb.get();
+ authentication.payloadLength = bb.get();
+ bb.getShort();
+ authentication.securityParamIndex = bb.getInt();
+ authentication.sequence = bb.getInt();
+ int icvLength = (authentication.payloadLength + MINUS) * LENGTH_UNIT - FIXED_HEADER_LENGTH;
+ authentication.integrityCheck = new byte[icvLength];
+ bb.get(authentication.integrityCheck, 0, icvLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(authentication.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(authentication.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ authentication.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ authentication.payload.setParent(authentication);
+
+ return authentication;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
new file mode 100644
index 00000000..f57b756e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/BaseOptions.java
@@ -0,0 +1,260 @@
+/*
+ * 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.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * 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);
+
+ Deserializer<? extends IPacket> 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 = 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;
+ }
+
+ /**
+ * Deserializer function for IPv6 base options.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<BaseOptions> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ BaseOptions baseOptions = new BaseOptions();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ baseOptions.nextHeader = bb.get();
+ baseOptions.headerExtLength = bb.get();
+ int optionLength =
+ FIXED_OPTIONS_LENGTH + LENGTH_UNIT * baseOptions.headerExtLength;
+
+ checkHeaderLength(bb.remaining(), optionLength);
+
+ baseOptions.options = new byte[optionLength];
+ bb.get(baseOptions.options, 0, optionLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(baseOptions.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(baseOptions.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ baseOptions.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ baseOptions.payload.setParent(baseOptions);
+
+ return baseOptions;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/DestinationOptions.java
new file mode 100644
index 00000000..208bdd7e
--- /dev/null
+++ b/framework/src/onos/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/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
new file mode 100644
index 00000000..e46a1261
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/EncapSecurityPayload.java
@@ -0,0 +1,188 @@
+/*
+ * 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.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * 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;
+ }
+
+ /**
+ * Deserializer function for encapsulated security payload headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<EncapSecurityPayload> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ EncapSecurityPayload encapSecurityPayload = new EncapSecurityPayload();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ encapSecurityPayload.securityParamIndex = bb.getInt();
+ encapSecurityPayload.sequence = bb.getInt();
+
+ encapSecurityPayload.payload = Data.deserializer().deserialize(
+ data, bb.position(), bb.limit() - bb.position());
+ encapSecurityPayload.payload.setParent(encapSecurityPayload);
+
+ return encapSecurityPayload;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
new file mode 100644
index 00000000..68015d31
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Fragment.java
@@ -0,0 +1,253 @@
+/*
+ * 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.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * 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();
+
+ Deserializer<? extends IPacket> 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 = 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;
+ }
+
+ /**
+ * Deserializer function for fragment headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Fragment> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ Fragment fragment = new Fragment();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ fragment.nextHeader = bb.get();
+ bb.get();
+ short sscratch = bb.getShort();
+ fragment.fragmentOffset = (short) (sscratch >> 3 & 0x1fff);
+ fragment.moreFragment = (byte) (sscratch & 0x1);
+ fragment.identification = bb.getInt();
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(fragment.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(fragment.nextHeader);
+ } else {
+ deserializer = Data.deserializer();
+ }
+ fragment.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ fragment.payload.setParent(fragment);
+
+ return fragment;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/HopByHopOptions.java
new file mode 100644
index 00000000..cd8c141c
--- /dev/null
+++ b/framework/src/onos/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/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/IExtensionHeader.java
new file mode 100644
index 00000000..252f1a3c
--- /dev/null
+++ b/framework/src/onos/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
+ */
+ byte getNextHeader();
+
+ /**
+ * Sets the type of next header.
+ *
+ * @param nextHeader the next header to set
+ * @return this
+ */
+ IExtensionHeader setNextHeader(final byte nextHeader);
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
new file mode 100644
index 00000000..d7d204a9
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/Routing.java
@@ -0,0 +1,291 @@
+/*
+ * 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.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.onlab.packet.PacketUtils.checkHeaderLength;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * 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);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(this.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(this.nextHeader);
+ } else {
+ deserializer = new 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.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;
+ }
+
+ /**
+ * Deserializer function for routing headers.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Routing> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, FIXED_HEADER_LENGTH);
+
+ Routing routing = new Routing();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ routing.nextHeader = bb.get();
+ routing.headerExtLength = bb.get();
+ routing.routingType = bb.get();
+ routing.segmentsLeft = bb.get();
+ int dataLength =
+ FIXED_ROUTING_DATA_LENGTH + LENGTH_UNIT * routing.headerExtLength;
+
+ checkHeaderLength(bb.remaining(), dataLength);
+
+ routing.routingData = new byte[dataLength];
+ bb.get(routing.routingData, 0, dataLength);
+
+ Deserializer<? extends IPacket> deserializer;
+ if (IPv6.PROTOCOL_DESERIALIZER_MAP.containsKey(routing.nextHeader)) {
+ deserializer = IPv6.PROTOCOL_DESERIALIZER_MAP.get(routing.nextHeader);
+ } else {
+ deserializer = new Data().deserializer();
+ }
+ routing.payload = deserializer.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+ routing.payload.setParent(routing);
+
+ return routing;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java
new file mode 100644
index 00000000..714fd1b2
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ipv6/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Utilities for decoding and encoding IPv6 extension headers.
+ */
+package org.onlab.packet.ipv6;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
new file mode 100644
index 00000000..99fa0dd6
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
@@ -0,0 +1,278 @@
+/*
+ * 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.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.Ip6Address;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Neighbor Advertisement packet format (RFC 4861).
+ */
+public class NeighborAdvertisement extends BasePacket {
+ public static final byte HEADER_LENGTH = 20; // bytes
+
+ protected byte routerFlag;
+ protected byte solicitedFlag;
+ protected byte overrideFlag;
+ protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
+
+ private final NeighborDiscoveryOptions options =
+ new NeighborDiscoveryOptions();
+
+ /**
+ * Gets router flag.
+ *
+ * @return the router flag
+ */
+ public byte getRouterFlag() {
+ return this.routerFlag;
+ }
+
+ /**
+ * Sets router flag.
+ *
+ * @param routerFlag the router flag to set
+ * @return this
+ */
+ public NeighborAdvertisement setRouterFlag(final byte routerFlag) {
+ this.routerFlag = routerFlag;
+ return this;
+ }
+
+ /**
+ * Gets solicited flag.
+ *
+ * @return the solicited flag
+ */
+ public byte getSolicitedFlag() {
+ return this.solicitedFlag;
+ }
+
+ /**
+ * Sets solicited flag.
+ *
+ * @param solicitedFlag the solicited flag to set
+ * @return this
+ */
+ public NeighborAdvertisement setSolicitedFlag(final byte solicitedFlag) {
+ this.solicitedFlag = solicitedFlag;
+ return this;
+ }
+
+ /**
+ * Gets override flag.
+ *
+ * @return the override flag
+ */
+ public byte getOverrideFlag() {
+ return this.overrideFlag;
+ }
+
+ /**
+ * Sets override flag.
+ *
+ * @param overrideFlag the override flag to set
+ * @return this
+ */
+ public NeighborAdvertisement setOverrideFlag(final byte overrideFlag) {
+ this.overrideFlag = overrideFlag;
+ return this;
+ }
+
+ /**
+ * Gets target address.
+ *
+ * @return the target IPv6 address
+ */
+ public byte[] getTargetAddress() {
+ return this.targetAddress;
+ }
+
+ /**
+ * Sets target address.
+ *
+ * @param targetAddress the target IPv6 address to set
+ * @return this
+ */
+ public NeighborAdvertisement setTargetAddress(final byte[] targetAddress) {
+ this.targetAddress =
+ Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public NeighborAdvertisement addOption(final byte type,
+ final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt((this.routerFlag & 0x1) << 31 |
+ (this.solicitedFlag & 0x1) << 30 |
+ (this.overrideFlag & 0x1) << 29);
+ bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ int iscratch;
+
+ iscratch = bb.getInt();
+ this.routerFlag = (byte) (iscratch >> 31 & 0x1);
+ this.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
+ this.overrideFlag = (byte) (iscratch >> 29 & 0x1);
+ bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ ByteBuffer bb;
+ result = prime * result + this.routerFlag;
+ result = prime * result + this.solicitedFlag;
+ result = prime * result + this.overrideFlag;
+ bb = ByteBuffer.wrap(this.targetAddress);
+ for (int i = 0; i < this.targetAddress.length / 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ result = prime * result + this.options.hashCode();
+ 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 NeighborAdvertisement)) {
+ return false;
+ }
+ final NeighborAdvertisement other = (NeighborAdvertisement) obj;
+ if (this.routerFlag != other.routerFlag) {
+ return false;
+ }
+ if (this.solicitedFlag != other.solicitedFlag) {
+ return false;
+ }
+ if (this.overrideFlag != other.overrideFlag) {
+ return false;
+ }
+ if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
+ return false;
+ }
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for neighbor advertisement packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<NeighborAdvertisement> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ NeighborAdvertisement neighborAdvertisement = new NeighborAdvertisement();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ int iscratch;
+
+ iscratch = bb.getInt();
+ neighborAdvertisement.routerFlag = (byte) (iscratch >> 31 & 0x1);
+ neighborAdvertisement.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
+ neighborAdvertisement.overrideFlag = (byte) (iscratch >> 29 & 0x1);
+ bb.get(neighborAdvertisement.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ neighborAdvertisement.addOption(option.type(), option.data());
+ }
+ }
+
+ return neighborAdvertisement;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java
new file mode 100644
index 00000000..00a26068
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborDiscoveryOptions.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 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.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Neighbor Discovery Protocol packet options.
+ */
+public class NeighborDiscoveryOptions extends BasePacket {
+ public static final byte TYPE_SOURCE_LL_ADDRESS = 1;
+ public static final byte TYPE_TARGET_LL_ADDRESS = 2;
+ public static final byte TYPE_PREFIX_INFORMATION = 3;
+ public static final byte TYPE_REDIRECTED_HEADER = 4;
+ public static final byte TYPE_MTU = 5;
+
+ public static final byte INITIAL_HEADER_REQUIRED = 2;
+
+ private static final String BUFFER_UNDERFLOW_ERROR =
+ "Not enough bytes in buffer to read option";
+
+ private final List<Option> options = new ArrayList<>();
+
+ /**
+ * Packet option.
+ */
+ public final class Option {
+ private final byte type;
+ private final byte[] data;
+
+ /**
+ * Constructor.
+ *
+ * @param type the option type
+ * @param data the option data
+ */
+ private Option(byte type, byte[] data) {
+ this.type = type;
+ this.data = Arrays.copyOfRange(data, 0, data.length);
+ }
+
+ /**
+ * Gets the option type.
+ *
+ * @return the option type
+ */
+ public byte type() {
+ return this.type;
+ }
+
+ /**
+ * Gets the option data.
+ *
+ * @return the option data
+ */
+ public byte[] data() {
+ return this.data;
+ }
+
+ /**
+ * Gets the option data length (in number of octets).
+ *
+ * @return the option data length (in number of octets)
+ */
+ public int dataLength() {
+ return data.length;
+ }
+
+ /**
+ * Gets the option length (in number of octets), including the type and
+ * length fields (one octet each).
+ *
+ * @return the option length (in number of octets), including the type
+ * and length fields
+ */
+ private int optionLength() {
+ return 2 + dataLength();
+ }
+
+ /**
+ * Gets the option length field value (in units of 8 octets).
+ *
+ * @return the option length field value (in units of 8 octets)
+ */
+ private byte optionLengthField() {
+ return (byte) ((optionLength() + 7) / 8);
+ }
+
+ /**
+ * Gets the option length on the wire (in number of octets).
+ *
+ * @return the option length on the wire (in number of octets)
+ */
+ private int optionWireLength() {
+ return 8 * optionLengthField();
+ }
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public NeighborDiscoveryOptions addOption(byte type, byte[] data) {
+ options.add(new Option(type, data));
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> options() {
+ return this.options;
+ }
+
+ /**
+ * Checks whether any options are included.
+ *
+ * @return true if options are included, otherwise false
+ */
+ public boolean hasOptions() {
+ return !this.options.isEmpty();
+ }
+
+ @Override
+ public byte[] serialize() {
+ // Compute first the total length on the wire for all options
+
+ int wireLength = 0;
+
+ for (Option option : this.options) {
+ wireLength += option.optionWireLength();
+ }
+
+ final byte[] data = new byte[wireLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ //
+ // Serialize all options
+ //
+ for (Option option : this.options) {
+ bb.put(option.type());
+ bb.put(option.optionLengthField());
+ bb.put(option.data());
+ // Add the padding
+ int paddingLength =
+ option.optionWireLength() - option.optionLength();
+ for (int i = 0; i < paddingLength; i++) {
+ bb.put((byte) 0);
+ }
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ options.clear();
+
+ //
+ // Deserialize all options
+ //
+ while (bb.hasRemaining()) {
+ byte type = bb.get();
+ if (!bb.hasRemaining()) {
+ break;
+ }
+ byte lengthField = bb.get();
+ int dataLength = lengthField * 8; // The data length field is in
+ // unit of 8 octets
+
+ // Exclude the type and length fields
+ if (dataLength < 2) {
+ break;
+ }
+ dataLength -= 2;
+
+ if (bb.remaining() < dataLength) {
+ break;
+ }
+ byte[] optionData = new byte[dataLength];
+ bb.get(optionData, 0, optionData.length);
+ addOption(type, optionData);
+ }
+
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+
+ for (Option option : this.options) {
+ result = prime * result + option.type();
+ result = prime * result + Arrays.hashCode(option.data());
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof NeighborDiscoveryOptions) {
+ NeighborDiscoveryOptions other = (NeighborDiscoveryOptions) obj;
+ return this.options.equals(other.options);
+ }
+ return false;
+ }
+
+ public static Deserializer<NeighborDiscoveryOptions> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, INITIAL_HEADER_REQUIRED);
+
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ NeighborDiscoveryOptions ndo = new NeighborDiscoveryOptions();
+
+ ndo.options.clear();
+
+ //
+ // Deserialize all options
+ //
+ while (bb.hasRemaining()) {
+ byte type = bb.get();
+ if (!bb.hasRemaining()) {
+ throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
+ }
+ byte lengthField = bb.get();
+ int dataLength = lengthField * 8; // The data length field is in
+ // unit of 8 octets
+
+ // Exclude the type and length fields
+ if (dataLength < 2) {
+ break;
+ }
+ dataLength -= 2;
+
+ if (bb.remaining() < dataLength) {
+ throw new DeserializationException(BUFFER_UNDERFLOW_ERROR);
+ }
+ byte[] optionData = new byte[dataLength];
+ bb.get(optionData, 0, optionData.length);
+ ndo.addOption(type, optionData);
+ }
+
+ return ndo;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java
new file mode 100644
index 00000000..77c119a0
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborSolicitation.java
@@ -0,0 +1,192 @@
+/*
+ * 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.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.Ip6Address;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Neighbor Solicitation packet format. (RFC 4861)
+ */
+public class NeighborSolicitation extends BasePacket {
+ public static final byte HEADER_LENGTH = 20; // bytes
+
+ protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
+
+ private final NeighborDiscoveryOptions options =
+ new NeighborDiscoveryOptions();
+
+ /**
+ * Gets target address.
+ *
+ * @return the target IPv6 address
+ */
+ public byte[] getTargetAddress() {
+ return this.targetAddress;
+ }
+
+ /**
+ * Sets target address.
+ *
+ * @param targetAddress the target IPv6 address to set
+ * @return this
+ */
+ public NeighborSolicitation setTargetAddress(final byte[] targetAddress) {
+ this.targetAddress =
+ Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public NeighborSolicitation addOption(final byte type,
+ final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(0);
+ bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+ bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ ByteBuffer bb;
+ bb = ByteBuffer.wrap(this.targetAddress);
+ for (int i = 0; i < this.targetAddress.length / 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ result = prime * result + this.options.hashCode();
+ 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 NeighborSolicitation)) {
+ return false;
+ }
+ final NeighborSolicitation other = (NeighborSolicitation) obj;
+ if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
+ return false;
+ }
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for neighbor solicitation packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<NeighborSolicitation> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ NeighborSolicitation neighborSolicitation = new NeighborSolicitation();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+ bb.get(neighborSolicitation.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ neighborSolicitation.addOption(option.type(), option.data());
+ }
+ }
+
+ return neighborSolicitation;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java
new file mode 100644
index 00000000..51256d41
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/Redirect.java
@@ -0,0 +1,225 @@
+/*
+ * 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.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.Ip6Address;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Redirect packet format. (RFC 4861)
+ */
+public class Redirect extends BasePacket {
+ public static final byte HEADER_LENGTH = 36; // bytes
+
+ protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
+ protected byte[] destinationAddress = new byte[Ip6Address.BYTE_LENGTH];
+
+ private final NeighborDiscoveryOptions options =
+ new NeighborDiscoveryOptions();
+
+ /**
+ * Gets target address.
+ *
+ * @return the target IPv6 address
+ */
+ public byte[] getTargetAddress() {
+ return this.targetAddress;
+ }
+
+ /**
+ * Sets target address.
+ *
+ * @param targetAddress the target IPv6 address to set
+ * @return this
+ */
+ public Redirect setTargetAddress(final byte[] targetAddress) {
+ this.targetAddress =
+ Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets destination address.
+ *
+ * @return the destination IPv6 address
+ */
+ public byte[] getDestinationAddress() {
+ return this.destinationAddress;
+ }
+
+ /**
+ * Sets destination address.
+ *
+ * @param destinationAddress the destination IPv6 address to set
+ * @return this
+ */
+ public Redirect setDestinationAddress(final byte[] destinationAddress) {
+ this.destinationAddress =
+ Arrays.copyOfRange(destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public Redirect addOption(final byte type, final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(0);
+ bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.put(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+ bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.get(this.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5807;
+ int result = super.hashCode();
+ ByteBuffer bb;
+ bb = ByteBuffer.wrap(this.targetAddress);
+ for (int i = 0; i < this.targetAddress.length / 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ bb = ByteBuffer.wrap(this.destinationAddress);
+ for (int i = 0; i < this.destinationAddress.length / 4; i++) {
+ result = prime * result + bb.getInt();
+ }
+ result = prime * result + this.options.hashCode();
+ 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 Redirect)) {
+ return false;
+ }
+ final Redirect other = (Redirect) obj;
+ if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
+ return false;
+ }
+ if (!Arrays.equals(this.destinationAddress,
+ other.destinationAddress)) {
+ return false;
+ }
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for redirect packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<Redirect> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ Redirect redirect = new Redirect();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+
+ bb.get(redirect.targetAddress, 0, Ip6Address.BYTE_LENGTH);
+ bb.get(redirect.destinationAddress, 0, Ip6Address.BYTE_LENGTH);
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ redirect.addOption(option.type(), option.data());
+ }
+ }
+
+ return redirect;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java
new file mode 100644
index 00000000..597fc9f8
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterAdvertisement.java
@@ -0,0 +1,325 @@
+/*
+ * 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.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Router Advertisement packet format. (RFC 4861)
+ */
+public class RouterAdvertisement extends BasePacket {
+ public static final byte HEADER_LENGTH = 12; // bytes
+
+ protected byte currentHopLimit;
+ protected byte mFlag;
+ protected byte oFlag;
+ protected short routerLifetime;
+ protected int reachableTime;
+ protected int retransmitTimer;
+
+ private final NeighborDiscoveryOptions options =
+ new NeighborDiscoveryOptions();
+
+ /**
+ * Gets current hop limit.
+ *
+ * @return the current hop limit
+ */
+ public byte getCurrentHopLimit() {
+ return this.currentHopLimit;
+ }
+
+ /**
+ * Sets current hop limit.
+ *
+ * @param currentHopLimit the current hop limit to set
+ * @return this
+ */
+ public RouterAdvertisement setCurrentHopLimit(final byte currentHopLimit) {
+ this.currentHopLimit = currentHopLimit;
+ return this;
+ }
+
+ /**
+ * Gets managed address configuration flag.
+ *
+ * @return the managed address configuration flag
+ */
+ public byte getMFlag() {
+ return this.mFlag;
+ }
+
+ /**
+ * Sets managed address configuration flag.
+ *
+ * @param mFlag the managed address configuration flag to set
+ * @return this
+ */
+ public RouterAdvertisement setMFlag(final byte mFlag) {
+ this.mFlag = mFlag;
+ return this;
+ }
+
+ /**
+ * Gets other configuration flag.
+ *
+ * @return the other configuration flag
+ */
+ public byte getOFlag() {
+ return this.oFlag;
+ }
+
+ /**
+ * Sets other configuration flag.
+ *
+ * @param oFlag the other configuration flag to set
+ * @return this
+ */
+ public RouterAdvertisement setOFlag(final byte oFlag) {
+ this.oFlag = oFlag;
+ return this;
+ }
+
+ /**
+ * Gets router lifetime.
+ *
+ * @return the router lifetime
+ */
+ public short getRouterLifetime() {
+ return this.routerLifetime;
+ }
+
+ /**
+ * Sets router lifetime.
+ *
+ * @param routerLifetime the router lifetime to set
+ * @return this
+ */
+ public RouterAdvertisement setRouterLifetime(final short routerLifetime) {
+ this.routerLifetime = routerLifetime;
+ return this;
+ }
+
+ /**
+ * Gets reachable time.
+ *
+ * @return the reachable time
+ */
+ public int getReachableTime() {
+ return this.reachableTime;
+ }
+
+ /**
+ * Sets reachable time.
+ *
+ * @param reachableTime the reachable time to set
+ * @return this
+ */
+ public RouterAdvertisement setReachableTime(final int reachableTime) {
+ this.reachableTime = reachableTime;
+ return this;
+ }
+
+ /**
+ * Gets retransmission timer.
+ *
+ * @return the retransmission timer
+ */
+ public int getRetransmitTimer() {
+ return this.retransmitTimer;
+ }
+
+ /**
+ * Sets retransmission timer.
+ *
+ * @param retransmitTimer the retransmission timer to set
+ * @return this
+ */
+ public RouterAdvertisement setRetransmitTimer(final int retransmitTimer) {
+ this.retransmitTimer = retransmitTimer;
+ return this;
+ }
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public RouterAdvertisement addOption(final byte type, final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.put(this.currentHopLimit);
+ bb.put((byte) ((this.mFlag & 0x1) << 7 | (this.oFlag & 0x1) << 6));
+ bb.putShort(routerLifetime);
+ bb.putInt(reachableTime);
+ bb.putInt(retransmitTimer);
+
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ int bscratch;
+
+ this.currentHopLimit = bb.get();
+ bscratch = bb.get();
+ this.mFlag = (byte) ((bscratch >> 7) & 0x1);
+ this.oFlag = (byte) ((bscratch >> 6) & 0x1);
+ this.routerLifetime = bb.getShort();
+ this.reachableTime = bb.getInt();
+ this.retransmitTimer = bb.getInt();
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ 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.currentHopLimit;
+ result = prime * result + this.mFlag;
+ result = prime * result + this.oFlag;
+ result = prime * result + this.routerLifetime;
+ result = prime * result + this.reachableTime;
+ result = prime * result + this.retransmitTimer;
+ result = prime * result + this.options.hashCode();
+ 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 RouterAdvertisement)) {
+ return false;
+ }
+ final RouterAdvertisement other = (RouterAdvertisement) obj;
+ if (this.currentHopLimit != other.currentHopLimit) {
+ return false;
+ }
+ if (this.mFlag != other.mFlag) {
+ return false;
+ }
+ if (this.oFlag != other.oFlag) {
+ return false;
+ }
+ if (this.routerLifetime != other.routerLifetime) {
+ return false;
+ }
+ if (this.reachableTime != other.reachableTime) {
+ return false;
+ }
+ if (this.retransmitTimer != other.retransmitTimer) {
+ return false;
+ }
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for router advertisement packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<RouterAdvertisement> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ RouterAdvertisement routerAdvertisement = new RouterAdvertisement();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ int bscratch;
+
+ routerAdvertisement.currentHopLimit = bb.get();
+ bscratch = bb.get();
+ routerAdvertisement.mFlag = (byte) ((bscratch >> 7) & 0x1);
+ routerAdvertisement.oFlag = (byte) ((bscratch >> 6) & 0x1);
+ routerAdvertisement.routerLifetime = bb.getShort();
+ routerAdvertisement.reachableTime = bb.getInt();
+ routerAdvertisement.retransmitTimer = bb.getInt();
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ routerAdvertisement.addOption(option.type(), option.data());
+ }
+ }
+
+ return routerAdvertisement;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java
new file mode 100644
index 00000000..e279a404
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/RouterSolicitation.java
@@ -0,0 +1,155 @@
+/*
+ * 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.ndp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ * Implements ICMPv6 Router Solicitation packet format. (RFC 4861)
+ */
+public class RouterSolicitation extends BasePacket {
+ public static final byte HEADER_LENGTH = 4; // bytes
+
+ private final NeighborDiscoveryOptions options = new NeighborDiscoveryOptions();
+
+ /**
+ * Gets the Neighbor Discovery Protocol packet options.
+ *
+ * @return the Neighbor Discovery Protocol packet options
+ */
+ public List<NeighborDiscoveryOptions.Option> getOptions() {
+ return this.options.options();
+ }
+
+ /**
+ * Adds a Neighbor Discovery Protocol packet option.
+ *
+ * @param type the option type
+ * @param data the option data
+ * @return this
+ */
+ public RouterSolicitation addOption(final byte type, final byte[] data) {
+ this.options.addOption(type, data);
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] optionsData = null;
+ if (this.options.hasOptions()) {
+ optionsData = this.options.serialize();
+ }
+
+ int optionsLength = 0;
+ if (optionsData != null) {
+ optionsLength = optionsData.length;
+ }
+
+ final byte[] data = new byte[HEADER_LENGTH + optionsLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+
+ bb.putInt(0);
+
+ if (optionsData != null) {
+ bb.put(optionsData);
+ }
+
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+
+ this.options.deserialize(data, bb.position(),
+ bb.limit() - bb.position());
+
+ 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.options.hashCode();
+ 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 RouterSolicitation)) {
+ return false;
+ }
+ final RouterSolicitation other = (RouterSolicitation) obj;
+ if (!this.options.equals(other.options)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Deserializer function for router solicitation packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<RouterSolicitation> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, HEADER_LENGTH);
+
+ RouterSolicitation routerSolicitation = new RouterSolicitation();
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+
+ bb.getInt();
+
+ if (bb.limit() - bb.position() > 0) {
+ NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
+ .deserialize(data, bb.position(), bb.limit() - bb.position());
+
+ for (NeighborDiscoveryOptions.Option option : options.options()) {
+ routerSolicitation.addOption(option.type(), option.data());
+ }
+ }
+
+ return routerSolicitation;
+ };
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/package-info.java
new file mode 100644
index 00000000..c62b1fba
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/ndp/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Utilities for decoding and encoding packets of Neighbor Discovery Protocol
+ * for IPv6 (RFC 4861).
+ */
+package org.onlab.packet.ndp;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/package-info.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/package-info.java
new file mode 100644
index 00000000..e8e0cb5e
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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 packets of various network protocols
+ * and encapsulations.
+ */
+package org.onlab.packet;