diff --git a/net/ip.go b/net/ip.go
index 2855235eae0e6c44753f351f2363ed790f2ed9e3..90628ece21c0edd8dd0df4dbb7a1c86ecdf4d7c0 100644
--- a/net/ip.go
+++ b/net/ip.go
@@ -41,6 +41,27 @@ func IPv6FromBlocks(b1, b2, b3, b4, b5, b6, b7, b8 uint16) IP {
 		uint64(uint64(b5)<<48+uint64(b6)<<32+uint64(b7)<<16+uint64(b8)))
 }
 
+// IPFromBytes returns an IP address for a byte slice
+func IPFromBytes(b []byte) (IP, error) {
+	if len(b) == 4 {
+		return IPv4FromOctets(b[0], b[1], b[2], b[3]), nil
+	}
+
+	if len(b) == 16 {
+		return IPv6FromBlocks(
+			uint16(b[0])<<8+uint16(b[1]),
+			uint16(b[2])<<8+uint16(b[3]),
+			uint16(b[4])<<8+uint16(b[5]),
+			uint16(b[6])<<8+uint16(b[7]),
+			uint16(b[8])<<8+uint16(b[9]),
+			uint16(b[10])<<8+uint16(b[11]),
+			uint16(b[12])<<8+uint16(b[13]),
+			uint16(b[14])<<8+uint16(b[15])), nil
+	}
+
+	return IP{}, fmt.Errorf("byte slice has an invalid legth. Expected either 4 (IPv4) or 16 (IPv6) bytes but got: %d", len(b))
+}
+
 // Equal returns true if ip is equal to other
 func (ip IP) Equal(other IP) bool {
 	return ip == other
diff --git a/net/ip_test.go b/net/ip_test.go
index 613e342d7ffe46460007aeec692da8c39286258f..f1edd4688cb83e8277b723e45e32cacfc0b8e33e 100644
--- a/net/ip_test.go
+++ b/net/ip_test.go
@@ -228,6 +228,50 @@ func TestIPv6FromBlocks(t *testing.T) {
 	}
 }
 
