Skip to content

Commit d9b070d

Browse files
Merge pull request #1 from randomizedcoder/igmp-serialize
Igmp serialize
2 parents 32ee382 + 201cf8f commit d9b070d

File tree

2 files changed

+157
-32
lines changed

2 files changed

+157
-32
lines changed

layers/igmp.go

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@ type IGMP struct {
9393

9494
// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
9595
//
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
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+
//
9899
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99100
// | Type | Max Resp Time | Checksum |
100101
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -204,8 +205,9 @@ func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
204205
return nil
205206
}
206207

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
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+
//
209211
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
210212
// | Type = 0x11 | Max Resp Code | Checksum |
211213
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -251,9 +253,34 @@ func igmpTimeDecode(t uint8) time.Duration {
251253
if t&0x80 == 0 {
252254
return time.Millisecond * 100 * time.Duration(t)
253255
}
254-
mant := (t & 0x70) >> 4
255-
exp := t & 0x0F
256-
return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
256+
exp := (t & 0x70) >> 4
257+
mant := t & 0x0F
258+
return time.Millisecond * 100 * time.Duration((uint64(mant)|0x10)<<(exp+3))
259+
}
260+
261+
func igmpTimeEncode(t time.Duration) uint8 {
262+
// in units of 1/10 second
263+
tm := t.Milliseconds() / 100
264+
if t < 128 {
265+
return uint8(tm)
266+
}
267+
// 0 1 2 3 4 5 6 7
268+
// +-+-+-+-+-+-+-+-+
269+
// |1| exp | mant |
270+
// +-+-+-+-+-+-+-+-+
271+
// Max Resp Time = (mant | 0x10) << (exp + 3)
272+
var expPlus3, mant, r uint8
273+
// range of 3 bit integer plus 3
274+
for expPlus3 = 3; expPlus3 < 11; expPlus3++ {
275+
if (tm>>expPlus3)&0x1f == (tm >> expPlus3) {
276+
break
277+
}
278+
}
279+
280+
mant = uint8((tm >> expPlus3) & 0x0f)
281+
r = ((expPlus3-3)|0x08)<<4 + mant&0x0f
282+
283+
return r
257284
}
258285

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

303+
func (i *IGMPv1or2) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
304+
305+
bytes, err := b.PrependBytes(8)
306+
if err != nil {
307+
return err
308+
}
309+
310+
bytes[0] = byte(i.Type)
311+
bytes[1] = igmpTimeEncode(i.MaxResponseTime)
312+
copy(bytes[4:8], i.GroupAddress)
313+
if opts.ComputeChecksums {
314+
csum := i.checksum(b.Bytes())
315+
if err != nil {
316+
return err
317+
}
318+
i.Checksum = csum
319+
}
320+
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
321+
return nil
322+
}
323+
324+
func (i *IGMPv1or2) checksum(bytes []byte) uint16 {
325+
// Clear checksum bytes
326+
bytes[2] = 0
327+
bytes[3] = 0
328+
329+
// Compute checksum
330+
var csum uint32
331+
for i := 0; i < len(bytes); i += 2 {
332+
csum += uint32(bytes[i]) << 8
333+
csum += uint32(bytes[i+1])
334+
}
335+
for {
336+
// Break when sum is less or equals to 0xFFFF
337+
if csum <= 65535 {
338+
break
339+
}
340+
// Add carry to the sum
341+
csum = (csum >> 16) + uint32(uint16(csum))
342+
}
343+
// Flip all the bits
344+
return ^uint16(csum)
345+
}
346+
276347
func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
277348
return gopacket.LayerTypeZero
278349
}

layers/igmp_test.go

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@
77
package layers
88

99
import (
10+
"reflect"
1011
"testing"
1112

1213
"github.com/google/gopacket"
1314
)
1415

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

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

