Repository: metallb/metallb
Pull request #1767 (2023-01-05 06:13:17)

l2: exclude common virtual interfaces for announce services
In arp mode, Some common virtual interfaces should not be used to announce services, such as kube-ipvs0, docker0, etc. This PR will dispatch these common virtual interfaces. 

Signed-off-by: cyclinder <qifeng.guo@daocloud.io>

https://github.com/metallb/metallb/issues/1727

l2: exclude common virtual interfaces for announce services.





fedepaol: cc @oribon @gclawes want to hear your thoughts about this
oribon: the idea seems good but maybe having it as a configmap is better
also, if someone has an interface selector that selects one of these will this "global exclude" override it? (might be unrelated, need to revisit the l2 code)
fedepaol: So, let's move this forward but let's have the list overrideable and not hardcoded. A configmap read upon start is good enough imo, no need to handle the live reload.
fedepaol: > So, let's move this forward but let's have the list overrideable and not hardcoded. A configmap read upon start is good enough imo, no need to handle the live reload.

Wdyt @cyclinder ?
cyclinder: > So, let's move this forward but let's have the list overrideable and not hardcoded. A configmap read upon start is good enough imo, no need to handle the live reload.

I think both are ok, but I prefer hardcoded :)

First of all, it shouldn't be related to the current interface-selector, it's more of a `global-exclude` list, right? If it is a configmap, I think this will increase its complexity. 🤔
fedepaol: > > So, let's move this forward but let's have the list overrideable and not hardcoded. A configmap read upon start is good enough imo, no need to handle the live reload.
> 
> I think both are ok, but I prefer hardcoded :)
> 
> First of all, it shouldn't be related to the current interface-selector, it's more of a `global-exclude` list, right? If it is a configmap, I think this will increase its complexity. thinking

