Skip to content

Commit

Permalink
Add serialization to a number of layers.
Browse files Browse the repository at this point in the history
Add serialization to ARP, EAP, ICMPv4, ICMPv6, MPLS, PPP, and PPPoE.
Fixes bug in MPLS where it was looking at the wrong bit for bottom-of-stack.
Added a number of tests for the new layer types, to make sure we could decode,
then serialize and get the same packet back.
  • Loading branch information
gconnell committed Oct 4, 2013
1 parent 7167728 commit 9688856
Show file tree
Hide file tree
Showing 10 changed files with 394 additions and 50 deletions.
38 changes: 38 additions & 0 deletions layers/arp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package layers
import (
"code.google.com/p/gopacket"
"encoding/binary"
"fmt"
)

// ARP is a ARP packet header.
Expand Down Expand Up @@ -47,6 +48,43 @@ func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
return nil
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (arp *ARP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
size := 8 + len(arp.SourceHwAddress) + len(arp.SourceProtAddress) + len(arp.DstHwAddress) + len(arp.DstProtAddress)
bytes, err := b.PrependBytes(size)
if err != nil {
return err
}
if opts.FixLengths {
if len(arp.SourceHwAddress) != len(arp.DstHwAddress) {
return fmt.Errorf("mismatched hardware address sizes")
}
arp.HwAddressSize = uint8(len(arp.SourceHwAddress))
if len(arp.SourceProtAddress) != len(arp.DstProtAddress) {
return fmt.Errorf("mismatched prot address sizes")
}
arp.ProtAddressSize = uint8(len(arp.SourceProtAddress))
}
binary.BigEndian.PutUint16(bytes, uint16(arp.AddrType))
binary.BigEndian.PutUint16(bytes[2:], uint16(arp.Protocol))
bytes[4] = arp.HwAddressSize
bytes[5] = arp.ProtAddressSize
binary.BigEndian.PutUint16(bytes[6:], arp.Operation)
start := 8
for _, addr := range [][]byte{
arp.SourceHwAddress,
arp.SourceProtAddress,
arp.DstHwAddress,
arp.DstProtAddress,
} {
copy(bytes[start:], addr)
start += len(addr)
}
return nil
}

// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (arp *ARP) CanDecode() gopacket.LayerClass {
return LayerTypeARP
Expand Down
1 change: 1 addition & 0 deletions layers/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func checkLayers(p gopacket.Packet, want []gopacket.LayerType, t *testing.T) {
for _, l := range layers {
t.Logf(" Got layer %v, %d bytes, payload of %d bytes", l.LayerType(), len(l.LayerContents()), len(l.LayerPayload()))
}
t.Log(p)
if len(layers) != len(want) {
t.Errorf(" Number of layers mismatch: got %d want %d", len(want), len(layers))
return
Expand Down
175 changes: 150 additions & 25 deletions layers/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,13 +422,25 @@ func TestDecodeSimpleTCPPacket(t *testing.T) {
}
}

// Test re-serialization.
testSerialization(t, p, testSimpleTCPPacket)
}

type canSetNetLayer interface {
SetNetworkLayerForChecksum(gopacket.NetworkLayer) error
}

