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  }