right, it must be independent and always applied, more or less as your current logic is but loading that list from a configmap when the process starts instead of having it hardcoded. This will help us to avoid releases when users hit a corner case we didn't think about
cyclinder: @fedepaol got it. I try to working on this.
cyclinder: Hi @fedepaol , Could you mind having a review?  Thanks. It seems this [CI failing](https://github.com/metallb/metallb/actions/runs/4111273107/jobs/7094872126) isn't related to this changes.
cyclinder: I made some testing base on these changes on my local, It works well, as speaker log shown below:

```
➜  metallb git:(l2ann) ✗ kubectl get po -n metallb-system         
NAME                          READY   STATUS    RESTARTS   AGE
controller-76f86696dd-xznhb   0/1     Running   0          6s
speaker-ccqtb                 0/1     Running   0          4s
speaker-pzznd                 0/1     Running   0          5s
speaker-vsd9l                 0/1     Running   0          4s
➜  metallb git:(l2ann) ✗ kubectl logs -f -n metallb-system speaker-ccqtb 
{"branch":"l2ann","caller":"main.go:97","commit":"f56103a5-dirty","goversion":"gc / go1.18.3 / amd64","level":"info","msg":"MetalLB speaker starting (commit f56103a5-dirty, branch l2ann)","ts":"2023-02-27T09:16:35Z","version":""}
{"caller":"main.go:148","interfacesToExclude":["docker.*","cbr.*","dummy.*","virbr.*","lxcbr.*","veth.*","lo","^cali.*","^tunl.*","flannel.*","kube-ipvs.*","cni.*","^nodelocaldns.*"],"level":"error","op":"show the list of interfaces that announce to exclude","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"lo","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"tunl0","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth313c8572","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth24b1f795","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth8013fa28","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:134","event":"createARPResponder","interface":"eth0","level":"info","msg":"created ARP responder for interface","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:143","event":"createNDPResponder","interface":"eth0","level":"info","msg":"created NDP responder for interface","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:134","event":"createARPResponder","interface":"eth1","level":"info","msg":"created ARP responder for interface","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:143","event":"createNDPResponder","interface":"eth1","level":"info","msg":"created NDP responder for interface","ts":"2023-02-27T09:16:35Z"}
{"caller":"k8s.go:389","level":"info","msg":"Starting Manager","op":"Run","ts":"2023-02-27T09:16:35Z"}
{"caller":"speakerlist.go:310","level":"info","msg":"node event - forcing sync","node addr":"172.18.0.6","node event":"NodeJoin","node name":"kind-control-plane","ts":"2023-02-27T09:16:35Z"}
{"level":"info","ts":1677489395.8405674,"msg":"Starting EventSource","controller":"service","controllerGroup":"","controllerKind":"Service","source":"kind source: *v1.Service"}
{"level":"info","ts":1677489395.8407547,"msg":"Starting EventSource","controller":"service","controllerGroup":"","controllerKind":"Service","source":"kind source: *v1.EndpointSlice"}
{"level":"info","ts":1677489395.8407753,"msg":"Starting EventSource","controller":"service","controllerGroup":"","controllerKind":"Service","source":"channel source: 0xc0004d80a0"}
{"level":"info","ts":1677489395.840808,"msg":"Starting Controller","controller":"service","controllerGroup":"","controllerKind":"Service"}
{"level":"info","ts":1677489395.8409452,"msg":"Starting EventSource","controller":"node","controllerGroup":"","controllerKind":"Node","source":"kind source: *v1.Node"}
{"level":"info","ts":1677489395.8409717,"msg":"Starting Controller","controller":"node","controllerGroup":"","controllerKind":"Node"}
{"level":"info","ts":1677489395.8416162,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1beta2.BGPPeer"}
{"level":"info","ts":1677489395.8424706,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1beta1.IPAddressPool"}
{"level":"info","ts":1677489395.8426557,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1.Node"}
{"level":"info","ts":1677489395.8429735,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1beta1.BGPAdvertisement"}
{"level":"info","ts":1677489395.8434613,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1beta1.L2Advertisement"}
{"level":"info","ts":1677489395.8437064,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1beta1.BFDProfile"}
{"level":"info","ts":1677489395.843945,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1beta1.AddressPool"}
{"level":"info","ts":1677489395.8442128,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1beta1.Community"}
{"level":"info","ts":1677489395.8444724,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1.Secret"}
{"level":"info","ts":1677489395.8447633,"msg":"Starting EventSource","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","source":"kind source: *v1.Namespace"}
{"level":"info","ts":1677489395.8449986,"msg":"Starting Controller","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer"}
W0227 09:16:35.861073       1 warnings.go:70] metallb.io v1beta1 AddressPool is deprecated, consider using IPAddressPool
W0227 09:16:35.865421       1 warnings.go:70] metallb.io v1beta1 AddressPool is deprecated, consider using IPAddressPool
{"level":"info","ts":1677489395.9425287,"msg":"Starting workers","controller":"node","controllerGroup":"","controllerKind":"Node","worker count":1}
{"level":"info","ts":1677489395.943213,"msg":"Starting workers","controller":"service","controllerGroup":"","controllerKind":"Service","worker count":1}
{"caller":"node_controller.go:45","controller":"NodeReconciler","level":"info","start reconcile":"/kind-control-plane","ts":"2023-02-27T09:16:35Z"}
{"caller":"bgp_controller.go:347","event":"nodeLabelsChanged","level":"info","msg":"Node labels changed, resyncing BGP peers","ts":"2023-02-27T09:16:35Z"}
{"caller":"speakerlist.go:271","level":"info","msg":"triggering discovery","op":"memberDiscovery","ts":"2023-02-27T09:16:35Z"}
{"caller":"node_controller.go:67","controller":"NodeReconciler","end reconcile":"/kind-control-plane","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"speakerlist.go:310","level":"info","msg":"node event - forcing sync","node addr":"172.18.0.4","node event":"NodeJoin","node name":"kind-worker2","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"speakerlist.go:256","error":null,"expected":2,"joined":1,"level":"error","msg":"partial join","op":"memberDiscovery","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller.go:60","controller":"ServiceReconciler","level":"info","start reconcile":"default/kubernetes","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller.go:103","controller":"ServiceReconciler","end reconcile":"default/kubernetes","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller.go:60","controller":"ServiceReconciler","level":"info","start reconcile":"kube-system/kube-dns","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller.go:103","controller":"ServiceReconciler","end reconcile":"kube-system/kube-dns","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller.go:60","controller":"ServiceReconciler","level":"info","start reconcile":"metallb-system/webhook-service","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller.go:103","controller":"ServiceReconciler","end reconcile":"metallb-system/webhook-service","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/kind-worker2","ts":"2023-02-27T09:16:35Z"}
{"level":"info","ts":1677489395.9530206,"msg":"Starting workers","controller":"bgppeer","controllerGroup":"metallb.io","controllerKind":"BGPPeer","worker count":1}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:157","controller":"ConfigReconciler","end reconcile":"/kind-worker2","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/kind-control-plane","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:157","controller":"ConfigReconciler","end reconcile":"/kind-control-plane","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/kind-worker","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:157","controller":"ConfigReconciler","end reconcile":"/kind-worker","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/kube-public","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:157","controller":"ConfigReconciler","end reconcile":"/kube-public","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/kube-node-lease","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:157","controller":"ConfigReconciler","end reconcile":"/kube-node-lease","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/default","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:157","controller":"ConfigReconciler","end reconcile":"/default","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/local-path-storage","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:157","controller":"ConfigReconciler","end reconcile":"/local-path-storage","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/metallb-system","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:157","controller":"ConfigReconciler","end reconcile":"/metallb-system","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:50","controller":"ConfigReconciler","level":"info","start reconcile":"/kube-system","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:61","controller":"ServiceReconciler - reprocessAll","level":"info","start reconcile":"metallbreload/reload","ts":"2023-02-27T09:16:35Z"}
{"caller":"service_controller_reload.go:104","controller":"ServiceReconciler - reprocessAll","end reconcile":"metallbreload/reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:145","controller":"ConfigReconciler","event":"force service reload","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"config_controller.go:156","controller":"ConfigReconciler","event":"config reloaded","level":"info","ts":"2023-02-27T09:16:35Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth24b1f795","level":"info","ts":"2023-02-27T09:16:45Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth8013fa28","level":"info","ts":"2023-02-27T09:16:45Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"lo","level":"info","ts":"2023-02-27T09:16:55Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"tunl0","level":"info","ts":"2023-02-27T09:16:55Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth313c8572","level":"info","ts":"2023-02-27T09:16:55Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth24b1f795","level":"info","ts":"2023-02-27T09:16:55Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth8013fa28","level":"info","ts":"2023-02-27T09:16:55Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"lo","level":"info","ts":"2023-02-27T09:17:05Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"tunl0","level":"info","ts":"2023-02-27T09:17:05Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth313c8572","level":"info","ts":"2023-02-27T09:17:05Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth24b1f795","level":"info","ts":"2023-02-27T09:17:05Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth8013fa28","level":"info","ts":"2023-02-27T09:17:05Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"lo","level":"info","ts":"2023-02-27T09:17:15Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"tunl0","level":"info","ts":"2023-02-27T09:17:15Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth313c8572","level":"info","ts":"2023-02-27T09:17:15Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth24b1f795","level":"info","ts":"2023-02-27T09:17:15Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth8013fa28","level":"info","ts":"2023-02-27T09:17:15Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"lo","level":"info","ts":"2023-02-27T09:17:25Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"tunl0","level":"info","ts":"2023-02-27T09:17:25Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth313c8572","level":"info","ts":"2023-02-27T09:17:25Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth24b1f795","level":"info","ts":"2023-02-27T09:17:25Z"}
{"caller":"announcer.go:85","event":"announced interface to exclude","interface":"veth8013fa28","level":"info","ts":"2023-02-27T09:17:25Z"}
```

Could you mind taking a look? Thanks :) @fedepaol 
fedepaol: sorry for the delay, left one last comment (I hope). I think the boolean parameter is not necessary as we are using a fixed path by convention, so we can rely on the presence or the absence of the file to drive the parsing
fedepaol: Did one last minute change about naming directly on your branch.
LGTM
fedepaol: There was a nit `confgmap.yaml`

