blob: d00841535bbcf7f5297c30d68a2e9ef0040d1e04 [file] [log] [blame]
Holger Hildebrandtfa074992020-03-27 15:42:06 +00001// 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
125// 0 1 2 3
126// 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
127// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
128// | Type = 0x22 | Reserved | Checksum |
129// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130// | Reserved | Number of Group Records (M) |
131// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
132// | |
133// . Group Record [1] .
134// | |
135// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
136// | |
137// . Group Record [2] .
138// | |
139// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
140// | |
141// . Group Record [M] .
142// | |
143// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
144
145// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146// | Record Type | Aux Data Len | Number of Sources (N) |
147// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148// | Multicast Address |
149// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
150// | Source Address [1] |
151// +- -+
152// | Source Address [2] |
153// +- -+
154// | Source Address [N] |
155// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
156// | |
157// . Auxiliary Data .
158// | |
159// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
160
161// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
162type IGMPv3GroupRecord struct {
163 Type IGMPv3GroupRecordType
164 AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
165 NumberOfSources uint16
166 MulticastAddress net.IP
167 SourceAddresses []net.IP
168 AuxData uint32 // NOT USED
169}
170
171func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
172 if len(data) < 8 {
173 return errors.New("IGMPv3 Membership Report too small #1")
174 }
175
176 i.Checksum = binary.BigEndian.Uint16(data[2:4])
177 i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
178
179 recordOffset := 8
180 for j := 0; j < int(i.NumberOfGroupRecords); j++ {
181 if len(data) < recordOffset+8 {
182 return errors.New("IGMPv3 Membership Report too small #2")
183 }
184
185 var gr IGMPv3GroupRecord
186 gr.Type = IGMPv3GroupRecordType(data[recordOffset])
187 gr.AuxDataLen = data[recordOffset+1]
188 gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
189 gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
190
191 if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
192 return errors.New("IGMPv3 Membership Report too small #3")
193 }
194
195 // append source address records.
196 for i := 0; i < int(gr.NumberOfSources); i++ {
197 sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
198 gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
199 }
200
201 i.GroupRecords = append(i.GroupRecords, gr)
202 recordOffset += 8 + 4*int(gr.NumberOfSources)
203 }
204 return nil
205}
206
207// 0 1 2 3
208// 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
209// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
210// | Type = 0x11 | Max Resp Code | Checksum |
211// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
212// | Group Address |
213// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
214// | Resv |S| QRV | QQIC | Number of Sources (N) |
215// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
216// | Source Address [1] |
217// +- -+
218// | Source Address [2] |
219// +- . -+
220// | Source Address [N] |
221// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
222//
223// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
224func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
225 if len(data) < 12 {
226 return errors.New("IGMPv3 Membership Query too small #1")
227 }
228
229 i.MaxResponseTime = igmpTimeDecode(data[1])
230 i.Checksum = binary.BigEndian.Uint16(data[2:4])
231 i.SupressRouterProcessing = data[8]&0x8 != 0
232 i.GroupAddress = net.IP(data[4:8])
233 i.RobustnessValue = data[8] & 0x7
234 i.IntervalTime = igmpTimeDecode(data[9])
235 i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
236
237 if len(data) < 12+int(i.NumberOfSources)*4 {
238 return errors.New("IGMPv3 Membership Query too small #2")
239 }
240
241 for j := 0; j < int(i.NumberOfSources); j++ {
242 i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
243 }
244
245 return nil
246}
247
248// igmpTimeDecode decodes the duration created by the given byte, using the
249// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
250func igmpTimeDecode(t uint8) time.Duration {
251 if t&0x80 == 0 {
252 return time.Millisecond * 100 * time.Duration(t)
253 }
254 mant := (t & 0x70) >> 4
255 exp := t & 0x0F
256 return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
257}
258
259// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
260func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }
261func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
262
263func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
264 if len(data) < 8 {
265 return errors.New("IGMP Packet too small")
266 }
267
268 i.Type = IGMPType(data[0])
269 i.MaxResponseTime = igmpTimeDecode(data[1])
270 i.Checksum = binary.BigEndian.Uint16(data[2:4])
271 i.GroupAddress = net.IP(data[4:8])
272
273 return nil
274}
275
276func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
277 return gopacket.LayerTypeZero
278}
279
280func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
281 return LayerTypeIGMP
282}
283
284// DecodeFromBytes decodes the given bytes into this layer.
285func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
286 if len(data) < 1 {
287 return errors.New("IGMP packet is too small")
288 }
289
290 // common IGMP header values between versions 1..3 of IGMP specification..
291 i.Type = IGMPType(data[0])
292
293 switch i.Type {
294 case IGMPMembershipQuery:
295 i.decodeIGMPv3MembershipQuery(data)
296 case IGMPMembershipReportV3:
297 i.decodeIGMPv3MembershipReport(data)
298 default:
299 return errors.New("unsupported IGMP type")
300 }
301
302 return nil
303}
304
305// CanDecode returns the set of layer types that this DecodingLayer can decode.
306func (i *IGMP) CanDecode() gopacket.LayerClass {
307 return LayerTypeIGMP
308}
309
310// NextLayerType returns the layer type contained by this DecodingLayer.
311func (i *IGMP) NextLayerType() gopacket.LayerType {
312 return gopacket.LayerTypeZero
313}
314
315// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
316// IGMP type are performed against byte[0], logic then iniitalizes and
317// passes the appropriate struct (IGMP or IGMPv1or2) to
318// decodingLayerDecoder.
319func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
320 if len(data) < 1 {
321 return errors.New("IGMP packet is too small")
322 }
323
324 // byte 0 contains IGMP message type.
325 switch IGMPType(data[0]) {
326 case IGMPMembershipQuery:
327 // IGMPv3 Membership Query payload is >= 12
328 if len(data) >= 12 {
329 i := &IGMP{Version: 3}
330 return decodingLayerDecoder(i, data, p)
331 } else if len(data) == 8 {
332 i := &IGMPv1or2{}
333 if data[1] == 0x00 {
334 i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
335 } else {
336 i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
337 }
338
339 return decodingLayerDecoder(i, data, p)
340 }
341 case IGMPMembershipReportV3:
342 i := &IGMP{Version: 3}
343 return decodingLayerDecoder(i, data, p)
344 case IGMPMembershipReportV1:
345 i := &IGMPv1or2{Version: 1}
346 return decodingLayerDecoder(i, data, p)
347 case IGMPLeaveGroup, IGMPMembershipReportV2:
348 // leave group and Query Report v2 used in IGMPv2 only.
349 i := &IGMPv1or2{Version: 2}
350 return decodingLayerDecoder(i, data, p)
351 default:
352 }
353
354 return errors.New("Unable to determine IGMP type.")
355}