diff --git a/config/BUILD.bazel b/config/BUILD.bazel
index 0597dcfbcb0107809739ca60ba91eb748ba56d5c..59561fc18788143092394d865b43cd731773674e 100644
--- a/config/BUILD.bazel
+++ b/config/BUILD.bazel
@@ -12,6 +12,7 @@ go_library(
         "//net:go_default_library",
         "//routingtable:go_default_library",
         "//routingtable/filter:go_default_library",
+        "//routingtable/locRIB:go_default_library",
         "//vendor/github.com/taktv6/tflow2/convert:go_default_library",
     ],
 )
diff --git a/config/peer.go b/config/peer.go
index 12124c7c6d3f44928dfaa5a1b9a05fa3bbf645a7..82a9f61f56fea089384b18ada019d4192834d6ea 100644
--- a/config/peer.go
+++ b/config/peer.go
@@ -6,6 +6,7 @@ import (
 	bnet "github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/routingtable"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
 )
 
 // Peer defines the configuration for a BGP session
@@ -22,10 +23,16 @@ type Peer struct {
 	RouterID                uint32
 	AddPathSend             routingtable.ClientOptions
 	AddPathRecv             bool
-	ImportFilter            *filter.Filter
-	ExportFilter            *filter.Filter
 	RouteServerClient       bool
 	RouteReflectorClient    bool
 	RouteReflectorClusterID uint32
-	IPv6                    bool
+	IPv4                    *AddressFamilyConfig
+	IPv6                    *AddressFamilyConfig
+}
+
+// AddressFamilyConfig represents all configuration parameters specific for an address family
+type AddressFamilyConfig struct {
+	RIB          *locRIB.LocRIB
+	ImportFilter *filter.Filter
+	ExportFilter *filter.Filter
 }
diff --git a/main_ipv4.go b/main_ipv4.go
index 65293f07853d5819abdb96a6ab5def218f0c2320..0d3c5a7cd4b0ce93fbe645136bd588cfbc872c2b 100644
--- a/main_ipv4.go
+++ b/main_ipv4.go
@@ -43,10 +43,13 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		AddPathSend: routingtable.ClientOptions{
 			MaxPaths: 10,
 		},
-		ImportFilter:      filter.NewAcceptAllFilter(),
-		ExportFilter:      filter.NewAcceptAllFilter(),
+		IPv4: &config.AddressFamilyConfig{
+			RIB:          rib,
+			ImportFilter: filter.NewAcceptAllFilter(),
+			ExportFilter: filter.NewAcceptAllFilter(),
+		},
 		RouteServerClient: true,
-	}, rib)
+	})
 
 	b.AddPeer(config.Peer{
 		AdminEnabled:      true,
@@ -63,8 +66,11 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 			MaxPaths: 10,
 		},
 		AddPathRecv:       true,
-		ImportFilter:      filter.NewAcceptAllFilter(),
-		ExportFilter:      filter.NewAcceptAllFilter(),
 		RouteServerClient: true,
-	}, rib)
+		IPv4: &config.AddressFamilyConfig{
+			RIB:          rib,
+			ImportFilter: filter.NewAcceptAllFilter(),
+			ExportFilter: filter.NewAcceptAllFilter(),
+		},
+	})
 }
diff --git a/main_ipv6.go b/main_ipv6.go
index 160007cc76481b968cfc98de0c459f1422b3b157..2d4a24100e2e1eea680fa63368235495846456ac 100644
--- a/main_ipv6.go
+++ b/main_ipv6.go
@@ -41,10 +41,12 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		AddPathSend: routingtable.ClientOptions{
 			BestOnly: true,
 		},
-		ImportFilter: filter.NewAcceptAllFilter(),
-		ExportFilter: filter.NewDrainFilter(),
-		IPv6:         true,
-	}, rib)
+		IPv6: &config.AddressFamilyConfig{
+			RIB:          rib,
+			ImportFilter: filter.NewAcceptAllFilter(),
+			ExportFilter: filter.NewDrainFilter(),
+		},
+	})
 
 	b.AddPeer(config.Peer{
 		AdminEnabled:      true,
@@ -60,8 +62,10 @@ func startServer(b server.BGPServer, rib *locRIB.LocRIB) {
 		AddPathSend: routingtable.ClientOptions{
 			BestOnly: true,
 		},
-		ImportFilter: filter.NewDrainFilter(),
-		ExportFilter: filter.NewAcceptAllFilter(),
-		IPv6:         true,
-	}, rib)
+		IPv6: &config.AddressFamilyConfig{
+			RIB:          rib,
+			ImportFilter: filter.NewDrainFilter(),
+			ExportFilter: filter.NewAcceptAllFilter(),
+		},
+	})
 }
