diff --git a/.gitignore b/.gitignore
index bb99a56915e4f0f56746244dbb3affd0ee20712a..c97ea2a6069695630909b3884565f91629e3965e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,8 @@
 
 # 'go build' binary
 bio-rd
+examples/bgp
+examples/bmp
 
 # bazel directories
 /bazel-*
diff --git a/protocols/bgp/packet/decoder.go b/protocols/bgp/packet/decoder.go
index 2abfeec67a6761723119a4b8d6e2719f99649239..de86c370a6c428d06e656e5c54872f5e1e33c752 100644
--- a/protocols/bgp/packet/decoder.go
+++ b/protocols/bgp/packet/decoder.go
@@ -49,7 +49,7 @@ func decodeUpdateMsg(buf *bytes.Buffer, l uint16, opt *DecodeOptions) (*BGPUpdat
 		return msg, err
 	}
 
-	msg.WithdrawnRoutes, err = decodeNLRIs(buf, uint16(msg.WithdrawnRoutesLen))
+	msg.WithdrawnRoutes, err = decodeNLRIs(buf, uint16(msg.WithdrawnRoutesLen), IPv4AFI)
 	if err != nil {
 		return msg, err
 	}
@@ -66,7 +66,7 @@ func decodeUpdateMsg(buf *bytes.Buffer, l uint16, opt *DecodeOptions) (*BGPUpdat
 
 	nlriLen := uint16(l) - 4 - uint16(msg.TotalPathAttrLen) - uint16(msg.WithdrawnRoutesLen)
 	if nlriLen > 0 {
-		msg.NLRI, err = decodeNLRIs(buf, nlriLen)
+		msg.NLRI, err = decodeNLRIs(buf, nlriLen, IPv4AFI)
 		if err != nil {
 			return msg, err
 		}
diff --git a/protocols/bgp/packet/decoder_test.go b/protocols/bgp/packet/decoder_test.go
index 3db3b4888742093b9d29e971e7a44e1fb2fca0c5..dfa7ce6469299ed0df2127505162045b5d79cf39 100644
--- a/protocols/bgp/packet/decoder_test.go
+++ b/protocols/bgp/packet/decoder_test.go
@@ -209,11 +209,9 @@ func TestDecode(t *testing.T) {
 				Body: &BGPUpdate{
 					WithdrawnRoutesLen: 5,
 					WithdrawnRoutes: &NLRI{
-						IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-						Pfxlen: 8,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 						Next: &NLRI{
-							IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-							Pfxlen: 16,
+							Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 						},
 					},
 				},
@@ -429,11 +427,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 			},
@@ -455,11 +451,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 5,
@@ -497,11 +491,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 14,
@@ -600,11 +592,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 20,
@@ -675,11 +665,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 27,
@@ -766,11 +754,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 34,
@@ -870,11 +856,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 41,
@@ -986,11 +970,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 44,
@@ -1118,11 +1100,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 53,
@@ -1207,8 +1187,7 @@ func TestDecodeUpdateMsg(t *testing.T) {
 					},
 				},
 				NLRI: &NLRI{
-					Pfxlen: 8,
-					IP:     bnet.IPv4FromOctets(11, 0, 0, 0),
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(11, 0, 0, 0), 8),
 				},
 			},
 		},
@@ -1290,11 +1269,9 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			expected: &BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				TotalPathAttrLen: 16,
diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go
index b08edca7a83d3d794b6411df58b8f8f85866a3f5..513bf5d40265adcca1e00e4186752139eddcd126 100644
--- a/protocols/bgp/packet/mp_reach_nlri.go
+++ b/protocols/bgp/packet/mp_reach_nlri.go
@@ -4,19 +4,17 @@ import (
 	"bytes"
 	"fmt"
 
-	"github.com/taktv6/tflow2/convert"
-
 	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/util/decode"
+	"github.com/taktv6/tflow2/convert"
 )
 