func testSerialization(t *testing.T, p gopacket.Packet, data []byte) {
// Test re-serialization.
slayers := []gopacket.SerializableLayer{}
for _, l := range p.Layers() {
slayers = append(slayers, l.(gopacket.SerializableLayer))
if h, ok := l.(canSetNetLayer); ok {
if err := h.SetNetworkLayerForChecksum(p.NetworkLayer()); err != nil {
t.Fatal("can't set network layer:", err)
}
}
}
p.Layer(LayerTypeTCP).(*TCP).SetNetworkLayerForChecksum(
p.NetworkLayer())
for _, opts := range []gopacket.SerializeOptions{
gopacket.SerializeOptions{},
gopacket.SerializeOptions{FixLengths: true},
Expand All @@ -439,8 +451,8 @@ func TestDecodeSimpleTCPPacket(t *testing.T) {
err := gopacket.SerializeLayers(buf, opts, slayers...)
if err != nil {
t.Errorf("unable to reserialize layers with opts %#v: %v", opts, err)
} else if !bytes.Equal(buf.Bytes(), testSimpleTCPPacket) {
t.Errorf("serialization failure with opts %#v :\n---want---\n%v\n---got---\n%v", opts, hex.Dump(testSimpleTCPPacket), hex.Dump(buf.Bytes()))
} else if !bytes.Equal(buf.Bytes(), data) {
t.Errorf("serialization failure with opts %#v:\n---want---\n%v\n---got---\n%v", opts, hex.Dump(data), hex.Dump(buf.Bytes()))
}
}
}
Expand All @@ -461,27 +473,7 @@ func TestDecodeSmallTCPPacketHasEmptyPayload(t *testing.T) {
t.Error("Payload found for empty TCP packet")
}

// Test re-serialization.
slayers := []gopacket.SerializableLayer{}
for _, l := range p.Layers() {
slayers = append(slayers, l.(gopacket.SerializableLayer))
}
p.Layer(LayerTypeTCP).(*TCP).SetNetworkLayerForChecksum(
p.NetworkLayer())
for _, opts := range []gopacket.SerializeOptions{
gopacket.SerializeOptions{},
gopacket.SerializeOptions{FixLengths: true},
gopacket.SerializeOptions{ComputeChecksums: true},
gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true},
} {
buf := gopacket.NewSerializeBuffer()
err := gopacket.SerializeLayers(buf, opts, slayers...)
if err != nil {
t.Errorf("unable to reserialize layers with opts %#v: %v", opts, err)
} else if !bytes.Equal(buf.Bytes(), smallPacket) {
t.Errorf("serialization failure with opts %#v :\n---want---\n%v\n---got---\n%v", opts, hex.Dump(smallPacket), hex.Dump(buf.Bytes()))
}
}
testSerialization(t, p, smallPacket)
}

func TestDecodeVLANPacket(t *testing.T) {
Expand Down Expand Up @@ -977,3 +969,136 @@ func TestDecodingLayerParserFullTCPPacket(t *testing.T) {
t.Error("Expected 4 layers parsed, instead got ", len(decoded))
}
}

// testICMP is the packet:
// 15:49:15.773265 IP 72.14.222.226 > 172.29.20.15: ICMP host 10.66.73.201 unreachable - admin prohibited filter, length 36
// 0x0000: 24be 0527 0b17 001f cab3 75c0 0800 4500 $..'......u...E.
// 0x0010: 0038 0000 0000 fc01 d7a7 480e dee2 ac1d .8........H.....
// 0x0020: 140f 030d 946e 0000 0000 4520 004d 0000 .....n....E..M..
// 0x0030: 4000 3e11 2849 ac1d 140f 0a42 49c9 8ecc @.>.(I.....BI...
// 0x0040: 62e1 0039 769d b..9v.
var testICMP = []byte{
0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x75, 0xc0, 0x08, 0x00, 0x45, 0x00,
0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xd7, 0xa7, 0x48, 0x0e, 0xde, 0xe2, 0xac, 0x1d,
0x14, 0x0f, 0x03, 0x0d, 0x94, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x45, 0x20, 0x00, 0x4d, 0x00, 0x00,
0x40, 0x00, 0x3e, 0x11, 0x28, 0x49, 0xac, 0x1d, 0x14, 0x0f, 0x0a, 0x42, 0x49, 0xc9, 0x8e, 0xcc,
0x62, 0xe1, 0x00, 0x39, 0x76, 0x9d,
}

func TestICMP(t *testing.T) {
p := gopacket.NewPacket(testICMP, LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
}
checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t)
testSerialization(t, p, testICMP)
}
func BenchmarkDecodeICMP(b *testing.B) {
for i := 0; i < b.N; i++ {
gopacket.NewPacket(testICMP, LinkTypeEthernet, gopacket.NoCopy)
}
}