fedepaol: > > There was a nit `confgmap.yaml`
> 
> Thanks, Does [CI Failling](https://github.com/metallb/metallb/actions/runs/4466226720/jobs/7844361419?pr=1767) is related to these changes?

nope, it's related to the operator not being updated to the latest frr yet (this PR: https://github.com/metallb/metallb-operator/pull/328)

We can ignore the failure for now.

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index ae08778d..a22cb0b1 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -56,7 +56,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
 
-      - uses: actions/setup-go@v3
+      - uses: actions/setup-go@v4
         with:
           go-version-file: "go.mod"
           cache: true
@@ -96,7 +96,7 @@ jobs:
         uses: docker/setup-buildx-action@v2
 
       - name: Build and export ${{ matrix.image }}
-        uses: docker/build-push-action@v3
+        uses: docker/build-push-action@v4
         with:
           context: .
           tags: quay.io/metallb/${{ matrix.image }}:dev-amd64
diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
index 86fe71f1..599caf3b 100644
--- a/.github/workflows/publish.yaml
+++ b/.github/workflows/publish.yaml
@@ -19,7 +19,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
 
-      - uses: actions/setup-go@v3
+      - uses: actions/setup-go@v4
         with:
           go-version-file: "go.mod"
           cache: true
@@ -94,7 +94,7 @@ jobs:
             org.opencontainers.image.description=${{ matrix.image }} for metallb, a network load-balancer implementation for Kubernetes using standard routing protocols
 
       - name: Build and push ${{ matrix.image }}
-        uses: docker/build-push-action@v3
+        uses: docker/build-push-action@v4
         id: build-and-push
         with:
           context: .
diff --git a/charts/metallb/README.md b/charts/metallb/README.md
index 11a9b077..29f7eb12 100644
--- a/charts/metallb/README.md
+++ b/charts/metallb/README.md
@@ -110,6 +110,7 @@ Kubernetes: `>= 1.19.0-0`
 | rbac.create | bool | `true` |  |
 | speaker.affinity | object | `{}` |  |
 | speaker.enabled | bool | `true` |  |
+| speaker.excludeInterfaces.enabled | bool | `true` |  |
 | speaker.frr.enabled | bool | `false` |  |
 | speaker.frr.image.pullPolicy | string | `nil` |  |
 | speaker.frr.image.repository | string | `"quay.io/frrouting/frr"` |  |
diff --git a/charts/metallb/templates/exclude-l2-config.yaml b/charts/metallb/templates/exclude-l2-config.yaml
new file mode 100644
index 00000000..7cc2ff34
--- /dev/null
+++ b/charts/metallb/templates/exclude-l2-config.yaml
@@ -0,0 +1,22 @@
+{{- if .Values.speaker.excludeInterfaces.enabled }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude:
+    - docker.*
+    - cbr.*
+    - dummy.*
+    - virbr.*
+    - lxcbr.*
+    - veth.*
+    - lo
+    - ^cali.*
+    - ^tunl.*
+    - flannel.*
+    - kube-ipvs.*
+    - cni.*
+    - ^nodelocaldns.*
+{{- end }}
\ No newline at end of file
diff --git a/charts/metallb/templates/servicemonitor.yaml b/charts/metallb/templates/servicemonitor.yaml
index 5c2ab422..43825b46 100644
--- a/charts/metallb/templates/servicemonitor.yaml
+++ b/charts/metallb/templates/servicemonitor.yaml
@@ -58,14 +58,6 @@ spec:
   selector:
     matchLabels:
       name: {{ template "metallb.fullname" . }}-speaker-monitor-service
-{{- if .Values.prometheus.serviceMonitor.metricRelabelings }}
-  metricRelabelings:
-{{- toYaml .Values.prometheus.serviceMonitor.metricRelabelings | nindent 4 }}
-{{- end }}
-{{- if .Values.prometheus.serviceMonitor.relabelings }}
-  relabelings:
-{{- toYaml .Values.prometheus.serviceMonitor.relabelings | nindent 4 }}
-{{- end }}      
 ---
 apiVersion: v1
 kind: Service
@@ -112,6 +104,14 @@ metadata:
 spec:
   endpoints:
     - port: {{ template "metrics.exposedportname" . }}
+      {{- if .Values.prometheus.serviceMonitor.metricRelabelings }}
+      metricRelabelings:
+      {{- toYaml .Values.prometheus.serviceMonitor.metricRelabelings | nindent 8 }}
+      {{- end -}}
+      {{- if .Values.prometheus.serviceMonitor.relabelings }}
+      relabelings:
+      {{- toYaml .Values.prometheus.serviceMonitor.relabelings | nindent 8 }}
+      {{- end }}
       {{- if .Values.prometheus.serviceMonitor.interval }}
       interval: {{ .Values.prometheus.serviceMonitor.interval }}
       {{- end }}
@@ -131,14 +131,6 @@ spec:
   selector:
     matchLabels:
       name: {{ template "metallb.fullname" . }}-controller-monitor-service
-{{- if .Values.prometheus.serviceMonitor.metricRelabelings }}
-  metricRelabelings:
-{{- toYaml .Values.prometheus.serviceMonitor.metricRelabelings | nindent 4 }}
-{{- end }}
-{{- if .Values.prometheus.serviceMonitor.relabelings }}
-  relabelings:
-{{- toYaml .Values.prometheus.serviceMonitor.relabelings | nindent 4 }}
-{{- end }}      
 ---
 apiVersion: v1
 kind: Service
diff --git a/charts/metallb/templates/speaker.yaml b/charts/metallb/templates/speaker.yaml
index c94eb615..d982f2f5 100644
--- a/charts/metallb/templates/speaker.yaml
+++ b/charts/metallb/templates/speaker.yaml
@@ -159,6 +159,12 @@ spec:
             secretName: {{ include "metallb.secretName" . }}
             defaultMode: 420
       {{- end }}
+      {{- if .Values.speaker.excludeInterfaces.enabled }}
+        - name: metallb-excludel2
+          configMap:
+            defaultMode: 256
+            name: metallb-excludel2
+      {{- end }}
       {{- if .Values.speaker.frr.enabled }}
         - name: frr-sockets
           emptyDir: {}
@@ -297,7 +303,7 @@ spec:
             - ALL
             add:
             - NET_RAW
-        {{- if or .Values.speaker.frr.enabled .Values.speaker.memberlist.enabled }}
+        {{- if or .Values.speaker.frr.enabled .Values.speaker.memberlist.enabled .Values.speaker.excludeInterfaces.enabled }}
         volumeMounts:
           {{- if .Values.speaker.memberlist.enabled }}
           - name: memberlist 
@@ -307,6 +313,10 @@ spec:
           - name: reloader
             mountPath: /etc/frr_reloader
           {{- end }}
+          {{- if .Values.speaker.excludeInterfaces.enabled }}
+          - name: metallb-excludel2
+            mountPath: /etc/metallb
+          {{- end }}
         {{- end }}
       {{- if .Values.speaker.frr.enabled }}
       - name: frr
diff --git a/charts/metallb/values.schema.json b/charts/metallb/values.schema.json
index 9d3516d0..5a92e56a 100644
--- a/charts/metallb/values.schema.json
+++ b/charts/metallb/values.schema.json
@@ -322,6 +322,14 @@
                 }
               }
             },
