123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- // Copyright 2012 Google, Inc. All rights reserved.
- // Copyright 2009-2011 Andreas Krennmair. All rights reserved.
- //
- // Use of this source code is governed by a BSD-style license
- // that can be found in the LICENSE file in the root of the source
- // tree.
- package layers
- import (
- "encoding/binary"
- "errors"
- "net"
- "time"
- "github.com/google/gopacket"
- )
- type IGMPType uint8
- const (
- IGMPMembershipQuery IGMPType = 0x11 // General or group specific query
- IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report
- IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report
- IGMPLeaveGroup IGMPType = 0x17 // Leave Group
- IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report
- )
- // String conversions for IGMP message types
- func (i IGMPType) String() string {
- switch i {
- case IGMPMembershipQuery:
- return "IGMP Membership Query"
- case IGMPMembershipReportV1:
- return "IGMPv1 Membership Report"
- case IGMPMembershipReportV2:
- return "IGMPv2 Membership Report"
- case IGMPMembershipReportV3:
- return "IGMPv3 Membership Report"
- case IGMPLeaveGroup:
- return "Leave Group"
- default:
- return ""
- }
- }
- type IGMPv3GroupRecordType uint8
- const (
- IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
- IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
- IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
- IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
- IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
- IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
- )
- func (i IGMPv3GroupRecordType) String() string {
- switch i {
- case IGMPIsIn:
- return "MODE_IS_INCLUDE"
- case IGMPIsEx:
- return "MODE_IS_EXCLUDE"
- case IGMPToIn:
- return "CHANGE_TO_INCLUDE_MODE"
- case IGMPToEx:
- return "CHANGE_TO_EXCLUDE_MODE"
- case IGMPAllow:
- return "ALLOW_NEW_SOURCES"
- case IGMPBlock:
- return "BLOCK_OLD_SOURCES"
- default:
- return ""
- }
- }
- // IGMP represents an IGMPv3 message.
- type IGMP struct {
- BaseLayer
- Type IGMPType
- MaxResponseTime time.Duration
- Checksum uint16
- GroupAddress net.IP
- SupressRouterProcessing bool
- RobustnessValue uint8
- IntervalTime time.Duration
- SourceAddresses []net.IP
- NumberOfGroupRecords uint16
- NumberOfSources uint16
- GroupRecords []IGMPv3GroupRecord
- Version uint8 // IGMP protocol version
- }
- // IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
- //
- // 0 1 2 3
- // 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
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Type | Max Resp Time | Checksum |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Group Address |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- type IGMPv1or2 struct {
- BaseLayer
- Type IGMPType // IGMP message type
- MaxResponseTime time.Duration // meaningful only in Membership Query messages
- Checksum uint16 // 16-bit checksum of entire ip payload
- GroupAddress net.IP // either 0 or an IP multicast address
- Version uint8
- }
- // decodeResponse dissects IGMPv1 or IGMPv2 packet.
- func (i *IGMPv1or2) decodeResponse(data []byte) error {
- if len(data) < 8 {
- return errors.New("IGMP packet too small")
- }
- i.MaxResponseTime = igmpTimeDecode(data[1])
- i.Checksum = binary.BigEndian.Uint16(data[2:4])
- i.GroupAddress = net.IP(data[4:8])
- return nil
- }
- // 0 1 2 3
- // 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
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Type = 0x22 | Reserved | Checksum |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Reserved | Number of Group Records (M) |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | |
- // . Group Record [1] .
- // | |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | |
- // . Group Record [2] .
- // | |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | |
- // . Group Record [M] .
- // | |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Record Type | Aux Data Len | Number of Sources (N) |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Multicast Address |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Source Address [1] |
- // +- -+
- // | Source Address [2] |
- // +- -+
- // | Source Address [N] |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | |
- // . Auxiliary Data .
- // | |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
- type IGMPv3GroupRecord struct {
- Type IGMPv3GroupRecordType
- AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
- NumberOfSources uint16
- MulticastAddress net.IP
- SourceAddresses []net.IP
- AuxData uint32 // NOT USED
- }
- func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
- if len(data) < 8 {
- return errors.New("IGMPv3 Membership Report too small #1")
- }
- i.Checksum = binary.BigEndian.Uint16(data[2:4])
- i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
- recordOffset := 8
- for j := 0; j < int(i.NumberOfGroupRecords); j++ {
- if len(data) < recordOffset+8 {
- return errors.New("IGMPv3 Membership Report too small #2")
- }
- var gr IGMPv3GroupRecord
- gr.Type = IGMPv3GroupRecordType(data[recordOffset])
- gr.AuxDataLen = data[recordOffset+1]
- gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
- gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
- if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
- return errors.New("IGMPv3 Membership Report too small #3")
- }
- // append source address records.
- for i := 0; i < int(gr.NumberOfSources); i++ {
- sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
- gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
- }
- i.GroupRecords = append(i.GroupRecords, gr)
- recordOffset += 8 + 4*int(gr.NumberOfSources)
- }
- return nil
- }
- // 0 1 2 3
- // 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
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Type = 0x11 | Max Resp Code | Checksum |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Group Address |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Resv |S| QRV | QQIC | Number of Sources (N) |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // | Source Address [1] |
- // +- -+
- // | Source Address [2] |
- // +- . -+
- // | Source Address [N] |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- //
- // decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
- func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
- if len(data) < 12 {
- return errors.New("IGMPv3 Membership Query too small #1")
- }
- i.MaxResponseTime = igmpTimeDecode(data[1])
- i.Checksum = binary.BigEndian.Uint16(data[2:4])
- i.SupressRouterProcessing = data[8]&0x8 != 0
- i.GroupAddress = net.IP(data[4:8])
- i.RobustnessValue = data[8] & 0x7
- i.IntervalTime = igmpTimeDecode(data[9])
- i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
- if len(data) < 12+int(i.NumberOfSources)*4 {
- return errors.New("IGMPv3 Membership Query too small #2")
- }
- for j := 0; j < int(i.NumberOfSources); j++ {
- i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
- }
- return nil
- }
- // igmpTimeDecode decodes the duration created by the given byte, using the
- // algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
- func igmpTimeDecode(t uint8) time.Duration {
- if t&0x80 == 0 {
- return time.Millisecond * 100 * time.Duration(t)
- }
- mant := (t & 0x70) >> 4
- exp := t & 0x0F
- return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
- }
- // LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
- func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }
- func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
- func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
- if len(data) < 8 {
- return errors.New("IGMP Packet too small")
- }
- i.Type = IGMPType(data[0])
- i.MaxResponseTime = igmpTimeDecode(data[1])
- i.Checksum = binary.BigEndian.Uint16(data[2:4])
- i.GroupAddress = net.IP(data[4:8])
- return nil
- }
- func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
- return gopacket.LayerTypeZero
- }
- func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
- return LayerTypeIGMP
- }
- // DecodeFromBytes decodes the given bytes into this layer.
- func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
- if len(data) < 1 {
- return errors.New("IGMP packet is too small")
- }
- // common IGMP header values between versions 1..3 of IGMP specification..
- i.Type = IGMPType(data[0])
- switch i.Type {
- case IGMPMembershipQuery:
- i.decodeIGMPv3MembershipQuery(data)
- case IGMPMembershipReportV3:
- i.decodeIGMPv3MembershipReport(data)
- default:
- return errors.New("unsupported IGMP type")
- }
- return nil
- }
- // CanDecode returns the set of layer types that this DecodingLayer can decode.
- func (i *IGMP) CanDecode() gopacket.LayerClass {
- return LayerTypeIGMP
- }
- // NextLayerType returns the layer type contained by this DecodingLayer.
- func (i *IGMP) NextLayerType() gopacket.LayerType {
- return gopacket.LayerTypeZero
- }
- // decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
- // IGMP type are performed against byte[0], logic then iniitalizes and
- // passes the appropriate struct (IGMP or IGMPv1or2) to
- // decodingLayerDecoder.
- func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
- if len(data) < 1 {
- return errors.New("IGMP packet is too small")
- }
- // byte 0 contains IGMP message type.
- switch IGMPType(data[0]) {
- case IGMPMembershipQuery:
- // IGMPv3 Membership Query payload is >= 12
- if len(data) >= 12 {
- i := &IGMP{Version: 3}
- return decodingLayerDecoder(i, data, p)
- } else if len(data) == 8 {
- i := &IGMPv1or2{}
- if data[1] == 0x00 {
- i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
- } else {
- i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
- }
- return decodingLayerDecoder(i, data, p)
- }
- case IGMPMembershipReportV3:
- i := &IGMP{Version: 3}
- return decodingLayerDecoder(i, data, p)
- case IGMPMembershipReportV1:
- i := &IGMPv1or2{Version: 1}
- return decodingLayerDecoder(i, data, p)
- case IGMPLeaveGroup, IGMPMembershipReportV2:
- // leave group and Query Report v2 used in IGMPv2 only.
- i := &IGMPv1or2{Version: 2}
- return decodingLayerDecoder(i, data, p)
- default:
- }
- return errors.New("Unable to determine IGMP type.")
- }
|