FRR validation incorrectly rejects different myASN values for BGPPeers targeting disjoint node pools
MetalLB Version
v0.15.3
Deployment method
Charts
Main CNI
calico
Kubernetes Version
v1.36.1
Cluster Distribution
No response
Describe the bug
FRR enforces a real constraint: one router bgp <ASN> per VRF. This is fundamental to BGP — a single routing table can only belong to one autonomous system. MetalLB correctly wants to prevent generating FRR configs that violate this.
But FRR doesn't run at cluster scope. Each node runs its own speaker pod with its own independent FRR process and its own frr.conf. Node A's FRR instance has no knowledge of node B's FRR instance. They are more isolated than two VRFs on the same host — VRFs share a kernel and can leak routes between each other; separate nodes share nothing.
The validation treats all these independent FRR instances as one, because every peer without an explicit VRF has VRFName: "". Two peers targeting completely different node pools both have VRFName: "", so the check sees them as conflicting — even though they will never coexist in the same FRR process on any node.
Problematic validation code:
// internal/config/validation.go — DiscardNativeOnly
for _, p := range c.Peers {
for _, p1 := range c.Peers[1:] {
if p.Spec.MyASN != p1.Spec.MyASN &&
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)
}
}
}Why This Is a Bug The one-ASN-per-VRF restriction is an FRR process constraint, not a cluster-wide constraint. Each speaker pod maintains its own FRR daemon and configuration. Since FRR instances are isolated per node, peers assigned to different nodes cannot conflict. The current validation therefore rejects configurations that are valid from both Kubernetes scheduling and FRR perspectives.
To Reproduce
- Deploy MetalLB in FRR mode.
- Create two Kubernetes node pools with mutually exclusive labels, for example:
- Node pool A:
pool=blue - Node pool B:
pool=green
- Node pool A:
- Create two
BGPPeerresources:- Peer A:
myASN: 65001nodeSelectors: pool=blue
- Peer B:
myASN: 65002nodeSelectors: pool=green
- Both use the default VRF (
VRFName: "").
- Peer A:
- Apply the manifests.
Actual Result
The admission webhook rejects the second BGPPeer with an error similar to:
peer <address> has myAsn different from <address>, in FRR mode all myAsn must be equal for the same VRFExpected Behavior
The configuration should be accepted because the two BGPPeer resources target disjoint node pools and therefore can never be rendered into the same FRR instance. The one-ASN-per-VRF constraint remains satisfied on every node.
Additional Context
Proposed Solution:
The fix should modify the validation shown above to check whether two peers with different myASN in the same VRF target the same node — instead of blindly comparing all peers cluster-wide. So that the one-ASN-per-VRF constraint is preserved and it is enforced at the correct scope.
The key insight is that two BGPPeers with different myASN in the same VRF are only a problem if they land on the same node. If nodeSelectors ensure they target different nodes, no single FRR instance ever sees both — the constraint is naturally satisfied.
The fix replaces the cluster-wide check with one that resolves nodeSelectors against actual node labels to determine whether two peers can land on the same node. The BGPPeer webhook already has the infrastructure for this — it just needs to pass NodeList to the validator, same as the BGP advertisement webhook already does.
I've read and agree with the following
- I've checked all open and closed issues and my request is not there.
- I've checked all open and closed pull requests and my request is not there.
I've read and agree with the following
- I've checked all open and closed issues and my issue is not there.
- This bug is reproducible when deploying MetalLB from the main branch
- I have read the troubleshooting guide and I am still not able to make it work
- I checked the logs and MetalLB is not discarding the configuration as not valid
- I enabled the debug logs, collected the information required from the cluster using the collect script and will attach them to the issue
- I will provide the definition of my service and the related endpoint slices and attach them to this issue
Source: metallb/metallb