Skip to content

Igmp serialize #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 78 additions & 7 deletions layers/igmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ type IGMP struct {

// 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
// 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 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Expand Down Expand Up @@ -204,8 +205,9 @@ func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
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
// 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 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Expand Down Expand Up @@ -251,9 +253,34 @@ 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))
exp := (t & 0x70) >> 4
mant := t & 0x0F
return time.Millisecond * 100 * time.Duration((uint64(mant)|0x10)<<(exp+3))
}

func igmpTimeEncode(t time.Duration) uint8 {
// in units of 1/10 second
tm := t.Milliseconds() / 100
if t < 128 {
return uint8(tm)
}
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |1| exp | mant |
// +-+-+-+-+-+-+-+-+
// Max Resp Time = (mant | 0x10) << (exp + 3)
var expPlus3, mant, r uint8
// range of 3 bit integer plus 3
for expPlus3 = 3; expPlus3 < 11; expPlus3++ {
if (tm>>expPlus3)&0x1f == (tm >> expPlus3) {
break
}
}

mant = uint8((tm >> expPlus3) & 0x0f)
r = ((expPlus3-3)|0x08)<<4 + mant&0x0f

return r
}

// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
Expand All @@ -273,6 +300,50 @@ func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) err
return nil
}

func (i *IGMPv1or2) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {

bytes, err := b.PrependBytes(8)
if err != nil {
return err
}

bytes[0] = byte(i.Type)
bytes[1] = igmpTimeEncode(i.MaxResponseTime)
copy(bytes[4:8], i.GroupAddress)
if opts.ComputeChecksums {
csum := i.checksum(b.Bytes())
if err != nil {
return err
}
i.Checksum = csum
}
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
return nil
}

func (i *IGMPv1or2) checksum(bytes []byte) uint16 {
// Clear checksum bytes
bytes[2] = 0
bytes[3] = 0

// Compute checksum
var csum uint32
for i := 0; i < len(bytes); i += 2 {
csum += uint32(bytes[i]) << 8
csum += uint32(bytes[i+1])
}
for {
// Break when sum is less or equals to 0xFFFF
if csum <= 65535 {
break
}
// Add carry to the sum
csum = (csum >> 16) + uint32(uint16(csum))
}
// Flip all the bits
return ^uint16(csum)
}

func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypeZero
}
Expand Down
104 changes: 79 additions & 25 deletions layers/igmp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@
package layers

import (
"reflect"
"testing"

"github.com/google/gopacket"
)

