Repository: metallb/metallb
Pull request #1829 (2023-02-13 09:44:29)

Update frr to 8.4.2
Bumping the frr version to 8.4.2
Also, changing the image revealed a misconfiguration we had in propagating the fixed ips to frr, so with this change we get rid of those fixed ips.

Fixes https://github.com/metallb/metallb/issues/1753


Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 37a57f63..54777942 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -266,7 +266,7 @@ jobs:
           rm -rf e2etest # we want to make sure we are not running current e2e by mistake
           cd metallb-v0.12.1
           sed -i -e 's/quay.io\/frrouting\/frr:stable_7.5/quay.io\/frrouting\/frr:7.5.1/g' e2etest/pkg/frr/container/container.go # replace with frr image from dockerhub since it has official image.
-          FOCUS="L2.*should work for ExternalTrafficPolicy=Cluster|BGP.*A service of protocol load balancer should work with.*IPV4 - ExternalTrafficPolicyCluster$|validate FRR running configuration"
+          FOCUS="L2.*should work for ExternalTrafficPolicy=Cluster|BGP.*A service of protocol load balancer should work with.*IPV4 - ExternalTrafficPolicyCluster$|BGP.*should work with the given bfd profile.*IPV4 - default"
           sudo -E env "PATH=$PATH" inv e2etest --skip-docker --use-operator --focus "$FOCUS" -e /tmp/kind_logs
 
       - name: Collect Logs
diff --git a/charts/metallb/README.md b/charts/metallb/README.md
index 65a3bff2..415183f2 100644
--- a/charts/metallb/README.md
+++ b/charts/metallb/README.md
@@ -112,7 +112,7 @@ Kubernetes: `>= 1.19.0-0`
 | speaker.frr.enabled | bool | `false` |  |
 | speaker.frr.image.pullPolicy | string | `nil` |  |
 | speaker.frr.image.repository | string | `"quay.io/frrouting/frr"` |  |
-| speaker.frr.image.tag | string | `"7.5.1"` |  |
+| speaker.frr.image.tag | string | `"8.4.2"` |  |
 | speaker.frr.metricsPort | int | `7473` |  |
 | speaker.frr.resources | object | `{}` |  |
 | speaker.frrMetrics.resources | object | `{}` |  |
diff --git a/charts/metallb/values.yaml b/charts/metallb/values.yaml
index c18a809a..12a58070 100644
--- a/charts/metallb/values.yaml
+++ b/charts/metallb/values.yaml
@@ -318,7 +318,7 @@ speaker:
     enabled: false
     image:
       repository: quay.io/frrouting/frr
-      tag: 7.5.1
+      tag: 8.4.2
       pullPolicy:
     metricsPort: 7473
     resources: {}
diff --git a/config/frr/speaker-patch.yaml b/config/frr/speaker-patch.yaml
index 5d50875a..3e637df5 100644
--- a/config/frr/speaker-patch.yaml
+++ b/config/frr/speaker-patch.yaml
@@ -29,7 +29,7 @@ spec:
           securityContext:
             runAsUser: 100
             runAsGroup: 101
-          image: quay.io/frrouting/frr:7.5.1
+          image: quay.io/frrouting/frr:8.4.2
           command: ["/bin/sh", "-c", "cp -rLf /tmp/frr/* /etc/frr/"]
           volumeMounts:
             - name: frr-startup
@@ -55,7 +55,7 @@ spec:
           securityContext:
             capabilities:
               add: ["NET_ADMIN", "NET_RAW", "SYS_ADMIN", "NET_BIND_SERVICE"]
-          image: quay.io/frrouting/frr:7.5.1
+          image: quay.io/frrouting/frr:8.4.2
           env:
             - name: TINI_SUBREAPER
               value: "true"
@@ -91,7 +91,7 @@ spec:
             failureThreshold: 30
             periodSeconds: 5
         - name: reloader
-          image: quay.io/frrouting/frr:7.5.1
+          image: quay.io/frrouting/frr:8.4.2
           command: ["/etc/frr_reloader/frr-reloader.sh"]
           volumeMounts:
             - name: frr-sockets
@@ -101,7 +101,7 @@ spec:
             - name: reloader
               mountPath: /etc/frr_reloader
         - name: frr-metrics
-          image: quay.io/frrouting/frr:7.5.1
+          image: quay.io/frrouting/frr:8.4.2
           command: ["/etc/frr_metrics/frr-metrics"]
           args:
             - --metrics-port=7473
diff --git a/config/manifests/metallb-frr-prometheus.yaml b/config/manifests/metallb-frr-prometheus.yaml
index eb687fca..3443f9e5 100644
--- a/config/manifests/metallb-frr-prometheus.yaml
+++ b/config/manifests/metallb-frr-prometheus.yaml
@@ -2073,7 +2073,7 @@ spec:
         env:
         - name: TINI_SUBREAPER
           value: "true"
-        image: quay.io/frrouting/frr:7.5.1
+        image: quay.io/frrouting/frr:8.4.2
         livenessProbe:
           failureThreshold: 3
           httpGet:
@@ -2101,7 +2101,7 @@ spec:
           name: frr-conf
       - command:
         - /etc/frr_reloader/frr-reloader.sh
-        image: quay.io/frrouting/frr:7.5.1
+        image: quay.io/frrouting/frr:8.4.2
         name: reloader
         volumeMounts:
         - mountPath: /var/run/frr
@@ -2114,7 +2114,7 @@ spec:
         - --metrics-port=7473
         command:
         - /etc/frr_metrics/frr-metrics
-        image: quay.io/frrouting/frr:7.5.1
+        image: quay.io/frrouting/frr:8.4.2
         name: frr-metrics
         ports:
         - containerPort: 7473
