Serialize / deserialize functions for IGMP, IGMPv3 Membership
Query and IGMPv3 Membership Report. IGMP has been added to
the IPv4 deserialization map.
Change-Id: I6d46c3771b6589f1cbd839c58521ffab94b5e230
diff --git a/utils/misc/src/main/java/org/onlab/packet/IGMP.java b/utils/misc/src/main/java/org/onlab/packet/IGMP.java
new file mode 100644
index 0000000..9d5535d
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/IGMP.java
@@ -0,0 +1,334 @@
+/*
+ * 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 recodnized";
+ 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/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java b/utils/misc/src/main/java/org/onlab/packet/IGMPGroup.java
new file mode 100644
index 0000000..70ff556
--- /dev/null
+++ b/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/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java b/utils/misc/src/main/java/org/onlab/packet/IGMPMembership.java
new file mode 100644
index 0000000..174bd6f
--- /dev/null
+++ b/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
+ * @throws DeserializationException
+ */
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java b/utils/misc/src/main/java/org/onlab/packet/IGMPQuery.java
new file mode 100644
index 0000000..378ca10
--- /dev/null
+++ b/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
+ */
+ public byte getQrv() {
+ return qrv;
+ }
+
+ /**
+ * Set the Querier Robustness Variable. Default is 2.
+ *
+ * @param qrv
+ */
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/utils/misc/src/main/java/org/onlab/packet/IPv4.java b/utils/misc/src/main/java/org/onlab/packet/IPv4.java
index 0d75245..d75b50a 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IPv4.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IPv4.java
@@ -32,6 +32,7 @@
*/
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 =
@@ -39,6 +40,7 @@
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());
}