-// MultiProtocolReachNLRI represents network layer reachability information for one prefix of an IP address family (rfc4760)
+// MultiProtocolReachNLRI represents network layer reachability information for an IP address family (rfc4760)
 type MultiProtocolReachNLRI struct {
-	AFI      uint16
-	SAFI     uint8
-	NextHop  bnet.IP
-	Prefixes []bnet.Prefix
-	PathID   uint32
+	AFI     uint16
+	SAFI    uint8
+	NextHop bnet.IP
+	NLRI    *NLRI
 }
 
 func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer, opt *EncodeOptions) uint16 {
@@ -28,11 +26,13 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer, opt *EncodeOptions
 	tempBuf.WriteByte(uint8(len(nextHop)))
 	tempBuf.Write(nextHop)
 	tempBuf.WriteByte(0) // RESERVED
-	for _, pfx := range n.Prefixes {
+
+	for cur := n.NLRI; cur != nil; cur = cur.Next {
 		if opt.UseAddPath {
-			tempBuf.Write(convert.Uint32Byte(n.PathID))
+			n.NLRI.serializeAddPath(tempBuf)
+		} else {
+			n.NLRI.serialize(tempBuf)
 		}
-		tempBuf.Write(serializePrefix(pfx))
 	}
 
 	buf.Write(tempBuf.Bytes())
@@ -73,34 +73,18 @@ func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error)
 	}
 	budget -= int(nextHopLength)
 
-	n.Prefixes = make([]bnet.Prefix, 0)
 	if budget == 0 {
 		return n, nil
 	}
 
 	variable = variable[1+nextHopLength:] // 1 <- RESERVED field
 
-	idx := uint16(0)
-	for idx < uint16(len(variable)) {
-		pfxLen := variable[idx]
-		numBytes := uint16(BytesInAddr(pfxLen))
-		idx++
-
-		r := uint16(len(variable)) - idx
-		if r < numBytes {
-			return MultiProtocolReachNLRI{}, fmt.Errorf("expected %d bytes for NLRI, only %d remaining", numBytes, r)
-		}
-
-		start := idx
-		end := idx + numBytes
-		pfx, err := deserializePrefix(variable[start:end], pfxLen, n.AFI)
-		if err != nil {
-			return MultiProtocolReachNLRI{}, err
-		}
-		n.Prefixes = append(n.Prefixes, pfx)
-
-		idx = idx + numBytes
+	buf := bytes.NewBuffer(variable)
+	nlri, err := decodeNLRIs(buf, uint16(buf.Len()), n.AFI)
+	if err != nil {
+		return MultiProtocolReachNLRI{}, err
 	}
+	n.NLRI = nlri
 
 	return n, nil
 }
diff --git a/protocols/bgp/packet/mp_reach_nlri_test.go b/protocols/bgp/packet/mp_reach_nlri_test.go
index cb6e5b544333d76f3244c8ef6895acb40783f149..a4878cf7d161c03fa0947d238f2203707e6c369d 100644
--- a/protocols/bgp/packet/mp_reach_nlri_test.go
+++ b/protocols/bgp/packet/mp_reach_nlri_test.go
@@ -21,8 +21,8 @@ func TestSerializeMultiProtocolReachNLRI(t *testing.T) {
 				AFI:     IPv6AFI,
 				SAFI:    UnicastSAFI,
 				NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2),
-				Prefixes: []bnet.Prefix{
-					bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48),
+				NLRI: &NLRI{
+					Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48),
 				},
 			},
 			expected: []byte{
@@ -39,10 +39,10 @@ func TestSerializeMultiProtocolReachNLRI(t *testing.T) {
 				AFI:     IPv6AFI,
 				SAFI:    UnicastSAFI,
 				NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2),
-				Prefixes: []bnet.Prefix{
-					bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48),
+				NLRI: &NLRI{
+					Prefix:         bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48),
+					PathIdentifier: 100,
 				},
