-
-
Notifications
You must be signed in to change notification settings - Fork 97
feat: Meraki Discovery Protocol layer parsing #54
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| // Copyright 2024 Google, Inc. 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 ( | ||
| "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])) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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()) | ||
| } | ||
| 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) | ||
| } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.