+            "excludeInterfaces": {
+              "type": "object",
+              "properties": {
+                "enabled": {
+                  "type": "boolean"
+                }
+              }
+            },
             "updateStrategy": {
               "type": "object",
               "properties": {
diff --git a/charts/metallb/values.yaml b/charts/metallb/values.yaml
index 1844f8fb..689424ca 100644
--- a/charts/metallb/values.yaml
+++ b/charts/metallb/values.yaml
@@ -261,6 +261,8 @@ speaker:
     enabled: true
     mlBindPort: 7946
     mlSecretKeyPath: "/etc/ml_secret_key"
+  excludeInterfaces:
+    enabled: true
   image:
     repository: quay.io/metallb/speaker
     tag:
diff --git a/config/controllers/speaker.yaml b/config/controllers/speaker.yaml
index bdf73c96..e45a2d32 100644
--- a/config/controllers/speaker.yaml
+++ b/config/controllers/speaker.yaml
@@ -85,6 +85,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       nodeSelector:
         kubernetes.io/os: linux
@@ -102,3 +105,7 @@ spec:
         secret: 
           secretName: memberlist
           defaultMode: 420
+      - name: metallb-excludel2
+        configMap:
+          defaultMode: 256
+          name: metallb-excludel2
diff --git a/config/manifests/metallb-frr-prometheus.yaml b/config/manifests/metallb-frr-prometheus.yaml
index 3443f9e5..93529105 100644
--- a/config/manifests/metallb-frr-prometheus.yaml
+++ b/config/manifests/metallb-frr-prometheus.yaml
@@ -1813,6 +1813,15 @@ metadata:
   namespace: metallb-system
 ---
 apiVersion: v1
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: webhook-server-cert
@@ -2194,6 +2203,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       initContainers:
       - command:
@@ -2256,6 +2268,10 @@ spec:
         secret:
           defaultMode: 420
           secretName: memberlist
+      - configMap:
+          defaultMode: 256
+          name: metallb-excludel2
+        name: metallb-excludel2
 ---
 apiVersion: monitoring.coreos.com/v1
 kind: ServiceMonitor
diff --git a/config/manifests/metallb-frr.yaml b/config/manifests/metallb-frr.yaml
index 404daaca..d2710bd5 100644
--- a/config/manifests/metallb-frr.yaml
+++ b/config/manifests/metallb-frr.yaml
@@ -1758,6 +1758,15 @@ metadata:
   namespace: metallb-system
 ---
 apiVersion: v1
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: webhook-server-cert
@@ -2017,6 +2026,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       initContainers:
       - command:
@@ -2079,6 +2091,10 @@ spec:
         secret:
           defaultMode: 420
           secretName: memberlist
+      - configMap:
+          defaultMode: 256
+          name: metallb-excludel2
+        name: metallb-excludel2
 ---
 apiVersion: admissionregistration.k8s.io/v1
 kind: ValidatingWebhookConfiguration
diff --git a/config/manifests/metallb-native-prometheus.yaml b/config/manifests/metallb-native-prometheus.yaml
index 3232cd84..45025fdc 100644
--- a/config/manifests/metallb-native-prometheus.yaml
+++ b/config/manifests/metallb-native-prometheus.yaml
@@ -1715,6 +1715,15 @@ subjects:
   namespace: metallb-system
 ---
 apiVersion: v1
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: webhook-server-cert
@@ -1988,6 +1997,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       nodeSelector:
         kubernetes.io/os: linux
@@ -2005,6 +2017,10 @@ spec:
         secret:
           defaultMode: 420
           secretName: memberlist
+      - configMap:
+          defaultMode: 256
+          name: metallb-excludel2
+        name: metallb-excludel2
 ---
 apiVersion: monitoring.coreos.com/v1
 kind: ServiceMonitor
diff --git a/config/manifests/metallb-native.yaml b/config/manifests/metallb-native.yaml
index ffdf2416..26131219 100644
--- a/config/manifests/metallb-native.yaml
+++ b/config/manifests/metallb-native.yaml
@@ -1660,6 +1660,15 @@ subjects:
   namespace: metallb-system
 ---
 apiVersion: v1
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: webhook-server-cert
@@ -1842,6 +1851,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       nodeSelector:
         kubernetes.io/os: linux
@@ -1859,6 +1871,10 @@ spec:
         secret:
           defaultMode: 420
           secretName: memberlist
+      - configMap:
+          defaultMode: 256
+          name: metallb-excludel2
+        name: metallb-excludel2
 ---
 apiVersion: admissionregistration.k8s.io/v1
 kind: ValidatingWebhookConfiguration
diff --git a/config/native/exclude-l2-config.yaml b/config/native/exclude-l2-config.yaml
new file mode 100644
index 00000000..25a80a00
--- /dev/null
+++ b/config/native/exclude-l2-config.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
diff --git a/config/native/kustomization.yaml b/config/native/kustomization.yaml
index 2c741aaa..6085bacc 100644
--- a/config/native/kustomization.yaml
+++ b/config/native/kustomization.yaml
@@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 resources:
 - ns.yaml
+- exclude-l2-config.yaml
 - ../crd
 - ../rbac
 - ../controllers
diff --git a/controller/service.go b/controller/service.go
index f78f6ebc..e6ccdae7 100644
--- a/controller/service.go
+++ b/controller/service.go
@@ -96,6 +96,7 @@ func (c *controller) convergeBalancer(l log.Logger, key string, svc *v1.Service)
 		// otherwise it'll fail and tell us why.
 		if err = c.ips.Assign(key, svc, lbIPs, k8salloc.Ports(svc), k8salloc.SharingKey(svc), k8salloc.BackendKey(svc)); err != nil {
 			level.Info(l).Log("event", "clearAssignment", "error", err, "msg", "current IP not allowed by config, clearing")
+			c.client.Infof(svc, "ClearAssignment", "current IP for %q not allowed by config, will attempt for new IP assignment: %s", key, err)
 			c.clearServiceState(key, svc)
 			lbIPs = []net.IP{}
 		}
diff --git a/go.mod b/go.mod
index 3ce21dae..bbe8cade 100644
--- a/go.mod
+++ b/go.mod
@@ -23,15 +23,15 @@ require (
 	github.com/prometheus/client_model v0.3.0
 	github.com/prometheus/common v0.39.0
 	golang.org/x/sync v0.1.0
-	golang.org/x/sys v0.4.0
+	golang.org/x/sys v0.6.0
 	k8s.io/api v0.26.1
 	k8s.io/apiextensions-apiserver v0.26.1
 	k8s.io/apimachinery v0.26.1
-	k8s.io/client-go v1.5.2
+	k8s.io/client-go v0.26.1
 	k8s.io/klog v1.0.0
 	k8s.io/kubernetes v1.26.1
 	k8s.io/pod-security-admission v0.0.0
-	sigs.k8s.io/controller-runtime v0.14.1
+	sigs.k8s.io/controller-runtime v0.14.5
 	sigs.k8s.io/yaml v1.3.0
 )
 
diff --git a/go.sum b/go.sum
index 11b6d18b..0532d17f 100644
--- a/go.sum
+++ b/go.sum
@@ -753,8 +753,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1019,8 +1019,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33 h1:LYqFq+6Cj2D0gFfrJvL7iElD4ET6ir3VDdhDdTK7rgc=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33/go.mod h1:soWkSNf2tZC7aMibXEqVhCd73GOY5fJikn8qbdzemB0=
-sigs.k8s.io/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GIpxpzM=
-sigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU=
+sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s=
+sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
diff --git a/internal/bgp/native/native.go b/internal/bgp/native/native.go
index a67b957f..d6084775 100644
--- a/internal/bgp/native/native.go
+++ b/internal/bgp/native/native.go
@@ -15,7 +15,6 @@ import (
 	"sync"
 	"syscall"
 	"time"
-	"unsafe"
 
 	"github.com/go-kit/log"
 	"github.com/go-kit/log/level"
@@ -489,25 +488,6 @@ func (s *session) Close() error {
 	return nil
 }
 
-const (
-	// TCP MD5 Signature (RFC2385).
-	tcpMD5SIG = 14
-)
-
-// This  struct is defined at; linux-kernel: include/uapi/linux/tcp.h,
-// It  must be kept in sync with that definition, see current version:
-// https://github.com/torvalds/linux/blob/v4.16/include/uapi/linux/tcp.h#L253.
-
-//nolint:structcheck
-type tcpmd5sig struct {
-	ssFamily uint16
-	ss       [126]byte
-	pad1     uint16
-	keylen   uint16
-	pad2     uint32
-	key      [80]byte
-}
-
 // 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
 // the low level FD's, skipping the net.Conn API as it has not hooks to set
@@ -592,9 +572,8 @@ func dialMD5(ctx context.Context, addr string, srcAddr net.IP, password string)
 
 	if password != "" {
 		sig := buildTCPMD5Sig(raddr.IP, password)
-		b := *(*[unsafe.Sizeof(sig)]byte)(unsafe.Pointer(&sig))
 		// Better way may be available in  Go 1.11, see go-review.googlesource.com/c/go/+/72810
-		if err = os.NewSyscallError("setsockopt", unix.SetsockoptString(fd, unix.IPPROTO_TCP, tcpMD5SIG, string(b[:]))); err != nil {
+		if err = os.NewSyscallError("setsockopt", unix.SetsockoptTCPMD5Sig(fd, unix.IPPROTO_TCP, unix.TCP_MD5SIG, sig)); err != nil {
 			return nil, err
 		}
 	}
@@ -665,20 +644,20 @@ func dialMD5(ctx context.Context, addr string, srcAddr net.IP, password string)
 	}
 }
 
-func buildTCPMD5Sig(addr net.IP, key string) tcpmd5sig {
-	t := tcpmd5sig{}
+func buildTCPMD5Sig(addr net.IP, key string) *unix.TCPMD5Sig {
+	t := unix.TCPMD5Sig{}
 	if addr.To4() != nil {
-		t.ssFamily = unix.AF_INET
-		copy(t.ss[2:], addr.To4())
+		t.Addr.Family = unix.AF_INET
+		copy(t.Addr.Data[2:], addr.To4())
 	} else {
-		t.ssFamily = unix.AF_INET6
-		copy(t.ss[6:], addr.To16())
+		t.Addr.Family = unix.AF_INET6
+		copy(t.Addr.Data[6:], addr.To16())
 	}
 
-	t.keylen = uint16(len(key))
-	copy(t.key[0:], []byte(key))
+	t.Keylen = uint16(len(key))
+	copy(t.Key[0:], []byte(key))
 
-	return t
+	return &t
 }
 
 // localAddressExists returns true if the address addr exists on any of the
diff --git a/internal/config/config.go b/internal/config/config.go
index dfd2600f..83b6f67d 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -524,8 +524,22 @@ func addressPoolServiceAllocationsFromCR(p metallbv1beta1.IPAddressPool, namespa
 	if p.Spec.AllocateTo == nil {
 		return nil, nil
 	}
-	serviceAllocations := &ServiceAllocation{Priority: p.Spec.AllocateTo.Priority,
-		Namespaces: sets.New(p.Spec.AllocateTo.Namespaces...)}
+	poolNamespaces := sets.Set[string]{}
+	for _, poolNs := range p.Spec.AllocateTo.Namespaces {
+		if poolNamespaces.Has(poolNs) {
+			return nil, errors.New("duplicate definition in namespaces field")
+		}
+		poolNamespaces.Insert(poolNs)
+	}
+	err := validateLabelSelectorDuplicate(p.Spec.AllocateTo.NamespaceSelectors, "namespaceSelectors")
+	if err != nil {
+		return nil, err
+	}
+	err = validateLabelSelectorDuplicate(p.Spec.AllocateTo.ServiceSelectors, "serviceSelectors")
+	if err != nil {
+		return nil, err
+	}
+	serviceAllocations := &ServiceAllocation{Priority: p.Spec.AllocateTo.Priority, Namespaces: poolNamespaces}
 	for i := range p.Spec.AllocateTo.NamespaceSelectors {
 		l, err := metav1.LabelSelectorAsSelector(&p.Spec.AllocateTo.NamespaceSelectors[i])
 		if err != nil {
diff --git a/internal/layer2/announcer.go b/internal/layer2/announcer.go
index 65a0fbb3..4ae3a46d 100644
--- a/internal/layer2/announcer.go
+++ b/internal/layer2/announcer.go
@@ -5,6 +5,7 @@ package layer2
 import (
 	"net"
 	"os"
+	"regexp"
 	"strconv"
 	"sync"
 	"time"
@@ -26,11 +27,12 @@ type Announce struct {
 
 	// This channel can block - do not write to it while holding the mutex
 	// to avoid deadlocking.
-	spamCh chan IPAdvertisement
+	spamCh        chan IPAdvertisement
+	excludeRegexp *regexp.Regexp
 }
 
 // New returns an initialized Announce.
-func New(l log.Logger) (*Announce, error) {
+func New(l log.Logger, excludeRegexp *regexp.Regexp) (*Announce, error) {
 	ret := &Announce{
 		logger:         l,
 		nodeInterfaces: []string{},
@@ -39,7 +41,9 @@ func New(l log.Logger) (*Announce, error) {
 		ips:            map[string][]IPAdvertisement{},
 		ipRefcnt:       map[string]int{},
 		spamCh:         make(chan IPAdvertisement, 1024),
+		excludeRegexp:  excludeRegexp,
 	}
+
 	go ret.interfaceScan()
 	go ret.spamLoop()
 
@@ -67,6 +71,12 @@ func (a *Announce) updateInterfaces() {
 	curIfs := make([]string, 0, len(ifs))
 	for _, intf := range ifs {
 		ifi := intf
+
+		if (a.excludeRegexp != nil) && a.excludeRegexp.MatchString(ifi.Name) {
+			level.Info(a.logger).Log("event", "announced interface to exclude", "interface", ifi.Name)
+			continue
+		}
+
 		curIfs = append(curIfs, ifi.Name)
 		l := log.With(a.logger, "interface", ifi.Name)
 		addrs, err := ifi.Addrs()
diff --git a/speaker/main.go b/speaker/main.go
index e372fc71..8b944df3 100644
--- a/speaker/main.go
+++ b/speaker/main.go
@@ -21,6 +21,8 @@ import (
 	"os"
 	"os/signal"
 	"path/filepath"
+	"regexp"
+	"strings"
 	"syscall"
 
 	"github.com/go-kit/log"
@@ -37,6 +39,7 @@ import (
 	"go.universe.tf/metallb/internal/speakerlist"
 	"go.universe.tf/metallb/internal/version"
 	v1 "k8s.io/api/core/v1"
+	"sigs.k8s.io/yaml"
 )
 
 var announcing = prometheus.NewGaugeVec(prometheus.GaugeOpts{
@@ -51,6 +54,10 @@ var announcing = prometheus.NewGaugeVec(prometheus.GaugeOpts{
 	"ip",
 })
 
+const (
+	excludeL2ConfigPath = "/etc/metallb/excludel2.yaml"
+)
+
 // Service offers methods to mutate a Kubernetes service object.
 type service interface {
 	UpdateStatus(svc *v1.Service) error
@@ -133,13 +140,21 @@ func main() {
 		os.Exit(1)
 	}
 
+	var interfacesToExclude *regexp.Regexp
+	interfacesToExclude, err = parseAnnouncedInterfacesToExclude()
+	if err != nil {
+		level.Error(logger).Log("op", "startup", "msg", "failed to parse announcedInterfacesToExclude from configMap", "error", err)
+		os.Exit(1)
+	}
+
 	// Setup all clients and speakers, config decides what is being done runtime.
 	ctrl, err := newController(controllerConfig{
-		MyNode:   *myNode,
-		Logger:   logger,
-		LogLevel: logging.Level(*logLevel),
-		SList:    sList,
-		bgpType:  bgpImplementation(bgpType),
+		MyNode:                 *myNode,
+		Logger:                 logger,
+		LogLevel:               logging.Level(*logLevel),
+		SList:                  sList,
+		bgpType:                bgpImplementation(bgpType),
+		InterfaceExcludeRegexp: interfacesToExclude,
 	})
 	if err != nil {
 		level.Error(logger).Log("op", "startup", "error", err, "msg", "failed to create MetalLB controller")
@@ -212,8 +227,10 @@ type controllerConfig struct {
 
 	// For testing only, and will be removed in a future release.
 	// See: https://github.com/metallb/metallb/issues/152.
-	DisableLayer2      bool
-	SupportedProtocols []config.Proto
+	DisableLayer2                bool
+	SupportedProtocols           []config.Proto
+	AnnouncedInterfacesToExclude []string `yaml:"announcedInterfacesToExclude"`
+	InterfaceExcludeRegexp       *regexp.Regexp
 }
 
 func newController(cfg controllerConfig) (*controller, error) {
@@ -229,7 +246,7 @@ func newController(cfg controllerConfig) (*controller, error) {
 	protocols := []config.Proto{config.BGP}
 
 	if !cfg.DisableLayer2 {
-		a, err := layer2.New(cfg.Logger)
+		a, err := layer2.New(cfg.Logger, cfg.InterfaceExcludeRegexp)
 		if err != nil {
 			return nil, fmt.Errorf("making layer2 announcer: %s", err)
 		}
@@ -443,6 +460,27 @@ func compareIPs(ips1, ips2 []net.IP) bool {
 	return true
 }
 
+func parseAnnouncedInterfacesToExclude() (*regexp.Regexp, error) {
+	configmapBytes, err := os.ReadFile(filepath.Clean(excludeL2ConfigPath))
+	if err != nil && !os.IsNotExist(err) {
+		return nil, err
+	}
+
+	var conf controllerConfig
+	if err = yaml.Unmarshal(configmapBytes, &conf); err != nil {
+		return nil, err
+	}
+
+	var excludeRegexp *regexp.Regexp
+	if len(conf.AnnouncedInterfacesToExclude) > 0 {
+		excludeRegexp, err = regexp.Compile("(" + strings.Join(conf.AnnouncedInterfacesToExclude, ")|(") + ")")
+		if err != nil {
+			return nil, err
+		}
+	}
+	return excludeRegexp, nil
+}
+
 func (c *controller) SetConfig(l log.Logger, cfg *config.Config) controllers.SyncState {
 
 	level.Debug(l).Log("event", "startUpdate", "msg", "start of config update")


Test patch:
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index a1a66ba5..37542bbd 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -416,6 +416,70 @@ func TestParse(t *testing.T) {
 			},
 		},
 
+		{
+			desc: "ip address pool with duplicate namespace selection",
+			crs: ClusterResources{
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "pool1",
+						},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.0.0/16",
+								"10.50.0.0/24",
+							},
+							AvoidBuggyIPs: true,
+							AutoAssign:    pointer.BoolPtr(false),
+							AllocateTo: &v1beta1.ServiceAllocation{Priority: 1,
+								Namespaces: []string{"test-ns1", "test-ns1"}},
+						},
+					},
+				},
+				Namespaces: []corev1.Namespace{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "test-ns1",
+						},
+					},
+				},
+			},
+		},
+
+		{
+			desc: "ip address pool with duplicate namespace selectors",
+			crs: ClusterResources{
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "pool1",
+						},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.0.0/16",
+								"10.50.0.0/24",
+							},
+							AvoidBuggyIPs: true,
+							AutoAssign:    pointer.BoolPtr(false),
+							AllocateTo: &v1beta1.ServiceAllocation{
+								Priority: 1,
+								NamespaceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}},
+									{MatchLabels: map[string]string{"foo": "bar"}}},
+							},
+						},
+					},
+				},
+				Namespaces: []corev1.Namespace{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name:   "test-ns1",
+							Labels: map[string]string{"foo": "bar"},
+						},
+					},
+				},
+			},
+		},
+
 		{
 			desc: "ip address pool with service selection",
 			crs: ClusterResources{
@@ -467,6 +531,32 @@ func TestParse(t *testing.T) {
 			},
 		},
 
+		{
+			desc: "ip address pool with duplicate service selection",
+			crs: ClusterResources{
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "pool1",
+						},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"30.0.0.0/8",
+							},
+							AllocateTo: &v1beta1.ServiceAllocation{Priority: 2,
+								ServiceSelectors: []v1.LabelSelector{{MatchExpressions: []metav1.LabelSelectorRequirement{
+									{
+										Key:      "foo",
+										Operator: metav1.LabelSelectorOpIn,
+										Values:   []string{"bar", "bar"},
+									},
+								}}}},
+						},
+					},
+				},
+			},
+		},
+
 		{
 			desc: "ip address pool with namespace and service selection",
 			crs: ClusterResources{