// testICMP6 is the packet:
// 16:17:37.758937 IP6 fe80::21f:caff:feb3:75c0 > 2620:0:1005:0:26be:5ff:fe27:b17: ICMP6, neighbor solicitation, who has 2620:0:1005:0:26be:5ff:fe27:b17, length 32
// 0x0000: 24be 0527 0b17 001f cab3 75c0 86dd 6e00 $..'......u...n.
// 0x0010: 0000 0020 3aff fe80 0000 0000 0000 021f ....:...........
// 0x0020: caff feb3 75c0 2620 0000 1005 0000 26be ....u.&.......&.
// 0x0030: 05ff fe27 0b17 8700 1eba 0000 0000 2620 ...'..........&.
// 0x0040: 0000 1005 0000 26be 05ff fe27 0b17 0101 ......&....'....
// 0x0050: 001f cab3 75c0 ....u.
var testICMP6 = []byte{
0x24, 0xbe, 0x05, 0x27, 0x0b, 0x17, 0x00, 0x1f, 0xca, 0xb3, 0x75, 0xc0, 0x86, 0xdd, 0x6e, 0x00,
0x00, 0x00, 0x00, 0x20, 0x3a, 0xff, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1f,
0xca, 0xff, 0xfe, 0xb3, 0x75, 0xc0, 0x26, 0x20, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x26, 0xbe,
0x05, 0xff, 0xfe, 0x27, 0x0b, 0x17, 0x87, 0x00, 0x1e, 0xba, 0x00, 0x00, 0x00, 0x00, 0x26, 0x20,
0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x26, 0xbe, 0x05, 0xff, 0xfe, 0x27, 0x0b, 0x17, 0x01, 0x01,
0x00, 0x1f, 0xca, 0xb3, 0x75, 0xc0,
}

func TestICMP6(t *testing.T) {
p := gopacket.NewPacket(testICMP6, LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
}
checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeIPv6, LayerTypeICMPv6, gopacket.LayerTypePayload}, t)
testSerialization(t, p, testICMP6)
}
func BenchmarkDecodeICMP6(b *testing.B) {
for i := 0; i < b.N; i++ {
gopacket.NewPacket(testICMP6, LinkTypeEthernet, gopacket.NoCopy)
}
}

// testMPLS is the packet:
// 12:48:57.201014 MPLS (label 29, exp 0, [S], ttl 255) IP 10.1.2.1 > 10.34.0.1: ICMP echo request, id 2618, seq 1579, length 80
// 0x0000: 0030 96e6 fc39 0030 9605 2838 8847 0001 .0...9.0..(8.G..
// 0x0010: d1ff 4500 0064 000b 0000 ff01 a569 0a01 ..E..d.......i..
// 0x0020: 0201 0a22 0001 0800 3a76 0a3a 062b 0000 ..."....:v.:.+..
// 0x0030: 0000 001f 3350 abcd abcd abcd abcd abcd ....3P..........
// 0x0040: abcd abcd abcd abcd abcd abcd abcd abcd ................
// 0x0050: abcd abcd abcd abcd abcd abcd abcd abcd ................
// 0x0060: abcd abcd abcd abcd abcd abcd abcd abcd ................
// 0x0070: abcd abcd abcd ......
var testMPLS = []byte{
0x00, 0x30, 0x96, 0xe6, 0xfc, 0x39, 0x00, 0x30, 0x96, 0x05, 0x28, 0x38, 0x88, 0x47, 0x00, 0x01,
0xd1, 0xff, 0x45, 0x00, 0x00, 0x64, 0x00, 0x0b, 0x00, 0x00, 0xff, 0x01, 0xa5, 0x69, 0x0a, 0x01,
0x02, 0x01, 0x0a, 0x22, 0x00, 0x01, 0x08, 0x00, 0x3a, 0x76, 0x0a, 0x3a, 0x06, 0x2b, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1f, 0x33, 0x50, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd,
}

