Repository: metallb/metallb
Pull request #1717 (2022-11-21 11:33:57)

Add BGPPeer VRF support
Here we add a VRF optional field to the BGPPeer to indicate the session we want to establish with the given peer must happen from an interface belonging to a linux VRF


Patch:
diff --git a/api/v1beta2/bgppeer_types.go b/api/v1beta2/bgppeer_types.go
index 284828de..9863736f 100644
--- a/api/v1beta2/bgppeer_types.go
+++ b/api/v1beta2/bgppeer_types.go
@@ -82,6 +82,11 @@ type BGPPeerSpec struct {
 	// To set if the BGPPeer is multi-hops away. Needed for FRR mode only.
 	// +optional
 	EBGPMultiHop bool `json:"ebgpMultiHop,omitempty"`
+
+	// To set if we want to peer with the BGPPeer using an interface belonging to
+	// a host vrf
+	// +optional
+	VRFName string `json:"vrf,omitempty"`
 	// Add future BGP configuration here
 }
 
diff --git a/charts/metallb/charts/crds/templates/crds.yaml b/charts/metallb/charts/crds/templates/crds.yaml
index 81447877..9b415acf 100644
--- a/charts/metallb/charts/crds/templates/crds.yaml
+++ b/charts/metallb/charts/crds/templates/crds.yaml
@@ -772,6 +772,10 @@ spec:
               sourceAddress:
                 description: Source address to use when establishing the session.
                 type: string
+              vrf:
+                description: To set if we want to peer with the BGPPeer using an interface
+                  belonging to a host vrf
+                type: string
             required:
             - myASN
             - peerASN
diff --git a/config/crd/bases/metallb.io_bgppeers.yaml b/config/crd/bases/metallb.io_bgppeers.yaml
index 2fb54dce..be0d0ca6 100644
--- a/config/crd/bases/metallb.io_bgppeers.yaml
+++ b/config/crd/bases/metallb.io_bgppeers.yaml
@@ -277,6 +277,10 @@ spec:
               sourceAddress:
                 description: Source address to use when establishing the session.
                 type: string
+              vrf:
+                description: To set if we want to peer with the BGPPeer using an interface
+                  belonging to a host vrf
+                type: string
             required:
             - myASN
             - peerASN
diff --git a/config/manifests/metallb-frr-prometheus.yaml b/config/manifests/metallb-frr-prometheus.yaml
index 3799d6af..69247195 100644
--- a/config/manifests/metallb-frr-prometheus.yaml
+++ b/config/manifests/metallb-frr-prometheus.yaml
@@ -832,6 +832,10 @@ spec:
               sourceAddress:
                 description: Source address to use when establishing the session.
                 type: string
+              vrf:
+                description: To set if we want to peer with the BGPPeer using an interface
+                  belonging to a host vrf
+                type: string
             required:
             - myASN
             - peerASN
diff --git a/config/manifests/metallb-frr.yaml b/config/manifests/metallb-frr.yaml
index 059a82bd..7f999eaf 100644
--- a/config/manifests/metallb-frr.yaml
+++ b/config/manifests/metallb-frr.yaml
@@ -832,6 +832,10 @@ spec:
               sourceAddress:
                 description: Source address to use when establishing the session.
                 type: string
+              vrf:
+                description: To set if we want to peer with the BGPPeer using an interface
+                  belonging to a host vrf
+                type: string
             required:
             - myASN
             - peerASN
diff --git a/config/manifests/metallb-native-prometheus.yaml b/config/manifests/metallb-native-prometheus.yaml
index cd255bbc..d0ddabe4 100644
--- a/config/manifests/metallb-native-prometheus.yaml
+++ b/config/manifests/metallb-native-prometheus.yaml
@@ -832,6 +832,10 @@ spec:
               sourceAddress:
                 description: Source address to use when establishing the session.
                 type: string
+              vrf:
+                description: To set if we want to peer with the BGPPeer using an interface
+                  belonging to a host vrf
+                type: string
             required:
             - myASN
             - peerASN
diff --git a/config/manifests/metallb-native.yaml b/config/manifests/metallb-native.yaml
index 9f6585d8..ca9e50db 100644
--- a/config/manifests/metallb-native.yaml
+++ b/config/manifests/metallb-native.yaml
@@ -832,6 +832,10 @@ spec:
               sourceAddress:
                 description: Source address to use when establishing the session.
                 type: string
+              vrf:
+                description: To set if we want to peer with the BGPPeer using an interface
+                  belonging to a host vrf
+                type: string
             required:
             - myASN
             - peerASN
diff --git a/internal/bgp/bgp.go b/internal/bgp/bgp.go
index 5a64f9bb..24e58647 100644
--- a/internal/bgp/bgp.go
+++ b/internal/bgp/bgp.go
@@ -60,7 +60,22 @@ type Session interface {
 	Set(advs ...*Advertisement) error
 }
 
+type SessionParameters struct {
+	PeerAddress   string
+	SourceAddress net.IP
+	MyASN         uint32
+	RouterID      net.IP
+	PeerASN       uint32
+	HoldTime      time.Duration
+	KeepAliveTime time.Duration
+	Password      string
+	CurrentNode   string
+	BFDProfile    string
+	EBGPMultiHop  bool
+	VRFName       string
+	SessionName   string
+}
 type SessionManager interface {
-	NewSession(logger log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, hold, keepalive time.Duration, password, myNode, bfdProfile string, ebgpMultiHop bool, name string) (Session, error)
+	NewSession(logger log.Logger, args SessionParameters) (Session, error)
 	SyncBFDProfiles(profiles map[string]*config.BFDProfile) error
 }
