Skip to content
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
2 changes: 2 additions & 0 deletions layers/enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
EthernetTypeCiscoDiscovery EthernetType = 0x2000
EthernetTypeNortelDiscovery EthernetType = 0x01a2
EthernetTypeTransparentEthernetBridging EthernetType = 0x6558
EthernetTypeMerakiDiscoveryProtocol EthernetType = 0x712
EthernetTypeDot1Q EthernetType = 0x8100
EthernetTypePPP EthernetType = 0x880b
EthernetTypePPPoEDiscovery EthernetType = 0x8863
Expand Down Expand Up @@ -323,6 +324,7 @@ func initActualTypeData() {
EthernetTypeMetadata[EthernetTypeQinQ] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
EthernetTypeMetadata[EthernetTypeTransparentEthernetBridging] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridging", LayerType: LayerTypeEthernet}
EthernetTypeMetadata[EthernetTypeERSPAN] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeERSPANII), Name: "ERSPAN Type II", LayerType: LayerTypeERSPANII}
EthernetTypeMetadata[EthernetTypeMerakiDiscoveryProtocol] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMDP), Name: "MDP", LayerType: LayerTypeMDP}

IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP}
Expand Down
1 change: 1 addition & 0 deletions layers/layertypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ var (
LayerTypeERSPANII = gopacket.RegisterLayerType(145, gopacket.LayerTypeMetadata{Name: "ERSPAN Type II", Decoder: gopacket.DecodeFunc(decodeERSPANII)})
LayerTypeRADIUS = gopacket.RegisterLayerType(146, gopacket.LayerTypeMetadata{Name: "RADIUS", Decoder: gopacket.DecodeFunc(decodeRADIUS)})
LayerTypeLinuxSLL2 = gopacket.RegisterLayerType(276, gopacket.LayerTypeMetadata{Name: "Linux SLL2", Decoder: gopacket.DecodeFunc(decodeLinuxSLL2)})
LayerTypeMDP = gopacket.RegisterLayerType(147, gopacket.LayerTypeMetadata{Name: "MDP", Decoder: gopacket.DecodeFunc(decodeMDP)})
)

var (
Expand Down
162 changes: 162 additions & 0 deletions layers/mdp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright 2024 Google, Inc. All rights reserved.
//
Comment thread
mosajjal marked this conversation as resolved.
// 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 (
"fmt"
"net"
"strconv"

"github.com/gopacket/gopacket"
)

const (
MdpTlvType uint8 = iota
MdpTlvLength
MdpTlvDeviceInfo
MdpTlvNetworkInfo
MdpTlvLongitude
MdpTlvLatitude
MdpTlvType6
MdpTlvType7
MdpTlvIP = 11
MdpTlvUnknownBool = 13
MdpTlvEnd = 255
)

// MDP defines a MDP over LLC layer.
type MDP struct {
BaseLayer
PreambleData []byte
DeviceInfo string
NetworkInfo string
Longitude float64
Latitude float64
Type6UUID string
Type7UUID string
IPAddress net.IP
Type13Bool bool

Type EthernetType
Length int
}

// LayerType returns LayerTypeMDP.
func (m *MDP) LayerType() gopacket.LayerType { return LayerTypeMDP }

