aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java')
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/IGMP.java335
1 files changed, 335 insertions, 0 deletions
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;
+ }
+}