blob: fce84dbad585c26295055550da56465a77364c25 [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301// Copyright 2012 Google, Inc. All rights reserved.
2// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style license
5// that can be found in the LICENSE file in the root of the source
6// tree.
7
8package layers
9
10import (
11 "encoding/binary"
12 "errors"
13 "net"
14 "time"
15
16 "github.com/google/gopacket"
17)
18
19type IGMPType uint8
20
21const (
22 IGMPMembershipQuery IGMPType = 0x11 // General or group specific query
23 IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report
24 IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report
25 IGMPLeaveGroup IGMPType = 0x17 // Leave Group
26 IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report
27)
28
29// String conversions for IGMP message types
30func (i IGMPType) String() string {
31 switch i {
32 case IGMPMembershipQuery:
33 return "IGMP Membership Query"
34 case IGMPMembershipReportV1:
35 return "IGMPv1 Membership Report"
36 case IGMPMembershipReportV2:
37 return "IGMPv2 Membership Report"
38 case IGMPMembershipReportV3:
39 return "IGMPv3 Membership Report"
40 case IGMPLeaveGroup:
41 return "Leave Group"
42 default:
43 return ""
44 }
45}
46
47type IGMPv3GroupRecordType uint8
48
49const (
50 IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
51 IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
52 IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
53 IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
54 IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
55 IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
56)
57
58func (i IGMPv3GroupRecordType) String() string {
59 switch i {
60 case IGMPIsIn:
61 return "MODE_IS_INCLUDE"
62 case IGMPIsEx:
63 return "MODE_IS_EXCLUDE"
64 case IGMPToIn:
65 return "CHANGE_TO_INCLUDE_MODE"
66 case IGMPToEx:
67 return "CHANGE_TO_EXCLUDE_MODE"
68 case IGMPAllow:
69 return "ALLOW_NEW_SOURCES"
70 case IGMPBlock:
71 return "BLOCK_OLD_SOURCES"
72 default:
73 return ""
74 }
75}
76
77// IGMP represents an IGMPv3 message.
78type IGMP struct {
79 BaseLayer
80 Type IGMPType
81 MaxResponseTime time.Duration
82 Checksum uint16
83 GroupAddress net.IP
84 SupressRouterProcessing bool
85 RobustnessValue uint8
86 IntervalTime time.Duration
87 SourceAddresses []net.IP
88 NumberOfGroupRecords uint16
89 NumberOfSources uint16
90 GroupRecords []IGMPv3GroupRecord
91 Version uint8 // IGMP protocol version
92}
93
94// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
95//
96// 0 1 2 3
97// 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
98// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99// | Type | Max Resp Time | Checksum |
100// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101// | Group Address |
102// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
103type IGMPv1or2 struct {
104 BaseLayer
105 Type IGMPType // IGMP message type
106 MaxResponseTime time.Duration // meaningful only in Membership Query messages
107 Checksum uint16 // 16-bit checksum of entire ip payload
108 GroupAddress net.IP // either 0 or an IP multicast address
109 Version uint8
110}
111
112// decodeResponse dissects IGMPv1 or IGMPv2 packet.
113func (i *IGMPv1or2) decodeResponse(data []byte) error {
114 if len(data) < 8 {
115 return errors.New("IGMP packet too small")
116 }
117
118 i.MaxResponseTime = igmpTimeDecode(data[1])
119 i.Checksum = binary.BigEndian.Uint16(data[2:4])
120 i.GroupAddress = net.IP(data[4:8])
121
122 return nil
123}
124
125func igmpchecksum(bytes []byte) uint16 {
126 // Assumed that the checksum bytes are set to zero
127 var csum uint32
128 for i := 0; i < len(bytes); i += 2 {
129 csum += uint32(bytes[i]) << 8
130 csum += uint32(bytes[i+1])
131 }
132 for {
133 // Break when sum is less or equals to 0xFFFF
134 if csum <= 65535 {
135 break
136 }
137 // Add carry to the sum
138 csum = (csum >> 16) + uint32(uint16(csum))
139 }
140 // Flip all the bits
141 return ^uint16(csum)
142}
143
144func (i *IGMPv1or2) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
145 bytes, err := b.PrependBytes(8)
146 if err != nil {
147 return err
148 }
149 // Put the packet type
150 bytes[0] = byte(i.Type)
151 bytes[1] = igmpTimeEncode(i.MaxResponseTime)
152 // Put the checksum as zero to start
153 binary.BigEndian.PutUint16(bytes[2:], 0)
154 addr, err := checkIPv4Address(i.GroupAddress)
155 if err != nil {
156 return err
157 }
158 i.GroupAddress = addr
159 copy(bytes[4:8], i.GroupAddress)
160 csum := igmpchecksum(bytes)
161 binary.BigEndian.PutUint16(bytes[2:], csum)
162 return nil
163}
164
165// 0 1 2 3
166// 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
167// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
168// | Type = 0x22 | Reserved | Checksum |
169// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
170// | Reserved | Number of Group Records (M) |
171// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
172// | |
173// . Group Record [1] .
174// | |
175// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
176// | |
177// . Group Record [2] .
178// | |
179// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
180// | |
181// . Group Record [M] .
182// | |
183// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
184
185// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
186// | Record Type | Aux Data Len | Number of Sources (N) |
187// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
188// | Multicast Address |
189// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
190// | Source Address [1] |
191// +- -+
192// | Source Address [2] |
193// +- -+
194// | Source Address [N] |
195// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
196// | |
197// . Auxiliary Data .
198// | |
199// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
200
201// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
202type IGMPv3GroupRecord struct {
203 Type IGMPv3GroupRecordType
204 AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
205 NumberOfSources uint16
206 MulticastAddress net.IP
207 SourceAddresses []net.IP
208 AuxData uint32 // NOT USED
209}
210
211func (g *IGMPv3GroupRecord) length() int {
212 return(8 + 4 * len(g.SourceAddresses))
213}
214
215func (g *IGMPv3GroupRecord) encode(b []byte) (int, error) {
216 length := g.length()
217 b[0] = byte(g.Type) // Record Type
218 b[1] = byte(0) // Aux data length
219 binary.BigEndian.PutUint16(b[2:], uint16(len(g.SourceAddresses)))
220 addr, err := checkIPv4Address(g.MulticastAddress)
221 if err != nil {
222 return 0, err
223 }
224 copy(b[4:8], addr)
225 start := 8
226 for i, src := range g.SourceAddresses {
227 addr1, err := checkIPv4Address(src)
228 if err != nil {
229 return 0, err
230 }
231 copy(b[start+i*4 : start+(i+1)*4], addr1)
232 }
233 return length, nil
234}
235
236func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
237 if len(data) < 8 {
238 return errors.New("IGMPv3 Membership Report too small #1")
239 }
240
241 i.Checksum = binary.BigEndian.Uint16(data[2:4])
242 i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
243
244 recordOffset := 8
245 for j := 0; j < int(i.NumberOfGroupRecords); j++ {
246 if len(data) < recordOffset+8 {
247 return errors.New("IGMPv3 Membership Report too small #2")
248 }
249
250 var gr IGMPv3GroupRecord
251 gr.Type = IGMPv3GroupRecordType(data[recordOffset])
252 gr.AuxDataLen = data[recordOffset+1]
253 gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
254 gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
255
256 if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
257 return errors.New("IGMPv3 Membership Report too small #3")
258 }
259
260 // append source address records.
261 for i := 0; i < int(gr.NumberOfSources); i++ {
262 sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
263 gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
264 }
265
266 i.GroupRecords = append(i.GroupRecords, gr)
267 recordOffset += 8 + 4*int(gr.NumberOfSources)
268 }
269 return nil
270}
271
272// 0 1 2 3
273// 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
274// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
275// | Type = 0x11 | Max Resp Code | Checksum |
276// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
277// | Group Address |
278// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
279// | Resv |S| QRV | QQIC | Number of Sources (N) |
280// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
281// | Source Address [1] |
282// +- -+
283// | Source Address [2] |
284// +- . -+
285// | Source Address [N] |
286// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
287//
288// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
289func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
290 if len(data) < 12 {
291 return errors.New("IGMPv3 Membership Query too small #1")
292 }
293
294 i.MaxResponseTime = igmpTimeDecode(data[1])
295 i.Checksum = binary.BigEndian.Uint16(data[2:4])
296 i.SupressRouterProcessing = data[8]&0x8 != 0
297 i.GroupAddress = net.IP(data[4:8])
298 i.RobustnessValue = data[8] & 0x7
299 i.IntervalTime = igmpTimeDecode(data[9])
300 i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
301
302 if len(data) < 12+int(i.NumberOfSources)*4 {
303 return errors.New("IGMPv3 Membership Query too small #2")
304 }
305
306 for j := 0; j < int(i.NumberOfSources); j++ {
307 i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
308 }
309
310 return nil
311}
312
313// igmpTimeDecode decodes the duration created by the given byte, using the
314// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
315func igmpTimeDecode(t uint8) time.Duration {
316 if t&0x80 == 0 {
317 return time.Millisecond * 100 * time.Duration(t)
318 }
319 mant := (t & 0x70) >> 4
320 exp := t & 0x0F
321 return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
322}
323
324func igmpTimeEncode(t time.Duration) byte {
325 decisecs := uint32(t/(100 * time.Millisecond))
326 maxexp := 7 + 3 // exp + 3, 7 from 3 bits of exp
327 maxmsb := maxexp + 4 + 1 // mant | 0x10
328 if decisecs < 127 {
329 return byte(decisecs)
330 } else {
331 for i := 31; i > 10; i-- {
332 mask := uint32(1) << uint8(i)
333 if decisecs & mask != 0 {
334 if i > maxmsb {
335 break
336 }
337 exp := byte(i - 3)
338 mant := byte(decisecs >> uint8(i - 5))
339 return byte(0x80) | exp << 4 | (mant & 0x0f)
340 }
341 }
342 }
343 return byte(127)
344}
345
346func igmpIntervalEncode(t time.Duration) byte {
347 secs := uint32(t/(time.Second))
348 maxexp := 7 + 3 // exp + 3, 7 from 3 bits of exp
349 maxmsb := maxexp + 4 + 1 // mant | 0x10
350 if secs < 127 {
351 return byte(secs)
352 } else {
353 for i := 31; i > 10; i-- {
354 mask := uint32(1) << uint8(i)
355 if secs & mask != 0 {
356 if i > maxmsb {
357 break
358 }
359 exp := byte(i - 3)
360 mant := byte(secs >> uint8(i - 5))
361 return byte(0x80) | exp << 4 | (mant & 0x0f)
362 }
363 }
364 }
365 return byte(127)
366}
367
368// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
369func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }
370func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
371
372func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
373 if len(data) < 8 {
374 return errors.New("IGMP Packet too small")
375 }
376
377 i.Type = IGMPType(data[0])
378 i.MaxResponseTime = igmpTimeDecode(data[1])
379 i.Checksum = binary.BigEndian.Uint16(data[2:4])
380 i.GroupAddress = net.IP(data[4:8])
381
382 return nil
383}
384
385func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
386 return gopacket.LayerTypeZero
387}
388
389func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
390 return LayerTypeIGMP
391}
392
393// DecodeFromBytes decodes the given bytes into this layer.
394func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
395 if len(data) < 1 {
396 return errors.New("IGMP packet is too small")
397 }
398
399 // common IGMP header values between versions 1..3 of IGMP specification..
400 i.Type = IGMPType(data[0])
401
402 switch i.Type {
403 case IGMPMembershipQuery:
404 i.decodeIGMPv3MembershipQuery(data)
405 case IGMPMembershipReportV3:
406 i.decodeIGMPv3MembershipReport(data)
407 default:
408 return errors.New("unsupported IGMP type")
409 }
410
411 return nil
412}
413
414func (i *IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
415 // Get the length of the packet and preprend bytes
416 switch i.Type {
417 case IGMPMembershipQuery:
418 return i.serializeIGMPv3MembershipQuery(b, opts)
419 case IGMPMembershipReportV3:
420 return i.serializeIGMPv3MembershipReport(b, opts)
421 default:
422 }
423 return errors.New("Unsupported IGMPv3 Message Type")
424}
425
426func (i *IGMP) serializeIGMPv3MembershipQuery(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
427 numsrcip := len(i.SourceAddresses)
428 length := 12 + (4 * numsrcip)
429 bytes, err := b.PrependBytes(length)
430 if err != nil {
431 return err
432 }
433 bytes[0] = byte(i.Type)
434 bytes[1] = igmpTimeEncode(i.MaxResponseTime)
435 // Set the checksum to 0 initially
436 binary.BigEndian.PutUint16(bytes[2:], 0)
437 addr, err := checkIPv4Address(i.GroupAddress)
438 if err != nil {
439 return err
440 }
441 i.GroupAddress = addr
442 copy(bytes[4:8], i.GroupAddress)
443 bytes[8] = 0
444 bytes[9] = igmpIntervalEncode(i.IntervalTime)
445 binary.BigEndian.PutUint16(bytes[10:], uint16(numsrcip))
446 for i, ip := range i.SourceAddresses {
447 addr1, err := checkIPv4Address(ip)
448 if err != nil {
449 return err
450 }
451 copy(bytes[12 + i*4:12 + (i+1)*4], addr1)
452 }
453 csum := igmpchecksum(bytes)
454 binary.BigEndian.PutUint16(bytes[2:], csum)
455 return nil
456}
457
458func (i *IGMP) serializeIGMPv3MembershipReport(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
459 // This is for the first two 32 bit rows in the report which is the fixed part
460 length := 8
461 for _, g := range i.GroupRecords {
462 length = length + g.length()
463 }
464 bytes, err := b.PrependBytes(length)
465 if err != nil {
466 return err
467 }
468 bytes[0] = byte(i.Type)
469 bytes[1] = byte(0)
470 // Checksum needs to go here
471 binary.BigEndian.PutUint16(bytes[2:], uint16(0))
472
473 // The reserved field and the number of IGMP group records
474 binary.BigEndian.PutUint16(bytes[4:], uint16(0))
475 binary.BigEndian.PutUint16(bytes[6:], uint16(len(i.GroupRecords)))
476 start := 8
477 for _, g := range i.GroupRecords {
478 numb, err := g.encode(bytes[start:])
479 if err != nil {
480 return err
481 }
482 start = start + numb
483 }
484 csum := igmpchecksum(bytes)
485 binary.BigEndian.PutUint16(bytes[2:], csum)
486 return nil
487}
488
489// CanDecode returns the set of layer types that this DecodingLayer can decode.
490func (i *IGMP) CanDecode() gopacket.LayerClass {
491 return LayerTypeIGMP
492}
493
494// NextLayerType returns the layer type contained by this DecodingLayer.
495func (i *IGMP) NextLayerType() gopacket.LayerType {
496 return gopacket.LayerTypeZero
497}
498
499// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
500// IGMP type are performed against byte[0], logic then iniitalizes and
501// passes the appropriate struct (IGMP or IGMPv1or2) to
502// decodingLayerDecoder.
503func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
504 if len(data) < 1 {
505 return errors.New("IGMP packet is too small")
506 }
507
508 // byte 0 contains IGMP message type.
509 switch IGMPType(data[0]) {
510 case IGMPMembershipQuery:
511 // IGMPv3 Membership Query payload is >= 12
512 if len(data) >= 12 {
513 i := &IGMP{Version: 3}
514 return decodingLayerDecoder(i, data, p)
515 } else if len(data) == 8 {
516 i := &IGMPv1or2{}
517 if data[1] == 0x00 {
518 i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
519 } else {
520 i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
521 }
522
523 return decodingLayerDecoder(i, data, p)
524 }
525 case IGMPMembershipReportV3:
526 i := &IGMP{Version: 3}
527 return decodingLayerDecoder(i, data, p)
528 case IGMPMembershipReportV1:
529 i := &IGMPv1or2{Version: 1}
530 return decodingLayerDecoder(i, data, p)
531 case IGMPLeaveGroup, IGMPMembershipReportV2:
532 // leave group and Query Report v2 used in IGMPv2 only.
533 i := &IGMPv1or2{Version: 2}
534 return decodingLayerDecoder(i, data, p)
535 default:
536 }
537
538 return errors.New("Unable to determine IGMP type.")
539}