func TestMPLS(t *testing.T) {
p := gopacket.NewPacket(testMPLS, LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
}
checkLayers(p, []gopacket.LayerType{LayerTypeEthernet, LayerTypeMPLS, LayerTypeIPv4, LayerTypeICMPv4, gopacket.LayerTypePayload}, t)
testSerialization(t, p, testMPLS)
}
func BenchmarkDecodeMPLS(b *testing.B) {
for i := 0; i < b.N; i++ {
gopacket.NewPacket(testMPLS, LinkTypeEthernet, gopacket.NoCopy)
}
}

// testPPPoE_ICMPv6 is the packet:
// 07:43:31.091560 PPPoE [ses 0x11] IP6 fe80::c801:eff:fe88:8 > ff02::1: ICMP6, neighbor advertisement, tgt is fe80::c801:eff:fe88:8, length 24
// 0x0000: cc05 0e88 0000 ca01 0e88 0006 8864 1100 .............d..
// 0x0010: 0011 0042 0057 6e00 0000 0018 3aff fe80 ...B.Wn.....:...
// 0x0020: 0000 0000 0000 c801 0eff fe88 0008 ff02 ................
// 0x0030: 0000 0000 0000 0000 0000 0000 0001 8800 ................
// 0x0040: 5083 8000 0000 fe80 0000 0000 0000 c801 P...............
// 0x0050: 0eff fe88 0008 ......
var testPPPoE_ICMPv6 = []byte{
0xcc, 0x05, 0x0e, 0x88, 0x00, 0x00, 0xca, 0x01, 0x0e, 0x88, 0x00, 0x06, 0x88, 0x64, 0x11, 0x00,
0x00, 0x11, 0x00, 0x42, 0x00, 0x57, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3a, 0xff, 0xfe, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x0e, 0xff, 0xfe, 0x88, 0x00, 0x08, 0xff, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00,
0x50, 0x83, 0x80, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x01,
0x0e, 0xff, 0xfe, 0x88, 0x00, 0x08,
}

func TestPPPoE_ICMPv6(t *testing.T) {
p := gopacket.NewPacket(testPPPoE_ICMPv6, LinkTypeEthernet, gopacket.Default)
if p.ErrorLayer() != nil {
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
}
checkLayers(p, []gopacket.LayerType{
LayerTypeEthernet,
LayerTypePPPoE,
LayerTypePPP,
LayerTypeIPv6,
LayerTypeICMPv6,
gopacket.LayerTypePayload,
}, t)
testSerialization(t, p, testPPPoE_ICMPv6)
}
func BenchmarkDecodePPPoE_ICMPv6(b *testing.B) {
for i := 0; i < b.N; i++ {
gopacket.NewPacket(testPPPoE_ICMPv6, LinkTypeEthernet, gopacket.NoCopy)
}
}
33 changes: 31 additions & 2 deletions layers/eap.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package layers
import (
"code.google.com/p/gopacket"
"encoding/binary"
"fmt"
)

type EAPCode uint8
Expand Down Expand Up @@ -49,18 +50,46 @@ func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
e.Code = EAPCode(data[0])
e.Id = data[1]
e.Length = binary.BigEndian.Uint16(data[2:4])
if e.Length > 4 {
switch {
case e.Length > 4:
e.Type = EAPType(data[4])
e.TypeData = data[5:]
} else {
case e.Length == 4:
e.Type = 0
e.TypeData = nil
default:
return fmt.Errorf("invalid EAP length %d", e.Length)
}
e.BaseLayer.Contents = data[:e.Length]
e.BaseLayer.Payload = data[e.Length:] // Should be 0 bytes
return nil
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (e *EAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if opts.FixLengths {
e.Length = uint16(len(e.TypeData) + 1)
}
size := len(e.TypeData) + 4
if size > 4 {
size++
}
bytes, err := b.PrependBytes(size)
if err != nil {
return err
}
bytes[0] = byte(e.Code)
bytes[1] = e.Id
binary.BigEndian.PutUint16(bytes[2:], e.Length)
if size > 4 {
bytes[4] = byte(e.Type)
copy(bytes[5:], e.TypeData)
}
return nil
}

// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (e *EAP) CanDecode() gopacket.LayerClass {
return LayerTypeEAP
Expand Down
Loading

0 comments on commit 9688856

Please sign in to comment.