@@ -2200,7 +2200,7 @@ spec:
         - /bin/sh
         - -c
         - cp -rLf /tmp/frr/* /etc/frr/
-        image: quay.io/frrouting/frr:7.5.1
+        image: quay.io/frrouting/frr:8.4.2
         name: cp-frr-files
         securityContext:
           runAsGroup: 101
diff --git a/config/manifests/metallb-frr.yaml b/config/manifests/metallb-frr.yaml
index ff444641..404daaca 100644
--- a/config/manifests/metallb-frr.yaml
+++ b/config/manifests/metallb-frr.yaml
@@ -1896,7 +1896,7 @@ spec:
         env:
         - name: TINI_SUBREAPER
           value: "true"
-        image: quay.io/frrouting/frr:7.5.1
+        image: quay.io/frrouting/frr:8.4.2
         livenessProbe:
           failureThreshold: 3
           httpGet:
@@ -1924,7 +1924,7 @@ spec:
           name: frr-conf
       - command:
         - /etc/frr_reloader/frr-reloader.sh
-        image: quay.io/frrouting/frr:7.5.1
+        image: quay.io/frrouting/frr:8.4.2
         name: reloader
         volumeMounts:
         - mountPath: /var/run/frr
@@ -1937,7 +1937,7 @@ spec:
         - --metrics-port=7473
         command:
         - /etc/frr_metrics/frr-metrics
-        image: quay.io/frrouting/frr:7.5.1
+        image: quay.io/frrouting/frr:8.4.2
         name: frr-metrics
         ports:
         - containerPort: 7473
@@ -2023,7 +2023,7 @@ spec:
         - /bin/sh
         - -c
         - cp -rLf /tmp/frr/* /etc/frr/
-        image: quay.io/frrouting/frr:7.5.1
+        image: quay.io/frrouting/frr:8.4.2
         name: cp-frr-files
         securityContext:
           runAsGroup: 101
diff --git a/internal/bgp/frr/config.go b/internal/bgp/frr/config.go
index eb983a7d..7f7b4c13 100644
--- a/internal/bgp/frr/config.go
+++ b/internal/bgp/frr/config.go
@@ -62,19 +62,21 @@ type BFDProfile struct {
 }
 
 type neighborConfig struct {
-	IPFamily       ipfamily.Family
-	Name           string
-	ASN            uint32
-	Addr           string
-	SrcAddr        string
-	Port           uint16
-	HoldTime       uint64
-	KeepaliveTime  uint64
-	Password       string
-	Advertisements []*advertisementConfig
-	BFDProfile     string
-	EBGPMultiHop   bool
-	VRFName        string
+	IPFamily            ipfamily.Family
+	Name                string
+	ASN                 uint32
+	Addr                string
+	SrcAddr             string
+	Port                uint16
+	HoldTime            uint64
+	KeepaliveTime       uint64
+	Password            string
+	Advertisements      []*advertisementConfig
+	BFDProfile          string
+	EBGPMultiHop        bool
+	VRFName             string
+	HasV4Advertisements bool
+	HasV6Advertisements bool
 }
 
 func (n *neighborConfig) ID() string {
diff --git a/internal/bgp/frr/frr.go b/internal/bgp/frr/frr.go
index ff4852cc..2f6217e4 100644
--- a/internal/bgp/frr/frr.go
+++ b/internal/bgp/frr/frr.go
@@ -286,8 +286,10 @@ func (sm *sessionManager) createConfig() (*frrConfig, error) {
 			switch family {
 			case ipfamily.IPv4:
 				rout.ipV4Prefixes[prefix] = prefix
+				neighbor.HasV4Advertisements = true
 			case ipfamily.IPv6:
 				rout.ipV6Prefixes[prefix] = prefix
+				neighbor.HasV6Advertisements = true
 			}
 		}
 	}
diff --git a/internal/bgp/frr/parse.go b/internal/bgp/frr/parse.go
index 94d27a04..288c0082 100644
--- a/internal/bgp/frr/parse.go
+++ b/internal/bgp/frr/parse.go
@@ -76,27 +76,28 @@ type FRRRoute struct {
 }
 
 type BFDPeer struct {
-	Multihop               bool   `json:"multihop"`
-	Peer                   string `json:"peer"`
-	Local                  string `json:"local"`
-	Vrf                    string `json:"vrf"`
-	Interface              string `json:"interface"`
-	ID                     int    `json:"id"`
-	RemoteID               int64  `json:"remote-id"`
-	PassiveMode            bool   `json:"passive-mode"`
-	Status                 string `json:"status"`
-	Uptime                 int    `json:"uptime"`
-	Diagnostic             string `json:"diagnostic"`
-	RemoteDiagnostic       string `json:"remote-diagnostic"`
-	ReceiveInterval        int    `json:"receive-interval"`
-	TransmitInterval       int    `json:"transmit-interval"`
-	EchoReceiveInterval    int    `json:"echo-receive-interval"`
-	EchoTransmitInterval   int    `json:"echo-transmit-interval"`
-	DetectMultiplier       int    `json:"detect-multiplier"`
-	RemoteReceiveInterval  int    `json:"remote-receive-interval"`
-	RemoteTransmitInterval int    `json:"remote-transmit-interval"`
-	RemoteEchoInterval     int    `json:"remote-echo-interval"`
-	RemoteDetectMultiplier int    `json:"remote-detect-multiplier"`
+	Multihop                  bool   `json:"multihop"`
+	Peer                      string `json:"peer"`
+	Local                     string `json:"local"`
+	Vrf                       string `json:"vrf"`
+	Interface                 string `json:"interface"`
+	ID                        int    `json:"id"`
+	RemoteID                  int64  `json:"remote-id"`
+	PassiveMode               bool   `json:"passive-mode"`
+	Status                    string `json:"status"`
+	Uptime                    int    `json:"uptime"`
+	Diagnostic                string `json:"diagnostic"`
+	RemoteDiagnostic          string `json:"remote-diagnostic"`
+	ReceiveInterval           int    `json:"receive-interval"`
+	TransmitInterval          int    `json:"transmit-interval"`
+	EchoReceiveInterval       int    `json:"echo-receive-interval"`
+	EchoTransmitInterval      int    `json:"echo-transmit-interval"`
+	DetectMultiplier          int    `json:"detect-multiplier"`
+	RemoteReceiveInterval     int    `json:"remote-receive-interval"`
+	RemoteTransmitInterval    int    `json:"remote-transmit-interval"`
+	RemoteEchoInterval        int    `json:"remote-echo-interval"`
+	RemoteEchoReceiveInterval int    `json:"remote-echo-receive-interval"`
+	RemoteDetectMultiplier    int    `json:"remote-detect-multiplier"`
 }
 
 // parseNeighbour takes the result of a show bgp neighbor x.y.w.z
diff --git a/internal/bgp/frr/templates/filters.tmpl b/internal/bgp/frr/templates/filters.tmpl
index 710d545d..7cf35a07 100644
--- a/internal/bgp/frr/templates/filters.tmpl
+++ b/internal/bgp/frr/templates/filters.tmpl
@@ -40,6 +40,12 @@ route-map {{$.neighbor.ID}}-out permit {{counter $.neighbor.ID}}
 route-map {{$.neighbor.ID}}-out permit {{counter $.neighbor.ID}}
   match ipv6 address prefix-list {{allowedPrefixList $.neighbor}}
 
+{{/* If the neighbor does not have an advertisement, we need to add a prefix to deny
+for when we have a prefix but a given peer is not selected for any prefixes */}}
+{{- if not .neighbor.HasV4Advertisements}}
 ip prefix-list {{allowedPrefixList $.neighbor }} deny any
+{{- end }}
+{{- if not .neighbor.HasV6Advertisements}}
 ipv6 prefix-list {{allowedPrefixList $.neighbor}} deny any
 {{- end -}}
