diff --git a/protocols/bmp/packet/per_peer_header.go b/protocols/bmp/packet/per_peer_header.go
index 87231c86920a6729ce72a5de9cbb6a4f4f12e122..f17a92014a496f68d6f1af280f854c6b434ac01a 100644
--- a/protocols/bmp/packet/per_peer_header.go
+++ b/protocols/bmp/packet/per_peer_header.go
@@ -7,6 +7,11 @@ import (
 	"github.com/bio-routing/tflow2/convert"
 )
 
+const (
+	// PerPeerHeaderLen is the length of a per peer header
+	PerPeerHeaderLen = 38
+)
+
 // PerPeerHeader represents a BMP per peer header
 type PerPeerHeader struct {
 	PeerType              uint8
diff --git a/protocols/bmp/packet/route_mirroring.go b/protocols/bmp/packet/route_mirroring.go
deleted file mode 100644
index 9c40969f7215a143587237b30d213bdd01db4a74..0000000000000000000000000000000000000000
--- a/protocols/bmp/packet/route_mirroring.go
+++ /dev/null
@@ -1 +0,0 @@
-package packet
diff --git a/protocols/bmp/packet/route_monitoring.go b/protocols/bmp/packet/route_monitoring.go
index 9c40969f7215a143587237b30d213bdd01db4a74..d76aa453fd9ecb5869f4f6e1436db4db83029eee 100644
--- a/protocols/bmp/packet/route_monitoring.go
+++ b/protocols/bmp/packet/route_monitoring.go
@@ -1 +1,46 @@
 package packet
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/bio-routing/bio-rd/util/decoder"
+)
+
+// RouteMonitoringMsg represents a Route Monitoring Message
+type RouteMonitoringMsg struct {
+	CommonHeader  *CommonHeader
+	PerPeerHeader *PerPeerHeader
+	BGPUpdate     []byte
+}
+
+// MsgType returns the type of this message
+func (rm *RouteMonitoringMsg) MsgType() uint8 {
+	return rm.CommonHeader.MsgType
+}
+
+func decodeRouteMonitoringMsg(buf *bytes.Buffer, ch *CommonHeader) (*RouteMonitoringMsg, error) {
+	rm := &RouteMonitoringMsg{
+		CommonHeader: ch,
+	}
+
+	pph, err := decodePerPeerHeader(buf)
+	if err != nil {
+		return nil, fmt.Errorf("Unable to decode per peer header: %v", err)
+	}
+
+	rm.PerPeerHeader = pph
+
+	rm.BGPUpdate = make([]byte, ch.MsgLength-CommonHeaderLen-PerPeerHeaderLen)
+
+	fields := []interface{}{
+		&rm.BGPUpdate,
+	}
+
+	err = decoder.Decode(buf, fields)
+	if err != nil {
+		return nil, err
+	}
+
+	return rm, nil
+}
diff --git a/protocols/bmp/packet/route_monitoring_test.go b/protocols/bmp/packet/route_monitoring_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..c6cee1f7f1aea3e48bcf57177fcc0f34b2aaf1d8
--- /dev/null
+++ b/protocols/bmp/packet/route_monitoring_test.go
@@ -0,0 +1,89 @@
+package packet
+
+import (
+	"bytes"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestDecodeRouteMonitoringMsg(t *testing.T) {
+	tests := []struct {
+		name     string
+		input    []byte
+		ch       *CommonHeader
+		wantFail bool
+		expected *RouteMonitoringMsg
+	}{
+		{
+			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,
+
+				100, 110, 120,
+			},
+			ch: &CommonHeader{
+				MsgLength: CommonHeaderLen + PerPeerHeaderLen + 3,
+			},
+			wantFail: false,
+			expected: &RouteMonitoringMsg{
+				CommonHeader: &CommonHeader{
+					MsgLength: CommonHeaderLen + PerPeerHeaderLen + 3,
+				},
+				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,
+				},
+				BGPUpdate: []byte{
+					100, 110, 120,
+				},
+			},
+		},
+		{
+			name: "Incomplete per peer header",
+			input: []byte{
+				1,
+				2,
+				0, 0, 0, 3,
+				1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+			},
+			ch: &CommonHeader{
+				MsgLength: CommonHeaderLen + PerPeerHeaderLen + 3,
+			},
+			wantFail: true,
+		},
+	}
+
+	for _, test := range tests {
+		buf := bytes.NewBuffer(test.input)
+		r, err := decodeRouteMonitoringMsg(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)
+	}
+}