diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go
new file mode 100644
index 0000000000000000000000000000000000000000..82205b5fa8acc0fcb510764f6052986a7b8d0281
--- /dev/null
+++ b/protocols/bgp/packet/mp_reach_nlri.go
@@ -0,0 +1,48 @@
+package packet
+
+import (
+	"bytes"
+	"math"
+
+	"github.com/taktv6/tflow2/convert"
+
+	bnet "github.com/bio-routing/bio-rd/net"
+)
+
+// MultiProtocolReachNLRI represents Network Layer Reachability Information for one prefix of an IP address family (rfc4760)
+type MultiProtocolReachNLRI struct {
+	AFI     uint16
+	SAFI    uint8
+	NextHop bnet.IP
+	Prefix  bnet.Prefix
+}
+
+func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 {
+	nextHop := n.NextHop.Bytes()
+
+	tempBuf := bytes.NewBuffer(nil)
+	tempBuf.Write(convert.Uint16Byte(n.AFI))
+	tempBuf.WriteByte(n.SAFI)
+	tempBuf.WriteByte(uint8(len(nextHop))) // NextHop length
+	tempBuf.Write(nextHop)
+	tempBuf.WriteByte(0) // RESERVED
+	tempBuf.Write(n.serializePrefix())
+
+	buf.Write(tempBuf.Bytes())
+
+	return uint8(tempBuf.Len())
+}
+
+func (n *MultiProtocolReachNLRI) serializePrefix() []byte {
+	if n.Prefix.Pfxlen() == 0 {
+		return []byte{}
+	}
+
+	numBytes := uint8(math.Ceil(float64(n.Prefix.Pfxlen()) / float64(8)))
+
+	b := make([]byte, numBytes+1)
+	b[0] = n.Prefix.Pfxlen()
+	copy(b[1:numBytes+1], n.Prefix.Addr().Bytes()[0:numBytes])
+
+	return b
+}
diff --git a/protocols/bgp/packet/mp_reach_nlri_test.go b/protocols/bgp/packet/mp_reach_nlri_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..935c6661f3ea84a8cb9ad07c3eeac2284c9bd314
--- /dev/null
+++ b/protocols/bgp/packet/mp_reach_nlri_test.go
@@ -0,0 +1,42 @@
+package packet
+
+import (
+	"bytes"
+	"testing"
+
+	bnet "github.com/bio-routing/bio-rd/net"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSerializeMultiProtocolNLRI(t *testing.T) {
+	tests := []struct {
+		name     string
+		nlri     MultiProtocolReachNLRI
+		expected []byte
+	}{
+		{
+			name: "Simple IPv6 prefix",
+			nlri: MultiProtocolReachNLRI{
+				AFI:     IPv6AFI,
+				SAFI:    UnicastSAFI,
+				NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2),
+				Prefix:  bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48),
+			},
+			expected: []byte{
+				0x00, 0x02, // AFI
+				0x01,                                                                                                 // SAFI
+				0x10, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // NextHop
+				0x00,                                     // RESERVED
+				0x30, 0x26, 0x00, 0x00, 0x06, 0xff, 0x05, // Prefix
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			buf := &bytes.Buffer{}
+			test.nlri.serialize(buf)
+			assert.Equal(t, test.expected, buf.Bytes())
+		})
+	}
+}