diff --git a/protocols/bgp/server/BUILD.bazel b/protocols/bgp/server/BUILD.bazel
index 631671e09122407f96bf8bf012840bc1e7d99391..78bdeb1f3635cabb3ead2855ae4431ee20a3c04c 100644
--- a/protocols/bgp/server/BUILD.bazel
+++ b/protocols/bgp/server/BUILD.bazel
@@ -6,6 +6,7 @@ go_library(
         "fake_conn.go",
         "fsm.go",
         "fsm_active.go",
+        "fsm_address_family.go",
         "fsm_cease.go",
         "fsm_connect.go",
         "fsm_established.go",
@@ -42,7 +43,7 @@ go_library(
 go_test(
     name = "go_default_test",
     srcs = [
-        "fsm_established_test.go",
+        "fsm_address_family_test.go",
         "fsm_open_sent_test.go",
         "fsm_test.go",
         "server_test.go",
diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go
index a5b48ef195df148ad69139c638601e0dae935586..c8d98157ebf1d447630202179bbf08963d01af56 100644
--- a/protocols/bgp/server/fsm.go
+++ b/protocols/bgp/server/fsm.go
@@ -10,8 +10,6 @@ import (
 
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/protocols/bgp/types"
-	"github.com/bio-routing/bio-rd/routingtable"
-	"github.com/bio-routing/bio-rd/routingtable/locRIB"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -62,10 +60,8 @@ type FSM struct {
 	local net.IP
 
 	ribsInitialized bool
-	adjRIBIn        routingtable.RouteTableClient
-	adjRIBOut       routingtable.RouteTableClient
-	rib             *locRIB.LocRIB
-	updateSender    *UpdateSender
+	ipv4Unicast     *fsmAddressFamily
+	ipv6Unicast     *fsmAddressFamily
 
 	neighborID uint32
 	state      state
@@ -93,7 +89,7 @@ func NewActiveFSM2(peer *peer) *FSM {
 }
 
 func newFSM2(peer *peer) *FSM {
-	return &FSM{
+	f := &FSM{
 		connectRetryTime: time.Minute,
 		peer:             peer,
 		eventCh:          make(chan int),
@@ -103,9 +99,18 @@ func newFSM2(peer *peer) *FSM {
 		msgRecvCh:        make(chan []byte),
 		msgRecvFailCh:    make(chan error),
 		stopMsgRecvCh:    make(chan struct{}),
-		rib:              peer.rib,
 		options:          &types.Options{},
 	}
+
+	if peer.ipv4 != nil {
+		f.ipv4Unicast = newFSMAddressFamily(packet.IPv4AFI, packet.UnicastSAFI, peer.ipv4, f)
+	}
+
+	if peer.ipv6 != nil {
+		f.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, peer.ipv6, f)
+	}
+
+	return f
 }
 
 func (fsm *FSM) start() {
diff --git a/protocols/bgp/server/fsm_address_family.go b/protocols/bgp/server/fsm_address_family.go
new file mode 100644
index 0000000000000000000000000000000000000000..daa5465e16f2bb90e0da43316b4bb475021ffd1b
--- /dev/null
+++ b/protocols/bgp/server/fsm_address_family.go
@@ -0,0 +1,206 @@
+package server
+
+import (
+	"time"
+
+	bnet "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/bio-routing/bio-rd/routingtable"
+	"github.com/bio-routing/bio-rd/routingtable/adjRIBIn"
+	"github.com/bio-routing/bio-rd/routingtable/adjRIBOut"
+	"github.com/bio-routing/bio-rd/routingtable/filter"
+	"github.com/bio-routing/bio-rd/routingtable/locRIB"
+)
+
+// fsmAddressFamily holds RIBs and the UpdateSender of an peer for an AFI/SAFI combination
+type fsmAddressFamily struct {
+	afi  uint16
+	safi uint8
+	fsm  *FSM
+
+	adjRIBIn  routingtable.RouteTableClient
+	adjRIBOut routingtable.RouteTableClient
+	rib       *locRIB.LocRIB
+
+	importFilter *filter.Filter
+	exportFilter *filter.Filter
+
+	updateSender *UpdateSender
+
+	initialized bool
+}
+
+func newFSMAddressFamily(afi uint16, safi uint8, params *familyParameters, fsm *FSM) *fsmAddressFamily {
+	return &fsmAddressFamily{
+		afi:          afi,
+		safi:         safi,
+		fsm:          fsm,
+		rib:          params.rib,
+		importFilter: params.importFilter,
+		exportFilter: params.exportFilter,
+	}
+}
+
+func (f *fsmAddressFamily) init(n *routingtable.Neighbor) {
+	contributingASNs := f.rib.GetContributingASNs()
+
+	f.adjRIBIn = adjRIBIn.New(f.importFilter, contributingASNs, f.fsm.peer.routerID, f.fsm.peer.clusterID)
+	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.updateSender = newUpdateSender(f.fsm, f.afi, f.safi)
+	f.updateSender.Start(time.Millisecond * 5)
+
+	f.adjRIBOut.Register(f.updateSender)
+	f.rib.RegisterWithOptions(f.adjRIBOut, clientOptions)
+}
+
+func (f *fsmAddressFamily) dispose() {
+	if !f.initialized {
+		return
+	}
+
+	f.rib.GetContributingASNs().Remove(f.fsm.peer.localASN)
+	f.adjRIBIn.Unregister(f.rib)
+	f.rib.Unregister(f.adjRIBOut)
+	f.adjRIBOut.Unregister(f.updateSender)
+	f.updateSender.Destroy()
+
+	f.adjRIBIn = nil
+	f.adjRIBOut = nil
+
+	f.initialized = false
+}
+
+func (f *fsmAddressFamily) processUpdate(u *packet.BGPUpdate) {
+	if f.afi == packet.IPv4AFI && f.safi == packet.UnicastSAFI {
+		f.withdraws(u)
+		f.updates(u)
+	}
+
+	if f.fsm.options.SupportsMultiProtocol {
+		f.multiProtocolUpdates(u)
+	}
+}
+
+func (f *fsmAddressFamily) withdraws(u *packet.BGPUpdate) {
+	for r := u.WithdrawnRoutes; r != nil; r = r.Next {
+		pfx := bnet.NewPfx(bnet.IPv4(r.IP), r.Pfxlen)
+		f.adjRIBIn.RemovePath(pfx, nil)
+	}
+}
+
+func (f *fsmAddressFamily) updates(u *packet.BGPUpdate) {
+	for r := u.NLRI; r != nil; r = r.Next {
+		pfx := bnet.NewPfx(bnet.IPv4(r.IP), r.Pfxlen)
+
+		path := f.newRoutePath()
+		f.processAttributes(u.PathAttributes, path)
+
+		f.adjRIBIn.AddPath(pfx, path)
+	}
+}
+
+func (f *fsmAddressFamily) multiProtocolUpdates(u *packet.BGPUpdate) {
+	if !f.fsm.options.SupportsMultiProtocol {
+		return
+	}
+
+	path := f.newRoutePath()
+	f.processAttributes(u.PathAttributes, path)
+
+	for pa := u.PathAttributes; pa != nil; pa = pa.Next {
+		switch pa.TypeCode {
+		case packet.MultiProtocolReachNLRICode:
+			f.multiProtocolUpdate(path, pa.Value.(packet.MultiProtocolReachNLRI))
+		case packet.MultiProtocolUnreachNLRICode:
+			f.multiProtocolWithdraw(path, pa.Value.(packet.MultiProtocolUnreachNLRI))
+		}
+	}
+}
+
+func (f *fsmAddressFamily) newRoutePath() *route.Path {
+	return &route.Path{
+		Type: route.BGPPathType,
+		BGPPath: &route.BGPPath{
+			Source: f.fsm.peer.addr,
+			EBGP:   f.fsm.peer.localASN != f.fsm.peer.peerASN,
+		},
+	}
+}
+
+func (f *fsmAddressFamily) multiProtocolUpdate(path *route.Path, nlri packet.MultiProtocolReachNLRI) {
+	path.BGPPath.NextHop = nlri.NextHop
+
+	for _, pfx := range nlri.Prefixes {
+		f.adjRIBIn.AddPath(pfx, path)
+	}
+}
+
+func (f *fsmAddressFamily) multiProtocolWithdraw(path *route.Path, nlri packet.MultiProtocolUnreachNLRI) {
+	for _, pfx := range nlri.Prefixes {
+		f.adjRIBIn.RemovePath(pfx, path)
+	}
+}
+
+func (f *fsmAddressFamily) processAttributes(attrs *packet.PathAttribute, path *route.Path) {
+	for pa := attrs; pa != nil; pa = pa.Next {
+		switch pa.TypeCode {
+		case packet.OriginAttr:
+			path.BGPPath.Origin = pa.Value.(uint8)
+		case packet.LocalPrefAttr:
+			path.BGPPath.LocalPref = pa.Value.(uint32)
+		case packet.MEDAttr:
+			path.BGPPath.MED = pa.Value.(uint32)
+		case packet.NextHopAttr:
+			path.BGPPath.NextHop = pa.Value.(bnet.IP)
+		case packet.ASPathAttr:
+			path.BGPPath.ASPath = pa.Value.(types.ASPath)
+			path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length()
+		case packet.AggregatorAttr:
+			aggr := pa.Value.(types.Aggregator)
+			path.BGPPath.Aggregator = &aggr
+		case packet.AtomicAggrAttr:
+			path.BGPPath.AtomicAggregate = true
+		case packet.CommunitiesAttr:
+			path.BGPPath.Communities = pa.Value.([]uint32)
+		case packet.LargeCommunitiesAttr:
+			path.BGPPath.LargeCommunities = pa.Value.([]types.LargeCommunity)
+		case packet.OriginatorIDAttr:
+			path.BGPPath.OriginatorID = pa.Value.(uint32)
+		case packet.ClusterListAttr:
+			path.BGPPath.ClusterList = pa.Value.([]uint32)
+		default:
+			unknownAttr := f.processUnknownAttribute(pa)
+			if unknownAttr != nil {
+				path.BGPPath.UnknownAttributes = append(path.BGPPath.UnknownAttributes, *unknownAttr)
+			}
+		}
+	}
+}
+
+func (f *fsmAddressFamily) processUnknownAttribute(attr *packet.PathAttribute) *types.UnknownPathAttribute {
+	if !attr.Transitive {
+		return nil
+	}
+
+	u := &types.UnknownPathAttribute{
+		Transitive: true,
+		Optional:   attr.Optional,
+		Partial:    attr.Partial,
+		TypeCode:   attr.TypeCode,
+		Value:      attr.Value.([]byte),
+	}
+
+	return u
+}
diff --git a/protocols/bgp/server/fsm_established_test.go b/protocols/bgp/server/fsm_address_family_test.go
similarity index 95%
rename from protocols/bgp/server/fsm_established_test.go
rename to protocols/bgp/server/fsm_address_family_test.go
index 61103d99c133e01e47ab923e81d121f5acc48018..54bd4b4ac9a7d40718f882d9408bf630d981b15a 100644
--- a/protocols/bgp/server/fsm_established_test.go
+++ b/protocols/bgp/server/fsm_address_family_test.go
@@ -43,12 +43,12 @@ func TestProcessAttributes(t *testing.T) {
 		Next: unknown1,
 	}
 
-	e := &establishedState{}
+	f := &fsmAddressFamily{}
 
 	p := &route.Path{
 		BGPPath: &route.BGPPath{},
 	}
-	e.processAttributes(asPath, p)
+	f.processAttributes(asPath, p)
 
 	expectedCodes := []uint8{200, 100}
 	expectedValues := [][]byte{[]byte{5, 6}, []byte{1, 2, 3, 4}}
diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go
index 2baf46ab430f9d000c67b65091e014f785945157..2ffa19415e0d1233b5718b9127b8dbbfaaf9874b 100644
--- a/protocols/bgp/server/fsm_established.go
+++ b/protocols/bgp/server/fsm_established.go
@@ -4,15 +4,12 @@ import (
 	"bytes"
 	"fmt"
 	"net"
-	"time"
 
 	bnet "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/bio-routing/bio-rd/routingtable"
-	"github.com/bio-routing/bio-rd/routingtable/adjRIBIn"
-	"github.com/bio-routing/bio-rd/routingtable/adjRIBOut"
+	log "github.com/sirupsen/logrus"
 )
 
 type establishedState struct {
@@ -57,12 +54,6 @@ func (s establishedState) run() (state, string) {
 }
 
 func (s *establishedState) init() error {
-	contributingASNs := s.fsm.rib.GetContributingASNs()
-
-	s.fsm.adjRIBIn = adjRIBIn.New(s.fsm.peer.importFilter, contributingASNs, s.fsm.peer.routerID, s.fsm.peer.clusterID)
-	contributingASNs.Add(s.fsm.peer.localASN)
-	s.fsm.adjRIBIn.Register(s.fsm.rib)
-
 	host, _, err := net.SplitHostPort(s.fsm.con.LocalAddr().String())
 	if err != nil {
 		return fmt.Errorf("Unable to get local address: %v", err)
@@ -88,35 +79,26 @@ func (s *establishedState) init() error {
 		ClusterID:            s.fsm.peer.clusterID,
 	}
 
-	s.fsm.adjRIBOut = adjRIBOut.New(n, s.fsm.peer.exportFilter)
-	clientOptions := routingtable.ClientOptions{
-		BestOnly: true,
-	}
-	if s.fsm.options.AddPathRX {
-		clientOptions = s.fsm.peer.addPathSend
+	if s.fsm.ipv4Unicast != nil {
+		s.fsm.ipv4Unicast.init(n)
 	}
 
-	s.fsm.updateSender = newUpdateSender(s.fsm)
-	s.fsm.updateSender.Start(time.Millisecond * 5)
-
-	s.fsm.adjRIBOut.Register(s.fsm.updateSender)
-	s.fsm.rib.RegisterWithOptions(s.fsm.adjRIBOut, clientOptions)
+	if s.fsm.ipv6Unicast != nil {
+		s.fsm.ipv6Unicast.init(n)
+	}
 
 	s.fsm.ribsInitialized = true
 	return nil
 }
 
 func (s *establishedState) uninit() {
-	s.fsm.rib.GetContributingASNs().Remove(s.fsm.peer.localASN)
-	s.fsm.adjRIBIn.Unregister(s.fsm.rib)
-	s.fsm.rib.Unregister(s.fsm.adjRIBOut)
-	s.fsm.adjRIBOut.Unregister(s.fsm.updateSender)
-	s.fsm.updateSender.Destroy()
-
-	s.fsm.adjRIBIn = nil
-	s.fsm.adjRIBOut = nil
+	if s.fsm.ipv4Unicast != nil {
+		s.fsm.ipv4Unicast.dispose()
+	}
 
-	s.fsm.ribsInitialized = false
+	if s.fsm.ipv6Unicast != nil {
+		s.fsm.ipv6Unicast.dispose()
+	}
 }
 
 func (s *establishedState) manualStop() (state, string) {
@@ -205,123 +187,47 @@ func (s *establishedState) update(msg *packet.BGPMessage) (state, string) {
 	}
 
 	u := msg.Body.(*packet.BGPUpdate)
-	s.withdraws(u)
-	s.updates(u)
-	s.multiProtocolUpdates(u)
-
-	return newEstablishedState(s.fsm), s.fsm.reason
-}
+	afi, safi := s.addressFamilyForUpdate(u)
 
-func (s *establishedState) withdraws(u *packet.BGPUpdate) {
-	for r := u.WithdrawnRoutes; r != nil; r = r.Next {
-		pfx := bnet.NewPfx(bnet.IPv4(r.IP), r.Pfxlen)
-		s.fsm.adjRIBIn.RemovePath(pfx, nil)
+	if safi != packet.UnicastSAFI {
+		// only unicast support, so other SAFIs are ignored
+		return newEstablishedState(s.fsm), s.fsm.reason
 	}
-}
-
-func (s *establishedState) updates(u *packet.BGPUpdate) {
-	for r := u.NLRI; r != nil; r = r.Next {
-		pfx := bnet.NewPfx(bnet.IPv4(r.IP), r.Pfxlen)
-
-		path := s.newRoutePath()
-		s.processAttributes(u.PathAttributes, path)
 
-		s.fsm.adjRIBIn.AddPath(pfx, path)
-	}
-}
-
-func (s *establishedState) multiProtocolUpdates(u *packet.BGPUpdate) {
-	if !s.fsm.options.SupportsMultiProtocol {
-		return
-	}
-
-	path := s.newRoutePath()
-	s.processAttributes(u.PathAttributes, path)
-
-	for pa := u.PathAttributes; pa != nil; pa = pa.Next {
-		switch pa.TypeCode {
-		case packet.MultiProtocolReachNLRICode:
-			s.multiProtocolUpdate(path, pa.Value.(packet.MultiProtocolReachNLRI))
-		case packet.MultiProtocolUnreachNLRICode:
-			s.multiProtocolWithdraw(path, pa.Value.(packet.MultiProtocolUnreachNLRI))
+	switch afi {
+	case packet.IPv4AFI:
+		if s.fsm.ipv4Unicast == nil {
+			log.Warnf("Received update for family IPv4 unicast, but this family is not configured.")
 		}
+		s.fsm.ipv4Unicast.processUpdate(u)
+	case packet.IPv6AFI:
+		if s.fsm.ipv6Unicast == nil {
+			log.Warnf("Received update for family IPv6 unicast, but this family is not configured.")
+		}
+		s.fsm.ipv6Unicast.processUpdate(u)
 	}
-}
-
-func (s *establishedState) newRoutePath() *route.Path {
-	return &route.Path{
-		Type: route.BGPPathType,
-		BGPPath: &route.BGPPath{
-			Source: s.fsm.peer.addr,
-			EBGP:   s.fsm.peer.localASN != s.fsm.peer.peerASN,
-		},
-	}
-}
 
-func (s *establishedState) multiProtocolUpdate(path *route.Path, nlri packet.MultiProtocolReachNLRI) {
-	path.BGPPath.NextHop = nlri.NextHop
-
-	for _, pfx := range nlri.Prefixes {
-		s.fsm.adjRIBIn.AddPath(pfx, path)
-	}
+	return newEstablishedState(s.fsm), s.fsm.reason
 }
 
-func (s *establishedState) multiProtocolWithdraw(path *route.Path, nlri packet.MultiProtocolUnreachNLRI) {
-	for _, pfx := range nlri.Prefixes {
-		s.fsm.adjRIBIn.RemovePath(pfx, path)
+func (s *establishedState) addressFamilyForUpdate(u *packet.BGPUpdate) (afi uint16, safi uint8) {
+	if !s.fsm.options.SupportsMultiProtocol || u.NLRI != nil || u.WithdrawnRoutes != nil {
+		return packet.IPv4AFI, packet.UnicastSAFI
 	}
-}
 
-func (s *establishedState) processAttributes(attrs *packet.PathAttribute, path *route.Path) {
-	for pa := attrs; pa != nil; pa = pa.Next {
-		switch pa.TypeCode {
-		case packet.OriginAttr:
-			path.BGPPath.Origin = pa.Value.(uint8)
-		case packet.LocalPrefAttr:
-			path.BGPPath.LocalPref = pa.Value.(uint32)
-		case packet.MEDAttr:
-			path.BGPPath.MED = pa.Value.(uint32)
-		case packet.NextHopAttr:
-			path.BGPPath.NextHop = pa.Value.(bnet.IP)
-		case packet.ASPathAttr:
-			path.BGPPath.ASPath = pa.Value.(types.ASPath)
-			path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length()
-		case packet.AggregatorAttr:
-			aggr := pa.Value.(types.Aggregator)
-			path.BGPPath.Aggregator = &aggr
-		case packet.AtomicAggrAttr:
-			path.BGPPath.AtomicAggregate = true
-		case packet.CommunitiesAttr:
-			path.BGPPath.Communities = pa.Value.([]uint32)
-		case packet.LargeCommunitiesAttr:
-			path.BGPPath.LargeCommunities = pa.Value.([]types.LargeCommunity)
-		case packet.OriginatorIDAttr:
-			path.BGPPath.OriginatorID = pa.Value.(uint32)
-		case packet.ClusterListAttr:
-			path.BGPPath.ClusterList = pa.Value.([]uint32)
-		default:
-			unknownAttr := s.processUnknownAttribute(pa)
-			if unknownAttr != nil {
-				path.BGPPath.UnknownAttributes = append(path.BGPPath.UnknownAttributes, *unknownAttr)
-			}
+	for cur := u.PathAttributes; cur != nil; cur = cur.Next {
+		if cur.TypeCode == packet.MultiProtocolReachNLRICode {
+			a := cur.Value.(packet.MultiProtocolReachNLRI)
+			return a.AFI, a.SAFI
 		}
-	}
-}
-
-func (s *establishedState) processUnknownAttribute(attr *packet.PathAttribute) *types.UnknownPathAttribute {
-	if !attr.Transitive {
-		return nil
-	}
 
-	u := &types.UnknownPathAttribute{
-		Transitive: true,
-		Optional:   attr.Optional,
-		Partial:    attr.Partial,
-		TypeCode:   attr.TypeCode,
-		Value:      attr.Value.([]byte),
+		if cur.TypeCode == packet.MultiProtocolUnreachNLRICode {
+			a := cur.Value.(packet.MultiProtocolUnreachNLRI)
+			return a.AFI, a.SAFI
+		}
 	}
 
-	return u
+	return
 }
 
 func (s *establishedState) keepaliveReceived() (state, string) {
diff --git a/protocols/bgp/server/fsm_test.go b/protocols/bgp/server/fsm_test.go
index 5cca8d85ead35eeca7d49d04ab64e0cf8d6dc7ec..50181d8e3aa66ec34dbc21e7c18827e1b6b3d4c8 100644
--- a/protocols/bgp/server/fsm_test.go
+++ b/protocols/bgp/server/fsm_test.go
@@ -13,14 +13,16 @@ import (
 	bnet "github.com/bio-routing/bio-rd/net"
 )
 
-// TestFSM100Updates emulates receiving 100 BGP updates and withdraws. Checks route counts.
-func TestFSM100Updates(t *testing.T) {
+// TestFSM255UpdatesIPv4 emulates receiving 255 BGP updates and withdraws. Checks route counts.
+func TestFSM100UpdatesIPv4(t *testing.T) {
 	fsmA := newFSM2(&peer{
-		addr:         bnet.IPv4FromOctets(169, 254, 100, 100),
-		rib:          locRIB.New(),
-		importFilter: filter.NewAcceptAllFilter(),
-		exportFilter: filter.NewAcceptAllFilter(),
-		routerID:     bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(),
+		addr:     bnet.IPv4FromOctets(169, 254, 100, 100),
+		routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(),
+		ipv4: &familyParameters{
+			rib:          locRIB.New(),
+			importFilter: filter.NewAcceptAllFilter(),
+			exportFilter: filter.NewAcceptAllFilter(),
+		},
 	})
 
 	fsmA.holdTimer = time.NewTimer(time.Second * 90)
@@ -94,7 +96,7 @@ func TestFSM100Updates(t *testing.T) {
 	}
 
 	time.Sleep(time.Second)
-	ribRouteCount := fsmA.rib.RouteCount()
+	ribRouteCount := fsmA.ipv4Unicast.rib.RouteCount()
 	if ribRouteCount != 255 {
 		t.Errorf("Unexpected route count in LocRIB: %d", ribRouteCount)
 	}
@@ -112,11 +114,126 @@ func TestFSM100Updates(t *testing.T) {
 			0, 0,
 		}
 		fsmA.msgRecvCh <- update
-		ribRouteCount = fsmA.rib.RouteCount()
+		ribRouteCount = fsmA.ipv4Unicast.rib.RouteCount()
+	}
+	time.Sleep(time.Second * 1)
+
+	ribRouteCount = fsmA.ipv4Unicast.rib.RouteCount()
+	if ribRouteCount != 0 {
+		t.Errorf("Unexpected route count in LocRIB: %d", ribRouteCount)
+	}
+
+	fsmA.eventCh <- ManualStop
+	wg.Wait()
+}
+
+// TestFSM255UpdatesIPv6 emulates receiving 255 BGP updates and withdraws. Checks route counts.
+func TestFSM255UpdatesIPv6(t *testing.T) {
+	fsmA := newFSM2(&peer{
+		addr:     bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0xffff, 0, 0, 0, 1),
+		routerID: bnet.IPv4FromOctets(1, 1, 1, 1).ToUint32(),
+		ipv6: &familyParameters{
+			rib:          locRIB.New(),
+			importFilter: filter.NewAcceptAllFilter(),
+			exportFilter: filter.NewAcceptAllFilter(),
+		},
+	})
+	fsmA.options.SupportsMultiProtocol = true
+
+	fsmA.holdTimer = time.NewTimer(time.Second * 90)
+	fsmA.keepaliveTimer = time.NewTimer(time.Second * 30)
+	fsmA.connectRetryTimer = time.NewTimer(time.Second * 120)
+	fsmA.state = newEstablishedState(fsmA)
+
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		fsmA.con = fakeConn{}
+		for {
+			nextState, reason := fsmA.state.run()
+			fsmA.state = nextState
+			stateName := stateName(nextState)
+			switch stateName {
+			case "idle":
+				wg.Done()
+				return
+			case "cease":
+				t.Errorf("Unexpected cease state: %s", reason)
+				wg.Done()
+				return
+			case "established":
+				continue
+			default:
+				t.Errorf("Unexpected new state: %s", reason)
+				wg.Done()
+				return
+			}
+		}
+
+	}()
+
+	for i := uint8(0); i < 255; i++ {
+		update := []byte{
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0, 76,
+			2,
+			0, 0,
+			0, 53,
+			64, // Attribute flags
+			1,  // Attribute Type code (ORIGIN)
+			1,  // Length
+			2,  // INCOMPLETE
+
+			64,     // Attribute flags
+			2,      // Attribute Type code (AS Path)
+			12,     // Length
+			2,      // Type = AS_SEQUENCE
+			2,      // Path Segement Length
+			59, 65, // AS15169
+			12, 248, // AS3320
+			1,      // Type = AS_SET
+			2,      // Path Segement Length
+			59, 65, // AS15169
+			12, 248, // AS3320
+
+			0x90,     // Attribute flags
+			0x0e,     // MP_REACH_NLRI
+			0x00, 30, // Length
+			0x00, 0x02, // AFI
+			0x01,                                                                                                 // SAFI
+			0x10, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // Nexthop
+			0x00,
+			64, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x0, i,
+		}
+
+		fsmA.msgRecvCh <- update
+	}
+
+	time.Sleep(time.Second)
+	ribRouteCount := fsmA.ipv6Unicast.rib.RouteCount()
+	if ribRouteCount != 255 {
+		t.Errorf("Unexpected route count in LocRIB: %d", ribRouteCount)
+	}
+
+	for i := uint8(0); i < 255; i++ {
+		update := []byte{
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0x00, 35, // Length
+			0x02,       // UPDATE
+			0x00, 0x00, // withdrawn routes
+			0x00, 0x0c,
+			0x90, 0x0f,
+			0x00, 12, // Length
+			0x00, 0x02, // AFI
+			0x01, // SAFI
+			64, 0x20, 0x01, 0x06, 0x78, 0x01, 0xe0, 0x0, i,
+		}
+		fsmA.msgRecvCh <- update
+		ribRouteCount = fsmA.ipv6Unicast.rib.RouteCount()
 	}
 	time.Sleep(time.Second * 1)
 
-	ribRouteCount = fsmA.rib.RouteCount()
+	ribRouteCount = fsmA.ipv6Unicast.rib.RouteCount()
 	if ribRouteCount != 0 {
 		t.Errorf("Unexpected route count in LocRIB: %d", ribRouteCount)
 	}
diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go
index 02cf053bf000c5d1dd232c2243b7b617062a8fcf..4f101d815a7789a26564b6d37527026746404e5e 100644
--- a/protocols/bgp/server/peer.go
+++ b/protocols/bgp/server/peer.go
@@ -28,7 +28,6 @@ type peer struct {
 	fsms   []*FSM
 	fsmsMu sync.Mutex
 
-	rib                  *locRIB.LocRIB
 	routerID             uint32
 	addPathSend          routingtable.ClientOptions
 	addPathRecv          bool
@@ -36,11 +35,18 @@ type peer struct {
 	keepaliveTime        time.Duration
 	holdTime             time.Duration
 	optOpenParams        []packet.OptParam
-	importFilter         *filter.Filter
-	exportFilter         *filter.Filter
 	routeServerClient    bool
 	routeReflectorClient bool
 	clusterID            uint32
+
+	ipv4 *familyParameters
+	ipv6 *familyParameters
+}
+
+type familyParameters struct {
+	rib          *locRIB.LocRIB
+	importFilter *filter.Filter
+	exportFilter *filter.Filter
 }
 
 func (p *peer) snapshot() PeerInfo {
@@ -103,37 +109,41 @@ func isEstablishedState(s state) bool {
 
 // NewPeer creates a new peer with the given config. If an connection is established, the adjRIBIN of the peer is connected
 // to the given rib. To actually connect the peer, call Start() on the returned peer.
-func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error) {
+func newPeer(c config.Peer, server *bgpServer) (*peer, error) {
 	if c.LocalAS == 0 {
 		c.LocalAS = server.localASN
 	}
+
 	p := &peer{
 		server:               server,
 		addr:                 c.PeerAddress,
 		peerASN:              c.PeerAS,
 		localASN:             c.LocalAS,
 		fsms:                 make([]*FSM, 0),
-		rib:                  rib,
 		addPathSend:          c.AddPathSend,
 		addPathRecv:          c.AddPathRecv,
 		reconnectInterval:    c.ReconnectInterval,
 		keepaliveTime:        c.KeepAlive,
 		holdTime:             c.HoldTime,
 		optOpenParams:        make([]packet.OptParam, 0),
-		importFilter:         filterOrDefault(c.ImportFilter),
-		exportFilter:         filterOrDefault(c.ExportFilter),
 		routeServerClient:    c.RouteServerClient,
 		routeReflectorClient: c.RouteReflectorClient,
 		clusterID:            c.RouteReflectorClusterID,
 	}
 
+	if c.IPv4 != nil {
+		p.ipv4 = &familyParameters{
+			rib:          c.IPv4.RIB,
+			importFilter: filterOrDefault(c.IPv4.ImportFilter),
+			exportFilter: filterOrDefault(c.IPv4.ExportFilter),
+		}
+	}
+
 	// If we are a route reflector and no ClusterID was set, use our RouterID
 	if p.routeReflectorClient && p.clusterID == 0 {
 		p.clusterID = c.RouterID
 	}
 
-	p.fsms = append(p.fsms, NewActiveFSM2(p))
-
 	caps := make(packet.Capabilities, 0)
 
 	addPathEnabled, addPathCap := handleAddPathCapability(c)
@@ -143,7 +153,12 @@ func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error
 
 	caps = append(caps, asn4Capability(c))
 
-	if c.IPv6 {
+	if c.IPv6 != nil {
+		p.ipv6 = &familyParameters{
+			rib:          c.IPv6.RIB,
+			importFilter: filterOrDefault(c.IPv6.ImportFilter),
+			exportFilter: filterOrDefault(c.IPv6.ExportFilter),
+		}
 		caps = append(caps, multiProtocolCapability(packet.IPv6AFI))
 	}
 
@@ -152,6 +167,8 @@ func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error
 		Value: caps,
 	})
 
+	p.fsms = append(p.fsms, NewActiveFSM2(p))
+
 	return p, nil
 }
 
diff --git a/protocols/bgp/server/server.go b/protocols/bgp/server/server.go
index a27e522276e3fd61791c7f8926db87b2041d2d3f..df63a4a92ef954a6f5be941b0433b2264324135e 100644
--- a/protocols/bgp/server/server.go
+++ b/protocols/bgp/server/server.go
@@ -26,7 +26,7 @@ type bgpServer struct {
 type BGPServer interface {
 	RouterID() uint32
 	Start(*config.Global) error
-	AddPeer(config.Peer, *locRIB.LocRIB) error
+	AddPeer(config.Peer) error
 	GetPeerInfoAll() map[string]PeerInfo
 }
 
@@ -110,8 +110,8 @@ func (b *bgpServer) incomingConnectionWorker() {
 	}
 }
 
-func (b *bgpServer) AddPeer(c config.Peer, rib *locRIB.LocRIB) error {
-	peer, err := newPeer(c, rib, b)
+func (b *bgpServer) AddPeer(c config.Peer) error {
+	peer, err := newPeer(c, b)
 	if err != nil {
 		return err
 	}
diff --git a/protocols/bgp/server/server_test.go b/protocols/bgp/server/server_test.go
index fffe879ce078deaa77a6a795d357f64ef74e3796..896c3201b01799c3317a5f1e4e1ad1eed194f2bd 100644
--- a/protocols/bgp/server/server_test.go
+++ b/protocols/bgp/server/server_test.go
@@ -41,10 +41,13 @@ func TestBgpServerPeerSnapshot(t *testing.T) {
 		AddPathSend: routingtable.ClientOptions{
 			MaxPaths: 10,
 		},
-		ImportFilter: filter.NewDrainFilter(),
-		ExportFilter: filter.NewAcceptAllFilter(),
+		IPv4: &config.AddressFamilyConfig{
+			RIB:          rib,
+			ImportFilter: filter.NewDrainFilter(),
+			ExportFilter: filter.NewAcceptAllFilter(),
+		},
 	}
-	s.AddPeer(pc, rib)
+	s.AddPeer(pc)
 
 	info = s.GetPeerInfoAll()
 	if want, got := 1, len(info); want != got {
diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go
index 478e9a08b137bf306ddbab0453b42ad869636411..dd1c799985a6b6fb66b26381f26ebbfd4ab1c920 100644
--- a/protocols/bgp/server/update_sender.go
+++ b/protocols/bgp/server/update_sender.go
@@ -16,6 +16,8 @@ import (
 type UpdateSender struct {
 	routingtable.ClientManager
 	fsm       *FSM
+	afi       uint16
+	safi      uint8
 	iBGP      bool
 	rrClient  bool
 	toSendMu  sync.Mutex
@@ -28,9 +30,11 @@ type pathPfxs struct {
 	pfxs []bnet.Prefix
 }
 
-func newUpdateSender(fsm *FSM) *UpdateSender {
+func newUpdateSender(fsm *FSM, afi uint16, safi uint8) *UpdateSender {
 	return &UpdateSender{
 		fsm:       fsm,
+		afi:       afi,
+		safi:      safi,
 		iBGP:      fsm.peer.localASN == fsm.peer.peerASN,
 		rrClient:  fsm.peer.routeReflectorClient,
 		destroyCh: make(chan struct{}),
@@ -125,19 +129,27 @@ func (u *UpdateSender) sender(aggrTime time.Duration) {
 }
 
 func (u *UpdateSender) updateOverhead() int {
-	// TODO: for multi RIB support we need the AFI/SAFI combination to determine overhead. For now: MultiProtocol = IPv6
-	if u.fsm.options.SupportsMultiProtocol {
-		// since we are replacing the next hop attribute IPv4Len has to be subtracted, we also add another byte for extended length
-		return packet.AFILen + packet.SAFILen + 1 + packet.IPv6Len - packet.IPv4Len + 1
+	if !u.fsm.options.SupportsMultiProtocol {
+		return 0
 	}
 
-	return 0
+	addrLen := packet.IPv4AFI
+	if u.afi == packet.IPv6AFI {
+		addrLen = packet.IPv6Len
+	}
+
+	// since we are replacing the next hop attribute IPv4Len has to be subtracted, we also add another byte for extended length
+	return packet.AFILen + packet.SAFILen + 1 + addrLen - packet.IPv4Len + 1
 }
 
 func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefixes [][]bnet.Prefix, pathID uint32) {
 	var err error
 	for _, prefixes := range updatePrefixes {
 		update := u.updateMessageForPrefixes(prefixes, pathAttrs, pathID)
+		if update == nil {
+			log.Errorf("Failed to create update: Neighbor does not support multi protocol.")
+			return
+		}
 
 		err = serializeAndSendUpdate(u.fsm.con, update, u.fsm.options)
 		if err != nil {
@@ -147,11 +159,15 @@ func (u *UpdateSender) sendUpdates(pathAttrs *packet.PathAttribute, updatePrefix
 }
 
 func (u *UpdateSender) updateMessageForPrefixes(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
+	if u.afi == packet.IPv4AFI && u.safi == packet.UnicastSAFI {
+		return u.bgpUpdate(pfxs, pa, pathID)
+	}
+
 	if u.fsm.options.SupportsMultiProtocol {
 		return u.bgpUpdateMultiProtocol(pfxs, pa, pathID)
 	}
 
-	return u.bgpUpdate(pfxs, pa, pathID)
+	return nil
 }
 
 func (u *UpdateSender) bgpUpdate(pfxs []bnet.Prefix, pa *packet.PathAttribute, pathID uint32) *packet.BGPUpdate {
@@ -179,8 +195,8 @@ func (u *UpdateSender) bgpUpdateMultiProtocol(pfxs []bnet.Prefix, pa *packet.Pat
 	attrs := &packet.PathAttribute{
 		TypeCode: packet.MultiProtocolReachNLRICode,
 		Value: packet.MultiProtocolReachNLRI{
-			AFI:      packet.IPv6AFI,
-			SAFI:     packet.UnicastSAFI,
+			AFI:      u.afi,
+			SAFI:     u.safi,
 			NextHop:  nextHop,
 			Prefixes: pfxs,
 		},
@@ -224,7 +240,7 @@ func (u *UpdateSender) RemovePath(pfx bnet.Prefix, p *route.Path) bool {
 
 func (u *UpdateSender) withdrawPrefix(pfx bnet.Prefix, p *route.Path) error {
 	if u.fsm.options.SupportsMultiProtocol {
-		return withDrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx)
+		return withDrawPrefixesMultiProtocol(u.fsm.con, u.fsm.options, pfx, u.afi, u.safi)
 	}
 
 	return withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p)
diff --git a/protocols/bgp/server/update_sender_test.go b/protocols/bgp/server/update_sender_test.go
index 5c934fd2afd55d193e17df762faa8e53a0de9a2a..40719d6972a7b3f551d23823c4635666e5c59eac 100644
--- a/protocols/bgp/server/update_sender_test.go
+++ b/protocols/bgp/server/update_sender_test.go
@@ -5,6 +5,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+
 	"github.com/stretchr/testify/assert"
 
 	bnet "github.com/bio-routing/bio-rd/net"
@@ -22,10 +24,11 @@ func TestSender(t *testing.T) {
 		generateNLRIs   uint64
 		expectedUpdates [][]byte
 		addPath         bool
-		ipv6            bool
+		afi             uint16
 	}{
 		{
 			name: "Two paths with 3 NLRIs each",
+			afi:  packet.IPv4AFI,
 			paths: []pathPfxs{
 				{
 					path: &route.Path{
@@ -87,6 +90,7 @@ func TestSender(t *testing.T) {
 		},
 		{
 			name:    "Two paths with 3 NLRIs each with BGP Add Path",
+			afi:     packet.IPv4AFI,
 			addPath: true,
 			paths: []pathPfxs{
 				{
@@ -165,6 +169,7 @@ func TestSender(t *testing.T) {
 		},
 		{
 			name: "Overflow. Too many NLRIs.",
+			afi:  packet.IPv4AFI,
 			paths: []pathPfxs{
 				{
 					path: &route.Path{
@@ -332,6 +337,7 @@ func TestSender(t *testing.T) {
 		},
 		{
 			name: "Overflow with IPv6. Too many NLRIs.",
+			afi:  packet.IPv6AFI,
 			paths: []pathPfxs{
 				{
 					path: &route.Path{
@@ -857,18 +863,30 @@ func TestSender(t *testing.T) {
 					0x0, 0x40, 0x1, 0x1, 0x0, 0x40, 0x5, 0x4, 0x0, 0x0, 0x0, 0x64,
 				},
 			},
-			ipv6: true,
 		},
 	}
 
 	for _, test := range tests {
 		fsmA := newFSM2(&peer{
-			addr:         bnet.IPv4FromOctets(169, 254, 100, 100),
-			rib:          locRIB.New(),
-			importFilter: filter.NewAcceptAllFilter(),
-			exportFilter: filter.NewAcceptAllFilter(),
+			addr: bnet.IPv4FromOctets(169, 254, 100, 100),
 		})
 
+		rib := locRIB.New()
+		if test.afi == packet.IPv6AFI {
+			fsmA.options.SupportsMultiProtocol = true
+			fsmA.ipv6Unicast = newFSMAddressFamily(packet.IPv6AFI, packet.UnicastSAFI, &familyParameters{
+				rib:          rib,
+				importFilter: filter.NewAcceptAllFilter(),
+				exportFilter: filter.NewAcceptAllFilter(),
+			}, fsmA)
+		} else {
+			fsmA.ipv4Unicast = newFSMAddressFamily(packet.IPv4AFI, packet.UnicastSAFI, &familyParameters{
+				rib:          rib,
+				importFilter: filter.NewAcceptAllFilter(),
+				exportFilter: filter.NewAcceptAllFilter(),
+			}, fsmA)
+		}
+
 		fsmA.holdTimer = time.NewTimer(time.Second * 90)
 		fsmA.keepaliveTimer = time.NewTimer(time.Second * 30)
 		fsmA.connectRetryTimer = time.NewTimer(time.Second * 120)
@@ -881,11 +899,7 @@ func TestSender(t *testing.T) {
 			}
 		}
 
-		if test.ipv6 {
-			fsmA.options.SupportsMultiProtocol = true
-		}
-
-		updateSender := newUpdateSender(fsmA)
+		updateSender := newUpdateSender(fsmA, test.afi, packet.UnicastSAFI)
 
 		for _, pathPfx := range test.paths {
 			for _, pfx := range pathPfx.pfxs {
@@ -897,7 +911,7 @@ func TestSender(t *testing.T) {
 					y := i - x
 
 					var pfx bnet.Prefix
-					if test.ipv6 {
+					if test.afi == packet.IPv6AFI {
 						pfx = bnet.NewPfx(bnet.IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0), 48)
 					} else {
 						pfx = bnet.NewPfx(bnet.IPv4FromOctets(10, 0, uint8(x), uint8(y)), 32)
diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go
index 013f6ce33c5b247d6021292c3b611f3c325f24f5..2ab554b682691c57d0164d95eadef6afbcd14936 100644
--- a/protocols/bgp/server/withdraw.go
+++ b/protocols/bgp/server/withdraw.go
@@ -59,16 +59,17 @@ func withDrawPrefixesAddPath(out io.Writer, opt *types.Options, pfx net.Prefix,
 	return serializeAndSendUpdate(out, update, opt)
 }
 
-func withDrawPrefixesMultiProtocol(out io.Writer, opt *types.Options, pfx net.Prefix) error {
+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:      packet.IPv6AFI,
-				SAFI:     packet.UnicastSAFI,
+				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
index 393be032dcd178b2cef772f576b24e1c5b2d29e4..17cb8751b8673dab38920a55db79699f6c643fb4 100644
--- a/protocols/bgp/server/withdraw_test.go
+++ b/protocols/bgp/server/withdraw_test.go
@@ -1,16 +1,15 @@
 package server
 
 import (
-	"testing"
-
-	"github.com/bio-routing/bio-rd/protocols/bgp/types"
-
-	"errors"
-
 	"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"
 )
 
@@ -91,7 +90,7 @@ func TestWithDrawPrefixesMultiProtocol(t *testing.T) {
 			opt := &types.Options{
 				AddPathRX: false,
 			}
-			err := withDrawPrefixesMultiProtocol(buf, opt, test.Prefix)
+			err := withDrawPrefixesMultiProtocol(buf, opt, test.Prefix, packet.IPv6AFI, packet.UnicastSAFI)
 			if err != nil {
 				t.Fatalf("unexpected error: %v", err)
 			}
diff --git a/routingtable/filter/BUILD.bazel b/routingtable/filter/BUILD.bazel
index 4ac13572ad6e4ec9364449e209fa641a2f7c8125..17bee85a8312740e989ae1f88fcf0150458437fa 100644
--- a/routingtable/filter/BUILD.bazel
+++ b/routingtable/filter/BUILD.bazel
@@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 go_library(
     name = "go_default_library",
     srcs = [
+        "action.go",
         "community_filter.go",
         "filter.go",
         "helper.go",
diff --git a/routingtable/filter/action.go b/routingtable/filter/action.go
new file mode 100644
index 0000000000000000000000000000000000000000..888ad3f5397f51337cfb04b487883956da107f80
--- /dev/null
+++ b/routingtable/filter/action.go
@@ -0,0 +1,12 @@
+package filter
+
+import (
+	"github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/route"
+	"github.com/bio-routing/bio-rd/routingtable/filter/actions"
+)
+
+// Action performs actions on a `route.Path`
+type Action interface {
+	Do(p net.Prefix, pa *route.Path) actions.Result
+}
diff --git a/routingtable/filter/actions/BUILD.bazel b/routingtable/filter/actions/BUILD.bazel
index b4195842fcc40a6d484111cb9aed065d02e1b604..d974ffd1c11bf232f50458e3a5c3a96ed8a08fbd 100644
--- a/routingtable/filter/actions/BUILD.bazel
+++ b/routingtable/filter/actions/BUILD.bazel
@@ -4,10 +4,10 @@ go_library(
     name = "go_default_library",
     srcs = [
         "accept_action.go",
+        "action.go",
         "add_community_action.go",
         "add_large_community_action.go",
         "as_path_prepend_action.go",
-        "filter_action.go",
         "reject_action.go",
         "set_local_pref_action.go",
         "set_nexthop_action.go",
diff --git a/routingtable/filter/actions/accept_action.go b/routingtable/filter/actions/accept_action.go
index 6c0cc2acbfa3d75603ff33cc10025bdad09b3a23..6e340d5394a178066b0b7dfa45c60e46590de3ff 100644
--- a/routingtable/filter/actions/accept_action.go
+++ b/routingtable/filter/actions/accept_action.go
@@ -8,6 +8,9 @@ import (
 type AcceptAction struct {
 }
 
-func (*AcceptAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
-	return pa, false
+func (*AcceptAction) Do(p net.Prefix, pa *route.Path) Result {
+	return Result{
+		Path:      pa,
+		Terminate: true,
+	}
 }
diff --git a/routingtable/filter/actions/action.go b/routingtable/filter/actions/action.go
new file mode 100644
index 0000000000000000000000000000000000000000..3cd6f0ac746ae168eb5c6fa8c6eb7f7d2fea9a75
--- /dev/null
+++ b/routingtable/filter/actions/action.go
@@ -0,0 +1,11 @@
+package actions
+
+import (
+	"github.com/bio-routing/bio-rd/route"
+)
+
+type Result struct {
+	Path      *route.Path
+	Reject    bool
+	Terminate bool
+}
diff --git a/routingtable/filter/actions/add_community_action.go b/routingtable/filter/actions/add_community_action.go
index c24107e8b94be6657174fbd3dffb06ae978ff767..4931056afc9bc901bef92b502e67775ee4e61d5c 100644
--- a/routingtable/filter/actions/add_community_action.go
+++ b/routingtable/filter/actions/add_community_action.go
@@ -15,9 +15,9 @@ func NewAddCommunityAction(coms []uint32) *AddCommunityAction {
 	}
 }
 
-func (a *AddCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
+func (a *AddCommunityAction) Do(p net.Prefix, pa *route.Path) Result {
 	if pa.BGPPath == nil || len(a.communities) == 0 {
-		return pa, false
+		return Result{Path: pa}
 	}
 
 	modified := pa.Copy()
@@ -26,5 +26,5 @@ func (a *AddCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Pa
 		modified.BGPPath.Communities = append(modified.BGPPath.Communities, com)
 	}
 
-	return modified, false
+	return Result{Path: modified}
 }
diff --git a/routingtable/filter/actions/add_community_action_test.go b/routingtable/filter/actions/add_community_action_test.go
index 9ec3d1401b87da808c324a984e1d5ef7620658cd..3527437b9efab61df4bd5203848318bbbf87849d 100644
--- a/routingtable/filter/actions/add_community_action_test.go
+++ b/routingtable/filter/actions/add_community_action_test.go
@@ -53,9 +53,9 @@ func TestAddingCommunities(t *testing.T) {
 			}
 
 			a := NewAddCommunityAction(test.communities)
-			modPath, _ := a.Do(net.Prefix{}, p)
+			res := a.Do(net.Prefix{}, p)
 
-			assert.Equal(t, test.expected, modPath.BGPPath.CommunitiesString())
+			assert.Equal(t, test.expected, res.Path.BGPPath.CommunitiesString())
 		})
 	}
 }
diff --git a/routingtable/filter/actions/add_large_community_action.go b/routingtable/filter/actions/add_large_community_action.go
index cb5f1a020ea6bfdde33948cc8535077df0fec26d..9e0a9da44955a1779ce8655551d09a3a31f077d4 100644
--- a/routingtable/filter/actions/add_large_community_action.go
+++ b/routingtable/filter/actions/add_large_community_action.go
@@ -16,13 +16,13 @@ func NewAddLargeCommunityAction(coms []types.LargeCommunity) *AddLargeCommunityA
 	}
 }
 
-func (a *AddLargeCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
+func (a *AddLargeCommunityAction) Do(p net.Prefix, pa *route.Path) Result {
 	if pa.BGPPath == nil || len(a.communities) == 0 {
-		return pa, false
+		return Result{Path: pa}
 	}
 
 	modified := pa.Copy()
 	modified.BGPPath.LargeCommunities = append(modified.BGPPath.LargeCommunities, a.communities...)
 
-	return modified, false
+	return Result{Path: modified}
 }
diff --git a/routingtable/filter/actions/add_large_community_action_test.go b/routingtable/filter/actions/add_large_community_action_test.go
index 6b569d2ebd79e7225791b0172c2ad32fab764d88..fe077817e5098a9966efc11a7d7d03e905212460 100644
--- a/routingtable/filter/actions/add_large_community_action_test.go
+++ b/routingtable/filter/actions/add_large_community_action_test.go
@@ -71,7 +71,7 @@ func TestAddingLargeCommunities(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
+		t.Run(test.name, func(t *testing.T) {
 			p := &route.Path{
 				BGPPath: &route.BGPPath{
 					LargeCommunities: test.current,
@@ -79,9 +79,9 @@ func TestAddingLargeCommunities(t *testing.T) {
 			}
 
 			a := NewAddLargeCommunityAction(test.communities)
-			modPath, _ := a.Do(net.Prefix{}, p)
+			res := a.Do(net.Prefix{}, p)
 
-			assert.Equal(te, test.expected, modPath.BGPPath.LargeCommunitiesString())
+			assert.Equal(t, test.expected, res.Path.BGPPath.LargeCommunitiesString())
 		})
 	}
 }
diff --git a/routingtable/filter/actions/as_path_prepend_action.go b/routingtable/filter/actions/as_path_prepend_action.go
index 30d63ca783fd91a9530f336f3311106c57ac5968..2e53b1e84526aec2e092b3fea3bb1056dc360f2a 100644
--- a/routingtable/filter/actions/as_path_prepend_action.go
+++ b/routingtable/filter/actions/as_path_prepend_action.go
@@ -17,13 +17,13 @@ func NewASPathPrependAction(asn uint32, times uint16) *ASPathPrependAction {
 	}
 }
 
-func (a *ASPathPrependAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
+func (a *ASPathPrependAction) Do(p net.Prefix, pa *route.Path) Result {
 	if pa.BGPPath == nil {
-		return pa, false
+		return Result{Path: pa}
 	}
 
 	modified := pa.Copy()
 	modified.BGPPath.Prepend(a.asn, a.times)
 
-	return modified, false
+	return Result{Path: modified}
 }
diff --git a/routingtable/filter/actions/as_path_prepend_action_test.go b/routingtable/filter/actions/as_path_prepend_action_test.go
index f7c7af59394670a51c6062d43df19ae45ec69eb2..12cc0267f22b9a2bbe32f334c0aeb4f3f48bcf30 100644
--- a/routingtable/filter/actions/as_path_prepend_action_test.go
+++ b/routingtable/filter/actions/as_path_prepend_action_test.go
@@ -54,9 +54,9 @@ func TestAppendPath(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
+		t.Run(test.name, func(t *testing.T) {
 			a := NewASPathPrependAction(12345, test.times)
-			p, _ := a.Do(bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{
+			res := a.Do(bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{
 				BGPPath: test.bgpPath,
 			})
 
@@ -64,8 +64,8 @@ func TestAppendPath(t *testing.T) {
 				return
 			}
 
-			assert.Equal(te, test.expectedPath, p.BGPPath.ASPath.String(), "ASPath")
-			assert.Equal(te, test.expectedLength, p.BGPPath.ASPathLen, "ASPathLen")
+			assert.Equal(t, test.expectedPath, res.Path.BGPPath.ASPath.String(), "ASPath")
+			assert.Equal(t, test.expectedLength, res.Path.BGPPath.ASPathLen, "ASPathLen")
 		})
 	}
 }
diff --git a/routingtable/filter/actions/filter_action.go b/routingtable/filter/actions/filter_action.go
deleted file mode 100644
index 7a2665146922719766b880f9093eb1049c6ae018..0000000000000000000000000000000000000000
--- a/routingtable/filter/actions/filter_action.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package actions
-
-import (
-	"github.com/bio-routing/bio-rd/net"
-	"github.com/bio-routing/bio-rd/route"
-)
-
-type FilterAction interface {
-	Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool)
-}
diff --git a/routingtable/filter/actions/reject_action.go b/routingtable/filter/actions/reject_action.go
index d740139626b99395db71a3b2e7c2cea1d04ebacf..4693bbfaad45aa8f0b83e914733834a6a52a99f8 100644
--- a/routingtable/filter/actions/reject_action.go
+++ b/routingtable/filter/actions/reject_action.go
@@ -8,6 +8,10 @@ import (
 type RejectAction struct {
 }
 
-func (*RejectAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
-	return pa, true
+func (*RejectAction) Do(p net.Prefix, pa *route.Path) Result {
+	return Result{
+		Path:      pa,
+		Reject:    true,
+		Terminate: true,
+	}
 }
diff --git a/routingtable/filter/actions/set_local_pref_action.go b/routingtable/filter/actions/set_local_pref_action.go
index e60b6faa0ab5e4e986f785d7b8322db2c6492e34..26d038ec0ad0f0f4d110bb1fdb9bfc3e08c67458 100644
--- a/routingtable/filter/actions/set_local_pref_action.go
+++ b/routingtable/filter/actions/set_local_pref_action.go
@@ -15,13 +15,13 @@ func NewSetLocalPrefAction(pref uint32) *SetLocalPrefAction {
 	}
 }
 
-func (a *SetLocalPrefAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
+func (a *SetLocalPrefAction) Do(p net.Prefix, pa *route.Path) Result {
 	if pa.BGPPath == nil {
-		return pa, false
+		return Result{Path: pa}
 	}
 
 	modified := *pa
 	modified.BGPPath.LocalPref = a.pref
 
-	return &modified, false
+	return Result{Path: &modified}
 }
diff --git a/routingtable/filter/actions/set_local_pref_action_test.go b/routingtable/filter/actions/set_local_pref_action_test.go
index 7b104d3949d127e794dcd34a2a05a7b7aa3f2b02..40ef0f6b3e649c6dc2cf45d3ba6b31fd6b8862b0 100644
--- a/routingtable/filter/actions/set_local_pref_action_test.go
+++ b/routingtable/filter/actions/set_local_pref_action_test.go
@@ -27,14 +27,14 @@ func TestSetLocalPref(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
+		t.Run(test.name, func(t *testing.T) {
 			a := NewSetLocalPrefAction(150)
-			p, _ := a.Do(bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{
+			res := a.Do(bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{
 				BGPPath: test.bgpPath,
 			})
 
 			if test.expectedLocalPref > 0 {
-				assert.Equal(te, test.expectedLocalPref, p.BGPPath.LocalPref)
+				assert.Equal(t, test.expectedLocalPref, res.Path.BGPPath.LocalPref)
 			}
 		})
 	}
diff --git a/routingtable/filter/actions/set_nexthop_action.go b/routingtable/filter/actions/set_nexthop_action.go
index de1e9bc959b8f9d827c48c844699aa2aed92fda9..8b00a8717744611d2bd17661b201973b727026bb 100644
--- a/routingtable/filter/actions/set_nexthop_action.go
+++ b/routingtable/filter/actions/set_nexthop_action.go
@@ -16,13 +16,13 @@ func NewSetNextHopAction(ip bnet.IP) *SetNextHopAction {
 	}
 }
 
-func (a *SetNextHopAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
+func (a *SetNextHopAction) Do(p net.Prefix, pa *route.Path) Result {
 	if pa.BGPPath == nil {
-		return pa, false
+		return Result{Path: pa}
 	}
 
 	modified := pa.Copy()
 	modified.BGPPath.NextHop = a.ip
 
-	return modified, false
+	return Result{Path: modified}
 }
diff --git a/routingtable/filter/actions/set_nexthop_action_test.go b/routingtable/filter/actions/set_nexthop_action_test.go
index 7f691d235f288e1155980ea997b4e6fddaa6a4de..bebbafebbd6fec029e7a74dad4b4fda9e2a50f29 100644
--- a/routingtable/filter/actions/set_nexthop_action_test.go
+++ b/routingtable/filter/actions/set_nexthop_action_test.go
@@ -31,12 +31,12 @@ func TestSetNextHopTest(t *testing.T) {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			a := NewSetNextHopAction(bnet.IPv4FromOctets(100, 64, 2, 1))
-			p, _ := a.Do(net.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{
+			res := a.Do(net.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8), &route.Path{
 				BGPPath: test.bgpPath,
 			})
 
 			if test.bgpPath != nil {
-				assert.Equal(t, test.expected, p.BGPPath.NextHop)
+				assert.Equal(t, test.expected, res.Path.BGPPath.NextHop)
 			}
 		})
 	}
diff --git a/routingtable/filter/filter.go b/routingtable/filter/filter.go
index cb88087983c31ebb2a5b58bf7a002117c829efb7..27f3b1f4fc69b4c879a8268408f4539add3183cd 100644
--- a/routingtable/filter/filter.go
+++ b/routingtable/filter/filter.go
@@ -21,10 +21,11 @@ func (f *Filter) ProcessTerms(p net.Prefix, pa *route.Path) (modPath *route.Path
 	modPath = pa
 
 	for _, t := range f.terms {
-		modPath, reject = t.Process(p, modPath)
-		if reject {
-			return modPath, true
+		res := t.Process(p, modPath)
+		if res.Terminate {
+			return res.Path, res.Reject
 		}
+		modPath = res.Path
 	}
 
 	return modPath, false
diff --git a/routingtable/filter/filter_test.go b/routingtable/filter/filter_test.go
index 52ebc8b9180aab9429b22489d75db95e210d2f65..0eeb4942df0938d3445a4df6598ac523fa2d577f 100644
--- a/routingtable/filter/filter_test.go
+++ b/routingtable/filter/filter_test.go
@@ -23,7 +23,7 @@ func TestProcessTerms(t *testing.T) {
 			prefix: net.NewPfx(net.IPv4(0), 0),
 			path:   &route.Path{},
 			term: &Term{
-				then: []FilterAction{
+				then: []Action{
 					&actions.AcceptAction{},
 				},
 			},
@@ -35,19 +35,32 @@ func TestProcessTerms(t *testing.T) {
 			prefix: net.NewPfx(net.IPv4(0), 0),
 			path:   &route.Path{},
 			term: &Term{
-				then: []FilterAction{
+				then: []Action{
 					&actions.RejectAction{},
 				},
 			},
 			exptectAccept:  false,
 			expectModified: false,
 		},
+		{
+			name:   "accept before reject",
+			prefix: net.NewPfx(net.IPv4(0), 0),
+			path:   &route.Path{},
+			term: &Term{
+				then: []Action{
+					&actions.AcceptAction{},
+					&actions.RejectAction{},
+				},
+			},
+			exptectAccept:  true,
+			expectModified: false,
+		},
 		{
 			name:   "modified",
 			prefix: net.NewPfx(net.IPv4(0), 0),
 			path:   &route.Path{},
 			term: &Term{
-				then: []FilterAction{
+				then: []Action{
 					&mockAction{},
 					&actions.AcceptAction{},
 				},
@@ -58,7 +71,7 @@ func TestProcessTerms(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
+		t.Run(test.name, func(t *testing.T) {
 			f := NewFilter([]*Term{test.term})
 			p, reject := f.ProcessTerms(test.prefix, test.path)
 
diff --git a/routingtable/filter/helper.go b/routingtable/filter/helper.go
index 17a7d73789d5be569bec1725592a35f1b3841386..1cfb6e5c8be7ea13e3e76a8609b8307b48e665e9 100644
--- a/routingtable/filter/helper.go
+++ b/routingtable/filter/helper.go
@@ -10,7 +10,7 @@ func NewAcceptAllFilter() *Filter {
 		[]*Term{
 			NewTerm(
 				[]*TermCondition{},
-				[]FilterAction{
+				[]Action{
 					&actions.AcceptAction{},
 				}),
 		})
@@ -22,7 +22,7 @@ func NewDrainFilter() *Filter {
 		[]*Term{
 			NewTerm(
 				[]*TermCondition{},
-				[]FilterAction{
+				[]Action{
 					&actions.RejectAction{},
 				}),
 		})
diff --git a/routingtable/filter/term.go b/routingtable/filter/term.go
index d644acba4a977e67e757fc5c7f71863531427e12..cdf56c7fa90eff0a0f844377a27985900235231d 100644
--- a/routingtable/filter/term.go
+++ b/routingtable/filter/term.go
@@ -5,18 +5,20 @@ import (
 	"github.com/bio-routing/bio-rd/route"
 )
 
-type FilterAction interface {
-	Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool)
-}
-
 // Term matches a path against a list of conditions and performs actions if it matches
 type Term struct {
 	from []*TermCondition
-	then []FilterAction
+	then []Action
+}
+
+type TermResult struct {
+	Path      *route.Path
+	Terminate bool
+	Reject    bool
 }
 
 // NewTerm creates a new term
-func NewTerm(from []*TermCondition, then []FilterAction) *Term {
+func NewTerm(from []*TermCondition, then []Action) *Term {
 	t := &Term{
 		from: from,
 		then: then,
@@ -26,7 +28,7 @@ func NewTerm(from []*TermCondition, then []FilterAction) *Term {
 }
 
 // Process processes a path returning if the path should be rejected and returns a possible modified version of the path
-func (t *Term) Process(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
+func (t *Term) Process(p net.Prefix, pa *route.Path) TermResult {
 	orig := pa
 
 	if len(t.from) == 0 {
@@ -39,18 +41,23 @@ func (t *Term) Process(p net.Prefix, pa *route.Path) (modPath *route.Path, rejec
 		}
 	}
 
-	return orig, false
+	return TermResult{Path: orig}
 }
 
-func (t *Term) processActions(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
-	modPath = pa
+func (t *Term) processActions(p net.Prefix, pa *route.Path) TermResult {
+	modPath := pa
 
 	for _, action := range t.then {
-		modPath, reject = action.Do(p, modPath)
-		if reject {
-			continue
+		res := action.Do(p, modPath)
+		if res.Terminate {
+			return TermResult{
+				Path:      modPath,
+				Terminate: true,
+				Reject:    res.Reject,
+			}
 		}
+		modPath = res.Path
 	}
 
-	return modPath, reject
+	return TermResult{Path: modPath}
 }
diff --git a/routingtable/filter/term_test.go b/routingtable/filter/term_test.go
index 876eb6691ec33a0a95697267f171ace3307d8a83..591fd234befde8d15538dd7cf293a9922479560d 100644
--- a/routingtable/filter/term_test.go
+++ b/routingtable/filter/term_test.go
@@ -12,11 +12,11 @@ import (
 type mockAction struct {
 }
 
-func (*mockAction) Do(p net.Prefix, pa *route.Path) (*route.Path, bool) {
+func (*mockAction) Do(p net.Prefix, pa *route.Path) actions.Result {
 	cp := *pa
 	cp.Type = route.OSPFPathType
 
-	return &cp, false
+	return actions.Result{Path: &cp}
 }
 
 func TestProcess(t *testing.T) {
@@ -25,7 +25,7 @@ func TestProcess(t *testing.T) {
 		prefix         net.Prefix
 		path           *route.Path
 		from           []*TermCondition
-		then           []FilterAction
+		then           []Action
 		expectReject   bool
 		expectModified bool
 	}{
@@ -34,7 +34,7 @@ func TestProcess(t *testing.T) {
 			prefix: net.NewPfx(net.IPv4FromOctets(100, 64, 0, 1), 8),
 			path:   &route.Path{},
 			from:   []*TermCondition{},
-			then: []FilterAction{
+			then: []Action{
 				&actions.AcceptAction{},
 			},
 			expectReject:   false,
@@ -48,7 +48,7 @@ func TestProcess(t *testing.T) {
 				NewTermConditionWithPrefixLists(
 					NewPrefixList(net.NewPfx(net.IPv4FromOctets(100, 64, 0, 1), 8))),
 			},
-			then: []FilterAction{
+			then: []Action{
 				&actions.AcceptAction{},
 			},
 			expectReject:   false,
@@ -62,7 +62,7 @@ func TestProcess(t *testing.T) {
 				NewTermConditionWithPrefixLists(
 					NewPrefixList(net.NewPfx(net.IPv4(0), 32))),
 			},
-			then: []FilterAction{
+			then: []Action{
 				&actions.AcceptAction{},
 			},
 			expectReject:   false,
@@ -76,7 +76,7 @@ func TestProcess(t *testing.T) {
 				NewTermConditionWithPrefixLists(
 					NewPrefixList(net.NewPfx(net.IPv4FromOctets(100, 64, 0, 1), 8))),
 			},
-			then: []FilterAction{
+			then: []Action{
 				&mockAction{},
 			},
 			expectReject:   false,
@@ -90,7 +90,7 @@ func TestProcess(t *testing.T) {
 				NewTermConditionWithRouteFilters(
 					NewRouteFilter(net.NewPfx(net.IPv4FromOctets(100, 64, 0, 1), 8), Exact())),
 			},
-			then: []FilterAction{
+			then: []Action{
 				&mockAction{},
 				&actions.AcceptAction{},
 			},
@@ -109,7 +109,7 @@ func TestProcess(t *testing.T) {
 					},
 				},
 			},
-			then: []FilterAction{
+			then: []Action{
 				&actions.AcceptAction{},
 			},
 			expectReject:   false,
@@ -118,18 +118,18 @@ func TestProcess(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
+		t.Run(test.name, func(t *testing.T) {
 			term := NewTerm(test.from, test.then)
 
-			pa, reject := term.Process(test.prefix, test.path)
-			assert.Equal(te, test.expectReject, reject, "reject")
+			res := term.Process(test.prefix, test.path)
+			assert.Equal(t, test.expectReject, res.Reject, "reject")
 
-			if pa != test.path && !test.expectModified {
-				te.Fatal("expected path to be not modified but was")
+			if res.Path != test.path && !test.expectModified {
+				t.Fatal("expected path to be not modified but was")
 			}
 
-			if pa == test.path && test.expectModified {
-				te.Fatal("expected path to be modified but was same reference")
+			if res.Path == test.path && test.expectModified {
+				t.Fatal("expected path to be modified but was same reference")
 			}
 		})
 	}