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: 1 addition & 1 deletion .travis.golint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
cd "$(dirname $0)"

go get github.com/golang/lint/golint
DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing"
DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing defrag/lcmdefrag"
# Add subdirectories here as we clean up golint on each.
for subdir in $DIRS; do
pushd $subdir
Expand Down
2 changes: 1 addition & 1 deletion .travis.govet.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

cd "$(dirname $0)"
DIRS=". layers pcap pcapgo pfring tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs"
DIRS=". layers pcap pcapgo pfring tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs defrag/lcmdefrag"
set -e
for subdir in $DIRS; do
pushd $subdir
Expand Down
144 changes: 144 additions & 0 deletions defrag/lcmdefrag/lcmdefrag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// 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 lcmdefrag contains a defragmenter for LCM messages.
package lcmdefrag

import (
"fmt"
"time"

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)

const (
// Packages are cleaned up/removed after no input was received for this
// amount of seconds.
timeout time.Duration = 3 * time.Second
)

type lcmPacket struct {
lastPacket time.Time
done bool
recFrags uint16
totalFrags uint16
frags map[uint16]*layers.LCM
}

// LCMDefragmenter supports defragmentation of LCM messages.
//
// References
// https://lcm-proj.github.io/
// https://github.com/lcm-proj/lcm
type LCMDefragmenter struct {
packets map[uint32]*lcmPacket
}

func newLCMPacket(totalFrags uint16) *lcmPacket {
return &lcmPacket{
done: false,
recFrags: 0,
totalFrags: totalFrags,
frags: make(map[uint16]*layers.LCM),
}
}

// NewLCMDefragmenter returns a new LCMDefragmenter.
func NewLCMDefragmenter() *LCMDefragmenter {
return &LCMDefragmenter{
packets: make(map[uint32]*lcmPacket),
}
}

func (lp *lcmPacket) append(in *layers.LCM) {
lp.frags[in.FragmentNumber] = in
lp.recFrags++
lp.lastPacket = time.Now()
}

func (lp *lcmPacket) assemble() (out *layers.LCM, err error) {
var blob []byte

//Extract packets
for i := uint16(0); i < lp.totalFrags; i++ {
fragment, ok := lp.frags[i]
if !ok {
err = fmt.Errorf("Tried to defragment incomplete packet. Waiting "+
"for more potential (unordered) packets... %d", i)
return
}

// For the very first packet, we also want the header.
if i == 0 {
blob = append(blob, fragment.LayerContents()...)
}

// Append the data for each packet.
blob = append(blob, fragment.Payload()...)
}

packet := gopacket.NewPacket(blob, layers.LayerTypeLCM, gopacket.NoCopy)
lcmHdrLayer := packet.Layer(layers.LayerTypeLCM)
out, ok := lcmHdrLayer.(*layers.LCM)
if !ok {
err = fmt.Errorf("Error while decoding the defragmented packet. " +
"Erasing/dropping packet.")
}

lp.done = true

return
}

func (ld *LCMDefragmenter) cleanUp() {
for key, packet := range ld.packets {
if packet.done || time.Now().Sub(packet.lastPacket) > timeout {
delete(ld.packets, key)
}
}
}

// Defrag takes a reference to an LCM packet and processes it.
// In case the packet does not need to be defragmented, it immediately returns
// the as in passed reference. In case in was the last missing fragment, out
// will be the defragmented packet. If in was a fragment, but we are awaiting
// more, out will be set to nil.
// In the case that in was nil, we will just run the internal cleanup of the
// defragmenter that times out packages.
// If an error was encountered during defragmentation, out will also be nil,
// while err will contain further information on the failure.
func (ld *LCMDefragmenter) Defrag(in *layers.LCM) (out *layers.LCM, err error) {
// Timeout old packages and erase error prone ones.
ld.cleanUp()

// For running cleanup only
if in == nil {
return
}

// Quick check if this is acutally a single packet. In that case, just
// return it quickly.
if !in.Fragmented {
out = in
return
}

// Do we need to start a new fragments obj?
if _, ok := ld.packets[in.SequenceNumber]; !ok {
ld.packets[in.SequenceNumber] = newLCMPacket(in.TotalFragments)
}

// Append the packet
ld.packets[in.SequenceNumber].append(in)

// Check if this is the last package of that series
if ld.packets[in.SequenceNumber].recFrags == in.TotalFragments {
out, err = ld.packets[in.SequenceNumber].assemble()
}

return
}
105 changes: 105 additions & 0 deletions defrag/lcmdefrag/lcmdefrag_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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 lcmdefrag

