1 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 Internet Group Management Protocol(IGMP) packet parser/serializer
19 [RFC 1112] IGMP v1 format::
22 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
23 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24 |Version| Type | Unused | Checksum |
25 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29 [RFC 2236] IGMP v2 format::
32 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
33 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34 | Type | Max Resp Time | Checksum |
35 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39 [RFC 3376] IGMP v3 Membership Query format::
42 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
43 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44 | Type = 0x11 | Max Resp Code | Checksum |
45 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48 | Resv |S| QRV | QQIC | Number of Sources (N) |
49 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50 | Source Address [1] |
52 | Source Address [2] |
57 | Source Address [N] |
58 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 IGMP v3 Membership Report format::
63 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
64 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 | Type = 0x22 | Reserved | Checksum |
66 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67 | Reserved | Number of Group Records (M) |
68 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
92 Where each Group Record has the following internal format::
94 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
95 | Record Type | Aux Data Len | Number of Sources (N) |
96 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99 | Source Address [1] |
101 | Source Address [2] |
107 | Source Address [N] |
108 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
114 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
119 from math import trunc
121 from ryu.lib import addrconv
122 from ryu.lib import stringify
123 from ryu.lib.packet import packet_base
124 from ryu.lib.packet import packet_utils
127 IGMP_TYPE_QUERY = 0x11
128 IGMP_TYPE_REPORT_V1 = 0x12
129 IGMP_TYPE_REPORT_V2 = 0x16
130 IGMP_TYPE_LEAVE = 0x17
131 IGMP_TYPE_REPORT_V3 = 0x22
133 QUERY_RESPONSE_INTERVAL = 10.0
134 LAST_MEMBER_QUERY_INTERVAL = 1.0
136 MULTICAST_IP_ALL_HOST = '224.0.0.1'
137 MULTICAST_MAC_ALL_HOST = '01:00:5e:00:00:01'
139 # for types of IGMPv3 Report Group Records
142 CHANGE_TO_INCLUDE_MODE = 3
143 CHANGE_TO_EXCLUDE_MODE = 4
144 ALLOW_NEW_SOURCES = 5
145 BLOCK_OLD_SOURCES = 6
148 class igmp(packet_base.PacketBase):
150 Internet Group Management Protocol(IGMP, RFC 1112, RFC 2236)
151 header encoder/decoder class.
153 http://www.ietf.org/rfc/rfc1112.txt
155 http://www.ietf.org/rfc/rfc2236.txt
157 An instance has the following attributes at least.
158 Most of them are same to the on-wire counterparts but in host byte
160 __init__ takes the corresponding args in this order.
162 =============== ====================================================
163 Attribute Description
164 =============== ====================================================
165 msgtype a message type for v2, or a combination of
166 version and a message type for v1.
167 maxresp max response time in unit of 1/10 second. it is
168 meaningful only in Query Message.
169 csum a check sum value. 0 means automatically-calculate
171 address a group address value.
172 =============== ====================================================
175 _MIN_LEN = struct.calcsize(_PACK_STR)
182 def __init__(self, msgtype=IGMP_TYPE_QUERY, maxresp=0, csum=0,
184 super(igmp, self).__init__()
185 self.msgtype = msgtype
186 self.maxresp = maxresp
188 self.address = address
191 def parser(cls, buf):
192 assert cls._MIN_LEN <= len(buf)
193 (msgtype, ) = struct.unpack_from('!B', buf)
194 if (IGMP_TYPE_QUERY == msgtype and
195 igmpv3_query.MIN_LEN <= len(buf)):
196 (instance, subclass, rest,) = igmpv3_query.parser(buf)
197 elif IGMP_TYPE_REPORT_V3 == msgtype:
198 (instance, subclass, rest,) = igmpv3_report.parser(buf)
200 (msgtype, maxresp, csum, address
201 ) = struct.unpack_from(cls._PACK_STR, buf)
202 instance = cls(msgtype, maxresp, csum,
203 addrconv.ipv4.bin_to_text(address))
205 rest = buf[cls._MIN_LEN:]
206 return instance, subclass, rest
208 def serialize(self, payload, prev):
209 hdr = bytearray(struct.pack(self._PACK_STR, self.msgtype,
210 trunc(self.maxresp), self.csum,
211 addrconv.ipv4.text_to_bin(self.address)))
214 self.csum = packet_utils.checksum(hdr)
215 struct.pack_into('!H', hdr, 2, self.csum)
220 class igmpv3_query(igmp):
222 Internet Group Management Protocol(IGMP, RFC 3376)
223 Membership Query message encoder/decoder class.
225 http://www.ietf.org/rfc/rfc3376.txt
227 An instance has the following attributes at least.
228 Most of them are same to the on-wire counterparts but in host byte
230 __init__ takes the corresponding args in this order.
232 .. tabularcolumns:: |l|L|
234 =============== ====================================================
235 Attribute Description
236 =============== ====================================================
237 msgtype a message type for v3.
238 maxresp max response time in unit of 1/10 second.
239 csum a check sum value. 0 means automatically-calculate
241 address a group address value.
242 s_flg when set to 1, routers suppress the timer process.
243 qrv robustness variable for a querier.
244 qqic an interval time for a querier in unit of seconds.
245 num a number of the multicast servers.
246 srcs a list of IPv4 addresses of the multicast servers.
247 =============== ====================================================
250 _PACK_STR = '!BBH4sBBH'
251 _MIN_LEN = struct.calcsize(_PACK_STR)
262 def __init__(self, msgtype=IGMP_TYPE_QUERY, maxresp=100, csum=0,
263 address='0.0.0.0', s_flg=0, qrv=2, qqic=0, num=0,
265 super(igmpv3_query, self).__init__(
266 msgtype, maxresp, csum, address)
272 assert isinstance(srcs, list)
274 assert isinstance(src, str)
278 def parser(cls, buf):
279 (msgtype, maxresp, csum, address, s_qrv, qqic, num
280 ) = struct.unpack_from(cls._PACK_STR, buf)
281 s_flg = (s_qrv >> 3) & 0b1
283 offset = cls._MIN_LEN
285 while 0 < len(buf[offset:]) and num > len(srcs):
286 assert 4 <= len(buf[offset:])
287 (src, ) = struct.unpack_from('4s', buf, offset)
288 srcs.append(addrconv.ipv4.bin_to_text(src))
290 assert num == len(srcs)
291 return (cls(msgtype, maxresp, csum,
292 addrconv.ipv4.bin_to_text(address), s_flg, qrv,
297 def serialize(self, payload, prev):
298 s_qrv = self.s_flg << 3 | self.qrv
299 buf = bytearray(struct.pack(self._PACK_STR, self.msgtype,
300 trunc(self.maxresp), self.csum,
301 addrconv.ipv4.text_to_bin(self.address),
302 s_qrv, trunc(self.qqic), self.num))
303 for src in self.srcs:
304 buf.extend(struct.pack('4s', addrconv.ipv4.text_to_bin(src)))
306 self.num = len(self.srcs)
307 struct.pack_into('!H', buf, 10, self.num)
309 self.csum = packet_utils.checksum(buf)
310 struct.pack_into('!H', buf, 2, self.csum)
311 return six.binary_type(buf)
314 return self._MIN_LEN + len(self.srcs) * 4
317 class igmpv3_report(igmp):
319 Internet Group Management Protocol(IGMP, RFC 3376)
320 Membership Report message encoder/decoder class.
322 http://www.ietf.org/rfc/rfc3376.txt
324 An instance has the following attributes at least.
325 Most of them are same to the on-wire counterparts but in host byte
327 __init__ takes the corresponding args in this order.
329 .. tabularcolumns:: |l|L|
331 =============== ====================================================
332 Attribute Description
333 =============== ====================================================
334 msgtype a message type for v3.
335 csum a check sum value. 0 means automatically-calculate
337 record_num a number of the group records.
338 records a list of ryu.lib.packet.igmp.igmpv3_report_group.
340 =============== ====================================================
343 _PACK_STR = '!BxH2xH'
344 _MIN_LEN = struct.calcsize(_PACK_STR)
345 _class_prefixes = ['igmpv3_report_group']
347 def __init__(self, msgtype=IGMP_TYPE_REPORT_V3, csum=0, record_num=0,
349 self.msgtype = msgtype
351 self.record_num = record_num
352 records = records or []
353 assert isinstance(records, list)
354 for record in records:
355 assert isinstance(record, igmpv3_report_group)
356 self.records = records
359 def parser(cls, buf):
360 (msgtype, csum, record_num
361 ) = struct.unpack_from(cls._PACK_STR, buf)
362 offset = cls._MIN_LEN
364 while 0 < len(buf[offset:]) and record_num > len(records):
365 record = igmpv3_report_group.parser(buf[offset:])
366 records.append(record)
367 offset += len(record)
368 assert record_num == len(records)
369 return (cls(msgtype, csum, record_num, records),
373 def serialize(self, payload, prev):
374 buf = bytearray(struct.pack(self._PACK_STR, self.msgtype,
375 self.csum, self.record_num))
376 for record in self.records:
377 buf.extend(record.serialize())
378 if 0 == self.record_num:
379 self.record_num = len(self.records)
380 struct.pack_into('!H', buf, 6, self.record_num)
382 self.csum = packet_utils.checksum(buf)
383 struct.pack_into('!H', buf, 2, self.csum)
384 return six.binary_type(buf)
388 for record in self.records:
389 records_len += len(record)
390 return self._MIN_LEN + records_len
393 class igmpv3_report_group(stringify.StringifyMixin):
395 Internet Group Management Protocol(IGMP, RFC 3376)
396 Membership Report Group Record message encoder/decoder class.
398 http://www.ietf.org/rfc/rfc3376.txt
400 This is used with ryu.lib.packet.igmp.igmpv3_report.
402 An instance has the following attributes at least.
403 Most of them are same to the on-wire counterparts but in host byte
405 __init__ takes the corresponding args in this order.
407 .. tabularcolumns:: |l|L|
409 =============== ====================================================
410 Attribute Description
411 =============== ====================================================
412 type\_ a group record type for v3.
413 aux_len the length of the auxiliary data.
414 num a number of the multicast servers.
415 address a group address value.
416 srcs a list of IPv4 addresses of the multicast servers.
417 aux the auxiliary data.
418 =============== ====================================================
421 _MIN_LEN = struct.calcsize(_PACK_STR)
431 def __init__(self, type_=0, aux_len=0, num=0, address='0.0.0.0',
432 srcs=None, aux=None):
434 self.aux_len = aux_len
436 self.address = address
438 assert isinstance(srcs, list)
440 assert isinstance(src, str)
445 def parser(cls, buf):
446 (type_, aux_len, num, address
447 ) = struct.unpack_from(cls._PACK_STR, buf)
448 offset = cls._MIN_LEN
450 while 0 < len(buf[offset:]) and num > len(srcs):
451 assert 4 <= len(buf[offset:])
452 (src, ) = struct.unpack_from('4s', buf, offset)
453 srcs.append(addrconv.ipv4.bin_to_text(src))
455 assert num == len(srcs)
458 (aux, ) = struct.unpack_from('%ds' % (aux_len * 4), buf, offset)
459 return cls(type_, aux_len, num,
460 addrconv.ipv4.bin_to_text(address), srcs, aux)
463 buf = bytearray(struct.pack(self._PACK_STR, self.type_,
464 self.aux_len, self.num,
465 addrconv.ipv4.text_to_bin(self.address)))
466 for src in self.srcs:
467 buf.extend(struct.pack('4s', addrconv.ipv4.text_to_bin(src)))
469 self.num = len(self.srcs)
470 struct.pack_into('!H', buf, 2, self.num)
471 if self.aux is not None:
472 mod = len(self.aux) % 4
474 self.aux += bytearray(4 - mod)
475 self.aux = six.binary_type(self.aux)
477 if 0 == self.aux_len:
478 self.aux_len = len(self.aux) // 4
479 struct.pack_into('!B', buf, 1, self.aux_len)
480 return six.binary_type(buf)
483 return self._MIN_LEN + len(self.srcs) * 4 + self.aux_len * 4