blob: 4dda457263f3132a05aebb014c0d678012821217 [file] [log] [blame]
Rusty Eddy1da61a22015-09-01 00:48:58 +00001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Rusty Eddy1da61a22015-09-01 00:48:58 +00003 *
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 */
16package org.onlab.packet;
17
18import java.nio.ByteBuffer;
19import static org.onlab.packet.PacketUtils.checkBufferLength;
20
21public class IGMPMembership extends IGMPGroup {
22
Jonathan Hart6ccfc5a2016-02-12 19:26:02 -080023 // TODO should be an enum
Rusty Eddy1da61a22015-09-01 00:48:58 +000024 public static final byte MODE_IS_INCLUDE = 0x1;
25 public static final byte MODE_IS_EXCLUDE = 0x2;
26 public static final byte CHANGE_TO_INCLUDE_MODE = 0x3;
27 public static final byte CHANGE_TO_EXCLUDE_MODE = 0x4;
28 public static final byte ALLOW_NEW_SOURCES = 0x5;
29 public static final byte BLOCK_OLD_SOURCES = 0x6;
30
31 private final int minGroupRecordLen = Ip4Address.BYTE_LENGTH + 4;
32
33 protected byte recordType;
34 protected byte auxDataLength = 0;
35 protected byte[] auxData;
36
37 /**
38 * Constructor initialized with a multicast group address.
39 *
40 * @param gaddr A multicast group address.
41 */
42 public IGMPMembership(Ip4Address gaddr) {
43 super(gaddr, 0);
44 }
45
46 /**
47 * Default constructor.
48 */
49 public IGMPMembership() {
50 super();
51 }
52
53 /**
Jonathan Hart6ccfc5a2016-02-12 19:26:02 -080054 * Gets the IGMP record type.
55 *
56 * @return record type
57 */
58 public byte getRecordType() {
59 return recordType;
60 }
61
62 /**
ke han08443072016-08-04 10:29:51 +080063 * Sets the IGMP record type.
64 *
65 * @param type A multicast record type, like MODE_IS_INCLUDE or MODE_IS_EXCLUDE.
66 */
67 public void setRecordType(byte type) {
68 recordType = type;
69 }
70
71
72 /**
Rusty Eddy1da61a22015-09-01 00:48:58 +000073 * Serialize this Membership Report.
74 *
75 * @param bb the ByteBuffer to write into, positioned at the next spot to be written to.
76 * @return serialized IGMP message.
77 */
78 @Override
79 public byte[] serialize(ByteBuffer bb) {
80
81 bb.put(recordType);
82 bb.put(auxDataLength); // reserved
83 bb.putShort((short) sources.size());
84 bb.put(gaddr.toOctets());
85 for (IpAddress ipaddr : sources) {
86 bb.put(ipaddr.toOctets());
87 }
88
89 if (auxDataLength > 0) {
90 bb.put(auxData);
91 }
92
93 return bb.array();
94 }
95
96 /**
97 * Deserialize the IGMP Membership report packet.
98 *
99 * @param bb the ByteBuffer wrapping the serialized message. The position of the
100 * ByteBuffer should be pointing at the head of either message type.
Ray Milkey9b36d812015-09-09 15:24:54 -0700101 * @return IGMP Group
102 * @throws DeserializationException if deserialization fails
Rusty Eddy1da61a22015-09-01 00:48:58 +0000103 */
104 public IGMPGroup deserialize(ByteBuffer bb) throws DeserializationException {
105
106 // Make sure there is enough buffer to read the header,
107 // including the number of sources
108 checkBufferLength(bb.remaining(), 0, minGroupRecordLen);
109 recordType = bb.get();
110 auxDataLength = bb.get();
111 int nsrcs = bb.getShort();
112
113 gaddr = Ip4Address.valueOf(bb.getInt());
114
alshabib64a8b9e2016-03-02 17:21:37 -0800115
Rusty Eddy1da61a22015-09-01 00:48:58 +0000116 for (; nsrcs > 0; nsrcs--) {
alshabib64a8b9e2016-03-02 17:21:37 -0800117 // Make sure we have enough buffer to hold all of these sources
118 checkBufferLength(bb.remaining(), 0, Ip4Address.BYTE_LENGTH * nsrcs);
Rusty Eddy1da61a22015-09-01 00:48:58 +0000119 Ip4Address src = Ip4Address.valueOf(bb.getInt());
120 this.sources.add(src);
121 }
122
123 if (auxDataLength > 0) {
124 auxData = new byte[auxDataLength];
125 bb.get(auxData, 0, auxDataLength);
126 }
127 return this;
128 }
129
130 /*
131 * (non-Javadoc)
132 *
133 * @see java.lang.Object#equals()
134 */
135 public boolean equals(Object obj) {
136 if (this == obj) {
137 return true;
138 }
139 if (!(obj instanceof IGMPMembership)) {
140 return false;
141 }
142 IGMPMembership other = (IGMPMembership) obj;
143
144 if (!this.gaddr.equals(other.gaddr)) {
145 return false;
146 }
147 if (this.recordType != other.recordType) {
148 return false;
149 }
150 if (this.auxDataLength != other.auxDataLength) {
151 return false;
152 }
153 if (this.sources.size() != other.sources.size()) {
154 return false;
155 }
156 // TODO: make these tolerant of order
157 if (!this.sources.equals(other.sources)) {
158 return false;
159 }
160
161 return true;
162 }
163
164 /*
165 * (non-Javadoc)
166 *
167 * @see java.lang.Object#hashCode()
168 */
169 @Override
170 public int hashCode() {
171 final int prime = 2521;
172 int result = super.hashCode();
173 result = prime * result + this.gaddr.hashCode();
174 result = prime * result + this.recordType;
175 result = prime * result + this.auxDataLength;
176 result = prime * result + this.sources.hashCode();
177 return result;
178 }
Ray Milkey9b36d812015-09-09 15:24:54 -0700179}