Repository: metallb/metallb
Pull request #1866 (2023-03-21 02:59:23)

Bump github.com/golang/protobuf from 1.5.2 to 1.5.3
Bumps [github.com/golang/protobuf](https://github.com/golang/protobuf) from 1.5.2 to 1.5.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/golang/protobuf/releases">github.com/golang/protobuf's releases</a>.</em></p>
<blockquote>
<h2>v1.5.3</h2>
<p>Notable changes</p>
<p>*( <a href="https://redirect.github.com/golang/protobuf/issues/1363">#1363</a>) jsonpb: accept 'null' as a valid representation of NullValue in unmarshal</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/protobuf/commit/5d5e8c018a13017f9d5b8bf4fad64aaa42a87308"><code>5d5e8c0</code></a> Merge pull request <a href="https://redirect.github.com/golang/protobuf/issues/1363">#1363</a> from neild/jsonnull</li>
<li><a href="https://github.com/golang/protobuf/commit/37828f962262b5a0f3d9560c429a14d96c711a21"><code>37828f9</code></a> jsonpb: accept 'null' as a valid representation of NullValue in unmarshal</li>
<li>See full diff in <a href="https://github.com/golang/protobuf/compare/v1.5.2...v1.5.3">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/golang/protobuf&package-manager=go_modules&previous-version=1.5.2&new-version=1.5.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index a22cb0b1..92f3eb9a 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -33,7 +33,7 @@ jobs:
           args: -exclude-dir e2etest -severity medium ./...
 
       - name: Golang Vulncheck
-        uses: Templum/govulncheck-action@v0.0.9
+        uses: Templum/govulncheck-action@v0.10.0
         with:
           skip-upload: true
 
diff --git a/.golangci.yml b/.golangci.yml
index b6cc9ad8..6d67cedc 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,8 +1,23 @@
+run:
+  issues-exit-code: 2
 linters:
   enable:
   - godot
   - goimports
   - gofmt
+  - ginkgolinter
+  - dogsled
+  - exportloopref
+  - gocritic
+  - misspell
+  - nolintlint
+  - stylecheck
+  - unconvert
+  - unparam
+  - whitespace
+  - revive
+  - unused
+  - wastedassign
 linters-settings:
   errcheck:
     exclude: .golangci.exclude
@@ -12,3 +27,27 @@ linters-settings:
     exclude:
     - 'SPDX-License-Identifier.*'
     - '\+groupName.*'
+  misspell:
+    locale: US
+    ignore-words:
+      - neighbours
+      - neighbour
+  stylecheck:
+    dot-import-whitelist:
+    - github.com/onsi/ginkgo
+    - github.com/onsi/gomega
+  gocritic:
+    disabled-checks:
+      - captLocal
+      - exitAfterDefer
+  revive:
+    rules:
+    - name: receiver-naming
+      disabled: true
+    - name: dot-imports
+      disabled: true
+issues:
+  exclude-rules:
+    - path: 'api'
+      linters:
+        - stylecheck
diff --git a/api/v1beta1/bfdprofile_webhook.go b/api/v1beta1/bfdprofile_webhook.go
index 2d8cbe51..5ffee158 100644
--- a/api/v1beta1/bfdprofile_webhook.go
+++ b/api/v1beta1/bfdprofile_webhook.go
@@ -63,7 +63,7 @@ func (bfdProfile *BFDProfile) ValidateDelete() error {
 
 	for _, peer := range existingBGPPeers.Items {
 		if bfdProfile.Name == peer.Spec.BFDProfile {
-			return fmt.Errorf("Failed to delete BFDProfile %s, used by BGPPeer %s", bfdProfile.Name, peer.Name)
+			return fmt.Errorf("failed to delete BFDProfile %s, used by BGPPeer %s", bfdProfile.Name, peer.Name)
 		}
 	}
 	return nil
diff --git a/api/v1beta1/bgpadvertisement_webhook.go b/api/v1beta1/bgpadvertisement_webhook.go
index c07fe864..3047b181 100644
--- a/api/v1beta1/bgpadvertisement_webhook.go
+++ b/api/v1beta1/bgpadvertisement_webhook.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/go-kit/log/level"
 	"github.com/pkg/errors"
+	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -61,8 +62,13 @@ func (bgpAdv *BGPAdvertisement) ValidateCreate() error {
 		return err
 	}
 
+	nodes, err := getExistingNodes()
+	if err != nil {
+		return err
+	}
+
 	toValidate := bgpAdvListWithUpdate(existingBGPAdvList, bgpAdv)
-	err = Validator.Validate(toValidate, addressPools, ipAddressPools)
+	err = Validator.Validate(toValidate, addressPools, ipAddressPools, nodes)
 	if err != nil {
 		level.Error(Logger).Log("webhook", "bgpadvertisement", "action", "create", "name", bgpAdv.Name, "namespace", bgpAdv.Namespace, "error", err)
 		return err
@@ -89,8 +95,13 @@ func (bgpAdv *BGPAdvertisement) ValidateUpdate(old runtime.Object) error {
 		return err
 	}
 
+	nodes, err := getExistingNodes()
+	if err != nil {
+		return err
+	}
+
 	toValidate := bgpAdvListWithUpdate(bgpAdvs, bgpAdv)
-	err = Validator.Validate(toValidate, addressPools, ipAddressPools)
+	err = Validator.Validate(toValidate, addressPools, ipAddressPools, nodes)
 	if err != nil {
 		level.Error(Logger).Log("webhook", "bgpadvertisement", "action", "create", "name", bgpAdv.Name, "namespace", bgpAdv.Namespace, "error", err)
 		return err
@@ -112,6 +123,15 @@ var getExistingBGPAdvs = func() (*BGPAdvertisementList, error) {
 	return existingBGPAdvList, nil
 }
 
+var getExistingNodes = func() (*v1.NodeList, error) {
+	existingNodeList := &v1.NodeList{}
+	err := WebhookClient.List(context.Background(), existingNodeList)
+	if err != nil {
+		return nil, errors.Wrapf(err, "Failed to get existing Node objects")
+	}
+	return existingNodeList, nil
+}
+
 func bgpAdvListWithUpdate(existing *BGPAdvertisementList, toAdd *BGPAdvertisement) *BGPAdvertisementList {
 	res := existing.DeepCopy()
 	for i, item := range res.Items { // We override the element with the fresh copy
diff --git a/charts/metallb/templates/rbac.yaml b/charts/metallb/templates/rbac.yaml
index 1d046257..6a38304b 100644
--- a/charts/metallb/templates/rbac.yaml
+++ b/charts/metallb/templates/rbac.yaml
@@ -9,6 +9,9 @@ rules:
 - apiGroups: [""]
   resources: ["services", "namespaces"]
   verbs: ["get", "list", "watch"]
+- apiGroups: [""]
+  resources: ["nodes"]
+  verbs: ["list"]
 - apiGroups: [""]
   resources: ["services/status"]
   verbs: ["update"]
@@ -76,6 +79,9 @@ rules:
 - apiGroups: [""]
   resources: ["secrets"]
   verbs: ["get", "list", "watch"]
+- apiGroups: [""]
+  resources: ["configmaps"]
+  verbs: ["get", "list", "watch"]
 - apiGroups: ["metallb.io"]
   resources: ["addresspools"]
   verbs: ["get", "list", "watch"]
diff --git a/charts/metallb/templates/speaker.yaml b/charts/metallb/templates/speaker.yaml
index d982f2f5..4bbbfb1d 100644
--- a/charts/metallb/templates/speaker.yaml
+++ b/charts/metallb/templates/speaker.yaml
@@ -413,7 +413,7 @@ spec:
       {{- if .Values.prometheus.secureMetricsPort }}
       - name: kube-rbac-proxy
         image: {{ .Values.prometheus.rbacProxy.repository }}:{{ .Values.prometheus.rbacProxy.tag }}
-        imagePullPolicy: IfNotPresent
+        imagePullPolicy: {{ .Values.prometheus.rbacProxy.pullPolicy }}
         args:
           - --logtostderr
           - --secure-listen-address=:{{ .Values.prometheus.secureMetricsPort }}
@@ -446,7 +446,7 @@ spec:
       {{- if .Values.speaker.frr.secureMetricsPort }}
       - name: kube-rbac-proxy-frr
         image: {{ .Values.prometheus.rbacProxy.repository }}:{{ .Values.prometheus.rbacProxy.tag | default .Chart.AppVersion }}
-        imagePullPolicy: IfNotPresent
+        imagePullPolicy: {{ .Values.prometheus.rbacProxy.pullPolicy }}
         args:
           - --logtostderr
           - --secure-listen-address=:{{ .Values.speaker.frr.secureMetricsPort }}
diff --git a/config/manifests/metallb-frr-prometheus.yaml b/config/manifests/metallb-frr-prometheus.yaml
index 93529105..59c2c2a7 100644
--- a/config/manifests/metallb-frr-prometheus.yaml
+++ b/config/manifests/metallb-frr-prometheus.yaml
@@ -1410,6 +1410,14 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - ""
+  resources:
+  - configmaps
+  verbs:
+  - get
+  - list
+  - watch
 - apiGroups:
   - metallb.io
   resources:
@@ -1512,6 +1520,12 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - ""
+  resources:
+  - nodes
+  verbs:
+  - list
 - apiGroups:
   - ""
   resources:
diff --git a/config/manifests/metallb-frr.yaml b/config/manifests/metallb-frr.yaml
index d2710bd5..18bfbe36 100644
--- a/config/manifests/metallb-frr.yaml
+++ b/config/manifests/metallb-frr.yaml
@@ -1410,6 +1410,14 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - ""
+  resources:
+  - configmaps
+  verbs:
+  - get
+  - list
+  - watch
 - apiGroups:
   - metallb.io
   resources:
@@ -1483,6 +1491,12 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - ""
+  resources:
+  - nodes
+  verbs:
+  - list
 - apiGroups:
   - ""
   resources:
diff --git a/config/manifests/metallb-native-prometheus.yaml b/config/manifests/metallb-native-prometheus.yaml
index 45025fdc..7b382c57 100644
--- a/config/manifests/metallb-native-prometheus.yaml
+++ b/config/manifests/metallb-native-prometheus.yaml
@@ -1410,6 +1410,14 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - ""
+  resources:
+  - configmaps
+  verbs:
+  - get
+  - list
+  - watch
 - apiGroups:
   - metallb.io
   resources:
@@ -1512,6 +1520,12 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - ""
+  resources:
+  - nodes
+  verbs:
+  - list
 - apiGroups:
   - ""
   resources:
diff --git a/config/manifests/metallb-native.yaml b/config/manifests/metallb-native.yaml
index 26131219..f34a3c9a 100644
--- a/config/manifests/metallb-native.yaml
+++ b/config/manifests/metallb-native.yaml
@@ -1410,6 +1410,14 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - ""
+  resources:
+  - configmaps
+  verbs:
+  - get
+  - list
+  - watch
 - apiGroups:
   - metallb.io
   resources:
@@ -1483,6 +1491,12 @@ rules:
   - get
   - list
   - watch
+- apiGroups:
+  - ""
+  resources:
+  - nodes
+  verbs:
+  - list
 - apiGroups:
   - ""
   resources:
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index 7bb5f510..fcc8a063 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -14,6 +14,12 @@ rules:
       - get
       - list
       - watch
+  - apiGroups:
+      - ""
+    resources:
+      - nodes
+    verbs:
+      - list
   - apiGroups:
       - ""
     resources:
@@ -148,6 +154,14 @@ rules:
       - get
       - list
       - watch
+  - apiGroups:
+      - ""
+    resources:
+      - configmaps
+    verbs:
+      - get
+      - list
+      - watch
   - apiGroups:
       - metallb.io
     resources:
diff --git a/configmaptocrs/main.go b/configmaptocrs/main.go
index 9fab77b2..b95953ba 100644
--- a/configmaptocrs/main.go
+++ b/configmaptocrs/main.go
@@ -64,7 +64,7 @@ func main() {
 
 	err = generate(f, *source)
 	if err != nil {
-		log.Fatalf("failed to generate resources: %s", err)
+		log.Printf("failed to generate resources: %s", err)
 	}
 }
 
@@ -112,7 +112,7 @@ func readConfig(origin string) ([]byte, error) {
 	fp := filepath.Join(inputDirPath, origin)
 	fp = filepath.Clean(fp)
 	if !strings.HasPrefix(fp, path.Clean(inputDirPath)) {
-		return nil, fmt.Errorf("Unsafe path %s", origin)
+		return nil, fmt.Errorf("unsafe path %s", origin)
 	}
 	f, err := os.Open(filepath.Clean(fp)) // Clean have to happen here to avoid https://github.com/securego/gosec/issues/893
 	if err != nil {
@@ -163,7 +163,7 @@ func getConfigMapData(raw []byte) ([]byte, error) {
 
 	data := []byte(cm.Data["config"])
 	if len(data) == 0 {
-		return nil, fmt.Errorf("bad ConfigMap. no data.")
+		return nil, fmt.Errorf("bad ConfigMap: no data")
 	}
 
 	return data, nil
@@ -396,7 +396,7 @@ func bgpAdvertisementsFor(c *configFile) []v1beta1.BGPAdvertisement {
 		for _, bgpAdv := range ap.BGPAdvertisements {
 			var b v1beta1.BGPAdvertisement
 			b.Name = fmt.Sprintf("bgpadvertisement%d", index)
-			index = index + 1
+			index++
 			b.Namespace = resourcesNameSpace
 			b.Spec.Communities = make([]string, len(bgpAdv.Communities))
 			copy(b.Spec.Communities, bgpAdv.Communities)
@@ -408,7 +408,7 @@ func bgpAdvertisementsFor(c *configFile) []v1beta1.BGPAdvertisement {
 		}
 		if len(ap.BGPAdvertisements) == 0 && ap.Protocol == BGP {
 			res = append(res, emptyBGPAdv(ap.Name, index))
-			index = index + 1
+			index++
 		}
 	}
 	return res
@@ -440,7 +440,7 @@ func l2AdvertisementsFor(c *configFile) []v1beta1.L2Advertisement {
 					IPAddressPools: []string{addresspool.Name},
 				},
 			}
-			index = index + 1
+			index++
 			res = append(res, l2Adv)
 		}
 	}
diff --git a/design/pool-configuration.md b/design/pool-configuration.md
index ae7b1eb5..8d41852a 100644
--- a/design/pool-configuration.md
+++ b/design/pool-configuration.md
@@ -177,7 +177,7 @@ All the BGP changes will happen from a "behavioural" point of view. Where needed
 The risk of not covering all possible cases / permutations is still there.
 
 That aside, this proposal covers the majority of the issues that were raised by the users (multiple times), and it won't prevent adding a finer level of configuration
-if the need ever arises. Moreover, providing a straightforward way to separate concerns will make it easier to accomodate new changes, and it will be more clear where
+if the need ever arises. Moreover, providing a straightforward way to separate concerns will make it easier to accommodate new changes, and it will be more clear where
 those new changes must be applied.
 
 ## Design Details
diff --git a/frr-tools/metrics/collector/bfd.go b/frr-tools/metrics/collector/bfd.go
index 2d1b1e55..5cfef1fa 100644
--- a/frr-tools/metrics/collector/bfd.go
+++ b/frr-tools/metrics/collector/bfd.go
@@ -91,7 +91,12 @@ type bfd struct {
 	frrCli vtysh.Cli
 }
 
-func NewBFD(l log.Logger) *bfd {
+func NewBFD(l log.Logger) prometheus.Collector {
+	log := log.With(l, "collector", subsystem)
+	return &bfd{Log: log, frrCli: vtysh.Run}
+}
+
+func mockNewBFD(l log.Logger) *bfd {
 	log := log.With(l, "collector", subsystem)
 	return &bfd{Log: log, frrCli: vtysh.Run}
 }
@@ -159,11 +164,11 @@ func getBFDPeers(frrCli vtysh.Cli) (map[string][]bgpfrr.BFDPeer, error) {
 	}
 	res := make(map[string][]bgpfrr.BFDPeer)
 	for _, vrf := range vrfs {
-		peersJson, err := frrCli(fmt.Sprintf("show bfd vrf %s peers json", vrf))
+		peersJSON, err := frrCli(fmt.Sprintf("show bfd vrf %s peers json", vrf))
 		if err != nil {
 			return nil, err
 		}
-		peers, err := bgpfrr.ParseBFDPeers(peersJson)
+		peers, err := bgpfrr.ParseBFDPeers(peersJSON)
 		if err != nil {
 			return nil, err
 		}
@@ -180,13 +185,13 @@ func getBFDPeersCounters(frrCli vtysh.Cli) (map[string][]bfdPeerCounters, error)
 
 	res := make(map[string][]bfdPeerCounters)
 	for _, vrf := range vrfs {
-		countersJson, err := frrCli(fmt.Sprintf("show bfd vrf %s peers counters json", vrf))
+		countersJSON, err := frrCli(fmt.Sprintf("show bfd vrf %s peers counters json", vrf))
 		if err != nil {
 			return nil, err
 		}
 
 		parseRes := []bfdPeerCounters{}
-		err = json.Unmarshal([]byte(countersJson), &parseRes)
+		err = json.Unmarshal([]byte(countersJSON), &parseRes)
 		if err != nil {
 			return nil, err
 		}
diff --git a/frr-tools/metrics/collector/bgp.go b/frr-tools/metrics/collector/bgp.go
index 4b8d0f51..427cb6d5 100644
--- a/frr-tools/metrics/collector/bgp.go
+++ b/frr-tools/metrics/collector/bgp.go
@@ -107,7 +107,12 @@ type bgp struct {
 	frrCli vtysh.Cli
 }
 
-func NewBGP(l log.Logger) *bgp {
+func NewBGP(l log.Logger) prometheus.Collector {
+	log := log.With(l, "collector", bgpmetrics.Subsystem)
+	return &bgp{Log: log, frrCli: vtysh.Run}
+}
+
+func mocknewBGP(l log.Logger) *bgp {
 	log := log.With(l, "collector", bgpmetrics.Subsystem)
 	return &bgp{Log: log, frrCli: vtysh.Run}
 }
@@ -144,7 +149,7 @@ func updateNeighborsMetrics(ch chan<- prometheus.Metric, neighbors map[string][]
 			if !n.Connected {
 				sessionUp = 0
 			}
-			peerLabel := fmt.Sprintf("%s:%d", n.Ip.String(), n.Port)
+			peerLabel := fmt.Sprintf("%s:%d", n.IP.String(), n.Port)
 
 			ch <- prometheus.MustNewConstMetric(sessionUpDesc, prometheus.GaugeValue, float64(sessionUp), peerLabel, vrf)
 			ch <- prometheus.MustNewConstMetric(prefixesDesc, prometheus.GaugeValue, float64(n.PrefixSent), peerLabel, vrf)
@@ -179,7 +184,6 @@ func getBGPNeighbors(frrCli vtysh.Cli) (map[string][]*bgpfrr.Neighbor, error) {
 			return nil, err
 		}
 		neighbors[vrf] = neighborsPerVRF
-
 	}
 	return neighbors, nil
 }
diff --git a/go.mod b/go.mod
index 81015b3d..c0befc00 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.19
 require (
 	github.com/davecgh/go-spew v1.1.1
 	github.com/go-kit/log v0.2.1
-	github.com/golang/protobuf v1.5.2
+	github.com/golang/protobuf v1.5.3
 	github.com/google/go-cmp v0.5.9
 	github.com/hashicorp/memberlist v0.5.0
 	github.com/mdlayher/arp v0.0.0-20220221190821-c37aaafac7f9
@@ -21,7 +21,7 @@ require (
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.14.0
 	github.com/prometheus/client_model v0.3.0
-	github.com/prometheus/common v0.39.0
+	github.com/prometheus/common v0.42.0
 	golang.org/x/sync v0.1.0
 	golang.org/x/sys v0.6.0
 	k8s.io/api v0.26.1
@@ -31,6 +31,7 @@ require (
 	k8s.io/klog v1.0.0
 	k8s.io/kubernetes v1.26.1
 	k8s.io/pod-security-admission v0.0.0
+	k8s.io/utils v0.0.0-20230115233650-391b47cb4029
 	sigs.k8s.io/controller-runtime v0.14.5
 	sigs.k8s.io/yaml v1.3.0
 )
@@ -101,7 +102,7 @@ require (
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.0.2 // indirect
-	github.com/opencontainers/runc v1.1.4 // indirect
+	github.com/opencontainers/runc v1.1.5 // indirect
 	github.com/openshift/api v0.0.0-20200829102639-8a3a835f1acf // indirect
 	github.com/openshift/client-go v0.0.0-20200827190008-3062137373b5 // indirect
 	github.com/pelletier/go-toml v1.9.3 // indirect
@@ -136,7 +137,7 @@ require (
 	go.uber.org/zap v1.24.0 // indirect
 	golang.org/x/crypto v0.5.0 // indirect
 	golang.org/x/net v0.7.0 // indirect
-	golang.org/x/oauth2 v0.4.0 // indirect
+	golang.org/x/oauth2 v0.5.0 // indirect
 	golang.org/x/term v0.5.0 // indirect
 	golang.org/x/text v0.7.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
@@ -157,7 +158,6 @@ require (
 	k8s.io/kube-openapi v0.0.0-20230123231816-1cb3ae25d79a // indirect
 	k8s.io/kubectl v0.0.0 // indirect
 	k8s.io/kubelet v0.0.0 // indirect
-	k8s.io/utils v0.0.0-20230115233650-391b47cb4029 // indirect
 	sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
diff --git a/go.sum b/go.sum
index e14d1a1d..ec0e6e04 100644
--- a/go.sum
+++ b/go.sum
@@ -227,8 +227,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
@@ -407,8 +408,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
 github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg=
-github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
+github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
+github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
 github.com/openshift-kni/k8sreporter v1.0.2 h1:qzcFYUScS/zSpenqcNl30BASmjHykuTAF4wVcFwSkjw=
@@ -453,8 +454,8 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
-github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI=
-github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y=
+github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
+github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -680,8 +681,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
-golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
+golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
+golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/internal/bgp/bgp.go b/internal/bgp/bgp.go
index 24e58647..c5950755 100644
--- a/internal/bgp/bgp.go
+++ b/internal/bgp/bgp.go
@@ -78,4 +78,5 @@ type SessionParameters struct {
 type SessionManager interface {
 	NewSession(logger log.Logger, args SessionParameters) (Session, error)
 	SyncBFDProfiles(profiles map[string]*config.BFDProfile) error
+	SyncExtraInfo(extras string) error
 }
diff --git a/internal/bgp/frr/config.go b/internal/bgp/frr/config.go
index 7f7b4c13..818dcdda 100644
--- a/internal/bgp/frr/config.go
+++ b/internal/bgp/frr/config.go
@@ -31,6 +31,7 @@ type frrConfig struct {
 	Hostname    string
 	Routers     []*routerConfig
 	BFDProfiles []BFDProfile
+	ExtraConfig string
 }
 
 type reloadEvent struct {
diff --git a/internal/bgp/frr/frr.go b/internal/bgp/frr/frr.go
index 2f6217e4..49e7899c 100644
--- a/internal/bgp/frr/frr.go
+++ b/internal/bgp/frr/frr.go
@@ -26,6 +26,7 @@ import (
 type sessionManager struct {
 	sessions     map[string]*session
 	bfdProfiles  []BFDProfile
+	extraConfig  string
 	reloadConfig chan reloadEvent
 	logLevel     string
 	sync.Mutex
@@ -153,6 +154,19 @@ func (sm *sessionManager) deleteSession(s *session) error {
 	return nil
 }
 
+func (sm *sessionManager) SyncExtraInfo(extraInfo string) error {
+	sm.Lock()
+	defer sm.Unlock()
+	sm.extraConfig = extraInfo
+	frrConfig, err := sm.createConfig()
+	if err != nil {
+		return err
+	}
+
+	sm.reloadConfig <- reloadEvent{config: frrConfig}
+	return nil
+}
+
 func (sm *sessionManager) SyncBFDProfiles(profiles map[string]*metallbconfig.BFDProfile) error {
 	sm.Lock()
 	defer sm.Unlock()
@@ -184,6 +198,7 @@ func (sm *sessionManager) createConfig() (*frrConfig, error) {
 		Hostname:    hostname,
 		Loglevel:    sm.logLevel,
 		BFDProfiles: sm.bfdProfiles,
+		ExtraConfig: sm.extraConfig,
 	}
 
 	type router struct {
@@ -311,7 +326,25 @@ func (sm *sessionManager) createConfig() (*frrConfig, error) {
 var debounceTimeout = 3 * time.Second
 var failureTimeout = time.Second * 5
 
-func NewSessionManager(l log.Logger, logLevel logging.Level) *sessionManager {
+func NewSessionManager(l log.Logger, logLevel logging.Level) bgp.SessionManager {
+	res := &sessionManager{
+		sessions:     map[string]*session{},
+		bfdProfiles:  []BFDProfile{},
+		reloadConfig: make(chan reloadEvent),
+		logLevel:     logLevelToFRR(logLevel),
+	}
+	reload := func(config *frrConfig) error {
+		return generateAndReloadConfigFile(config, l)
+	}
+
+	debouncer(reload, res.reloadConfig, debounceTimeout, failureTimeout, l)
+
+	reloadValidator(l, res.reloadConfig)
+
+	return res
+}
+
+func mockNewSessionManager(l log.Logger, logLevel logging.Level) *sessionManager {
 	res := &sessionManager{
 		sessions:     map[string]*session{},
 		bfdProfiles:  []BFDProfile{},
diff --git a/internal/bgp/frr/parse.go b/internal/bgp/frr/parse.go
index 288c0082..fcf9bb4f 100644
--- a/internal/bgp/frr/parse.go
+++ b/internal/bgp/frr/parse.go
@@ -13,7 +13,7 @@ import (
 )
 
 type Neighbor struct {
-	Ip             net.IP
+	IP             net.IP
 	VRF            string
 	Connected      bool
 	LocalAS        string
@@ -128,7 +128,7 @@ func ParseNeighbour(vtyshRes string) (*Neighbor, error) {
 			prefixSent += s.SentPrefixCounter
 		}
 		return &Neighbor{
-			Ip:             ip,
+			IP:             ip,
 			Connected:      connected,
 			LocalAS:        strconv.Itoa(n.LocalAs),
 			RemoteAS:       strconv.Itoa(n.RemoteAs),
@@ -165,7 +165,7 @@ func ParseNeighbours(vtyshRes string) ([]*Neighbor, error) {
 			prefixSent += s.SentPrefixCounter
 		}
 		res = append(res, &Neighbor{
-			Ip:             ip,
+			IP:             ip,
 			Connected:      connected,
 			LocalAS:        strconv.Itoa(n.LocalAs),
 			RemoteAS:       strconv.Itoa(n.RemoteAs),
diff --git a/internal/bgp/frr/templates/frr.tmpl b/internal/bgp/frr/templates/frr.tmpl
index d1274137..f732de91 100644
--- a/internal/bgp/frr/templates/frr.tmpl
+++ b/internal/bgp/frr/templates/frr.tmpl
@@ -64,3 +64,7 @@ bfd
 {{- template "bfdprofile" dict "profile" . -}}
 {{- end }}
 {{- end }}
+
+{{- if .ExtraConfig }}
+{{ .ExtraConfig }}
+{{- end }}
diff --git a/internal/bgp/native/native.go b/internal/bgp/native/native.go
index d6084775..3ca880fc 100644
--- a/internal/bgp/native/native.go
+++ b/internal/bgp/native/native.go
@@ -49,7 +49,7 @@ type session struct {
 type sessionManager struct {
 }
 
-func NewSessionManager(l log.Logger) *sessionManager {
+func NewSessionManager(l log.Logger) bgp.SessionManager {
 	return &sessionManager{}
 }
 
@@ -78,6 +78,13 @@ func (sm *sessionManager) SyncBFDProfiles(profiles map[string]*config.BFDProfile
 	return errors.New("bfd profiles not supported in native mode")
 }
 
+func (sm *sessionManager) SyncExtraInfo(extras string) error {
+	if extras != "" {
+		return errors.New("bgp extra info not supported in native mode")
+	}
+	return nil
+}
+
 // run tries to stay connected to the peer, and pumps route updates to it.
 func (s *session) run() {
 	defer stats.DeleteSession(s.PeerAddress)
@@ -274,7 +281,7 @@ func (s *session) connect() error {
 	return nil
 }
 
-func hashRouterId(hostname string) (net.IP, error) {
+func hashRouterID(hostname string) (net.IP, error) {
 	buf := new(bytes.Buffer)
 	err := binary.Write(buf, binary.LittleEndian, crc32.ChecksumIEEE([]byte(hostname)))
 	if err != nil {
@@ -292,7 +299,7 @@ func getRouterID(addr net.IP, myNode string) (net.IP, error) {
 
 	ifaces, err := net.Interfaces()
 	if err != nil {
-		return hashRouterId(myNode)
+		return hashRouterID(myNode)
 	}
 	for _, i := range ifaces {
 		addrs, err := i.Addrs()
@@ -323,11 +330,11 @@ func getRouterID(addr net.IP, myNode string) (net.IP, error) {
 						return ip, nil
 					}
 				}
-				return hashRouterId(myNode)
+				return hashRouterID(myNode)
 			}
 		}
 	}
-	return hashRouterId(myNode)
+	return hashRouterID(myNode)
 }
 
 // sendKeepalives sends BGP KEEPALIVE packets at the negotiated rate
@@ -489,9 +496,9 @@ func (s *session) Close() error {
 }
 
 // DialTCP does the part of creating a connection manually,  including setting the
-// proper TCP MD5 options when the password is not empty. Works by manupulating
+// proper TCP MD5 options when the password is not empty. Works by manipulating
 // the low level FD's, skipping the net.Conn API as it has not hooks to set
-// the neccessary sockopts for TCP MD5.
+// the necessary sockopts for TCP MD5.
 func dialMD5(ctx context.Context, addr string, srcAddr net.IP, password string) (net.Conn, error) {
 	// If srcAddr exists on any of the local network interfaces, use it as the
 	// source address of the TCP socket. Otherwise, use the IPv6 unspecified
@@ -501,11 +508,11 @@ func dialMD5(ctx context.Context, addr string, srcAddr net.IP, password string)
 	if srcAddr != nil {
 		ifs, err := net.Interfaces()
 		if err != nil {
-			return nil, fmt.Errorf("Querying local interfaces: %w", err)
+			return nil, fmt.Errorf("querying local interfaces: %w", err)
 		}
 
 		if !localAddressExists(ifs, srcAddr) {
-			return nil, fmt.Errorf("Address %q doesn't exist on this host", srcAddr)
+			return nil, fmt.Errorf("address %q doesn't exist on this host", srcAddr)
 		}
 
 		a = fmt.Sprintf("[%s]", srcAddr.String())
@@ -513,7 +520,7 @@ func dialMD5(ctx context.Context, addr string, srcAddr net.IP, password string)
 
 	laddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", a))
 	if err != nil {
-		return nil, fmt.Errorf("Error resolving local address: %s ", err)
+		return nil, fmt.Errorf("error resolving local address: %s ", err)
 	}
 
 	raddr, err := net.ResolveTCPAddr("tcp", addr)
diff --git a/internal/config/config.go b/internal/config/config.go
index 83b6f67d..7df5f8a5 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -32,6 +32,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/util/sets"
+	"k8s.io/utils/strings/slices"
 )
 
 type ClusterResources struct {
@@ -45,6 +46,7 @@ type ClusterResources struct {
 	PasswordSecrets    map[string]corev1.Secret          `json:"passwordsecrets"`
 	Nodes              []corev1.Node                     `json:"nodes"`
 	Namespaces         []corev1.Namespace                `json:"namespaces"`
+	BGPExtras          corev1.ConfigMap                  `json:"bgpextras"`
 }
 
 // Config is a parsed MetalLB configuration.
@@ -55,6 +57,8 @@ type Config struct {
 	Pools *Pools
 	// BFD profiles that can be used by peers.
 	BFDProfiles map[string]*BFDProfile
+	// Protocol dependent extra config. Currently used only by FRR
+	BGPExtras string
 }
 
 // Pools contains address pools and its namespace/service specific allocations.
@@ -76,6 +80,8 @@ const (
 	Layer2 Proto = "layer2"
 )
 
+const bgpExtrasField = "extras"
+
 var Protocols = []Proto{
 	BGP, Layer2,
 }
@@ -226,6 +232,11 @@ func For(resources ClusterResources, validate Validate) (*Config, error) {
 		return nil, err
 	}
 
+	cfg.BGPExtras = bgpExtrasFor(resources)
+	if err != nil {
+		return nil, err
+	}
+
 	err = validateConfig(cfg)
 	if err != nil {
 		return nil, err
@@ -249,7 +260,7 @@ func bfdProfilesFor(resources ClusterResources) (map[string]*BFDProfile, error)
 }
 
 func peersFor(resources ClusterResources, BFDProfiles map[string]*BFDProfile) (map[string]*Peer, error) {
-	var res map[string]*Peer = make(map[string]*Peer)
+	var res = make(map[string]*Peer)
 	for _, p := range resources.Peers {
 		peer, err := peerFromCR(p, resources.PasswordSecrets)
 		if err != nil {
@@ -351,6 +362,13 @@ func poolsFor(resources ClusterResources) (*Pools, error) {
 		ByServiceSelector: poolsByServiceSelector(pools)}, nil
 }
 
+func bgpExtrasFor(resources ClusterResources) string {
+	if resources.BGPExtras.Data == nil {
+		return ""
+	}
+	return resources.BGPExtras.Data[bgpExtrasField]
+}
+
 func communitiesFromCrs(cs []metallbv1beta1.Community) (map[string]uint32, error) {
 	communities := map[string]uint32{}
 	for _, c := range cs {
@@ -917,13 +935,13 @@ func getCommunityValue(community string, communities map[string]uint32) (uint32,
 	}
 
 	v, err := ParseCommunity(community)
-	if errors.Is(err, invalidCommunityValue) {
+	if errors.Is(err, errInvalidaCommunityValue) {
 		return 0, err
 	}
 
 	// Return TransientError on invalidCommunityFormat, in case it refers
 	// a Community resource that doesn't exist yet.
-	if errors.Is(err, invalidCommunityFormat) {
+	if errors.Is(err, errInvalidCommunityFormat) {
 		return 0, TransientError{err.Error()}
 	}
 
@@ -956,24 +974,65 @@ func validateBGPAdvPerPool(adv *BGPAdvertisement, pool *Pool) error {
 				"this pool is more specific than the aggregation length for addresses %s", adv.AggregationLength, lowest, addr)
 		}
 	}
+
+	// Verify that BGP ADVs set a unique local preference value per BGP update.
+	for _, bgpAdv := range pool.BGPAdvertisements {
+		if adv.LocalPref != bgpAdv.LocalPref {
+			if !advertisementsAreCompatible(adv, bgpAdv) {
+				return fmt.Errorf("invalid local preference %d: local preferernce %d was "+
+					"already set for the same type of BGP update. Check existing BGP advertisements "+
+					"with common pools and aggregation lengths", adv.LocalPref, bgpAdv.LocalPref)
+			}
+		}
+	}
+
 	return nil
 }
 
-var invalidCommunityValue = errors.New("invalid community value")
-var invalidCommunityFormat = errors.New("invalid community format")
+func advertisementsAreCompatible(newAdv, adv *BGPAdvertisement) bool {
+	if adv.AggregationLength != newAdv.AggregationLength && adv.AggregationLengthV6 != newAdv.AggregationLengthV6 {
+		return true
+	}
+
+	// BGP ADVs with different set of BGP peers do not collide.
+	if len(newAdv.Peers) != 0 && len(adv.Peers) != 0 {
+		equalPeer := false
+		for _, peer := range newAdv.Peers {
+			if slices.Contains(adv.Peers, peer) {
+				equalPeer = true
+				break
+			}
+		}
+		if !equalPeer {
+			return true
+		}
+	}
+
+	// BGP ADVs with different set of nodes do not collide.
+	for node := range newAdv.Nodes {
+		if _, ok := adv.Nodes[node]; ok {
+			return false
+		}
+	}
+
+	return true
+}
+
+var errInvalidaCommunityValue = errors.New("invalid community value")
+var errInvalidCommunityFormat = errors.New("invalid community format")
 
 func ParseCommunity(c string) (uint32, error) {
 	fs := strings.Split(c, ":")
 	if len(fs) != 2 {
-		return 0, fmt.Errorf("%w: %s", invalidCommunityFormat, c)
+		return 0, fmt.Errorf("%w: %s", errInvalidCommunityFormat, c)
 	}
 	a, err := strconv.ParseUint(fs[0], 10, 16)
 	if err != nil {
-		return 0, fmt.Errorf("%w: invalid first section of community %q: %s", invalidCommunityValue, fs[0], err)
+		return 0, fmt.Errorf("%w: invalid first section of community %q: %s", errInvalidaCommunityValue, fs[0], err)
 	}
 	b, err := strconv.ParseUint(fs[1], 10, 16)
 	if err != nil {
-		return 0, fmt.Errorf("%w: invalid second section of community %q: %s", invalidCommunityValue, fs[1], err)
+		return 0, fmt.Errorf("%w: invalid second section of community %q: %s", errInvalidaCommunityValue, fs[1], err)
 	}
 
 	return (uint32(a) << 16) + uint32(b), nil
@@ -1113,10 +1172,8 @@ OUTER:
 				continue OUTER
 			}
 		}
-
 	}
 	return res, nil
-
 }
 
 func selectedPools(pools []metallbv1beta1.IPAddressPool, selectors []metav1.LabelSelector) ([]string, error) {
diff --git a/internal/config/validation.go b/internal/config/validation.go
index 84be177d..27d0c00f 100644
--- a/internal/config/validation.go
+++ b/internal/config/validation.go
@@ -91,7 +91,6 @@ func DiscardNativeOnly(c ClusterResources) error {
 				p.Spec.VRFName == p1.Spec.VRFName {
 				return fmt.Errorf("peer %s has myAsn different from %s, in FRR mode all myAsn must be equal for the same VRF", p.Spec.Address, p1.Spec.Address)
 			}
-
 		}
 	}
 	return nil
diff --git a/internal/config/validator.go b/internal/config/validator.go
index d9bb5e94..c0d6996b 100644
--- a/internal/config/validator.go
+++ b/internal/config/validator.go
@@ -9,6 +9,7 @@ import (
 	metallbv1beta2 "go.universe.tf/metallb/api/v1beta2"
 	apivalidate "go.universe.tf/metallb/api/validate"
 
+	v1 "k8s.io/api/core/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
@@ -53,6 +54,8 @@ func (v *validator) Validate(resources ...client.ObjectList) error {
 			clusterResources.LegacyAddressPools = append(clusterResources.LegacyAddressPools, list.Items...)
 		case *metallbv1beta1.CommunityList:
 			clusterResources.Communities = append(clusterResources.Communities, list.Items...)
+		case *v1.NodeList:
+			clusterResources.Nodes = append(clusterResources.Nodes, list.Items...)
 		}
 	}
 	_, err := For(clusterResources, v.validate)
diff --git a/internal/ipfamily/ipfamily.go b/internal/ipfamily/ipfamily.go
index b7cb01a1..e9124888 100644
--- a/internal/ipfamily/ipfamily.go
+++ b/internal/ipfamily/ipfamily.go
@@ -30,9 +30,8 @@ func ForAddresses(ips []string) (Family, error) {
 		ip := net.ParseIP(ips[0])
 		if ip.To4() != nil {
 			return IPv4, nil
-		} else {
-			return IPv6, nil
 		}
+		return IPv6, nil
 	case 2:
 		ip1 := net.ParseIP(ips[0])
 		ip2 := net.ParseIP(ips[1])
diff --git a/internal/k8s/controllers/config_controller.go b/internal/k8s/controllers/config_controller.go
index 8a9702fe..712e167f 100644
--- a/internal/k8s/controllers/config_controller.go
+++ b/internal/k8s/controllers/config_controller.go
@@ -26,6 +26,7 @@ import (
 	metallbv1beta2 "go.universe.tf/metallb/api/v1beta2"
 	"go.universe.tf/metallb/internal/config"
 	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -36,6 +37,8 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/source"
 )
 
+const bgpExtrasConfigName = "bgpextras"
+
 type ConfigReconciler struct {
 	client.Client
 	Logger         log.Logger
@@ -116,6 +119,13 @@ var requestHandler = func(r *ConfigReconciler, ctx context.Context, req ctrl.Req
 		return ctrl.Result{}, err
 	}
 
+	var extrasMap corev1.ConfigMap
+	key := client.ObjectKey{Name: bgpExtrasConfigName, Namespace: r.Namespace}
+	if err := r.Get(ctx, key, &extrasMap); err != nil && !apierrors.IsNotFound(err) {
+		level.Error(r.Logger).Log("controller", "ConfigReconciler", "message", "failed to get the frr configmap", "error", err)
+		return ctrl.Result{}, err
+	}
+
 	resources := config.ClusterResources{
 		Pools:              ipAddressPools.Items,
 		Peers:              bgpPeers.Items,
@@ -127,6 +137,7 @@ var requestHandler = func(r *ConfigReconciler, ctx context.Context, req ctrl.Req
 		PasswordSecrets:    secrets,
 		Nodes:              nodes.Items,
 		Namespaces:         namespaces.Items,
+		BGPExtras:          extrasMap,
 	}
 
 	level.Debug(r.Logger).Log("controller", "ConfigReconciler", "metallb CRs and Secrets", dumpClusterResources(&resources))
@@ -138,6 +149,9 @@ var requestHandler = func(r *ConfigReconciler, ctx context.Context, req ctrl.Req
 		return ctrl.Result{}, nil
 	}
 
+	if cfg.BGPExtras != "" {
+		level.Info(r.Logger).Log("controller", "ConfigReconciler", "warning message", "BGP Extras provided, please note that this configuration is not supported and used at your own risk")
+	}
 	level.Debug(r.Logger).Log("controller", "ConfigReconciler", "rendered config", dumpConfig(cfg))
 	if r.currentConfig != nil && reflect.DeepEqual(r.currentConfig, cfg) {
 		level.Debug(r.Logger).Log("controller", "ConfigReconciler", "event", "configuration did not change, ignoring")
@@ -156,7 +170,7 @@ var requestHandler = func(r *ConfigReconciler, ctx context.Context, req ctrl.Req
 		// which is not what we want here.
 		r.currentConfig = nil
 		level.Error(r.Logger).Log("controller", "ConfigReconciler", "metallb CRs and Secrets", dumpClusterResources(&resources), "event", "reload failed, retry")
-		return ctrl.Result{}, retryError
+		return ctrl.Result{}, errRetry
 	case SyncStateReprocessAll:
 		level.Info(r.Logger).Log("controller", "ConfigReconciler", "event", "force service reload")
 		r.ForceReload()
@@ -176,7 +190,7 @@ var requestHandler = func(r *ConfigReconciler, ctx context.Context, req ctrl.Req
 func (r *ConfigReconciler) SetupWithManager(mgr ctrl.Manager) error {
 	p := predicate.Funcs{
 		UpdateFunc: func(e event.UpdateEvent) bool {
-			return filterNodeEvent(e) && filterNamespaceEvent(e)
+			return filterNodeEvent(e) && filterNamespaceEvent(e) && filterConfigmapEvent(e)
 		},
 	}
 	return ctrl.NewControllerManagedBy(mgr).
@@ -190,6 +204,7 @@ func (r *ConfigReconciler) SetupWithManager(mgr ctrl.Manager) error {
 		Watches(&source.Kind{Type: &metallbv1beta1.Community{}}, &handler.EnqueueRequestForObject{}).
 		Watches(&source.Kind{Type: &corev1.Secret{}}, &handler.EnqueueRequestForObject{}).
 		Watches(&source.Kind{Type: &corev1.Namespace{}}, &handler.EnqueueRequestForObject{}).
+		Watches(&source.Kind{Type: &corev1.ConfigMap{}}, &handler.EnqueueRequestForObject{}).
 		WithEventFilter(p).
 		Complete(r)
 }
@@ -225,6 +240,17 @@ func filterNamespaceEvent(e event.UpdateEvent) bool {
 	return true
 }
 
+func filterConfigmapEvent(e event.UpdateEvent) bool {
+	cm, ok := e.ObjectNew.(*corev1.ConfigMap)
+	if !ok {
+		return true
+	}
+	if cm.Name != bgpExtrasConfigName {
+		return false
+	}
+	return true
+}
+
 func (r *ConfigReconciler) getSecrets(ctx context.Context) (map[string]corev1.Secret, error) {
 	var secrets corev1.SecretList
 	if err := r.List(ctx, &secrets, client.InNamespace(r.Namespace)); err != nil {
diff --git a/internal/k8s/controllers/dump.go b/internal/k8s/controllers/dump.go
index c9b55d6a..9311381c 100644
--- a/internal/k8s/controllers/dump.go
+++ b/internal/k8s/controllers/dump.go
@@ -20,6 +20,7 @@ func dumpClusterResources(c *config.ClusterResources) string {
 		BGPAdvs:            c.BGPAdvs,
 		LegacyAddressPools: c.LegacyAddressPools,
 		Communities:        c.Communities,
+		BGPExtras:          c.BGPExtras,
 	}
 	withNoSecret.PasswordSecrets = make(map[string]corev1.Secret)
 	for k, s := range c.PasswordSecrets {
diff --git a/internal/k8s/controllers/node_controller.go b/internal/k8s/controllers/node_controller.go
index 3c0931d0..a2ee7eda 100644
--- a/internal/k8s/controllers/node_controller.go
+++ b/internal/k8s/controllers/node_controller.go
@@ -23,7 +23,6 @@ import (
 	"github.com/go-kit/log/level"
 
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	ctrl "sigs.k8s.io/controller-runtime"
@@ -38,7 +37,7 @@ type NodeReconciler struct {
 	Scheme    *runtime.Scheme
 	NodeName  string
 	Namespace string
-	Handler   func(log.Logger, *v1.Node) SyncState
+	Handler   func(log.Logger, *corev1.Node) SyncState
 }
 
 func (r *NodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
@@ -46,7 +45,7 @@ func (r *NodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
 	defer level.Info(r.Logger).Log("controller", "NodeReconciler", "end reconcile", req.NamespacedName.String())
 	updates.Inc()
 
-	var n v1.Node
+	var n corev1.Node
 	err := r.Get(ctx, req.NamespacedName, &n)
 	if err != nil {
 		return ctrl.Result{}, client.IgnoreNotFound(err)
@@ -56,7 +55,7 @@ func (r *NodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
 	switch res {
 	case SyncStateError:
 		updateErrors.Inc()
-		return ctrl.Result{}, retryError
+		return ctrl.Result{}, errRetry
 	case SyncStateReprocessAll:
 		level.Error(r.Logger).Log("controller", "NodeReconciler", "error", "unexpected result reprocess all")
 		return ctrl.Result{}, nil
@@ -97,13 +96,13 @@ func (r *NodeReconciler) SetupWithManager(mgr ctrl.Manager) error {
 		},
 	}
 	return ctrl.NewControllerManagedBy(mgr).
-		For(&v1.Node{}).
+		For(&corev1.Node{}).
 		WithEventFilter(p).
 		Complete(r)
 }
 
 func (r *NodeReconciler) filterOtherNodes(obj client.Object) bool {
-	node, ok := obj.(*v1.Node)
+	node, ok := obj.(*corev1.Node)
 	if !ok {
 		level.Error(r.Logger).Log("controller", "NodeReconciler", "error", "object is not node", "name", obj.GetName())
 		return false
diff --git a/internal/k8s/controllers/pool_controller.go b/internal/k8s/controllers/pool_controller.go
index 3fe000d1..9bcbdd77 100644
--- a/internal/k8s/controllers/pool_controller.go
+++ b/internal/k8s/controllers/pool_controller.go
@@ -96,7 +96,7 @@ func (r *PoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
 		updateErrors.Inc()
 		configStale.Set(1)
 		level.Error(r.Logger).Log("controller", "PoolReconciler", "metallb CRs and Secrets", dumpClusterResources(&resources), "event", "reload failed, retry")
-		return ctrl.Result{}, retryError
+		return ctrl.Result{}, errRetry
 	case SyncStateReprocessAll:
 		level.Info(r.Logger).Log("controller", "PoolReconciler", "event", "force service reload")
 		r.ForceReload()
diff --git a/internal/k8s/controllers/service_controller.go b/internal/k8s/controllers/service_controller.go
index 909b86b3..744d9a33 100644
--- a/internal/k8s/controllers/service_controller.go
+++ b/internal/k8s/controllers/service_controller.go
@@ -90,7 +90,7 @@ func (r *ServiceReconciler) reconcileService(ctx context.Context, req ctrl.Reque
 	case SyncStateError:
 		updateErrors.Inc()
 		level.Info(r.Logger).Log("controller", "ServiceReconciler", "name", req.NamespacedName.String(), "service", dumpResource(service), "endpoints", dumpResource(epSlices), "event", "failed to handle service")
-		return ctrl.Result{}, retryError
+		return ctrl.Result{}, errRetry
 	case SyncStateReprocessAll:
 		level.Info(r.Logger).Log("controller", "ServiceReconciler", "event", "force service reload")
 		r.forceReload()
diff --git a/internal/k8s/controllers/service_controller_reload.go b/internal/k8s/controllers/service_controller_reload.go
index f1e4f91d..7c2aeb9a 100644
--- a/internal/k8s/controllers/service_controller_reload.go
+++ b/internal/k8s/controllers/service_controller_reload.go
@@ -99,7 +99,7 @@ func (r *ServiceReconciler) reprocessAll(ctx context.Context, req ctrl.Request)
 		// in case we want to retry, we return an error to trigger the exponential backoff mechanism so that
 		// this controller won't loop at full speed
 		level.Info(r.Logger).Log("controller", "ServiceReconciler - reprocessAll", "event", "force service reload")
-		return ctrl.Result{}, retryError
+		return ctrl.Result{}, errRetry
 	}
 	return ctrl.Result{}, nil
 }
diff --git a/internal/k8s/controllers/values.go b/internal/k8s/controllers/values.go
index a953062f..172e3df2 100644
--- a/internal/k8s/controllers/values.go
+++ b/internal/k8s/controllers/values.go
@@ -29,4 +29,4 @@ const (
 	EndpointSlices
 )
 
-var retryError = errors.New("event handling failed, retrying")
+var errRetry = errors.New("event handling failed, retrying")
diff --git a/internal/k8s/epslices/endpoint_slices.go b/internal/k8s/epslices/endpoint_slices.go
index 5c93b493..872c6977 100644
--- a/internal/k8s/epslices/endpoint_slices.go
+++ b/internal/k8s/epslices/endpoint_slices.go
@@ -50,13 +50,13 @@ func ServiceKeyForSlice(endpointSlice *discovery.EndpointSlice) (types.Namespace
 func SlicesServiceIndex(obj interface{}) ([]string, error) {
 	endpointSlice, ok := obj.(*discovery.EndpointSlice)
 	if !ok {
-		return nil, fmt.Errorf("Passed object is not a slice")
+		return nil, fmt.Errorf("passed object is not a slice")
 	}
 	serviceKey, err := ServiceKeyForSlice(endpointSlice)
 	if err != nil {
 		return nil, err
 	}
-	return []string{string(serviceKey.String())}, nil
+	return []string{serviceKey.String()}, nil
 }
 
 func serviceNameForSlice(endpointSlice *discovery.EndpointSlice) (string, error) {
diff --git a/internal/k8s/k8s.go b/internal/k8s/k8s.go
index 745daffd..03d558e2 100644
--- a/internal/k8s/k8s.go
+++ b/internal/k8s/k8s.go
@@ -31,7 +31,6 @@ import (
 	"github.com/go-kit/log/level"
 	"github.com/pkg/errors"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
-	v1 "k8s.io/api/core/v1"
 	discovery "k8s.io/api/discovery/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/fields"
@@ -119,8 +118,6 @@ type Config struct {
 //
 // The client uses processName to identify itself to the cluster
 // (e.g. when logging events).
-//
-//nolint:godot
 func New(cfg *Config) (*Client, error) {
 	namespaceSelector := cache.ObjectSelector{
 		Field: fields.ParseSelectorOrDie(fmt.Sprintf("metadata.namespace=%s", cfg.Namespace)),
@@ -142,6 +139,7 @@ func New(cfg *Config) (*Client, error) {
 				&metallbv1beta2.BGPPeer{}:          namespaceSelector,
 				&metallbv1beta1.Community{}:        namespaceSelector,
 				&corev1.Secret{}:                   namespaceSelector,
+				&corev1.ConfigMap{}:                namespaceSelector,
 			},
 		}),
 	})
@@ -347,7 +345,7 @@ func (c *Client) CreateMlSecret(namespace, controllerDeploymentName, secretName
 	// Create the K8S Secret object.
 	_, err = c.client.CoreV1().Secrets(namespace).Create(
 		context.TODO(),
-		&v1.Secret{
+		&corev1.Secret{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: secretName,
 				OwnerReferences: []metav1.OwnerReference{{
@@ -396,19 +394,19 @@ func (c *Client) Run(stopCh <-chan struct{}) error {
 
 // UpdateStatus writes the protected "status" field of svc back into
 // the Kubernetes cluster.
-func (c *Client) UpdateStatus(svc *v1.Service) error {
+func (c *Client) UpdateStatus(svc *corev1.Service) error {
 	_, err := c.client.CoreV1().Services(svc.Namespace).UpdateStatus(context.TODO(), svc, metav1.UpdateOptions{})
 	return err
 }
 
 // Infof logs an informational event about svc to the Kubernetes cluster.
-func (c *Client) Infof(svc *v1.Service, kind, msg string, args ...interface{}) {
-	c.events.Eventf(svc, v1.EventTypeNormal, kind, msg, args...)
+func (c *Client) Infof(svc *corev1.Service, kind, msg string, args ...interface{}) {
+	c.events.Eventf(svc, corev1.EventTypeNormal, kind, msg, args...)
 }
 
 // Errorf logs an error event about svc to the Kubernetes cluster.
-func (c *Client) Errorf(svc *v1.Service, kind, msg string, args ...interface{}) {
-	c.events.Eventf(svc, v1.EventTypeWarning, kind, msg, args...)
+func (c *Client) Errorf(svc *corev1.Service, kind, msg string, args ...interface{}) {
+	c.events.Eventf(svc, corev1.EventTypeWarning, kind, msg, args...)
 }
 
 // UseEndpointSlices detect if Endpoints Slices are enabled in the cluster.
diff --git a/internal/layer2/ip_advertisement.go b/internal/layer2/ip_advertisement.go
index c7a5d415..298a895c 100644
--- a/internal/layer2/ip_advertisement.go
+++ b/internal/layer2/ip_advertisement.go
@@ -23,23 +23,23 @@ func NewIPAdvertisement(ip net.IP, allInterfaces bool, interfaces sets.Set[strin
 	}
 }
 
-func (i1 *IPAdvertisement) Equal(i2 *IPAdvertisement) bool {
-	if i1 == nil && i2 == nil {
+func (i *IPAdvertisement) Equal(other *IPAdvertisement) bool {
+	if i == nil && other == nil {
 		return true
 	}
-	if i1 == nil || i2 == nil {
+	if i == nil || other == nil {
 		return false
 	}
-	if !i1.ip.Equal(i2.ip) {
+	if !i.ip.Equal(other.ip) {
 		return false
 	}
-	if i1.allInterfaces != i2.allInterfaces {
+	if i.allInterfaces != other.allInterfaces {
 		return false
 	}
-	if i1.allInterfaces {
+	if i.allInterfaces {
 		return true
 	}
-	return i1.interfaces.Equal(i2.interfaces)
+	return i.interfaces.Equal(other.interfaces)
 }
 
 func (i *IPAdvertisement) MatchInterfaces(intfs ...string) bool {
diff --git a/internal/logging/logging.go b/internal/logging/logging.go
index 1c864548..f6b94c8c 100644
--- a/internal/logging/logging.go
+++ b/internal/logging/logging.go
@@ -87,7 +87,7 @@ func Init(lvl string) (log.Logger, error) {
 func collectGlogs(f *os.File, logger log.Logger) {
 	defer func() {
 		if err := f.Close(); err != nil {
-			//cant log here, as this is the logger
+			// cant log here, as this is the logger
 			errorString := fmt.Sprintf("Error closing file: %s", err)
 			panic(errorString)
 		}
diff --git a/internal/speakerlist/speakerlist.go b/internal/speakerlist/speakerlist.go
index 8795eea6..f20ab040 100644
--- a/internal/speakerlist/speakerlist.go
+++ b/internal/speakerlist/speakerlist.go
@@ -255,7 +255,7 @@ func (sl *SpeakerList) mlJoin() {
 	if err != nil || nr != len(joinIPs) {
 		level.Error(sl.l).Log("op", "memberDiscovery", "msg", "partial join", "joined", nr, "expected", len(joinIPs), "error", err)
 	} else {
-		level.Info(sl.l).Log("op", "Member detection", "msg", "memberlist join succesfully", "number of other nodes", nr)
+		level.Info(sl.l).Log("op", "Member detection", "msg", "memberlist join successfully", "number of other nodes", nr)
 	}
 }
 
diff --git a/speaker/bgp_controller.go b/speaker/bgp_controller.go
index 638f4927..01abe343 100644
--- a/speaker/bgp_controller.go
+++ b/speaker/bgp_controller.go
@@ -98,6 +98,11 @@ newPeers:
 	if err != nil {
 		return errors.Wrap(err, "failed to sync bfd profiles")
 	}
+	err = c.sessionManager.SyncExtraInfo(cfg.BGPExtras)
+	if err != nil {
+		return errors.Wrap(err, "failed to sync extra info")
+	}
+
 	return c.syncPeers(l)
 }
 
diff --git a/speaker/main.go b/speaker/main.go
index 8b944df3..d9b20a43 100644
--- a/speaker/main.go
+++ b/speaker/main.go
@@ -30,7 +30,6 @@ import (
 	"github.com/prometheus/client_golang/prometheus"
 	"go.universe.tf/metallb/internal/bgp"
 	"go.universe.tf/metallb/internal/config"
-	metallbcfg "go.universe.tf/metallb/internal/config"
 	"go.universe.tf/metallb/internal/k8s"
 	"go.universe.tf/metallb/internal/k8s/controllers"
 	"go.universe.tf/metallb/internal/k8s/epslices"
@@ -161,11 +160,11 @@ func main() {
 		os.Exit(1)
 	}
 
-	var validateConfig metallbcfg.Validate
+	var validateConfig config.Validate
 	if bgpType == "native" {
-		validateConfig = metallbcfg.DiscardFRROnly
+		validateConfig = config.DiscardFRROnly
 	} else {
-		validateConfig = metallbcfg.DiscardNativeOnly
+		validateConfig = config.DiscardNativeOnly
 	}
 
 	client, err := k8s.New(&k8s.Config{
@@ -339,7 +338,6 @@ func (c *controller) handleService(l log.Logger,
 	svc *v1.Service, pool *config.Pool,
 	eps epslices.EpsOrSlices,
 	protocol config.Proto) controllers.SyncState {
-
 	l = log.With(l, "protocol", protocol)
 	handler := c.protocolHandlers[protocol]
 	if handler == nil {
@@ -482,7 +480,6 @@ func parseAnnouncedInterfacesToExclude() (*regexp.Regexp, error) {
 }
 
 func (c *controller) SetConfig(l log.Logger, cfg *config.Config) controllers.SyncState {
-
 	level.Debug(l).Log("event", "startUpdate", "msg", "start of config update")
 	defer level.Debug(l).Log("event", "endUpdate", "msg", "end of config update")
 
diff --git a/tasks.py b/tasks.py
index bcb8ee4e..6e939ded 100644
--- a/tasks.py
+++ b/tasks.py
@@ -691,7 +691,7 @@ def lint(ctx, env="container"):
     convenient to install the golangci-lint binaries on the host. This can be
     achieved by running `inv lint --env host`.
     """
-    version = "1.50.1"
+    version = "1.52.2"
     golangci_cmd = "golangci-lint run --timeout 10m0s ./..."
 
     if env == "container":


Test patch:
diff --git a/api/v1beta1/bgpadvertisement_webhook_test.go b/api/v1beta1/bgpadvertisement_webhook_test.go
index b3402710..a3b469c4 100644
--- a/api/v1beta1/bgpadvertisement_webhook_test.go
+++ b/api/v1beta1/bgpadvertisement_webhook_test.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/go-kit/log"
 	"github.com/google/go-cmp/cmp"
+	v1core "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -37,11 +38,16 @@ func TestValidateBGPAdvertisement(t *testing.T) {
 	getExistingIPAddressPools = func() (*IPAddressPoolList, error) {
 		return &IPAddressPoolList{}, nil
 	}
+	toRestoreNodes := getExistingNodes
+	getExistingNodes = func() (*v1core.NodeList, error) {
+		return &v1core.NodeList{}, nil
+	}
 
 	defer func() {
 		getExistingBGPAdvs = toRestore
 		getExistingAddressPools = toRestoreAddresspools
 		getExistingIPAddressPools = toRestoreIPAddressPools
+		getExistingNodes = toRestoreNodes
 	}()
 
 	tests := []struct {
diff --git a/api/v1beta1/mock_validator_test.go b/api/v1beta1/mock_validator_test.go
index 0be0ecc9..f186b6fb 100644
--- a/api/v1beta1/mock_validator_test.go
+++ b/api/v1beta1/mock_validator_test.go
@@ -4,6 +4,7 @@ package v1beta1
 
 import (
 	"github.com/pkg/errors"
+	v1 "k8s.io/api/core/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
@@ -13,6 +14,7 @@ type mockValidator struct {
 	bgpAdvs        *BGPAdvertisementList
 	l2Advs         *L2AdvertisementList
 	communities    *CommunityList
+	nodes          *v1.NodeList
 	forceError     bool
 }
 
@@ -29,13 +31,15 @@ func (m *mockValidator) Validate(objects ...client.ObjectList) error {
 			m.ipAddressPools = list
 		case *CommunityList:
 			m.communities = list
+		case *v1.NodeList:
+			m.nodes = list
 		default:
 			panic("unexpected type")
 		}
 	}
 
 	if m.forceError {
-		return errors.New("Error!")
+		return errors.New("error!")
 	}
 	return nil
 }
diff --git a/api/v1beta2/mock_validator_test.go b/api/v1beta2/mock_validator_test.go
index 3498d3bc..88593b38 100644
--- a/api/v1beta2/mock_validator_test.go
+++ b/api/v1beta2/mock_validator_test.go
@@ -23,7 +23,7 @@ func (m *mockValidator) Validate(objects ...client.ObjectList) error {
 	}
 
 	if m.forceError {
-		return errors.New("Error!")
+		return errors.New("error!")
 	}
 	return nil
 }
diff --git a/controller/controller_test.go b/controller/controller_test.go
index e02e800f..20d8192a 100644
--- a/controller/controller_test.go
+++ b/controller/controller_test.go
@@ -61,14 +61,6 @@ func statusAssigned(ips []string) v1.ServiceStatus {
 	}
 }
 
-func selector(s string) labels.Selector {
-	ret, err := labels.Parse(s)
-	if err != nil {
-		panic(err)
-	}
-	return ret
-}
-
 // testK8S implements service by recording what the controller wants
 // to do to k8s.
 type testK8S struct {
@@ -117,6 +109,10 @@ func (s *testK8S) gotService(in *v1.Service) *v1.Service {
 }
 
 func TestControllerMutation(t *testing.T) {
+	testSelector, err := labels.Parse("team=metallb")
+	if err != nil {
+		t.Fatalf("failed to parse test selector")
+	}
 	k := &testK8S{t: t}
 	c := &controller{
 		ips:    allocator.New(),
@@ -166,7 +162,7 @@ func TestControllerMutation(t *testing.T) {
 			Name:       "pool8",
 			AutoAssign: true,
 			CIDR:       []*net.IPNet{ipnet("13.14.15.0/31")},
-			ServiceAllocations: &config.ServiceAllocation{ServiceSelectors: []labels.Selector{selector("team=metallb")},
+			ServiceAllocations: &config.ServiceAllocation{ServiceSelectors: []labels.Selector{testSelector},
 				Priority: 9},
 		},
 		"pool9": {
@@ -179,7 +175,7 @@ func TestControllerMutation(t *testing.T) {
 			Name:       "pool10",
 			AutoAssign: true,
 			CIDR:       []*net.IPNet{ipnet("19.20.21.0/31")},
-			ServiceAllocations: &config.ServiceAllocation{ServiceSelectors: []labels.Selector{selector("team=metallb")},
+			ServiceAllocations: &config.ServiceAllocation{ServiceSelectors: []labels.Selector{testSelector},
 				Priority: 8},
 		},
 		"pool11": {
@@ -187,14 +183,14 @@ func TestControllerMutation(t *testing.T) {
 			AutoAssign: true,
 			CIDR:       []*net.IPNet{ipnet("22.23.24.0/31")},
 			ServiceAllocations: &config.ServiceAllocation{Namespaces: sets.New("test-ns1"),
-				ServiceSelectors: []labels.Selector{selector("team=metallb")}, Priority: 8},
+				ServiceSelectors: []labels.Selector{testSelector}, Priority: 8},
 		},
 		"pool12": {
 			Name:       "pool12",
 			AutoAssign: true,
 			CIDR:       []*net.IPNet{ipnet("25.26.27.0/31")},
 			ServiceAllocations: &config.ServiceAllocation{Namespaces: sets.New("test-ns1"),
-				ServiceSelectors: []labels.Selector{selector("team=metallb")}, Priority: 5},
+				ServiceSelectors: []labels.Selector{testSelector}, Priority: 5},
 		},
 	}, ByNamespace: map[string][]string{"test-ns1": {"pool6", "pool7", "pool11", "pool12"}, "test-ns2": {"pool9"}},
 		ByServiceSelector: []string{"pool8", "pool10", "pool11", "pool12"},
diff --git a/e2etest/bgptests/bgp.go b/e2etest/bgptests/bgp.go
index ac0241d8..b0ff3d87 100644
--- a/e2etest/bgptests/bgp.go
+++ b/e2etest/bgptests/bgp.go
@@ -32,7 +32,6 @@ import (
 	"go.universe.tf/metallb/e2etest/pkg/k8s"
 	"go.universe.tf/metallb/e2etest/pkg/mac"
 	"go.universe.tf/metallb/e2etest/pkg/metallb"
-	"go.universe.tf/metallb/e2etest/pkg/service"
 	metallbconfig "go.universe.tf/metallb/internal/config"
 	"go.universe.tf/metallb/internal/pointer"
 
@@ -128,7 +127,7 @@ var _ = ginkgo.Describe("BGP", func() {
 		validateDesiredLB(svc)
 
 		for _, c := range FRRContainers {
-			validateService(cs, svc, allNodes.Items, c)
+			validateService(svc, allNodes.Items, c)
 		}
 	},
 		ginkgo.Entry("IPV4", ipfamily.IPv4, []string{v4PoolAddresses}, func(_ *corev1.Service) {}),
@@ -165,7 +164,7 @@ var _ = ginkgo.Describe("BGP", func() {
 		framework.ExpectNoError(err)
 
 		for _, c := range FRRContainers {
-			validateService(cs, svc, epNodes, c)
+			validateService(svc, epNodes, c)
 		}
 	},
 		ginkgo.Entry("IPV4", ipfamily.IPv4, []string{v4PoolAddresses}, func(_ *corev1.Service) {}),
@@ -227,19 +226,19 @@ var _ = ginkgo.Describe("BGP", func() {
 		framework.ExpectNoError(err)
 
 		svc, _ := testservice.CreateWithBackendPort(cs, f.Namespace.Name, "first-service",
-			service.TestServicePort,
+			testservice.TestServicePort,
 			func(svc *corev1.Service) {
 				svc.Spec.LoadBalancerIP = serviceIP
 				svc.Annotations = map[string]string{"metallb.universe.tf/allow-shared-ip": "foo"}
-				svc.Spec.Ports[0].Port = int32(service.TestServicePort)
+				svc.Spec.Ports[0].Port = int32(testservice.TestServicePort)
 			})
 		defer testservice.Delete(cs, svc)
 		svc1, _ := testservice.CreateWithBackendPort(cs, f.Namespace.Name, "second-service",
-			service.TestServicePort+1,
+			testservice.TestServicePort+1,
 			func(svc *corev1.Service) {
 				svc.Spec.LoadBalancerIP = serviceIP
 				svc.Annotations = map[string]string{"metallb.universe.tf/allow-shared-ip": "foo"}
-				svc.Spec.Ports[0].Port = int32(service.TestServicePort + 1)
+				svc.Spec.Ports[0].Port = int32(testservice.TestServicePort + 1)
 			})
 		defer testservice.Delete(cs, svc1)
 
@@ -247,8 +246,8 @@ var _ = ginkgo.Describe("BGP", func() {
 		validateDesiredLB(svc1)
 
 		for _, c := range FRRContainers {
-			validateService(cs, svc, allNodes.Items, c)
-			validateService(cs, svc1, allNodes.Items, c)
+			validateService(svc, allNodes.Items, c)
+			validateService(svc1, allNodes.Items, c)
 		}
 	},
 		ginkgo.Entry("IPV4", ipfamily.IPv4, []string{v4PoolAddresses}),
@@ -295,7 +294,7 @@ var _ = ginkgo.Describe("BGP", func() {
 			framework.ExpectNoError(err)
 
 			for _, c := range FRRContainers {
-				validateService(cs, svc, allNodes.Items, c)
+				validateService(svc, allNodes.Items, c)
 			}
 		},
 			ginkgo.Entry("IPV4 - test AddressPool defined by address range", []metallbv1beta1.IPAddressPool{
@@ -458,7 +457,7 @@ var _ = ginkgo.Describe("BGP", func() {
 			}
 
 			for _, c := range FRRContainers {
-				validateService(cs, svc, allNodes.Items, c)
+				validateService(svc, allNodes.Items, c)
 			}
 
 			Eventually(func() error {
@@ -624,7 +623,7 @@ var _ = ginkgo.Describe("BGP", func() {
 
 					ginkgo.By(fmt.Sprintf("checking connectivity of service %d to its external VIP", j+1))
 					for _, c := range FRRContainers {
-						validateService(cs, svc, allNodes.Items, c)
+						validateService(svc, allNodes.Items, c)
 					}
 				}
 			}
@@ -740,9 +739,9 @@ var _ = ginkgo.Describe("BGP", func() {
 				framework.ExpectNoError(err)
 
 				for _, c := range FRRContainers {
-					validateService(cs, svcAdvertisement, allNodes.Items, c)
-					validateService(cs, svcAdvertisement1, allNodes.Items, c)
-					validateService(cs, svcNoAdvertisement, allNodes.Items, c)
+					validateService(svcAdvertisement, allNodes.Items, c)
+					validateService(svcAdvertisement1, allNodes.Items, c)
+					validateService(svcNoAdvertisement, allNodes.Items, c)
 					Eventually(func() error {
 						for _, community := range advertisement.Spec.Communities {
 							// Get community value for test cases with Community CRD.
@@ -977,70 +976,147 @@ var _ = ginkgo.Describe("BGP", func() {
 
 	})
 
-	ginkgo.DescribeTable("MetalLB FRR rejects any routes advertised by any neighbor", func(addressesRange, toInject string, pairingIPFamily ipfamily.Family) {
-		resources := metallbconfig.ClusterResources{
-			Pools: []metallbv1beta1.IPAddressPool{
-				{
-					ObjectMeta: metav1.ObjectMeta{
-						Name: "rejectroutes",
-					},
-					Spec: metallbv1beta1.IPAddressPoolSpec{
-						Addresses: []string{
-							addressesRange,
+	ginkgo.Context("MetalLB FRR rejects", func() {
+		ginkgo.DescribeTable("any routes advertised by any neighbor", func(addressesRange, toInject string, pairingIPFamily ipfamily.Family) {
+			resources := metallbconfig.ClusterResources{
+				Pools: []metallbv1beta1.IPAddressPool{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "rejectroutes",
+						},
+						Spec: metallbv1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								addressesRange,
+							},
 						},
 					},
 				},
-			},
-			Peers:   metallb.PeersForContainers(FRRContainers, pairingIPFamily),
-			BGPAdvs: []metallbv1beta1.BGPAdvertisement{emptyBGPAdvertisement},
-		}
+				Peers:   metallb.PeersForContainers(FRRContainers, pairingIPFamily),
+				BGPAdvs: []metallbv1beta1.BGPAdvertisement{emptyBGPAdvertisement},
+			}
 
-		neighborAnnounce := func(frr *frrcontainer.FRR) {
-			frr.NeighborConfig.ToAdvertise = toInject
-		}
+			for _, c := range FRRContainers {
+				err := frrcontainer.PairWithNodes(cs, c, pairingIPFamily, func(frr *frrcontainer.FRR) {
+					frr.NeighborConfig.ToAdvertise = toInject
+				})
+				framework.ExpectNoError(err)
+			}
 
-		for _, c := range FRRContainers {
-			err := frrcontainer.PairWithNodes(cs, c, pairingIPFamily, neighborAnnounce)
+			speakerPods, err := metallb.SpeakerPods(cs)
 			framework.ExpectNoError(err)
-		}
+			checkRoute := func() error {
+				return checkRouteInjected(speakerPods, pairingIPFamily, toInject, "all")
+			}
 
-		err := ConfigUpdater.Update(resources)
-		framework.ExpectNoError(err)
+			err = ConfigUpdater.Update(resources)
+			framework.ExpectNoError(err)
 
-		for _, c := range FRRContainers {
-			validateFRRPeeredWithAllNodes(cs, c, pairingIPFamily)
-		}
-		speakerPods, err := metallb.SpeakerPods(cs)
-		framework.ExpectNoError(err)
+			for _, c := range FRRContainers {
+				validateFRRPeeredWithAllNodes(cs, c, pairingIPFamily)
+			}
 
-		checkRoutesInjected := func() error {
-			for _, pod := range speakerPods {
-				podExec := executor.ForPod(pod.Namespace, pod.Name, "frr")
-				routes, frrRoutesV6, err := frr.Routes(podExec)
+			Consistently(checkRoute, 30*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
+			svc, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "external-local-lb")
+			defer testservice.Delete(cs, svc)
+
+			Consistently(checkRoute, 30*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
+		},
+			ginkgo.Entry("IPV4", "192.168.10.0/24", "172.16.1.1/32", ipfamily.IPv4),
+			ginkgo.Entry("IPV6", "fc00:f853:0ccd:e799::/116", "fc00:f853:ccd:e800::1/128", ipfamily.IPv6),
+		)
+	})
+
+	ginkgo.Context("MetalLB allows adding extra FRR configuration", func() {
+		type whenApply string
+		var before whenApply = "before"
+		var after whenApply = "after"
+		ginkgo.AfterEach(func() {
+			err := k8s.RemoveConfigmap(cs, "bgpextras", metallb.Namespace)
+			framework.ExpectNoError(err)
+		})
+		ginkgo.DescribeTable("to accept any routes advertised by any neighbor", func(addressesRange, toInject string, pairingIPFamily ipfamily.Family, when whenApply) {
+			resources := metallbconfig.ClusterResources{
+				Pools: []metallbv1beta1.IPAddressPool{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "rejectroutes",
+						},
+						Spec: metallbv1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								addressesRange,
+							},
+						},
+					},
+				},
+				Peers:   metallb.PeersForContainers(FRRContainers, pairingIPFamily),
+				BGPAdvs: []metallbv1beta1.BGPAdvertisement{emptyBGPAdvertisement},
+			}
+
+			for i, c := range FRRContainers {
+				err := frrcontainer.PairWithNodes(cs, c, pairingIPFamily, func(frr *frrcontainer.FRR) {
+					// We advertise a different route for each different container, to ensure all
+					// of them are able to advertise regardless of the configuration
+					frr.NeighborConfig.ToAdvertise = fmt.Sprintf(toInject, i+1)
+				})
 				framework.ExpectNoError(err)
+			}
 
-				if pairingIPFamily == ipfamily.IPv6 {
-					routes = frrRoutesV6
+			speakerPods, err := metallb.SpeakerPods(cs)
+			framework.ExpectNoError(err)
+			checkRoutesAreInjected := func() error {
+				for i, c := range FRRContainers {
+					err := checkRouteInjected(speakerPods, pairingIPFamily, fmt.Sprintf(toInject, i+1), c.RouterConfig.VRF)
+					if err == nil {
+						return fmt.Errorf("route not injected from %s", c.Name)
+					}
 				}
+				return nil
+			}
 
-				for _, route := range routes {
-					if route.Destination.String() == toInject {
-						return fmt.Errorf("Found %s in %s routes", toInject, pod.Name)
-					}
+			data := ""
+			for _, c := range FRRContainers {
+				ip := c.Ipv4
+				if pairingIPFamily == ipfamily.IPv6 {
+					ip = c.Ipv6
 				}
+				ruleName := ip
+				if c.RouterConfig.VRF != "" {
+					ruleName = fmt.Sprintf("%s-%s", ip, c.RouterConfig.VRF)
+				}
+				data += fmt.Sprintf("route-map %s-in permit 20\n", ruleName)
+			}
+			extraData := map[string]string{
+				"extras": data,
 			}
-			return nil
-		}
 
-		Consistently(checkRoutesInjected, 30*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
-		svc, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "external-local-lb")
-		defer testservice.Delete(cs, svc)
+			if when == before {
+				ginkgo.By("Applying a configmap that allows incoming routes")
 
-		Consistently(checkRoutesInjected, 30*time.Second, 1*time.Second).ShouldNot(HaveOccurred())
-	},
-		ginkgo.Entry("IPV4", "192.168.10.0/24", "172.16.1.10/32", ipfamily.IPv4),
-		ginkgo.Entry("IPV6", "fc00:f853:0ccd:e799::/116", "fc00:f853:ccd:e800::1/128", ipfamily.IPv6),
-	)
+				err = k8s.CreateConfigmap(cs, "bgpextras", metallb.Namespace, extraData)
+				framework.ExpectNoError(err)
+			}
+
+			ginkgo.By("Applying the FRR configuration")
+			err = ConfigUpdater.Update(resources)
+			framework.ExpectNoError(err)
+
+			for _, c := range FRRContainers {
+				validateFRRPeeredWithAllNodes(cs, c, pairingIPFamily)
+			}
+
+			if when == after {
+				ginkgo.By("Applying a configmap that allows incoming routes")
+				err = k8s.CreateConfigmap(cs, "bgpextras", metallb.Namespace, extraData)
+				framework.ExpectNoError(err)
+			}
+			Eventually(checkRoutesAreInjected, time.Minute, 1*time.Second).Should(Not(HaveOccurred()))
+		},
+			ginkgo.Entry("IPV4 - before config", "192.168.10.0/24", "172.16.1.%d/32", ipfamily.IPv4, before),
+			ginkgo.Entry("IPV6 - before config", "fc00:f853:0ccd:e799::/116", "fc00:f853:ccd:e800::%d/128", ipfamily.IPv6, before),
+			ginkgo.Entry("IPV4 - after config", "192.168.10.0/24", "172.16.1.%d/32", ipfamily.IPv4, after),
+			ginkgo.Entry("IPV6 - after config", "fc00:f853:0ccd:e799::/116", "fc00:f853:ccd:e800::%d/128", ipfamily.IPv6, after),
+		)
+	})
 
 	ginkgo.Context("FRR validate reload feedback", func() {
 		ginkgo.It("should update MetalLB config and log reload-validate success", func() {
@@ -1182,7 +1258,7 @@ var _ = ginkgo.Describe("BGP", func() {
 
 		ginkgo.By("Checking the service is reacheable via BGP")
 		for _, c := range FRRContainers {
-			validateService(cs, svc, allNodes.Items, c)
+			validateService(svc, allNodes.Items, c)
 		}
 
 		checkServiceL2 := func() error {
@@ -1217,7 +1293,7 @@ var _ = ginkgo.Describe("BGP", func() {
 
 		ginkgo.By("Checking the service is still reacheable via BGP")
 		for _, c := range FRRContainers {
-			validateService(cs, svc, allNodes.Items, c)
+			validateService(svc, allNodes.Items, c)
 		}
 
 		ginkgo.By("Deleting the l2 advertisement")
diff --git a/e2etest/bgptests/bgp_multiprotocol.go b/e2etest/bgptests/bgp_multiprotocol.go
index 196eac7b..0ae8f552 100644
--- a/e2etest/bgptests/bgp_multiprotocol.go
+++ b/e2etest/bgptests/bgp_multiprotocol.go
@@ -102,7 +102,7 @@ var _ = ginkgo.Describe("BGP Multiprotocol", func() {
 				validateFRRPeeredWithAllNodes(cs, c, pairingFamily)
 			}
 			for _, c := range FRRContainers {
-				validateService(cs, svc, allNodes.Items, c)
+				validateService(svc, allNodes.Items, c)
 			}
 		},
 			ginkgo.Entry("DUALSTACK - via ipv4",
@@ -191,7 +191,7 @@ var _ = ginkgo.Describe("BGP Multiprotocol", func() {
 				framework.ExpectNoError(err)
 
 				for _, c := range FRRContainers {
-					validateService(cs, svc, allNodes.Items, c)
+					validateService(svc, allNodes.Items, c)
 					for _, ip := range svc.Status.LoadBalancer.Ingress {
 						ingressIP := e2eservice.GetIngressPoint(&ip)
 						Eventually(func() error {
diff --git a/e2etest/bgptests/bgppeer_selector.go b/e2etest/bgptests/bgppeer_selector.go
index e34e0081..95ce168d 100644
--- a/e2etest/bgptests/bgppeer_selector.go
+++ b/e2etest/bgptests/bgppeer_selector.go
@@ -145,12 +145,12 @@ var _ = ginkgo.Describe("BGP Peer Selector", func() {
 			framework.ExpectNoError(err)
 
 			ginkgo.By(fmt.Sprintf("checking connectivity of service 1 to external frr container %s", frrContainerForAdv1.Name))
-			validateService(cs, svcAdvertisement1, allNodes.Items, frrContainerForAdv1)
+			validateService(svcAdvertisement1, allNodes.Items, frrContainerForAdv1)
 			ginkgo.By("checking service 1 not advertised to other frr containers")
 			validateServiceNotAdvertised(svcAdvertisement1, FRRContainers, frrContainerForAdv1.Name, ipFamily)
 
 			ginkgo.By(fmt.Sprintf("checking connectivity of service 2 to external frr container %s", frrContainerForAdv2.Name))
-			validateService(cs, svcAdvertisement2, allNodes.Items, frrContainerForAdv2)
+			validateService(svcAdvertisement2, allNodes.Items, frrContainerForAdv2)
 			ginkgo.By("checking service 2 not advertised to other frr containers")
 			validateServiceNotAdvertised(svcAdvertisement2, FRRContainers, frrContainerForAdv2.Name, ipFamily)
 
@@ -162,12 +162,12 @@ var _ = ginkgo.Describe("BGP Peer Selector", func() {
 
 			for _, c := range FRRContainers {
 				ginkgo.By(fmt.Sprintf("checking connectivity of service 1 to external frr container %s", c.Name))
-				validateService(cs, svcAdvertisement1, allNodes.Items, c)
+				validateService(svcAdvertisement1, allNodes.Items, c)
 			}
 
 			for _, c := range FRRContainers {
 				ginkgo.By(fmt.Sprintf("checking connectivity of service 2 to external frr container %s", c.Name))
-				validateService(cs, svcAdvertisement2, allNodes.Items, c)
+				validateService(svcAdvertisement2, allNodes.Items, c)
 			}
 		},
 		ginkgo.Entry("IPV4", []string{"192.168.10.0/24"},
diff --git a/e2etest/bgptests/dump.go b/e2etest/bgptests/dump.go
index b08ea9e9..010c2d0d 100644
--- a/e2etest/bgptests/dump.go
+++ b/e2etest/bgptests/dump.go
@@ -18,7 +18,7 @@ import (
 )
 
 func dumpBGPInfo(basePath, testName string, cs clientset.Interface, f *framework.Framework) {
-	testPath := path.Join(basePath, strings.Replace(testName, " ", "-", -1))
+	testPath := path.Join(basePath, strings.ReplaceAll(testName, " ", "-"))
 	err := os.Mkdir(testPath, 0755)
 	if err != nil && !errors.Is(err, os.ErrExist) {
 		fmt.Fprintf(os.Stderr, "failed to create test dir: %v\n", err)
diff --git a/e2etest/bgptests/infra_setup.go b/e2etest/bgptests/infra_setup.go
index b37c0934..3d10b66e 100644
--- a/e2etest/bgptests/infra_setup.go
+++ b/e2etest/bgptests/infra_setup.go
@@ -53,7 +53,6 @@ var (
 )
 
 func init() {
-
 	if ip := os.Getenv("PROVISIONING_HOST_EXTERNAL_IPV4"); len(ip) != 0 {
 		hostIPv4 = ip
 	}
@@ -145,7 +144,7 @@ func KindnetContainersSetup(cs *clientset.Clientset) ([]*frrcontainer.FRR, error
 }
 
 /*
-	In order to test MetalLB's announcemnet via VRFs, we:
+	In order to test MetalLB's announcement via VRFs, we:
 
 	* create an additional "vrf-net" docker network
 	* for each node, create a vrf named "red" and move the interface in that vrf
@@ -239,7 +238,7 @@ func infraTearDown(cs *clientset.Clientset, containers []*frrcontainer.FRR, next
 func multiHopSetUp(containers []*frrcontainer.FRR, nextHop nextHopSettings, cs *clientset.Clientset) error {
 	err := addContainerToNetwork(nextHop.nextHopContainerName, nextHop.multiHopNetwork)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to connect %s to %s", nextHop.nextHopContainerName, nextHop.multiHopNetwork)
+		return errors.Wrapf(err, "failed to connect %s to %s", nextHop.nextHopContainerName, nextHop.multiHopNetwork)
 	}
 
 	multiHopRoutes, err := container.Networks(nextHop.nextHopContainerName)
@@ -251,13 +250,13 @@ func multiHopSetUp(containers []*frrcontainer.FRR, nextHop nextHopSettings, cs *
 		if c.Network == nextHop.multiHopNetwork {
 			err = container.AddMultiHop(c, c.Network, nextHop.nodeNetwork, defaultRoutingTable, multiHopRoutes)
 			if err != nil {
-				return errors.Wrapf(err, "Failed to set up the multi-hop network for container %s", c.Name)
+				return errors.Wrapf(err, "failed to set up the multi-hop network for container %s", c.Name)
 			}
 		}
 	}
 	err = addMultiHopToNodes(cs, nextHop.nodeNetwork, nextHop.multiHopNetwork, nextHop.nodeRoutingTable, multiHopRoutes)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to set up the multi-hop network")
+		return errors.Wrapf(err, "failed to set up the multi-hop network")
 	}
 
 	return nil
@@ -271,7 +270,7 @@ func vrfSetup(cs *clientset.Clientset) error {
 	for _, pod := range speakerPods {
 		err := addContainerToNetwork(pod.Spec.NodeName, vrfNetwork)
 		if err != nil {
-			return errors.Wrapf(err, "Failed to connect %s to %s", pod.Spec.NodeName, vrfNetwork)
+			return errors.Wrapf(err, "failed to connect %s to %s", pod.Spec.NodeName, vrfNetwork)
 		}
 
 		err = container.SetupVRFForNetwork(pod.Spec.NodeName, vrfNetwork, vrfName, vrfNextHopSettings.nodeRoutingTable)
@@ -492,7 +491,7 @@ func vrfContainersConfig() map[string]frrcontainer.Config {
 func multiHopTearDown(nextHop nextHopSettings, routes map[string]container.NetworkSettings, cs *clientset.Clientset) error {
 	out, err := executor.Host.Exec(executor.ContainerRuntime, "network", "rm", nextHop.multiHopNetwork)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to remove %s: %s", nextHop.multiHopNetwork, out)
+		return errors.Wrapf(err, "failed to remove %s: %s", nextHop.multiHopNetwork, out)
 	}
 
 	speakerPods, err := metallb.SpeakerPods(cs)
@@ -503,9 +502,8 @@ func multiHopTearDown(nextHop nextHopSettings, routes map[string]container.Netwo
 		nodeExec := executor.ForContainer(pod.Spec.NodeName)
 		err = container.DeleteMultiHop(nodeExec, nextHop.nodeNetwork, nextHop.multiHopNetwork, nextHop.nodeRoutingTable, routes)
 		if err != nil {
-			return errors.Wrapf(err, "Failed to delete multihop routes for pod %s", pod.ObjectMeta.Name)
+			return errors.Wrapf(err, "failed to delete multihop routes for pod %s", pod.ObjectMeta.Name)
 		}
-
 	}
 
 	return nil
@@ -537,7 +535,7 @@ func addMultiHopToNodes(cs *clientset.Clientset, targetNetwork, multiHopNetwork
 // The valid names are: ibgp-single-hop / ibgp-multi-hop / ebgp-single-hop / ebgp-multi-hop.
 func validateContainersNames(containerNames string) error {
 	if len(containerNames) == 0 {
-		return fmt.Errorf("Failed to validate containers names: got empty string")
+		return fmt.Errorf("failed to validate containers names: got empty string")
 	}
 	validNames := map[string]bool{
 		"ibgp-single-hop": true,
@@ -549,10 +547,10 @@ func validateContainersNames(containerNames string) error {
 	for _, n := range names {
 		v, ok := validNames[n]
 		if !ok {
-			return fmt.Errorf("Failed to validate container name: %s invalid name", n)
+			return fmt.Errorf("failed to validate container name: %s invalid name", n)
 		}
 		if !v {
-			return fmt.Errorf("Failed to validate container name: %s duplicate name", n)
+			return fmt.Errorf("failed to validate container name: %s duplicate name", n)
 		}
 		validNames[n] = false
 	}
@@ -587,7 +585,7 @@ func addContainerToNetwork(containerName, network string) error {
 		return nil
 	}
 	if err != nil {
-		return errors.Wrapf(err, "Failed to connect %s to %s: %s", containerName, network, out)
+		return errors.Wrapf(err, "failed to connect %s to %s: %s", containerName, network, out)
 	}
 	return nil
 }
diff --git a/e2etest/bgptests/node_selector.go b/e2etest/bgptests/node_selector.go
index 5d45bffe..49a0e07e 100644
--- a/e2etest/bgptests/node_selector.go
+++ b/e2etest/bgptests/node_selector.go
@@ -22,7 +22,6 @@ import (
 	frrconfig "go.universe.tf/metallb/e2etest/pkg/frr/config"
 	frrcontainer "go.universe.tf/metallb/e2etest/pkg/frr/container"
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	clientset "k8s.io/client-go/kubernetes"
 	"k8s.io/kubernetes/test/e2e/framework"
 	admissionapi "k8s.io/pod-security-admission/api"
@@ -31,7 +30,7 @@ import (
 var _ = ginkgo.Describe("BGP Node Selector", func() {
 	var cs clientset.Interface
 	var f *framework.Framework
-	var nodeToLabel *v1.Node
+	var nodeToLabel *corev1.Node
 
 	ginkgo.AfterEach(func() {
 		if nodeToLabel != nil {
@@ -124,8 +123,8 @@ var _ = ginkgo.Describe("BGP Node Selector", func() {
 			secondService, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "second-lb", testservice.WithSpecificPool("second-pool"))
 			defer testservice.Delete(cs, secondService)
 
-			checkServiceOnlyOnNodes(cs, firstService, expectedNodesForFirstPool, pairingIPFamily)
-			checkServiceOnlyOnNodes(cs, secondService, expectedNodesForSecondPool, pairingIPFamily)
+			checkServiceOnlyOnNodes(firstService, expectedNodesForFirstPool, pairingIPFamily)
+			checkServiceOnlyOnNodes(secondService, expectedNodesForSecondPool, pairingIPFamily)
 		},
 		ginkgo.Entry("IPV4 - two on first, two on second", ipfamily.IPv4, []string{"192.168.10.0/24", "192.168.16.0/24"}, []int{0, 1}, []int{0, 1}),
 		ginkgo.Entry("IPV4 - one on first, two on second", ipfamily.IPv4, []string{"192.168.10.0/24", "192.168.16.0/24"}, []int{0}, []int{0, 1}),
@@ -187,8 +186,8 @@ var _ = ginkgo.Describe("BGP Node Selector", func() {
 		svc, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "first-lb", testservice.TrafficPolicyCluster)
 		defer testservice.Delete(cs, svc)
 
-		checkCommunitiesOnlyOnNodes(cs, svc, CommunityNoAdv, expectedNodesForFirstAdv, pairingIPFamily)
-		checkCommunitiesOnlyOnNodes(cs, svc, CommunityGracefulShut, expectedNodesForSecondAdv, pairingIPFamily)
+		checkCommunitiesOnlyOnNodes(svc, CommunityNoAdv, expectedNodesForFirstAdv, pairingIPFamily)
+		checkCommunitiesOnlyOnNodes(svc, CommunityGracefulShut, expectedNodesForSecondAdv, pairingIPFamily)
 	},
 		ginkgo.Entry("IPV4 - two on first, two on second", ipfamily.IPv4, "192.168.10.0/24", []int{0, 1}, []int{0, 1}),
 		ginkgo.Entry("IPV4 - one on first, two on second", ipfamily.IPv4, "192.168.10.0/24", []int{0}, []int{0, 1}),
@@ -246,18 +245,18 @@ var _ = ginkgo.Describe("BGP Node Selector", func() {
 			defer testservice.Delete(cs, svc)
 
 			ginkgo.By("Validating service IP not advertised")
-			checkServiceNotOnNodes(cs, svc, allNodes.Items, pairingIPFamily)
+			checkServiceNotOnNodes(svc, allNodes.Items, pairingIPFamily)
 
 			nodeToLabel = &allNodes.Items[0]
 			ginkgo.By(fmt.Sprintf("Adding advertisement label to node %s", nodeToLabel.Name))
 			k8s.AddLabelToNode(nodeToLabel.Name, "bgp-node-selector-test", "true", cs)
 
 			ginkgo.By(fmt.Sprintf("Validating service IP advertised by %s", nodeToLabel.Name))
-			checkServiceOnlyOnNodes(cs, svc, []v1.Node{*nodeToLabel}, pairingIPFamily)
+			checkServiceOnlyOnNodes(svc, []corev1.Node{*nodeToLabel}, pairingIPFamily)
 
 			ginkgo.By("Validating service IP not advertised by other nodes")
 			nodesNotSelected := nodesNotSelected(allNodes.Items, []int{0})
-			checkServiceNotOnNodes(cs, svc, nodesNotSelected, pairingIPFamily)
+			checkServiceNotOnNodes(svc, nodesNotSelected, pairingIPFamily)
 		},
 		ginkgo.Entry("IPV4", ipfamily.IPv4, "192.168.10.0/24"),
 		ginkgo.Entry("IPV6", ipfamily.IPv6, "fc00:f853:0ccd:e799::/116"),
@@ -295,7 +294,7 @@ var _ = ginkgo.Describe("BGP Node Selector", func() {
 							p.Spec.NodeSelectors = k8s.SelectorsForNodes(nodes)
 							return
 						}
-						p.Spec.NodeSelectors = k8s.SelectorsForNodes([]v1.Node{})
+						p.Spec.NodeSelectors = k8s.SelectorsForNodes([]corev1.Node{})
 					}),
 			}
 			err = ConfigUpdater.Update(resources)
@@ -309,8 +308,8 @@ var _ = ginkgo.Describe("BGP Node Selector", func() {
 			for i, c := range FRRContainers {
 				selected := nodesForSelection(allNodes.Items, nodesForPeers[i])
 				nonSelected := nodesNotSelected(allNodes.Items, nodesForPeers[i])
-				validateFRRPeeredWithNodes(cs, selected, c, ipfamily.IPv4)
-				validateFRRNotPeeredWithNodes(cs, nonSelected, c, ipfamily.IPv4)
+				validateFRRPeeredWithNodes(selected, c, ipfamily.IPv4)
+				validateFRRNotPeeredWithNodes(nonSelected, c, ipfamily.IPv4)
 			}
 
 		},
diff --git a/e2etest/bgptests/validate.go b/e2etest/bgptests/validate.go
index 060078ca..f3d074d0 100644
--- a/e2etest/bgptests/validate.go
+++ b/e2etest/bgptests/validate.go
@@ -32,10 +32,10 @@ import (
 func validateFRRPeeredWithAllNodes(cs clientset.Interface, c *frrcontainer.FRR, ipFamily ipfamily.Family) {
 	allNodes, err := cs.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
 	framework.ExpectNoError(err)
-	validateFRRPeeredWithNodes(cs, allNodes.Items, c, ipFamily)
+	validateFRRPeeredWithNodes(allNodes.Items, c, ipFamily)
 }
 
-func validateFRRNotPeeredWithNodes(cs clientset.Interface, nodes []corev1.Node, c *frrcontainer.FRR, ipFamily ipfamily.Family) {
+func validateFRRNotPeeredWithNodes(nodes []corev1.Node, c *frrcontainer.FRR, ipFamily ipfamily.Family) {
 	for _, node := range nodes {
 		ginkgo.By(fmt.Sprintf("checking node %s is not peered with the frr instance %s", node.Name, c.Name))
 		Eventually(func() error {
@@ -47,7 +47,7 @@ func validateFRRNotPeeredWithNodes(cs clientset.Interface, nodes []corev1.Node,
 	}
 }
 
-func validateFRRPeeredWithNodes(cs clientset.Interface, nodes []corev1.Node, c *frrcontainer.FRR, ipFamily ipfamily.Family) {
+func validateFRRPeeredWithNodes(nodes []corev1.Node, c *frrcontainer.FRR, ipFamily ipfamily.Family) {
 	ginkgo.By(fmt.Sprintf("checking nodes are peered with the frr instance %s", c.Name))
 	Eventually(func() error {
 		neighbors, err := frr.NeighborsInfo(c)
@@ -60,13 +60,13 @@ func validateFRRPeeredWithNodes(cs clientset.Interface, nodes []corev1.Node, c *
 	}, 4*time.Minute, 1*time.Second).Should(BeNil())
 }
 
-func validateService(cs clientset.Interface, svc *corev1.Service, nodes []corev1.Node, c *frrcontainer.FRR) {
+func validateService(svc *corev1.Service, nodes []corev1.Node, c *frrcontainer.FRR) {
 	Eventually(func() error {
-		return validateServiceNoWait(cs, svc, nodes, c)
+		return validateServiceNoWait(svc, nodes, c)
 	}, 4*time.Minute, 1*time.Second).Should(BeNil())
 }
 
-func validateServiceNoWait(cs clientset.Interface, svc *corev1.Service, nodes []corev1.Node, c *frrcontainer.FRR) error {
+func validateServiceNoWait(svc *corev1.Service, nodes []corev1.Node, c *frrcontainer.FRR) error {
 	port := strconv.Itoa(int(svc.Spec.Ports[0].Port))
 
 	if len(svc.Status.LoadBalancer.Ingress) == 2 {
@@ -75,7 +75,6 @@ func validateServiceNoWait(cs clientset.Interface, svc *corev1.Service, nodes []
 		framework.ExpectNotEqual(ip1.To4(), ip2.To4())
 	}
 	for _, ip := range svc.Status.LoadBalancer.Ingress {
-
 		ingressIP := e2eservice.GetIngressPoint(&ip)
 
 		// TODO: in case of VRF there's currently no host wiring to the service.
@@ -155,16 +154,16 @@ func frrIsPairedOnPods(cs clientset.Interface, n *frrcontainer.FRR, ipFamily ipf
 
 func checkBFDConfigPropagated(nodeConfig metallbv1beta1.BFDProfile, peerConfig bgpfrr.BFDPeer) error {
 	if peerConfig.Status != "up" {
-		return fmt.Errorf("Peer status not up")
+		return fmt.Errorf("peer status not up")
 	}
 	if peerConfig.RemoteReceiveInterval != int(*nodeConfig.Spec.ReceiveInterval) {
-		return fmt.Errorf("RemoteReceiveInterval: expecting %d, got %d", *nodeConfig.Spec.ReceiveInterval, peerConfig.RemoteReceiveInterval)
+		return fmt.Errorf("remoteReceiveInterval: expecting %d, got %d", *nodeConfig.Spec.ReceiveInterval, peerConfig.RemoteReceiveInterval)
 	}
 	if peerConfig.RemoteTransmitInterval != int(*nodeConfig.Spec.TransmitInterval) {
-		return fmt.Errorf("RemoteTransmitInterval: expecting %d, got %d", *nodeConfig.Spec.TransmitInterval, peerConfig.RemoteTransmitInterval)
+		return fmt.Errorf("remoteTransmitInterval: expecting %d, got %d", *nodeConfig.Spec.TransmitInterval, peerConfig.RemoteTransmitInterval)
 	}
 	if peerConfig.RemoteEchoReceiveInterval != int(*nodeConfig.Spec.EchoInterval) {
-		return fmt.Errorf("EchoInterval: expecting %d, got %d", *nodeConfig.Spec.EchoInterval, peerConfig.RemoteEchoReceiveInterval)
+		return fmt.Errorf("echoInterval: expecting %d, got %d", *nodeConfig.Spec.EchoInterval, peerConfig.RemoteEchoReceiveInterval)
 	}
 	return nil
 }
@@ -177,7 +176,7 @@ func validateDesiredLB(svc *corev1.Service) {
 	framework.ExpectEqual(desiredLbIPs, strings.Join(getIngressIPs(svc.Status.LoadBalancer.Ingress), ","))
 }
 
-func checkServiceOnlyOnNodes(cs clientset.Interface, svc *corev1.Service, expectedNodes []corev1.Node, ipFamily ipfamily.Family) {
+func checkServiceOnlyOnNodes(svc *corev1.Service, expectedNodes []corev1.Node, ipFamily ipfamily.Family) {
 	if len(expectedNodes) == 0 {
 		return
 	}
@@ -186,7 +185,7 @@ func checkServiceOnlyOnNodes(cs clientset.Interface, svc *corev1.Service, expect
 	for _, c := range FRRContainers {
 		nodeIps, err := k8s.NodeIPsForFamily(expectedNodes, ipFamily, c.RouterConfig.VRF)
 		framework.ExpectNoError(err)
-		validateService(cs, svc, expectedNodes, c)
+		validateService(svc, expectedNodes, c)
 		Eventually(func() error {
 			routes, err := frr.RoutesForFamily(c, ipFamily)
 			if len(routes[ip].NextHops) != len(nodeIps) {
@@ -200,14 +199,14 @@ func checkServiceOnlyOnNodes(cs clientset.Interface, svc *corev1.Service, expect
 						continue OUTER
 					}
 				}
-				return fmt.Errorf("UnexpectedIP found %s, nodes %s in container %s for service %s", n.String(), nodeIps, c.Name, ip)
+				return fmt.Errorf("unexpectedIP found %s, nodes %s in container %s for service %s", n.String(), nodeIps, c.Name, ip)
 			}
 			return err
 		}, time.Minute, time.Second).Should(Not(HaveOccurred()))
 	}
 }
 
-func checkServiceNotOnNodes(cs clientset.Interface, svc *corev1.Service, expectedNodes []corev1.Node, ipFamily ipfamily.Family) {
+func checkServiceNotOnNodes(svc *corev1.Service, expectedNodes []corev1.Node, ipFamily ipfamily.Family) {
 	if len(expectedNodes) == 0 {
 		return
 	}
@@ -231,7 +230,7 @@ func checkServiceNotOnNodes(cs clientset.Interface, svc *corev1.Service, expecte
 	}
 }
 
-func checkCommunitiesOnlyOnNodes(cs clientset.Interface, svc *corev1.Service, community string, expectedNodes []corev1.Node, ipFamily ipfamily.Family) {
+func checkCommunitiesOnlyOnNodes(svc *corev1.Service, community string, expectedNodes []corev1.Node, ipFamily ipfamily.Family) {
 	if len(expectedNodes) == 0 {
 		return
 	}
@@ -254,7 +253,7 @@ func checkCommunitiesOnlyOnNodes(cs clientset.Interface, svc *corev1.Service, co
 						continue OUTER
 					}
 				}
-				return fmt.Errorf("UnexpectedIP found %s, nodes %s in container %s for service %s", n.String(), nodeIps, c.Name, ip)
+				return fmt.Errorf("unexpectedIP found %s, nodes %s in container %s for service %s", n.String(), nodeIps, c.Name, ip)
 			}
 			return err
 		}, 10*time.Minute, time.Second).Should(Not(HaveOccurred()))
@@ -265,7 +264,7 @@ func nodesForSelection(nodes []corev1.Node, selected []int) []corev1.Node {
 	selectedNodes := []corev1.Node{}
 	for _, i := range selected {
 		if i >= len(nodes) {
-			ginkgo.Skip("Not enough nodes")
+			ginkgo.Skip("not enough nodes")
 		}
 		selectedNodes = append(selectedNodes, nodes[i])
 	}
@@ -350,3 +349,22 @@ func validateServiceNotInRoutesForCommunity(c *frrcontainer.FRR, community strin
 		return nil
 	}, 4*time.Minute, 1*time.Second).Should(MatchError(ContainSubstring("not in routes")))
 }
+
+func checkRouteInjected(pods []*corev1.Pod, pairingFamily ipfamily.Family, routeToCheck, vrf string) error {
+	for _, pod := range pods {
+		podExec := executor.ForPod(pod.Namespace, pod.Name, "frr")
+		routes, frrRoutesV6, err := frr.RoutesForVRF(vrf, podExec)
+		framework.ExpectNoError(err)
+
+		if pairingFamily == ipfamily.IPv6 {
+			routes = frrRoutesV6
+		}
+
+		for _, route := range routes {
+			if route.Destination.String() == routeToCheck {
+				return fmt.Errorf("found %s in %s routes", routeToCheck, pod.Name)
+			}
+		}
+	}
+	return nil
+}
diff --git a/e2etest/l2tests/assignment.go b/e2etest/l2tests/assignment.go
index 3799b3c1..00348d4b 100644
--- a/e2etest/l2tests/assignment.go
+++ b/e2etest/l2tests/assignment.go
@@ -15,8 +15,6 @@ import (
 	"go.universe.tf/metallb/e2etest/pkg/service"
 	internalconfig "go.universe.tf/metallb/internal/config"
 
-	testservice "go.universe.tf/metallb/e2etest/pkg/service"
-	corev1 "k8s.io/api/core/v1"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	clientset "k8s.io/client-go/kubernetes"
@@ -25,6 +23,18 @@ import (
 	admissionapi "k8s.io/pod-security-admission/api"
 )
 
+const secondNamespace = "test-namespace"
+
+var (
+	firstNsLabels = map[string]string{
+		"first-ns": "true",
+	}
+	secondNsLabels = map[string]string{
+		"second-ns":                    "true",
+		admissionapi.EnforceLevelLabel: string(admissionapi.LevelPrivileged),
+	}
+)
+
 var _ = ginkgo.Describe("IP Assignment", func() {
 	var cs clientset.Interface
 
@@ -37,6 +47,8 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 		// Clean previous configuration.
 		err := ConfigUpdater.Clean()
 		framework.ExpectNoError(err)
+		err = k8s.DeleteNamespace(cs, secondNamespace)
+		framework.ExpectNoError(err)
 	})
 
 	f = framework.NewDefaultFramework("assignment")
@@ -48,6 +60,15 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 		ginkgo.By("Clearing any previous configuration")
 		err := ConfigUpdater.Clean()
 		framework.ExpectNoError(err)
+
+		ginkgo.By("Updating the first namespace labels")
+		err = k8s.ApplyLabelsToNamespace(cs, f.Namespace.Name, firstNsLabels)
+		framework.ExpectNoError(err)
+
+		ginkgo.By("Creating a second namespace")
+		err = k8s.CreateNamespace(cs, secondNamespace, secondNsLabels)
+		framework.ExpectNoError(err)
+
 	})
 
 	ginkgo.Context("IPV4 Assignment", func() {
@@ -83,7 +104,7 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 			})
 			framework.ExpectNoError(err)
 			defer func() {
-				testservice.Delete(cs, svc1)
+				service.Delete(cs, svc1)
 			}()
 
 			gomega.Consistently(func() int {
@@ -164,13 +185,13 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 			err := ConfigUpdater.Update(resources)
 			framework.ExpectNoError(err)
 
-			svc1, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-pool-1")
-			svc2, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-pool-2")
-			svc3, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-pool-3")
+			svc1, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-pool-1")
+			svc2, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-pool-2")
+			svc3, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-pool-3")
 			defer func() {
-				testservice.Delete(cs, svc1)
-				testservice.Delete(cs, svc2)
-				testservice.Delete(cs, svc3)
+				service.Delete(cs, svc1)
+				service.Delete(cs, svc2)
+				service.Delete(cs, svc3)
 			}()
 
 			// The createWithBackend method always wait for service to acquire an ingress IP, so
@@ -232,13 +253,13 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 			err = ConfigUpdater.Update(resources)
 			framework.ExpectNoError(err)
 
-			svc1, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-label-pool-1")
-			svc2, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-label-pool-2")
-			svc3, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-label-pool-3")
+			svc1, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-label-pool-1")
+			svc2, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-label-pool-2")
+			svc3, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-label-pool-3")
 			defer func() {
-				testservice.Delete(cs, svc1)
-				testservice.Delete(cs, svc2)
-				testservice.Delete(cs, svc3)
+				service.Delete(cs, svc1)
+				service.Delete(cs, svc2)
+				service.Delete(cs, svc3)
 			}()
 
 			// The createWithBackend method always wait for service to acquire an ingress IP, so
@@ -295,19 +316,19 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 			err := ConfigUpdater.Update(resources)
 			framework.ExpectNoError(err)
 
-			svcTweakWithLabel := func(svc *corev1.Service) {
+			svcTweakWithLabel := func(svc *v1.Service) {
 				if svc.Labels == nil {
 					svc.Labels = make(map[string]string)
 				}
 				svc.Labels["test"] = "e2e"
 			}
-			svc1, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-svc-label-pool-1", svcTweakWithLabel)
-			svc2, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-svc-label-pool-2", svcTweakWithLabel)
-			svc3, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-test-svc-label-pool-3", svcTweakWithLabel)
+			svc1, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-svc-label-pool-1", svcTweakWithLabel)
+			svc2, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-svc-label-pool-2", svcTweakWithLabel)
+			svc3, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-svc-label-pool-3", svcTweakWithLabel)
 			defer func() {
-				testservice.Delete(cs, svc1)
-				testservice.Delete(cs, svc2)
-				testservice.Delete(cs, svc3)
+				service.Delete(cs, svc1)
+				service.Delete(cs, svc2)
+				service.Delete(cs, svc3)
 			}()
 
 			// The createWithBackend method always wait for service to acquire an ingress IP, so
@@ -363,19 +384,19 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 			err := ConfigUpdater.Update(resources)
 			framework.ExpectNoError(err)
 
-			svcTweakWithLabel := func(svc *corev1.Service) {
+			svcTweakWithLabel := func(svc *v1.Service) {
 				if svc.Labels == nil {
 					svc.Labels = make(map[string]string)
 				}
 				svc.Labels["test"] = "e2e"
 			}
-			svc1, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-ns-svc-label-pool-1", svcTweakWithLabel)
-			svc2, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-ns-svc-label-pool-2", svcTweakWithLabel)
-			svc3, _ := testservice.CreateWithBackend(cs, f.Namespace.Name, "svc-ns-svc-label-pool-3", svcTweakWithLabel)
+			svc1, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-ns-svc-label-pool-1", svcTweakWithLabel)
+			svc2, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-ns-svc-label-pool-2", svcTweakWithLabel)
+			svc3, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-ns-svc-label-pool-3", svcTweakWithLabel)
 			defer func() {
-				testservice.Delete(cs, svc1)
-				testservice.Delete(cs, svc2)
-				testservice.Delete(cs, svc3)
+				service.Delete(cs, svc1)
+				service.Delete(cs, svc2)
+				service.Delete(cs, svc3)
 			}()
 
 			// The createWithBackend method always wait for service to acquire an ingress IP, so
@@ -394,5 +415,66 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 				&svc3.Status.LoadBalancer.Ingress[0]))
 			framework.ExpectNoError(err)
 		})
+
+		ginkgo.It("with namespace with labels", func() {
+			firstNamespacePool := metallbv1beta1.IPAddressPool{
+				ObjectMeta: metav1.ObjectMeta{Name: "first-ns-labels-ip-pool"},
+				Spec: metallbv1beta1.IPAddressPoolSpec{
+					Addresses: []string{
+						"192.168.20.0/32",
+					},
+					AllocateTo: &metallbv1beta1.ServiceAllocation{Priority: 10, NamespaceSelectors: []metav1.LabelSelector{{MatchLabels: firstNsLabels}}},
+				},
+			}
+			secondNamespacePool := metallbv1beta1.IPAddressPool{
+				ObjectMeta: metav1.ObjectMeta{Name: "second-ns-labels-ip-pool"},
+				Spec: metallbv1beta1.IPAddressPoolSpec{
+					Addresses: []string{
+						"192.168.30.0/32",
+					},
+					AllocateTo: &metallbv1beta1.ServiceAllocation{Priority: 10, NamespaceSelectors: []metav1.LabelSelector{{MatchLabels: secondNsLabels}}},
+				},
+			}
+
+			noNamespacePool := metallbv1beta1.IPAddressPool{
+				ObjectMeta: metav1.ObjectMeta{Name: "no-ns-labels-ip-pool"},
+				Spec: metallbv1beta1.IPAddressPoolSpec{
+					Addresses: []string{
+						"192.168.40.0/32",
+					},
+				},
+			}
+
+			resources := internalconfig.ClusterResources{
+				Pools: []metallbv1beta1.IPAddressPool{firstNamespacePool, secondNamespacePool, noNamespacePool},
+			}
+			err := ConfigUpdater.Update(resources)
+			framework.ExpectNoError(err)
+
+			svc1, _ := service.CreateWithBackend(cs, secondNamespace, "second-ns-service")
+			svc2, _ := service.CreateWithBackend(cs, f.Namespace.Name, "default-ns-service")
+			svc3, _ := service.CreateWithBackend(cs, f.Namespace.Name, "default-ns-service2")
+			defer func() {
+				service.Delete(cs, svc1)
+				service.Delete(cs, svc2)
+				service.Delete(cs, svc3)
+			}()
+
+			// The createWithBackend method always wait for service to acquire an ingress IP, so
+			// just validate service ingress ip address are assigned from appropriate ip
+			// address pool.
+			ginkgo.By("validate LoadBalancer IP is allocated from 1st higher priority address pool")
+			err = config.ValidateIPInRange([]metallbv1beta1.IPAddressPool{secondNamespacePool}, e2eservice.GetIngressPoint(
+				&svc1.Status.LoadBalancer.Ingress[0]))
+			framework.ExpectNoError(err)
+			ginkgo.By("validate LoadBalancer IP is allocated from 2nd higher priority address pool")
+			err = config.ValidateIPInRange([]metallbv1beta1.IPAddressPool{firstNamespacePool}, e2eservice.GetIngressPoint(
+				&svc2.Status.LoadBalancer.Ingress[0]))
+			framework.ExpectNoError(err)
+			ginkgo.By("validate LoadBalancer IP is allocated from default address pool")
+			err = config.ValidateIPInRange([]metallbv1beta1.IPAddressPool{noNamespacePool}, e2eservice.GetIngressPoint(
+				&svc3.Status.LoadBalancer.Ingress[0]))
+			framework.ExpectNoError(err)
+		})
 	})
 })
diff --git a/e2etest/l2tests/interface_selector.go b/e2etest/l2tests/interface_selector.go
index 0a3452a1..e0177b0d 100644
--- a/e2etest/l2tests/interface_selector.go
+++ b/e2etest/l2tests/interface_selector.go
@@ -12,7 +12,6 @@ import (
 	metallbv1beta1 "go.universe.tf/metallb/api/v1beta1"
 	internalconfig "go.universe.tf/metallb/internal/config"
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	clientset "k8s.io/client-go/kubernetes"
 	"k8s.io/kubernetes/test/e2e/framework"
@@ -280,7 +279,7 @@ var _ = ginkgo.Describe("L2-interface selector", func() {
 							},
 							Spec: metallbv1beta1.L2AdvertisementSpec{
 								Interfaces:    []string{NodeNics[0]},
-								NodeSelectors: k8s.SelectorsForNodes([]v1.Node{node}),
+								NodeSelectors: k8s.SelectorsForNodes([]corev1.Node{node}),
 							},
 						},
 					},
diff --git a/e2etest/l2tests/l2.go b/e2etest/l2tests/l2.go
index 0913cfcf..9503ff8c 100644
--- a/e2etest/l2tests/l2.go
+++ b/e2etest/l2tests/l2.go
@@ -398,7 +398,7 @@ var _ = ginkgo.Describe("L2", func() {
 				return errors.New("service2 not announced")
 			}
 			if service1Announce != service2Announce {
-				return fmt.Errorf("Service announced from different nodes %s %s", service1Announce, service2Announce)
+				return fmt.Errorf("service announced from different nodes %s %s", service1Announce, service2Announce)
 			}
 			return nil
 		}, 2*time.Minute, 1*time.Second).Should(gomega.BeNil())
diff --git a/e2etest/l2tests/node_selector.go b/e2etest/l2tests/node_selector.go
index 6fba4585..e6fbc148 100644
--- a/e2etest/l2tests/node_selector.go
+++ b/e2etest/l2tests/node_selector.go
@@ -20,7 +20,6 @@ import (
 
 	internalconfig "go.universe.tf/metallb/internal/config"
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	clientset "k8s.io/client-go/kubernetes"
@@ -31,7 +30,7 @@ import (
 
 var _ = ginkgo.Describe("L2", func() {
 	var cs clientset.Interface
-	var nodeToLabel *v1.Node
+	var nodeToLabel *corev1.Node
 
 	var f *framework.Framework
 	ginkgo.AfterEach(func() {
@@ -99,7 +98,7 @@ var _ = ginkgo.Describe("L2", func() {
 						Name: "with-selector",
 					},
 					Spec: metallbv1beta1.L2AdvertisementSpec{
-						NodeSelectors: k8s.SelectorsForNodes([]v1.Node{node}),
+						NodeSelectors: k8s.SelectorsForNodes([]corev1.Node{node}),
 					},
 				}
 
@@ -170,7 +169,7 @@ var _ = ginkgo.Describe("L2", func() {
 						Name: "with-selector",
 					},
 					Spec: metallbv1beta1.L2AdvertisementSpec{
-						NodeSelectors: k8s.SelectorsForNodes([]v1.Node{allNodes.Items[1]}),
+						NodeSelectors: k8s.SelectorsForNodes([]corev1.Node{allNodes.Items[1]}),
 					},
 				}, {
 					ObjectMeta: metav1.ObjectMeta{
diff --git a/e2etest/pkg/config/update.go b/e2etest/pkg/config/update.go
index 4f980e5c..1adce1b3 100644
--- a/e2etest/pkg/config/update.go
+++ b/e2etest/pkg/config/update.go
@@ -27,7 +27,7 @@ type beta1Updater struct {
 	namespace string
 }
 
-func UpdaterForCRs(r *rest.Config, ns string) (*beta1Updater, error) {
+func UpdaterForCRs(r *rest.Config, ns string) (Updater, error) {
 	myScheme := runtime.NewScheme()
 
 	if err := metallbv1beta1.AddToScheme(myScheme); err != nil {
@@ -67,49 +67,49 @@ func (o beta1Updater) Update(r config.ClusterResources) error {
 	for _, pool := range r.Pools {
 		objects[key] = pool.DeepCopy()
 		oldValues[key] = pool.DeepCopy()
-		key = key + 1
+		key++
 	}
 
 	for _, secret := range r.PasswordSecrets {
 		objects[key] = secret.DeepCopy()
 		oldValues[key] = secret.DeepCopy()
-		key = key + 1
+		key++
 	}
 
 	for _, peer := range r.Peers {
 		objects[key] = peer.DeepCopy()
 		oldValues[key] = peer.DeepCopy()
-		key = key + 1
+		key++
 	}
 
 	for _, bfdProfile := range r.BFDProfiles {
 		objects[key] = bfdProfile.DeepCopy()
 		oldValues[key] = bfdProfile.DeepCopy()
-		key = key + 1
+		key++
 	}
 
 	for _, bgpAdv := range r.BGPAdvs {
 		objects[key] = bgpAdv.DeepCopy()
 		oldValues[key] = bgpAdv.DeepCopy()
-		key = key + 1
+		key++
 	}
 
 	for _, l2Adv := range r.L2Advs {
 		objects[key] = l2Adv.DeepCopy()
 		oldValues[key] = l2Adv.DeepCopy()
-		key = key + 1
+		key++
 	}
 
 	for _, legacyPool := range r.LegacyAddressPools {
 		objects[key] = legacyPool.DeepCopy()
 		oldValues[key] = legacyPool.DeepCopy()
-		key = key + 1
+		key++
 	}
 
 	for _, community := range r.Communities {
 		objects[key] = community.DeepCopy()
 		oldValues[key] = community.DeepCopy()
-		key = key + 1
+		key++
 	}
 
 	// Iterating over the map will return the items in a random order.
diff --git a/e2etest/pkg/container/routes.go b/e2etest/pkg/container/routes.go
index 89f3c4d2..3f9cac68 100644
--- a/e2etest/pkg/container/routes.go
+++ b/e2etest/pkg/container/routes.go
@@ -72,7 +72,7 @@ func SetupVRFForNetwork(containerName, vrfNetwork, vrfName, vrfRoutingTable stri
 	}
 	r, ok := containerNetworks[vrfNetwork]
 	if !ok {
-		return fmt.Errorf("Network %s not found in container %s", vrfNetwork, containerName)
+		return fmt.Errorf("network %s not found in container %s", vrfNetwork, containerName)
 	}
 	exec := executor.ForContainer(containerName)
 	// Get the interface beloning to the given network
diff --git a/e2etest/pkg/executor/executor.go b/e2etest/pkg/executor/executor.go
index 2cbab292..b5e18589 100644
--- a/e2etest/pkg/executor/executor.go
+++ b/e2etest/pkg/executor/executor.go
@@ -51,7 +51,7 @@ type podExecutor struct {
 	container string
 }
 
-func ForPod(namespace, name, container string) *podExecutor {
+func ForPod(namespace, name, container string) Executor {
 	return &podExecutor{
 		namespace: namespace,
 		name:      name,
diff --git a/e2etest/pkg/frr/bgp.go b/e2etest/pkg/frr/bgp.go
index 8e027c44..413665ea 100644
--- a/e2etest/pkg/frr/bgp.go
+++ b/e2etest/pkg/frr/bgp.go
@@ -49,7 +49,17 @@ func NeighborsInfo(exec executor.Executor) ([]*bgpfrr.Neighbor, error) {
 // Routes returns informations about routes in the given executor
 // first for ipv4 routes and then for ipv6 routes.
 func Routes(exec executor.Executor) (map[string]bgpfrr.Route, map[string]bgpfrr.Route, error) {
-	res, err := exec.Exec("vtysh", "-c", "show bgp ipv4 json")
+	return RoutesForVRF("", exec)
+}
+
+// RoutesForVRF returns informations about routes in the given executor
+// first for ipv4 routes and then for ipv6 routes for the given vrf.
+func RoutesForVRF(vrf string, exec executor.Executor) (map[string]bgpfrr.Route, map[string]bgpfrr.Route, error) {
+	cmd := "show bgp ipv4 json"
+	if vrf != "" {
+		cmd = fmt.Sprintf("show bgp vrf %s ipv4  json", vrf)
+	}
+	res, err := exec.Exec("vtysh", "-c", cmd)
 	if err != nil {
 		return nil, nil, errors.Wrapf(err, "Failed to query routes")
 	}
@@ -57,7 +67,11 @@ func Routes(exec executor.Executor) (map[string]bgpfrr.Route, map[string]bgpfrr.
 	if err != nil {
 		return nil, nil, errors.Wrapf(err, "Failed to parse routes %s", res)
 	}
-	res, err = exec.Exec("vtysh", "-c", "show bgp ipv6 json")
+	cmd = "show bgp ipv6 json"
+	if vrf != "" {
+		cmd = fmt.Sprintf("show bgp vrf %s ipv6 json", vrf)
+	}
+	res, err = exec.Exec("vtysh", "-c", cmd)
 	if err != nil {
 		return nil, nil, errors.Wrapf(err, "Failed to query routes")
 	}
@@ -111,8 +125,8 @@ func RoutesForCommunity(exec executor.Executor, community string, family ipfamil
 
 // NeighborConnected tells if the neighbor in the given
 // json format is connected.
-func NeighborConnected(neighborJson string) (bool, error) {
-	n, err := bgpfrr.ParseNeighbour(neighborJson)
+func NeighborConnected(neighborJSON string) (bool, error) {
+	n, err := bgpfrr.ParseNeighbour(neighborJSON)
 	if err != nil {
 		return false, err
 	}
@@ -129,43 +143,43 @@ func RawDump(exec executor.Executor, filesToDump ...string) (string, error) {
 	if err != nil {
 		allerrs = errors.Wrapf(allerrs, "\nFailed exec show bgp neighbor: %v", err)
 	}
-	res = res + out
+	res += out
 
 	for _, file := range filesToDump {
-		res = res + fmt.Sprintf("####### Dumping file %s\n", file)
+		res += fmt.Sprintf("####### Dumping file %s\n", file)
 		// limiting the output to 500 lines:
 		out, err = exec.Exec("bash", "-c", fmt.Sprintf("cat %s | tail -n 500", file))
 		if err != nil {
 			allerrs = errors.Wrapf(allerrs, "\nFailed to cat file %s: %v", file, err)
 		}
-		res = res + out
+		res += out
 	}
 
-	res = res + "####### BGP Neighbors\n"
+	res += "####### BGP Neighbors\n"
 	out, err = exec.Exec("vtysh", "-c", "show bgp neighbor")
 	if err != nil {
 		allerrs = errors.Wrapf(allerrs, "\nFailed exec show bgp neighbor: %v", err)
 	}
-	res = res + out
+	res += out
 
-	res = res + "####### BFD Peers\n"
+	res += "####### BFD Peers\n"
 	out, err = exec.Exec("vtysh", "-c", "show bfd peer")
 	if err != nil {
 		allerrs = errors.Wrapf(allerrs, "\nFailed exec show bfd peer: %v", err)
 	}
-	res = res + out
+	res += out
 
-	res = res + "####### Check for any crashinfo files\n"
+	res += "####### Check for any crashinfo files\n"
 	if crashInfo, err := exec.Exec("bash", "-c", "ls /var/tmp/frr/bgpd.*/crashlog"); err == nil {
 		crashInfo = strings.TrimSuffix(crashInfo, "\n")
 		files := strings.Split(crashInfo, "\n")
 		for _, file := range files {
-			res = res + fmt.Sprintf("####### Dumping crash file %s\n", file)
+			res += fmt.Sprintf("####### Dumping crash file %s\n", file)
 			out, err = exec.Exec("bash", "-c", fmt.Sprintf("cat %s", file))
 			if err != nil {
 				allerrs = errors.Wrapf(allerrs, "\nFailed to cat bgpd crashinfo file %s: %v", file, err)
 			}
-			res = res + out
+			res += out
 		}
 	}
 
diff --git a/e2etest/pkg/frr/container/container.go b/e2etest/pkg/frr/container/container.go
index 6b1634af..2c597dd4 100644
--- a/e2etest/pkg/frr/container/container.go
+++ b/e2etest/pkg/frr/container/container.go
@@ -14,7 +14,6 @@ import (
 	"github.com/pkg/errors"
 	"go.universe.tf/metallb/e2etest/pkg/executor"
 	"go.universe.tf/metallb/e2etest/pkg/frr"
-	"go.universe.tf/metallb/e2etest/pkg/frr/config"
 	frrconfig "go.universe.tf/metallb/e2etest/pkg/frr/config"
 	"go.universe.tf/metallb/e2etest/pkg/frr/consts"
 	"go.universe.tf/metallb/internal/ipfamily"
@@ -38,18 +37,18 @@ type FRR struct {
 	executor.Executor
 	Name           string
 	configDir      string
-	NeighborConfig config.NeighborConfig
-	RouterConfig   config.RouterConfig
+	NeighborConfig frrconfig.NeighborConfig
+	RouterConfig   frrconfig.RouterConfig
 	Ipv4           string
 	Ipv6           string
 	Network        string
-	MultiProtocol  config.MultiProtocol
+	MultiProtocol  frrconfig.MultiProtocol
 }
 
 type Config struct {
 	Name     string
-	Neighbor config.NeighborConfig
-	Router   config.RouterConfig
+	Neighbor frrconfig.NeighborConfig
+	Router   frrconfig.RouterConfig
 	HostIPv4 string
 	HostIPv6 string
 	Network  string
@@ -89,7 +88,7 @@ func Create(configs map[string]Config) ([]*FRR, error) {
 				frrContainers = append(frrContainers, c)
 			}
 			if err != nil {
-				return errors.Wrapf(err, "Failed to wait for daemons %v", toFind)
+				return errors.Wrapf(err, "failed to wait for daemons %v", toFind)
 			}
 
 			return nil
@@ -138,12 +137,12 @@ func ConfigureExisting(c map[string]Config) ([]*FRR, error) {
 	for _, cfg := range c {
 		err := containerIsRunning(cfg.Name)
 		if err != nil {
-			return nil, fmt.Errorf("Failed to use an existing container %s. %w", cfg.Name, err)
+			return nil, fmt.Errorf("failed to use an existing container %s. %w", cfg.Name, err)
 		}
 
 		frr, err := configureContainer(cfg, cfg.Name)
 		if err != nil {
-			return nil, fmt.Errorf("Failed to create container configurations for %s. %w", cfg.Name, err)
+			return nil, fmt.Errorf("failed to create container configurations for %s. %w", cfg.Name, err)
 		}
 
 		frrContainers = append(frrContainers, frr)
@@ -162,10 +161,10 @@ func start(cfg Config) (*FRR, error) {
 	srcFiles := fmt.Sprintf("%s/.", frrConfigDir)
 	res, err := exec.Command("cp", "-r", srcFiles, testDirName).CombinedOutput()
 	if err != nil {
-		return nil, errors.Wrapf(err, "Failed to copy FRR config directory. %s", string(res))
+		return nil, errors.Wrapf(err, "failed to copy FRR config directory. %s", string(res))
 	}
 
-	err = config.SetDaemonsConfig(testDirName, cfg.Router)
+	err = frrconfig.SetDaemonsConfig(testDirName, cfg.Router)
 	if err != nil {
 		return nil, err
 	}
@@ -174,12 +173,12 @@ func start(cfg Config) (*FRR, error) {
 	args := []string{"run", "-d", "--privileged", "--network", cfg.Network, "--rm", "--ulimit", "core=-1", "--name", cfg.Name, "--volume", volume, frrImage}
 	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)
+		return nil, errors.Wrapf(err, "failed to start %s container. %s", cfg.Name, out)
 	}
 
 	frr, err := configureContainer(cfg, testDirName)
 	if err != nil {
-		return frr, fmt.Errorf("Failed to create container configurations for %s. %w", cfg.Name, err)
+		return frr, fmt.Errorf("failed to create container configurations for %s. %w", cfg.Name, err)
 	}
 
 	return frr, nil
@@ -232,7 +231,7 @@ func (c *FRR) updateIPS() (err error) {
 	containerIP, err := exec.Command(executor.ContainerRuntime, "inspect", "-f", "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}",
 		c.Name).CombinedOutput()
 	if err != nil {
-		return errors.Wrapf(err, "Failed to get FRR IPv4 address")
+		return errors.Wrapf(err, "failed to get FRR IPv4 address")
 	}
 
 	containerIPv4 := strings.TrimSuffix(string(containerIP), "\n")
@@ -240,13 +239,13 @@ func (c *FRR) updateIPS() (err error) {
 	containerIP, err = exec.Command(executor.ContainerRuntime, "inspect", "-f", "{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}",
 		c.Name).CombinedOutput()
 	if err != nil {
-		return errors.Wrapf(err, "Failed to get FRR IPv6 address")
+		return errors.Wrapf(err, "failed to get FRR IPv6 address")
 	}
 
 	containerIPv6 := strings.TrimSuffix(string(containerIP), "\n")
 
 	if containerIPv4 == "" && containerIPv6 == "" {
-		return errors.Errorf("Failed to get FRR IP addresses")
+		return errors.Errorf("failed to get FRR IP addresses")
 	}
 	c.Ipv4 = containerIPv4
 	c.Ipv6 = containerIPv6
@@ -256,14 +255,14 @@ func (c *FRR) updateIPS() (err error) {
 
 // Updating the BGP config file.
 func (c *FRR) UpdateBGPConfigFile(bgpConfig string) error {
-	err := config.SetBGPConfig(c.configDir, bgpConfig)
+	err := frrconfig.SetBGPConfig(c.configDir, bgpConfig)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to update BGP config file")
+		return errors.Wrapf(err, "failed to update BGP config file")
 	}
 
 	err = reloadFRRConfig(consts.BGPConfigFile, c)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to reload BGP config file")
+		return errors.Wrapf(err, "failed to reload BGP config file")
 	}
 
 	return nil
@@ -274,12 +273,12 @@ func (c *FRR) delete() error {
 	// Kill the BGP router container.
 	out, err := exec.Command(executor.ContainerRuntime, "kill", c.Name).CombinedOutput()
 	if err != nil {
-		return errors.Wrapf(err, "Failed to kill %s container. %s", c.Name, out)
+		return errors.Wrapf(err, "failed to kill %s container. %s", c.Name, out)
 	}
 
 	err = os.RemoveAll(c.configDir)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to delete FRR config directory.")
+		return errors.Wrapf(err, "failed to delete FRR config directory")
 	}
 
 	return nil
@@ -316,7 +315,7 @@ func (c *FRR) updateVolumePermissions() error {
 	cmd := fmt.Sprintf("chown -R %d:%d %s", uid, gid, frrMountPath)
 	out, err := exec.Command(executor.ContainerRuntime, "exec", c.Name, "sh", "-c", cmd).CombinedOutput()
 	if err != nil {
-		return errors.Wrapf(err, "Failed to change %s container volume permissions. %s", c.Name, string(out))
+		return errors.Wrapf(err, "failed to change %s container volume permissions. %s", c.Name, string(out))
 	}
 
 	return nil
@@ -328,14 +327,14 @@ func reloadFRRConfig(configFile string, exec executor.Executor) error {
 	cmd := fmt.Sprintf("python3 /usr/lib/frr/frr-reload.py --test --stdout %s/%s", frrMountPath, configFile)
 	out, err := exec.Exec("sh", "-c", cmd)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to check configuration file. %s", string(out))
+		return errors.Wrapf(err, "failed to check configuration file. %s", out)
 	}
 
 	// Applying the configuration file.
 	cmd = fmt.Sprintf("python3 /usr/lib/frr/frr-reload.py --reload --overwrite --stdout %s/%s", frrMountPath, configFile)
 	out, err = exec.Exec("sh", "-c", cmd)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to apply configuration file. %s", string(out))
+		return errors.Wrapf(err, "failed to apply configuration file. %s", out)
 	}
 
 	return nil
@@ -352,15 +351,15 @@ func isPodman() bool {
 func containerIsRunning(containerName string) error {
 	out, err := exec.Command(executor.ContainerRuntime, "ps", "--format", "{{.Status}}", "--filter", fmt.Sprintf("name=%s", containerName)).CombinedOutput()
 	if err != nil {
-		return fmt.Errorf("Failed to validate container %s is running. %w", containerName, err)
+		return fmt.Errorf("failed to validate container %s is running. %w", containerName, err)
 	}
 
 	if len(out) == 0 {
-		return fmt.Errorf("Container %s doesn't exist.", containerName)
+		return fmt.Errorf("container %s doesn't exist", containerName)
 	}
 
 	if string(out[:2]) != "Up" {
-		return fmt.Errorf("Container %s is not up. status is: %s", containerName, out)
+		return fmt.Errorf("container %s is not up. status is: %s", containerName, out)
 	}
 
 	return nil
diff --git a/e2etest/pkg/frr/validation.go b/e2etest/pkg/frr/validation.go
index 4a5e747a..c0d86d2b 100644
--- a/e2etest/pkg/frr/validation.go
+++ b/e2etest/pkg/frr/validation.go
@@ -27,13 +27,13 @@ func NeighborsMatchNodes(nodes []v1.Node, neighbors []*bgpfrr.Neighbor, ipFamily
 		nodesIPs[ip] = struct{}{}
 	}
 	for _, n := range neighbors {
-		if _, ok := nodesIPs[n.Ip.String()]; !ok { // skipping neighbors that are not nodes
+		if _, ok := nodesIPs[n.IP.String()]; !ok { // skipping neighbors that are not nodes
 			continue
 		}
 		if !n.Connected {
-			return fmt.Errorf("node %s BGP session not established", n.Ip.String())
+			return fmt.Errorf("node %s BGP session not established", n.IP.String())
 		}
-		delete(nodesIPs, n.Ip.String())
+		delete(nodesIPs, n.IP.String())
 	}
 	if len(nodesIPs) != 0 { // some leftover, meaning more nodes than neighbors
 		return fmt.Errorf("IP %v found in nodes but not in neighbors", nodesIPs)
diff --git a/e2etest/pkg/k8s/configmap.go b/e2etest/pkg/k8s/configmap.go
new file mode 100644
index 00000000..7150711f
--- /dev/null
+++ b/e2etest/pkg/k8s/configmap.go
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier:Apache-2.0
+
+package k8s
+
+import (
+	"context"
+
+	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	clientset "k8s.io/client-go/kubernetes"
+)
+
+func CreateConfigmap(cs clientset.Interface, name, namespace string, data map[string]string) error {
+	cm := corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+		Data: data,
+	}
+	_, err := cs.CoreV1().ConfigMaps(namespace).Create(context.Background(), &cm, metav1.CreateOptions{})
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func RemoveConfigmap(cs clientset.Interface, name, namespace string) error {
+	err := cs.CoreV1().ConfigMaps(namespace).Delete(context.Background(), name, metav1.DeleteOptions{})
+	if err != nil && !apierrors.IsNotFound(err) {
+		return err
+	}
+	return nil
+}
diff --git a/e2etest/pkg/k8s/namespace.go b/e2etest/pkg/k8s/namespace.go
new file mode 100644
index 00000000..0e064df9
--- /dev/null
+++ b/e2etest/pkg/k8s/namespace.go
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier:Apache-2.0
+
+package k8s
+
+import (
+	"context"
+	"time"
+
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/wait"
+	clientset "k8s.io/client-go/kubernetes"
+)
+
+func CreateNamespace(cs clientset.Interface, name string, labels map[string]string) error {
+	_, err := cs.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:   name,
+			Labels: labels,
+		},
+	}, metav1.CreateOptions{})
+	return err
+}
+
+func DeleteNamespace(cs clientset.Interface, name string) error {
+	backoff := wait.Backoff{
+		Steps:    5,
+		Duration: 10 * time.Second,
+		Factor:   2,
+	}
+	err := cs.CoreV1().Namespaces().Delete(context.Background(), name, metav1.DeleteOptions{})
+	if err != nil {
+		return err
+	}
+	err = wait.ExponentialBackoff(backoff, func() (bool, error) {
+		_, err := cs.CoreV1().Namespaces().Get(context.Background(), name, metav1.GetOptions{})
+		if errors.IsNotFound(err) {
+			return true, nil
+		}
+		if err != nil {
+			return false, err
+		}
+		return false, nil
+	})
+	return err
+}
+
+func ApplyLabelsToNamespace(cs clientset.Interface, name string, labels map[string]string) error {
+	ns, err := cs.CoreV1().Namespaces().Get(context.Background(), name, metav1.GetOptions{})
+	if err != nil {
+		return err
+	}
+	if ns.Labels == nil {
+		ns.Labels = make(map[string]string)
+	}
+	for k, v := range labels {
+		ns.Labels[k] = v
+	}
+	_, err = cs.CoreV1().Namespaces().Update(context.Background(), ns, metav1.UpdateOptions{})
+	return err
+}
diff --git a/e2etest/pkg/k8s/pods.go b/e2etest/pkg/k8s/pods.go
index 499f2f33..59a1e508 100644
--- a/e2etest/pkg/k8s/pods.go
+++ b/e2etest/pkg/k8s/pods.go
@@ -12,9 +12,9 @@ import (
 	clientset "k8s.io/client-go/kubernetes"
 )
 
-func PodLogsSinceTime(cs clientset.Interface, pod *corev1.Pod, SpeakerContainerName string, sinceTime *metav1.Time) (string, error) {
+func PodLogsSinceTime(cs clientset.Interface, pod *corev1.Pod, speakerContainerName string, sinceTime *metav1.Time) (string, error) {
 	podLogOpt := corev1.PodLogOptions{
-		Container: SpeakerContainerName,
+		Container: speakerContainerName,
 		SinceTime: sinceTime,
 	}
 	return PodLogs(cs, pod, podLogOpt)
diff --git a/e2etest/pkg/k8s/reporter.go b/e2etest/pkg/k8s/reporter.go
index 8b254ff1..14ffd7f9 100644
--- a/e2etest/pkg/k8s/reporter.go
+++ b/e2etest/pkg/k8s/reporter.go
@@ -62,6 +62,6 @@ func InitReporter(kubeconfig, path, namespace string) *k8sreporter.KubernetesRep
 }
 
 func DumpInfo(reporter *k8sreporter.KubernetesReporter, testName string) {
-	testNameNoSpaces := strings.Replace(ginkgo.CurrentSpecReport().LeafNodeText, " ", "-", -1)
+	testNameNoSpaces := strings.ReplaceAll(ginkgo.CurrentSpecReport().LeafNodeText, " ", "-")
 	reporter.Dump(10*time.Minute, testNameNoSpaces)
 }
diff --git a/e2etest/pkg/mac/mac.go b/e2etest/pkg/mac/mac.go
index a1cfca4e..aae52c57 100644
--- a/e2etest/pkg/mac/mac.go
+++ b/e2etest/pkg/mac/mac.go
@@ -174,7 +174,7 @@ func GetIfaceMac(iface string, exec executor.Executor) (net.HardwareAddr, error)
 
 	macRe := regexp.MustCompile("([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})")
 
-	res := strings.Split(string(out), "\n")
+	res := strings.Split(out, "\n")
 	for _, line := range res {
 		cur := strings.Split(line, " ")
 		if cur[0] == iface {
diff --git a/e2etest/pkg/metallb/metallb.go b/e2etest/pkg/metallb/metallb.go
index 65d8c858..3dd27b1b 100644
--- a/e2etest/pkg/metallb/metallb.go
+++ b/e2etest/pkg/metallb/metallb.go
@@ -34,10 +34,10 @@ func SpeakerPods(cs clientset.Interface) ([]*corev1.Pod, error) {
 		LabelSelector: speakerLabelgSelector,
 	})
 	if err != nil {
-		return nil, errors.Wrap(err, "Failed to fetch speaker pods")
+		return nil, errors.Wrap(err, "failed to fetch speaker pods")
 	}
 	if len(speakers.Items) == 0 {
-		return nil, errors.New("No speaker pods found")
+		return nil, errors.New("no speaker pods found")
 	}
 	speakerPods := make([]*corev1.Pod, 0)
 	for _, item := range speakers.Items {
@@ -53,10 +53,10 @@ func ControllerPod(cs clientset.Interface) (*corev1.Pod, error) {
 		LabelSelector: ControllerLabelSelector,
 	})
 	if err != nil {
-		return nil, errors.Wrap(err, "Failed to fetch controller pods")
+		return nil, errors.Wrap(err, "failed to fetch controller pods")
 	}
 	if len(pods.Items) != 1 {
-		return nil, fmt.Errorf("Expected one controller pod, found %d", len(pods.Items))
+		return nil, fmt.Errorf("expected one controller pod, found %d", len(pods.Items))
 	}
 	return &pods.Items[0], nil
 }
diff --git a/e2etest/pkg/metrics/metrics.go b/e2etest/pkg/metrics/metrics.go
index 07d2c519..cde2c9fd 100644
--- a/e2etest/pkg/metrics/metrics.go
+++ b/e2etest/pkg/metrics/metrics.go
@@ -44,8 +44,8 @@ func ForPod(controller, target *corev1.Pod, namespace string) ([]map[string]*dto
 
 	podExecutor := executor.ForPod(namespace, controller.Name, "controller")
 	for _, p := range ports {
-		metricsUrl := path.Join(net.JoinHostPort(target.Status.PodIP, strconv.Itoa(p)), "metrics")
-		metrics, err := podExecutor.Exec("wget", "-qO-", metricsUrl)
+		metricsURL := path.Join(net.JoinHostPort(target.Status.PodIP, strconv.Itoa(p)), "metrics")
+		metrics, err := podExecutor.Exec("wget", "-qO-", metricsURL)
 		if err != nil {
 			return nil, errors.Wrapf(err, "failed to scrape metrics for %s", target.Name)
 		}
@@ -73,7 +73,7 @@ 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.
+// ValidateGaugeValue checks that the value corresponding 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 {
@@ -83,7 +83,7 @@ func ValidateGaugeValue(expectedValue int, metricName string, labels map[string]
 	}, metricName, labels, allMetrics)
 }
 
-// ValidateGaugeValueCompare checks that the value corresponing to the given metric against the given compare function.
+// ValidateGaugeValueCompare checks that the value corresponding 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 {
@@ -102,7 +102,6 @@ func ValidateGaugeValueCompare(check func(int) error, metricName string, labels
 		return fmt.Errorf("metric %s not found", metricName)
 	}
 	return nil
-
 }
 
 // ValidateCounterValue checks that the value related to the given metric is at most the expectedMax value.
diff --git a/e2etest/pkg/metrics/prometheus.go b/e2etest/pkg/metrics/prometheus.go
index 5ef8532f..3213cba6 100644
--- a/e2etest/pkg/metrics/prometheus.go
+++ b/e2etest/pkg/metrics/prometheus.go
@@ -14,7 +14,7 @@ import (
 	clientset "k8s.io/client-go/kubernetes"
 )
 
-// ValidateOnPrometheus checks the existance of the given metric directly on prometheus pods.
+// ValidateOnPrometheus checks the existence of the given metric directly on prometheus pods.
 func ValidateOnPrometheus(prometheusPod *corev1.Pod, query string, expected CheckType) error {
 	exec := executor.ForPod(prometheusPod.Namespace, prometheusPod.Name, prometheusPod.Spec.Containers[0].Name)
 	url := fmt.Sprintf("localhost:9090/api/v1/query?%s", (url.Values{"query": []string{query}}).Encode())
diff --git a/e2etest/pkg/routes/patch.go b/e2etest/pkg/routes/patch.go
index aba8fcb2..565b9f31 100644
--- a/e2etest/pkg/routes/patch.go
+++ b/e2etest/pkg/routes/patch.go
@@ -55,7 +55,7 @@ func Delete(exec executor.Executor, target, via, routingTable string) error {
 	args = append(args, dst.String(), "via", gw.String())
 	out, err := exec.Exec(cmd, args...)
 	if err != nil {
-		return errors.Wrapf(err, "Failed to delete route %s", out)
+		return errors.Wrapf(err, "failed to delete route %s", out)
 	}
 
 	return nil
diff --git a/e2etest/pkg/routes/routes.go b/e2etest/pkg/routes/routes.go
index 5c141847..a6248aaf 100644
--- a/e2etest/pkg/routes/routes.go
+++ b/e2etest/pkg/routes/routes.go
@@ -29,7 +29,7 @@ func init() {
 // (or in the current host) to reach the service ip.
 func ForIP(target string, exec executor.Executor) []net.IP {
 	dst := net.ParseIP(target)
-	framework.ExpectNotEqual(dst, nil, "Failed to convert", target, "to ip")
+	framework.ExpectNotEqual(dst, nil, "failed to convert", target, "to ip")
 
 	re := Ipv4Re
 	res, err := exec.Exec("ip", []string{"route", "show", target}...)
@@ -61,7 +61,7 @@ func ForIP(target string, exec executor.Executor) []net.IP {
 			continue
 		}
 		netIP := net.ParseIP(ip)
-		framework.ExpectNotEqual(netIP, nil, "Failed to convert", ip, "to ip")
+		framework.ExpectNotEqual(netIP, nil, "failed to convert", ip, "to ip")
 		routes = append(routes, netIP)
 	}
 
diff --git a/e2etest/pkg/service/create.go b/e2etest/pkg/service/create.go
index d7051414..3b60101b 100644
--- a/e2etest/pkg/service/create.go
+++ b/e2etest/pkg/service/create.go
@@ -7,7 +7,6 @@ import (
 	"fmt"
 
 	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	clientset "k8s.io/client-go/kubernetes"
@@ -49,11 +48,11 @@ func Delete(cs clientset.Interface, svc *corev1.Service) {
 	framework.ExpectNoError(err)
 }
 
-func tweakServicePort(svc *v1.Service, port int) {
+func tweakServicePort(svc *corev1.Service, port int) {
 	svc.Spec.Ports[0].TargetPort = intstr.FromInt(port)
 }
 
-func tweakRCPort(rc *v1.ReplicationController, port int) {
+func tweakRCPort(rc *corev1.ReplicationController, port int) {
 	rc.Spec.Template.Spec.Containers[0].Args = []string{"netexec", fmt.Sprintf("--http-port=%d", port), fmt.Sprintf("--udp-port=%d", port)}
 	rc.Spec.Template.Spec.Containers[0].ReadinessProbe.HTTPGet.Port = intstr.FromInt(port)
 }
diff --git a/e2etest/pkg/wget/wget.go b/e2etest/pkg/wget/wget.go
index 59080813..af4f522c 100644
--- a/e2etest/pkg/wget/wget.go
+++ b/e2etest/pkg/wget/wget.go
@@ -16,7 +16,7 @@ const (
 
 func Do(address string, exc executor.Executor) error {
 	retrycnt := 0
-	code := 0
+	var code int
 	var err error
 
 	// Retry loop to handle wget NetworkFailure errors
diff --git a/e2etest/webhookstests/webhooks.go b/e2etest/webhookstests/webhooks.go
index 4a63ab0b..a160e01c 100644
--- a/e2etest/webhookstests/webhooks.go
+++ b/e2etest/webhookstests/webhooks.go
@@ -371,7 +371,7 @@ var _ = ginkgo.Describe("Webhooks", func() {
 			ginkgo.By("Deleting the profile used by BGPPeer")
 			err = ConfigUpdater.Client().Delete(context.TODO(), &testBFDProfile, &client.DeleteOptions{})
 			framework.ExpectError(err)
-			Expect(err.Error()).To(ContainSubstring("Failed to delete BFDProfile"))
+			Expect(err.Error()).To(ContainSubstring("failed to delete BFDProfile"))
 
 			ginkgo.By("Deleting the BGPPeer")
 			err = ConfigUpdater.Client().Delete(context.TODO(), &testPeer, &client.DeleteOptions{})
diff --git a/frr-tools/metrics/collector/bfd_test.go b/frr-tools/metrics/collector/bfd_test.go
index 9c959ad7..cd9c4bab 100644
--- a/frr-tools/metrics/collector/bfd_test.go
+++ b/frr-tools/metrics/collector/bfd_test.go
@@ -200,7 +200,7 @@ func TestBFDCollect(t *testing.T) {
 			}
 
 			l := log.NewNopLogger()
-			collector := NewBFD(l)
+			collector := mockNewBFD(l)
 			cmdOutput := map[string]string{
 				"show bgp vrf all json":                    vrfVtysh,
 				"show bfd vrf default peers json":          test.vtyshPeersOutput,
@@ -221,6 +221,5 @@ func TestBFDCollect(t *testing.T) {
 				t.Errorf("expected no error but got %s", err)
 			}
 		})
-
 	}
 }
diff --git a/frr-tools/metrics/collector/bgp_test.go b/frr-tools/metrics/collector/bgp_test.go
index 35557a3f..51cb4362 100644
--- a/frr-tools/metrics/collector/bgp_test.go
+++ b/frr-tools/metrics/collector/bgp_test.go
@@ -466,7 +466,7 @@ func TestCollect(t *testing.T) {
 			}
 
 			l := log.NewNopLogger()
-			collector := NewBGP(l)
+			collector := mocknewBGP(l)
 			cmdOutput := map[string]string{
 				"show bgp vrf all json":               vrfVtysh,
 				"show bgp vrf default neighbors json": tc.vtyshOutput,
@@ -484,6 +484,5 @@ func TestCollect(t *testing.T) {
 				t.Errorf("expected no error but got %s", err)
 			}
 		})
-
 	}
 }
diff --git a/frr-tools/metrics/liveness/liveness_test.go b/frr-tools/metrics/liveness/liveness_test.go
index 8d87b631..6b05a068 100644
--- a/frr-tools/metrics/liveness/liveness_test.go
+++ b/frr-tools/metrics/liveness/liveness_test.go
@@ -53,8 +53,6 @@ func TestLiveness(t *testing.T) {
 			if res.StatusCode != test.expectedStatusCode {
 				t.Errorf("status code %d different from expected %d", res.StatusCode, test.expectedStatusCode)
 			}
-
 		})
 	}
-
 }
diff --git a/internal/allocator/allocator_test.go b/internal/allocator/allocator_test.go
index 2d654112..651feeab 100644
--- a/internal/allocator/allocator_test.go
+++ b/internal/allocator/allocator_test.go
@@ -1076,7 +1076,6 @@ func TestPoolAllocation(t *testing.T) {
 		if test.wantErr {
 			if err == nil {
 				t.Errorf("%s: should have caused an error, but did not", test.desc)
-
 			}
 			continue
 		}
@@ -1375,7 +1374,7 @@ func TestAllocation(t *testing.T) {
 			unassign: true,
 			ipFamily: ipfamily.IPv6,
 		},
-		//Dual-stack test cases
+		// Dual-stack test cases
 		{
 			desc:     "s1 gets dual-stack IPs",
 			svcKey:   "s1",
@@ -1536,7 +1535,6 @@ func TestBuggyIPs(t *testing.T) {
 		if test.wantErr {
 			if err == nil {
 				t.Errorf("#%d should have caused an error, but did not", i+1)
-
 			}
 			continue
 		}
@@ -1549,7 +1547,6 @@ func TestBuggyIPs(t *testing.T) {
 			}
 		}
 	}
-
 }
 
 func TestConfigReload(t *testing.T) {
diff --git a/internal/bgp/frr/debounce_test.go b/internal/bgp/frr/debounce_test.go
index 3cd70c14..b37337ec 100644
--- a/internal/bgp/frr/debounce_test.go
+++ b/internal/bgp/frr/debounce_test.go
@@ -14,7 +14,7 @@ const timer = 10 * time.Millisecond
 const failureTimer = 10 * time.Millisecond
 
 func TestDebounce(t *testing.T) {
-	result := make(chan *frrConfig, 10) // buffered to accomodate spurious rewrites
+	result := make(chan *frrConfig, 10) // buffered to accommodate spurious rewrites
 	dummyUpdate := func(config *frrConfig) error {
 		result <- config
 		return nil
@@ -52,7 +52,7 @@ func TestDebounce(t *testing.T) {
 }
 
 func TestDebounceRetry(t *testing.T) {
-	result := make(chan *frrConfig, 10) // buffered to accomodate spurious rewrites
+	result := make(chan *frrConfig, 10) // buffered to accommodate spurious rewrites
 	count := 0
 	dummyUpdate := func(config *frrConfig) error {
 		count++
@@ -83,7 +83,7 @@ func TestDebounceRetry(t *testing.T) {
 }
 
 func TestDebounceReuseOld(t *testing.T) {
-	result := make(chan *frrConfig, 10) // buffered to accomodate spurious rewrites
+	result := make(chan *frrConfig, 10) // buffered to accommodate spurious rewrites
 	dummyUpdate := func(config *frrConfig) error {
 		result <- config
 		return nil
@@ -118,7 +118,7 @@ func TestDebounceReuseOld(t *testing.T) {
 }
 
 func TestDebounceSameConfig(t *testing.T) {
-	result := make(chan *frrConfig, 10) // buffered to accomodate spurious rewrites
+	result := make(chan *frrConfig, 10) // buffered to accommodate spurious rewrites
 	dummyUpdate := func(config *frrConfig) error {
 		result <- config
 		return nil
diff --git a/internal/bgp/frr/frr_bfd_test.go b/internal/bgp/frr/frr_bfd_test.go
index 3d87a108..ac6a71dd 100644
--- a/internal/bgp/frr/frr_bfd_test.go
+++ b/internal/bgp/frr/frr_bfd_test.go
@@ -40,7 +40,7 @@ func TestBFDProfileNoSessions(t *testing.T) {
 		},
 	}
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	err := sessionManager.SyncBFDProfiles(pp)
@@ -72,7 +72,7 @@ func TestBFDProfileCornerCases(t *testing.T) {
 	}
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	err := sessionManager.SyncBFDProfiles(pp)
@@ -114,7 +114,7 @@ func TestBFDWithSession(t *testing.T) {
 	}
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	err := sessionManager.SyncBFDProfiles(pp)
@@ -168,7 +168,7 @@ func TestBFDProfileAllDefault(t *testing.T) {
 	}
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	err := sessionManager.SyncBFDProfiles(pp)
diff --git a/internal/bgp/frr/frr_test.go b/internal/bgp/frr/frr_test.go
index 91a976ca..b3526274 100644
--- a/internal/bgp/frr/frr_test.go
+++ b/internal/bgp/frr/frr_test.go
@@ -31,7 +31,6 @@ func testOsHostname() (string, error) {
 }
 
 func testCompareFiles(t *testing.T, configFile, goldenFile string) {
-
 	var lastError error
 
 	// Try comparing files multiple times because tests can generate more than one configuration
@@ -102,7 +101,7 @@ func TestSingleEBGPSessionMultiHop(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -129,7 +128,7 @@ func TestSingleEBGPSessionOneHop(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -157,7 +156,7 @@ func TestSingleIPv6EBGPSessionOneHop(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -185,7 +184,7 @@ func TestSingleIBGPSession(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -213,7 +212,7 @@ func TestSingleIPv6IBGPSession(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -240,7 +239,7 @@ func TestSingleSessionClose(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	session, err := sessionManager.NewSession(l,
@@ -268,7 +267,7 @@ func TestTwoSessions(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session1, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -314,7 +313,7 @@ func TestTwoIPv6Sessions(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	session1, err := sessionManager.NewSession(l,
@@ -359,7 +358,7 @@ func TestTwoSessionsDuplicate(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session1, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -404,7 +403,7 @@ func TestTwoSessionsDuplicateRouter(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	session1, err := sessionManager.NewSession(l,
@@ -450,7 +449,7 @@ func TestSingleAdvertisement(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -497,7 +496,7 @@ func TestSingleAdvertisementNoRouterID(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -539,7 +538,7 @@ func TestSingleAdvertisementInvalidNoPort(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -567,7 +566,7 @@ func TestSingleAdvertisementStop(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -614,7 +613,7 @@ func TestSingleAdvertisementChange(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -669,7 +668,7 @@ func TestSingleAdvertisementWithPeerSelector(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	session, err := sessionManager.NewSession(l,
@@ -718,7 +717,7 @@ func TestSingleAdvertisementNonExistingPeer(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -766,7 +765,7 @@ func TestTwoAdvertisements(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -819,7 +818,7 @@ func TestTwoAdvertisementsTwoSessions(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -896,7 +895,7 @@ func TestTwoAdvertisementsTwoSessionsOneWithPeerSelector(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -970,11 +969,43 @@ func TestTwoAdvertisementsTwoSessionsOneWithPeerSelector(t *testing.T) {
 	testCheckConfigFile(t)
 }
 
+func TestSingleSessionExtras(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
+	defer close(sessionManager.reloadConfig)
+	err := sessionManager.SyncExtraInfo("# hello")
+	if err != nil {
+		t.Fatalf("Could not sync extra info")
+	}
+	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)
+	}
+	defer session.Close()
+
+	testCheckConfigFile(t)
+}
+
 func TestLoggingConfiguration(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelWarn)
+	sessionManager := mockNewSessionManager(l, logging.LevelWarn)
 	defer close(sessionManager.reloadConfig)
 
 	config, err := sessionManager.createConfig()
@@ -990,7 +1021,7 @@ func TestLoggingConfigurationDebug(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelDebug)
+	sessionManager := mockNewSessionManager(l, logging.LevelDebug)
 	defer close(sessionManager.reloadConfig)
 
 	config, err := sessionManager.createConfig()
@@ -1010,7 +1041,7 @@ func TestLoggingConfigurationOverrideByEnvironmentVar(t *testing.T) {
 	t.Cleanup(func() { os.Setenv("FRR_LOGGING_LEVEL", orig) })
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelDebug)
+	sessionManager := mockNewSessionManager(l, logging.LevelDebug)
 	defer close(sessionManager.reloadConfig)
 
 	config, err := sessionManager.createConfig()
diff --git a/internal/bgp/frr/frr_vrf_test.go b/internal/bgp/frr/frr_vrf_test.go
index bc17a9ea..95d38dea 100644
--- a/internal/bgp/frr/frr_vrf_test.go
+++ b/internal/bgp/frr/frr_vrf_test.go
@@ -17,7 +17,7 @@ func TestVRFSingleEBGPSessionMultiHop(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -45,7 +45,7 @@ func TestSingleVRFIBGPSession(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -74,7 +74,7 @@ func TestTwoSessionsOneVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session1, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -121,7 +121,7 @@ func TestTwoSessionsSameIPVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session1, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -168,7 +168,7 @@ func TestTwoSessionsSameIPRouterIDASNVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session1, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -211,7 +211,7 @@ func TestSingleAdvertisementVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -259,7 +259,7 @@ func TestSingleAdvertisementChangeVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -315,7 +315,7 @@ func TestSingleAdvertisementWithPeerSelectorVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 
 	session, err := sessionManager.NewSession(l,
@@ -365,7 +365,7 @@ func TestTwoAdvertisementsVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -419,7 +419,7 @@ func TestTwoAdvertisementsTwoSessionsOneVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -497,7 +497,7 @@ func TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
@@ -577,7 +577,7 @@ func TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
-	sessionManager := NewSessionManager(l, logging.LevelInfo)
+	sessionManager := mockNewSessionManager(l, logging.LevelInfo)
 	defer close(sessionManager.reloadConfig)
 	session, err := sessionManager.NewSession(l,
 		bgp.SessionParameters{
diff --git a/internal/bgp/frr/parse_test.go b/internal/bgp/frr/parse_test.go
index c1b7a42f..66c99955 100644
--- a/internal/bgp/frr/parse_test.go
+++ b/internal/bgp/frr/parse_test.go
@@ -151,8 +151,8 @@ func TestNeighbour(t *testing.T) {
 			if err != nil {
 				t.Fatal("Failed to parse ", err)
 			}
-			if !n.Ip.Equal(net.ParseIP(tt.neighborIP)) {
-				t.Fatal("Expected neighbour ip", tt.neighborIP, "got", n.Ip.String())
+			if !n.IP.Equal(net.ParseIP(tt.neighborIP)) {
+				t.Fatal("Expected neighbour ip", tt.neighborIP, "got", n.IP.String())
 			}
 			if n.RemoteAS != tt.remoteAS {
 				t.Fatal("Expected remote as", tt.remoteAS, "got", n.RemoteAS)
@@ -499,16 +499,16 @@ func TestNeighbours(t *testing.T) {
 		t.Fatalf("Expected 4 neighbours, got %d", len(nn))
 	}
 	sort.Slice(nn, func(i, j int) bool {
-		return (bytes.Compare(nn[i].Ip, nn[j].Ip) < 0)
+		return (bytes.Compare(nn[i].IP, nn[j].IP) < 0)
 	})
 
-	if !nn[0].Ip.Equal(net.ParseIP("172.18.0.2")) {
+	if !nn[0].IP.Equal(net.ParseIP("172.18.0.2")) {
 		t.Fatal("neighbour ip not matching")
 	}
-	if !nn[1].Ip.Equal(net.ParseIP("172.18.0.3")) {
+	if !nn[1].IP.Equal(net.ParseIP("172.18.0.3")) {
 		t.Fatal("neighbour ip not matching")
 	}
-	if !nn[2].Ip.Equal(net.ParseIP("172.18.0.4")) {
+	if !nn[2].IP.Equal(net.ParseIP("172.18.0.4")) {
 		t.Fatal("neighbour ip not matching")
 	}
 
diff --git a/internal/bgp/frr/testdata/TestSingleSessionExtras.golden b/internal/bgp/frr/testdata/TestSingleSessionExtras.golden
new file mode 100644
index 00000000..3f7e0eeb
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestSingleSessionExtras.golden
@@ -0,0 +1,40 @@
+log file /etc/frr/frr.log informational
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 127.0.0.2-in deny 20
+
+route-map 127.0.0.2-out permit 1
+  match ip address prefix-list 127.0.0.2-pl-ipv4
+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
+
+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 127.0.0.2 remote-as 200
+  neighbor 127.0.0.2 port 179
+  neighbor 127.0.0.2 timers 1 1
+  neighbor 127.0.0.2 password password
+  neighbor 127.0.0.2 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 127.0.0.2 activate
+    neighbor 127.0.0.2 route-map 127.0.0.2-in in
+    neighbor 127.0.0.2 route-map 127.0.0.2-out out
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 127.0.0.2 activate
+    neighbor 127.0.0.2 route-map 127.0.0.2-in in
+    neighbor 127.0.0.2 route-map 127.0.0.2-out out
+  exit-address-family
+
+# hello
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 7a2a9dc5..edc53a26 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -13,7 +13,6 @@ import (
 	"go.universe.tf/metallb/internal/pointer"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/util/sets"
 )
@@ -38,7 +37,7 @@ func TestParse(t *testing.T) {
 	testAdvName := "testAdv"
 	testPoolName := "testPool"
 	testPool := v1beta1.IPAddressPool{
-		ObjectMeta: v1.ObjectMeta{Name: testPoolName},
+		ObjectMeta: metav1.ObjectMeta{Name: testPoolName},
 		Spec: v1beta1.IPAddressPoolSpec{
 			Addresses: []string{
 				"10.20.0.0/16",
@@ -65,7 +64,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -73,7 +72,7 @@ func TestParse(t *testing.T) {
 							ASN:          142,
 							Address:      "1.2.3.4",
 							Port:         1179,
-							HoldTime:     v1.Duration{Duration: 180 * time.Second},
+							HoldTime:     metav1.Duration{Duration: 180 * time.Second},
 							RouterID:     "10.20.30.40",
 							SrcAddress:   "10.20.30.40",
 							EBGPMultiHop: true,
@@ -81,7 +80,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer2",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -89,12 +88,12 @@ func TestParse(t *testing.T) {
 							ASN:          200,
 							Address:      "2.3.4.5",
 							EBGPMultiHop: false,
-							NodeSelectors: []v1.LabelSelector{
+							NodeSelectors: []metav1.LabelSelector{
 								{
 									MatchLabels: map[string]string{
 										"foo": "bar",
 									},
-									MatchExpressions: []v1.LabelSelectorRequirement{
+									MatchExpressions: []metav1.LabelSelectorRequirement{
 										{
 											Key:      "bar",
 											Operator: "In",
@@ -108,7 +107,7 @@ func TestParse(t *testing.T) {
 				},
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -121,7 +120,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool2",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -131,7 +130,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool3",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -144,7 +143,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool4",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -156,7 +155,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -168,7 +167,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv2",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -178,7 +177,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv3",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -188,7 +187,7 @@ func TestParse(t *testing.T) {
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv1",
 						},
 						Spec: v1beta1.L2AdvertisementSpec{
@@ -196,14 +195,14 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv2",
 						},
 					},
 				},
 				Communities: []v1beta1.Community{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "community",
 						},
 						Spec: v1beta1.CommunitySpec{
@@ -334,7 +333,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -349,7 +348,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool2",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -357,11 +356,11 @@ func TestParse(t *testing.T) {
 								"30.0.0.0/8",
 							},
 							AllocateTo: &v1beta1.ServiceAllocation{Priority: 2,
-								NamespaceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"team": "metallb"}}}},
+								NamespaceSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"team": "metallb"}}}},
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool3",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -369,18 +368,18 @@ func TestParse(t *testing.T) {
 								"40.0.0.0/8",
 							},
 							AllocateTo: &v1beta1.ServiceAllocation{Priority: 3,
-								NamespaceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"team": "red"}}}},
+								NamespaceSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"team": "red"}}}},
 						},
 					},
 				},
 				Namespaces: []corev1.Namespace{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "test-ns1",
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "test-ns2",
 							Labels: map[string]string{"team": "metallb"},
 						},
@@ -421,7 +420,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -438,7 +437,7 @@ func TestParse(t *testing.T) {
 				},
 				Namespaces: []corev1.Namespace{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "test-ns1",
 						},
 					},
@@ -451,7 +450,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -463,7 +462,7 @@ func TestParse(t *testing.T) {
 							AutoAssign:    pointer.BoolPtr(false),
 							AllocateTo: &v1beta1.ServiceAllocation{
 								Priority: 1,
-								NamespaceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}},
+								NamespaceSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}},
 									{MatchLabels: map[string]string{"foo": "bar"}}},
 							},
 						},
@@ -471,7 +470,7 @@ func TestParse(t *testing.T) {
 				},
 				Namespaces: []corev1.Namespace{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "test-ns1",
 							Labels: map[string]string{"foo": "bar"},
 						},
@@ -485,7 +484,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -493,11 +492,11 @@ func TestParse(t *testing.T) {
 								"30.0.0.0/8",
 							},
 							AllocateTo: &v1beta1.ServiceAllocation{Priority: 2,
-								ServiceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"team": "metallb"}}}},
+								ServiceSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"team": "metallb"}}}},
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool2",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -505,7 +504,7 @@ func TestParse(t *testing.T) {
 								"40.0.0.0/8",
 							},
 							AllocateTo: &v1beta1.ServiceAllocation{Priority: 3,
-								ServiceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"team": "red"}}}},
+								ServiceSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"team": "red"}}}},
 						},
 					},
 				},
@@ -536,7 +535,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -544,7 +543,7 @@ func TestParse(t *testing.T) {
 								"30.0.0.0/8",
 							},
 							AllocateTo: &v1beta1.ServiceAllocation{Priority: 2,
-								ServiceSelectors: []v1.LabelSelector{{MatchExpressions: []metav1.LabelSelectorRequirement{
+								ServiceSelectors: []metav1.LabelSelector{{MatchExpressions: []metav1.LabelSelectorRequirement{
 									{
 										Key:      "foo",
 										Operator: metav1.LabelSelectorOpIn,
@@ -562,7 +561,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -571,11 +570,11 @@ func TestParse(t *testing.T) {
 							},
 							AllocateTo: &v1beta1.ServiceAllocation{Priority: 2,
 								Namespaces:       []string{"test-ns1"},
-								ServiceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"testsvc-1": "1"}}}},
+								ServiceSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"testsvc-1": "1"}}}},
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool2",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -583,19 +582,19 @@ func TestParse(t *testing.T) {
 								"40.0.0.0/8",
 							},
 							AllocateTo: &v1beta1.ServiceAllocation{Priority: 3,
-								NamespaceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"team": "metallb"}}},
-								ServiceSelectors:   []v1.LabelSelector{{MatchLabels: map[string]string{"testsvc-2": "2"}}}},
+								NamespaceSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"team": "metallb"}}},
+								ServiceSelectors:   []metav1.LabelSelector{{MatchLabels: map[string]string{"testsvc-2": "2"}}}},
 						},
 					},
 				},
 				Namespaces: []corev1.Namespace{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "test-ns1",
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "test-ns2",
 							Labels: map[string]string{"team": "metallb"},
 						},
@@ -726,7 +725,7 @@ func TestParse(t *testing.T) {
 							MyASN:    42,
 							ASN:      42,
 							Address:  "1.2.3.4",
-							HoldTime: v1.Duration{Duration: time.Second},
+							HoldTime: metav1.Duration{Duration: time.Second},
 						},
 					},
 				},
@@ -752,7 +751,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -788,12 +787,12 @@ func TestParse(t *testing.T) {
 							MyASN:   42,
 							ASN:     42,
 							Address: "1.2.3.4",
-							NodeSelectors: []v1.LabelSelector{
+							NodeSelectors: []metav1.LabelSelector{
 								{
 									MatchLabels: map[string]string{
 										"foo": "bar",
 									},
-									MatchExpressions: []v1.LabelSelectorRequirement{
+									MatchExpressions: []metav1.LabelSelectorRequirement{
 										{
 											Operator: "In",
 											Values:   []string{"quux"},
@@ -815,12 +814,12 @@ func TestParse(t *testing.T) {
 							MyASN:   42,
 							ASN:     42,
 							Address: "1.2.3.4",
-							NodeSelectors: []v1.LabelSelector{
+							NodeSelectors: []metav1.LabelSelector{
 								{
 									MatchLabels: map[string]string{
 										"foo": "bar",
 									},
-									MatchExpressions: []v1.LabelSelectorRequirement{
+									MatchExpressions: []metav1.LabelSelectorRequirement{
 										{
 											Key:    "bar",
 											Values: []string{"quux"},
@@ -842,12 +841,12 @@ func TestParse(t *testing.T) {
 							MyASN:   42,
 							ASN:     42,
 							Address: "1.2.3.4",
-							NodeSelectors: []v1.LabelSelector{
+							NodeSelectors: []metav1.LabelSelector{
 								{
 									MatchLabels: map[string]string{
 										"foo": "bar",
 									},
-									MatchExpressions: []v1.LabelSelectorRequirement{
+									MatchExpressions: []metav1.LabelSelectorRequirement{
 										{
 											Key:      "bar",
 											Operator: "surrounds",
@@ -866,7 +865,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -874,12 +873,12 @@ func TestParse(t *testing.T) {
 							ASN:          200,
 							Address:      "2.3.4.5",
 							EBGPMultiHop: false,
-							NodeSelectors: []v1.LabelSelector{
+							NodeSelectors: []metav1.LabelSelector{
 								{
 									MatchLabels: map[string]string{
 										"foo": "bar",
 									},
-									MatchExpressions: []v1.LabelSelectorRequirement{
+									MatchExpressions: []metav1.LabelSelectorRequirement{
 										{
 											Key:      "bar",
 											Operator: metav1.LabelSelectorOpIn,
@@ -927,7 +926,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec:       v1beta1.IPAddressPoolSpec{},
 					},
 				},
@@ -938,7 +937,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 					},
 				},
 			},
@@ -948,7 +947,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"100.200.300.400/24",
@@ -963,7 +962,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"1.2.3.0/33",
@@ -978,7 +977,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"1.2.3.10-1.2.3.1",
@@ -993,7 +992,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"1.2.3.0/24",
@@ -1003,7 +1002,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv3",
 						},
 					},
@@ -1035,7 +1034,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"1.2.3.0/24",
@@ -1045,7 +1044,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv3",
 						},
 					},
@@ -1077,7 +1076,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"1.2.3.10-1.2.3.1",
@@ -1099,7 +1098,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"10.20.30.40/24",
@@ -1117,12 +1116,313 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
+		{
+			desc: "different local pref - same peers and nodes",
+			crs: ClusterResources{
+				Nodes: []corev1.Node{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node1",
+						},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node2",
+						},
+					},
+				},
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.30.40/24",
+							},
+						},
+					},
+				},
+				BGPAdvs: []v1beta1.BGPAdvertisement{
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref: 100,
+						},
+					},
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref: 200,
+						},
+					},
+				},
+			},
+		},
+		{
+			desc: "different local pref - different ipv4 aggregation length",
+			crs: ClusterResources{
+				Nodes: []corev1.Node{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node1",
+						},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node2",
+						},
+					},
+				},
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.30.40/24",
+							},
+						},
+					},
+				},
+				BGPAdvs: []v1beta1.BGPAdvertisement{
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref:         100,
+							AggregationLength: pointer.Int32Ptr(24),
+						},
+					},
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref: 200,
+						},
+					},
+				},
+			},
+		},
+		{
+			desc: "different local pref - different aggregation lengths",
+			crs: ClusterResources{
+				Nodes: []corev1.Node{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node1",
+						},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node2",
+						},
+					},
+				},
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.30.40/24",
+							},
+						},
+					},
+				},
+				BGPAdvs: []v1beta1.BGPAdvertisement{
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref:           100,
+							AggregationLength:   pointer.Int32Ptr(24),
+							AggregationLengthV6: pointer.Int32Ptr(120),
+						},
+					},
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref: 200,
+						},
+					},
+				},
+			},
+			want: &Config{
+				Pools: &Pools{ByName: map[string]*Pool{
+					"pool1": {
+						Name:       "pool1",
+						AutoAssign: true,
+						CIDR: []*net.IPNet{
+							ipnet("10.20.30.40/24"),
+						},
+						BGPAdvertisements: []*BGPAdvertisement{
+							{
+								AggregationLength:   24,
+								AggregationLengthV6: 120,
+								LocalPref:           100,
+								Communities:         map[uint32]bool{},
+								Nodes:               map[string]bool{"node1": true, "node2": true},
+							},
+							{
+								AggregationLength:   32,
+								AggregationLengthV6: 128,
+								LocalPref:           200,
+								Communities:         map[uint32]bool{},
+								Nodes:               map[string]bool{"node1": true, "node2": true},
+							},
+						},
+					},
+				}},
+				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
+			},
+		},
+		{
+			desc: "different local pref - same peers & different nodes",
+			crs: ClusterResources{
+				Nodes: []corev1.Node{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name:   "node1",
+							Labels: map[string]string{"node": "node1"},
+						},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name:   "node2",
+							Labels: map[string]string{"node": "node2"},
+						},
+					},
+				},
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.30.40/24",
+							},
+						},
+					},
+				},
+				BGPAdvs: []v1beta1.BGPAdvertisement{
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref: 100,
+							NodeSelectors: []metav1.LabelSelector{
+								{
+									MatchLabels: map[string]string{"node": "node1"},
+								},
+							},
+						},
+					},
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref: 200,
+							NodeSelectors: []metav1.LabelSelector{
+								{
+									MatchLabels: map[string]string{"node": "node2"},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: &Config{
+				Pools: &Pools{ByName: map[string]*Pool{
+					"pool1": {
+						Name:       "pool1",
+						AutoAssign: true,
+						CIDR: []*net.IPNet{
+							ipnet("10.20.30.40/24"),
+						},
+						BGPAdvertisements: []*BGPAdvertisement{
+							{
+								AggregationLength:   32,
+								AggregationLengthV6: 128,
+								LocalPref:           100,
+								Communities:         map[uint32]bool{},
+								Nodes:               map[string]bool{"node1": true},
+							},
+							{
+								AggregationLength:   32,
+								AggregationLengthV6: 128,
+								LocalPref:           200,
+								Communities:         map[uint32]bool{},
+								Nodes:               map[string]bool{"node2": true},
+							},
+						},
+					},
+				}},
+				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
+			},
+		},
+		{
+			desc: "different local pref - different peers & same nodes",
+			crs: ClusterResources{
+				Nodes: []corev1.Node{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node1",
+						},
+					},
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "node2",
+						},
+					},
+				},
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.30.40/24",
+							},
+						},
+					},
+				},
+				BGPAdvs: []v1beta1.BGPAdvertisement{
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref: 100,
+							Peers:     []string{"peer1"},
+						},
+					},
+					{
+						Spec: v1beta1.BGPAdvertisementSpec{
+							LocalPref: 200,
+							Peers:     []string{"peer2"},
+						},
+					},
+				},
+			},
+			want: &Config{
+				Pools: &Pools{ByName: map[string]*Pool{
+					"pool1": {
+						Name:       "pool1",
+						AutoAssign: true,
+						CIDR: []*net.IPNet{
+							ipnet("10.20.30.40/24"),
+						},
+						BGPAdvertisements: []*BGPAdvertisement{
+							{
+								AggregationLength:   32,
+								AggregationLengthV6: 128,
+								LocalPref:           100,
+								Peers:               []string{"peer1"},
+								Communities:         map[uint32]bool{},
+								Nodes:               map[string]bool{"node1": true, "node2": true},
+							},
+							{
+								AggregationLength:   32,
+								AggregationLengthV6: 128,
+								LocalPref:           200,
+								Peers:               []string{"peer2"},
+								Communities:         map[uint32]bool{},
+								Nodes:               map[string]bool{"node1": true, "node2": true},
+							},
+						},
+					},
+				}},
+				BFDProfiles: map[string]*BFDProfile{},
+				Peers:       map[string]*Peer{},
+			},
+		},
 		{
 			desc: "aggregation length by range",
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"3.3.3.2-3.3.3.254",
@@ -1177,7 +1477,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"3.3.3.2-3.3.3.254",
@@ -1200,7 +1500,7 @@ func TestParse(t *testing.T) {
 				Pools: []v1beta1.IPAddressPool{testPool},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: testAdvName},
+						ObjectMeta: metav1.ObjectMeta{Name: testAdvName},
 						Spec: v1beta1.L2AdvertisementSpec{
 							IPAddressPools: []string{testPoolName, testPoolName},
 						},
@@ -1214,7 +1514,7 @@ func TestParse(t *testing.T) {
 				Pools: []v1beta1.IPAddressPool{testPool},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: testAdvName},
+						ObjectMeta: metav1.ObjectMeta{Name: testAdvName},
 						Spec: v1beta1.BGPAdvertisementSpec{
 							IPAddressPools: []string{testPoolName, testPoolName},
 						},
@@ -1228,7 +1528,7 @@ func TestParse(t *testing.T) {
 				Pools: []v1beta1.IPAddressPool{testPool},
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -1240,7 +1540,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: testAdvName},
+						ObjectMeta: metav1.ObjectMeta{Name: testAdvName},
 						Spec: v1beta1.BGPAdvertisementSpec{
 							IPAddressPools: []string{testPoolName},
 							Peers:          []string{"peer1", "peer1"},
@@ -1255,7 +1555,7 @@ func TestParse(t *testing.T) {
 				Pools: []v1beta1.IPAddressPool{testPool},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: testAdvName},
+						ObjectMeta: metav1.ObjectMeta{Name: testAdvName},
 						Spec: v1beta1.BGPAdvertisementSpec{
 							Communities:    []string{"1234"},
 							IPAddressPools: []string{testPoolName},
@@ -1270,7 +1570,7 @@ func TestParse(t *testing.T) {
 				Pools: []v1beta1.IPAddressPool{testPool},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: testAdvName},
+						ObjectMeta: metav1.ObjectMeta{Name: testAdvName},
 						Spec: v1beta1.BGPAdvertisementSpec{
 							Communities:    []string{"99999999:1"},
 							IPAddressPools: []string{testPoolName},
@@ -1285,7 +1585,7 @@ func TestParse(t *testing.T) {
 				Pools: []v1beta1.IPAddressPool{testPool},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: testAdvName},
+						ObjectMeta: metav1.ObjectMeta{Name: testAdvName},
 						Spec: v1beta1.BGPAdvertisementSpec{
 							Communities:    []string{"1:99999999"},
 							IPAddressPools: []string{testPoolName},
@@ -1300,7 +1600,7 @@ func TestParse(t *testing.T) {
 				Pools: []v1beta1.IPAddressPool{testPool},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: testAdvName},
+						ObjectMeta: metav1.ObjectMeta{Name: testAdvName},
 						Spec: v1beta1.BGPAdvertisementSpec{
 							Communities:    []string{"community"},
 							IPAddressPools: []string{testPoolName},
@@ -1315,7 +1615,7 @@ func TestParse(t *testing.T) {
 				Pools: []v1beta1.IPAddressPool{testPool},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: testAdvName},
+						ObjectMeta: metav1.ObjectMeta{Name: testAdvName},
 						Spec: v1beta1.BGPAdvertisementSpec{
 							Communities:    []string{"1234:5678", "1234:5678"},
 							IPAddressPools: []string{testPoolName},
@@ -1329,7 +1629,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Communities: []v1beta1.Community{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "community",
 						},
 						Spec: v1beta1.CommunitySpec{
@@ -1349,7 +1649,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Communities: []v1beta1.Community{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "community",
 						},
 						Spec: v1beta1.CommunitySpec{
@@ -1369,7 +1669,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Communities: []v1beta1.Community{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "community",
 						},
 						Spec: v1beta1.CommunitySpec{
@@ -1389,7 +1689,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Communities: []v1beta1.Community{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "community1",
 						},
 						Spec: v1beta1.CommunitySpec{
@@ -1402,7 +1702,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "community2",
 						},
 						Spec: v1beta1.CommunitySpec{
@@ -1422,7 +1722,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Communities: []v1beta1.Community{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "community",
 						},
 						Spec: v1beta1.CommunitySpec{
@@ -1446,15 +1746,15 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec:       v1beta1.IPAddressPoolSpec{},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool2"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool2"},
 						Spec:       v1beta1.IPAddressPoolSpec{},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec:       v1beta1.IPAddressPoolSpec{},
 					},
 				},
@@ -1465,7 +1765,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								" 10.0.0.0/8",
@@ -1473,7 +1773,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool2"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool2"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								" 10.0.0.0/8",
@@ -1488,7 +1788,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool1"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool1"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								" 10.0.0.0/8",
@@ -1496,7 +1796,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{Name: "pool2"},
+						ObjectMeta: metav1.ObjectMeta{Name: "pool2"},
 						Spec: v1beta1.IPAddressPoolSpec{
 							Addresses: []string{
 								"10.0.0.0/16",
@@ -1511,7 +1811,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -1524,7 +1824,7 @@ func TestParse(t *testing.T) {
 				},
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -1536,14 +1836,14 @@ func TestParse(t *testing.T) {
 				},
 				BFDProfiles: []v1beta1.BFDProfile{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "default",
 						},
 					},
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv3",
 						},
 					},
@@ -1617,7 +1917,7 @@ func TestParse(t *testing.T) {
 					},
 				},
 				PasswordSecrets: map[string]corev1.Secret{
-					"bgpsecret": {Type: corev1.SecretTypeOpaque, ObjectMeta: v1.ObjectMeta{Name: "bgpsecret", Namespace: "metallb-system"}},
+					"bgpsecret": {Type: corev1.SecretTypeOpaque, ObjectMeta: metav1.ObjectMeta{Name: "bgpsecret", Namespace: "metallb-system"}},
 				},
 			},
 		},
@@ -1636,7 +1936,7 @@ func TestParse(t *testing.T) {
 					},
 				},
 				PasswordSecrets: map[string]corev1.Secret{
-					"bgpsecret": {Type: corev1.SecretTypeBasicAuth, ObjectMeta: v1.ObjectMeta{Name: "bgpsecret", Namespace: "metallb-system"}},
+					"bgpsecret": {Type: corev1.SecretTypeBasicAuth, ObjectMeta: metav1.ObjectMeta{Name: "bgpsecret", Namespace: "metallb-system"}},
 				},
 			},
 		},
@@ -1645,7 +1945,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -1659,8 +1959,8 @@ func TestParse(t *testing.T) {
 					},
 				},
 				PasswordSecrets: map[string]corev1.Secret{
-					"bgpsecret": {Type: corev1.SecretTypeBasicAuth, ObjectMeta: v1.ObjectMeta{Name: "bgpsecret", Namespace: "metallb-system"},
-						Data: map[string][]byte{"password": []byte([]byte("nopass"))}},
+					"bgpsecret": {Type: corev1.SecretTypeBasicAuth, ObjectMeta: metav1.ObjectMeta{Name: "bgpsecret", Namespace: "metallb-system"},
+						Data: map[string][]byte{"password": []byte("nopass")}},
 				},
 			},
 			want: &Config{
@@ -1713,7 +2013,7 @@ func TestParse(t *testing.T) {
 				},
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -1725,7 +2025,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv3",
 						},
 					},
@@ -1737,7 +2037,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -1749,17 +2049,17 @@ func TestParse(t *testing.T) {
 				},
 				BFDProfiles: []v1beta1.BFDProfile{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "default",
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "foo",
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "foo",
 						},
 					},
@@ -1771,7 +2071,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -1783,7 +2083,7 @@ func TestParse(t *testing.T) {
 				},
 				BFDProfiles: []v1beta1.BFDProfile{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "nondefault",
 						},
 						Spec: v1beta1.BFDProfileSpec{
@@ -1799,7 +2099,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv3",
 						},
 					},
@@ -1843,7 +2143,7 @@ func TestParse(t *testing.T) {
 
 				BFDProfiles: []v1beta1.BFDProfile{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "default",
 						},
 						Spec: v1beta1.BFDProfileSpec{
@@ -1859,7 +2159,7 @@ func TestParse(t *testing.T) {
 
 				BFDProfiles: []v1beta1.BFDProfile{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "default",
 						},
 						Spec: v1beta1.BFDProfileSpec{
@@ -1874,7 +2174,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -1886,7 +2186,7 @@ func TestParse(t *testing.T) {
 				},
 				BFDProfiles: []v1beta1.BFDProfile{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "nondefault",
 						},
 						Spec: v1beta1.BFDProfileSpec{
@@ -1902,14 +2202,14 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv3",
 						},
 					},
 				},
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -1917,7 +2217,7 @@ func TestParse(t *testing.T) {
 							ASN:          142,
 							Address:      "1.2.3.4",
 							Port:         1179,
-							HoldTime:     v1.Duration{Duration: 180 * time.Second},
+							HoldTime:     metav1.Duration{Duration: 180 * time.Second},
 							RouterID:     "10.20.30.40",
 							SrcAddress:   "10.20.30.40",
 							EBGPMultiHop: true,
@@ -1932,7 +2232,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -1944,7 +2244,7 @@ func TestParse(t *testing.T) {
 				},
 				BFDProfiles: []v1beta1.BFDProfile{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "with-echo",
 						},
 						Spec: v1beta1.BFDProfileSpec{
@@ -1954,14 +2254,14 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv",
 						},
 					},
 				},
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -2016,7 +2316,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -2024,7 +2324,7 @@ func TestParse(t *testing.T) {
 							ASN:          142,
 							Address:      "1.2.3.4",
 							Port:         1179,
-							HoldTime:     v1.Duration{Duration: 180 * time.Second},
+							HoldTime:     metav1.Duration{Duration: 180 * time.Second},
 							RouterID:     "10.20.30.40",
 							SrcAddress:   "10.20.30.40",
 							EBGPMultiHop: true,
@@ -2033,7 +2333,7 @@ func TestParse(t *testing.T) {
 				},
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -2048,7 +2348,7 @@ func TestParse(t *testing.T) {
 				},
 				LegacyAddressPools: []v1beta1.AddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "legacyl2pool1",
 						},
 						Spec: v1beta1.AddressPoolSpec{
@@ -2061,7 +2361,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "legacybgppool1",
 						},
 						Spec: v1beta1.AddressPoolSpec{
@@ -2083,7 +2383,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2163,7 +2463,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				LegacyAddressPools: []v1beta1.AddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "legacybgppool1",
 						},
 						Spec: v1beta1.AddressPoolSpec{
@@ -2185,7 +2485,7 @@ func TestParse(t *testing.T) {
 				},
 				Communities: []v1beta1.Community{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "community",
 						},
 						Spec: v1beta1.CommunitySpec{
@@ -2226,7 +2526,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -2241,7 +2541,7 @@ func TestParse(t *testing.T) {
 				},
 				LegacyAddressPools: []v1beta1.AddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "legacyl2pool1",
 						},
 						Spec: v1beta1.AddressPoolSpec{
@@ -2254,7 +2554,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "legacybgppool1",
 						},
 						Spec: v1beta1.AddressPoolSpec{
@@ -2273,7 +2573,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "pool1",
 							Labels: map[string]string{"test": "pool1"},
 						},
@@ -2284,7 +2584,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "pool2",
 							Labels: map[string]string{"test": "pool2"},
 						},
@@ -2297,7 +2597,7 @@ func TestParse(t *testing.T) {
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv1",
 						},
 						Spec: v1beta1.L2AdvertisementSpec{
@@ -2313,7 +2613,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2329,7 +2629,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv2",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2393,7 +2693,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "pool1",
 							Labels: map[string]string{"test": "pool1"},
 						},
@@ -2406,7 +2706,7 @@ func TestParse(t *testing.T) {
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv1",
 						},
 						Spec: v1beta1.L2AdvertisementSpec{
@@ -2444,7 +2744,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "pool1",
 							Labels: map[string]string{"test": "pool1"},
 						},
@@ -2457,7 +2757,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2485,7 +2785,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "pool1",
 							Labels: map[string]string{"test": "pool1"},
 						},
@@ -2498,7 +2798,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2525,7 +2825,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "pool1",
 							Labels: map[string]string{"test": "pool1"},
 						},
@@ -2538,7 +2838,7 @@ func TestParse(t *testing.T) {
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.L2AdvertisementSpec{
@@ -2564,7 +2864,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "pool1",
 							Labels: map[string]string{"test": "pool1"},
 						},
@@ -2575,7 +2875,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name:   "pool2",
 							Labels: map[string]string{"test": "pool2"},
 						},
@@ -2588,7 +2888,7 @@ func TestParse(t *testing.T) {
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv1",
 						},
 						Spec: v1beta1.L2AdvertisementSpec{
@@ -2604,7 +2904,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2620,7 +2920,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv2",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2663,7 +2963,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -2673,7 +2973,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool2",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -2685,7 +2985,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2700,7 +3000,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv2",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2717,7 +3017,7 @@ func TestParse(t *testing.T) {
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv1",
 						},
 						Spec: v1beta1.L2AdvertisementSpec{
@@ -2734,7 +3034,7 @@ func TestParse(t *testing.T) {
 				},
 				LegacyAddressPools: []v1beta1.AddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "legacyl2pool1",
 						},
 						Spec: v1beta1.AddressPoolSpec{
@@ -2747,7 +3047,7 @@ func TestParse(t *testing.T) {
 						},
 					},
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "legacybgppool1",
 						},
 						Spec: v1beta1.AddressPoolSpec{
@@ -2853,7 +3153,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -2865,7 +3165,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2887,7 +3187,7 @@ func TestParse(t *testing.T) {
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv1",
 						},
 						Spec: v1beta1.L2AdvertisementSpec{
@@ -2931,7 +3231,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -2943,7 +3243,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -2969,7 +3269,7 @@ func TestParse(t *testing.T) {
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv1",
 						},
 						Spec: v1beta1.L2AdvertisementSpec{
@@ -3012,7 +3312,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -3024,14 +3324,14 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 					},
 				},
 				L2Advs: []v1beta1.L2Advertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "l2adv1",
 						},
 					},
@@ -3090,7 +3390,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				Pools: []v1beta1.IPAddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "pool1",
 						},
 						Spec: v1beta1.IPAddressPoolSpec{
@@ -3102,7 +3402,7 @@ func TestParse(t *testing.T) {
 				},
 				Peers: []v1beta2.BGPPeer{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "peer1",
 						},
 						Spec: v1beta2.BGPPeerSpec{
@@ -3114,7 +3414,7 @@ func TestParse(t *testing.T) {
 				},
 				BGPAdvs: []v1beta1.BGPAdvertisement{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "adv1",
 						},
 						Spec: v1beta1.BGPAdvertisementSpec{
@@ -3165,7 +3465,7 @@ func TestParse(t *testing.T) {
 			crs: ClusterResources{
 				LegacyAddressPools: []v1beta1.AddressPool{
 					{
-						ObjectMeta: v1.ObjectMeta{
+						ObjectMeta: metav1.ObjectMeta{
 							Name: "legacybgppool1",
 						},
 						Spec: v1beta1.AddressPoolSpec{
diff --git a/internal/k8s/controllers/config_controller_test.go b/internal/k8s/controllers/config_controller_test.go
index 0b48cce9..801a1c80 100644
--- a/internal/k8s/controllers/config_controller_test.go
+++ b/internal/k8s/controllers/config_controller_test.go
@@ -30,7 +30,6 @@ import (
 	v1beta1 "go.universe.tf/metallb/api/v1beta1"
 	v1beta2 "go.universe.tf/metallb/api/v1beta2"
 	"go.universe.tf/metallb/internal/config"
-	metallbcfg "go.universe.tf/metallb/internal/config"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -87,7 +86,7 @@ func TestConfigController(t *testing.T) {
 		},
 	}
 	for _, test := range tests {
-		var resources metallbcfg.ClusterResources
+		var resources config.ClusterResources
 		if test.validResources {
 			resources = configControllerValidResources
 		} else {
@@ -105,7 +104,7 @@ func TestConfigController(t *testing.T) {
 			t.Fatalf("test %s failed to create config, got unexpected error: %v", test.desc, err)
 		}
 
-		cmpOpt := cmpopts.IgnoreUnexported(metallbcfg.Pool{})
+		cmpOpt := cmpopts.IgnoreUnexported(config.Pool{})
 
 		mockHandler := func(l log.Logger, cfg *config.Config) SyncState {
 			if !cmp.Equal(expectedCfg, cfg, cmpOpt) {
@@ -206,7 +205,7 @@ func TestSecretShouldntTrigger(t *testing.T) {
 
 	handlerCalled = false
 	err = fakeClient.Create(context.TODO(), &corev1.Secret{Type: corev1.SecretTypeBasicAuth, ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: testNamespace},
-		Data: map[string][]byte{"password": []byte([]byte("nopass"))}})
+		Data: map[string][]byte{"password": []byte("nopass")}})
 	if err != nil {
 		t.Fatalf("create failed on secret foo: %v", err)
 	}
@@ -227,17 +226,17 @@ func TestNodeEvent(t *testing.T) {
 		Scheme:                scheme,
 	}
 	cfg, err := testEnv.Start()
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	defer func() {
 		err = testEnv.Stop()
-		g.Expect(err).To(BeNil())
+		g.Expect(err).ToNot(HaveOccurred())
 	}()
 	err = v1beta1.AddToScheme(k8sscheme.Scheme)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	err = v1beta2.AddToScheme(k8sscheme.Scheme)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	m, err := manager.New(cfg, manager.Options{MetricsBindAddress: "0"})
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 
 	var configUpdate int
 	var mutex sync.Mutex
@@ -259,11 +258,11 @@ func TestNodeEvent(t *testing.T) {
 		ValidateConfig: config.DontValidate,
 	}
 	err = r.SetupWithManager(m)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	ctx := context.Background()
 	go func() {
 		err = m.Start(ctx)
-		g.Expect(err).To(BeNil())
+		g.Expect(err).ToNot(HaveOccurred())
 	}()
 
 	// count for update on namespace events
@@ -286,7 +285,7 @@ func TestNodeEvent(t *testing.T) {
 	node.Labels = make(map[string]string)
 	node.Labels["test"] = "e2e"
 	err = m.GetClient().Create(ctx, node)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	g.Eventually(func() int {
 		mutex.Lock()
 		defer mutex.Unlock()
@@ -334,13 +333,12 @@ func TestNodeEvent(t *testing.T) {
 		defer mutex.Unlock()
 		return configUpdate
 	}, 5*time.Second, 200*time.Millisecond).Should(Equal(initialConfigUpdateCount + 2))
-
 }
 
 var (
 	testNamespace                  = "test-controller"
 	scheme                         = runtime.NewScheme()
-	configControllerValidResources = metallbcfg.ClusterResources{
+	configControllerValidResources = config.ClusterResources{
 		Peers: []v1beta2.BGPPeer{
 			{
 				ObjectMeta: metav1.ObjectMeta{
@@ -397,7 +395,7 @@ var (
 		},
 		PasswordSecrets: map[string]corev1.Secret{
 			"bgpsecret": {Type: corev1.SecretTypeBasicAuth, ObjectMeta: metav1.ObjectMeta{Name: "bgpsecret", Namespace: testNamespace},
-				Data: map[string][]byte{"password": []byte([]byte("nopass"))}},
+				Data: map[string][]byte{"password": []byte("nopass")}},
 		},
 		LegacyAddressPools: []v1beta1.AddressPool{
 			{
@@ -430,7 +428,7 @@ var (
 			},
 		},
 	}
-	configControllerInvalidResources = metallbcfg.ClusterResources{
+	configControllerInvalidResources = config.ClusterResources{
 		Peers: []v1beta2.BGPPeer{
 			{
 				ObjectMeta: metav1.ObjectMeta{
diff --git a/internal/k8s/controllers/fakeclient_test.go b/internal/k8s/controllers/fakeclient_test.go
index 5d46f5d5..f3a79120 100644
--- a/internal/k8s/controllers/fakeclient_test.go
+++ b/internal/k8s/controllers/fakeclient_test.go
@@ -79,7 +79,6 @@ func objectsFromResources(r config.ClusterResources) []client.Object {
 
 	for _, bgpAdv := range r.BGPAdvs {
 		objects = append(objects, bgpAdv.DeepCopy())
-
 	}
 
 	for _, l2Adv := range r.L2Advs {
diff --git a/internal/k8s/controllers/node_controller_test.go b/internal/k8s/controllers/node_controller_test.go
index 28d47a28..a7a12531 100644
--- a/internal/k8s/controllers/node_controller_test.go
+++ b/internal/k8s/controllers/node_controller_test.go
@@ -125,17 +125,17 @@ func TestNodeReconciler_SetupWithManager(t *testing.T) {
 		Scheme:                scheme,
 	}
 	cfg, err := testEnv.Start()
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	defer func() {
 		err = testEnv.Stop()
-		g.Expect(err).To(BeNil())
+		g.Expect(err).ToNot(HaveOccurred())
 	}()
 	err = v1beta1.AddToScheme(k8sscheme.Scheme)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	err = v1beta2.AddToScheme(k8sscheme.Scheme)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	m, err := manager.New(cfg, manager.Options{MetricsBindAddress: "0"})
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 
 	var configUpdate int
 	var mutex sync.Mutex
@@ -154,11 +154,11 @@ func TestNodeReconciler_SetupWithManager(t *testing.T) {
 		NodeName:  "test-node",
 	}
 	err = r.SetupWithManager(m)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	ctx := context.Background()
 	go func() {
 		err = m.Start(ctx)
-		g.Expect(err).To(BeNil())
+		g.Expect(err).ToNot(HaveOccurred())
 	}()
 
 	// test new node event.
@@ -169,7 +169,7 @@ func TestNodeReconciler_SetupWithManager(t *testing.T) {
 	node.Labels = make(map[string]string)
 	node.Labels["test"] = "e2e"
 	err = m.GetClient().Create(ctx, node)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 	g.Eventually(func() int {
 		mutex.Lock()
 		defer mutex.Unlock()
diff --git a/internal/k8s/controllers/pool_controller_test.go b/internal/k8s/controllers/pool_controller_test.go
index bbfef327..20209aef 100644
--- a/internal/k8s/controllers/pool_controller_test.go
+++ b/internal/k8s/controllers/pool_controller_test.go
@@ -24,7 +24,6 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	v1beta1 "go.universe.tf/metallb/api/v1beta1"
-	"go.universe.tf/metallb/internal/config"
 	metallbcfg "go.universe.tf/metallb/internal/config"
 	"go.universe.tf/metallb/internal/pointer"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -90,14 +89,14 @@ func TestPoolController(t *testing.T) {
 			t.Fatalf("test %s failed to create fake client: %v", test.desc, err)
 		}
 
-		expectedCfg, err := config.For(resources, config.DontValidate)
+		expectedCfg, err := metallbcfg.For(resources, metallbcfg.DontValidate)
 		if err != nil && test.validResources {
 			t.Fatalf("test %s failed to create config, got unexpected error: %v", test.desc, err)
 		}
 
 		cmpOpt := cmpopts.IgnoreUnexported(metallbcfg.Pool{})
 
-		mockHandler := func(l log.Logger, pools *config.Pools) SyncState {
+		mockHandler := func(l log.Logger, pools *metallbcfg.Pools) SyncState {
 			if !cmp.Equal(expectedCfg.Pools, pools, cmpOpt) {
 				t.Errorf("test %s failed, handler called with unexpected config: %s", test.desc, cmp.Diff(expectedCfg.Pools, pools, cmpOpt))
 			}
@@ -112,7 +111,7 @@ func TestPoolController(t *testing.T) {
 			Logger:         log.NewNopLogger(),
 			Scheme:         scheme,
 			Namespace:      testNamespace,
-			ValidateConfig: config.DontValidate,
+			ValidateConfig: metallbcfg.DontValidate,
 			Handler:        mockHandler,
 			ForceReload:    mockForceReload,
 		}
diff --git a/internal/k8s/controllers/service_controller_test.go b/internal/k8s/controllers/service_controller_test.go
index f15b7523..bdc60c7f 100644
--- a/internal/k8s/controllers/service_controller_test.go
+++ b/internal/k8s/controllers/service_controller_test.go
@@ -258,7 +258,6 @@ func TestServiceController(t *testing.T) {
 }
 
 func TestLBClass(t *testing.T) {
-
 	tests := []struct {
 		desc           string
 		serviceLBClass *string
diff --git a/speaker/bgp_controller_test.go b/speaker/bgp_controller_test.go
index b41c92f7..9fc7869b 100644
--- a/speaker/bgp_controller_test.go
+++ b/speaker/bgp_controller_test.go
@@ -126,6 +126,10 @@ func (f *fakeBGPSessionManager) SyncBFDProfiles(profiles map[string]*config.BFDP
 	return nil
 }
 
+func (f *fakeBGPSessionManager) SyncExtraInfo(extra string) error {
+	return nil
+}
+
 func (f *fakeBGPSessionManager) Ads() map[string][]*bgp.Advertisement {
 	ret := map[string][]*bgp.Advertisement{}
 
diff --git a/speaker/layer2_controller_test.go b/speaker/layer2_controller_test.go
index 9da66ff1..99243019 100644
--- a/speaker/layer2_controller_test.go
+++ b/speaker/layer2_controller_test.go
@@ -1160,14 +1160,14 @@ func TestShouldAnnounce(t *testing.T) {
 
 		for _, svc := range test.svcs {
 			lbIP := net.ParseIP(svc.Status.LoadBalancer.Ingress[0].IP)
-			lbIP_s := lbIP.String()
-			response1 := c1.protocolHandlers[config.Layer2].ShouldAnnounce(l, "balancer", []net.IP{lbIP}, test.config.Pools.ByName["default"], svc, test.eps[lbIP_s])
-			response2 := c2.protocolHandlers[config.Layer2].ShouldAnnounce(l, "balancer", []net.IP{lbIP}, test.config.Pools.ByName["default"], svc, test.eps[lbIP_s])
-			if response1 != test.c1ExpectedResult[lbIP_s] {
-				t.Errorf("%q: shouldAnnounce for controller 1 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIP_s, test.c1ExpectedResult[lbIP_s], response1)
+			lbIPStr := lbIP.String()
+			response1 := c1.protocolHandlers[config.Layer2].ShouldAnnounce(l, "balancer", []net.IP{lbIP}, test.config.Pools.ByName["default"], svc, test.eps[lbIPStr])
+			response2 := c2.protocolHandlers[config.Layer2].ShouldAnnounce(l, "balancer", []net.IP{lbIP}, test.config.Pools.ByName["default"], svc, test.eps[lbIPStr])
+			if response1 != test.c1ExpectedResult[lbIPStr] {
+				t.Errorf("%q: shouldAnnounce for controller 1 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIPStr, test.c1ExpectedResult[lbIPStr], response1)
 			}
-			if response2 != test.c2ExpectedResult[lbIP_s] {
-				t.Errorf("%q: shouldAnnounce for controller 2 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIP_s, test.c2ExpectedResult[lbIP_s], response2)
+			if response2 != test.c2ExpectedResult[lbIPStr] {
+				t.Errorf("%q: shouldAnnounce for controller 2 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIPStr, test.c2ExpectedResult[lbIPStr], response2)
 			}
 		}
 	}
@@ -2095,14 +2095,14 @@ func TestShouldAnnounceEPSlices(t *testing.T) {
 
 		for _, svc := range test.svcs {
 			lbIP := net.ParseIP(svc.Status.LoadBalancer.Ingress[0].IP)
-			lbIP_s := lbIP.String()
-			response1 := c1.protocolHandlers[config.Layer2].ShouldAnnounce(l, "test1", []net.IP{lbIP}, test.config.Pools.ByName["default"], svc, test.eps[lbIP_s])
-			response2 := c2.protocolHandlers[config.Layer2].ShouldAnnounce(l, "test1", []net.IP{lbIP}, test.config.Pools.ByName["default"], svc, test.eps[lbIP_s])
-			if response1 != test.c1ExpectedResult[lbIP_s] {
-				t.Errorf("%q: shouldAnnounce for controller 1 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIP_s, test.c1ExpectedResult[lbIP_s], response1)
+			lbIPStr := lbIP.String()
+			response1 := c1.protocolHandlers[config.Layer2].ShouldAnnounce(l, "test1", []net.IP{lbIP}, test.config.Pools.ByName["default"], svc, test.eps[lbIPStr])
+			response2 := c2.protocolHandlers[config.Layer2].ShouldAnnounce(l, "test1", []net.IP{lbIP}, test.config.Pools.ByName["default"], svc, test.eps[lbIPStr])
+			if response1 != test.c1ExpectedResult[lbIPStr] {
+				t.Errorf("%q: shouldAnnounce for controller 1 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIPStr, test.c1ExpectedResult[lbIPStr], response1)
 			}
-			if response2 != test.c2ExpectedResult[lbIP_s] {
-				t.Errorf("%q: shouldAnnounce for controller 2 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIP_s, test.c2ExpectedResult[lbIP_s], response2)
+			if response2 != test.c2ExpectedResult[lbIPStr] {
+				t.Errorf("%q: shouldAnnounce for controller 2 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIPStr, test.c2ExpectedResult[lbIPStr], response2)
 			}
 		}
 	}
@@ -2332,14 +2332,14 @@ func TestShouldAnnounceNodeSelector(t *testing.T) {
 		}
 
 		lbIP := net.ParseIP(svc.Status.LoadBalancer.Ingress[0].IP)
-		lbIP_s := lbIP.String()
-		response1 := c1.protocolHandlers[config.Layer2].ShouldAnnounce(l, "test1", []net.IP{lbIP}, cfg.Pools.ByName["default"], &svc, test.eps[lbIP_s])
-		response2 := c2.protocolHandlers[config.Layer2].ShouldAnnounce(l, "test1", []net.IP{lbIP}, cfg.Pools.ByName["default"], &svc, test.eps[lbIP_s])
-		if response1 != test.c1ExpectedResult[lbIP_s] {
-			t.Errorf("%q: shouldAnnounce for controller 1 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIP_s, test.c1ExpectedResult[lbIP_s], response1)
+		lbIPStr := lbIP.String()
+		response1 := c1.protocolHandlers[config.Layer2].ShouldAnnounce(l, "test1", []net.IP{lbIP}, cfg.Pools.ByName["default"], &svc, test.eps[lbIPStr])
+		response2 := c2.protocolHandlers[config.Layer2].ShouldAnnounce(l, "test1", []net.IP{lbIP}, cfg.Pools.ByName["default"], &svc, test.eps[lbIPStr])
+		if response1 != test.c1ExpectedResult[lbIPStr] {
+			t.Errorf("%q: shouldAnnounce for controller 1 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIPStr, test.c1ExpectedResult[lbIPStr], response1)
 		}
-		if response2 != test.c2ExpectedResult[lbIP_s] {
-			t.Errorf("%q: shouldAnnounce for controller 2 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIP_s, test.c2ExpectedResult[lbIP_s], response2)
+		if response2 != test.c2ExpectedResult[lbIPStr] {
+			t.Errorf("%q: shouldAnnounce for controller 2 for service %s returned incorrect result, expected '%s', but received '%s'", test.desc, lbIPStr, test.c2ExpectedResult[lbIPStr], response2)
 		}
 	}
 }
diff --git a/speaker/speaker_test.go b/speaker/speaker_test.go
index 98a0b232..e81efcad 100644
--- a/speaker/speaker_test.go
+++ b/speaker/speaker_test.go
@@ -16,7 +16,7 @@ import (
 
 var logger = log.NewNopLogger()
 
-func NewController(l2Handler *MockProtocol, bgpHandler *MockProtocol, t *testing.T) *controller {
+func mockNewController(l2Handler *MockProtocol, bgpHandler *MockProtocol, t *testing.T) *controller {
 	ret := &controller{
 		myNode:  "nodeName",
 		bgpType: "frr",
@@ -44,7 +44,7 @@ func TestLoadBalancerCreation(t *testing.T) {
 		protocol:       config.BGP,
 		shouldAnnounce: true,
 	}
-	c := NewController(l2MockHandler, bgpMockHandler, t)
+	c := mockNewController(l2MockHandler, bgpMockHandler, t)
 
 	svc := &v1.Service{
 		ObjectMeta: metav1.ObjectMeta{