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