+{{- end -}}
diff --git a/internal/config/config.go b/internal/config/config.go
index 8c63c4a3..dfd2600f 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -50,7 +50,7 @@ type ClusterResources struct {
 // Config is a parsed MetalLB configuration.
 type Config struct {
 	// Routers that MetalLB should peer with.
-	Peers []*Peer
+	Peers map[string]*Peer
 	// Address pools from which to allocate load balancer IPs.
 	Pools *Pools
 	// BFD profiles that can be used by peers.
@@ -156,6 +156,8 @@ type ServiceAllocation struct {
 
 // BGPAdvertisement describes one translation from an IP address to a BGP advertisement.
 type BGPAdvertisement struct {
+	// The name of the advertisement
+	Name string
 	// Roll up the IP address into a CIDR prefix of this
 	// length. Optional, defaults to 32 (i.e. no aggregation) if not
 	// specified.
@@ -224,6 +226,10 @@ func For(resources ClusterResources, validate Validate) (*Config, error) {
 		return nil, err
 	}
 
+	err = validateConfig(cfg)
+	if err != nil {
+		return nil, err
+	}
 	return cfg, nil
 }
 
@@ -242,8 +248,8 @@ func bfdProfilesFor(resources ClusterResources) (map[string]*BFDProfile, error)
 	return res, nil
 }
 
-func peersFor(resources ClusterResources, BFDProfiles map[string]*BFDProfile) ([]*Peer, error) {
-	var res []*Peer
+func peersFor(resources ClusterResources, BFDProfiles map[string]*BFDProfile) (map[string]*Peer, error) {
+	var res map[string]*Peer = make(map[string]*Peer)
 	for _, p := range resources.Peers {
 		peer, err := peerFromCR(p, resources.PasswordSecrets)
 		if err != nil {
@@ -262,7 +268,7 @@ func peersFor(resources ClusterResources, BFDProfiles map[string]*BFDProfile) ([
 				return nil, fmt.Errorf("peer %s already exists", p.Name)
 			}
 		}
-		res = append(res, peer)
+		res[peer.Name] = peer
 	}
 	return res, nil
 }
@@ -774,6 +780,7 @@ func bgpAdvertisementFromCR(crdAd metallbv1beta1.BGPAdvertisement, communities m
 	}
 
 	ad := &BGPAdvertisement{
+		Name:                crdAd.Name,
 		AggregationLength:   32,
 		AggregationLengthV6: 128,
 		LocalPref:           0,
diff --git a/internal/config/validation.go b/internal/config/validation.go
index 982c9633..84be177d 100644
--- a/internal/config/validation.go
+++ b/internal/config/validation.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/pkg/errors"
 	metallbv1beta2 "go.universe.tf/metallb/api/v1beta2"
+	"go.universe.tf/metallb/internal/ipfamily"
 )
 
 type Validate func(ClusterResources) error
@@ -96,6 +97,52 @@ func DiscardNativeOnly(c ClusterResources) error {
 	return nil
 }
 
+// validateConfig is meant to validate all the inter-dependencies of a parsed configuration.
+// In this case, we ensure that bfd echo is not enabled on a v6 pool.
+func validateConfig(cfg *Config) error {
+	for _, p := range cfg.Pools.ByName {
+		containsV6 := false
+		for _, cidr := range p.CIDR {
+			if ipfamily.ForCIDR(cidr) == ipfamily.IPv6 {
+				containsV6 = true
+				break
+			}
+		}
+		if !containsV6 { // we only care about v6 advertisements
+			continue
+		}
+		for _, a := range p.BGPAdvertisements {
+			if len(a.Peers) == 0 { // all peers
+				for _, peer := range cfg.Peers {
+					if hasBFDEcho(peer, cfg.BFDProfiles) {
+						return fmt.Errorf("pool %s has bgpadvertisement %s which references peer %s which has bfd echo enabled, which is not possible", p.Name, a.Name, peer.Name)
+					}
+				}
+				continue
+			}
+			for _, peerName := range a.Peers {
+				if peer, ok := cfg.Peers[peerName]; ok {
+					if hasBFDEcho(peer, cfg.BFDProfiles) {
+						return fmt.Errorf("pool %s has bgpadvertisement %s which references peer %s which has bfd echo enabled, which is not possible", p.Name, a.Name, peer.Name)
+					}
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func hasBFDEcho(peer *Peer, bfdProfiles map[string]*BFDProfile) bool {
+	profile, ok := bfdProfiles[peer.BFDProfile]
+	if !ok {
+		return false
+	}
+	if profile.EchoMode {
+		return true
+	}
+	return false
+}
+
 func peerAddressKey(peer metallbv1beta2.BGPPeerSpec) string {
 	return fmt.Sprintf("%s-%s", peer.Address, peer.VRFName)
 }
diff --git a/internal/k8s/controllers/dump.go b/internal/k8s/controllers/dump.go
index 4836b2fd..c9b55d6a 100644
--- a/internal/k8s/controllers/dump.go
+++ b/internal/k8s/controllers/dump.go
@@ -32,11 +32,11 @@ func dumpClusterResources(c *config.ClusterResources) string {
 
 func dumpConfig(cfg *config.Config) string {
 	toDump := *cfg
-	toDump.Peers = make([]*config.Peer, 0)
+	toDump.Peers = make(map[string]*config.Peer, 0)
 	for _, p := range cfg.Peers {
 		p1 := *p
 		p1.Password = "<retracted>"
-		toDump.Peers = append(toDump.Peers, &p1)
+		toDump.Peers[p.Name] = &p1
 	}
 	return spew.Sdump(toDump)
 }
diff --git a/tasks.py b/tasks.py
index 6ec130d8..bcb8ee4e 100644
--- a/tasks.py
+++ b/tasks.py
@@ -485,7 +485,7 @@ def bgp_dev_env(ip_family, frr_volume_dir):
         '    docker rm -f $frr ; '
         'done', echo=True)
     run("docker run -d --privileged --network kind --rm --ulimit core=-1 --name frr --volume %s:/etc/frr "
-            "quay.io/frrouting/frr:7.5.1" % frr_volume_dir, echo=True)
+            "quay.io/frrouting/frr:8.4.2" % frr_volume_dir, echo=True)
 
     if ip_family == "ipv4":
         peer_address = run('docker inspect -f "{{ '
@@ -515,20 +515,16 @@ def get_available_ips(ip_family=None):
             used_list = v4 if ip_family == 4 else v6
             last_used = ipaddress.ip_interface(used_list[-1])
 
-            for_containers = []
-            for i in range(2):
-                last_used = last_used + 1
-                for_containers.append(str(last_used))
-
-            # try to get 10 IP addresses after the last assigned node address in the kind network subnet
+            # try to get 10 IP addresses after the last assigned node address in the kind network subnet,
+            # plus we give room to thr frr single hop containers.
             # if failed, just quit (recreate kind cluster might solve the situation)
-            service_ip_range_start = last_used + 1
-            service_ip_range_end = last_used + 11
+            service_ip_range_start = last_used + 5
+            service_ip_range_end = last_used + 15
             if service_ip_range_start not in network:
                 raise Exit(message='network range %s is not in %s' % (service_ip_range_start, network))
             if service_ip_range_end not in network:
                 raise Exit(message='network range %s is not in %s' % (service_ip_range_end, network))
-            return '%s-%s' % (service_ip_range_start.ip, service_ip_range_end.ip), ','.join(for_containers)
+            return '%s-%s' % (service_ip_range_start.ip, service_ip_range_end.ip)
 
 @task(help={
     "name": "name of the kind cluster to delete.",
@@ -784,15 +780,11 @@ def e2etest(ctx, name="kind", export=None, kubeconfig=None, system_namespaces="k
     if local_nics == "kind":
         local_nics = _get_local_nics()
 
-    ips_for_containers_v4 = ""
     if ipv4_service_range is None:
-        ipv4_service_range, ips = get_available_ips(4)
-        ips_for_containers_v4 = "--ips-for-containers-v4=" + ips
+        ipv4_service_range = get_available_ips(4)
 
-    ips_for_containers_v6 = ""
     if ipv6_service_range is None:
-        ipv6_service_range, ips = get_available_ips(6)
-        ips_for_containers_v6 = "--ips-for-containers-v6=" + ips
+        ipv6_service_range = get_available_ips(6)
 
     if export != None:
         report_path = export
@@ -809,7 +801,7 @@ def e2etest(ctx, name="kind", export=None, kubeconfig=None, system_namespaces="k
         external_containers = "--external-containers="+(external_containers)
 
     testrun = run("cd `git rev-parse --show-toplevel`/e2etest &&"
-            "KUBECONFIG={} ginkgo --timeout=3h {} {} -- --provider=local --kubeconfig={} --service-pod-port={} {} {} -ipv4-service-range={} -ipv6-service-range={} {} --report-path {} {} -node-nics {} -local-nics {} {}  -bgp-native-mode={}".format(kubeconfig, ginkgo_focus, ginkgo_skip, kubeconfig, service_pod_port, ips_for_containers_v4, ips_for_containers_v6, ipv4_service_range, ipv6_service_range, opt_skip_docker, report_path, prometheus_namespace, node_nics, local_nics, external_containers, native_bgp), warn="True")
+            "KUBECONFIG={} ginkgo --timeout=3h {} {} -- --provider=local --kubeconfig={} --service-pod-port={} -ipv4-service-range={} -ipv6-service-range={} {} --report-path {} {} -node-nics {} -local-nics {} {}  -bgp-native-mode={}".format(kubeconfig, ginkgo_focus, ginkgo_skip, kubeconfig, service_pod_port, ipv4_service_range, ipv6_service_range, opt_skip_docker, report_path, prometheus_namespace, node_nics, local_nics, external_containers, native_bgp), warn="True")
 
     if export != None:
         run("kind export logs {}".format(export))


Test patch:
diff --git a/e2etest/bgptests/bgp.go b/e2etest/bgptests/bgp.go
index 68b2fa10..ac0241d8 100644
--- a/e2etest/bgptests/bgp.go
+++ b/e2etest/bgptests/bgp.go
@@ -538,20 +538,6 @@ var _ = ginkgo.Describe("BGP", func() {
 						MinimumTTL:       pointer.Uint32Ptr(254),
 					},
 				}, ipfamily.IPv6, []string{v6PoolAddresses}, testservice.TrafficPolicyCluster),
-			ginkgo.Entry("IPV6 - echo mode enabled",
-				metallbv1beta1.BFDProfile{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "echo",
-					},
-					Spec: metallbv1beta1.BFDProfileSpec{
-						ReceiveInterval:  pointer.Uint32Ptr(80),
-						TransmitInterval: pointer.Uint32Ptr(81),
-						EchoInterval:     pointer.Uint32Ptr(82),
-						EchoMode:         pointer.BoolPtr(true),
-						PassiveMode:      pointer.BoolPtr(false),
-						MinimumTTL:       pointer.Uint32Ptr(254),
-					},
-				}, ipfamily.IPv6, []string{v6PoolAddresses}, testservice.TrafficPolicyCluster),
 			ginkgo.Entry("DUALSTACK - full params",
 				metallbv1beta1.BFDProfile{
 					ObjectMeta: metav1.ObjectMeta{
@@ -1169,7 +1155,7 @@ var _ = ginkgo.Describe("BGP", func() {
 						WithTransform(substringCount("\n profile fullbfdprofile1"), Equal(1)),
 						ContainSubstring("receive-interval 93"),
 						ContainSubstring("transmit-interval 95"),
-						ContainSubstring("echo-interval 97"),
+						MatchRegexp("echo.*interval 97"), // TODO: this is backward compatible to 7.5, let's remove it when we consolidate the frr version
 						ContainSubstring("minimum-ttl 253"),
 						ContainSubstring("passive-mode"),
 						ContainSubstring("echo-mode"),
diff --git a/e2etest/bgptests/infra_setup.go b/e2etest/bgptests/infra_setup.go
index 176345da..b37c0934 100644
--- a/e2etest/bgptests/infra_setup.go
+++ b/e2etest/bgptests/infra_setup.go
@@ -7,7 +7,6 @@ import (
 	"os"
 	"strings"
 
-	. "github.com/onsi/gomega"
 	"github.com/pkg/errors"
 	"go.universe.tf/metallb/e2etest/pkg/container"
 	"go.universe.tf/metallb/e2etest/pkg/executor"
@@ -123,11 +122,8 @@ func HostContainerSetup() ([]*frrcontainer.FRR, error) {
 	See `e2etest/README.md` for more details.
 */
 
-func KindnetContainersSetup(ipv4Addresses, ipv6Addresses []string, cs *clientset.Clientset) ([]*frrcontainer.FRR, error) {
-	Expect(len(ipv4Addresses)).Should(BeNumerically(">=", 2))
-	Expect(len(ipv6Addresses)).Should(BeNumerically(">=", 2))
-
-	configs := frrContainersConfigs(ipv4Addresses, ipv6Addresses)
+func KindnetContainersSetup(cs *clientset.Clientset) ([]*frrcontainer.FRR, error) {
+	configs := frrContainersConfigs()
 
 	var out string
 	out, err := executor.Host.Exec(executor.ContainerRuntime, "network", "create", defaultNextHopSettings.multiHopNetwork, "--ipv6",
@@ -282,12 +278,6 @@ func vrfSetup(cs *clientset.Clientset) error {
 		if err != nil {
 			return err
 		}
-		// this is required to allow frr to listen for udp connections
-		// in vrfs, in particular to allow bfd to work
-		err = enableL3masterDomains(pod.Spec.NodeName)
-		if err != nil {
-			return err
-		}
 	}
 	return nil
 }
@@ -368,7 +358,7 @@ func hostnetContainerConfig() map[string]frrcontainer.Config {
 	return res
 }
 
-func frrContainersConfigs(ipv4Addresses, ipv6Addresses []string) map[string]frrcontainer.Config {
+func frrContainersConfigs() map[string]frrcontainer.Config {
 	res := make(map[string]frrcontainer.Config)
 	res["ibgp-single-hop"] = frrcontainer.Config{
 		Name: "ibgp-single-hop",
@@ -382,11 +372,9 @@ func frrContainersConfigs(ipv4Addresses, ipv6Addresses []string) map[string]frrc
 			BGPPort:  179,
 			Password: "ibgp-test",
 		},
-		Network:     kindNetwork,
-		HostIPv4:    hostIPv4,
-		HostIPv6:    hostIPv6,
-		IPv4Address: ipv4Addresses[0],
-		IPv6Address: ipv6Addresses[0],
+		Network:  kindNetwork,
+		HostIPv4: hostIPv4,
+		HostIPv6: hostIPv6,
 	}
 	res["ibgp-multi-hop"] = frrcontainer.Config{
 		Name: "ibgp-multi-hop",
@@ -431,8 +419,6 @@ func frrContainersConfigs(ipv4Addresses, ipv6Addresses []string) map[string]frrc
 			ASN:     externalASN,
 			BGPPort: 179,
 		},
-		IPv4Address: ipv4Addresses[0],
-		IPv6Address: ipv6Addresses[0],
 	}
 	return res
 }
@@ -606,16 +592,6 @@ func addContainerToNetwork(containerName, network string) error {
 	return nil
 }
 
-func enableL3masterDomains(container string) error {
-	nodeExec := executor.ForContainer(container)
-	_, err := nodeExec.Exec("sysctl", "net.ipv4.udp_l3mdev_accept=1")
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
 func isVRFContainer(c *frrcontainer.FRR) bool {
 	return strings.Contains(c.Name, "vrf")
 }
diff --git a/e2etest/bgptests/metrics.go b/e2etest/bgptests/metrics.go
index 87a1fe23..847f3041 100644
--- a/e2etest/bgptests/metrics.go
+++ b/e2etest/bgptests/metrics.go
@@ -137,7 +137,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 						return err
 					}
 					for _, selector := range selectors {
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(0), "metallb_bgp_opens_sent", selector.labelsBGP, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(0), "metallb_bgp_opens_sent", selector.labelsBGP, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -147,7 +147,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(0), "metallb_bgp_opens_received", selector.labelsBGP, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(0), "metallb_bgp_opens_received", selector.labelsBGP, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -157,7 +157,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_bgp_updates_total_received", selector.labelsBGP, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_bgp_updates_total_received", selector.labelsBGP, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -167,7 +167,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(0), "metallb_bgp_keepalives_sent", selector.labelsBGP, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(0), "metallb_bgp_keepalives_sent", selector.labelsBGP, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -177,7 +177,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(0), "metallb_bgp_keepalives_received", selector.labelsBGP, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(0), "metallb_bgp_keepalives_received", selector.labelsBGP, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -187,7 +187,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(0), "metallb_bgp_route_refresh_sent", selector.labelsBGP, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(0), "metallb_bgp_route_refresh_sent", selector.labelsBGP, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -197,7 +197,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(0), "metallb_bgp_total_sent", selector.labelsBGP, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(0), "metallb_bgp_total_sent", selector.labelsBGP, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -207,7 +207,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(0), "metallb_bgp_total_received", selector.labelsBGP, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(0), "metallb_bgp_total_received", selector.labelsBGP, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -370,7 +370,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_bgp_updates_total", map[string]string{"peer": addr}, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_bgp_updates_total", map[string]string{"peer": addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -452,7 +452,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 						return err
 					}
 
-					err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_bfd_control_packet_input", selector.labelsBFD, speakerMetrics)
+					err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_bfd_control_packet_input", selector.labelsBFD, speakerMetrics)
 					if err != nil {
 						return err
 					}
@@ -461,7 +461,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 						return err
 					}
 
-					err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_bfd_control_packet_output", selector.labelsBFD, speakerMetrics)
+					err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_bfd_control_packet_output", selector.labelsBFD, speakerMetrics)
 					if err != nil {
 						return err
 					}
@@ -470,16 +470,16 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 						return err
 					}
 
-					err = metrics.ValidateGaugeValue(0, "metallb_bfd_session_down_events", selector.labelsBFD, speakerMetrics)
+					err = metrics.ValidateGaugeValueCompare(metrics.GreaterOrEqualThan(0), "metallb_bfd_session_down_events", selector.labelsBFD, speakerMetrics)
 					if err != nil {
 						return err
 					}
-					err = metrics.ValidateOnPrometheus(promPod, fmt.Sprintf(`metallb_bfd_session_down_events{%s} == 0`, selector.labelsForQueryBFD), metrics.There)
+					err = metrics.ValidateOnPrometheus(promPod, fmt.Sprintf(`metallb_bfd_session_down_events{%s} >= 0`, selector.labelsForQueryBFD), metrics.There)
 					if err != nil {
 						return err
 					}
 
-					err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_bfd_session_up_events", selector.labelsBFD, speakerMetrics)
+					err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_bfd_session_up_events", selector.labelsBFD, speakerMetrics)
 					if err != nil {
 						return err
 					}
@@ -488,7 +488,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 						return err
 					}
 
-					err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_bfd_zebra_notifications", selector.labelsBFD, speakerMetrics)
+					err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_bfd_zebra_notifications", selector.labelsBFD, speakerMetrics)
 					if err != nil {
 						return err
 					}
@@ -502,7 +502,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 						if selector.noEcho {
 							echoVal = 0
 						}
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(echoVal), "metallb_bfd_echo_packet_input", selector.labelsBFD, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(echoVal), "metallb_bfd_echo_packet_input", selector.labelsBFD, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -511,7 +511,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 							return err
 						}
 
-						err = metrics.ValidateCounterValue(metrics.GreaterThan(echoVal), "metallb_bfd_echo_packet_output", selector.labelsBFD, speakerMetrics)
+						err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(echoVal), "metallb_bfd_echo_packet_output", selector.labelsBFD, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -553,7 +553,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 						return err
 					}
 
-					err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_bfd_session_down_events", selector.labelsBFD, speakerMetrics)
+					err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_bfd_session_down_events", selector.labelsBFD, speakerMetrics)
 					if err != nil {
 						return err
 					}
@@ -592,20 +592,6 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 					Name: "bar",
 				},
 			}, ipfamily.IPv6, []string{v6PoolAddresses}),
-		ginkgo.Entry("IPV6 - echo mode enabled",
-			metallbv1beta1.BFDProfile{
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "echo",
-				},
-				Spec: metallbv1beta1.BFDProfileSpec{
-					ReceiveInterval:  pointer.Uint32Ptr(80),
-					TransmitInterval: pointer.Uint32Ptr(81),
-					EchoInterval:     pointer.Uint32Ptr(82),
-					EchoMode:         pointer.BoolPtr(true),
-					PassiveMode:      pointer.BoolPtr(false),
-					MinimumTTL:       pointer.Uint32Ptr(254),
-				},
-			}, ipfamily.IPv6, []string{v6PoolAddresses}),
 		ginkgo.Entry("DUALSTACK - full params",
 			metallbv1beta1.BFDProfile{
 				ObjectMeta: metav1.ObjectMeta{
@@ -693,7 +679,7 @@ var _ = ginkgo.Describe("BGP metrics", func() {
 					return err
 				}
 				// we don't know how many events we are processing
-				err = metrics.ValidateCounterValue(metrics.GreaterThan(0), "metallb_k8s_client_updates_total", map[string]string{}, podMetrics)
+				err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(0), "metallb_k8s_client_updates_total", map[string]string{}, podMetrics)
 				if err != nil {
 					return err
 				}
diff --git a/e2etest/bgptests/validate.go b/e2etest/bgptests/validate.go
index 4f31ae84..060078ca 100644
--- a/e2etest/bgptests/validate.go
+++ b/e2etest/bgptests/validate.go
@@ -163,8 +163,8 @@ func checkBFDConfigPropagated(nodeConfig metallbv1beta1.BFDProfile, peerConfig b
 	if peerConfig.RemoteTransmitInterval != int(*nodeConfig.Spec.TransmitInterval) {
 		return fmt.Errorf("RemoteTransmitInterval: expecting %d, got %d", *nodeConfig.Spec.TransmitInterval, peerConfig.RemoteTransmitInterval)
 	}
-	if peerConfig.RemoteEchoInterval != int(*nodeConfig.Spec.EchoInterval) {
-		return fmt.Errorf("EchoInterval: expecting %d, got %d", *nodeConfig.Spec.EchoInterval, peerConfig.RemoteEchoInterval)
+	if peerConfig.RemoteEchoReceiveInterval != int(*nodeConfig.Spec.EchoInterval) {
+		return fmt.Errorf("EchoInterval: expecting %d, got %d", *nodeConfig.Spec.EchoInterval, peerConfig.RemoteEchoReceiveInterval)
 	}
 	return nil
 }
diff --git a/e2etest/e2etest_suite_test.go b/e2etest/e2etest_suite_test.go
index dcb4acce..d801a9ec 100644
--- a/e2etest/e2etest_suite_test.go
+++ b/e2etest/e2etest_suite_test.go
@@ -45,8 +45,6 @@ import (
 
 var (
 	skipDockerCmd       bool
-	ipv4ForContainers   string
-	ipv6ForContainers   string
 	useOperator         bool
 	reportPath          string
 	updater             testsconfig.Updater
@@ -75,8 +73,6 @@ func handleFlags() {
 
 	flag.IntVar(&service.TestServicePort, "service-pod-port", 80, "port number that pod opens, default: 80")
 	flag.BoolVar(&skipDockerCmd, "skip-docker", false, "set this to true if the BGP daemon is running on the host instead of in a container")
-	flag.StringVar(&ipv4ForContainers, "ips-for-containers-v4", "0", "a comma separated list of IPv4 addresses available for containers")
-	flag.StringVar(&ipv6ForContainers, "ips-for-containers-v6", "0", "a comma separated list of IPv6 addresses available for containers")
 	flag.StringVar(&l2tests.IPV4ServiceRange, "ipv4-service-range", "0", "a range of IPv4 addresses for MetalLB to use when running in layer2 mode")
 	flag.StringVar(&l2tests.IPV6ServiceRange, "ipv6-service-range", "0", "a range of IPv6 addresses for MetalLB to use when running in layer2 mode")
 	flag.StringVar(&nodeNics, "node-nics", "", "node's interfaces list separated by comma and used when running in interface selector")
@@ -130,9 +126,6 @@ var _ = ginkgo.BeforeSuite(func() {
 	cs, err := framework.LoadClientset()
 	framework.ExpectNoError(err)
 
-	v4Addresses := strings.Split(ipv4ForContainers, ",")
-	v6Addresses := strings.Split(ipv6ForContainers, ",")
-
 	switch {
 	case externalContainers != "":
 		bgptests.FRRContainers, err = bgptests.ExternalContainersSetup(externalContainers, cs)
@@ -141,7 +134,7 @@ var _ = ginkgo.BeforeSuite(func() {
 		bgptests.FRRContainers, err = bgptests.HostContainerSetup()
 		framework.ExpectNoError(err)
 	default:
-		bgptests.FRRContainers, err = bgptests.KindnetContainersSetup(v4Addresses, v6Addresses, cs)
+		bgptests.FRRContainers, err = bgptests.KindnetContainersSetup(cs)
 		framework.ExpectNoError(err)
 		if !bgpNativeMode {
 			vrfFRRContainers, err := bgptests.VRFContainersSetup(cs)
diff --git a/e2etest/l2tests/l2.go b/e2etest/l2tests/l2.go
index d7835ac9..0913cfcf 100644
--- a/e2etest/l2tests/l2.go
+++ b/e2etest/l2tests/l2.go
@@ -553,7 +553,7 @@ var _ = ginkgo.Describe("L2", func() {
 					return err
 				}
 
-				err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_layer2_requests_received", map[string]string{"ip": ingressIP}, speakerMetrics)
+				err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_layer2_requests_received", map[string]string{"ip": ingressIP}, speakerMetrics)
 				if err != nil {
 					return err
 				}
@@ -563,7 +563,7 @@ var _ = ginkgo.Describe("L2", func() {
 					return err
 				}
 
-				err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_layer2_responses_sent", map[string]string{"ip": ingressIP}, speakerMetrics)
+				err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_layer2_responses_sent", map[string]string{"ip": ingressIP}, speakerMetrics)
 				if err != nil {
 					return err
 				}
@@ -573,7 +573,7 @@ var _ = ginkgo.Describe("L2", func() {
 					return err
 				}
 
-				err = metrics.ValidateCounterValue(metrics.GreaterThan(1), "metallb_layer2_gratuitous_sent", map[string]string{"ip": ingressIP}, speakerMetrics)
+				err = metrics.ValidateCounterValue(metrics.GreaterOrEqualThan(1), "metallb_layer2_gratuitous_sent", map[string]string{"ip": ingressIP}, speakerMetrics)
 				if err != nil {
 					return err
 				}
diff --git a/e2etest/pkg/frr/container/container.go b/e2etest/pkg/frr/container/container.go
index 896d6e99..6b1634af 100644
--- a/e2etest/pkg/frr/container/container.go
+++ b/e2etest/pkg/frr/container/container.go
@@ -27,7 +27,7 @@ const (
 	// BGP configuration directory.
 	frrConfigDir = "config/frr"
 	// FRR routing image.
-	frrImage = "quay.io/frrouting/frr:7.5.1"
+	frrImage = "quay.io/frrouting/frr:8.4.2"
 	// Host network name.
 	hostNetwork = "host"
 	// FRR container mount destination path.
@@ -47,14 +47,12 @@ type FRR struct {
 }
 
 type Config struct {
-	Name        string
-	Neighbor    config.NeighborConfig
-	Router      config.RouterConfig
-	HostIPv4    string
-	HostIPv6    string
-	IPv4Address string
-	IPv6Address string
-	Network     string
+	Name     string
+	Neighbor config.NeighborConfig
+	Router   config.RouterConfig
+	HostIPv4 string
+	HostIPv6 string
+	Network  string
 }
 
 // Create creates a set of frr containers corresponding to the given configurations.
@@ -174,12 +172,6 @@ func start(cfg Config) (*FRR, error) {
 
 	volume := fmt.Sprintf("%s:%s", testDirName, frrMountPath)
 	args := []string{"run", "-d", "--privileged", "--network", cfg.Network, "--rm", "--ulimit", "core=-1", "--name", cfg.Name, "--volume", volume, frrImage}
-	if cfg.IPv4Address != "" {
-		args = append(args, "--ip", cfg.IPv4Address)
-	}
-	if cfg.IPv6Address != "" {
-		args = append(args, "--ip", cfg.IPv6Address)
-	}
 	out, err := exec.Command(executor.ContainerRuntime, args...).CombinedOutput()
 	if err != nil {
 		return nil, errors.Wrapf(err, "Failed to start %s container. %s", cfg.Name, out)
diff --git a/e2etest/pkg/metrics/metrics.go b/e2etest/pkg/metrics/metrics.go
index 4d7f319b..07d2c519 100644
--- a/e2etest/pkg/metrics/metrics.go
+++ b/e2etest/pkg/metrics/metrics.go
@@ -75,14 +75,25 @@ func GaugeForLabels(metricName string, labels map[string]string, metrics map[str
 
 // ValidateGaugeValue checks that the value corresponing to the given metric is the same as expected value.
 func ValidateGaugeValue(expectedValue int, metricName string, labels map[string]string, allMetrics []map[string]*dto.MetricFamily) error {
+	return ValidateGaugeValueCompare(func(value int) error {
+		if value != expectedValue {
+			return fmt.Errorf("expecting %d, got %d", expectedValue, value)
+		}
+		return nil
+	}, metricName, labels, allMetrics)
+}
+
+// ValidateGaugeValueCompare checks that the value corresponing to the given metric against the given compare function.
+func ValidateGaugeValueCompare(check func(int) error, metricName string, labels map[string]string, allMetrics []map[string]*dto.MetricFamily) error {
 	found := false
 	for _, m := range allMetrics {
 		value, err := GaugeForLabels(metricName, labels, m)
 		if err != nil {
 			continue
 		}
-		if value != expectedValue {
-			return fmt.Errorf("invalid value %d for %s, expecting %d", value, metricName, expectedValue)
+		err = check(value)
+		if err != nil {
+			return fmt.Errorf("invalid value %d for %s, %w", value, metricName, err)
 		}
 		found = true
 	}
@@ -124,7 +135,7 @@ func CounterForLabels(metricName string, labels map[string]string, metrics map[s
 	})
 }
 
-func GreaterThan(min int) func(value int) error {
+func GreaterOrEqualThan(min int) func(value int) error {
 	return func(value int) error {
 		if value < min {
 			return fmt.Errorf("value %d is less than %d", value, min)
diff --git a/internal/bgp/frr/docker_test.go b/internal/bgp/frr/docker_test.go
index c486ce53..17d6a50f 100644
--- a/internal/bgp/frr/docker_test.go
+++ b/internal/bgp/frr/docker_test.go
@@ -22,7 +22,7 @@ var (
 )
 
 const (
-	frrImageTag = "7.5.1"
+	frrImageTag = "8.4.2"
 )
 
 func TestMain(m *testing.M) {
diff --git a/internal/bgp/frr/testdata/TestBFDWithSession.golden b/internal/bgp/frr/testdata/TestBFDWithSession.golden
index d57be20b..2607f5ea 100644
--- a/internal/bgp/frr/testdata/TestBFDWithSession.golden
+++ b/internal/bgp/frr/testdata/TestBFDWithSession.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisement.golden b/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
index 7377ab2d..0f9917cc 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
@@ -28,7 +28,7 @@ route-map 10.2.2.254-out permit 4
 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
 
 router bgp 100
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
index f3e8441e..9902a985 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
@@ -13,7 +13,7 @@ route-map 10.2.2.254-out permit 1
 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
 
 router bgp 100
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden
index 03bfdae7..3db3b494 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden
@@ -13,7 +13,7 @@ route-map 10.2.2.254-red-out permit 1
 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
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
index 6aaa610e..4aa7a366 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
@@ -13,7 +13,7 @@ route-map 10.2.2.254-out permit 1
 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
 
 router bgp 100
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementNonExistingPeer.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementNonExistingPeer.golden
index bc8b004f..025535d0 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementNonExistingPeer.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementNonExistingPeer.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
index bc8b004f..025535d0 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden
index aa3e5db6..ed618c0c 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden
@@ -28,7 +28,7 @@ route-map 10.2.2.254-red-out permit 4
 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
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelector.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelector.golden
index 7377ab2d..0f9917cc 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelector.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelector.golden
@@ -28,7 +28,7 @@ route-map 10.2.2.254-out permit 4
 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
 
 router bgp 100
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden
index aa3e5db6..ed618c0c 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden
@@ -28,7 +28,7 @@ route-map 10.2.2.254-red-out permit 4
 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
diff --git a/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden b/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
index bc8b004f..025535d0 100644
--- a/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
+++ b/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden b/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden
index 96309ff1..ade9336c 100644
--- a/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden
+++ b/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden
@@ -10,6 +10,7 @@ route-map 127.0.0.2-out permit 1
 route-map 127.0.0.2-out permit 2
   match ipv6 address prefix-list 127.0.0.2-pl-ipv4
 
+
 ip prefix-list 127.0.0.2-pl-ipv4 deny any
 ipv6 prefix-list 127.0.0.2-pl-ipv4 deny any
 
diff --git a/internal/bgp/frr/testdata/TestSingleIBGPSession.golden b/internal/bgp/frr/testdata/TestSingleIBGPSession.golden
index 42355e39..9a6e241e 100644
--- a/internal/bgp/frr/testdata/TestSingleIBGPSession.golden
+++ b/internal/bgp/frr/testdata/TestSingleIBGPSession.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestSingleIPv6EBGPSessionOneHop.golden b/internal/bgp/frr/testdata/TestSingleIPv6EBGPSessionOneHop.golden
index b5b930e9..8e625ad1 100644
--- a/internal/bgp/frr/testdata/TestSingleIPv6EBGPSessionOneHop.golden
+++ b/internal/bgp/frr/testdata/TestSingleIPv6EBGPSessionOneHop.golden
@@ -10,6 +10,7 @@ route-map 127:0:0::2-out permit 1
 route-map 127:0:0::2-out permit 2
   match ipv6 address prefix-list 127:0:0::2-pl-ipv6
 
+
 ip prefix-list 127:0:0::2-pl-ipv6 deny any
 ipv6 prefix-list 127:0:0::2-pl-ipv6 deny any
 
diff --git a/internal/bgp/frr/testdata/TestSingleIPv6IBGPSession.golden b/internal/bgp/frr/testdata/TestSingleIPv6IBGPSession.golden
index 6917aafd..7bbef022 100644
--- a/internal/bgp/frr/testdata/TestSingleIPv6IBGPSession.golden
+++ b/internal/bgp/frr/testdata/TestSingleIPv6IBGPSession.golden
@@ -10,6 +10,7 @@ route-map 10:2:2::254-out permit 1
 route-map 10:2:2::254-out permit 2
   match ipv6 address prefix-list 10:2:2::254-pl-ipv6
 
+
 ip prefix-list 10:2:2::254-pl-ipv6 deny any
 ipv6 prefix-list 10:2:2::254-pl-ipv6 deny any
 
diff --git a/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden b/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden
index 32dfd564..bf67975b 100644
--- a/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden
+++ b/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-red-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisements.golden b/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
index 091f7c41..35808ed0 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
@@ -21,7 +21,7 @@ route-map 10.2.2.254-out permit 2
 route-map 10.2.2.254-out permit 3
   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
 
 router bgp 100
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessions.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessions.golden
index 1a1c625d..4f2ca952 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessions.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessions.golden
@@ -31,7 +31,7 @@ route-map 10.2.2.254-out permit 4
 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-in deny 20
 
@@ -61,7 +61,7 @@ route-map 10.2.2.255-out permit 4
 route-map 10.2.2.255-out permit 5
   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
 
 router bgp 100
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden
index 1adf4629..a25e4b91 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden
@@ -31,7 +31,7 @@ route-map 10.2.2.254-out permit 4
 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
 
@@ -61,7 +61,7 @@ route-map 10.2.2.255-red-out permit 4
 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
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelector.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelector.golden
index 15ffad86..9c4745ef 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelector.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelector.golden
@@ -31,7 +31,7 @@ route-map 10.2.2.254-out permit 4
 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-in deny 20
 
@@ -53,7 +53,7 @@ route-map 10.2.2.255-out permit 3
 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
 
 router bgp 100
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden
index 385efca0..334a1575 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden
@@ -23,7 +23,7 @@ route-map 10.2.2.255-out permit 3
 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
 
@@ -53,7 +53,7 @@ route-map 10.2.2.254-red-out permit 4
 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
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden
index a3ff4796..395a50e9 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden
@@ -23,7 +23,7 @@ route-map 10.2.2.255-blue-out permit 3
 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
 
@@ -40,7 +40,7 @@ route-map 10.2.2.254-red-out permit 2
 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
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden
index 2214b0e8..4da8638f 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden
@@ -21,7 +21,7 @@ route-map 10.2.2.254-red-out permit 2
 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
diff --git a/internal/bgp/frr/testdata/TestTwoIPv6Sessions.golden b/internal/bgp/frr/testdata/TestTwoIPv6Sessions.golden
index 5ff5d78a..81d4f7d8 100644
--- a/internal/bgp/frr/testdata/TestTwoIPv6Sessions.golden
+++ b/internal/bgp/frr/testdata/TestTwoIPv6Sessions.golden
@@ -10,6 +10,7 @@ route-map 10:2:2::254-out permit 1
 route-map 10:2:2::254-out permit 2
   match ipv6 address prefix-list 10:2:2::254-pl-ipv6
 
+
 ip prefix-list 10:2:2::254-pl-ipv6 deny any
 ipv6 prefix-list 10:2:2::254-pl-ipv6 deny any
 route-map 10:4:4::255-in deny 20
@@ -19,6 +20,7 @@ route-map 10:4:4::255-out permit 1
 route-map 10:4:4::255-out permit 2
   match ipv6 address prefix-list 10:4:4::255-pl-ipv6
 
+
 ip prefix-list 10:4:4::255-pl-ipv6 deny any
 ipv6 prefix-list 10:4:4::255-pl-ipv6 deny any
 
diff --git a/internal/bgp/frr/testdata/TestTwoSessions.golden b/internal/bgp/frr/testdata/TestTwoSessions.golden
index f50a1ee4..d06b0f0b 100644
--- a/internal/bgp/frr/testdata/TestTwoSessions.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessions.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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-in deny 20
@@ -19,6 +20,7 @@ route-map 10.4.4.255-out permit 1
 route-map 10.4.4.255-out permit 2
   match ipv6 address prefix-list 10.4.4.255-pl-ipv4
 
+
 ip prefix-list 10.4.4.255-pl-ipv4 deny any
 ipv6 prefix-list 10.4.4.255-pl-ipv4 deny any
 
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden b/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
index bc8b004f..025535d0 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden b/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
index ce6bc0b1..a332eeea 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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-in deny 20
@@ -19,6 +20,7 @@ route-map 10.4.4.255-out permit 1
 route-map 10.4.4.255-out permit 2
   match ipv6 address prefix-list 10.4.4.255-pl-ipv4
 
+
 ip prefix-list 10.4.4.255-pl-ipv4 deny any
 ipv6 prefix-list 10.4.4.255-pl-ipv4 deny any
 
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden b/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden
index 6ce01ff6..b515bf70 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
@@ -19,6 +20,7 @@ route-map 10.4.4.255-red-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsSameIPRouterIDASNVRF.golden b/internal/bgp/frr/testdata/TestTwoSessionsSameIPRouterIDASNVRF.golden
index 4b64fbae..1fc2d5e3 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsSameIPRouterIDASNVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsSameIPRouterIDASNVRF.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
@@ -19,6 +20,7 @@ route-map 10.2.2.254-red-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden b/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden
index caef3dfd..921d1eec 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-out permit 1
 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
@@ -19,6 +20,7 @@ route-map 10.2.2.254-red-out permit 1
 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
 
diff --git a/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden b/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden
index bc7d4135..72f5c65a 100644
--- a/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden
+++ b/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden
@@ -10,6 +10,7 @@ route-map 10.2.2.254-red-out permit 1
 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
 
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index fce5c8e4..a1a66ba5 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -56,6 +56,7 @@ func TestParse(t *testing.T) {
 			want: &Config{
 				Pools:       &Pools{ByName: map[string]*Pool{}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 
@@ -217,8 +218,8 @@ func TestParse(t *testing.T) {
 				},
 			},
 			want: &Config{
-				Peers: []*Peer{
-					{
+				Peers: map[string]*Peer{
+					"peer1": {
 						Name:          "peer1",
 						MyASN:         42,
 						ASN:           142,
@@ -232,7 +233,7 @@ func TestParse(t *testing.T) {
 						EBGPMultiHop:  true,
 						VRF:           "foo",
 					},
-					{
+					"peer2": {
 						Name:          "peer2",
 						MyASN:         100,
 						ASN:           200,
@@ -251,6 +252,7 @@ func TestParse(t *testing.T) {
 						AutoAssign:    false,
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv1",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								LocalPref:           100,
@@ -261,6 +263,7 @@ func TestParse(t *testing.T) {
 								Peers: []string{"peer1"},
 							},
 							{
+								Name:                "adv2",
 								AggregationLength:   24,
 								AggregationLengthV6: 64,
 								Communities:         map[uint32]bool{},
@@ -278,6 +281,7 @@ func TestParse(t *testing.T) {
 						AutoAssign: true,
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv3",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								Communities:         map[uint32]bool{},
@@ -408,6 +412,7 @@ func TestParse(t *testing.T) {
 					ByNamespace: map[string][]string{"test-ns1": {"pool1"}, "test-ns2": {"pool2"}},
 				},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 
@@ -458,6 +463,7 @@ func TestParse(t *testing.T) {
 				},
 					ByServiceSelector: []string{"pool1", "pool2"}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 
@@ -526,14 +532,19 @@ func TestParse(t *testing.T) {
 					ByNamespace:       map[string][]string{"test-ns1": {"pool1"}, "test-ns2": {"pool2"}},
 					ByServiceSelector: []string{"pool1", "pool2"}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 
 		{
 			desc: "peer-only",
 			crs: ClusterResources{
+
 				Peers: []v1beta2.BGPPeer{
 					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "peer1",
+						},
 						Spec: v1beta2.BGPPeerSpec{
 							MyASN:   42,
 							ASN:     42,
@@ -543,8 +554,9 @@ func TestParse(t *testing.T) {
 				},
 			},
 			want: &Config{
-				Peers: []*Peer{
-					{
+				Peers: map[string]*Peer{
+					"peer1": {
+						Name:          "peer1",
 						MyASN:         42,
 						ASN:           42,
 						Addr:          net.ParseIP("1.2.3.4"),
@@ -650,6 +662,9 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "peer1",
+						},
 						Spec: v1beta2.BGPPeerSpec{
 							MyASN:   42,
 							ASN:     42,
@@ -659,8 +674,9 @@ func TestParse(t *testing.T) {
 				},
 			},
 			want: &Config{
-				Peers: []*Peer{
-					{
+				Peers: map[string]*Peer{
+					"peer1": {
+						Name:          "peer1",
 						MyASN:         42,
 						ASN:           42,
 						Addr:          net.ParseIP("1.2.3.4"),
@@ -911,6 +927,7 @@ func TestParse(t *testing.T) {
 						CIDR:       []*net.IPNet{ipnet("1.2.3.0/24")},
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv3",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								Communities:         map[uint32]bool{},
@@ -920,6 +937,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -951,6 +969,7 @@ func TestParse(t *testing.T) {
 						CIDR:       []*net.IPNet{ipnet("1.2.3.0/24")},
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv3",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								Communities:         map[uint32]bool{},
@@ -960,6 +979,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -1059,6 +1079,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -1400,6 +1421,9 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "peer1",
+						},
 						Spec: v1beta2.BGPPeerSpec{
 							MyASN:      42,
 							ASN:        42,
@@ -1436,8 +1460,9 @@ func TestParse(t *testing.T) {
 				},
 			},
 			want: &Config{
-				Peers: []*Peer{
-					{
+				Peers: map[string]*Peer{
+					"peer1": {
+						Name:          "peer1",
 						MyASN:         42,
 						ASN:           42,
 						Addr:          net.ParseIP("1.2.3.4"),
@@ -1454,6 +1479,7 @@ func TestParse(t *testing.T) {
 						CIDR:       []*net.IPNet{ipnet("1.2.3.0/24")},
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv3",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								Communities:         map[uint32]bool{},
@@ -1529,6 +1555,9 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "peer1",
+						},
 						Spec: v1beta2.BGPPeerSpec{
 							MyASN:   42,
 							ASN:     42,
@@ -1545,8 +1574,9 @@ func TestParse(t *testing.T) {
 				},
 			},
 			want: &Config{
-				Peers: []*Peer{
-					{
+				Peers: map[string]*Peer{
+					"peer1": {
+						Name:          "peer1",
 						MyASN:         42,
 						ASN:           42,
 						Addr:          net.ParseIP("1.2.3.4"),
@@ -1693,6 +1723,7 @@ func TestParse(t *testing.T) {
 						CIDR:       []*net.IPNet{ipnet("1.2.3.0/24")},
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv3",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								Communities:         map[uint32]bool{},
@@ -1713,6 +1744,7 @@ func TestParse(t *testing.T) {
 						PassiveMode:      true,
 					},
 				},
+				Peers: map[string]*Peer{},
 			},
 		},
 		{
@@ -1747,11 +1779,156 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
+		{
+			desc: "Session with ipv6 and bfd echo mode",
+			crs: ClusterResources{
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "pool1",
+						},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"fc00:f853:0ccd:e793::/64",
+							},
+						},
+					},
+				},
+				BFDProfiles: []v1beta1.BFDProfile{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "nondefault",
+						},
+						Spec: v1beta1.BFDProfileSpec{
+							ReceiveInterval:  pointer.Uint32Ptr(50),
+							TransmitInterval: pointer.Uint32Ptr(51),
+							DetectMultiplier: pointer.Uint32Ptr(52),
+							EchoInterval:     pointer.Uint32Ptr(54),
+							EchoMode:         pointer.BoolPtr(true),
+							PassiveMode:      pointer.BoolPtr(true),
+							MinimumTTL:       pointer.Uint32Ptr(55),
+						},
+					},
+				},
+				BGPAdvs: []v1beta1.BGPAdvertisement{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "adv3",
+						},
+					},
+				},
+				Peers: []v1beta2.BGPPeer{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "peer1",
+						},
+						Spec: v1beta2.BGPPeerSpec{
+							MyASN:        42,
+							ASN:          142,
+							Address:      "1.2.3.4",
+							Port:         1179,
+							HoldTime:     v1.Duration{Duration: 180 * time.Second},
+							RouterID:     "10.20.30.40",
+							SrcAddress:   "10.20.30.40",
+							EBGPMultiHop: true,
+							BFDProfile:   "nondefault",
+						},
+					},
+				},
+			},
+		},
+		{
+			desc: "Session with ipv4 and bfd echo mode",
+			crs: ClusterResources{
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "pool1",
+						},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"1.2.3.0/24",
+							},
+						},
+					},
+				},
+				BFDProfiles: []v1beta1.BFDProfile{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "with-echo",
+						},
+						Spec: v1beta1.BFDProfileSpec{
+							EchoMode: pointer.BoolPtr(true),
+						},
+					},
+				},
+				BGPAdvs: []v1beta1.BGPAdvertisement{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "adv",
+						},
+					},
+				},
+				Peers: []v1beta2.BGPPeer{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "peer1",
+						},
+						Spec: v1beta2.BGPPeerSpec{
+							MyASN:      42,
+							ASN:        142,
+							Address:    "1.2.3.4",
+							Port:       1179,
+							BFDProfile: "with-echo",
+						},
+					},
+				},
+			},
+			want: &Config{
+				Peers: map[string]*Peer{
+					"peer1": {
+						Name:          "peer1",
+						MyASN:         42,
+						ASN:           142,
+						Addr:          net.ParseIP("1.2.3.4"),
+						Port:          1179,
+						HoldTime:      90 * time.Second,
+						KeepaliveTime: 30 * time.Second,
+						NodeSelectors: []labels.Selector{labels.Everything()},
+						BFDProfile:    "with-echo"},
+				},
+				Pools: &Pools{ByName: map[string]*Pool{
+					"pool1": {
+						Name:       "pool1",
+						CIDR:       []*net.IPNet{ipnet("1.2.3.4/24")},
+						AutoAssign: true,
+						BGPAdvertisements: []*BGPAdvertisement{
+							{
+								Name:                "adv",
+								AggregationLength:   32,
+								AggregationLengthV6: 128,
+								Communities:         map[uint32]bool{},
+								Nodes:               map[string]bool{},
+							},
+						},
+					},
+				}},
+				BFDProfiles: map[string]*BFDProfile{
+					"with-echo": {
+						Name:     "with-echo",
+						EchoMode: true,
+					},
+				},
+			},
+		},
 		{
 			desc: "config mixing legacy pools with IP pools",
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "peer1",
+						},
 						Spec: v1beta2.BGPPeerSpec{
 							MyASN:        42,
 							ASN:          142,
@@ -1829,8 +2006,9 @@ func TestParse(t *testing.T) {
 				},
 			},
 			want: &Config{
-				Peers: []*Peer{
-					{
+				Peers: map[string]*Peer{
+					"peer1": {
+						Name:          "peer1",
 						MyASN:         42,
 						ASN:           142,
 						Addr:          net.ParseIP("1.2.3.4"),
@@ -1851,6 +2029,7 @@ func TestParse(t *testing.T) {
 						AutoAssign:    false,
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv1",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								LocalPref:           100,
@@ -1949,6 +2128,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -2084,6 +2264,7 @@ func TestParse(t *testing.T) {
 						AutoAssign: true,
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv1",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								LocalPref:           100,
@@ -2102,6 +2283,7 @@ func TestParse(t *testing.T) {
 						AutoAssign: true,
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv2",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								LocalPref:           200,
@@ -2113,6 +2295,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -2163,6 +2346,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -2381,6 +2565,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -2539,6 +2724,7 @@ func TestParse(t *testing.T) {
 						AutoAssign: true,
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv1",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								Communities:         map[uint32]bool{},
@@ -2558,6 +2744,7 @@ func TestParse(t *testing.T) {
 						AutoAssign: true,
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv2",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								Communities:         map[uint32]bool{},
@@ -2568,6 +2755,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -2784,6 +2972,7 @@ func TestParse(t *testing.T) {
 						AutoAssign: true,
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv1",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								Communities:         map[uint32]bool{},
@@ -2803,6 +2992,7 @@ func TestParse(t *testing.T) {
 					},
 				}},
 				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
 			},
 		},
 		{
@@ -2847,8 +3037,8 @@ func TestParse(t *testing.T) {
 				},
 			},
 			want: &Config{
-				Peers: []*Peer{
-					{
+				Peers: map[string]*Peer{
+					"peer1": {
 						Name:          "peer1",
 						MyASN:         42,
 						ASN:           42,
@@ -2866,6 +3056,7 @@ func TestParse(t *testing.T) {
 						CIDR:       []*net.IPNet{ipnet("1.2.3.0/24")},
 						BGPAdvertisements: []*BGPAdvertisement{
 							{
+								Name:                "adv1",
 								AggregationLength:   32,
 								AggregationLengthV6: 128,
 								LocalPref:           100,
diff --git a/speaker/bgp_controller_test.go b/speaker/bgp_controller_test.go
index 5d4e09bf..b41c92f7 100644
--- a/speaker/bgp_controller_test.go
+++ b/speaker/bgp_controller_test.go
@@ -261,8 +261,8 @@ func TestBGPSpeaker(t *testing.T) {
 		{
 			desc: "One peer, no services",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -569,8 +569,8 @@ func TestBGPSpeaker(t *testing.T) {
 		{
 			desc: "Multiple advertisement config",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -636,8 +636,8 @@ func TestBGPSpeaker(t *testing.T) {
 		{
 			desc: "Multiple advertisement config, one only for my node",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -700,8 +700,8 @@ func TestBGPSpeaker(t *testing.T) {
 		{
 			desc: "Multiple advertisement config, one with peer selector",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Name:          "peer1",
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
@@ -771,12 +771,12 @@ func TestBGPSpeaker(t *testing.T) {
 		{
 			desc: "Multiple peers",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
-					{
+					"peer2": {
 						Addr:          net.ParseIP("1.2.3.5"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -990,8 +990,8 @@ func TestBGPSpeaker(t *testing.T) {
 		{
 			desc: "Delete peer",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.5"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -1132,8 +1132,8 @@ func TestBGPSpeakerEPSlices(t *testing.T) {
 		{
 			desc: "One peer, no services",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -1476,8 +1476,8 @@ func TestBGPSpeakerEPSlices(t *testing.T) {
 		{
 			desc: "Multiple advertisement config",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -1545,12 +1545,12 @@ func TestBGPSpeakerEPSlices(t *testing.T) {
 		{
 			desc: "Multiple peers",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
-					{
+					"peer2": {
 						Addr:          net.ParseIP("1.2.3.5"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -1766,8 +1766,8 @@ func TestBGPSpeakerEPSlices(t *testing.T) {
 		{
 			desc: "Delete peer",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.5"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -1891,8 +1891,8 @@ func TestNodeSelectors(t *testing.T) {
 		{
 			desc: "One peer, default node selector, no node labels",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
@@ -1907,12 +1907,12 @@ func TestNodeSelectors(t *testing.T) {
 		{
 			desc: "Second peer, non-matching node selector",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
-					{
+					"peer2": {
 						Addr: net.ParseIP("2.3.4.5"),
 						NodeSelectors: []labels.Selector{
 							mustSelector("foo=bar"),
@@ -1958,12 +1958,12 @@ func TestNodeSelectors(t *testing.T) {
 		{
 			desc: "Change node selector so it matches again",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
-					{
+					"peer2": {
 						Addr: net.ParseIP("2.3.4.5"),
 						NodeSelectors: []labels.Selector{
 							mustSelector("foo in (bar, baz)"),
@@ -1996,12 +1996,12 @@ func TestNodeSelectors(t *testing.T) {
 		{
 			desc: "Multiple node selectors, only one matches",
 			config: &config.Config{
-				Peers: []*config.Peer{
-					{
+				Peers: map[string]*config.Peer{
+					"peer1": {
 						Addr:          net.ParseIP("1.2.3.4"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
 					},
-					{
+					"peer2": {
 						Addr: net.ParseIP("2.3.4.5"),
 						NodeSelectors: []labels.Selector{
 							mustSelector("host=frontend"),