diff --git a/config/peer.go b/config/peer.go
index 82a9f61f56fea089384b18ada019d4192834d6ea..fa963092ec84cf76d3db5ce05c45672131cd3f93 100644
--- a/config/peer.go
+++ b/config/peer.go
@@ -21,8 +21,6 @@ type Peer struct {
 	PeerAS                  uint32
 	Passive                 bool
 	RouterID                uint32
-	AddPathSend             routingtable.ClientOptions
-	AddPathRecv             bool
 	RouteServerClient       bool
 	RouteReflectorClient    bool
 	RouteReflectorClusterID uint32
@@ -35,4 +33,6 @@ type AddressFamilyConfig struct {
 	RIB          *locRIB.LocRIB
 	ImportFilter *filter.Filter
 	ExportFilter *filter.Filter
+	AddPathSend  routingtable.ClientOptions
+	AddPathRecv  bool
 }
diff --git a/main_ipv4.go b/main_ipv4.go
index 0d3c5a7cd4b0ce93fbe645136bd588cfbc872c2b..c280c0723e28449664dcf2692f0150e4a60c5fc2 100644
--- a/main_ipv4.go
+++ b/main_ipv4.go
@@ -40,13 +40,13 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		KeepAlive:         time.Second * 30,
 		Passive:           true,
 		RouterID:          b.RouterID(),
-		AddPathSend: routingtable.ClientOptions{
-			MaxPaths: 10,
-		},
 		IPv4: &config.AddressFamilyConfig{
 			RIB:          rib,
 			ImportFilter: filter.NewAcceptAllFilter(),
 			ExportFilter: filter.NewAcceptAllFilter(),
+			AddPathSend: routingtable.ClientOptions{
+				MaxPaths: 10,
+			},
 		},
 		RouteServerClient: true,
 	})
@@ -62,15 +62,15 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		KeepAlive:         time.Second * 30,
 		Passive:           true,
 		RouterID:          b.RouterID(),
-		AddPathSend: routingtable.ClientOptions{
-			MaxPaths: 10,
-		},
-		AddPathRecv:       true,
 		RouteServerClient: true,
 		IPv4: &config.AddressFamilyConfig{
 			RIB:          rib,
 			ImportFilter: filter.NewAcceptAllFilter(),
 			ExportFilter: filter.NewAcceptAllFilter(),
+			AddPathSend: routingtable.ClientOptions{
+				MaxPaths: 10,
+			},
+			AddPathRecv: true,
 		},
 	})
 }
diff --git a/main_ipv6.go b/main_ipv6.go
index 2d4a24100e2e1eea680fa63368235495846456ac..30467c00d23fb2d367f8110c0c02fd0e431f4ab6 100644
--- a/main_ipv6.go
+++ b/main_ipv6.go
@@ -38,13 +38,13 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		KeepAlive:         time.Second * 30,
 		Passive:           true,
 		RouterID:          b.RouterID(),
-		AddPathSend: routingtable.ClientOptions{
-			BestOnly: true,
-		},
 		IPv6: &config.AddressFamilyConfig{
 			RIB:          rib,
 			ImportFilter: filter.NewAcceptAllFilter(),
 			ExportFilter: filter.NewDrainFilter(),
+			AddPathSend: routingtable.ClientOptions{
+				BestOnly: true,
+			},
 		},
 	})
 
@@ -59,13 +59,13 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		KeepAlive:         time.Second * 30,
 		Passive:           true,
 		RouterID:          b.RouterID(),
-		AddPathSend: routingtable.ClientOptions{
-			BestOnly: true,
-		},
 		IPv6: &config.AddressFamilyConfig{
 			RIB:          rib,
 			ImportFilter: filter.NewDrainFilter(),
 			ExportFilter: filter.NewAcceptAllFilter(),
+			AddPathSend: routingtable.ClientOptions{
+				BestOnly: true,
+			},
 		},
 	})
 }