diff --git a/internal/bgp/frr/config.go b/internal/bgp/frr/config.go
index 0da402d1..eb983a7d 100644
--- a/internal/bgp/frr/config.go
+++ b/internal/bgp/frr/config.go
@@ -74,6 +74,14 @@ type neighborConfig struct {
 	Advertisements []*advertisementConfig
 	BFDProfile     string
 	EBGPMultiHop   bool
+	VRFName        string
+}
+
+func (n *neighborConfig) ID() string {
+	if n.VRFName == "" {
+		return n.Addr
+	}
+	return fmt.Sprintf("%s-%s", n.Addr, n.VRFName)
 }
 
 type advertisementConfig struct {
@@ -85,14 +93,14 @@ type advertisementConfig struct {
 
 // routerName() defines the format of the key of the "Routers" map in the
 // frrConfig struct.
-func routerName(srcAddr string, myASN uint32) string {
-	return fmt.Sprintf("%d@%s", myASN, srcAddr)
+func routerName(srcAddr string, myASN uint32, vrfName string) string {
+	return fmt.Sprintf("%d@%s@%s", myASN, srcAddr, vrfName)
 }
 
 // neighborName() defines the format of key of the 'Neighbors' map in the
 // routerConfig struct.
-func neighborName(peerAddr string, ASN uint32) string {
-	return fmt.Sprintf("%d@%s", ASN, peerAddr)
+func neighborName(peerAddr string, ASN uint32, vrfName string) string {
+	return fmt.Sprintf("%d@%s@%s", ASN, peerAddr, vrfName)
 }
 
 // templateConfig uses the template library to template
@@ -117,13 +125,13 @@ func templateConfig(data interface{}) (string, error) {
 				return "ip"
 			},
 			"localPrefPrefixList": func(neighbor *neighborConfig, localPreference uint32) string {
-				return fmt.Sprintf("%s-%d-%s-localpref-prefixes", neighbor.Addr, localPreference, neighbor.IPFamily)
+				return fmt.Sprintf("%s-%d-%s-localpref-prefixes", neighbor.ID(), localPreference, neighbor.IPFamily)
 			},
 			"communityPrefixList": func(neighbor *neighborConfig, community string) string {
-				return fmt.Sprintf("%s-%s-%s-community-prefixes", neighbor.Addr, community, neighbor.IPFamily)
+				return fmt.Sprintf("%s-%s-%s-community-prefixes", neighbor.ID(), community, neighbor.IPFamily)
 			},
 			"allowedPrefixList": func(neighbor *neighborConfig) string {
-				return fmt.Sprintf("%s-pl-%s", neighbor.Addr, neighbor.IPFamily)
+				return fmt.Sprintf("%s-pl-%s", neighbor.ID(), neighbor.IPFamily)
 			},
 			"mustDisableConnectedCheck": func(ipFamily ipfamily.Family, myASN, asn uint32, eBGPMultiHop bool) bool {
 				// return true only for IPv6 eBGP sessions
diff --git a/internal/bgp/frr/frr.go b/internal/bgp/frr/frr.go
index 465bd787..d80347d1 100644
--- a/internal/bgp/frr/frr.go
+++ b/internal/bgp/frr/frr.go
@@ -32,21 +32,10 @@ type sessionManager struct {
 }
 
 type session struct {
-	name           string
-	myASN          uint32
-	routerID       net.IP // May be nil, meaning "derive from context"
-	myNode         string
-	addr           string
-	srcAddr        net.IP
-	asn            uint32
-	holdTime       time.Duration
-	keepaliveTime  time.Duration
-	logger         log.Logger
-	password       string
-	advertised     []*bgp.Advertisement
-	bfdProfile     string
-	ebgpMultiHop   bool
+	bgp.SessionParameters
 	sessionManager *sessionManager
+	advertised     []*bgp.Advertisement
+	logger         log.Logger
 }
 
 // Create a variable for os.Hostname() in order to make it easy to mock out
@@ -69,9 +58,9 @@ func validate(adv *bgp.Advertisement) error {
 func (s *session) Set(advs ...*bgp.Advertisement) error {
 	s.sessionManager.Lock()
 	defer s.sessionManager.Unlock()
-	sessionName := sessionName(s.srcAddr.String(), s.myASN, s.addr, s.asn)
+	sessionName := sessionName(s.SourceAddress.String(), s.MyASN, s.PeerAddress, s.PeerASN)
 	if _, found := s.sessionManager.sessions[sessionName]; !found {
-		return fmt.Errorf("session not established before advertisement")
+		return fmt.Errorf("session %s not established before advertisement", sessionName)
 	}
 
 	newAdvs := []*bgp.Advertisement{}
@@ -118,25 +107,14 @@ func (s *session) Close() error {
 //
 // The session will immediately try to connect and synchronize its
 // local state with the peer.
-func (sm *sessionManager) NewSession(l log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, holdTime, keepaliveTime time.Duration, password, myNode, bfdProfile string, ebgpMultiHop bool, name string) (bgp.Session, error) {
+func (sm *sessionManager) NewSession(l log.Logger, args bgp.SessionParameters) (bgp.Session, error) {
 	sm.Lock()
 	defer sm.Unlock()
 	s := &session{
-		name:           name,
-		myASN:          myASN,
-		routerID:       routerID,
-		myNode:         myNode,
-		addr:           addr,
-		srcAddr:        srcAddr,
-		asn:            asn,
-		holdTime:       holdTime,
-		keepaliveTime:  keepaliveTime,
-		logger:         log.With(l, "peer", addr, "localASN", myASN, "peerASN", asn),
-		password:       password,
-		advertised:     []*bgp.Advertisement{},
-		sessionManager: sm,
-		bfdProfile:     bfdProfile,
-		ebgpMultiHop:   ebgpMultiHop,
+		logger:            log.With(l, "peer", args.PeerAddress, "localASN", args.MyASN, "peerASN", args.PeerASN),
+		advertised:        []*bgp.Advertisement{},
+		sessionManager:    sm,
+		SessionParameters: args,
 	}
 
 	_ = sm.addSession(s)
@@ -155,7 +133,7 @@ func (sm *sessionManager) addSession(s *session) error {
 	if s == nil {
 		return fmt.Errorf("invalid session")
 	}
-	sessionName := sessionName(s.srcAddr.String(), s.myASN, s.addr, s.asn)
+	sessionName := sessionName(s.SourceAddress.String(), s.MyASN, s.PeerAddress, s.PeerASN)
 	sm.sessions[sessionName] = s
 
 	return nil
@@ -165,7 +143,7 @@ func (sm *sessionManager) deleteSession(s *session) error {
 	if s == nil {
 		return fmt.Errorf("invalid session")
 	}
-	sessionName := sessionName(s.srcAddr.String(), s.myASN, s.addr, s.asn)
+	sessionName := sessionName(s.SourceAddress.String(), s.MyASN, s.PeerAddress, s.PeerASN)
 	delete(sm.sessions, sessionName)
 
 	return nil
@@ -226,23 +204,24 @@ func (sm *sessionManager) createConfig() (*frrConfig, error) {
 		var exist bool
 		var rout *router
 
-		routerName := routerName(s.routerID.String(), s.myASN)
+		routerName := routerName(s.RouterID.String(), s.MyASN, s.VRFName)
 		if rout, exist = routers[routerName]; !exist {
 			rout = &router{
-				myASN:        s.myASN,
+				myASN:        s.MyASN,
 				neighbors:    make(map[string]*neighborConfig),
 				ipV4Prefixes: make(map[string]string),
 				ipV6Prefixes: make(map[string]string),
+				vrf:          s.VRFName,
 			}
-			if s.routerID != nil {
-				rout.routerID = s.routerID.String()
+			if s.RouterID != nil {
+				rout.routerID = s.RouterID.String()
 			}
 			routers[routerName] = rout
 		}
 
-		neighborName := neighborName(s.addr, s.asn)
+		neighborName := neighborName(s.PeerAddress, s.PeerASN, s.VRFName)
 		if neighbor, exist = rout.neighbors[neighborName]; !exist {
-			host, port, err := net.SplitHostPort(s.addr)
+			host, port, err := net.SplitHostPort(s.PeerAddress)
 			if err != nil {
 				return nil, err
 			}
@@ -256,18 +235,19 @@ func (sm *sessionManager) createConfig() (*frrConfig, error) {
 
 			neighbor = &neighborConfig{
 				IPFamily:       family,
-				ASN:            s.asn,
+				ASN:            s.PeerASN,
 				Addr:           host,
 				Port:           uint16(portUint),
-				HoldTime:       uint64(s.holdTime / time.Second),
-				KeepaliveTime:  uint64(s.keepaliveTime / time.Second),
-				Password:       s.password,
+				HoldTime:       uint64(s.HoldTime / time.Second),
+				KeepaliveTime:  uint64(s.KeepAliveTime / time.Second),
+				Password:       s.Password,
 				Advertisements: make([]*advertisementConfig, 0),
-				BFDProfile:     s.bfdProfile,
-				EBGPMultiHop:   s.ebgpMultiHop,
+				BFDProfile:     s.BFDProfile,
+				EBGPMultiHop:   s.EBGPMultiHop,
+				VRFName:        s.VRFName,
 			}
-			if s.srcAddr != nil {
-				neighbor.SrcAddr = s.srcAddr.String()
+			if s.SourceAddress != nil {
+				neighbor.SrcAddr = s.SourceAddress.String()
 			}
 			rout.neighbors[neighborName] = neighbor
 		}
@@ -276,7 +256,7 @@ func (sm *sessionManager) createConfig() (*frrConfig, error) {
 		   duplicate prefixes and can, therefore, just add them to the
 		   'neighbor.Advertisements' list. */
 		for _, adv := range s.advertised {
-			if !adv.MatchesPeer(s.name) {
+			if !adv.MatchesPeer(s.SessionName) {
 				continue
 			}
 
diff --git a/internal/bgp/frr/templates/filters.tmpl b/internal/bgp/frr/templates/filters.tmpl
index 501aac6b..710d545d 100644
--- a/internal/bgp/frr/templates/filters.tmpl
+++ b/internal/bgp/frr/templates/filters.tmpl
@@ -1,6 +1,6 @@
 {{- define "localpreffilter" -}}
 {{frrIPFamily .advertisement.IPFamily}} prefix-list {{localPrefPrefixList .neighbor .advertisement.LocalPref}} permit {{.advertisement.Prefix}}
-route-map {{.neighbor.Addr}}-out permit {{counter .neighbor.Addr}}
+route-map {{.neighbor.ID}}-out permit {{counter .neighbor.ID}}
   match {{frrIPFamily .advertisement.IPFamily}} address prefix-list {{localPrefPrefixList .neighbor .advertisement.LocalPref}}
   set local-preference {{.advertisement.LocalPref}}
   on-match next
@@ -8,7 +8,7 @@ route-map {{.neighbor.Addr}}-out permit {{counter .neighbor.Addr}}
 
 {{- define "communityfilter" -}}
 {{frrIPFamily .advertisement.IPFamily}} prefix-list {{communityPrefixList .neighbor .community}} permit {{.advertisement.Prefix}}
-route-map {{.neighbor.Addr}}-out permit {{counter .neighbor.Addr}}
+route-map {{.neighbor.ID}}-out permit {{counter .neighbor.ID}}
   match {{frrIPFamily .advertisement.IPFamily}} address prefix-list {{communityPrefixList .neighbor .community}}
   set community {{.community}} additive
   on-match next
@@ -20,7 +20,7 @@ route-map {{.neighbor.Addr}}-out permit {{counter .neighbor.Addr}}
      deny all the others.*/ -}}
 {{- define "neighborfilters" -}}
 
-route-map {{.neighbor.Addr}}-in deny 20
+route-map {{.neighbor.ID}}-in deny 20
 {{- range $a := .neighbor.Advertisements }}
 {{/* Advertisements for which we must enable set the local pref */}}
 {{- if not (eq $a.LocalPref 0)}}
@@ -35,11 +35,11 @@ route-map {{.neighbor.Addr}}-in deny 20
 {{frrIPFamily $a.IPFamily}} prefix-list {{allowedPrefixList $.neighbor}} permit {{$a.Prefix}}
 {{- end }}
 
-route-map {{$.neighbor.Addr}}-out permit {{counter $.neighbor.Addr}}
+route-map {{$.neighbor.ID}}-out permit {{counter $.neighbor.ID}}
   match ip address prefix-list {{allowedPrefixList $.neighbor}}
-route-map {{$.neighbor.Addr}}-out permit {{counter $.neighbor.Addr}}
+route-map {{$.neighbor.ID}}-out permit {{counter $.neighbor.ID}}
   match ipv6 address prefix-list {{allowedPrefixList $.neighbor}}
 
 ip prefix-list {{allowedPrefixList $.neighbor }} deny any
 ipv6 prefix-list {{allowedPrefixList $.neighbor}} deny any
-{{- end -}}
\ No newline at end of file
+{{- end -}}
diff --git a/internal/bgp/frr/templates/frr.tmpl b/internal/bgp/frr/templates/frr.tmpl
index d8e9f9b5..d1274137 100644
--- a/internal/bgp/frr/templates/frr.tmpl
+++ b/internal/bgp/frr/templates/frr.tmpl
@@ -19,14 +19,14 @@ hostname {{.Hostname}}
 ip nht resolve-via-default
 ipv6 nht resolve-via-default
 
-{{- range .Routers }}
+{{- range $r := .Routers }}
 {{- range .Neighbors }}
-{{template "neighborfilters" dict "neighbor" .}}
+{{template "neighborfilters" dict "neighbor" . "router" $r}}
 {{- end }}
 {{- end }}
 
 {{range $r := .Routers -}}
-router bgp {{$r.MyASN}}
+router bgp {{$r.MyASN}}{{ if $r.VRF }} vrf {{$r.VRF}}{{end}}
   no bgp ebgp-requires-policy
   no bgp network import-check
   no bgp default ipv4-unicast
diff --git a/internal/bgp/frr/templates/neighboripfamily.tmpl b/internal/bgp/frr/templates/neighboripfamily.tmpl
index 5b5c600b..7a56f800 100644
--- a/internal/bgp/frr/templates/neighboripfamily.tmpl
+++ b/internal/bgp/frr/templates/neighboripfamily.tmpl
@@ -2,12 +2,12 @@
 {{/* no bgp default ipv4-unicast prevents peering if no address families are defined. We declare an ipv4 one for the peer to make the pairing happen */}}
   address-family ipv4 unicast
     neighbor {{.Addr}} activate
-    neighbor {{.Addr}} route-map {{.Addr}}-in in
-    neighbor {{.Addr}} route-map {{.Addr}}-out out
+    neighbor {{.Addr}} route-map {{.ID}}-in in
+    neighbor {{.Addr}} route-map {{.ID}}-out out
   exit-address-family
   address-family ipv6 unicast
     neighbor {{.Addr}} activate
-    neighbor {{.Addr}} route-map {{.Addr}}-in in
-    neighbor {{.Addr}} route-map {{.Addr}}-out out
+    neighbor {{.Addr}} route-map {{.ID}}-in in
+    neighbor {{.Addr}} route-map {{.ID}}-out out
   exit-address-family
-{{- end -}}
\ No newline at end of file
+{{- end -}}
diff --git a/internal/bgp/native/native.go b/internal/bgp/native/native.go
index 2725bb87..a67b957f 100644
--- a/internal/bgp/native/native.go
+++ b/internal/bgp/native/native.go
@@ -28,18 +28,10 @@ var errClosed = errors.New("session closed")
 
 // session represents one BGP session to an external router.
 type session struct {
-	name             string
-	myASN            uint32
-	routerID         net.IP // May be nil, meaning "derive from context"
-	myNode           string
-	addr             string
-	srcAddr          net.IP
-	asn              uint32
+	bgp.SessionParameters
 	peerFBASNSupport bool
-	holdTime         time.Duration
-	keepaliveTime    time.Duration
-	logger           log.Logger
-	password         string
+
+	logger log.Logger
 
 	newHoldTime chan bool
 	backoff     backoff
@@ -66,28 +58,19 @@ func NewSessionManager(l log.Logger) *sessionManager {
 //
 // The session will immediately try to connect and synchronize its
 // local state with the peer.
-func (sm *sessionManager) NewSession(l log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, holdTime, keepaliveTime time.Duration, password, myNode, bfdProfile string, ebgpMultiHop bool, name string) (bgp.Session, error) {
+func (sm *sessionManager) NewSession(l log.Logger, args bgp.SessionParameters) (bgp.Session, error) {
 	ret := &session{
-		name:          name,
-		addr:          addr,
-		srcAddr:       srcAddr,
-		myASN:         myASN,
-		routerID:      routerID.To4(),
-		myNode:        myNode,
-		asn:           asn,
-		holdTime:      holdTime,
-		keepaliveTime: keepaliveTime,
-		logger:        log.With(l, "peer", addr, "localASN", myASN, "peerASN", asn),
-		newHoldTime:   make(chan bool, 1),
-		advertised:    map[string]*bgp.Advertisement{},
-		password:      password,
+		SessionParameters: args,
+		logger:            log.With(l, "peer", args.PeerAddress, "localASN", args.MyASN, "peerASN", args.PeerASN),
+		newHoldTime:       make(chan bool, 1),
+		advertised:        map[string]*bgp.Advertisement{},
 	}
 	ret.cond = sync.NewCond(&ret.mu)
 	go ret.sendKeepalives()
 	go ret.run()
 
-	stats.sessionUp.WithLabelValues(ret.addr).Set(0)
-	stats.prefixes.WithLabelValues(ret.addr).Set(0)
+	stats.sessionUp.WithLabelValues(ret.PeerAddress).Set(0)
+	stats.prefixes.WithLabelValues(ret.PeerAddress).Set(0)
 
 	return ret, nil
 }
@@ -98,7 +81,7 @@ func (sm *sessionManager) SyncBFDProfiles(profiles map[string]*config.BFDProfile
 
 // run tries to stay connected to the peer, and pumps route updates to it.
 func (s *session) run() {
-	defer stats.DeleteSession(s.addr)
+	defer stats.DeleteSession(s.PeerAddress)
 	for {
 		if err := s.connect(); err != nil {
 			if err == errClosed {
@@ -109,7 +92,7 @@ func (s *session) run() {
 			time.Sleep(backoff)
 			continue
 		}
-		stats.SessionUp(s.addr)
+		stats.SessionUp(s.PeerAddress)
 		s.backoff.Reset()
 
 		level.Info(s.logger).Log("event", "sessionUp", "msg", "BGP session established")
@@ -117,7 +100,7 @@ func (s *session) run() {
 		if !s.sendUpdates() {
 			return
 		}
-		stats.SessionDown(s.addr)
+		stats.SessionDown(s.PeerAddress)
 		level.Warn(s.logger).Log("event", "sessionDown", "msg", "BGP session down")
 	}
 }
@@ -135,7 +118,7 @@ func (s *session) sendUpdates() bool {
 		return true
 	}
 
-	ibgp := s.myASN == s.asn
+	ibgp := s.MyASN == s.PeerASN
 	fbasn := s.peerFBASNSupport
 
 	if s.new != nil {
@@ -143,14 +126,14 @@ func (s *session) sendUpdates() bool {
 	}
 
 	for c, adv := range s.advertised {
-		if err := sendUpdate(s.conn, s.myASN, ibgp, fbasn, s.nextHop, adv); err != nil {
+		if err := sendUpdate(s.conn, s.MyASN, ibgp, fbasn, s.nextHop, adv); err != nil {
 			s.abort()
 			level.Error(s.logger).Log("op", "sendUpdate", "ip", c, "error", err, "msg", "failed to send BGP update")
 			return true
 		}
-		stats.UpdateSent(s.addr)
+		stats.UpdateSent(s.PeerAddress)
 	}
-	stats.AdvertisedPrefixes(s.addr, len(s.advertised))
+	stats.AdvertisedPrefixes(s.PeerAddress, len(s.advertised))
 
 	for {
 		for s.new == nil && s.conn != nil {
@@ -176,12 +159,12 @@ func (s *session) sendUpdates() bool {
 				continue
 			}
 
-			if err := sendUpdate(s.conn, s.myASN, ibgp, fbasn, s.nextHop, adv); err != nil {
+			if err := sendUpdate(s.conn, s.MyASN, ibgp, fbasn, s.nextHop, adv); err != nil {
 				s.abort()
 				level.Error(s.logger).Log("op", "sendUpdate", "prefix", c, "error", err, "msg", "failed to send BGP update")
 				return true
 			}
-			stats.UpdateSent(s.addr)
+			stats.UpdateSent(s.PeerAddress)
 		}
 
 		wdr := []*net.IPNet{}
@@ -198,10 +181,10 @@ func (s *session) sendUpdates() bool {
 				}
 				return true
 			}
-			stats.UpdateSent(s.addr)
+			stats.UpdateSent(s.PeerAddress)
 		}
 		s.advertised, s.new = s.new, nil
-		stats.AdvertisedPrefixes(s.addr, len(s.advertised))
+		stats.AdvertisedPrefixes(s.PeerAddress, len(s.advertised))
 	}
 }
 
@@ -218,47 +201,47 @@ func (s *session) connect() error {
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
 	deadline, _ := ctx.Deadline()
-	conn, err := dialMD5(ctx, s.addr, s.srcAddr, s.password)
+	conn, err := dialMD5(ctx, s.PeerAddress, s.SourceAddress, s.Password)
 	if err != nil {
-		return fmt.Errorf("dial %q: %s", s.addr, err)
+		return fmt.Errorf("dial %q: %s", s.PeerAddress, err)
 	}
 
 	if err = conn.SetDeadline(deadline); err != nil {
 		conn.Close()
-		return fmt.Errorf("setting deadline on conn to %q: %s", s.addr, err)
+		return fmt.Errorf("setting deadline on conn to %q: %s", s.PeerAddress, err)
 	}
 
 	addr, ok := conn.LocalAddr().(*net.TCPAddr)
 	if !ok {
 		conn.Close()
-		return fmt.Errorf("getting local addr for default nexthop to %q: %s", s.addr, err)
+		return fmt.Errorf("getting local addr for default nexthop to %q: %s", s.PeerAddress, err)
 	}
 	s.nextHop = addr.IP
 
-	routerID := s.routerID
+	routerID := s.RouterID
 	if routerID == nil {
-		routerID, err = getRouterID(s.nextHop, s.myNode)
+		routerID, err = getRouterID(s.nextHop, s.CurrentNode)
 		if err != nil {
 			return err
 		}
 	}
 
-	if err = sendOpen(conn, s.myASN, routerID, s.holdTime); err != nil {
+	if err = sendOpen(conn, s.MyASN, routerID, s.HoldTime); err != nil {
 		conn.Close()
-		return fmt.Errorf("send OPEN to %q: %s", s.addr, err)
+		return fmt.Errorf("send OPEN to %q: %s", s.PeerAddress, err)
 	}
 
 	op, err := readOpen(conn)
 	if err != nil {
 		conn.Close()
-		return fmt.Errorf("read OPEN from %q: %s", s.addr, err)
+		return fmt.Errorf("read OPEN from %q: %s", s.PeerAddress, err)
 	}
-	if op.asn != s.asn {
+	if op.asn != s.PeerASN {
 		conn.Close()
-		return fmt.Errorf("unexpected peer ASN %d, want %d", op.asn, s.asn)
+		return fmt.Errorf("unexpected peer ASN %d, want %d", op.asn, s.PeerASN)
 	}
 	s.peerFBASNSupport = op.fbasn
-	if s.myASN > 65536 && !s.peerFBASNSupport {
+	if s.MyASN > 65536 && !s.peerFBASNSupport {
 		conn.Close()
 		return fmt.Errorf("peer does not support 4-byte ASNs")
 	}
@@ -266,7 +249,7 @@ func (s *session) connect() error {
 	// BGP session is established, clear the connect timeout deadline.
 	if err := conn.SetDeadline(time.Time{}); err != nil {
 		conn.Close()
-		return fmt.Errorf("clearing deadline on conn to %q: %s", s.addr, err)
+		return fmt.Errorf("clearing deadline on conn to %q: %s", s.PeerAddress, err)
 	}
 
 	// Consume BGP messages until the connection closes.
@@ -275,11 +258,11 @@ func (s *session) connect() error {
 	// Send one keepalive to say that yes, we accept the OPEN.
 	if err := sendKeepalive(conn); err != nil {
 		conn.Close()
-		return fmt.Errorf("accepting peer OPEN from %q: %s", s.addr, err)
+		return fmt.Errorf("accepting peer OPEN from %q: %s", s.PeerAddress, err)
 	}
 
 	// Set up regular keepalives from now on.
-	s.actualHoldTime = s.holdTime
+	s.actualHoldTime = s.HoldTime
 	if op.holdTime < s.actualHoldTime {
 		s.actualHoldTime = op.holdTime
 	}
@@ -396,7 +379,7 @@ func (s *session) sendKeepalive() error {
 	if err := sendKeepalive(s.conn); err != nil {
 		s.abort()
 		level.Error(s.logger).Log("op", "sendKeepalive", "error", err, "msg", "failed to send keepalive")
-		return fmt.Errorf("sending keepalive to %q: %s", s.addr, err)
+		return fmt.Errorf("sending keepalive to %q: %s", s.PeerAddress, err)
 	}
 	return nil
 }
@@ -463,7 +446,7 @@ func (s *session) Set(advs ...*bgp.Advertisement) error {
 
 	newAdvs := map[string]*bgp.Advertisement{}
 	for _, adv := range advs {
-		if !adv.MatchesPeer(s.name) {
+		if !adv.MatchesPeer(s.SessionName) {
 			continue
 		}
 		err := validate(adv)
@@ -474,7 +457,7 @@ func (s *session) Set(advs ...*bgp.Advertisement) error {
 	}
 
 	s.new = newAdvs
-	stats.PendingPrefixes(s.addr, len(s.new))
+	stats.PendingPrefixes(s.PeerAddress, len(s.new))
 	s.cond.Broadcast()
 
 	return nil
@@ -486,13 +469,13 @@ func (s *session) abort() {
 	if s.conn != nil {
 		s.conn.Close()
 		s.conn = nil
-		stats.SessionDown(s.addr)
+		stats.SessionDown(s.PeerAddress)
 	}
 	// Next time we retry the connection, we can just skip straight to
 	// the desired end state.
 	if s.new != nil {
 		s.advertised, s.new = s.new, nil
-		stats.PendingPrefixes(s.addr, len(s.advertised))
+		stats.PendingPrefixes(s.PeerAddress, len(s.advertised))
 	}
 	s.cond.Broadcast()
 }
diff --git a/internal/config/config.go b/internal/config/config.go
index 9161a323..21fc084f 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -98,6 +98,8 @@ type Peer struct {
 	BFDProfile string
 	// Optional ebgp peer is multi-hops away.
 	EBGPMultiHop bool
+	// Optional name of the vrf to establish the session from
+	VRF string
 	// TODO: more BGP session settings
 }
 
@@ -414,6 +416,7 @@ func peerFromCR(p metallbv1beta2.BGPPeer, passwordSecrets map[string]corev1.Secr
 		Password:      password,
 		BFDProfile:    p.Spec.BFDProfile,
 		EBGPMultiHop:  p.Spec.EBGPMultiHop,
+		VRF:           p.Spec.VRFName,
 	}, nil
 }
 
diff --git a/internal/config/validation.go b/internal/config/validation.go
index e874b086..418871f6 100644
--- a/internal/config/validation.go
+++ b/internal/config/validation.go
@@ -30,6 +30,9 @@ func DiscardFRROnly(c ClusterResources) error {
 		if p.Spec.KeepaliveTime.Duration != 0 {
 			return fmt.Errorf("peer %s has keepalive-time set on native bgp mode", p.Spec.Address)
 		}
+		if p.Spec.VRFName != "" {
+			return fmt.Errorf("peer %s has vrf set on native bgp mode", p.Spec.Address)
+		}
 	}
 	if len(c.BFDProfiles) > 0 {
 		return errors.New("bfd profiles section set")
diff --git a/speaker/bgp_controller.go b/speaker/bgp_controller.go
index 5147e669..638f4927 100644
--- a/speaker/bgp_controller.go
+++ b/speaker/bgp_controller.go
@@ -216,7 +216,24 @@ func (c *bgpController) syncPeers(l log.Logger) error {
 			if p.cfg.RouterID != nil {
 				routerID = p.cfg.RouterID
 			}
-			s, err := c.sessionManager.NewSession(c.logger, net.JoinHostPort(p.cfg.Addr.String(), strconv.Itoa(int(p.cfg.Port))), p.cfg.SrcAddr, p.cfg.MyASN, routerID, p.cfg.ASN, p.cfg.HoldTime, p.cfg.KeepaliveTime, p.cfg.Password, c.myNode, p.cfg.BFDProfile, p.cfg.EBGPMultiHop, p.cfg.Name)
+			s, err := c.sessionManager.NewSession(c.logger,
+				bgp.SessionParameters{
+					PeerAddress:   net.JoinHostPort(p.cfg.Addr.String(), strconv.Itoa(int(p.cfg.Port))),
+					SourceAddress: p.cfg.SrcAddr,
+					MyASN:         p.cfg.MyASN,
+					RouterID:      routerID,
+					PeerASN:       p.cfg.ASN,
+					HoldTime:      p.cfg.HoldTime,
+					KeepAliveTime: p.cfg.KeepaliveTime,
+					Password:      p.cfg.Password,
+					CurrentNode:   c.myNode,
+					BFDProfile:    p.cfg.BFDProfile,
+					EBGPMultiHop:  p.cfg.EBGPMultiHop,
+					SessionName:   p.cfg.Name,
+					VRFName:       p.cfg.VRF,
+				},
+			)
+
 			if err != nil {
 				level.Error(l).Log("op", "syncPeers", "error", err, "peer", p.cfg.Addr, "msg", "failed to create BGP session")
 				errs++
diff --git a/website/content/apis/_index.md b/website/content/apis/_index.md
index e4f2eefc..2738b4cb 100644
--- a/website/content/apis/_index.md
+++ b/website/content/apis/_index.md
@@ -947,6 +947,19 @@ bool
 <p>To set if the BGPPeer is multi-hops away. Needed for FRR mode only.</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>vrf</code><br/>
+<em>
+string
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>To set if we want to peer with the BGPPeer using an interface belonging to
+a host vrf</p>
+</td>
+</tr>
 </table>
 </td>
 </tr>


Test patch:
diff --git a/internal/bgp/frr/frr_bfd_test.go b/internal/bgp/frr/frr_bfd_test.go
index d239e292..3d87a108 100644
--- a/internal/bgp/frr/frr_bfd_test.go
+++ b/internal/bgp/frr/frr_bfd_test.go
@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/go-kit/log"
+	"go.universe.tf/metallb/internal/bgp"
 	"go.universe.tf/metallb/internal/config"
 	"go.universe.tf/metallb/internal/logging"
 	"go.universe.tf/metallb/internal/pointer"
@@ -121,7 +122,20 @@ func TestBFDWithSession(t *testing.T) {
 		t.Fatalf("Failed to sync bfd profiles %s", err)
 	}
 
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, 2*time.Second, "password", "hostname", "foo", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: 2 * time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer",
+			BFDProfile:    "foo"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
diff --git a/internal/bgp/frr/frr_test.go b/internal/bgp/frr/frr_test.go
index a9a0a27f..91a976ca 100644
--- a/internal/bgp/frr/frr_test.go
+++ b/internal/bgp/frr/frr_test.go
@@ -104,7 +104,19 @@ func TestSingleEBGPSessionMultiHop(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -119,7 +131,20 @@ func TestSingleEBGPSessionOneHop(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "127.0.0.2:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", false, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "127.0.0.2:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -134,7 +159,20 @@ func TestSingleIPv6EBGPSessionOneHop(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "[127:0:0::2]:179", net.ParseIP("10:1:1::254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", false, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "[127:0:0::2]:179",
+			SourceAddress: net.ParseIP("10:1:1::254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -149,7 +187,20 @@ func TestSingleIBGPSession(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 100, time.Second, time.Second, "password", "hostname", "", false, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       100,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -164,7 +215,19 @@ func TestSingleIPv6IBGPSession(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "[10:2:2::254]:179", net.ParseIP("10:1:1::254"), 100, net.ParseIP("10.1.1.254"), 100, time.Second, time.Second, "password", "hostname", "", false, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "[10:2:2::254]:179",
+			SourceAddress: net.ParseIP("10:1:1::254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       100,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -180,7 +243,20 @@ func TestSingleSessionClose(t *testing.T) {
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -194,12 +270,38 @@ func TestTwoSessions(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer1")
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer1"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session1.Close()
-	session2, err := sessionManager.NewSession(l, "10.4.4.255:179", net.ParseIP("10.3.3.254"), 300, net.ParseIP("10.3.3.254"), 400, time.Second, time.Second, "password", "hostname", "", true, "test-peer2")
+	session2, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.4.4.255:179",
+			SourceAddress: net.ParseIP("10.3.3.254"),
+			MyASN:         300,
+			RouterID:      net.ParseIP("10.3.3.254"),
+			PeerASN:       400,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer2"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -214,17 +316,42 @@ func TestTwoIPv6Sessions(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session1, err := sessionManager.NewSession(l, "[10:2:2::254]:179", net.ParseIP("10:1:1::254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", false, "test-peer1")
+
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "[10:2:2::254]:179",
+			SourceAddress: net.ParseIP("10:1:1::254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer1"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session1.Close()
-	session2, err := sessionManager.NewSession(l, "[10:4:4::255]:179", net.ParseIP("10:3:3::254"), 300, net.ParseIP("10.3.3.254"), 400, time.Second, time.Second, "password", "hostname", "", false, "test-peer2")
+	session2, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "[10:4:4::255]:179",
+			SourceAddress: net.ParseIP("10:3:3::254"),
+			MyASN:         300,
+			RouterID:      net.ParseIP("10.3.3.254"),
+			PeerASN:       400,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer2"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session2.Close()
-
 	testCheckConfigFile(t)
 }
 
@@ -234,12 +361,37 @@ func TestTwoSessionsDuplicate(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer1")
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer1"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session1.Close()
-	session2, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer2")
+	session2, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer2"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -254,12 +406,38 @@ func TestTwoSessionsDuplicateRouter(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer1")
+
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer1"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session1.Close()
-	session2, err := sessionManager.NewSession(l, "10.4.4.255:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 400, time.Second, time.Second, "password", "hostname", "", true, "test-peer2")
+	session2, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.4.4.255:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       400,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer2"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -274,7 +452,19 @@ func TestSingleAdvertisement(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -309,7 +499,20 @@ func TestSingleAdvertisementNoRouterID(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, nil, 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      nil,
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -338,7 +541,20 @@ func TestSingleAdvertisementInvalidNoPort(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
+
 	if err == nil {
 		session.Close()
 		t.Fatalf("Should not be able to create session")
@@ -353,7 +569,20 @@ func TestSingleAdvertisementStop(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
+
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -387,7 +616,19 @@ func TestSingleAdvertisementChange(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -430,7 +671,20 @@ func TestSingleAdvertisementWithPeerSelector(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -466,7 +720,19 @@ func TestSingleAdvertisementNonExistingPeer(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -502,7 +768,19 @@ func TestTwoAdvertisements(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -543,13 +821,37 @@ func TestTwoAdvertisementsTwoSessions(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session.Close()
 
-	session1, err := sessionManager.NewSession(l, "10.2.2.255:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer1")
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.255:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer1"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -596,13 +898,37 @@ func TestTwoAdvertisementsTwoSessionsOneWithPeerSelector(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer")
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session.Close()
 
-	session1, err := sessionManager.NewSession(l, "10.2.2.255:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true, "test-peer1")
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.255:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer1"})
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
diff --git a/internal/bgp/frr/frr_vrf_test.go b/internal/bgp/frr/frr_vrf_test.go
new file mode 100644
index 00000000..05d15a65
--- /dev/null
+++ b/internal/bgp/frr/frr_vrf_test.go
@@ -0,0 +1,613 @@
+// SPDX-License-Identifier:Apache-2.0
+
+package frr
+
+import (
+	"net"
+	"testing"
+	"time"
+
+	"github.com/go-kit/log"
+	"go.universe.tf/metallb/internal/bgp"
+	"go.universe.tf/metallb/internal/config"
+	"go.universe.tf/metallb/internal/logging"
+)
+
+func TestVRFSingleEBGPSessionMultiHop(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer",
+			VRFName:       "red"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	testCheckConfigFile(t)
+}
+
+func TestSingleVRFIBGPSession(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       100,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer",
+			VRFName:       "red"})
+
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	testCheckConfigFile(t)
+}
+
+func TestTwoSessionsOneVRF(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer1"})
+
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session1.Close()
+	session2, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.4.4.255:179",
+			SourceAddress: net.ParseIP("10.3.3.254"),
+			MyASN:         300,
+			RouterID:      net.ParseIP("10.3.3.254"),
+			PeerASN:       400,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer2",
+			VRFName:       "red"})
+
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session2.Close()
+
+	testCheckConfigFile(t)
+}
+
+func TestTwoSessionsSameIPVRF(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer1"})
+
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session1.Close()
+	session2, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.3.3.254"),
+			MyASN:         300,
+			RouterID:      net.ParseIP("10.3.3.254"),
+			PeerASN:       400,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  true,
+			SessionName:   "test-peer2",
+			VRFName:       "red"})
+
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session2.Close()
+
+	testCheckConfigFile(t)
+}
+
+func TestSingleAdvertisementVRF(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer",
+			VRFName:       "red"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	prefix := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.10"),
+		Mask: classCMask,
+	}
+	communities := []uint32{}
+	community, _ := config.ParseCommunity("1111:2222")
+	communities = append(communities, community)
+	community, _ = config.ParseCommunity("3333:4444")
+	communities = append(communities, community)
+	adv := &bgp.Advertisement{
+		Prefix:      prefix,
+		Communities: communities,
+		LocalPref:   300,
+	}
+
+	err = session.Set(adv)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+
+	testCheckConfigFile(t)
+}
+
+func TestSingleAdvertisementChangeVRF(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer",
+			VRFName:       "red"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	prefix := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.10"),
+		Mask: classCMask,
+	}
+
+	adv := &bgp.Advertisement{
+		Prefix: prefix,
+	}
+
+	err = session.Set(adv)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+
+	prefix = &net.IPNet{
+		IP:   net.ParseIP("172.16.1.11"),
+		Mask: classCMask,
+	}
+
+	adv = &bgp.Advertisement{
+		Prefix: prefix,
+	}
+
+	err = session.Set(adv)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+
+	testCheckConfigFile(t)
+}
+
+func TestSingleAdvertisementWithPeerSelectorVRF(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer",
+			VRFName:       "red"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	prefix := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.10"),
+		Mask: classCMask,
+	}
+	communities := []uint32{}
+	community, _ := config.ParseCommunity("1111:2222")
+	communities = append(communities, community)
+	community, _ = config.ParseCommunity("3333:4444")
+	communities = append(communities, community)
+	adv := &bgp.Advertisement{
+		Prefix:      prefix,
+		Communities: communities,
+		LocalPref:   300,
+		Peers:       []string{"test-peer"},
+	}
+
+	err = session.Set(adv)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+
+	testCheckConfigFile(t)
+}
+
+func TestTwoAdvertisementsVRF(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer",
+			VRFName:       "red"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	prefix1 := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.10"),
+		Mask: classCMask,
+	}
+	communities := []uint32{}
+	community, _ := config.ParseCommunity("1111:2222")
+	communities = append(communities, community)
+	adv1 := &bgp.Advertisement{
+		Prefix:      prefix1,
+		Communities: communities,
+	}
+
+	prefix2 := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.11"),
+		Mask: classCMask,
+	}
+
+	adv2 := &bgp.Advertisement{
+		Prefix: prefix2,
+	}
+
+	err = session.Set(adv1, adv2)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+
+	testCheckConfigFile(t)
+}
+
+func TestTwoAdvertisementsTwoSessionsOneVRF(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.255:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer1",
+			VRFName:       "red"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session1.Close()
+
+	prefix1 := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.10"),
+		Mask: classCMask,
+	}
+	communities := []uint32{}
+	community, _ := config.ParseCommunity("1111:2222")
+	communities = append(communities, community)
+	adv1 := &bgp.Advertisement{
+		Prefix:      prefix1,
+		Communities: communities,
+	}
+
+	prefix2 := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.11"),
+		Mask: classCMask,
+	}
+
+	adv2 := &bgp.Advertisement{
+		Prefix:      prefix2,
+		Communities: communities,
+		LocalPref:   2,
+	}
+
+	err = session.Set(adv1, adv2)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+	err = session1.Set(adv1, adv2)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+
+	testCheckConfigFile(t)
+}
+
+func TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer",
+			VRFName:       "red"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.255:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer1",
+		})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session1.Close()
+
+	prefix1 := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.10"),
+		Mask: classCMask,
+	}
+	communities := []uint32{}
+	community, _ := config.ParseCommunity("1111:2222")
+	communities = append(communities, community)
+	adv1 := &bgp.Advertisement{
+		Prefix:      prefix1,
+		Communities: communities,
+		Peers:       []string{"test-peer"},
+	}
+
+	prefix2 := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.11"),
+		Mask: classCMask,
+	}
+
+	adv2 := &bgp.Advertisement{
+		Prefix:      prefix2,
+		Communities: communities,
+		LocalPref:   2,
+	}
+
+	err = session.Set(adv1, adv2)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+	err = session1.Set(adv1, adv2)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+
+	testCheckConfigFile(t)
+}
+
+func TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.254:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer",
+			VRFName:       "red"})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	session1, err := sessionManager.NewSession(l,
+		bgp.SessionParameters{
+			PeerAddress:   "10.2.2.255:179",
+			SourceAddress: net.ParseIP("10.1.1.254"),
+			MyASN:         100,
+			RouterID:      net.ParseIP("10.1.1.254"),
+			PeerASN:       200,
+			HoldTime:      time.Second,
+			KeepAliveTime: time.Second,
+			Password:      "password",
+			CurrentNode:   "hostname",
+			EBGPMultiHop:  false,
+			SessionName:   "test-peer1",
+			VRFName:       "blue",
+		})
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session1.Close()
+
+	prefix1 := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.10"),
+		Mask: classCMask,
+	}
+	communities := []uint32{}
+	community, _ := config.ParseCommunity("1111:2222")
+	communities = append(communities, community)
+	adv1 := &bgp.Advertisement{
+		Prefix:      prefix1,
+		Communities: communities,
+		Peers:       []string{"test-peer"},
+	}
+
+	prefix2 := &net.IPNet{
+		IP:   net.ParseIP("172.16.1.11"),
+		Mask: classCMask,
+	}
+
+	adv2 := &bgp.Advertisement{
+		Prefix:      prefix2,
+		Communities: communities,
+		LocalPref:   2,
+		Peers:       []string{"test-peer1"},
+	}
+
+	err = session.Set(adv1, adv2)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+	err = session1.Set(adv1, adv2)
+	if err != nil {
+		t.Fatalf("Could not advertise prefix: %s", err)
+	}
+
+	testCheckConfigFile(t)
+}
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden
new file mode 100644
index 00000000..03bfdae7
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden
@@ -0,0 +1,45 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-red-in deny 20
+
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.11/24
+
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 2
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.11/24
+  exit-address-family
+
+
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden
new file mode 100644
index 00000000..aa3e5db6
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden
@@ -0,0 +1,60 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-red-in deny 20
+
+ip prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes
+  set local-preference 300
+  on-match next
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 2
+  match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+ip prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 3
+  match ip address prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes
+  set community 3333:4444 additive
+  on-match next
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+route-map 10.2.2.254-red-out permit 4
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 5
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.10/24
+  exit-address-family
+
+
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden
new file mode 100644
index 00000000..aa3e5db6
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden
@@ -0,0 +1,60 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-red-in deny 20
+
+ip prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes
+  set local-preference 300
+  on-match next
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 2
+  match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+ip prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 3
+  match ip address prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes
+  set community 3333:4444 additive
+  on-match next
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+route-map 10.2.2.254-red-out permit 4
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 5
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.10/24
+  exit-address-family
+
+
diff --git a/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden b/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden
new file mode 100644
index 00000000..32dfd564
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden
@@ -0,0 +1,38 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-red-in deny 20
+
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 2
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 100
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden
new file mode 100644
index 00000000..1adf4629
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden
@@ -0,0 +1,121 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-in deny 20
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-out permit 1
+  match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+route-map 10.2.2.254-out permit 2
+  match ip address prefix-list 10.2.2.254-2-ipv4-localpref-prefixes
+  set local-preference 2
+  on-match next
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+route-map 10.2.2.254-out permit 3
+  match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.11/24
+
+route-map 10.2.2.254-out permit 4
+  match ip address prefix-list 10.2.2.254-pl-ipv4
+route-map 10.2.2.254-out permit 5
+  match ipv6 address prefix-list 10.2.2.254-pl-ipv4
+
+ip prefix-list 10.2.2.254-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
+route-map 10.2.2.255-red-in deny 20
+
+ip prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.255-red-out permit 1
+  match ip address prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.255-red-pl-ipv4 permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.255-red-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+route-map 10.2.2.255-red-out permit 2
+  match ip address prefix-list 10.2.2.255-red-2-ipv4-localpref-prefixes
+  set local-preference 2
+  on-match next
+ip prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+route-map 10.2.2.255-red-out permit 3
+  match ip address prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.255-red-pl-ipv4 permit 172.16.1.11/24
+
+route-map 10.2.2.255-red-out permit 4
+  match ip address prefix-list 10.2.2.255-red-pl-ipv4
+route-map 10.2.2.255-red-out permit 5
+  match ipv6 address prefix-list 10.2.2.255-red-pl-ipv4
+
+ip prefix-list 10.2.2.255-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.255-red-pl-ipv4 deny any
+
+router bgp 100
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.10/24
+    network 172.16.1.11/24
+  exit-address-family
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.255 remote-as 200
+  neighbor 10.2.2.255 port 179
+  neighbor 10.2.2.255 timers 1 1
+  neighbor 10.2.2.255 password password
+  neighbor 10.2.2.255 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.255 activate
+    neighbor 10.2.2.255 route-map 10.2.2.255-red-in in
+    neighbor 10.2.2.255 route-map 10.2.2.255-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.255 activate
+    neighbor 10.2.2.255 route-map 10.2.2.255-red-in in
+    neighbor 10.2.2.255 route-map 10.2.2.255-red-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.10/24
+    network 172.16.1.11/24
+  exit-address-family
+
+
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden
new file mode 100644
index 00000000..385efca0
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden
@@ -0,0 +1,112 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.255-in deny 20
+
+ip prefix-list 10.2.2.255-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+route-map 10.2.2.255-out permit 1
+  match ip address prefix-list 10.2.2.255-2-ipv4-localpref-prefixes
+  set local-preference 2
+  on-match next
+ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+route-map 10.2.2.255-out permit 2
+  match ip address prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.255-pl-ipv4 permit 172.16.1.11/24
+
+route-map 10.2.2.255-out permit 3
+  match ip address prefix-list 10.2.2.255-pl-ipv4
+route-map 10.2.2.255-out permit 4
+  match ipv6 address prefix-list 10.2.2.255-pl-ipv4
+
+ip prefix-list 10.2.2.255-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.255-pl-ipv4 deny any
+route-map 10.2.2.254-red-in deny 20
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+route-map 10.2.2.254-red-out permit 2
+  match ip address prefix-list 10.2.2.254-red-2-ipv4-localpref-prefixes
+  set local-preference 2
+  on-match next
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+route-map 10.2.2.254-red-out permit 3
+  match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.11/24
+
+route-map 10.2.2.254-red-out permit 4
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 5
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.255 remote-as 200
+  neighbor 10.2.2.255 port 179
+  neighbor 10.2.2.255 timers 1 1
+  neighbor 10.2.2.255 password password
+  neighbor 10.2.2.255 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.255 activate
+    neighbor 10.2.2.255 route-map 10.2.2.255-in in
+    neighbor 10.2.2.255 route-map 10.2.2.255-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.255 activate
+    neighbor 10.2.2.255 route-map 10.2.2.255-in in
+    neighbor 10.2.2.255 route-map 10.2.2.255-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.11/24
+  exit-address-family
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.10/24
+    network 172.16.1.11/24
+  exit-address-family
+
+
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden
new file mode 100644
index 00000000..a3ff4796
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden
@@ -0,0 +1,98 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.255-blue-in deny 20
+
+ip prefix-list 10.2.2.255-blue-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+route-map 10.2.2.255-blue-out permit 1
+  match ip address prefix-list 10.2.2.255-blue-2-ipv4-localpref-prefixes
+  set local-preference 2
+  on-match next
+ip prefix-list 10.2.2.255-blue-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+route-map 10.2.2.255-blue-out permit 2
+  match ip address prefix-list 10.2.2.255-blue-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.255-blue-pl-ipv4 permit 172.16.1.11/24
+
+route-map 10.2.2.255-blue-out permit 3
+  match ip address prefix-list 10.2.2.255-blue-pl-ipv4
+route-map 10.2.2.255-blue-out permit 4
+  match ipv6 address prefix-list 10.2.2.255-blue-pl-ipv4
+
+ip prefix-list 10.2.2.255-blue-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.255-blue-pl-ipv4 deny any
+route-map 10.2.2.254-red-in deny 20
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+route-map 10.2.2.254-red-out permit 2
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 3
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100 vrf blue
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.255 remote-as 200
+  neighbor 10.2.2.255 port 179
+  neighbor 10.2.2.255 timers 1 1
+  neighbor 10.2.2.255 password password
+  neighbor 10.2.2.255 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.255 activate
+    neighbor 10.2.2.255 route-map 10.2.2.255-blue-in in
+    neighbor 10.2.2.255 route-map 10.2.2.255-blue-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.255 activate
+    neighbor 10.2.2.255 route-map 10.2.2.255-blue-in in
+    neighbor 10.2.2.255 route-map 10.2.2.255-blue-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.11/24
+  exit-address-family
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.10/24
+  exit-address-family
+
+
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden
new file mode 100644
index 00000000..2214b0e8
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden
@@ -0,0 +1,54 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-red-in deny 20
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
+  set community 1111:2222 additive
+  on-match next
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.11/24
+
+route-map 10.2.2.254-red-out permit 2
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 3
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv4 unicast
+    network 172.16.1.10/24
+    network 172.16.1.11/24
+  exit-address-family
+
+
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden b/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden
new file mode 100644
index 00000000..6ce01ff6
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden
@@ -0,0 +1,71 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-in deny 20
+
+route-map 10.2.2.254-out permit 1
+  match ip address prefix-list 10.2.2.254-pl-ipv4
+route-map 10.2.2.254-out permit 2
+  match ipv6 address prefix-list 10.2.2.254-pl-ipv4
+
+ip prefix-list 10.2.2.254-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
+route-map 10.4.4.255-red-in deny 20
+
+route-map 10.4.4.255-red-out permit 1
+  match ip address prefix-list 10.4.4.255-red-pl-ipv4
+route-map 10.4.4.255-red-out permit 2
+  match ipv6 address prefix-list 10.4.4.255-red-pl-ipv4
+
+ip prefix-list 10.4.4.255-red-pl-ipv4 deny any
+ipv6 prefix-list 10.4.4.255-red-pl-ipv4 deny any
+
+router bgp 100
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-out out
+  exit-address-family
+router bgp 300 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.3.3.254
+  neighbor 10.4.4.255 remote-as 400
+  neighbor 10.4.4.255 ebgp-multihop
+  neighbor 10.4.4.255 port 179
+  neighbor 10.4.4.255 timers 1 1
+  neighbor 10.4.4.255 password password
+  neighbor 10.4.4.255 update-source 10.3.3.254
+
+  address-family ipv4 unicast
+    neighbor 10.4.4.255 activate
+    neighbor 10.4.4.255 route-map 10.4.4.255-red-in in
+    neighbor 10.4.4.255 route-map 10.4.4.255-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.4.4.255 activate
+    neighbor 10.4.4.255 route-map 10.4.4.255-red-in in
+    neighbor 10.4.4.255 route-map 10.4.4.255-red-out out
+  exit-address-family
+
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden b/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden
new file mode 100644
index 00000000..caef3dfd
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden
@@ -0,0 +1,71 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-in deny 20
+
+route-map 10.2.2.254-out permit 1
+  match ip address prefix-list 10.2.2.254-pl-ipv4
+route-map 10.2.2.254-out permit 2
+  match ipv6 address prefix-list 10.2.2.254-pl-ipv4
+
+ip prefix-list 10.2.2.254-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
+route-map 10.2.2.254-red-in deny 20
+
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 2
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-out out
+  exit-address-family
+router bgp 300 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.3.3.254
+  neighbor 10.2.2.254 remote-as 400
+  neighbor 10.2.2.254 ebgp-multihop
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.3.3.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+
diff --git a/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden b/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden
new file mode 100644
index 00000000..bc7d4135
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden
@@ -0,0 +1,39 @@
+log file /etc/frr/frr.log 
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-red-in deny 20
+
+route-map 10.2.2.254-red-out permit 1
+  match ip address prefix-list 10.2.2.254-red-pl-ipv4
+route-map 10.2.2.254-red-out permit 2
+  match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
+
+router bgp 100 vrf red
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+  neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-in in
+    neighbor 10.2.2.254 route-map 10.2.2.254-red-out out
+  exit-address-family
+
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 8f2acd29..d67c8197 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -75,6 +75,7 @@ func TestParse(t *testing.T) {
 							RouterID:     "10.20.30.40",
 							SrcAddress:   "10.20.30.40",
 							EBGPMultiHop: true,
+							VRFName:      "foo",
 						},
 					},
 					{
@@ -228,6 +229,7 @@ func TestParse(t *testing.T) {
 						RouterID:      net.ParseIP("10.20.30.40"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 						EBGPMultiHop:  true,
+						VRF:           "foo",
 					},
 					{
 						Name:          "peer2",
diff --git a/speaker/bgp_controller_test.go b/speaker/bgp_controller_test.go
index cf872b06..432ae437 100644
--- a/speaker/bgp_controller_test.go
+++ b/speaker/bgp_controller_test.go
@@ -9,7 +9,6 @@ import (
 	"sort"
 	"sync"
 	"testing"
-	"time"
 
 	"go.universe.tf/metallb/internal/bgp"
 	"go.universe.tf/metallb/internal/config"
@@ -106,20 +105,20 @@ type fakeBGPSessionManager struct {
 	gotAds map[string][]*bgp.Advertisement
 }
 
-func (f *fakeBGPSessionManager) NewSession(_ log.Logger, addr string, _ net.IP, _ uint32, _ net.IP, _ uint32, _ time.Duration, _ time.Duration, _, _, _ string, _ bool, name string) (bgp.Session, error) {
+func (f *fakeBGPSessionManager) NewSession(_ log.Logger, args bgp.SessionParameters) (bgp.Session, error) {
 	f.Lock()
 	defer f.Unlock()
 
-	if _, ok := f.gotAds[addr]; ok {
-		f.t.Errorf("Tried to create already existing BGP session to %q", addr)
+	if _, ok := f.gotAds[args.PeerAddress]; ok {
+		f.t.Errorf("Tried to create already existing BGP session to %q", args.PeerAddress)
 		return nil, errors.New("invariant violation")
 	}
 	// Nil because we haven't programmed any routes for it yet, but
 	// the key now exists in the map.
-	f.gotAds[addr] = nil
+	f.gotAds[args.PeerAddress] = nil
 	return &fakeSession{
 		f:    f,
-		addr: addr,
+		addr: args.PeerAddress,
 	}, nil
 }