7881
// igmpv2MembershipReportPacket is the packet:
79-
// 02:47:32.417288 IP 10.60.5.103 > 239.255.255.253: igmp v2 report 239.255.255.253
80-
// 0x0000: 0100 5e7f fffd 0015 58dc d9f6 0800 4600 ..^.....X.....F.
81-
// 0x0010: 0020 79f0 0000 0102 ab47 0a3c 0567 efff ..y......G.<.g..
82-
// 0x0020: fffd 9404 0000 1600 fa01 efff fffd 0000 ................
83-
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
82+
//
83+
// 02:47:32.417288 IP 10.60.5.103 > 239.255.255.253: igmp v2 report 239.255.255.253
84+
// 0x0000: 0100 5e7f fffd 0015 58dc d9f6 0800 4600 ..^.....X.....F.
85+
// 0x0010: 0020 79f0 0000 0102 ab47 0a3c 0567 efff ..y......G.<.g..
86+
// 0x0020: fffd 9404 0000 1600 fa01 efff fffd 0000 ................
87+
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
8488
var igmpv2MembershipReportPacket = []byte{
8589
0x01, 0x00, 0x5e, 0x7f, 0xff, 0xfd, 0x00, 0x15, 0x58, 0xdc, 0xd9, 0xf6, 0x08, 0x00, 0x46, 0x00,
8690
0x00, 0x20, 0x79, 0xf0, 0x00, 0x00, 0x01, 0x02, 0xab, 0x47, 0x0a, 0x3c, 0x05, 0x67, 0xef, 0xff,
8791
0xff, 0xfd, 0x94, 0x04, 0x00, 0x00, 0x16, 0x00, 0xfa, 0x01, 0xef, 0xff, 0xff, 0xfd, 0x00, 0x00,
8892
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8993
}
94+
var igmpv2MembershipReportPacket2 = []byte{
95+
0x01, 0x00, 0x5e, 0x0a, 0x0a, 0x0a, 0x00, 0x02, 0x02, 0x19, 0x51, 0x28, 0x08, 0x00, 0x46, 0x00,
96+
0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x01, 0x02, 0x2d, 0x52, 0xc0, 0xa8, 0x0b, 0xc9, 0xe1, 0x0a,
97+
0x0a, 0x0a, 0x94, 0x04, 0x00, 0x00, 0x16, 0x00, 0xfe, 0xea, 0xe1, 0x0a, 0x0a, 0x0a, 0x00, 0x00,
98+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99+
}
100+
101+
var igmpv2MembershipReportPacketWithMaxRespTime = []byte{
102+
0x01, 0x00, 0x5e, 0x7f, 0xff, 0xfd, 0x00, 0x15, 0x58, 0xdc, 0xd9, 0xf6, 0x08, 0x00, 0x46, 0x00,
103+
0x00, 0x20, 0x79, 0xf0, 0x00, 0x00, 0x01, 0x02, 0xab, 0x47, 0x0a, 0x3c, 0x05, 0x67, 0xef, 0xff,
104+
0xff, 0xfd, 0x94, 0x04, 0x00, 0x00, 0x16, 0xa8, 0xf9, 0x59, 0xef, 0xff, 0xff, 0xfd, 0x00, 0x00,
105+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106+
}
90107

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

126+
func TestIGMPv2MembershipReportSerialize(t *testing.T) {
127+
want := gopacket.NewPacket(igmpv2MembershipReportPacketWithMaxRespTime, LinkTypeEthernet, gopacket.Default)
128+
p := gopacket.NewPacket(igmpv2MembershipReportPacketWithMaxRespTime, LinkTypeEthernet, gopacket.Default)
129+
if p.ErrorLayer() != nil {
130+
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
131+
}
132+
checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeIGMP}, t)
133+
134+
igmp := p.Layer(LayerTypeIGMP).(*IGMPv1or2)
135+
if igmp.Type != IGMPMembershipReportV2 {
136+
t.Fatal("Invalid IGMP type")
137+
}
138+
ip4 := p.Layer(LayerTypeIPv4).(*IPv4)
139+
140+
var serialize = make([]gopacket.SerializableLayer, 0, 3)
141+
serialize = append(serialize, p.Layer(LayerTypeEthernet).(*Ethernet))
142+
serialize = append(serialize, ip4)
143+
serialize = append(serialize, igmp)
144+
145+
buffer := gopacket.NewSerializeBuffer()
146+
opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
147+
err := gopacket.SerializeLayers(buffer, opts, serialize...)
148+
if err != nil {
149+
t.Fatal(err)
150+
}
151+
got := gopacket.NewPacket(buffer.Bytes(), LinkTypeEthernet, gopacket.Default)
152+
if p.ErrorLayer() != nil {
153+
t.Fatal("Failed to decode packet:", p.ErrorLayer().Error())
154+
}
155+
if !reflect.DeepEqual(got, want) {
156+
t.Errorf("Bad serialization:\ngot:\n%v\n\nwant:\n%v\n\n", got.Dump(), want.Dump())
157+
}
158+
159+
}
160+
109161
// igmp3v3MembershipQueryPacket is the packet:
110-
// 10:07:30.488511 IP 192.168.1.254 > 224.0.0.1: igmp query v3 [max resp time 2.4s]
111-
// 0x0000: 0100 5e00 0001 0026 446c 1eda 0800 46c0 ..^....&Dl....F.
112-
// 0x0010: 0024 17f1 4000 0102 297b c0a8 01fe e000 .$..@...){......
113-
// 0x0020: 0001 9404 0000 1118 ecd3 0000 0000 0214 ................
114-
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
162+
//
163+
// 10:07:30.488511 IP 192.168.1.254 > 224.0.0.1: igmp query v3 [max resp time 2.4s]
164+
// 0x0000: 0100 5e00 0001 0026 446c 1eda 0800 46c0 ..^....&Dl....F.
165+
// 0x0010: 0024 17f1 4000 0102 297b c0a8 01fe e000 .$..@...){......
166+
// 0x0020: 0001 9404 0000 1118 ecd3 0000 0000 0214 ................
167+
// 0x0030: 0000 0000 0000 0000 0000 0000 ............
115168
var igmp3v3MembershipQueryPacket = []byte{
116169
0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x00, 0x26, 0x44, 0x6c, 0x1e, 0xda, 0x08, 0x00, 0x46, 0xc0,
117170
0x00, 0x24, 0x17, 0xf1, 0x40, 0x00, 0x01, 0x02, 0x29, 0x7b, 0xc0, 0xa8, 0x01, 0xfe, 0xe0, 0x00,
@@ -139,11 +192,12 @@ func BenchmarkDecodeigmp3v3MembershipQueryPacket(b *testing.B) {
139192
}
140193

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

0 commit comments

Comments
 (0)