diff --git a/protocols/bgp/packet/BUILD.bazel b/protocols/bgp/packet/BUILD.bazel
index f9886e41d0eea9ee21bac03dc902ad7f4218b131..9ea14d6607ee4dfe18b135e29c8de7c9d3a4b8a3 100644
--- a/protocols/bgp/packet/BUILD.bazel
+++ b/protocols/bgp/packet/BUILD.bazel
@@ -4,7 +4,9 @@ go_library(
     name = "go_default_library",
     srcs = [
         "bgp.go",
+        "decode_options.go",
         "decoder.go",
+        "encode_options.go",
         "encoder.go",
         "helper.go",
         "mp_reach_nlri.go",
diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go
index 2ef1009d179cc98c0f197cfa827b21c5ac859016..80c79ae74fe72e38a5afb2267bc32a35a021d38b 100644
--- a/protocols/bgp/packet/bgp.go
+++ b/protocols/bgp/packet/bgp.go
@@ -158,3 +158,15 @@ type PathAttribute struct {
 	Value          interface{}
 	Next           *PathAttribute
 }
+
+// AFIName returns the name of an address family
+func AFIName(afi uint16) string {
+	switch afi {
+	case IPv4AFI:
+		return "IPv4"
+	case IPv6AFI:
+		return "IPv6"
+	default:
+		return "Unknown AFI"
+	}
+}
diff --git a/protocols/bgp/packet/decode_options.go b/protocols/bgp/packet/decode_options.go
new file mode 100644
index 0000000000000000000000000000000000000000..9cea580caaccdd2c2176bae4ab5081f281a6f729
--- /dev/null
+++ b/protocols/bgp/packet/decode_options.go
@@ -0,0 +1,5 @@
+package packet
+
+type DecodeOptions struct {
+	Use32BitASN bool
+}
diff --git a/protocols/bgp/packet/decoder.go b/protocols/bgp/packet/decoder.go
index cd951463a15215bd6aaa43e8799e59c95cee549b..0d719fc04c20779ae818e21e756ba925c4bb4e5e 100644
--- a/protocols/bgp/packet/decoder.go
+++ b/protocols/bgp/packet/decoder.go
@@ -6,12 +6,11 @@ import (
 	"fmt"
 	"net"
 
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
 	"github.com/taktv6/tflow2/convert"
 )
 
 // Decode decodes a BGP message
-func Decode(buf *bytes.Buffer, opt *types.Options) (*BGPMessage, error) {
+func Decode(buf *bytes.Buffer, opt *DecodeOptions) (*BGPMessage, error) {
 	hdr, err := decodeHeader(buf)
 	if err != nil {
 		return nil, fmt.Errorf("Failed to decode header: %v", err)
@@ -28,7 +27,7 @@ func Decode(buf *bytes.Buffer, opt *types.Options) (*BGPMessage, error) {
 	}, nil
 }
 
-func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16, opt *types.Options) (interface{}, error) {
+func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16, opt *DecodeOptions) (interface{}, error) {
 	switch msgType {
 	case OpenMsg:
 		return decodeOpenMsg(buf)
@@ -42,7 +41,7 @@ func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16, opt *types.Option
 	return nil, fmt.Errorf("Unknown message type: %d", msgType)
 }
 
-func decodeUpdateMsg(buf *bytes.Buffer, l uint16, opt *types.Options) (*BGPUpdate, error) {
+func decodeUpdateMsg(buf *bytes.Buffer, l uint16, opt *DecodeOptions) (*BGPUpdate, error) {
 	msg := &BGPUpdate{}
 
 	err := decode(buf, []interface{}{&msg.WithdrawnRoutesLen})
diff --git a/protocols/bgp/packet/decoder_test.go b/protocols/bgp/packet/decoder_test.go
index ac611ceff133e4375e70995a2b564900671ed8aa..74e5b84f978f084c38360b85b72030fe79fe945e 100644
--- a/protocols/bgp/packet/decoder_test.go
+++ b/protocols/bgp/packet/decoder_test.go
@@ -74,7 +74,7 @@ func BenchmarkDecodeUpdateMsg(b *testing.B) {
 
 	for i := 0; i < b.N; i++ {
 		buf := bytes.NewBuffer(input)
-		_, err := decodeUpdateMsg(buf, uint16(len(input)), &types.Options{})
+		_, err := decodeUpdateMsg(buf, uint16(len(input)), &DecodeOptions{})
 		if err != nil {
 			fmt.Printf("decodeUpdateMsg failed: %v\n", err)
 		}
@@ -255,7 +255,7 @@ func TestDecode(t *testing.T) {
 
 	for _, test := range tests {
 		buf := bytes.NewBuffer(test.input)
-		msg, err := Decode(buf, &types.Options{})
+		msg, err := Decode(buf, &DecodeOptions{})
 
 		if err != nil && !test.wantFail {
 			t.Errorf("Unexpected error in test %d: %v", test.testNum, err)
@@ -1369,7 +1369,7 @@ func TestDecodeUpdateMsg(t *testing.T) {
 			if l == 0 {
 				l = uint16(len(test.input))
 			}
-			msg, err := decodeUpdateMsg(buf, l, &types.Options{})
+			msg, err := decodeUpdateMsg(buf, l, &DecodeOptions{})
 
 			if err != nil && !test.wantFail {
 				t.Fatalf("Unexpected error in test %d: %v", test.testNum, err)
@@ -1405,7 +1405,7 @@ func TestDecodeMsgBody(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		res, err := decodeMsgBody(test.buffer, test.msgType, test.length, &types.Options{})
+		res, err := decodeMsgBody(test.buffer, test.msgType, test.length, &DecodeOptions{})
 		if test.wantFail && err == nil {
 			t.Errorf("Expected error dit not happen in test %q", test.name)
 		}
diff --git a/protocols/bgp/packet/encode_options.go b/protocols/bgp/packet/encode_options.go
new file mode 100644
index 0000000000000000000000000000000000000000..6cf08a5e6175f90b98fec9d79e9a416c5c93ff67
--- /dev/null
+++ b/protocols/bgp/packet/encode_options.go
@@ -0,0 +1,6 @@
+package packet
+
+type EncodeOptions struct {
+	Use32BitASN bool
+	UseAddPath  bool
+}
diff --git a/protocols/bgp/packet/fuzzing.go b/protocols/bgp/packet/fuzzing.go
index 9f829cc89c3dcfb7566287fa9c6c3ffbab11ce5b..d821b53f99b275b05ef15f246f725ab1a8977936 100644
--- a/protocols/bgp/packet/fuzzing.go
+++ b/protocols/bgp/packet/fuzzing.go
@@ -1,11 +1,12 @@
-// foobar
-// +bu ild go fuzz
+// +build go fuzz
 
 package packet
 
 import (
 	"bytes"
 
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+
 	"github.com/bio-routing/bio-rd/protocols/bgp/types"
 )
 
@@ -16,9 +17,8 @@ const (
 )
 
 func Fuzz(data []byte) int {
-
 	buf := bytes.NewBuffer(data)
-	for _, option := range getAllOptions() {
+	for _, option := range getAllDecodingOptions() {
 		msg, err := Decode(buf, &option)
 		if err != nil {
 			if msg != nil {
@@ -26,27 +26,21 @@ func Fuzz(data []byte) int {
 			}
 
 		}
+
 		return INC_PRIO
 	}
+
 	return KEEP
 }
 
-func getAllOptions() []types.Options {
+func getAllDecodingOptions() []DecodeOptions {
 	parameters := []bool{true, false}
-	var ret []types.Options
+	var ret []DecodeOptions
 	for _, octet := range parameters {
-		for _, mpbgp4 := range parameters {
-			for _, mpbgp6 := range parameters {
-				for _, addPathX := range parameters {
-					ret = append(ret, types.Options{
-						Supports4OctetASN: octet,
-						MultiProtocolIPv4: mpbgp4,
-						MultiProtocolIPv6: mpbgp6,
-						AddPathRX:         addPathX,
-					})
-				}
-			}
-		}
+		ret = append(ret, DecodeOptions{
+			Use32BitASN: octet
+		})
 	}
+
 	return ret
 }
diff --git a/protocols/bgp/packet/mp_reach_nlri.go b/protocols/bgp/packet/mp_reach_nlri.go
index 65665f29182e3b81e337d2f1bc8e25d2afbc8b50..32009637f8b17fd282392ad0a7231a67940ce44b 100644
--- a/protocols/bgp/packet/mp_reach_nlri.go
+++ b/protocols/bgp/packet/mp_reach_nlri.go
@@ -15,9 +15,10 @@ type MultiProtocolReachNLRI struct {
 	SAFI     uint8
 	NextHop  bnet.IP
 	Prefixes []bnet.Prefix
+	PathID   uint32
 }
 
-func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint16 {
+func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer, opt *EncodeOptions) uint16 {
 	nextHop := n.NextHop.Bytes()
 
 	tempBuf := bytes.NewBuffer(nil)
@@ -27,6 +28,9 @@ func (n *MultiProtocolReachNLRI) serialize(buf *bytes.Buffer) uint16 {
 	tempBuf.Write(nextHop)
 	tempBuf.WriteByte(0) // RESERVED
 	for _, pfx := range n.Prefixes {
+		if opt.UseAddPath {
+			tempBuf.Write(convert.Uint32Byte(n.PathID))
+		}
 		tempBuf.Write(serializePrefix(pfx))
 	}
 
diff --git a/protocols/bgp/packet/mp_reach_nlri_test.go b/protocols/bgp/packet/mp_reach_nlri_test.go
index f16ac3c4a181eb3bd0534f0e831bbb00eb09c8cf..cb6e5b544333d76f3244c8ef6895acb40783f149 100644
--- a/protocols/bgp/packet/mp_reach_nlri_test.go
+++ b/protocols/bgp/packet/mp_reach_nlri_test.go
@@ -13,6 +13,7 @@ func TestSerializeMultiProtocolReachNLRI(t *testing.T) {
 		name     string
 		nlri     MultiProtocolReachNLRI
 		expected []byte
+		addPath  bool
 	}{
 		{
 			name: "Simple IPv6 prefix",
@@ -32,12 +33,35 @@ func TestSerializeMultiProtocolReachNLRI(t *testing.T) {
 				0x30, 0x26, 0x00, 0x00, 0x06, 0xff, 0x05, // Prefix
 			},
 		},
+		{
+			name: "IPv6 prefix with ADD-PATH",
+			nlri: MultiProtocolReachNLRI{
+				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),
+				},
+				PathID: 100,
+			},
+			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
+				0x00, 0x00, 0x00, 100, // PathID
+				0x30, 0x26, 0x00, 0x00, 0x06, 0xff, 0x05, // Prefix
+			},
+			addPath: true,
+		},
 	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			buf := &bytes.Buffer{}
-			test.nlri.serialize(buf)
+			test.nlri.serialize(buf, &EncodeOptions{
+				UseAddPath: test.addPath,
+			})
 			assert.Equal(t, test.expected, buf.Bytes())
 		})
 	}
diff --git a/protocols/bgp/packet/mp_unreach_nlri.go b/protocols/bgp/packet/mp_unreach_nlri.go
index 63cccd580dfe79e474c1be33f96485eebec1d43b..fd72618c51b58fe3437b5f7445f827f0b3820bcd 100644
--- a/protocols/bgp/packet/mp_unreach_nlri.go
+++ b/protocols/bgp/packet/mp_unreach_nlri.go
@@ -13,13 +13,17 @@ type MultiProtocolUnreachNLRI struct {
 	AFI      uint16
 	SAFI     uint8
 	Prefixes []bnet.Prefix
+	PathID   uint32
 }
 
-func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer) uint16 {
+func (n *MultiProtocolUnreachNLRI) serialize(buf *bytes.Buffer, opt *EncodeOptions) uint16 {
 	tempBuf := bytes.NewBuffer(nil)
 	tempBuf.Write(convert.Uint16Byte(n.AFI))
 	tempBuf.WriteByte(n.SAFI)
 	for _, pfx := range n.Prefixes {
+		if opt.UseAddPath {
+			tempBuf.Write(convert.Uint32Byte(n.PathID))
+		}
 		tempBuf.Write(serializePrefix(pfx))
 	}
 
diff --git a/protocols/bgp/packet/mp_unreach_nlri_test.go b/protocols/bgp/packet/mp_unreach_nlri_test.go
index 02e1b00fff6d161b37509e1748d20bd04dcb3d35..467b19e030dd208f5e1103d81465107f531591ec 100644
--- a/protocols/bgp/packet/mp_unreach_nlri_test.go
+++ b/protocols/bgp/packet/mp_unreach_nlri_test.go
@@ -13,6 +13,7 @@ func TestSerializeMultiProtocolUnreachNLRI(t *testing.T) {
 		name     string
 		nlri     MultiProtocolUnreachNLRI
 		expected []byte
+		addPath  bool
 	}{
 		{
 			name: "Simple IPv6 prefix",
@@ -29,12 +30,32 @@ func TestSerializeMultiProtocolUnreachNLRI(t *testing.T) {
 				0x2c, 0x26, 0x20, 0x01, 0x10, 0x90, 0x00, // Prefix
 			},
 		},
+		{
+			name: "IPv6 prefix with ADD-PATH",
+			nlri: MultiProtocolUnreachNLRI{
+				AFI:  IPv6AFI,
+				SAFI: UnicastSAFI,
+				Prefixes: []bnet.Prefix{
+					bnet.NewPfx(bnet.IPv6FromBlocks(0x2620, 0x110, 0x9000, 0, 0, 0, 0, 0), 44),
+				},
+				PathID: 100,
+			},
+			expected: []byte{
+				0x00, 0x02, // AFI
+				0x01,                  // SAFI
+				0x00, 0x00, 0x00, 100, // PathID
+				0x2c, 0x26, 0x20, 0x01, 0x10, 0x90, 0x00, // Prefix
+			},
+			addPath: true,
+		},
 	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			buf := &bytes.Buffer{}
-			test.nlri.serialize(buf)
+			test.nlri.serialize(buf, &EncodeOptions{
+				UseAddPath: test.addPath,
+			})
 			assert.Equal(t, test.expected, buf.Bytes())
 		})
 	}
diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go
index 22ea8a8c3e1c0ba253e98c87296a7bdde0b92f02..3f2f48e089ba6614b0a06234eb1cc3e4875e5ff5 100644
--- a/protocols/bgp/packet/path_attributes.go
+++ b/protocols/bgp/packet/path_attributes.go
@@ -11,7 +11,7 @@ import (
 	"github.com/taktv6/tflow2/convert"
 )
 
-func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *types.Options) (*PathAttribute, error) {
+func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *DecodeOptions) (*PathAttribute, error) {
 	var ret *PathAttribute
 	var eol *PathAttribute
 	var pa *PathAttribute
@@ -38,7 +38,7 @@ func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *types.Options) (*PathA
 	return ret, nil
 }
 
-func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, consumed uint16, err error) {
+func decodePathAttr(buf *bytes.Buffer, opt *DecodeOptions) (pa *PathAttribute, consumed uint16, err error) {
 	pa = &PathAttribute{}
 
 	err = decodePathAttrFlags(buf, pa)
@@ -66,7 +66,7 @@ func decodePathAttr(buf *bytes.Buffer, opt *types.Options) (pa *PathAttribute, c
 		}
 	case ASPathAttr:
 		asnLength := uint8(2)
-		if opt.Supports4OctetASN {
+		if opt.Use32BitASN {
 			asnLength = 4
 		}
 
@@ -445,7 +445,7 @@ func dumpNBytes(buf *bytes.Buffer, n uint16) error {
 }
 
 // Serialize serializes a path attribute
-func (pa *PathAttribute) Serialize(buf *bytes.Buffer, opt *types.Options) uint16 {
+func (pa *PathAttribute) Serialize(buf *bytes.Buffer, opt *EncodeOptions) uint16 {
 	pathAttrLen := uint16(0)
 
 	switch pa.TypeCode {
@@ -468,9 +468,9 @@ func (pa *PathAttribute) Serialize(buf *bytes.Buffer, opt *types.Options) uint16
 	case LargeCommunitiesAttr:
 		pathAttrLen = uint16(pa.serializeLargeCommunities(buf))
 	case MultiProtocolReachNLRICode:
-		pathAttrLen = pa.serializeMultiProtocolReachNLRI(buf)
+		pathAttrLen = pa.serializeMultiProtocolReachNLRI(buf, opt)
 	case MultiProtocolUnreachNLRICode:
-		pathAttrLen = pa.serializeMultiProtocolUnreachNLRI(buf)
+		pathAttrLen = pa.serializeMultiProtocolUnreachNLRI(buf, opt)
 	case OriginatorIDAttr:
 		pathAttrLen = uint16(pa.serializeOriginatorID(buf))
 	case ClusterListAttr:
@@ -493,14 +493,14 @@ func (pa *PathAttribute) serializeOrigin(buf *bytes.Buffer) uint8 {
 	return 4
 }
 
-func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *types.Options) uint8 {
+func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *EncodeOptions) uint8 {
 	attrFlags := uint8(0)
 	attrFlags = setTransitive(attrFlags)
 	buf.WriteByte(attrFlags)
 	buf.WriteByte(ASPathAttr)
 
 	asnLength := uint8(2)
-	if opt.Supports4OctetASN {
+	if opt.Use32BitASN {
 		asnLength = 4
 	}
 
@@ -511,10 +511,10 @@ func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *types.Options)
 		segmentsBuf.WriteByte(uint8(len(segment.ASNs)))
 
 		for _, asn := range segment.ASNs {
-			if asnLength == 2 {
-				segmentsBuf.Write(convert.Uint16Byte(uint16(asn)))
-			} else {
+			if opt.Use32BitASN {
 				segmentsBuf.Write(convert.Uint32Byte(asn))
+			} else {
+				segmentsBuf.Write(convert.Uint16Byte(uint16(asn)))
 			}
 		}
 		length += 2 + uint8(len(segment.ASNs))*asnLength
@@ -695,22 +695,22 @@ func (pa *PathAttribute) serializeUnknownAttribute(buf *bytes.Buffer) uint16 {
 	return uint16(len(b) + 2)
 }
 
-func (pa *PathAttribute) serializeMultiProtocolReachNLRI(buf *bytes.Buffer) uint16 {
+func (pa *PathAttribute) serializeMultiProtocolReachNLRI(buf *bytes.Buffer, opt *EncodeOptions) uint16 {
 	v := pa.Value.(MultiProtocolReachNLRI)
 	pa.Optional = true
 
 	tempBuf := bytes.NewBuffer(nil)
-	v.serialize(tempBuf)
+	v.serialize(tempBuf, opt)
 
 	return pa.serializeGeneric(tempBuf.Bytes(), buf)
 }
 
-func (pa *PathAttribute) serializeMultiProtocolUnreachNLRI(buf *bytes.Buffer) uint16 {
+func (pa *PathAttribute) serializeMultiProtocolUnreachNLRI(buf *bytes.Buffer, opt *EncodeOptions) uint16 {
 	v := pa.Value.(MultiProtocolUnreachNLRI)
 	pa.Optional = true
 
 	tempBuf := bytes.NewBuffer(nil)
-	v.serialize(tempBuf)
+	v.serialize(tempBuf, opt)
 
 	return pa.serializeGeneric(tempBuf.Bytes(), buf)
 }
diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go
index 762d604d885a3135c4aff72c874a8f8dbfc5c51f..958da99b783d20121e89fe4b85c4d213891bf550 100644
--- a/protocols/bgp/packet/path_attributes_test.go
+++ b/protocols/bgp/packet/path_attributes_test.go
@@ -52,7 +52,7 @@ func TestDecodePathAttrs(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		res, err := decodePathAttrs(bytes.NewBuffer(test.input), uint16(len(test.input)), &types.Options{})
+		res, err := decodePathAttrs(bytes.NewBuffer(test.input), uint16(len(test.input)), &DecodeOptions{})
 
 		if test.wantFail && err == nil {
 			t.Errorf("Expected error did not happen for test %q", test.name)
@@ -249,7 +249,7 @@ func TestDecodePathAttr(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		res, _, err := decodePathAttr(bytes.NewBuffer(test.input), &types.Options{})
+		res, _, err := decodePathAttr(bytes.NewBuffer(test.input), &DecodeOptions{})
 
 		if test.wantFail && err == nil {
 			t.Errorf("Expected error did not happen for test %q", test.name)
@@ -1577,8 +1577,8 @@ func TestSerializeASPath(t *testing.T) {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			buf := bytes.NewBuffer(nil)
-			opt := &types.Options{
-				Supports4OctetASN: test.use32BitASN,
+			opt := &EncodeOptions{
+				Use32BitASN: test.use32BitASN,
 			}
 			n := test.input.serializeASPath(buf, opt)
 			if n != test.expectedLen {
@@ -2081,10 +2081,7 @@ func TestSerialize(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		opt := &types.Options{
-			AddPathRX: false,
-		}
-		res, err := test.msg.SerializeUpdate(opt)
+		res, err := test.msg.SerializeUpdate(&EncodeOptions{})
 		if err != nil {
 			if test.wantFail {
 				continue
@@ -2294,8 +2291,8 @@ func TestSerializeAddPath(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		opt := &types.Options{
-			AddPathRX: true,
+		opt := &EncodeOptions{
+			UseAddPath: true,
 		}
 		res, err := test.msg.SerializeUpdate(opt)
 		if err != nil {
diff --git a/protocols/bgp/packet/update.go b/protocols/bgp/packet/update.go
index 55b0cc0b9099076edbaef35fcfab1cfc83443330..167439ab06b77d669c5de258a252b5c2105794a6 100644
--- a/protocols/bgp/packet/update.go
+++ b/protocols/bgp/packet/update.go
@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"fmt"
 
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
 	"github.com/taktv6/tflow2/convert"
 )
 
@@ -17,14 +16,14 @@ type BGPUpdate struct {
 }
 
 // SerializeUpdate serializes an BGPUpdate to wire format
-func (b *BGPUpdate) SerializeUpdate(opt *types.Options) ([]byte, error) {
+func (b *BGPUpdate) SerializeUpdate(opt *EncodeOptions) ([]byte, error) {
 	budget := MaxLen - MinLen
 	nlriLen := 0
 	buf := bytes.NewBuffer(nil)
 
 	withdrawBuf := bytes.NewBuffer(nil)
 	for withdraw := b.WithdrawnRoutes; withdraw != nil; withdraw = withdraw.Next {
-		if opt.AddPathRX {
+		if opt.UseAddPath {
 			nlriLen = int(withdraw.serializeAddPath(withdrawBuf))
 		} else {
 			nlriLen = int(withdraw.serialize(withdrawBuf))
@@ -47,7 +46,7 @@ func (b *BGPUpdate) SerializeUpdate(opt *types.Options) ([]byte, error) {
 
 	nlriBuf := bytes.NewBuffer(nil)
 	for nlri := b.NLRI; nlri != nil; nlri = nlri.Next {
-		if opt.AddPathRX {
+		if opt.UseAddPath {
 			nlriLen = int(nlri.serializeAddPath(nlriBuf))
 		} else {
 			nlriLen = int(nlri.serialize(nlriBuf))
@@ -87,7 +86,7 @@ func (b *BGPUpdate) SerializeUpdate(opt *types.Options) ([]byte, error) {
 	return buf.Bytes(), nil
 }
 
-func (b *BGPUpdate) SerializeUpdateAddPath(opt *types.Options) ([]byte, error) {
+func (b *BGPUpdate) SerializeUpdateAddPath(opt *EncodeOptions) ([]byte, error) {
 	budget := MaxLen - MinLen
 	buf := bytes.NewBuffer(nil)
 
diff --git a/protocols/bgp/server/BUILD.bazel b/protocols/bgp/server/BUILD.bazel
index 78bdeb1f3635cabb3ead2855ae4431ee20a3c04c..bd45a72d0abd1f419175febf884094109f6eb982 100644
--- a/protocols/bgp/server/BUILD.bazel
+++ b/protocols/bgp/server/BUILD.bazel
@@ -21,7 +21,6 @@ go_library(
         "update_helper.go",
         "update_sender.go",
         "util.go",
-        "withdraw.go",
     ],
     importpath = "github.com/bio-routing/bio-rd/protocols/bgp/server",
     visibility = ["//visibility:public"],
@@ -49,7 +48,6 @@ go_test(
         "server_test.go",
         "update_helper_test.go",
         "update_sender_test.go",
-        "withdraw_test.go",
     ],
     embed = [":go_default_library"],
     deps = [
diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go
index 60eb417202135a54f1488f0fd10af620c997b2ec..c0697f1afc7686031b151055ba45a7c831761a3b 100644
--- a/protocols/bgp/server/fsm.go
+++ b/protocols/bgp/server/fsm.go
@@ -9,7 +9,6 @@ import (
 	"time"
 
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -55,14 +54,14 @@ type FSM struct {
 	msgRecvFailCh chan error
 	stopMsgRecvCh chan struct{}
 
-	options *types.Options
-
 	local net.IP
 
 	ribsInitialized bool
 	ipv4Unicast     *fsmAddressFamily
 	ipv6Unicast     *fsmAddressFamily
 
+	supports4OctetASN bool
+
 	neighborID uint32
 	state      state
 	stateMu    sync.RWMutex
@@ -99,7 +98,6 @@ func newFSM(peer *peer) *FSM {
 		msgRecvCh:        make(chan []byte),
 		msgRecvFailCh:    make(chan error),
 		stopMsgRecvCh:    make(chan struct{}),
-		options:          &types.Options{},
 	}
 
 	if peer.ipv4 != nil {
@@ -113,6 +111,21 @@ func newFSM(peer *peer) *FSM {
 	return f
 }
 
+func (fsm *FSM) addressFamily(afi uint16, safi uint8) *fsmAddressFamily {
+	if safi != packet.UnicastSAFI {
+		return nil
+	}
+
+	switch afi {
+	case packet.IPv4AFI:
+		return fsm.ipv4Unicast
+	case packet.IPv6AFI:
+		return fsm.ipv6Unicast
+	default:
+		return nil
+	}
+}
+
 func (fsm *FSM) start() {
 	ctx, cancel := context.WithCancel(context.Background())
 	fsm.connectionCancelFunc = cancel
@@ -228,6 +241,12 @@ func (fsm *FSM) msgReceiver() error {
 	}
 }
 
+func (fsm *FSM) decodeOptions() *packet.DecodeOptions {
+	return &packet.DecodeOptions{
+		Use32BitASN: fsm.supports4OctetASN,
+	}
+}
+
 func (fsm *FSM) startConnectRetryTimer() {
 	fsm.connectRetryTimer = time.NewTimer(fsm.connectRetryTime)
 }
diff --git a/protocols/bgp/server/fsm_address_family.go b/protocols/bgp/server/fsm_address_family.go
index f81f4d82905f3aeb9f60157cb2cad611859b18ec..092572c50463ff4109efa669f798e484d218e5ee 100644
--- a/protocols/bgp/server/fsm_address_family.go
+++ b/protocols/bgp/server/fsm_address_family.go
@@ -29,17 +29,25 @@ type fsmAddressFamily struct {
 
 	updateSender *UpdateSender
 
+	addPathSend         routingtable.ClientOptions
+	addPathTXConfigured bool
+	addPathTX           bool
+
+	multiProtocol bool
+
 	initialized bool
 }
 
-func newFSMAddressFamily(afi uint16, safi uint8, params *familyParameters, fsm *FSM) *fsmAddressFamily {
+func newFSMAddressFamily(afi uint16, safi uint8, family *peerAddressFamily, fsm *FSM) *fsmAddressFamily {
 	return &fsmAddressFamily{
-		afi:          afi,
-		safi:         safi,
-		fsm:          fsm,
-		rib:          params.rib,
-		importFilter: params.importFilter,
-		exportFilter: params.exportFilter,
+		afi:                 afi,
+		safi:                safi,
+		fsm:                 fsm,
+		rib:                 family.rib,
+		importFilter:        family.importFilter,
+		exportFilter:        family.exportFilter,
+		addPathTXConfigured: family.addPathReceive, // at this point we switch from peers view to our view
+		addPathSend:         family.addPathSend,
 	}
 }
 
@@ -50,18 +58,19 @@ func (f *fsmAddressFamily) init(n *routingtable.Neighbor) {
 	contributingASNs.Add(f.fsm.peer.localASN)
 	f.adjRIBIn.Register(f.rib)
 
-	f.adjRIBOut = adjRIBOut.New(n, f.exportFilter)
-	clientOptions := routingtable.ClientOptions{
-		BestOnly: true,
-	}
-	if f.fsm.options.AddPathRX {
-		clientOptions = f.fsm.peer.addPathSend
-	}
+	f.adjRIBOut = adjRIBOut.New(n, f.exportFilter, f.addPathTX)
 
 	f.updateSender = newUpdateSender(f.fsm, f.afi, f.safi)
 	f.updateSender.Start(time.Millisecond * 5)
 
 	f.adjRIBOut.Register(f.updateSender)
+
+	clientOptions := routingtable.ClientOptions{
+		BestOnly: true,
+	}
+	if f.addPathTX {
+		clientOptions = f.addPathSend
+	}
 	f.rib.RegisterWithOptions(f.adjRIBOut, clientOptions)
 }
 
@@ -87,19 +96,7 @@ func (f *fsmAddressFamily) processUpdate(u *packet.BGPUpdate) {
 		return
 	}
 
-	mp := false
-	switch f.afi {
-	case packet.IPv4AFI:
-		if f.fsm.options.MultiProtocolIPv4 {
-			mp = true
-		}
-	case packet.IPv6AFI:
-		if f.fsm.options.MultiProtocolIPv6 {
-			mp = true
-		}
-	}
-
-	if mp {
+	if f.multiProtocol {
 		f.multiProtocolUpdates(u)
 		return
 	}
diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go
index 246c7cbf6a9df6018c33fe135b2eae2fcf2e2ae2..c2e1f3a9576f6287029dff70dae09c6f73530198 100644
--- a/protocols/bgp/server/fsm_established.go
+++ b/protocols/bgp/server/fsm_established.go
@@ -30,6 +30,8 @@ func (s establishedState) run() (state, string) {
 		}
 	}
 
+	opt := s.fsm.decodeOptions()
+
 	for {
 		select {
 		case e := <-s.fsm.eventCh:
@@ -48,7 +50,7 @@ func (s establishedState) run() (state, string) {
 		case <-s.fsm.keepaliveTimer.C:
 			return s.keepaliveTimerExpired()
 		case recvMsg := <-s.fsm.msgRecvCh:
-			return s.msgReceived(recvMsg)
+			return s.msgReceived(recvMsg, opt)
 		}
 	}
 }
@@ -74,7 +76,6 @@ func (s *establishedState) init() error {
 		LocalASN:             s.fsm.peer.localASN,
 		RouteServerClient:    s.fsm.peer.routeServerClient,
 		LocalAddress:         localAddr,
-		CapAddPathRX:         s.fsm.options.AddPathRX,
 		RouteReflectorClient: s.fsm.peer.routeReflectorClient,
 		ClusterID:            s.fsm.peer.clusterID,
 	}
@@ -148,8 +149,8 @@ func (s *establishedState) keepaliveTimerExpired() (state, string) {
 	return newEstablishedState(s.fsm), s.fsm.reason
 }
 
-func (s *establishedState) msgReceived(data []byte) (state, string) {
-	msg, err := packet.Decode(bytes.NewBuffer(data), s.fsm.options)
+func (s *establishedState) msgReceived(data []byte, opt *packet.DecodeOptions) (state, string) {
+	msg, err := packet.Decode(bytes.NewBuffer(data), opt)
 	if err != nil {
 		switch bgperr := err.(type) {
 		case packet.BGPError:
diff --git a/protocols/bgp/server/fsm_open_confirm.go b/protocols/bgp/server/fsm_open_confirm.go
index 07b8a6152dd64c56055c7cbd0745980e462c3886..17df63c1a3481b09f6b1671ce412d761e27cf240 100644
--- a/protocols/bgp/server/fsm_open_confirm.go
+++ b/protocols/bgp/server/fsm_open_confirm.go
@@ -18,6 +18,8 @@ func newOpenConfirmState(fsm *FSM) *openConfirmState {
 }
 
 func (s openConfirmState) run() (state, string) {
+	opt := s.fsm.decodeOptions()
+
 	for {
 		select {
 		case e := <-s.fsm.eventCh:
@@ -34,7 +36,7 @@ func (s openConfirmState) run() (state, string) {
 		case <-s.fsm.keepaliveTimer.C:
 			return s.keepaliveTimerExpired()
 		case recvMsg := <-s.fsm.msgRecvCh:
-			return s.msgReceived(recvMsg)
+			return s.msgReceived(recvMsg, opt)
 		}
 	}
 }
@@ -81,8 +83,8 @@ func (s *openConfirmState) keepaliveTimerExpired() (state, string) {
 	return newOpenConfirmState(s.fsm), s.fsm.reason
 }
 
-func (s *openConfirmState) msgReceived(data []byte) (state, string) {
-	msg, err := packet.Decode(bytes.NewBuffer(data), s.fsm.options)
+func (s *openConfirmState) msgReceived(data []byte, opt *packet.DecodeOptions) (state, string) {
+	msg, err := packet.Decode(bytes.NewBuffer(data), opt)
 	if err != nil {
 		switch bgperr := err.(type) {
 		case packet.BGPError:
diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go
index be9be952ff27b615f0d24f1ec8920198a1b813be..664e0d19399efd933fef5ee42330c7b5f2016a48 100644
--- a/protocols/bgp/server/fsm_open_sent.go
+++ b/protocols/bgp/server/fsm_open_sent.go
@@ -22,6 +22,9 @@ func newOpenSentState(fsm *FSM) *openSentState {
 
 func (s openSentState) run() (state, string) {
 	go s.fsm.msgReceiver()
+
+	opt := s.fsm.decodeOptions()
+
 	for {
 		select {
 		case e := <-s.fsm.eventCh:
@@ -38,7 +41,7 @@ func (s openSentState) run() (state, string) {
 		case <-s.fsm.holdTimer.C:
 			return s.holdTimerExpired()
 		case recvMsg := <-s.fsm.msgRecvCh:
-			return s.msgReceived(recvMsg)
+			return s.msgReceived(recvMsg, opt)
 		}
 	}
 }
@@ -73,8 +76,8 @@ func (s *openSentState) holdTimerExpired() (state, string) {
 	return newIdleState(s.fsm), "Holdtimer expired"
 }
 
-func (s *openSentState) msgReceived(data []byte) (state, string) {
-	msg, err := packet.Decode(bytes.NewBuffer(data), s.fsm.options)
+func (s *openSentState) msgReceived(data []byte, opt *packet.DecodeOptions) (state, string) {
+	msg, err := packet.Decode(bytes.NewBuffer(data), opt)
 	if err != nil {
 		switch bgperr := err.(type) {
 		case packet.BGPError:
@@ -175,47 +178,47 @@ func (s *openSentState) processCapability(cap packet.Capability) {
 }
 
 func (s *openSentState) processMultiProtocolCapability(cap packet.MultiProtocolCapability) {
-	if cap.SAFI != 0 {
+	if cap.SAFI != packet.UnicastSAFI {
 		return
 	}
 
-	switch cap.AFI {
-	case packet.IPv4AFI:
-		s.fsm.options.MultiProtocolIPv4 = true
-	case packet.IPv6AFI:
-		s.fsm.options.MultiProtocolIPv6 = true
+	f := s.fsm.addressFamily(cap.AFI, cap.SAFI)
+	if f != nil {
+		f.multiProtocol = true
 	}
 }
 
 func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapability) {
-	if addPathCap.AFI != 1 {
+	if addPathCap.SAFI != packet.UnicastSAFI {
 		return
 	}
-	if addPathCap.SAFI != 1 {
+
+	f := s.fsm.addressFamily(addPathCap.AFI, addPathCap.SAFI)
+	if f == nil {
 		return
 	}
 
 	switch addPathCap.SendReceive {
 	case packet.AddPathReceive:
-		if !s.fsm.peer.addPathSend.BestOnly {
-			s.fsm.options.AddPathRX = true
+		if !f.addPathSend.BestOnly {
+			f.addPathTX = true
 		}
 	case packet.AddPathSend:
-		if s.fsm.peer.addPathRecv {
-			s.fsm.options.AddPathRX = true
+		if f.addPathTXConfigured {
+			f.addPathTX = true
 		}
 	case packet.AddPathSendReceive:
-		if !s.fsm.peer.addPathSend.BestOnly {
-			s.fsm.options.AddPathRX = true
+		if !f.addPathSend.BestOnly {
+			f.addPathTX = true
 		}
-		if s.fsm.peer.addPathRecv {
-			s.fsm.options.AddPathRX = true
+		if f.addPathTXConfigured {
+			f.addPathTX = true
 		}
 	}
 }
 
 func (s *openSentState) processASN4Capability(cap packet.ASN4Capability) {
-	s.fsm.options.Supports4OctetASN = true
+	s.fsm.supports4OctetASN = true
 
 	if s.peerASNRcvd == packet.ASTransASN {
 		s.peerASNRcvd = cap.ASN4
diff --git a/protocols/bgp/server/fsm_test.go b/protocols/bgp/server/fsm_test.go
index 5cddf8aee4663202b1ad716e59e922486dd45019..ba0e1429e41d4ccd4d65837142a8a7d456d6df6a 100644
--- a/protocols/bgp/server/fsm_test.go
+++ b/protocols/bgp/server/fsm_test.go
@@ -18,7 +18,7 @@ func TestFSM255UpdatesIPv4(t *testing.T) {
 	fsmA := newFSM(&peer{
 		addr:     bnet.IPv4FromOctets(169, 254, 100, 100),
 		routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(),
-		ipv4: &familyParameters{
+		ipv4: &peerAddressFamily{
 			rib:          locRIB.New(),
 			importFilter: filter.NewAcceptAllFilter(),
 			exportFilter: filter.NewAcceptAllFilter(),
@@ -132,14 +132,14 @@ func TestFSM255UpdatesIPv6(t *testing.T) {
 	fsmA := newFSM(&peer{
 		addr:     bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0xffff, 0, 0, 0, 1),
 		routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(),
-		ipv6: &familyParameters{
+		ipv6: &peerAddressFamily{
 			rib:          locRIB.New(),
 			importFilter: filter.NewAcceptAllFilter(),
 			exportFilter: filter.NewAcceptAllFilter(),
 		},
 	})
 
-	fsmA.options.MultiProtocolIPv6 = true
+	fsmA.ipv6Unicast.multiProtocol = true
 	fsmA.holdTimer = time.NewTimer(time.Second * 90)
 	fsmA.keepaliveTimer = time.NewTimer(time.Second * 30)
 	fsmA.connectRetryTimer = time.NewTimer(time.Second * 120)
diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go
index b70534b0898a2c3b3a68d4509e47e4ae17039a97..dd877d77f4eee02eb6a21578b9dbaf54a58336a9 100644
--- a/protocols/bgp/server/peer.go
+++ b/protocols/bgp/server/peer.go
@@ -30,8 +30,6 @@ type peer struct {
 	fsmsMu sync.Mutex
 
 	routerID             uint32
-	addPathSend          routingtable.ClientOptions
-	addPathRecv          bool
 	reconnectInterval    time.Duration
 	keepaliveTime        time.Duration
 	holdTime             time.Duration
@@ -40,14 +38,18 @@ type peer struct {
 	routeReflectorClient bool
 	clusterID            uint32
 
-	ipv4 *familyParameters
-	ipv6 *familyParameters
+	ipv4 *peerAddressFamily
+	ipv6 *peerAddressFamily
 }
 
-type familyParameters struct {
-	rib          *locRIB.LocRIB
+type peerAddressFamily struct {
+	rib *locRIB.LocRIB
+
 	importFilter *filter.Filter
 	exportFilter *filter.Filter
+
+	addPathSend    routingtable.ClientOptions
+	addPathReceive bool
 }
 
 func (p *peer) snapshot() PeerInfo {
@@ -130,8 +132,6 @@ func newPeer(c config.Peer, server *bgpServer) (*peer, error) {
 		peerASN:              c.PeerAS,
 		localASN:             c.LocalAS,
 		fsms:                 make([]*FSM, 0),
-		addPathSend:          c.AddPathSend,
-		addPathRecv:          c.AddPathRecv,
 		reconnectInterval:    c.ReconnectInterval,
 		keepaliveTime:        c.KeepAlive,
 		holdTime:             c.HoldTime,
@@ -142,10 +142,12 @@ func newPeer(c config.Peer, server *bgpServer) (*peer, error) {
 	}
 
 	if c.IPv4 != nil {
-		p.ipv4 = &familyParameters{
-			rib:          c.IPv4.RIB,
-			importFilter: filterOrDefault(c.IPv4.ImportFilter),
-			exportFilter: filterOrDefault(c.IPv4.ExportFilter),
+		p.ipv4 = &peerAddressFamily{
+			rib:            c.IPv4.RIB,
+			importFilter:   filterOrDefault(c.IPv4.ImportFilter),
+			exportFilter:   filterOrDefault(c.IPv4.ExportFilter),
+			addPathReceive: c.IPv4.AddPathRecv,
+			addPathSend:    c.IPv4.AddPathSend,
 		}
 	}
 
@@ -156,18 +158,17 @@ func newPeer(c config.Peer, server *bgpServer) (*peer, error) {
 
 	caps := make(packet.Capabilities, 0)
 
-	addPathEnabled, addPathCap := handleAddPathCapability(c)
-	if addPathEnabled {
-		caps = append(caps, addPathCap)
-	}
+	caps = append(caps, addPathCapabilities(c)...)
 
 	caps = append(caps, asn4Capability(c))
 
 	if c.IPv6 != nil {
-		p.ipv6 = &familyParameters{
-			rib:          c.IPv6.RIB,
-			importFilter: filterOrDefault(c.IPv6.ImportFilter),
-			exportFilter: filterOrDefault(c.IPv6.ExportFilter),
+		p.ipv6 = &peerAddressFamily{
+			rib:            c.IPv6.RIB,
+			importFilter:   filterOrDefault(c.IPv6.ImportFilter),
+			exportFilter:   filterOrDefault(c.IPv6.ExportFilter),
+			addPathReceive: c.IPv6.AddPathRecv,
+			addPathSend:    c.IPv6.AddPathSend,
 		}
 		caps = append(caps, multiProtocolCapability(packet.IPv6AFI))
 	}
@@ -201,12 +202,32 @@ func multiProtocolCapability(afi uint16) packet.Capability {
 	}
 }
 
-func handleAddPathCapability(c config.Peer) (bool, packet.Capability) {
+func addPathCapabilities(c config.Peer) []packet.Capability {
+	caps := make([]packet.Capability, 0)
+
+	enabled, cap := addPathCapabilityForFamily(c.IPv4, packet.IPv4AFI, packet.UnicastSAFI)
+	if enabled {
+		caps = append(caps, cap)
+	}
+
+	enabled, cap = addPathCapabilityForFamily(c.IPv6, packet.IPv6AFI, packet.UnicastSAFI)
+	if enabled {
+		caps = append(caps, cap)
+	}
+
+	return caps
+}
+
+func addPathCapabilityForFamily(f *config.AddressFamilyConfig, afi uint16, safi uint8) (enabled bool, cap packet.Capability) {
+	if f == nil {
+		return false, packet.Capability{}
+	}
+
 	addPath := uint8(0)
-	if c.AddPathRecv {
+	if f.AddPathRecv {
 		addPath += packet.AddPathReceive
 	}
-	if !c.AddPathSend.BestOnly {
+	if !f.AddPathSend.BestOnly {
 		addPath += packet.AddPathSend
 	}
 
@@ -217,8 +238,8 @@ func handleAddPathCapability(c config.Peer) (bool, packet.Capability) {
 	return true, packet.Capability{
 		Code: packet.AddPathCapabilityCode,
 		Value: packet.AddPathCapability{
-			AFI:         packet.IPv4AFI,
-			SAFI:        packet.UnicastSAFI,
+			AFI:         afi,
+			SAFI:        safi,
 			SendReceive: addPath,
 		},
 	}
diff --git a/protocols/bgp/server/server_test.go b/protocols/bgp/server/server_test.go
index 2b4e8385204a411fed304e847c3c18c4c107cc7c..9d9248c4d8e1f574e3cc3f519d412de3bbf5140a 100644
--- a/protocols/bgp/server/server_test.go
+++ b/protocols/bgp/server/server_test.go
@@ -39,13 +39,13 @@ func TestBgpServerPeerSnapshot(t *testing.T) {
 		KeepAlive:         time.Second * 30,
 		Passive:           true,
 		RouterID:          s.RouterID(),
-		AddPathSend: routingtable.ClientOptions{
-			MaxPaths: 10,
-		},
 		IPv4: &config.AddressFamilyConfig{
 			RIB:          rib,
 			ImportFilter: filter.NewDrainFilter(),
 			ExportFilter: filter.NewAcceptAllFilter(),
+			AddPathSend: routingtable.ClientOptions{
+				MaxPaths: 10,
+			},
 		},
 	}
 	s.AddPeer(pc)
diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go
index 2cb82934a0a12d2f25243db2bc82cd60fca10e02..a510e54e98c10e8db673b1156c26e3de1cf19f24 100644
--- a/protocols/bgp/server/update_helper.go
+++ b/protocols/bgp/server/update_helper.go
@@ -4,11 +4,12 @@ import (
 	"fmt"
 	"io"
 
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+
 	log "github.com/sirupsen/logrus"
 )
 
-func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *types.Options) error {
+func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *packet.EncodeOptions) error {
 	updateBytes, err := update.SerializeUpdate(opt)
 	if err != nil {
 		log.Errorf("Unable to serialize BGP Update: %v", err)
@@ -23,5 +24,5 @@ func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *type
 }
 
 type serializeAbleUpdate interface {
-	SerializeUpdate(opt *types.Options) ([]byte, error)
+	SerializeUpdate(opt *packet.EncodeOptions) ([]byte, error)
 }
diff --git a/protocols/bgp/server/update_helper_test.go b/protocols/bgp/server/update_helper_test.go
index 6c599f18c965d55a9a611509ea10e2c5d5ffa35e..86a70f64d5626c4cbb38fb4eca5b793d7a548093 100644
--- a/protocols/bgp/server/update_helper_test.go
+++ b/protocols/bgp/server/update_helper_test.go
@@ -7,7 +7,6 @@ import (
 	"testing"
 
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
 
 	"github.com/bio-routing/bio-rd/net"
 	"github.com/stretchr/testify/assert"
@@ -15,7 +14,7 @@ import (
 
 type failingUpdate struct{}
 
-func (f *failingUpdate) SerializeUpdate(opt *types.Options) ([]byte, error) {
+func (f *failingUpdate) SerializeUpdate(opt *packet.EncodeOptions) ([]byte, error) {
 	return nil, errors.New("general error")
 }
 
@@ -93,7 +92,7 @@ func TestSerializeAndSendUpdate(t *testing.T) {
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			opt := &types.Options{}
+			opt := &packet.EncodeOptions{}
 			err := serializeAndSendUpdate(test.buf, test.testUpdate, opt)
 			assert.Equal(t, test.err, err)
 
diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go
index ffcfae44d649c424731e0f7983961d7af5215a02..9e40303fc4e52aa36b3678026bf855be8992b931 100644
--- a/protocols/bgp/server/update_sender.go
+++ b/protocols/bgp/server/update_sender.go
@@ -1,7 +1,9 @@
 package server
 
 import (
+	"errors"
 	"fmt"
+	"io"
 	"sync"
 	"time"
 
@@ -16,14 +18,14 @@ import (
 // UpdateSender converts table changes into BGP update messages
 type UpdateSender struct {
 	routingtable.ClientManager
-	fsm       *FSM
-	afi       uint16
-	safi      uint8
-	iBGP      bool
-	rrClient  bool
-	toSendMu  sync.Mutex
-	toSend    map[string]*pathPfxs
-	destroyCh chan struct{}
+	fsm           *FSM
+	addressFamily *fsmAddressFamily
+	options       *packet.EncodeOptions
+	iBGP          bool
+	rrClient      bool
+	toSendMu      sync.Mutex
+	toSend        map[string]*pathPfxs
+	destroyCh     chan struct{}
 }
 
 type pathPfxs struct {
@@ -32,14 +34,19 @@ type pathPfxs struct {
 }
 
 func newUpdateSender(fsm *FSM, afi uint16, safi uint8) *UpdateSender {
+	f := fsm.addressFamily(afi, safi)
+
 	return &UpdateSender{
-		fsm:       fsm,
-		afi:       afi,
-		safi:      safi,
-		iBGP:      fsm.peer.localASN == fsm.peer.peerASN,
-		rrClient:  fsm.peer.routeReflectorClient,
-		destroyCh: make(chan struct{}),
-		toSend:    make(map[string]*pathPfxs),
+		fsm:           fsm,
+		addressFamily: f,
+		iBGP:          fsm.peer.localASN == fsm.peer.peerASN,
+		rrClient:      fsm.peer.routeReflectorClient,
+		destroyCh:     make(chan struct{}),
+		toSend:        make(map[string]*pathPfxs),
+		options: &packet.EncodeOptions{
+			Use32BitASN: fsm.supports4OctetASN,
+			UseAddPath:  f.addPathTX,
+		},
 	}
 }
 
@@ -107,6 +114,10 @@ func (u *UpdateSender) sender(aggrTime time.Duration) {
 			for _, pfx := range pathNLRIs.pfxs {
 				budget -= int(packet.BytesInAddr(pfx.Pfxlen())) + 1
 
+				if u.options.UseAddPath {
+					budget -= 4
+				}
+
 				if budget < 0 {
 					updatesPrefixes = append(updatesPrefixes, prefixes)
 					prefixes = make([]bnet.Prefix, 0, 1)
@@ -130,12 +141,12 @@ func (u *UpdateSender) sender(aggrTime time.Duration) {
 }
 
 func (u *UpdateSender) updateOverhead() int {
-	if u.afi == packet.IPv4AFI && !u.fsm.options.MultiProtocolIPv4 {
+	if u.addressFamily.afi == packet.IPv4AFI && !u.addressFamily.multiProtocol {
 		return 0
 	}
 
 	addrLen := packet.IPv4AFI
-	if u.afi == packet.IPv6AFI {
+	if u.addressFamily.afi == packet.IPv6AFI {
 		addrLen = packet.IPv6Len
 	}
 
@@ -152,7 +163,7 @@ func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefix
 			return
 		}
 
-		err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options)
+		err = serializeAndSendUpdate(u.fsm.con, update, u.options)
 		if err != nil {
 			log.Errorf("Failed to serialize and send: %v", err)
 		}
@@ -160,18 +171,14 @@ func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefix
 }
 
 func (u *UpdateSender) updateMessageForPrefixes(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
-	switch u.afi {
-	case packet.IPv4AFI:
-		if u.fsm.options.MultiProtocolIPv4 {
-			return u.bgpUpdateMultiProtocol(pfxs, pa, pathID)
-		}
+	if u.addressFamily.afi == packet.IPv4AFI && !u.addressFamily.multiProtocol {
 		return u.bgpUpdate(pfxs, pa, pathID)
-	case packet.IPv6AFI:
-		if u.fsm.options.MultiProtocolIPv6 {
-			return u.bgpUpdateMultiProtocol(pfxs, pa, pathID)
-		}
-		return nil
 	}
+
+	if u.addressFamily.multiProtocol {
+		return u.bgpUpdateMultiProtocol(pfxs, pa, pathID)
+	}
+
 	return nil
 }
 
@@ -200,10 +207,11 @@ func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.Pat
 	attrs := &packet.PathAttribute{
 		TypeCode: packet.MultiProtocolReachNLRICode,
 		Value: packet.MultiProtocolReachNLRI{
-			AFI:      u.afi,
-			SAFI:     u.safi,
+			AFI:      u.addressFamily.afi,
+			SAFI:     u.addressFamily.safi,
 			NextHop:  nextHop,
 			Prefixes: pfxs,
+			PathID:   pathID,
 		},
 	}
 	attrs.Next = pa
@@ -235,40 +243,66 @@ func (u *UpdateSender) copyAttributesWithoutNextHop(pa *packet.PathAttribute) (a
 
 // RemovePath withdraws prefix `pfx` from a peer
 func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
-	err := u.withdrawPrefix(pfx, p)
+	err := u.withdrawPrefix(u.fsm.con, pfx, p)
 	if err != nil {
 		log.Errorf("Unable to withdraw prefix: %v", err)
 		return false
 	}
+
 	return true
 }
 
-func (u *UpdateSender) withdrawPrefix(pfx bnet.Prefix, p *route.Path) error {
-	if u.afi == packet.IPv4AFI {
-		return u.withdrawPrefixIPv4(pfx, p)
+func (u *UpdateSender) withdrawPrefix(out io.Writer, pfx bnet.Prefix, p *route.Path) error {
+	if p.Type != route.BGPPathType {
+		return errors.New("wrong path type, expected BGPPathType")
 	}
 
-	if u.afi == packet.IPv6AFI {
-		return u.withdrawPrefixIPv6(pfx, p)
+	if p.BGPPath == nil {
+		return errors.New("got nil BGPPath")
 	}
 
-	return fmt.Errorf("Unsupported AFI: %v", u.afi)
+	if u.addressFamily.afi == packet.IPv4AFI && !u.addressFamily.multiProtocol {
+		return u.withdrawPrefixIPv4(out, pfx, p)
+	}
+
+	if !u.addressFamily.multiProtocol {
+		return fmt.Errorf(packet.AFIName(u.addressFamily.afi) + " was not negotiated")
+	}
+
+	return u.withdrawPrefixMultiProtocol(out, pfx, p)
 }
 
-func (u *UpdateSender) withdrawPrefixIPv4(pfx bnet.Prefix, p *route.Path) error {
-	if u.fsm.options.MultiProtocolIPv4 {
-		return withdrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx, u.afi, u.safi)
+func (u *UpdateSender) withdrawPrefixIPv4(out io.Writer, pfx bnet.Prefix, p *route.Path) error {
+	update := &packet.BGPUpdate{
+		WithdrawnRoutes: &packet.NLRI{
+			PathIdentifier: p.BGPPath.PathIdentifier,
+			IP:             pfx.Addr().ToUint32(),
+			Pfxlen:         pfx.Pfxlen(),
+		},
 	}
 
-	return withdrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p)
+	return serializeAndSendUpdate(out, update, u.options)
 }
 
-func (u *UpdateSender) withdrawPrefixIPv6(pfx bnet.Prefix, p *route.Path) error {
-	if !u.fsm.options.MultiProtocolIPv6 {
-		return fmt.Errorf("IPv6 was not negotiated")
+func (u *UpdateSender) withdrawPrefixMultiProtocol(out io.Writer, pfx bnet.Prefix, p *route.Path) error {
+	pathID := uint32(0)
+	if p.BGPPath != nil {
+		pathID = p.BGPPath.PathIdentifier
+	}
+
+	update := &packet.BGPUpdate{
+		PathAttributes: &packet.PathAttribute{
+			TypeCode: packet.MultiProtocolUnreachNLRICode,
+			Value: packet.MultiProtocolUnreachNLRI{
+				AFI:      u.addressFamily.afi,
+				SAFI:     u.addressFamily.safi,
+				Prefixes: []bnet.Prefix{pfx},
+				PathID:   pathID,
+			},
+		},
 	}
 
-	return withdrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p)
+	return serializeAndSendUpdate(out, update, u.options)
 }
 
 // UpdateNewClient does nothing
diff --git a/protocols/bgp/server/update_sender_test.go b/protocols/bgp/server/update_sender_test.go
index 6085dd8f4441f05ee7ebe7b803700a28ac9261fc..4b6ae30722018a287f86aaa046d30252008bd944 100644
--- a/protocols/bgp/server/update_sender_test.go
+++ b/protocols/bgp/server/update_sender_test.go
@@ -1,6 +1,8 @@
 package server
 
 import (
+	"bytes"
+	"errors"
 	"reflect"
 	"testing"
 	"time"
@@ -10,7 +12,6 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	bnet "github.com/bio-routing/bio-rd/net"
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
 	"github.com/bio-routing/bio-rd/routingtable/locRIB"
@@ -873,18 +874,20 @@ func TestSender(t *testing.T) {
 
 		rib := locRIB.New()
 		if test.afi == packet.IPv6AFI {
-			fsmA.options.MultiProtocolIPv6 = true
-			fsmA.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, &familyParameters{
+			fsmA.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, &peerAddressFamily{
 				rib:          rib,
 				importFilter: filter.NewAcceptAllFilter(),
 				exportFilter: filter.NewAcceptAllFilter(),
 			}, fsmA)
+			fsmA.ipv6Unicast.multiProtocol = true
+			fsmA.ipv6Unicast.addPathTX = test.addPath
 		} else {
-			fsmA.ipv4Unicast = newFSMAddressFamily(packet.IPv4AFI, packet.UnicastSAFI, &familyParameters{
+			fsmA.ipv4Unicast = newFSMAddressFamily(packet.IPv4AFI, packet.UnicastSAFI, &peerAddressFamily{
 				rib:          rib,
 				importFilter: filter.NewAcceptAllFilter(),
 				exportFilter: filter.NewAcceptAllFilter(),
 			}, fsmA)
+			fsmA.ipv4Unicast.addPathTX = test.addPath
 		}
 
 		fsmA.holdTimer = time.NewTimer(time.Second * 90)
@@ -893,12 +896,6 @@ func TestSender(t *testing.T) {
 		fsmA.state = newEstablishedState(fsmA)
 		fsmA.con = btest.NewMockConn()
 
-		if test.addPath {
-			fsmA.options = &types.Options{
-				AddPathRX: true,
-			}
-		}
-
 		updateSender := newUpdateSender(fsmA, test.afi, packet.UnicastSAFI)
 
 		for _, pathPfx := range test.paths {
@@ -945,3 +942,179 @@ func TestSender(t *testing.T) {
 		}
 	}
 }
+
+func TestWithdrawPrefix(t *testing.T) {
+	testcases := []struct {
+		name          string
+		addPathTX     bool
+		afi           uint16
+		multiProtocol bool
+		prefix        bnet.Prefix
+		path          *route.Path
+		expected      []byte
+		expectedError error
+	}{
+		{
+			name:          "Non bgp withdraw with ADD-PATH",
+			afi:           packet.IPv4AFI,
+			multiProtocol: false,
+			addPathTX:     true,
+			prefix:        bnet.NewPfx(bnet.IPv4(1413010532), 24),
+			path: &route.Path{
+				Type: route.StaticPathType,
+			},
+			expected:      []byte{},
+			expectedError: errors.New("wrong path type, expected BGPPathType"),
+		},
+		{
+			name:          "Nil BGPPathType with ADD-PATH",
+			afi:           packet.IPv4AFI,
+			multiProtocol: false,
+			addPathTX:     true,
+			prefix:        bnet.NewPfx(bnet.IPv4(1413010532), 24),
+			path: &route.Path{
+				Type: route.BGPPathType,
+			},
+			expected:      []byte{},
+			expectedError: errors.New("got nil BGPPath"),
+		},
+		{
+			name:          "Normal withdraw with ADD-PATH",
+			afi:           packet.IPv4AFI,
+			multiProtocol: false,
+			addPathTX:     true,
+			prefix:        bnet.NewPfx(bnet.IPv4(1413010532), 24),
+			path: &route.Path{
+				Type: route.BGPPathType,
+				BGPPath: &route.BGPPath{
+					PathIdentifier: 1,
+				},
+			},
+			expected: []byte{
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
+				0x00, 0x1f, // BGP Message Length
+				0x02,       // BGP Message Type == Update
+				0x00, 0x08, // WithDraw Octet length
+				0x00, 0x00, 0x00, 0x01, // NLRI Path Identifier
+				0x18,             // Prefix Length
+				0x54, 0x38, 0xd4, // Prefix,
+				0x00, 0x00, // Total Path Attribute Length
+			},
+			expectedError: nil,
+		},
+		{
+			name:          "Normal withdraw without ADD-PATH",
+			afi:           packet.IPv4AFI,
+			multiProtocol: false,
+			addPathTX:     false,
+			prefix:        bnet.NewPfx(bnet.IPv4(1413010532), 24),
+			path: &route.Path{
+				Type: route.BGPPathType,
+				BGPPath: &route.BGPPath{
+					PathIdentifier: 1,
+				},
+			},
+			expected: []byte{
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
+				0x00, 0x1b, // BGP Message Length
+				0x02,       // BGP Message Type == Update
+				0x00, 0x04, // WithDraw Octet length
+				0x18,             // Prefix Length
+				0x54, 0x38, 0xd4, // Prefix,
+				0x00, 0x00, // Total Path Attribute Length
+			},
+			expectedError: nil,
+		},
+		{
+			name:          "IPv6 MP_UNREACH_NLRI",
+			afi:           packet.IPv6AFI,
+			multiProtocol: true,
+			addPathTX:     false,
+			prefix:        bnet.NewPfx(bnet.IPv6FromBlocks(0x2804, 0x148c, 0, 0, 0, 0, 0, 0), 32),
+			path: &route.Path{
+				Type:    route.BGPPathType,
+				BGPPath: &route.BGPPath{},
+			},
+			expected: []byte{
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
+				0x00, 0x22, // BGP Message Length
+				0x02,       // BGP Message Type == Update
+				0x00, 0x00, // WithDraw Octet length
+				0x00, 0x0b, // Length
+				0x80,       // Flags
+				0x0f,       // Attribute Code
+				0x08,       // Attribute length
+				0x00, 0x02, // AFI
+				0x01,                         // SAFI
+				0x20, 0x28, 0x04, 0x14, 0x8c, // Prefix
+			},
+		},
+		{
+			name:          "IPv6 MP_UNREACH_NLRI with ADD-PATH",
+			afi:           packet.IPv6AFI,
+			multiProtocol: true,
+			addPathTX:     true,
+			prefix:        bnet.NewPfx(bnet.IPv6FromBlocks(0x2804, 0x148c, 0, 0, 0, 0, 0, 0), 32),
+			path: &route.Path{
+				Type: route.BGPPathType,
+				BGPPath: &route.BGPPath{
+					PathIdentifier: 100,
+				},
+			},
+			expected: []byte{
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
+				0x00, 0x26, // BGP Message Length
+				0x02,       // BGP Message Type == Update
+				0x00, 0x00, // WithDraw Octet length
+				0x00, 0x0f, // Length
+				0x80,       // Flags
+				0x0f,       // Attribute Code
+				0x0c,       // Attribute length
+				0x00, 0x02, // AFI
+				0x01,                  // SAFI
+				0x00, 0x00, 0x00, 100, // Path Identifier
+				0x20, 0x28, 0x04, 0x14, 0x8c, // Prefix
+			},
+		},
+		{
+			name:          "IPv6 MP_UNREACH_NLRI without multi protocol beeing negotiated",
+			afi:           packet.IPv6AFI,
+			multiProtocol: false,
+			addPathTX:     false,
+			prefix:        bnet.NewPfx(bnet.IPv6FromBlocks(0x2804, 0x148c, 0, 0, 0, 0, 0, 0), 32),
+			path: &route.Path{
+				Type: route.BGPPathType,
+				BGPPath: &route.BGPPath{
+					PathIdentifier: 1,
+				},
+			},
+			expected:      []byte{},
+			expectedError: errors.New("IPv6 was not negotiated"),
+		},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			buf := bytes.NewBuffer([]byte{})
+
+			u := &UpdateSender{
+				fsm: &FSM{},
+				addressFamily: &fsmAddressFamily{
+					addPathTX:     tc.addPathTX,
+					multiProtocol: tc.multiProtocol,
+					afi:           tc.afi,
+					safi:          packet.UnicastSAFI,
+				},
+				options: &packet.EncodeOptions{
+					UseAddPath: tc.addPathTX,
+				},
+			}
+
+			err := u.withdrawPrefix(buf, tc.prefix, tc.path)
+			assert.Equal(t, tc.expectedError, err, "error mismatch")
+			assert.Equal(t, tc.expected, buf.Bytes(), "expected different bytes")
+		})
+	}
+}
diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go
deleted file mode 100644
index 842d50f5497a31ff6562342f0f31eed79b5c32aa..0000000000000000000000000000000000000000
--- a/protocols/bgp/server/withdraw.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package server
-
-import (
-	"errors"
-	"io"
-
-	"github.com/bio-routing/bio-rd/net"
-	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
-	"github.com/bio-routing/bio-rd/route"
-)
-
-// withdrawPrefixes generates a BGPUpdate message and write it to the given
-// io.Writer.
-func withdrawPrefixes(out io.Writer, opt *types.Options, prefixes ...net.Prefix) error {
-	if len(prefixes) < 1 {
-		return nil
-	}
-	var rootNLRI *packet.NLRI
-	var currentNLRI *packet.NLRI
-	for _, pfx := range prefixes {
-		if rootNLRI == nil {
-			rootNLRI = &packet.NLRI{
-				IP:     pfx.Addr().ToUint32(),
-				Pfxlen: pfx.Pfxlen(),
-			}
-			currentNLRI = rootNLRI
-		} else {
-			currentNLRI.Next = &packet.NLRI{
-				IP:     pfx.Addr().ToUint32(),
-				Pfxlen: pfx.Pfxlen(),
-			}
-			currentNLRI = currentNLRI.Next
-		}
-	}
-	update := &packet.BGPUpdate{
-		WithdrawnRoutes: rootNLRI,
-	}
-	return serializeAndSendUpdate(out, update, opt)
-
-}
-
-// withdrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given
-// io.Writer.
-func withdrawPrefixesAddPath(out io.Writer, opt *types.Options, pfx net.Prefix, p *route.Path) error {
-	if p.Type != route.BGPPathType {
-		return errors.New("wrong path type, expected BGPPathType")
-	}
-	if p.BGPPath == nil {
-		return errors.New("got nil BGPPath")
-	}
-	update := &packet.BGPUpdate{
-		WithdrawnRoutes: &packet.NLRI{
-			PathIdentifier: p.BGPPath.PathIdentifier,
-			IP:             pfx.Addr().ToUint32(),
-			Pfxlen:         pfx.Pfxlen(),
-		},
-	}
-	return serializeAndSendUpdate(out, update, opt)
-}
-
-func withdrawPrefixesMultiProtocol(out io.Writer, opt *types.Options, pfx net.Prefix, afi uint16, safi uint8) error {
-	update := &packet.BGPUpdate{
-		PathAttributes: &packet.PathAttribute{
-			TypeCode: packet.MultiProtocolUnreachNLRICode,
-			Value: packet.MultiProtocolUnreachNLRI{
-				AFI:      afi,
-				SAFI:     safi,
-				Prefixes: []net.Prefix{pfx},
-			},
-		},
-	}
-
-	return serializeAndSendUpdate(out, update, opt)
-}
diff --git a/protocols/bgp/server/withdraw_test.go b/protocols/bgp/server/withdraw_test.go
deleted file mode 100644
index b379e2a00bf106fe8ff86c4866a351a2ddae8968..0000000000000000000000000000000000000000
--- a/protocols/bgp/server/withdraw_test.go
+++ /dev/null
@@ -1,160 +0,0 @@
-package server
-
-import (
-	"bytes"
-	"errors"
-	"testing"
-
-	"github.com/bio-routing/bio-rd/net"
-	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
-	"github.com/bio-routing/bio-rd/route"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestWithdrawPrefixes(t *testing.T) {
-	testcases := []struct {
-		Name          string
-		Prefix        []net.Prefix
-		Expected      []byte
-		ExpectedError error
-	}{
-		{
-			Name:   "One withdraw",
-			Prefix: []net.Prefix{net.NewPfx(net.IPv4(1413010532), 24)},
-			Expected: []byte{
-				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
-				0x00, 0x1b, // BGP Message Length
-				0x02,       // BGP Message Type == Update
-				0x00, 0x04, // WithDraw Octet length
-				0x18,             // Prefix Length
-				0x54, 0x38, 0xd4, // Prefix,
-				0x00, 0x00, // Total Path Attribute Length
-			},
-			ExpectedError: nil,
-		},
-		{
-			Name:   "two withdraws",
-			Prefix: []net.Prefix{net.NewPfx(net.IPv4(1413010532), 24), net.NewPfx(net.IPv4(1413010534), 25)},
-			Expected: []byte{
-				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
-				0x00, 0x20, // BGP Message Length
-				0x02,       // BGP Message Type == Update
-				0x00, 0x09, // WithDraw Octet length
-				0x18,             // Prefix Length first
-				0x54, 0x38, 0xd4, // Prefix,
-				0x19,                   // Prefix Length second
-				0x54, 0x38, 0xd4, 0x66, // Prefix,
-				0x00, 0x00, // Total Path Attribute Length
-			},
-			ExpectedError: nil,
-		},
-	}
-	for _, tc := range testcases {
-		buf := bytes.NewBuffer([]byte{})
-		opt := &types.Options{}
-		err := withdrawPrefixes(buf, opt, tc.Prefix...)
-		assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name)
-		assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name)
-	}
-}
-
-func TestWithDrawPrefixesMultiProtocol(t *testing.T) {
-	tests := []struct {
-		Name     string
-		Prefix   net.Prefix
-		Expected []byte
-	}{
-		{
-			Name:   "IPv6 MP_UNREACH_NLRI",
-			Prefix: net.NewPfx(net.IPv6FromBlocks(0x2804, 0x148c, 0, 0, 0, 0, 0, 0), 32),
-			Expected: []byte{
-				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
-				0x00, 0x22, // BGP Message Length
-				0x02,       // BGP Message Type == Update
-				0x00, 0x00, // WithDraw Octet length
-				0x00, 0x0b, // Length
-				0x80,       // Flags
-				0x0f,       // Attribute Code
-				0x08,       // Attribute length
-				0x00, 0x02, // AFI
-				0x01,                         // SAFI
-				0x20, 0x28, 0x04, 0x14, 0x8c, // Prefix
-			},
-		},
-	}
-	for _, test := range tests {
-		t.Run(test.Name, func(t *testing.T) {
-			buf := bytes.NewBuffer([]byte{})
-			opt := &types.Options{
-				AddPathRX: false,
-			}
-			err := withdrawPrefixesMultiProtocol(buf, opt, test.Prefix, packet.IPv6AFI, packet.UnicastSAFI)
-			if err != nil {
-				t.Fatalf("unexpected error: %v", err)
-			}
-
-			assert.Equal(t, test.Expected, buf.Bytes())
-		})
-	}
-}
-
-func TestWithDrawPrefixesAddPath(t *testing.T) {
-	testcases := []struct {
-		Name          string
-		Prefix        net.Prefix
-		Path          *route.Path
-		Expected      []byte
-		ExpectedError error
-	}{
-		{
-			Name:   "Normal withdraw",
-			Prefix: net.NewPfx(net.IPv4(1413010532), 24),
-			Path: &route.Path{
-				Type: route.BGPPathType,
-				BGPPath: &route.BGPPath{
-					PathIdentifier: 1,
-				},
-			},
-			Expected: []byte{
-				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // BGP Marker
-				0x00, 0x1f, // BGP Message Length
-				0x02,       // BGP Message Type == Update
-				0x00, 0x08, // WithDraw Octet length
-				0x00, 0x00, 0x00, 0x01, // NLRI Path Identifier
-				0x18,             // Prefix Length
-				0x54, 0x38, 0xd4, // Prefix,
-				0x00, 0x00, // Total Path Attribute Length
-			},
-			ExpectedError: nil,
-		},
-		{
-			Name:   "Non bgp withdraw",
-			Prefix: net.NewPfx(net.IPv4(1413010532), 24),
-			Path: &route.Path{
-				Type: route.StaticPathType,
-			},
-			Expected:      []byte{},
-			ExpectedError: errors.New("wrong path type, expected BGPPathType"),
-		},
-		{
-			Name:   "Nil BGPPathType",
-			Prefix: net.NewPfx(net.IPv4(1413010532), 24),
-			Path: &route.Path{
-				Type: route.BGPPathType,
-			},
-			Expected:      []byte{},
-			ExpectedError: errors.New("got nil BGPPath"),
-		},
-	}
-	for _, tc := range testcases {
-		buf := bytes.NewBuffer([]byte{})
-		opt := &types.Options{
-			AddPathRX: true,
-		}
-		err := withdrawPrefixesAddPath(buf, opt, tc.Prefix, tc.Path)
-		assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name)
-		assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name)
-	}
-}
diff --git a/protocols/bgp/types/BUILD.bazel b/protocols/bgp/types/BUILD.bazel
index 5f94662087b2e46f593e4386d2bac21e65896225..50a86ef428ad5e92ffbb6365f30e566c0267873b 100644
--- a/protocols/bgp/types/BUILD.bazel
+++ b/protocols/bgp/types/BUILD.bazel
@@ -7,7 +7,6 @@ go_library(
         "as_path.go",
         "community.go",
         "large_community.go",
-        "options.go",
         "unknown_attribute.go",
     ],
     importpath = "github.com/bio-routing/bio-rd/protocols/bgp/types",
diff --git a/protocols/bgp/types/options.go b/protocols/bgp/types/options.go
deleted file mode 100644
index 5bc87827dc6faff3948b0ec817d8151bdbc34d03..0000000000000000000000000000000000000000
--- a/protocols/bgp/types/options.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package types
-
-// Options represents options to the update sender, decoder and encoder
-type Options struct {
-	Supports4OctetASN bool
-	AddPathRX         bool
-	MultiProtocolIPv4 bool
-	MultiProtocolIPv6 bool
-}
diff --git a/routingtable/adjRIBOut/adj_rib_out.go b/routingtable/adjRIBOut/adj_rib_out.go
index b4271ea3c82734d6dd8f5e8dae7f2fb48db34c4b..a93a5cfe44ab570ce41873b7b6ad416dbd9fb8c3 100644
--- a/routingtable/adjRIBOut/adj_rib_out.go
+++ b/routingtable/adjRIBOut/adj_rib_out.go
@@ -17,18 +17,20 @@ type AdjRIBOut struct {
 	routingtable.ClientManager
 	rt            *routingtable.RoutingTable
 	neighbor      *routingtable.Neighbor
+	addPathTX     bool
 	pathIDManager *pathIDManager
-	mu            sync.RWMutex
 	exportFilter  *filter.Filter
+	mu            sync.RWMutex
 }
 
 // New creates a new Adjacency RIB Out with BGP add path
-func New(neighbor *routingtable.Neighbor, exportFilter *filter.Filter) *AdjRIBOut {
+func New(neighbor *routingtable.Neighbor, exportFilter *filter.Filter, addPathTX bool) *AdjRIBOut {
 	a := &AdjRIBOut{
 		rt:            routingtable.NewRoutingTable(),
 		neighbor:      neighbor,
 		pathIDManager: newPathIDManager(),
 		exportFilter:  exportFilter,
+		addPathTX:     addPathTX,
 	}
 	a.ClientManager = routingtable.NewClientManager(a)
 	return a
@@ -47,7 +49,7 @@ func (a *AdjRIBOut) RouteCount() int64 {
 // AddPath adds path p to prefix `pfx`
 func (a *AdjRIBOut) AddPath(pfx bnet.Prefix, p *route.Path) error {
 	if !routingtable.ShouldPropagateUpdate(pfx, p, a.neighbor) {
-		if a.neighbor.CapAddPathRX {
+		if a.addPathTX {
 			a.removePathsForPrefix(pfx)
 		}
 		return nil
@@ -94,8 +96,7 @@ func (a *AdjRIBOut) AddPath(pfx bnet.Prefix, p *route.Path) error {
 	a.mu.Lock()
 	defer a.mu.Unlock()
 
-	// AddPathRX capable neighbor
-	if a.neighbor.CapAddPathRX {
+	if a.addPathTX {
 		pathID, err := a.pathIDManager.addPath(p)
 		if err != nil {
 			return fmt.Errorf("Unable to get path ID: %v", err)
@@ -140,7 +141,7 @@ func (a *AdjRIBOut) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
 	a.rt.RemovePath(pfx, p)
 
 	// If the neighbar has AddPath capabilities, try to find the PathID
-	if a.neighbor.CapAddPathRX {
+	if a.addPathTX {
 		pathID, err := a.pathIDManager.releasePath(p)
 		if err != nil {
 			log.Warningf("Unable to release path for prefix %s: %v", pfx.String(), err)
diff --git a/routingtable/adjRIBOut/adj_rib_out_test.go b/routingtable/adjRIBOut/adj_rib_out_test.go
index 26eceeeed34b37d62303a9156f9074ba28411129..51465849d2e14bfb17086cbff14561c4f8b7eefe 100644
--- a/routingtable/adjRIBOut/adj_rib_out_test.go
+++ b/routingtable/adjRIBOut/adj_rib_out_test.go
@@ -22,10 +22,9 @@ func TestBestPathOnlyEBGP(t *testing.T) {
 		IBGP:              false,
 		LocalASN:          41981,
 		RouteServerClient: false,
-		CapAddPathRX:      false,
 	}
 
-	adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter())
+	adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter(), false)
 
 	tests := []struct {
 		name          string
@@ -329,10 +328,9 @@ func TestBestPathOnlyIBGP(t *testing.T) {
 		IBGP:              true,
 		LocalASN:          41981,
 		RouteServerClient: false,
-		CapAddPathRX:      false,
 	}
 
-	adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter())
+	adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter(), false)
 
 	tests := []struct {
 		name          string
@@ -544,12 +542,11 @@ func TestBestPathOnlyRRClient(t *testing.T) {
 		IBGP:                 true,
 		LocalASN:             41981,
 		RouteServerClient:    false,
-		CapAddPathRX:         false,
 		RouteReflectorClient: true,
 		ClusterID:            net.IPv4FromOctets(2, 2, 2, 2).ToUint32(),
 	}
 
-	adjRIBOut := New(neighborBestOnlyRR, filter.NewAcceptAllFilter())
+	adjRIBOut := New(neighborBestOnlyRR, filter.NewAcceptAllFilter(), false)
 
 	tests := []struct {
 		name          string
@@ -875,10 +872,9 @@ func TestAddPathIBGP(t *testing.T) {
 		IBGP:              true,
 		LocalASN:          41981,
 		RouteServerClient: false,
-		CapAddPathRX:      true,
 	}
 
-	adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter())
+	adjRIBOut := New(neighborBestOnlyEBGP, filter.NewAcceptAllFilter(), true)
 
 	tests := []struct {
 		name          string
diff --git a/routingtable/neighbor.go b/routingtable/neighbor.go
index 8b320636806792a2091cdc3cb4556c1b55a842bc..b72c7a193bbf5f0867ea573aa0e0d0f94e319aab 100644
--- a/routingtable/neighbor.go
+++ b/routingtable/neighbor.go
@@ -27,7 +27,4 @@ type Neighbor struct {
 
 	// ClusterID is our route reflectors clusterID
 	ClusterID uint32
-
-	// CapAddPathRX indicates if the peer supports receiving multiple BGP paths
-	CapAddPathRX bool
 }