From 423a5d10c211cea7072aa024a6762704d1667b28 Mon Sep 17 00:00:00 2001 From: Oliver Herms <oliver.herms@exaring.de> Date: Fri, 5 Oct 2018 16:44:28 +0200 Subject: [PATCH] Adding route mirroring --- protocols/bmp/packet/BUILD.bazel | 2 + protocols/bmp/packet/decode.go | 20 +++- protocols/bmp/packet/route_mirroring.go | 41 +++++++ protocols/bmp/packet/route_mirroring_test.go | 120 +++++++++++++++++++ 4 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 protocols/bmp/packet/route_mirroring.go create mode 100644 protocols/bmp/packet/route_mirroring_test.go diff --git a/protocols/bmp/packet/BUILD.bazel b/protocols/bmp/packet/BUILD.bazel index 9eab9b8a..4f2b195e 100644 --- a/protocols/bmp/packet/BUILD.bazel +++ b/protocols/bmp/packet/BUILD.bazel @@ -32,6 +32,8 @@ go_test( "peer_down_test.go", "peer_up_test.go", "per_peer_header_test.go", + "route_mirroring_test.go", + "route_monitoring_test.go", "stats_report_test.go", "termination_message_test.go", ], diff --git a/protocols/bmp/packet/decode.go b/protocols/bmp/packet/decode.go index 522e57a9..2e0a0682 100644 --- a/protocols/bmp/packet/decode.go +++ b/protocols/bmp/packet/decode.go @@ -5,11 +5,8 @@ import ( "fmt" ) -type Msg interface { - MsgType() uint8 -} - const ( + // MinLen is the minimal length of a BMP message MinLen = 6 RouteMonitoringType = 0 @@ -25,8 +22,16 @@ const ( ErroredPDU = 0 MessageLost = 1 + + // BMPVersion is the supported BMP version + BMPVersion = 3 ) +// Msg is an interface that every BMP message must fulfill +type Msg interface { + MsgType() uint8 +} + // Decode decodes a BMP message func Decode(msg []byte) (Msg, error) { buf := bytes.NewBuffer(msg) @@ -36,13 +41,18 @@ func Decode(msg []byte) (Msg, error) { return nil, fmt.Errorf("Unable to decode common header: %v", err) } - if ch.Version != 3 { + if ch.Version != BMPVersion { return nil, fmt.Errorf("Unsupported BMP version: %d", ch.Version) } switch ch.MsgType { case RouteMonitoringType: + rm, err := decodeRouteMonitoringMsg(buf, ch) + if err != nil { + return nil, fmt.Errorf("Unable to decode route monitoring message: %v", err) + } + return rm, err case StatisticsReportType: sr, err := decodeStatsReport(buf, ch) if err != nil { diff --git a/protocols/bmp/packet/route_mirroring.go b/protocols/bmp/packet/route_mirroring.go new file mode 100644 index 00000000..007f1262 --- /dev/null +++ b/protocols/bmp/packet/route_mirroring.go @@ -0,0 +1,41 @@ +package packet + +import ( + "bytes" + "fmt" +) + +// RouteMirroringMsg represents a route mirroring message +type RouteMirroringMsg struct { + CommonHeader *CommonHeader + PerPeerHeader *PerPeerHeader + TLVs []*InformationTLV +} + +func decodeRouteMirroringMsg(buf *bytes.Buffer, ch *CommonHeader) (*RouteMirroringMsg, error) { + rm := &RouteMirroringMsg{ + CommonHeader: ch, + } + + pph, err := decodePerPeerHeader(buf) + if err != nil { + return nil, fmt.Errorf("Unable to decode per peer header: %v", err) + } + + rm.PerPeerHeader = pph + + toRead := buf.Len() + read := 0 + + for read < toRead { + tlv, err := decodeInformationTLV(buf) + if err != nil { + return nil, fmt.Errorf("Unable to decode TLV: %v", err) + } + + rm.TLVs = append(rm.TLVs, tlv) + read += int(tlv.InformationLength) + MinInformationTLVLen + } + + return rm, nil +} diff --git a/protocols/bmp/packet/route_mirroring_test.go b/protocols/bmp/packet/route_mirroring_test.go new file mode 100644 index 00000000..dd885aca --- /dev/null +++ b/protocols/bmp/packet/route_mirroring_test.go @@ -0,0 +1,120 @@ +package packet + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDecodeRouteMirroringMsg(t *testing.T) { + tests := []struct { + name string + input []byte + ch *CommonHeader + wantFail bool + expected *RouteMirroringMsg + }{ + { + name: "Full", + input: []byte{ + 1, + 2, + 0, 0, 0, 3, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 0, 0, 200, 124, + 0, 0, 0, 123, + 0, 0, 0, 100, + 0, 0, 0, 200, + + 0, 1, 0, 2, 100, 200, + 0, 1, 0, 2, 100, 200, + }, + ch: &CommonHeader{ + MsgLength: CommonHeaderLen + PerPeerHeaderLen + 12, + }, + wantFail: false, + expected: &RouteMirroringMsg{ + CommonHeader: &CommonHeader{ + MsgLength: CommonHeaderLen + PerPeerHeaderLen + 12, + }, + PerPeerHeader: &PerPeerHeader{ + PeerType: 1, + PeerFlags: 2, + PeerDistinguisher: 3, + PeerAddress: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + PeerAS: 51324, + PeerBGPID: 123, + Timestamp: 100, + TimestampMicroSeconds: 200, + }, + TLVs: []*InformationTLV{ + { + InformationType: 1, + InformationLength: 2, + Information: []byte{100, 200}, + }, + { + InformationType: 1, + InformationLength: 2, + Information: []byte{100, 200}, + }, + }, + }, + }, + { + name: "Incomplete", + input: []byte{ + 1, + 2, + 0, 0, 0, 3, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 0, 0, 200, 124, + }, + ch: &CommonHeader{ + MsgLength: CommonHeaderLen + PerPeerHeaderLen + 12, + }, + wantFail: true, + }, + { + name: "Incomplete TLV", + input: []byte{ + 1, + 2, + 0, 0, 0, 3, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 0, 0, 200, 124, + 0, 0, 0, 123, + 0, 0, 0, 100, + 0, 0, 0, 200, + + 0, 1, 0, 2, 100, 200, + 0, 1, 0, 2, + }, + ch: &CommonHeader{ + MsgLength: CommonHeaderLen + PerPeerHeaderLen + 12, + }, + wantFail: true, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(test.input) + r, err := decodeRouteMirroringMsg(buf, test.ch) + if err != nil { + if test.wantFail { + continue + } + + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + continue + } + + if test.wantFail { + t.Errorf("Unexpected success for test %q", test.name) + continue + } + + assert.Equalf(t, test.expected, r, "Test %q", test.name) + } +} -- GitLab