123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- // Copyright 2018 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 (
- "encoding/binary"
- "fmt"
- "github.com/google/gopacket"
- )
- const (
- // LCMShortHeaderMagic is the LCM small message header magic number
- LCMShortHeaderMagic uint32 = 0x4c433032
- // LCMFragmentedHeaderMagic is the LCM fragmented message header magic number
- LCMFragmentedHeaderMagic uint32 = 0x4c433033
- )
- // LCM (Lightweight Communications and Marshalling) is a set of libraries and
- // tools for message passing and data marshalling, targeted at real-time systems
- // where high-bandwidth and low latency are critical. It provides a
- // publish/subscribe message passing model and automatic
- // marshalling/unmarshalling code generation with bindings for applications in a
- // variety of programming languages.
- //
- // References
- // https://lcm-proj.github.io/
- // https://github.com/lcm-proj/lcm
- type LCM struct {
- // Common (short & fragmented header) fields
- Magic uint32
- SequenceNumber uint32
- // Fragmented header only fields
- PayloadSize uint32
- FragmentOffset uint32
- FragmentNumber uint16
- TotalFragments uint16
- // Common field
- ChannelName string
- // Gopacket helper fields
- Fragmented bool
- fingerprint LCMFingerprint
- contents []byte
- payload []byte
- }
- // LCMFingerprint is the type of a LCM fingerprint.
- type LCMFingerprint uint64
- var (
- // lcmLayerTypes contains a map of all LCM fingerprints that we support and
- // their LayerType
- lcmLayerTypes = map[LCMFingerprint]gopacket.LayerType{}
- layerTypeIndex = 1001
- )
- // RegisterLCMLayerType allows users to register decoders for the underlying
- // LCM payload. This is done based on the fingerprint that every LCM message
- // contains and which identifies it uniquely. If num is not the zero value it
- // will be used when registering with RegisterLayerType towards gopacket,
- // otherwise an incremental value starting from 1001 will be used.
- func RegisterLCMLayerType(num int, name string, fingerprint LCMFingerprint,
- decoder gopacket.Decoder) gopacket.LayerType {
- metadata := gopacket.LayerTypeMetadata{Name: name, Decoder: decoder}
- if num == 0 {
- num = layerTypeIndex
- layerTypeIndex++
- }
- lcmLayerTypes[fingerprint] = gopacket.RegisterLayerType(num, metadata)
- return lcmLayerTypes[fingerprint]
- }
- // SupportedLCMFingerprints returns a slice of all LCM fingerprints that has
- // been registered so far.
- func SupportedLCMFingerprints() []LCMFingerprint {
- fingerprints := make([]LCMFingerprint, 0, len(lcmLayerTypes))
- for fp := range lcmLayerTypes {
- fingerprints = append(fingerprints, fp)
- }
- return fingerprints
- }
- // GetLCMLayerType returns the underlying LCM message's LayerType.
- // This LayerType has to be registered by using RegisterLCMLayerType.
- func GetLCMLayerType(fingerprint LCMFingerprint) gopacket.LayerType {
- layerType, ok := lcmLayerTypes[fingerprint]
- if !ok {
- return gopacket.LayerTypePayload
- }
- return layerType
- }
- func decodeLCM(data []byte, p gopacket.PacketBuilder) error {
- lcm := &LCM{}
- err := lcm.DecodeFromBytes(data, p)
- if err != nil {
- return err
- }
- p.AddLayer(lcm)
- p.SetApplicationLayer(lcm)
- return p.NextDecoder(lcm.NextLayerType())
- }
- // DecodeFromBytes decodes the given bytes into this layer.
- func (lcm *LCM) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
- offset := 0
- lcm.Magic = binary.BigEndian.Uint32(data[offset:4])
- offset += 4
- if lcm.Magic != LCMShortHeaderMagic && lcm.Magic != LCMFragmentedHeaderMagic {
- return fmt.Errorf("Received LCM header magic %v does not match know "+
- "LCM magic numbers. Dropping packet.", lcm.Magic)
- }
- lcm.SequenceNumber = binary.BigEndian.Uint32(data[offset:8])
- offset += 4
- if lcm.Magic == LCMFragmentedHeaderMagic {
- lcm.Fragmented = true
- lcm.PayloadSize = binary.BigEndian.Uint32(data[offset : offset+4])
- offset += 4
- lcm.FragmentOffset = binary.BigEndian.Uint32(data[offset : offset+4])
- offset += 4
- lcm.FragmentNumber = binary.BigEndian.Uint16(data[offset : offset+2])
- offset += 2
- lcm.TotalFragments = binary.BigEndian.Uint16(data[offset : offset+2])
- offset += 2
- } else {
- lcm.Fragmented = false
- }
- if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) {
- buffer := make([]byte, 0)
- for _, b := range data[offset:] {
- offset++
- if b == 0 {
- break
- }
- buffer = append(buffer, b)
- }
- lcm.ChannelName = string(buffer)
- }
- lcm.fingerprint = LCMFingerprint(
- binary.BigEndian.Uint64(data[offset : offset+8]))
- lcm.contents = data[:offset]
- lcm.payload = data[offset:]
- return nil
- }
- // CanDecode returns a set of layers that LCM objects can decode.
- // As LCM objects can only decode the LCM layer, we just return that layer.
- func (lcm LCM) CanDecode() gopacket.LayerClass {
- return LayerTypeLCM
- }
- // NextLayerType specifies the LCM payload layer type following this header.
- // As LCM packets are serialized structs with uniq fingerprints for each uniq
- // combination of data types, lookup of correct layer type is based on that
- // fingerprint.
- func (lcm LCM) NextLayerType() gopacket.LayerType {
- if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) {
- return GetLCMLayerType(lcm.fingerprint)
- }
- return gopacket.LayerTypeFragment
- }
- // LayerType returns LayerTypeLCM
- func (lcm LCM) LayerType() gopacket.LayerType {
- return LayerTypeLCM
- }
- // LayerContents returns the contents of the LCM header.
- func (lcm LCM) LayerContents() []byte {
- return lcm.contents
- }
- // LayerPayload returns the payload following this LCM header.
- func (lcm LCM) LayerPayload() []byte {
- return lcm.payload
- }
- // Payload returns the payload following this LCM header.
- func (lcm LCM) Payload() []byte {
- return lcm.LayerPayload()
- }
- // Fingerprint returns the LCM fingerprint of the underlying message.
- func (lcm LCM) Fingerprint() LCMFingerprint {
- return lcm.fingerprint
- }
|