+func TestIPFromBytes(t *testing.T) {
+	tests := []struct {
+		name     string
+		bytes    []byte
+		expected IP
+		wantFail bool
+	}{
+		{
+			name:  "IPV4: 172.217.16.195",
+			bytes: []byte{172, 217, 16, 195},
+			expected: IP{
+				higher:    0,
+				lower:     2899906755,
+				ipVersion: 4,
+			},
+		},
+		{
+			name:  "IPV6: IPv6 2001:678:1E0:1234:5678:DEAD:BEEF:CAFE",
+			bytes: []byte{0x20, 0x01, 0x06, 0x78, 0x01, 0xE0, 0x12, 0x34, 0x56, 0x78, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE},
+			expected: IP{
+				higher:    2306131596687708724,
+				lower:     6230974922281175806,
+				ipVersion: 6,
+			},
+		},
+		{
+			name:     "invalid length",
+			bytes:    []byte{172, 217, 123, 231, 95},
+			wantFail: true,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			ip, err := IPFromBytes(test.bytes)
+			if err == nil && test.wantFail {
+				t.Fatalf("Expected test to fail, but did not")
+			}
+
+			assert.Equal(t, test.expected, ip)
+		})
+	}
+}
+
 func TestToNetIP(t *testing.T) {
 	tests := []struct {
 		name     string
diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go
index 3f25ddf60adea2bfdf13b6043e06ef05818a999c..e008205a2ad95e97872c0a691a479efe42888c24 100644
--- a/protocols/bgp/packet/bgp.go
+++ b/protocols/bgp/packet/bgp.go
@@ -85,17 +85,26 @@ const (
 	ConnectionCollisionResolution = 7
 	OutOfResoutces                = 8
 
-	IPv4AFI                     = 1
-	IPv6AFI                     = 2
-	UnicastSAFI                 = 1
-	CapabilitiesParamType       = 2
-	MultiProtocolCapabilityCode = 1
-	AddPathCapabilityCode       = 69
-	ASN4CapabilityCode          = 65
-	AddPathReceive              = 1
-	AddPathSend                 = 2
-	AddPathSendReceive          = 3
-	ASTransASN                  = 23456
+	IPv4AFI                      = 1
+	IPv6AFI                      = 2
+	UnicastSAFI                  = 1
+	CapabilitiesParamType        = 2
+	MultiProtocolCapabilityCode  = 1
+	MultiProtocolReachNLRICode   = 14
+	MultiProtocolUnreachNLRICode = 15
+	AddPathCapabilityCode        = 69
+	ASN4CapabilityCode           = 65
+	AddPathReceive               = 1
+	AddPathSend                  = 2
+	AddPathSendReceive           = 3
+	ASTransASN                   = 23456
+)
+
+var (
+	afiAddrLenBytes = map[uint16]uint8{
+		1: 4,
+		2: 16,
+	}
 )
 
 type BGPError struct {
diff --git a/protocols/bgp/packet/helper.go b/protocols/bgp/packet/helper.go
index 1fd55ffe033864a44fca2a09f462d3105a237d13..18176cf1ec461e4f46d788d6f6c22072f83af687 100644
--- a/protocols/bgp/packet/helper.go
+++ b/protocols/bgp/packet/helper.go
@@ -1,6 +1,7 @@
 package packet
 
 import (
+	"fmt"
 	"math"
 
 	bnet "github.com/bio-routing/bio-rd/net"
@@ -11,7 +12,7 @@ func serializePrefix(pfx bnet.Prefix) []byte {
 		return []byte{}
 	}
 
-	numBytes := uint8(math.Ceil(float64(pfx.Pfxlen()) / float64(8)))
+	numBytes := uint8(math.Ceil(float64(pfx.Pfxlen()) / 8))
 
 	b := make([]byte, numBytes+1)
 	b[0] = pfx.Pfxlen()
@@ -19,3 +20,21 @@ func serializePrefix(pfx bnet.Prefix) []byte {
 
 	return b
 }
+
+func deserializePrefix(b []byte, pfxLen uint8, afi uint16) (bnet.Prefix, error) {
+	numBytes := int(math.Ceil(float64(pfxLen) / 8))
+
+	if numBytes != len(b) {
+		return bnet.Prefix{}, fmt.Errorf("could not parse prefix of legth %d. Expected %d bytes, got %d", pfxLen, numBytes, len(b))
+	}
+
+	ipBytes := make([]byte, afiAddrLenBytes[afi])
+	copy(ipBytes, b)
+
+	ip, err := bnet.IPFromBytes(ipBytes)
+	if err != nil {
+		return bnet.Prefix{}, err
+	}
+
+	return bnet.NewPfx(ip, pfxLen), nil
+}
diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go
index 64c76066015b503c9f5b5f0c848c267e062aefe2..e886f27c3f6da4116958759d1fcbddbcb1c65f0c 100644
--- a/protocols/bgp/packet/mp_reach_nlri.go
+++ b/protocols/bgp/packet/mp_reach_nlri.go
@@ -2,6 +2,7 @@ package packet
 
 import (
 	"bytes"
+	"fmt"
 
 	"github.com/taktv6/tflow2/convert"
 
@@ -31,3 +32,32 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint8 {
 
 	return uint8(tempBuf.Len())
 }
+
+func deserializeMultiProtocolReachNLRI(b []byte) (MultiProtocolReachNLRI, error) {
+	n := MultiProtocolReachNLRI{}
+	nextHopLength := uint8(0)
+	variable := make([]byte, len(b)-4)
+
+	fields := []interface{}{
+		&n.AFI,
+		&n.SAFI,
+		&nextHopLength,
+		&variable,
+	}
+	err := decode(bytes.NewBuffer(b), fields)
+	if err != nil {
+		return MultiProtocolReachNLRI{}, err
+	}
+
+	n.NextHop, err = bnet.IPFromBytes(variable[:nextHopLength])
+	if err != nil {
+		return MultiProtocolReachNLRI{}, fmt.Errorf("Failed to decode next hop IP: %v", err)
+	}
+
+	n.Prefix, err = deserializePrefix(variable[nextHopLength+2:], variable[nextHopLength+1], n.AFI)
+	if err != nil {
+		return MultiProtocolReachNLRI{}, err
+	}
+
+	return n, nil
+}
diff --git a/protocols/bgp/packet/mp_unreach_nlri.go b/protocols/bgp/packet/mp_unreach_nlri.go
index 1c0250931c03f0dd20fdfca7c006d077028c846e..e3e43822caecceda91ff72e5ff44f55c73299adb 100644
--- a/protocols/bgp/packet/mp_unreach_nlri.go
+++ b/protocols/bgp/packet/mp_unreach_nlri.go
@@ -24,3 +24,25 @@ func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint8 {
 
 	return uint8(tempBuf.Len())
 }
+
+func deserializeMultiProtocolUnreachNLRI(b []byte) (MultiProtocolUnreachNLRI, error) {
+	n := MultiProtocolUnreachNLRI{}
+	prefix := make([]byte, len(b)-3)
+
+	fields := []interface{}{
+		&n.AFI,
+		&n.SAFI,
+		&prefix,
+	}
+	err := decode(bytes.NewBuffer(b), fields)
+	if err != nil {
+		return MultiProtocolUnreachNLRI{}, err
+	}
+
+	n.Prefix, err = deserializePrefix(prefix[1:], prefix[0], n.AFI)
+	if err != nil {
+		return MultiProtocolUnreachNLRI{}, err
+	}
+
+	return n, nil
+}
diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go
index f1aae242153e09da32de808fabe40efbcc719456..77025f38ef976917f088509a8481aa3e53a363a5 100644
--- a/protocols/bgp/packet/path_attributes.go
+++ b/protocols/bgp/packet/path_attributes.go
@@ -99,6 +99,14 @@ func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, c
 		if err := pa.decodeCommunities(buf); err != nil {
 			return nil, consumed, fmt.Errorf("Failed to decode Community: %v", err)
 		}
+	case MultiProtocolReachNLRICode:
+		if err := pa.decodeMultiProtocolReachNLRI(buf); err != nil {
+			return nil, consumed, fmt.Errorf("Failed to multi protocol reachable NLRI: %v", err)
+		}
+	case MultiProtocolUnreachNLRICode:
+		if err := pa.decodeMultiProtocolUnreachNLRI(buf); err != nil {
+			return nil, consumed, fmt.Errorf("Failed to multi protocol unreachable NLRI: %v", err)
+		}
 	case AS4AggregatorAttr:
 		if err := pa.decodeAS4Aggregator(buf); err != nil {
 			return nil, consumed, fmt.Errorf("Failed to skip not supported AS4Aggregator: %v", err)
@@ -116,18 +124,53 @@ func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, c
 	return pa, consumed + pa.Length, nil
 }
 
+func (pa *PathAttribute) decodeMultiProtocolReachNLRI(buf *bytes.Buffer) error {
+	b := make([]byte, pa.Length)
+	n, err := buf.Read(b)
+	if err != nil {
+		return fmt.Errorf("Unable to read %d bytes from buffer: %v", pa.Length, err)
+	}
+	if n != int(pa.Length) {
+		return fmt.Errorf("Unable to read %d bytes from buffer, only got %d bytes", pa.Length, n)
+	}
+
+	nlri, err := deserializeMultiProtocolReachNLRI(b)
+	if err != nil {
+		return fmt.Errorf("Unable to decode MP_REACH_NLRI: %v", err)
+	}
+
+	pa.Value = nlri
+	return nil
+}
+
+func (pa *PathAttribute) decodeMultiProtocolUnreachNLRI(buf *bytes.Buffer) error {
+	b := make([]byte, pa.Length)
+	n, err := buf.Read(b)
+	if err != nil {
+		return fmt.Errorf("Unable to read %d bytes from buffer: %v", pa.Length, err)
+	}
+	if n != int(pa.Length) {
+		return fmt.Errorf("Unable to read %d bytes from buffer, only got %d bytes", pa.Length, n)
+	}
+
+	nlri, err := deserializeMultiProtocolUnreachNLRI(b)
+	if err != nil {
+		return fmt.Errorf("Unable to decode MP_UNREACH_NLRI: %v", err)
+	}
+
+	pa.Value = nlri
+	return nil
+}
+
 func (pa *PathAttribute) decodeUnknown(buf *bytes.Buffer) error {
 	u := make([]byte, pa.Length)
 
-	p := uint16(0)
 	err := decode(buf, []interface{}{&u})
 	if err != nil {
 		return fmt.Errorf("Unable to decode: %v", err)
 	}
 
 	pa.Value = u
-	p += pa.Length
-
 	return nil
 }
 
diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go
index 4a0966e6b3dee2a6bbbf3bb4712247eefc7696e1..0048f9005eba42d009e90281d17a2a2745505f70 100644
--- a/protocols/bgp/packet/path_attributes_test.go
+++ b/protocols/bgp/packet/path_attributes_test.go
@@ -746,6 +746,133 @@ func TestDecodeCommunity(t *testing.T) {
 	}
 }
 
+func TestDecodeMultiProtocolReachNLRI(t *testing.T) {
+	tests := []struct {
+		name           string
+		input          []byte
+		wantFail       bool
+		explicitLength uint16
+		expected       *PathAttribute
+	}{
+		{
+			name:           "incomplete",
+			input:          []byte{0, 0, 0, 0},
+			wantFail:       true,
+			explicitLength: 32,
+		},
+		{
+			name: "valid MP_REACH_NLRI",
+			input: []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
+			},
+			expected: &PathAttribute{
+				Length: 28,
+				Value: 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),
+				},
+			},
+		},
+	}
+
+	t.Parallel()
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			l := uint16(len(test.input))
+			if test.explicitLength != 0 {
+				l = test.explicitLength
+			}
+
+			pa := &PathAttribute{
+				Length: l,
+			}
+			err := pa.decodeMultiProtocolReachNLRI(bytes.NewBuffer(test.input))
+
+			if test.wantFail {
+				if err != nil {
+					return
+				}
+				t.Fatalf("Expected error did not happen for test %q", test.name)
+			}
+
+			if err != nil {
+				t.Fatalf("Unexpected failure for test %q: %v", test.name, err)
+			}
+
+			assert.Equal(t, test.expected, pa)
+		})
+	}
+}
+
+func TestDecodeMultiProtocolUnreachNLRI(t *testing.T) {
+	tests := []struct {
+		name           string
+		input          []byte
+		wantFail       bool
+		explicitLength uint16
+		expected       *PathAttribute
+	}{
+		{
+			name:           "incomplete",
+			input:          []byte{0, 0, 0, 0},
+			wantFail:       true,
+			explicitLength: 32,
+		},
+		{
+			name: "valid MP_UNREACH_NLRI",
+			input: []byte{
+				0x00, 0x02, // AFI
+				0x01,                                     // SAFI
+				0x2c, 0x26, 0x20, 0x01, 0x10, 0x90, 0x00, // Prefix
+			},
+			expected: &PathAttribute{
+				Length: 10,
+				Value: MultiProtocolUnreachNLRI{
+					AFI:    IPv6AFI,
+					SAFI:   UnicastSAFI,
+					Prefix: bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44),
+				},
+			},
+		},
+	}
+
+	t.Parallel()
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			l := uint16(len(test.input))
+			if test.explicitLength != 0 {
+				l = test.explicitLength
+			}
+
+			pa := &PathAttribute{
+				Length: l,
+			}
+			err := pa.decodeMultiProtocolUnreachNLRI(bytes.NewBuffer(test.input))
+
+			if test.wantFail {
+				if err != nil {
+					return
+				}
+				t.Fatalf("Expected error did not happen for test %q", test.name)
+			}
+
+			if err != nil {
+				t.Fatalf("Unexpected failure for test %q: %v", test.name, err)
+			}
+
+			assert.Equal(t, test.expected, pa)
+		})
+	}
+}
+
 func TestSetLength(t *testing.T) {
 	tests := []struct {
 		name             string