-				PathID: 100,
 			},
 			expected: []byte{
 				0x00, 0x02, // AFI
diff --git a/protocols/bgp/packet/nlri.go b/protocols/bgp/packet/nlri.go
index bc404cc347b2672c2da711d5dfae2d0174f6dc98..9406c776d2dcadd66f96a4bd71220a6466d2ce31 100644
--- a/protocols/bgp/packet/nlri.go
+++ b/protocols/bgp/packet/nlri.go
@@ -4,21 +4,18 @@ import (
 	"bytes"
 	"fmt"
 	"math"
-	"net"
 
 	bnet "github.com/bio-routing/bio-rd/net"
-	"github.com/bio-routing/bio-rd/util/decode"
 	"github.com/taktv6/tflow2/convert"
 )
 
 type NLRI struct {
 	PathIdentifier uint32
-	IP             bnet.IP
-	Pfxlen         uint8
+	Prefix         bnet.Prefix
 	Next           *NLRI
 }
 
-func decodeNLRIs(buf *bytes.Buffer, length uint16) (*NLRI, error) {
+func decodeNLRIs(buf *bytes.Buffer, length uint16, afi uint16) (*NLRI, error) {
 	var ret *NLRI
 	var eol *NLRI
 	var nlri *NLRI
@@ -27,7 +24,7 @@ func decodeNLRIs(buf *bytes.Buffer, length uint16) (*NLRI, error) {
 	p := uint16(0)
 
 	for p < length {
-		nlri, consumed, err = decodeNLRI(buf)
+		nlri, consumed, err = decodeNLRI(buf, afi)
 		if err != nil {
 			return nil, fmt.Errorf("Unable to decode NLRI: %v", err)
 		}
@@ -46,39 +43,39 @@ func decodeNLRIs(buf *bytes.Buffer, length uint16) (*NLRI, error) {
 	return ret, nil
 }
 
-func decodeNLRI(buf *bytes.Buffer) (*NLRI, uint8, error) {
-	addr := make([]byte, 4)
+func decodeNLRI(buf *bytes.Buffer, afi uint16) (*NLRI, uint8, error) {
 	nlri := &NLRI{}
 
-	err := decode.Decode(buf, []interface{}{&nlri.Pfxlen})
+	consumed := uint8(0)
+	pfxLen, err := buf.ReadByte()
 	if err != nil {
-		return nil, 0, err
+		return nil, consumed, err
 	}
+	consumed++
 
-	toCopy := uint8(math.Ceil(float64(nlri.Pfxlen) / float64(OctetLen)))
-	for i := uint8(0); i < net.IPv4len%OctetLen; i++ {
-		if i < toCopy {
-			err := decode.Decode(buf, []interface{}{&addr[i]})
-			if err != nil {
-				return nil, 0, err
-			}
-		} else {
-			addr[i] = 0
-		}
+	numBytes := uint8(BytesInAddr(pfxLen))
+	bytes := make([]byte, numBytes)
+
+	r, err := buf.Read(bytes)
+	consumed += uint8(r)
+	if r < int(numBytes) {
+		return nil, consumed, fmt.Errorf("expected %d bytes for NLRI, only %d remaining", numBytes, r)
 	}
-	nlri.IP, err = bnet.IPFromBytes(addr)
+
+	pfx, err := deserializePrefix(bytes, pfxLen, afi)
 	if err != nil {
-		return nil, 0, err
+		return nil, consumed, err
 	}
+	nlri.Prefix = pfx
 
-	return nlri, toCopy + 1, nil
+	return nlri, consumed, nil
 }
 
 func (n *NLRI) serialize(buf *bytes.Buffer) uint8 {
-	buf.WriteByte(n.Pfxlen)
-	b := n.IP.Bytes()
+	buf.WriteByte(n.Prefix.Pfxlen())
+	b := n.Prefix.Addr().Bytes()
 
-	nBytes := BytesInAddr(n.Pfxlen)
+	nBytes := BytesInAddr(n.Prefix.Pfxlen())
 	buf.Write(b[:nBytes])
 
 	return nBytes + 1
diff --git a/protocols/bgp/packet/nlri_test.go b/protocols/bgp/packet/nlri_test.go
index b109fe052de7526307b499422b8abd7a6378dcb0..bd4f6653c5e18b905abdb67b0d4ac176b6677a16 100644
--- a/protocols/bgp/packet/nlri_test.go
+++ b/protocols/bgp/packet/nlri_test.go
@@ -24,14 +24,11 @@ func TestDecodeNLRIs(t *testing.T) {
 			},
 			wantFail: false,
 			expected: &NLRI{
-				IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-				Pfxlen: 24,
+				Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 24),
 				Next: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(172, 16, 0, 0),
-						Pfxlen: 17,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(172, 16, 0, 0), 17),
 					},
 				},
 			},
@@ -49,7 +46,7 @@ func TestDecodeNLRIs(t *testing.T) {
 
 	for _, test := range tests {
 		buf := bytes.NewBuffer(test.input)
-		res, err := decodeNLRIs(buf, uint16(len(test.input)))
+		res, err := decodeNLRIs(buf, uint16(len(test.input)), IPv4AFI)
 
 		if test.wantFail && err == nil {
 			t.Errorf("Expected error did not happen for test %q", test.name)
@@ -77,8 +74,7 @@ func TestDecodeNLRI(t *testing.T) {
 			},
 			wantFail: false,
 			expected: &NLRI{
-				IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-				Pfxlen: 24,
+				Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 24),
 			},
 		},
 		{
@@ -88,8 +84,7 @@ func TestDecodeNLRI(t *testing.T) {
 			},
 			wantFail: false,
 			expected: &NLRI{
-				IP:     bnet.IPv4FromOctets(192, 168, 0, 128),
-				Pfxlen: 25,
+				Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 128), 25),
 			},
 		},
 		{
@@ -115,7 +110,7 @@ func TestDecodeNLRI(t *testing.T) {
 
 	for _, test := range tests {
 		buf := bytes.NewBuffer(test.input)
-		res, _, err := decodeNLRI(buf)
+		res, _, err := decodeNLRI(buf, IPv4AFI)
 
 		if test.wantFail && err == nil {
 			t.Errorf("Expected error did not happen for test %q", test.name)
@@ -179,24 +174,21 @@ func TestNLRISerialize(t *testing.T) {
 		{
 			name: "Test #1",
 			nlri: &NLRI{
-				IP:     bnet.IPv4FromOctets(1, 2, 3, 0),
-				Pfxlen: 25,
+				Prefix: bnet.NewPfx(bnet.IPv4FromOctets(1, 2, 3, 0), 25),
 			},
 			expected: []byte{25, 1, 2, 3, 0},
 		},
 		{
 			name: "Test #2",
 			nlri: &NLRI{
-				IP:     bnet.IPv4FromOctets(1, 2, 3, 0),
-				Pfxlen: 24,
+				Prefix: bnet.NewPfx(bnet.IPv4FromOctets(1, 2, 3, 0), 24),
 			},
 			expected: []byte{24, 1, 2, 3},
 		},
 		{
 			name: "Test #3",
 			nlri: &NLRI{
-				IP:     bnet.IPv4FromOctets(100, 200, 128, 0),
-				Pfxlen: 17,
+				Prefix: bnet.NewPfx(bnet.IPv4FromOctets(100, 200, 128, 0), 17),
 			},
 			expected: []byte{17, 100, 200, 128},
 		},
@@ -220,8 +212,7 @@ func TestNLRIAddPathSerialize(t *testing.T) {
 			name: "Test #1",
 			nlri: &NLRI{
 				PathIdentifier: 100,
-				IP:             bnet.IPv4FromOctets(1, 2, 3, 0),
-				Pfxlen:         25,
+				Prefix:         bnet.NewPfx(bnet.IPv4FromOctets(1, 2, 3, 0), 25),
 			},
 			expected: []byte{0, 0, 0, 100, 25, 1, 2, 3, 0},
 		},
@@ -229,8 +220,7 @@ func TestNLRIAddPathSerialize(t *testing.T) {
 			name: "Test #2",
 			nlri: &NLRI{
 				PathIdentifier: 100,
-				IP:             bnet.IPv4FromOctets(1, 2, 3, 0),
-				Pfxlen:         24,
+				Prefix:         bnet.NewPfx(bnet.IPv4FromOctets(1, 2, 3, 0), 24),
 			},
 			expected: []byte{0, 0, 0, 100, 24, 1, 2, 3},
 		},
@@ -238,8 +228,7 @@ func TestNLRIAddPathSerialize(t *testing.T) {
 			name: "Test #3",
 			nlri: &NLRI{
 				PathIdentifier: 100,
-				IP:             bnet.IPv4FromOctets(100, 200, 128, 0),
-				Pfxlen:         17,
+				Prefix:         bnet.NewPfx(bnet.IPv4FromOctets(100, 200, 128, 0), 17),
 			},
 			expected: []byte{0, 0, 0, 100, 17, 100, 200, 128},
 		},
diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go
index 1b7cce5be4170738d58ef90574b3ed7d573f4b43..2ddc92834c5592ab5ac30d5274712a389f543be4 100644
--- a/protocols/bgp/packet/path_attributes_test.go
+++ b/protocols/bgp/packet/path_attributes_test.go
@@ -928,8 +928,8 @@ func TestDecodeMultiProtocolReachNLRI(t *testing.T) {
 					AFI:     IPv6AFI,
 					SAFI:    UnicastSAFI,
 					NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2),
-					Prefixes: []bnet.Prefix{
-						bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48),
+					NLRI: &NLRI{
+						Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2600, 0x6, 0xff05, 0, 0, 0, 0, 0), 48),
 					},
 				},
 			},
@@ -961,10 +961,9 @@ func TestDecodeMultiProtocolReachNLRI(t *testing.T) {
 			expected: &PathAttribute{
 				Length: 21,
 				Value: MultiProtocolReachNLRI{
-					AFI:      IPv6AFI,
-					SAFI:     UnicastSAFI,
-					NextHop:  bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2),
-					Prefixes: []bnet.Prefix{},
+					AFI:     IPv6AFI,
+					SAFI:    UnicastSAFI,
+					NextHop: bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0x2),
 				},
 			},
 		},
@@ -1869,8 +1868,7 @@ func TestSerialize(t *testing.T) {
 			name: "Withdraw only",
 			msg: &BGPUpdate{
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(100, 110, 120, 0),
-					Pfxlen: 24,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(100, 110, 120, 0), 24),
 				},
 			},
 			expected: []byte{
@@ -1886,8 +1884,7 @@ func TestSerialize(t *testing.T) {
 			name: "NLRI only",
 			msg: &BGPUpdate{
 				NLRI: &NLRI{
-					IP:     bnet.IPv4FromOctets(100, 110, 128, 0),
-					Pfxlen: 17,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(100, 110, 128, 0), 17),
 				},
 			},
 			expected: []byte{
@@ -1925,11 +1922,9 @@ func TestSerialize(t *testing.T) {
 			name: "Full test",
 			msg: &BGPUpdate{
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				PathAttributes: &PathAttribute{
@@ -1972,11 +1967,9 @@ func TestSerialize(t *testing.T) {
 					},
 				},
 				NLRI: &NLRI{
-					IP:     bnet.IPv4FromOctets(8, 8, 8, 0),
-					Pfxlen: 24,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(8, 8, 8, 0), 24),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(185, 65, 240, 0),
-						Pfxlen: 22,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(185, 65, 240, 0), 22),
 					},
 				},
 			},
@@ -2042,8 +2035,7 @@ func TestSerialize(t *testing.T) {
 			name: "Reflected NLRI",
 			msg: &BGPUpdate{
 				NLRI: &NLRI{
-					IP:     bnet.IPv4FromOctets(100, 110, 128, 0),
-					Pfxlen: 17,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(100, 110, 128, 0), 17),
 				},
 				PathAttributes: &PathAttribute{
 					TypeCode: OriginatorIDAttr,
@@ -2112,8 +2104,7 @@ func TestSerializeAddPath(t *testing.T) {
 			msg: &BGPUpdate{
 				WithdrawnRoutes: &NLRI{
 					PathIdentifier: 257,
-					IP:             bnet.IPv4FromOctets(100, 110, 120, 0),
-					Pfxlen:         24,
+					Prefix:         bnet.NewPfx(bnet.IPv4FromOctets(100, 110, 120, 0), 24),
 				},
 			},
 			expected: []byte{
@@ -2131,8 +2122,7 @@ func TestSerializeAddPath(t *testing.T) {
 			msg: &BGPUpdate{
 				NLRI: &NLRI{
 					PathIdentifier: 257,
-					IP:             bnet.IPv4FromOctets(100, 110, 128, 0),
-					Pfxlen:         17,
+					Prefix:         bnet.NewPfx(bnet.IPv4FromOctets(100, 110, 128, 0), 17),
 				},
 			},
 			expected: []byte{
@@ -2171,11 +2161,9 @@ func TestSerializeAddPath(t *testing.T) {
 			name: "Full test",
 			msg: &BGPUpdate{
 				WithdrawnRoutes: &NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 				PathAttributes: &PathAttribute{
@@ -2218,11 +2206,9 @@ func TestSerializeAddPath(t *testing.T) {
 					},
 				},
 				NLRI: &NLRI{
-					IP:     bnet.IPv4FromOctets(8, 8, 8, 0),
-					Pfxlen: 24,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(8, 8, 8, 0), 24),
 					Next: &NLRI{
-						IP:     bnet.IPv4FromOctets(185, 65, 240, 0),
-						Pfxlen: 22,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(185, 65, 240, 0), 22),
 					},
 				},
 			},
diff --git a/protocols/bgp/server/fsm_address_family.go b/protocols/bgp/server/fsm_address_family.go
index 8eac38966eab6533ed42a3a44207c1cfcbdb0f42..8d3e042ed5b600d0a4a77932a4be2141ea41b242 100644
--- a/protocols/bgp/server/fsm_address_family.go
+++ b/protocols/bgp/server/fsm_address_family.go
@@ -107,19 +107,16 @@ func (f *fsmAddressFamily) processUpdate(u *packet.BGPUpdate) {
 
 func (f *fsmAddressFamily) withdraws(u *packet.BGPUpdate) {
 	for r := u.WithdrawnRoutes; r != nil; r = r.Next {
-		pfx := bnet.NewPfx(r.IP, r.Pfxlen)
-		f.adjRIBIn.RemovePath(pfx, nil)
+		f.adjRIBIn.RemovePath(r.Prefix, nil)
 	}
 }
 
 func (f *fsmAddressFamily) updates(u *packet.BGPUpdate) {
 	for r := u.NLRI; r != nil; r = r.Next {
-		pfx := bnet.NewPfx(r.IP, r.Pfxlen)
-
 		path := f.newRoutePath()
 		f.processAttributes(u.PathAttributes, path)
 
-		f.adjRIBIn.AddPath(pfx, path)
+		f.adjRIBIn.AddPath(r.Prefix, path)
 	}
 }
 
@@ -154,8 +151,12 @@ func (f *fsmAddressFamily) multiProtocolUpdate(path *route.Path, nlri packet.Mul
 
 	path.BGPPath.NextHop = nlri.NextHop
 
-	for _, pfx := range nlri.Prefixes {
-		f.adjRIBIn.AddPath(pfx, path)
+	if nlri.NLRI == nil {
+		return
+	}
+
+	for n := nlri.NLRI; n != nil; n = n.Next {
+		f.adjRIBIn.AddPath(n.Prefix, path)
 	}
 }
 
diff --git a/protocols/bgp/server/update_helper_test.go b/protocols/bgp/server/update_helper_test.go
index a7f45b5b39ee7279c44168395303ac6480cceff0..fee9d895b8d491599eeaf5f4e9ba560eee5dc848 100644
--- a/protocols/bgp/server/update_helper_test.go
+++ b/protocols/bgp/server/update_helper_test.go
@@ -6,9 +6,8 @@ import (
 	"io"
 	"testing"
 
-	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
-
 	bnet "github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -49,11 +48,9 @@ func TestSerializeAndSendUpdate(t *testing.T) {
 			testUpdate: &packet.BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &packet.NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &packet.NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 			},
@@ -79,11 +76,9 @@ func TestSerializeAndSendUpdate(t *testing.T) {
 			testUpdate: &packet.BGPUpdate{
 				WithdrawnRoutesLen: 5,
 				WithdrawnRoutes: &packet.NLRI{
-					IP:     bnet.IPv4FromOctets(10, 0, 0, 0),
-					Pfxlen: 8,
+					Prefix: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
 					Next: &packet.NLRI{
-						IP:     bnet.IPv4FromOctets(192, 168, 0, 0),
-						Pfxlen: 16,
+						Prefix: bnet.NewPfx(bnet.IPv4FromOctets(192, 168, 0, 0), 16),
 					},
 				},
 			},
diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go
index ca7a11d5f9738d07c82c55591830652ab4ec6b11..87e999caca45a87a124ee1f2269db7169d0eae80 100644
--- a/protocols/bgp/server/update_sender.go
+++ b/protocols/bgp/server/update_sender.go
@@ -7,11 +7,10 @@ import (
 	"sync"
 	"time"
 
+	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/bio-routing/bio-rd/routingtable"
-
-	bnet "github.com/bio-routing/bio-rd/net"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -191,8 +190,7 @@ func (u *UpdateSender) bgpUpdate(pfxs []bnet.Prefix, pa *packet.PathAttribute, p
 	for _, pfx := range pfxs {
 		nlri = &packet.NLRI{
 			PathIdentifier: pathID,
-			IP:             pfx.Addr(),
-			Pfxlen:         pfx.Pfxlen(),
+			Prefix:         pfx,
 			Next:           update.NLRI,
 		}
 		update.NLRI = nlri
@@ -207,11 +205,10 @@ func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.Pat
 	attrs := &packet.PathAttribute{
 		TypeCode: packet.MultiProtocolReachNLRICode,
 		Value: packet.MultiProtocolReachNLRI{
-			AFI:      u.addressFamily.afi,
-			SAFI:     u.addressFamily.safi,
-			NextHop:  nextHop,
-			Prefixes: pfxs,
-			PathID:   pathID,
+			AFI:     u.addressFamily.afi,
+			SAFI:    u.addressFamily.safi,
+			NextHop: nextHop,
+			NLRI:    u.nlriForPrefixes(pfxs, pathID),
 		},
 	}
 	attrs.Next = pa
@@ -221,6 +218,27 @@ func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.Pat
 	}
 }
 
+func (u *UpdateSender) nlriForPrefixes(pfxs []bnet.Prefix, pathID uint32) *packet.NLRI {
+	var prev, res *packet.NLRI
+	for _, pfx := range pfxs {
+		cur := &packet.NLRI{
+			Prefix:         pfx,
+			PathIdentifier: pathID,
+		}
+
+		if res == nil {
+			res = cur
+			prev = cur
+			continue
+		}
+
+		prev.Next = cur
+		prev = cur
+	}
+
+	return res
+}
+
 func (u *UpdateSender) copyAttributesWithoutNextHop(pa *packet.PathAttribute) (attrs *packet.PathAttribute, nextHop bnet.IP) {
 	var curCopy, lastCopy *packet.PathAttribute
 	for cur := pa; cur != nil; cur = cur.Next {
@@ -276,8 +294,7 @@ func (u *UpdateSender) withdrawPrefixIPv4(out io.Writer, pfx bnet.Prefix, p *rou
 	update := &packet.BGPUpdate{
 		WithdrawnRoutes: &packet.NLRI{
 			PathIdentifier: p.BGPPath.PathIdentifier,
-			IP:             pfx.Addr(),
-			Pfxlen:         pfx.Pfxlen(),
+			Prefix:         pfx,
 		},
 	}