| // 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" |
| "errors" |
| "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 { |
| if len(data) < 8 { |
| df.SetTruncated() |
| return errors.New("LCM < 8 bytes") |
| } |
| 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 |
| } |