// DecodeFromBytes decodes the given bytes into this layer.
func (m *MDP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
var length int
if len(data) < 28 {
df.SetTruncated()
return fmt.Errorf("MDP length %d too short", len(data))
}
m.Type = EthernetTypeMerakiDiscoveryProtocol
m.Length = len(data)
offset := 28
m.PreambleData = data[:offset]

for {
if offset >= m.Length {
break
}
t := data[offset]
switch t {
case MdpTlvDeviceInfo:
offset += 2
length = int(data[offset-1])
m.Contents = append(m.Contents, data[offset-2:offset+length]...)
m.DeviceInfo = string(data[offset : offset+length])
offset += length
break
case MdpTlvNetworkInfo:
offset += 2
length = int(data[offset-1])
m.NetworkInfo = string(data[offset : offset+length])
offset += length
break
case MdpTlvLongitude:
offset += 2
length = int(data[offset-1])
m.Longitude, _ = strconv.ParseFloat(string(data[offset:offset+length]), 64)
offset += length
break
case MdpTlvLatitude:
offset += 2
length = int(data[offset-1])
m.Latitude, _ = strconv.ParseFloat(string(data[offset:offset+length]), 64)
offset += length
break
case MdpTlvType6:
offset += 2
length = int(data[offset-1])
m.Type6UUID = string(data[offset : offset+length])
offset += length
break
case MdpTlvType7:
offset += 2
length = int(data[offset-1])
m.Type7UUID = string(data[offset : offset+length])
offset += length
break
case MdpTlvIP:
offset += 2
length = int(data[offset-1])
m.IPAddress = net.ParseIP(string(data[offset : offset+length]))
offset += length
break
case MdpTlvUnknownBool:
offset += 2
length = int(data[offset-1])
m.Type13Bool, _ = strconv.ParseBool(string(data[offset : offset+length]))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does this come from? I can't see something similar in Wireshark's code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MdpTlvIP and MdpTlvUnknownBool are two types I saw when capturing MDP from a WiFi AP. The types and contents were obvious based on the ASCII.

AFAIK this protocol isn't officially documented so these types (and others) may have been added to the protocol since the Wireshark dissector was created.

offset += length
break
case MdpTlvEnd:
offset = m.Length
break
default:
// Skip over unknown junk
offset += 2
length = int(data[offset-1])
offset += length
break

}
}
m.BaseLayer = BaseLayer{Contents: data, Payload: nil}
return nil
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer
func (m *MDP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
// bytes, _ := b.PrependBytes(4)
// bytes[0] = m.Version
// bytes[1] = byte(m.Type)
// binary.BigEndian.PutUint16(bytes[2:], m.Length)
return nil
}

// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (m *MDP) CanDecode() gopacket.LayerClass {
return LayerTypeMDP
}

// NextLayerType returns the layer type contained by this DecodingLayer.
func (m *MDP) NextLayerType() gopacket.LayerType {
return m.Type.LayerType()
}

func decodeMDP(data []byte, p gopacket.PacketBuilder) error {
m := &MDP{}
err := m.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(m)
return p.NextDecoder(m.NextLayerType())
}
122 changes: 122 additions & 0 deletions layers/mdp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2024 GoPacket Authors. 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 (
"net"
"reflect"
"testing"

"github.com/gopacket/gopacket"
)

// Frame 6: 279 bytes on wire (2232 bits), 279 bytes captured (2232 bits)
// Radiotap Header v0, Length 56
// 802.11 radio information
// IEEE 802.11 Data, Flags: ........C
// Logical-Link Control
// DSAP: SNAP (0xaa)
// SSAP: SNAP (0xaa)
// Control field: U, func=UI (0x03)
// Organization Code: 00:00:00 (Officially Xerox, but
// Type: Unknown (0x0712)
// Meraki Discovery Protocol
// Preamble Data: 0e0800002b58000400180004009f000009bef0040004cc1909bef004
// Device Info
// Type: Device Info (2)
// Length: 13
// Device Info: MR36-1 Eville
// Network Info
// Type: Network Info (3)
// Length: 24
// Network Info: Emeryville HQ - wireless
// Longitude
// Type: Longitude (4)
// Length: 8
// Longitude: 37.84773
// Latitude
// Type: Latitude (5)
// Length: 10
// Latitude: -122.29011
// Type 6 UID
// Type: Type 6 UID (6)
// Length: 32
// Type 6 UID: a1297f347ad7bcdfca5287fdba3d14d5
// Type 7 UID
// Type: Type 7 UID (7)
// Length: 32
// Type 7 UID: d484773ba890356f86e7afbdec76a646
// Unknown type
// Type: Unknown (11)
// Length: 11
// Unknown Data: 31302e3132382e312e3236
// Unknown type
// Type: Unknown (13)
// Length: 5
// Unknown Data: 66616c7365
// Unknown type
// Type: Unknown (12)
// Length: 4
// Unknown Data: 053b9f00
// End
// Type: End (255)
// Length: 0

