Skip to content

Commit bc877dd

Browse files
committed
IPV6 bgp validation: validate bgpadvertisement selected pools
We can have l2 ipv6 addresses when using metallb in native mode. The important thing to consider is, not having any v6 based pool selected by a bgp advertisement. Here we narrow down the scope of the checking function. Signed-off-by: Federico Paolinelli <[email protected]>
1 parent 1c4b49f commit bc877dd

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

internal/config/validation.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import (
77

88
"errors"
99

10+
metallbv1beta1 "go.universe.tf/metallb/api/v1beta1"
1011
metallbv1beta2 "go.universe.tf/metallb/api/v1beta2"
1112
"go.universe.tf/metallb/internal/bgp/community"
1213
"go.universe.tf/metallb/internal/ipfamily"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/labels"
1316
)
1417

1518
type Validate func(ClusterResources) error
@@ -69,7 +72,17 @@ func findIPv6BGPAdvertisement(c ClusterResources) error {
6972
if len(c.BGPAdvs) == 0 {
7073
return nil
7174
}
75+
76+
bgpSelectors, err := poolSelectorsForBGP(c)
77+
if err != nil {
78+
return err
79+
}
80+
7281
for _, p := range c.Pools {
82+
if !bgpSelectors.matchesPool(p) {
83+
continue
84+
}
85+
7386
for _, cidr := range p.Spec.Addresses {
7487
nets, err := ParseCIDR(cidr)
7588
if err != nil {
@@ -203,3 +216,49 @@ func hasBFDEcho(peer *Peer, bfdProfiles map[string]*BFDProfile) bool {
203216
func peerAddressKey(peer metallbv1beta2.BGPPeerSpec) string {
204217
return fmt.Sprintf("%s-%s", peer.Address, peer.VRFName)
205218
}
219+
220+
type poolSelector struct {
221+
byName map[string]struct{}
222+
byLabels []labels.Selector
223+
}
224+
225+
func (s poolSelector) matchesPool(p metallbv1beta1.IPAddressPool) bool {
226+
if len(s.byLabels) == 0 && len(s.byName) == 0 {
227+
return true
228+
}
229+
230+
if _, ok := s.byName[p.Name]; ok {
231+
return true
232+
}
233+
for _, l := range s.byLabels {
234+
if l.Matches(labels.Set(p.Labels)) {
235+
return true
236+
}
237+
}
238+
return false
239+
}
240+
241+
func poolSelectorsForBGP(c ClusterResources) (poolSelector, error) {
242+
selectedPools := make(map[string]struct{})
243+
poolsSelectors := []labels.Selector{}
244+
for _, adv := range c.BGPAdvs {
245+
if len(adv.Spec.IPAddressPools) == 0 &&
246+
len(adv.Spec.IPAddressPoolSelectors) == 0 {
247+
return poolSelector{}, nil // no selectors, let's catch em all!
248+
}
249+
for _, p := range adv.Spec.IPAddressPools {
250+
selectedPools[p] = struct{}{}
251+
}
252+
for _, selector := range adv.Spec.IPAddressPoolSelectors {
253+
l, err := metav1.LabelSelectorAsSelector(&selector)
254+
if err != nil {
255+
return poolSelector{}, fmt.Errorf("invalid label selector %v", selector)
256+
}
257+
poolsSelectors = append(poolsSelectors, l)
258+
}
259+
}
260+
return poolSelector{
261+
byName: selectedPools,
262+
byLabels: poolsSelectors,
263+
}, nil
264+
}

internal/config/validation_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,104 @@ func TestValidate(t *testing.T) {
7070
},
7171
mustFail: true,
7272
},
73+
{
74+
desc: "v6 address but pool not selected",
75+
config: ClusterResources{
76+
Pools: []v1beta1.IPAddressPool{
77+
{
78+
ObjectMeta: v1.ObjectMeta{
79+
Name: "foo",
80+
},
81+
Spec: v1beta1.IPAddressPoolSpec{
82+
Addresses: []string{"2001:db8::/64"},
83+
},
84+
},
85+
},
86+
BGPAdvs: []v1beta1.BGPAdvertisement{
87+
{
88+
ObjectMeta: v1.ObjectMeta{
89+
Name: "foo",
90+
},
91+
Spec: v1beta1.BGPAdvertisementSpec{
92+
IPAddressPools: []string{"bar"},
93+
},
94+
},
95+
},
96+
},
97+
},
98+
{
99+
desc: "v6 address and selected",
100+
config: ClusterResources{
101+
Pools: []v1beta1.IPAddressPool{
102+
{
103+
ObjectMeta: v1.ObjectMeta{
104+
Name: "foo",
105+
},
106+
Spec: v1beta1.IPAddressPoolSpec{
107+
Addresses: []string{"2001:db8::/64"},
108+
},
109+
},
110+
},
111+
BGPAdvs: []v1beta1.BGPAdvertisement{
112+
{
113+
ObjectMeta: v1.ObjectMeta{
114+
Name: "bar",
115+
},
116+
Spec: v1beta1.BGPAdvertisementSpec{
117+
IPAddressPools: []string{"foo1"},
118+
},
119+
},
120+
{
121+
ObjectMeta: v1.ObjectMeta{
122+
Name: "bar",
123+
},
124+
Spec: v1beta1.BGPAdvertisementSpec{
125+
IPAddressPools: []string{"foo"},
126+
},
127+
},
128+
},
129+
},
130+
mustFail: true,
131+
},
132+
{
133+
desc: "v6 address and selected by labels",
134+
config: ClusterResources{
135+
Pools: []v1beta1.IPAddressPool{
136+
{
137+
ObjectMeta: v1.ObjectMeta{
138+
Name: "foo",
139+
Labels: map[string]string{"key": "value"},
140+
},
141+
Spec: v1beta1.IPAddressPoolSpec{
142+
Addresses: []string{"2001:db8::/64"},
143+
},
144+
},
145+
},
146+
BGPAdvs: []v1beta1.BGPAdvertisement{
147+
{
148+
ObjectMeta: v1.ObjectMeta{
149+
Name: "bar",
150+
},
151+
Spec: v1beta1.BGPAdvertisementSpec{
152+
IPAddressPools: []string{"foo1"},
153+
},
154+
},
155+
{
156+
ObjectMeta: v1.ObjectMeta{
157+
Name: "bar",
158+
},
159+
Spec: v1beta1.BGPAdvertisementSpec{
160+
IPAddressPoolSelectors: []v1.LabelSelector{
161+
{
162+
MatchLabels: map[string]string{"key": "value"},
163+
},
164+
},
165+
},
166+
},
167+
},
168+
},
169+
mustFail: true,
170+
},
73171
{
74172
desc: "enable BGP GracefulRestart",
75173
config: ClusterResources{

0 commit comments

Comments
 (0)