import (
"testing"

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)

var (
fragmentOne = []byte{
0x4c, 0x43, 0x30, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d,
0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x02, 0x4c, 0x43, 0x4d, 0x5f,
0x53, 0x45, 0x4c, 0x46, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x00, 0x6c, 0x63,
0x6d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x74, 0x65, 0x73, 0x74,
}

fragmentTwo = []byte{
0x4c, 0x43, 0x30, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d,
0x00, 0x00, 0x00, 0x2d, 0x00, 0x01, 0x00, 0x02, 0x6c, 0x63, 0x6d, 0x20,
0x73, 0x65, 0x6c, 0x66, 0x20, 0x74, 0x65, 0x73, 0x74,
}

completePacket = []byte{
0x4c, 0x43, 0x30, 0x32, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x43, 0x4d, 0x5f,
0x53, 0x45, 0x4c, 0x46, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x00, 0x6c, 0x63,
0x6d, 0x20, 0x73, 0x65, 0x6c, 0x66, 0x20, 0x74, 0x65, 0x73, 0x74,
}
)

func TestOrderedLCMDefrag(t *testing.T) {
defragmenter := NewLCMDefragmenter()
var err error

packet := gopacket.NewPacket(fragmentOne, layers.LayerTypeLCM, gopacket.NoCopy)
lcm := packet.Layer(layers.LayerTypeLCM).(*layers.LCM)

lcm, err = defragmenter.Defrag(lcm)
if lcm != nil {
t.Fatal("Returned incomplete LCM message.")
}
if err != nil {
t.Fatal(err)
}

packet = gopacket.NewPacket(fragmentTwo, layers.LayerTypeLCM, gopacket.NoCopy)
lcm = packet.Layer(layers.LayerTypeLCM).(*layers.LCM)

lcm, err = defragmenter.Defrag(lcm)
if lcm == nil {
t.Fatal("Did not receive defragmented LCM message.")
}
if err != nil {
t.Fatal(err)
}
}

func TestUnorderedLCMDefrag(t *testing.T) {
defragmenter := NewLCMDefragmenter()
var err error

packet := gopacket.NewPacket(fragmentTwo, layers.LayerTypeLCM, gopacket.NoCopy)
lcm := packet.Layer(layers.LayerTypeLCM).(*layers.LCM)

lcm, err = defragmenter.Defrag(lcm)
if lcm != nil {
t.Fatal("Returned incomplete LCM message.")
}
if err != nil {
t.Fatal(err)
}

packet = gopacket.NewPacket(fragmentOne, layers.LayerTypeLCM, gopacket.NoCopy)
lcm = packet.Layer(layers.LayerTypeLCM).(*layers.LCM)

lcm, err = defragmenter.Defrag(lcm)
if lcm == nil {
t.Fatal("Did not receive defragmented LCM message.")
}
if err != nil {
t.Fatal(err)
}
}

func TestNonLCMDefrag(t *testing.T) {
defragmenter := NewLCMDefragmenter()
var err error

packet := gopacket.NewPacket(completePacket, layers.LayerTypeLCM, gopacket.NoCopy)
lcm := packet.Layer(layers.LayerTypeLCM).(*layers.LCM)

lcm, err = defragmenter.Defrag(lcm)
if lcm == nil {
t.Fatal("Did not receive complete LCM message.")
}
if err != nil {
t.Fatal(err)
}
}
6 changes: 6 additions & 0 deletions gc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ if [ -f /usr/include/pfring.h ]; then
go build
popd
fi
pushd ip4defrag
go test ./...
popd
pushd defrag
go test ./...
popd

for travis_script in `ls .travis.*.sh`; do
./$travis_script
Expand Down
6 changes: 3 additions & 3 deletions layers/lcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,11 @@ func (lcm LCM) CanDecode() gopacket.LayerClass {
// combination of data types, lookup of correct layer type is based on that
// fingerprint.
func (lcm LCM) NextLayerType() gopacket.LayerType {
if lcm.Fragmented {
return gopacket.LayerTypeFragment
if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) {
return GetLCMLayerType(lcm.fingerprint)
}

return GetLCMLayerType(lcm.fingerprint)
return gopacket.LayerTypeFragment
}

// LayerType returns LayerTypeLCM
Expand Down