var testMDPFrame = []byte{
0x00, 0x00, 0x38, 0x00, 0x2f, 0x40, 0x40, 0xa0, 0x20, 0x08, 0x00, 0xa0, 0x20, 0x08, 0x00, 0x00,
0x61, 0x2b, 0x54, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x9e, 0x09, 0xa0, 0x00, 0xd1, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x2a, 0x54, 0x02, 0x00, 0x00, 0x00, 0x00,
0x16, 0x00, 0x11, 0x03, 0xa8, 0x00, 0xd1, 0x01, 0x08, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xa6, 0x18, 0x88, 0xbe, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xbb,
0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x07, 0x12, 0x0e, 0x08, 0x00, 0x00, 0x2b, 0x58, 0x00, 0x04,
0x00, 0x18, 0x00, 0x04, 0x00, 0x9f, 0x00, 0x00, 0x09, 0xbe, 0xf0, 0x04, 0x00, 0x04, 0xcc, 0x19,
0x09, 0xbe, 0xf0, 0x04, 0x02, 0x0d, 0x4d, 0x52, 0x33, 0x36, 0x2d, 0x31, 0x20, 0x45, 0x76, 0x69,
0x6c, 0x6c, 0x65, 0x03, 0x18, 0x45, 0x6d, 0x65, 0x72, 0x79, 0x76, 0x69, 0x6c, 0x6c, 0x65, 0x20,
0x48, 0x51, 0x20, 0x2d, 0x20, 0x77, 0x69, 0x72, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x04, 0x08, 0x33,
0x37, 0x2e, 0x38, 0x34, 0x37, 0x37, 0x33, 0x05, 0x0a, 0x2d, 0x31, 0x32, 0x32, 0x2e, 0x32, 0x39,
0x30, 0x31, 0x31, 0x06, 0x20, 0x61, 0x31, 0x32, 0x39, 0x37, 0x66, 0x33, 0x34, 0x37, 0x61, 0x64,
0x37, 0x62, 0x63, 0x64, 0x66, 0x63, 0x61, 0x35, 0x32, 0x38, 0x37, 0x66, 0x64, 0x62, 0x61, 0x33,
0x64, 0x31, 0x34, 0x64, 0x35, 0x07, 0x20, 0x64, 0x34, 0x38, 0x34, 0x37, 0x37, 0x33, 0x62, 0x61,
0x38, 0x39, 0x30, 0x33, 0x35, 0x36, 0x66, 0x38, 0x36, 0x65, 0x37, 0x61, 0x66, 0x62, 0x64, 0x65,
0x63, 0x37, 0x36, 0x61, 0x36, 0x34, 0x36, 0x0b, 0x0b, 0x31, 0x30, 0x2e, 0x31, 0x32, 0x38, 0x2e,
0x31, 0x2e, 0x32, 0x36, 0x0d, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0c, 0x04, 0x05, 0x3b, 0x9f,
0x00, 0xff, 0x00, 0x56, 0xe0, 0xf1, 0xf5,
}

func TestPacketMDP(t *testing.T) {
p := gopacket.NewPacket(testMDPFrame, LinkTypeIEEE80211Radio, gopacket.Default)
if p.ErrorLayer() != nil {
t.Error("Failed to decode packet:", p.ErrorLayer().Error())
}
checkLayers(p, []gopacket.LayerType{
LayerTypeRadioTap, LayerTypeDot11, LayerTypeDot11Data, LayerTypeLLC, LayerTypeSNAP, LayerTypeMDP,
}, t)

if got, ok := p.Layer(LayerTypeMDP).(*MDP); ok {
want := &MDP{
BaseLayer: BaseLayer{
Contents: testMDPFrame[88 : len(testMDPFrame)-4],
Payload: nil,
},
Type: EthernetTypeMerakiDiscoveryProtocol,
Length: len(testMDPFrame) - 92,
PreambleData: testMDPFrame[88 : 88+28],
DeviceInfo: "MR36-1 Eville",
NetworkInfo: "Emeryville HQ - wireless",
Longitude: 37.84773,
Latitude: -122.29011,
Type6UUID: "a1297f347ad7bcdfca5287fdba3d14d5",
Type7UUID: "d484773ba890356f86e7afbdec76a646",
IPAddress: net.ParseIP("10.128.1.26"),
Type13Bool: false,
}

if !reflect.DeepEqual(got, want) {
t.Errorf("Dot11 packet processing failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", got, want)
}
}
}