// igmpv1MembershipReportPacket is the packet:
// 02:45:36.033916 IP 10.60.0.132 > 224.0.1.60: igmp v1 report 224.0.1.60
// 0x0000: 0100 5e00 013c 0030 c1bf 5755 0800 4500 ..^..<.0..WU..E.
// 0x0010: 001c 6a7f 0000 0102 6365 0a3c 0084 e000 ..j.....ce.<....
// 0x0020: 013c 1200 0cc3 e000 013c 0000 0000 0000 .<.......<......
// 0x0030: ffff ffff ffff 0452 0000 0000 .......R....
//
// 02:45:36.033916 IP 10.60.0.132 > 224.0.1.60: igmp v1 report 224.0.1.60
// 0x0000: 0100 5e00 013c 0030 c1bf 5755 0800 4500 ..^..<.0..WU..E.
// 0x0010: 001c 6a7f 0000 0102 6365 0a3c 0084 e000 ..j.....ce.<....
// 0x0020: 013c 1200 0cc3 e000 013c 0000 0000 0000 .<.......<......
// 0x0030: ffff ffff ffff 0452 0000 0000 .......R....
var igmpv1MembershipReportPacket = []byte{
0x01, 0x00, 0x5e, 0x00, 0x01, 0x3c, 0x00, 0x30, 0xc1, 0xbf, 0x57, 0x55, 0x08, 0x00, 0x45, 0x00,
0x00, 0x1c, 0x6a, 0x7f, 0x00, 0x00, 0x01, 0x02, 0x63, 0x65, 0x0a, 0x3c, 0x00, 0x84, 0xe0, 0x00,
Expand Down Expand Up @@ -45,11 +47,12 @@ func BenchmarkDecodeigmpv1MembershipReportPacket(b *testing.B) {
}

// igmpv2MembershipQueryPacket is the packet:
// 02:45:28.071636 IP 10.60.0.189 > 224.0.0.1: igmp query v2
// 0x0000: 0100 5e00 0001 0001 636f c800 0800 45c0 ..^.....co....E.
// 0x0010: 001c 0153 0000 0102 ccd3 0a3c 00bd e000 ...S.......<....
// 0x0020: 0001 1164 ee9b 0000 0000 0000 0000 0000 ...d............
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
//
// 02:45:28.071636 IP 10.60.0.189 > 224.0.0.1: igmp query v2
// 0x0000: 0100 5e00 0001 0001 636f c800 0800 45c0 ..^.....co....E.
// 0x0010: 001c 0153 0000 0102 ccd3 0a3c 00bd e000 ...S.......<....
// 0x0020: 0001 1164 ee9b 0000 0000 0000 0000 0000 ...d............
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
var igmpv2MembershipQueryPacket = []byte{
0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x00, 0x01, 0x63, 0x6f, 0xc8, 0x00, 0x08, 0x00, 0x45, 0xc0,
0x00, 0x1c, 0x01, 0x53, 0x00, 0x00, 0x01, 0x02, 0xcc, 0xd3, 0x0a, 0x3c, 0x00, 0xbd, 0xe0, 0x00,
Expand All @@ -76,17 +79,31 @@ func BenchmarkDecodeigmpv2MembershipQueryPacket(b *testing.B) {
}

// igmpv2MembershipReportPacket is the packet:
// 02:47:32.417288 IP 10.60.5.103 > 239.255.255.253: igmp v2 report 239.255.255.253
// 0x0000: 0100 5e7f fffd 0015 58dc d9f6 0800 4600 ..^.....X.....F.
// 0x0010: 0020 79f0 0000 0102 ab47 0a3c 0567 efff ..y......G.<.g..
// 0x0020: fffd 9404 0000 1600 fa01 efff fffd 0000 ................
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
//
// 02:47:32.417288 IP 10.60.5.103 > 239.255.255.253: igmp v2 report 239.255.255.253
// 0x0000: 0100 5e7f fffd 0015 58dc d9f6 0800 4600 ..^.....X.....F.
// 0x0010: 0020 79f0 0000 0102 ab47 0a3c 0567 efff ..y......G.<.g..
// 0x0020: fffd 9404 0000 1600 fa01 efff fffd 0000 ................
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
var igmpv2MembershipReportPacket = []byte{
0x01, 0x00, 0x5e, 0x7f, 0xff, 0xfd, 0x00, 0x15, 0x58, 0xdc, 0xd9, 0xf6, 0x08, 0x00, 0x46, 0x00,
0x00, 0x20, 0x79, 0xf0, 0x00, 0x00, 0x01, 0x02, 0xab, 0x47, 0x0a, 0x3c, 0x05, 0x67, 0xef, 0xff,
0xff, 0xfd, 0x94, 0x04, 0x00, 0x00, 0x16, 0x00, 0xfa, 0x01, 0xef, 0xff, 0xff, 0xfd, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
var igmpv2MembershipReportPacket2 = []byte{
0x01, 0x00, 0x5e, 0x0a, 0x0a, 0x0a, 0x00, 0x02, 0x02, 0x19, 0x51, 0x28, 0x08, 0x00, 0x46, 0x00,
0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x01, 0x02, 0x2d, 0x52, 0xc0, 0xa8, 0x0b, 0xc9, 0xe1, 0x0a,
0x0a, 0x0a, 0x94, 0x04, 0x00, 0x00, 0x16, 0x00, 0xfe, 0xea, 0xe1, 0x0a, 0x0a, 0x0a, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}

var igmpv2MembershipReportPacketWithMaxRespTime = []byte{
0x01, 0x00, 0x5e, 0x7f, 0xff, 0xfd, 0x00, 0x15, 0x58, 0xdc, 0xd9, 0xf6, 0x08, 0x00, 0x46, 0x00,
0x00, 0x20, 0x79, 0xf0, 0x00, 0x00, 0x01, 0x02, 0xab, 0x47, 0x0a, 0x3c, 0x05, 0x67, 0xef, 0xff,
0xff, 0xfd, 0x94, 0x04, 0x00, 0x00, 0x16, 0xa8, 0xf9, 0x59, 0xef, 0xff, 0xff, 0xfd, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}

func TestIGMPv2MembershipReport(t *testing.T) {
p := gopacket.NewPacket(igmpv2MembershipReportPacket, LinkTypeEthernet, gopacket.Default)
Expand All @@ -106,12 +123,48 @@ func BenchmarkDecodeigmpv2MembershipReportPacket(b *testing.B) {
}
}

func TestIGMPv2MembershipReportSerialize(t *testing.T) {
want := gopacket.NewPacket(igmpv2MembershipReportPacketWithMaxRespTime, LinkTypeEthernet, gopacket.Default)
p := gopacket.NewPacket(igmpv2MembershipReportPacketWithMaxRespTime, LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
}
checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIGMP}, t)

igmp := p.Layer(LayerTypeIGMP).(*IGMPv1or2)
if igmp.Type != IGMPMembershipReportV2 {
t.Fatal("Invalid IGMP type")
}
ip4 := p.Layer(LayerTypeIPv4).(*IPv4)

var serialize = make([]gopacket.SerializableLayer, 0, 3)
serialize = append(serialize, p.Layer(LayerTypeEthernet).(*Ethernet))
serialize = append(serialize, ip4)
serialize = append(serialize, igmp)

buffer := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
err := gopacket.SerializeLayers(buffer, opts, serialize...)
if err != nil {
t.Fatal(err)
}
got := gopacket.NewPacket(buffer.Bytes(), LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
t.Fatal("Failed to decode packet:", p.ErrorLayer().Error())
}
if !reflect.DeepEqual(got, want) {
t.Errorf("Bad serialization:\ngot:\n%v\n\nwant:\n%v\n\n", got.Dump(), want.Dump())
}

}

// igmp3v3MembershipQueryPacket is the packet:
// 10:07:30.488511 IP 192.168.1.254 > 224.0.0.1: igmp query v3 [max resp time 2.4s]
// 0x0000: 0100 5e00 0001 0026 446c 1eda 0800 46c0 ..^....&Dl....F.
// 0x0010: 0024 17f1 4000 0102 297b c0a8 01fe e000 .$..@...){......
// 0x0020: 0001 9404 0000 1118 ecd3 0000 0000 0214 ................
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
//
// 10:07:30.488511 IP 192.168.1.254 > 224.0.0.1: igmp query v3 [max resp time 2.4s]
// 0x0000: 0100 5e00 0001 0026 446c 1eda 0800 46c0 ..^....&Dl....F.
// 0x0010: 0024 17f1 4000 0102 297b c0a8 01fe e000 .$..@...){......
// 0x0020: 0001 9404 0000 1118 ecd3 0000 0000 0214 ................
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
var igmp3v3MembershipQueryPacket = []byte{
0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x00, 0x26, 0x44, 0x6c, 0x1e, 0xda, 0x08, 0x00, 0x46, 0xc0,
0x00, 0x24, 0x17, 0xf1, 0x40, 0x00, 0x01, 0x02, 0x29, 0x7b, 0xc0, 0xa8, 0x01, 0xfe, 0xe0, 0x00,
Expand Down Expand Up @@ -139,11 +192,12 @@ func BenchmarkDecodeigmp3v3MembershipQueryPacket(b *testing.B) {
}

// igmpv3MembershipReport2Records is the packet:
// 10:07:29.756202 IP 192.168.1.66 > 224.0.0.22: igmp v3 report, 2 group record(s)
// 0x0000: 0100 5e00 0016 0025 2e51 c381 0800 4658 ..^....%.Q....FX
// 0x0010: 0030 013c 0000 0102 8133 c0a8 0142 e000 .0.<.....3...B..
// 0x0020: 0016 9404 0000 2200 f33c 0000 0002 0200 ......"..<......
// 0x0030: 0000 efc3 0702 0200 0000 efff fffa ..............
//
// 10:07:29.756202 IP 192.168.1.66 > 224.0.0.22: igmp v3 report, 2 group record(s)
// 0x0000: 0100 5e00 0016 0025 2e51 c381 0800 4658 ..^....%.Q....FX
// 0x0010: 0030 013c 0000 0102 8133 c0a8 0142 e000 .0.<.....3...B..
// 0x0020: 0016 9404 0000 2200 f33c 0000 0002 0200 ......"..<......
// 0x0030: 0000 efc3 0702 0200 0000 efff fffa ..............
var igmpv3MembershipReport2Records = []byte{
0x01, 0x00, 0x5e, 0x00, 0x00, 0x16, 0x00, 0x25, 0x2e, 0x51, 0xc3, 0x81, 0x08, 0x00, 0x46, 0x58,
0x00, 0x30, 0x01, 0x3c, 0x00, 0x00, 0x01, 0x02, 0x81, 0x33, 0xc0, 0xa8, 0x01, 0x42, 0xe0, 0x00,
Expand Down