Rusty Eddy
Committed by Gerrit Code Review

Serialize / deserialize functions for IGMP, IGMPv3 Membership

Query and IGMPv3 Membership Report.  IGMP has been added to
the IPv4 deserialization map.

Change-Id: I6d46c3771b6589f1cbd839c58521ffab94b5e230
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.packet;
17 +
18 +import java.nio.ByteBuffer;
19 +import java.util.ArrayList;
20 +import java.util.HashMap;
21 +import java.util.List;
22 +import java.util.Map;
23 +import org.slf4j.Logger;
24 +
25 +import static org.slf4j.LoggerFactory.getLogger;
26 +import static com.google.common.base.Preconditions.checkNotNull;
27 +import static org.onlab.packet.PacketUtils.checkInput;
28 +
29 +
30 +/**
31 + * Implements IGMP control packet format.
32 + */
33 +public class IGMP extends BasePacket {
34 + private final Logger log = getLogger(getClass());
35 +
36 + public static final byte TYPE_IGMPV3_MEMBERSHIP_QUERY = 0x11;
37 + public static final byte TYPE_IGMPV1_MEMBERSHIP_REPORT = 0x12;
38 + public static final byte TYPE_IGMPV2_MEMBERSHIP_REPORT = 0x16;
39 + public static final byte TYPE_IGMPV2_LEAVE_GROUP = 0x17;
40 + public static final byte TYPE_IGMPV3_MEMBERSHIP_REPORT = 0x22;
41 + public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP = new HashMap<>();
42 +
43 + public static final int MINIMUM_HEADER_LEN = 12;
44 +
45 + List<IGMPGroup> groups = new ArrayList<>();
46 +
47 + // Fields contained in the IGMP header
48 + private byte igmpType;
49 + private byte resField = 0;
50 + private short checksum = 0;
51 +
52 + private byte[] unsupportTypeData;
53 +
54 + public IGMP() {
55 + }
56 +
57 + /**
58 + * Get the IGMP message type.
59 + *
60 + * @return the IGMP message type
61 + */
62 + public byte getIgmpType() {
63 + return igmpType;
64 + }
65 +
66 + /**
67 + * Set the IGMP message type.
68 + *
69 + * @param msgType IGMP message type
70 + */
71 + public void setIgmpType(byte msgType) {
72 + igmpType = msgType;
73 + }
74 +
75 + /**
76 + * Get the checksum of this message.
77 + *
78 + * @return the checksum
79 + */
80 + public short getChecksum() {
81 + return checksum;
82 + }
83 +
84 + /**
85 + * get the Max Resp Code.
86 + *
87 + * @return The Maximum Time allowed before before sending a responding report.
88 + */
89 + public byte getMaxRespField() {
90 + return resField;
91 + }
92 +
93 + /**
94 + * Set the Max Resp Code.
95 + *
96 + * @param respCode the Maximum Response Code.
97 + */
98 + public void setMaxRespCode(byte respCode) {
99 + if (igmpType != IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY) {
100 + log.debug("Requesting the max response code for an incorrect field: ");
101 + }
102 + this.resField = respCode;
103 + }
104 +
105 + /**
106 + * Get the list of IGMPGroups. The group objects will be either IGMPQuery or IGMPMembership
107 + * depending on the IGMP message type. For IGMP Query, the groups list should only be
108 + * one group.
109 + *
110 + * @return The list of IGMP groups.
111 + */
112 + public List<IGMPGroup> getGroups() {
113 + return groups;
114 + }
115 +
116 + /**
117 + * Add a multicast group to this IGMP message.
118 + *
119 + * @param group the IGMPGroup will be IGMPQuery or IGMPMembership depending on the message type.
120 + * @return true if group was valid and added, false otherwise.
121 + */
122 + public boolean addGroup(IGMPGroup group) {
123 + checkNotNull(group);
124 + switch (this.igmpType) {
125 + case TYPE_IGMPV3_MEMBERSHIP_QUERY:
126 + if (group instanceof IGMPMembership) {
127 + return false;
128 + }
129 +
130 + if (group.sources.size() > 1) {
131 + return false;
132 + }
133 + break;
134 +
135 + case TYPE_IGMPV3_MEMBERSHIP_REPORT:
136 + if (group instanceof IGMPMembership) {
137 + return false;
138 + }
139 + break;
140 +
141 + default:
142 + log.debug("Warning no IGMP message type has been set");
143 + }
144 +
145 + this.groups.add(group);
146 + return true;
147 + }
148 +
149 + /**
150 + * Serialize this IGMP packet. This will take care
151 + * of serializing IGMPv3 Queries and IGMPv3 Membership
152 + * Reports.
153 + *
154 + * @return the serialized IGMP message
155 + */
156 + @Override
157 + public byte[] serialize() {
158 + byte [] data = new byte[8915];
159 +
160 + ByteBuffer bb = ByteBuffer.wrap(data);
161 + bb.put(this.getIgmpType());
162 +
163 + // reserved or max resp code depending on type.
164 + bb.put(this.resField);
165 +
166 + // Must calculate checksum
167 + bb.putShort((short) 0);
168 +
169 + switch (this.igmpType) {
170 +
171 + case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
172 + // reserved
173 + bb.putShort((short) 0);
174 + // Number of groups
175 + bb.putShort((short) groups.size());
176 + // Fall through
177 +
178 + case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
179 +
180 + for (IGMPGroup grp : groups) {
181 + grp.serialize(bb);
182 + }
183 + break;
184 +
185 + default:
186 + bb.put(this.unsupportTypeData);
187 + break;
188 + }
189 +
190 + int size = bb.position();
191 + bb.position(0);
192 + byte [] rdata = new byte[size];
193 + bb.get(rdata, 0, size);
194 + return rdata;
195 + }
196 +
197 + /**
198 + * Deserialize an IGMP message.
199 + *
200 + * @param data bytes to deserialize
201 + * @param offset offset to start deserializing from
202 + * @param length length of the data to deserialize
203 + * @return populated IGMP object
204 + */
205 + @Override
206 + public IPacket deserialize(final byte[] data, final int offset,
207 + final int length) {
208 +
209 + IGMP igmp = new IGMP();
210 + try {
211 + igmp = IGMP.deserializer().deserialize(data, offset, length);
212 + } catch (DeserializationException e) {
213 + log.error(e.getStackTrace().toString());
214 + return this;
215 + }
216 + this.igmpType = igmp.igmpType;
217 + this.resField = igmp.resField;
218 + this.checksum = igmp.checksum;
219 + this.groups = igmp.groups;
220 + return this;
221 + }
222 +
223 + /**
224 + * Deserializer function for IPv4 packets.
225 + *
226 + * @return deserializer function
227 + */
228 + public static Deserializer<IGMP> deserializer() {
229 + return (data, offset, length) -> {
230 + checkInput(data, offset, length, MINIMUM_HEADER_LEN);
231 +
232 + IGMP igmp = new IGMP();
233 +
234 + ByteBuffer bb = ByteBuffer.wrap(data);
235 + igmp.igmpType = bb.get();
236 + igmp.resField = bb.get();
237 + igmp.checksum = bb.getShort();
238 + int len = MINIMUM_HEADER_LEN;
239 + String msg;
240 +
241 + switch (igmp.igmpType) {
242 +
243 + case TYPE_IGMPV3_MEMBERSHIP_QUERY:
244 + IGMPQuery qgroup = new IGMPQuery();
245 + qgroup.deserialize(bb);
246 + igmp.groups.add(qgroup);
247 + break;
248 +
249 + case TYPE_IGMPV3_MEMBERSHIP_REPORT:
250 + bb.getShort(); // Ignore resvd
251 + int ngrps = bb.getShort();
252 +
253 + for (; ngrps > 0; ngrps--) {
254 + IGMPMembership mgroup = new IGMPMembership();
255 + mgroup.deserialize(bb);
256 + igmp.groups.add(mgroup);
257 + }
258 + break;
259 +
260 + /*
261 + * NOTE: according to the IGMPv3 spec. These previous IGMP type fields
262 + * must be supported. At this time we are going to <b>assume</b> we run
263 + * in a modern network where all devices are IGMPv3 capable.
264 + */
265 + case TYPE_IGMPV1_MEMBERSHIP_REPORT:
266 + case TYPE_IGMPV2_MEMBERSHIP_REPORT:
267 + case TYPE_IGMPV2_LEAVE_GROUP:
268 + igmp.unsupportTypeData = bb.array(); // Is this the entire array?
269 + msg = "IGMP message type: " + igmp.igmpType + " is not supported";
270 + igmp.log.debug(msg);
271 + break;
272 +
273 + default:
274 + msg = "IGMP message type: " + igmp.igmpType + " is not recodnized";
275 + igmp.log.debug(msg);
276 + break;
277 + }
278 + return igmp;
279 + };
280 + }
281 +
282 + /*
283 + * (non-Javadoc)
284 + *
285 + * @see java.lang.Object#equals(java.lang.Object)
286 + */
287 + @Override
288 + public boolean equals(final Object obj) {
289 + if (this == obj) {
290 + return true;
291 + }
292 + if (!super.equals(obj)) {
293 + return false;
294 + }
295 + if (!(obj instanceof IGMP)) {
296 + return false;
297 + }
298 + final IGMP other = (IGMP) obj;
299 + if (this.igmpType != other.igmpType) {
300 + return false;
301 + }
302 + if (this.resField != other.resField) {
303 + return false;
304 + }
305 + if (this.checksum != other.checksum) {
306 + return false;
307 + }
308 + if (this.groups.size() != other.groups.size()) {
309 + return false;
310 + }
311 + // TODO: equals should be true regardless of order.
312 + if (!groups.equals(other.groups)) {
313 + return false;
314 + }
315 + return true;
316 + }
317 +
318 + /*
319 + * (non-Javadoc)
320 + *
321 + * @see java.lang.Object#hashCode()
322 + */
323 + @Override
324 + public int hashCode() {
325 + final int prime = 2521;
326 + int result = super.hashCode();
327 + result = prime * result + this.igmpType;
328 + result = prime * result + this.groups.size();
329 + result = prime * result + this.resField;
330 + result = prime * result + this.checksum;
331 + result = prime * result + this.groups.hashCode();
332 + return result;
333 + }
334 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.packet;
17 +
18 +import java.nio.ByteBuffer;
19 +import java.util.ArrayList;
20 +import java.util.List;
21 +
22 +/**
23 + * A class to represent Groups for membership query and reports.
24 + */
25 +public abstract class IGMPGroup {
26 +
27 + protected int auxInfo;
28 + protected IpAddress gaddr;
29 + protected List<IpAddress> sources = new ArrayList<>();
30 +
31 + public IGMPGroup() {
32 + }
33 +
34 + /**
35 + * Initialize this object with a multicast group address and additional info.
36 + *
37 + * @param gaddr: the multicast group address for this message type.
38 + * @param auxInfo: additional info potentially used by IGMPQuery
39 + */
40 + public IGMPGroup(IpAddress gaddr, int auxInfo) {
41 + this.gaddr = gaddr;
42 + this.auxInfo = auxInfo;
43 + }
44 +
45 + /**
46 + * Get the multicast group address.
47 + *
48 + * @return the group address
49 + */
50 + public IpAddress getGaddr() {
51 + return this.gaddr;
52 + }
53 +
54 + /**
55 + * Get the auxillary info.
56 + *
57 + * @return the auxillary info
58 + */
59 + public int getAuxInfo() {
60 + return this.auxInfo;
61 + }
62 +
63 + /**
64 + * Add a unicast source address to this message.
65 + *
66 + * @param saddr IPv4 unicast source address
67 + */
68 + public void addSource(IpAddress saddr) {
69 + sources.add(saddr);
70 + }
71 +
72 + /**
73 + * Return the list of source addresses.
74 + *
75 + * @return list of source addresses
76 + */
77 + public List<IpAddress> getSources() {
78 + return sources;
79 + }
80 +
81 + /**
82 + * Deserialize an IGMPQuery or IGMPMembership message.
83 + *
84 + * @param bb the ByteBuffer wrapping the serialized message. The position of the
85 + * ByteBuffer should be pointing at the head of either message type.
86 + * @return An object populated with the respective IGMPGroup subclass
87 + * @throws DeserializationException in case deserialization goes wrong
88 + */
89 + public abstract IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException;
90 +
91 + /**
92 + * Serialize the IGMPGroup subclass.
93 + *
94 + * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
95 + * @return The serialized message
96 + */
97 + public abstract byte[] serialize(ByteBuffer bb);
98 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.packet;
17 +
18 +import java.nio.ByteBuffer;
19 +import static org.onlab.packet.PacketUtils.checkBufferLength;
20 +
21 +public class IGMPMembership extends IGMPGroup {
22 +
23 + public static final byte MODE_IS_INCLUDE = 0x1;
24 + public static final byte MODE_IS_EXCLUDE = 0x2;
25 + public static final byte CHANGE_TO_INCLUDE_MODE = 0x3;
26 + public static final byte CHANGE_TO_EXCLUDE_MODE = 0x4;
27 + public static final byte ALLOW_NEW_SOURCES = 0x5;
28 + public static final byte BLOCK_OLD_SOURCES = 0x6;
29 +
30 + private final int minGroupRecordLen = Ip4Address.BYTE_LENGTH + 4;
31 +
32 + protected byte recordType;
33 + protected byte auxDataLength = 0;
34 + protected byte[] auxData;
35 +
36 + /**
37 + * Constructor initialized with a multicast group address.
38 + *
39 + * @param gaddr A multicast group address.
40 + */
41 + public IGMPMembership(Ip4Address gaddr) {
42 + super(gaddr, 0);
43 + }
44 +
45 + /**
46 + * Default constructor.
47 + */
48 + public IGMPMembership() {
49 + super();
50 + }
51 +
52 + /**
53 + * Serialize this Membership Report.
54 + *
55 + * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
56 + * @return serialized IGMP message.
57 + */
58 + @Override
59 + public byte[] serialize(ByteBuffer bb) {
60 +
61 + bb.put(recordType);
62 + bb.put(auxDataLength); // reserved
63 + bb.putShort((short) sources.size());
64 + bb.put(gaddr.toOctets());
65 + for (IpAddress ipaddr : sources) {
66 + bb.put(ipaddr.toOctets());
67 + }
68 +
69 + if (auxDataLength > 0) {
70 + bb.put(auxData);
71 + }
72 +
73 + return bb.array();
74 + }
75 +
76 + /**
77 + * Deserialize the IGMP Membership report packet.
78 + *
79 + * @param bb the ByteBuffer wrapping the serialized message. The position of the
80 + * ByteBuffer should be pointing at the head of either message type.
81 + * @return
82 + * @throws DeserializationException
83 + */
84 + public IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException {
85 +
86 + // Make sure there is enough buffer to read the header,
87 + // including the number of sources
88 + checkBufferLength(bb.remaining(), 0, minGroupRecordLen);
89 + recordType = bb.get();
90 + auxDataLength = bb.get();
91 + int nsrcs = bb.getShort();
92 +
93 + gaddr = Ip4Address.valueOf(bb.getInt());
94 +
95 + // Make sure we have enough buffer to hold all of these sources
96 + checkBufferLength(bb.remaining(), 0, Ip4Address.BYTE_LENGTH * nsrcs);
97 + for (; nsrcs > 0; nsrcs--) {
98 + Ip4Address src = Ip4Address.valueOf(bb.getInt());
99 + this.sources.add(src);
100 + }
101 +
102 + if (auxDataLength > 0) {
103 + auxData = new byte[auxDataLength];
104 + bb.get(auxData, 0, auxDataLength);
105 + }
106 + return this;
107 + }
108 +
109 + /*
110 + * (non-Javadoc)
111 + *
112 + * @see java.lang.Object#equals()
113 + */
114 + public boolean equals(Object obj) {
115 + if (this == obj) {
116 + return true;
117 + }
118 + if (!(obj instanceof IGMPMembership)) {
119 + return false;
120 + }
121 + IGMPMembership other = (IGMPMembership) obj;
122 +
123 + if (!this.gaddr.equals(other.gaddr)) {
124 + return false;
125 + }
126 + if (this.recordType != other.recordType) {
127 + return false;
128 + }
129 + if (this.auxDataLength != other.auxDataLength) {
130 + return false;
131 + }
132 + if (this.sources.size() != other.sources.size()) {
133 + return false;
134 + }
135 + // TODO: make these tolerant of order
136 + if (!this.sources.equals(other.sources)) {
137 + return false;
138 + }
139 +
140 + return true;
141 + }
142 +
143 + /*
144 + * (non-Javadoc)
145 + *
146 + * @see java.lang.Object#hashCode()
147 + */
148 + @Override
149 + public int hashCode() {
150 + final int prime = 2521;
151 + int result = super.hashCode();
152 + result = prime * result + this.gaddr.hashCode();
153 + result = prime * result + this.recordType;
154 + result = prime * result + this.auxDataLength;
155 + result = prime * result + this.sources.hashCode();
156 + return result;
157 + }
158 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.packet;
17 +
18 +import java.nio.ByteBuffer;
19 +
20 +public class IGMPQuery extends IGMPGroup {
21 +
22 + // Bits and bytes after the group address
23 + private byte resv = 0;
24 + private boolean sbit = false;
25 + private byte qrv = 2;
26 + private byte qqic = 0x7d;
27 +
28 + /**
29 + * Create IGMP Query message.
30 + *
31 + * @param gaddr initiaze with a group address.
32 + * @param auxInfo auxillary info.
33 + */
34 + public IGMPQuery(IpAddress gaddr, int auxInfo) {
35 + super(gaddr, auxInfo);
36 + }
37 +
38 + /**
39 + * Create IGMP Query message.
40 + */
41 + public IGMPQuery() {
42 + super();
43 + }
44 +
45 + /**
46 + * Is the S flag set? Telling adjacent routers to suppress normal timer updates.
47 + *
48 + * @return true if the flag is set, false otherwise
49 + */
50 + public boolean isSbit() {
51 + return sbit;
52 + }
53 +
54 + /**
55 + * Set the S flag. Default is false.
56 + *
57 + * @param sbit true or false
58 + */
59 + public void setSbit(boolean sbit) {
60 + this.sbit = sbit;
61 + }
62 +
63 + /**
64 + * Get the Querier Robustness Variable.
65 + *
66 + * @return
67 + */
68 + public byte getQrv() {
69 + return qrv;
70 + }
71 +
72 + /**
73 + * Set the Querier Robustness Variable. Default is 2.
74 + *
75 + * @param qrv
76 + */
77 + public void setQrv(byte qrv) {
78 + this.qrv = qrv;
79 + }
80 +
81 + /**
82 + * Get the reserved field. Should be zero, but ignored regardless of it's value.
83 + *
84 + * @return the reserved field.
85 + */
86 + public byte getResv() {
87 + return resv;
88 + }
89 +
90 + /**
91 + * Set the reserved field. Should be 0 and ignored by receivers.
92 + *
93 + * @param resv the reserved field.
94 + */
95 + public void setResv(byte resv) {
96 + this.resv = resv;
97 + }
98 +
99 + /**
100 + * Serialize this IGMPQuery.
101 + *
102 + * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
103 + * @return the serialized message
104 + */
105 + @Override
106 + public byte[] serialize(ByteBuffer bb) {
107 +
108 + bb.put(gaddr.toOctets());
109 + byte fld = (byte) (0x7 & qrv);
110 + bb.put(fld);
111 + bb.put(qqic);
112 + bb.putShort((short) sources.size());
113 + for (IpAddress ipaddr : sources) {
114 + bb.put(ipaddr.toOctets());
115 + }
116 + return bb.array();
117 + }
118 +
119 + /**
120 + * Deserialize the IGMP Query group structure.
121 + *
122 + * @param bb ByteBuffer pointing at the IGMP Query group address
123 + * @return the IGMP Group object
124 + */
125 + public IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException {
126 +
127 + gaddr = Ip4Address.valueOf(bb.getInt());
128 + byte fld = bb.get();
129 +
130 + // Just ignore the reserved bits
131 + resv = 0;
132 + this.sbit = ((fld & 0x8) == 0x8);
133 + qrv = (byte) (fld & 0x7);
134 +
135 + // QQIC field
136 + qqic = bb.get();
137 +
138 + // Get the number of sources.
139 + short nsrcs = bb.getShort();
140 +
141 + // Do a sanity check on the amount of space we have in our buffer.
142 + int lengthNeeded = (Ip4Address.BYTE_LENGTH * nsrcs);
143 + PacketUtils.checkHeaderLength(bb.remaining(), lengthNeeded);
144 +
145 + for (; nsrcs > 0; nsrcs--) {
146 + Ip4Address ipaddr = Ip4Address.valueOf(bb.getInt());
147 + this.sources.add(ipaddr);
148 + }
149 + return this;
150 + }
151 +
152 + /*
153 + * (non-Javadoc)
154 + *
155 + * @see java.lang.Object#equals()
156 + */
157 + public boolean equals(Object obj) {
158 + if (this == obj) {
159 + return true;
160 + }
161 + if (!(obj instanceof IGMPQuery)) {
162 + return false;
163 + }
164 + IGMPQuery other = (IGMPQuery) obj;
165 +
166 + if (this.sbit != other.sbit) {
167 + return false;
168 + }
169 + if (this.qrv != other.qrv) {
170 + return false;
171 + }
172 + if (this.qqic != other.qqic) {
173 + return false;
174 + }
175 + if (this.sources.size() != other.sources.size()) {
176 + return false;
177 + }
178 +
179 + // TODO: make these tolerant of order
180 + if (!this.sources.equals(other.sources)) {
181 + return false;
182 + }
183 +
184 + return true;
185 + }
186 +
187 + /*
188 + * (non-Javadoc)
189 + *
190 + * @see java.lang.Object#hashCode()
191 + */
192 + @Override
193 + public int hashCode() {
194 + final int prime = 2521;
195 + int result = super.hashCode();
196 + result = prime * result + this.gaddr.hashCode();
197 + result = prime * result + this.qqic;
198 + result = prime * result + this.qrv;
199 + result = prime * result + this.sources.hashCode();
200 + return result;
201 + }
202 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -32,6 +32,7 @@ import static org.onlab.packet.PacketUtils.*; ...@@ -32,6 +32,7 @@ import static org.onlab.packet.PacketUtils.*;
32 */ 32 */
33 public class IPv4 extends BasePacket { 33 public class IPv4 extends BasePacket {
34 public static final byte PROTOCOL_ICMP = 0x1; 34 public static final byte PROTOCOL_ICMP = 0x1;
35 + public static final byte PROTOCOL_IGMP = 0x2;
35 public static final byte PROTOCOL_TCP = 0x6; 36 public static final byte PROTOCOL_TCP = 0x6;
36 public static final byte PROTOCOL_UDP = 0x11; 37 public static final byte PROTOCOL_UDP = 0x11;
37 public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP = 38 public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
...@@ -39,6 +40,7 @@ public class IPv4 extends BasePacket { ...@@ -39,6 +40,7 @@ public class IPv4 extends BasePacket {
39 40
40 static { 41 static {
41 IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_ICMP, ICMP.deserializer()); 42 IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_ICMP, ICMP.deserializer());
43 + IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_IGMP, IGMP.deserializer());
42 IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_TCP, TCP.deserializer()); 44 IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_TCP, TCP.deserializer());
43 IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_UDP, UDP.deserializer()); 45 IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_UDP, UDP.deserializer());
44 } 46 }
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.packet;
17 +
18 +import org.junit.Before;
19 +import org.junit.Test;
20 +
21 +import static junit.framework.Assert.assertTrue;
22 +
23 +/**
24 + * Unit tests for IGMP class.
25 + */
26 +public class IGMPTest {
27 + private Deserializer<IGMP> deserializer;
28 +
29 + private IGMP igmpQuery;
30 + private IGMP igmpMembership;
31 +
32 + private Ip4Address gaddr1;
33 + private Ip4Address gaddr2;
34 + private Ip4Address saddr1;
35 + private Ip4Address saddr2;
36 +
37 + @Before
38 + public void setUp() throws Exception {
39 + gaddr1 = Ip4Address.valueOf(0xe1010101);
40 + gaddr2 = Ip4Address.valueOf(0xe2020202);
41 + saddr1 = Ip4Address.valueOf(0x0a010101);
42 + saddr2 = Ip4Address.valueOf(0x0b020202);
43 +
44 + deserializer = IGMP.deserializer();
45 +
46 + // Create an IGMP Query object
47 + igmpQuery = new IGMP();
48 + igmpQuery.setIgmpType(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY);
49 + igmpQuery.setMaxRespCode((byte) 0x7f);
50 + IGMPQuery q = new IGMPQuery(gaddr1, (byte) 0x7f);
51 + q.addSource(saddr1);
52 + q.addSource(saddr2);
53 + q.setSbit(false);
54 + igmpQuery.groups.add(q);
55 +
56 + // Create an IGMP Membership Object
57 + igmpMembership = new IGMP();
58 + igmpMembership.setIgmpType(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT);
59 + IGMPMembership g1 = new IGMPMembership(gaddr1);
60 + g1.addSource(saddr1);
61 + g1.addSource(saddr2);
62 + igmpMembership.groups.add(g1);
63 + IGMPMembership g2 = new IGMPMembership(gaddr2);
64 + g2.addSource(saddr1);
65 + g2.addSource(saddr2);
66 + igmpMembership.groups.add(g2);
67 + }
68 +
69 + @Test
70 + public void testDeserializeBadInput() throws Exception {
71 + PacketTestUtils.testDeserializeBadInput(deserializer);
72 + }
73 +
74 + @Test
75 + public void testDeserializeTruncated() throws Exception {
76 + byte [] bits = igmpQuery.serialize();
77 + PacketTestUtils.testDeserializeTruncated(deserializer, bits);
78 +
79 + bits = igmpMembership.serialize();
80 + PacketTestUtils.testDeserializeTruncated(deserializer, bits);
81 + }
82 +
83 + @Test
84 + public void testDeserializeQuery() throws Exception {
85 + byte [] data = igmpQuery.serialize();
86 + IGMP igmp = deserializer.deserialize(data, 0, data.length);
87 + assertTrue(igmp.equals(igmpQuery));
88 + }
89 +
90 + @Test
91 + public void testDeserializeMembership() throws Exception {
92 + byte [] data = igmpMembership.serialize();
93 + IGMP igmp = deserializer.deserialize(data, 0, data.length);
94 + assertTrue(igmp.equals(igmpMembership));
95 + }
96 +}