Skip to content

Commit ab2bbed

Browse files
authored
Merge pull request #4946 from jcaamano/fix-disableMP
RouteAdvertisements: fail if DisableMP is unset
2 parents 8b650b4 + 298c667 commit ab2bbed

File tree

2 files changed

+91
-13
lines changed

2 files changed

+91
-13
lines changed

go-controller/pkg/clustermanager/routeadvertisements/controller.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -506,13 +506,16 @@ func (c *Controller) generateFRRConfigurations(ra *ratypes.RouteAdvertisements)
506506
matchedNetworks := sets.New[string]()
507507
for _, frrConfig := range frrConfigs {
508508
// generate FRRConfiguration for each source FRRConfiguration/node combination
509-
new := c.generateFRRConfiguration(
509+
new, err := c.generateFRRConfiguration(
510510
ra,
511511
frrConfig,
512512
nodeName,
513513
selectedNetworks,
514514
matchedNetworks,
515515
)
516+
if err != nil {
517+
return nil, nil, err
518+
}
516519
if new == nil {
517520
// if we got nil, we didn't match any VRF
518521
return nil, nil, fmt.Errorf("%w: FRRConfiguration %q selected for node %q has no VRF matching the RouteAdvertisements target VRF or any selected network",
@@ -538,7 +541,7 @@ func (c *Controller) generateFRRConfiguration(
538541
nodeName string,
539542
selectedNetworks *selectedNetworks,
540543
matchedNetworks sets.Set[string],
541-
) *frrtypes.FRRConfiguration {
544+
) (*frrtypes.FRRConfiguration, error) {
542545
routers := []frrtypes.Router{}
543546
advertisements := sets.New(ra.Spec.Advertisements...)
544547

@@ -597,16 +600,30 @@ func (c *Controller) generateFRRConfiguration(
597600
targetRouter.Prefixes = advertisePrefixes
598601
targetRouter.Neighbors = make([]frrtypes.Neighbor, 0, len(source.Spec.BGP.Routers[i].Neighbors))
599602
for _, neighbor := range source.Spec.BGP.Routers[i].Neighbors {
600-
advertisePrefixes := advertisePrefixes
601-
receivePrefixes := receivePrefixes
602-
if neighbor.DisableMP {
603-
isIPV6 := utilnet.IsIPv6String(neighbor.Address)
604-
advertisePrefixes = util.MatchAllIPNetsStringFamily(isIPV6, advertisePrefixes)
605-
receivePrefixes = util.MatchAllIPNetsStringFamily(isIPV6, receivePrefixes)
603+
// If MultiProtocol is enabled (default) then a BGP session carries
604+
// prefixes of both IPv4 and IPv6 families. Our problem is that with
605+
// an IPv4 session, FRR can incorrectly pick the masquerade IPv6
606+
// address (instead of the real address) as next hop for IPv6
607+
// prefixes and that won't work. Note that with a dedicated IPv6
608+
// session that can't happen since FRR will use the same address
609+
// that was used to stablish the session. Let's enforce the use of
610+
// DisableMP for now.
611+
if !neighbor.DisableMP {
612+
return nil, fmt.Errorf("%w: DisableMP==false not supported, seen on FRRConfiguration %s/%s neighbor %s",
613+
errConfig,
614+
source.Namespace,
615+
source.Name,
616+
neighbor.Address,
617+
)
606618
}
619+
620+
isIPV6 := utilnet.IsIPv6String(neighbor.Address)
621+
advertisePrefixes := util.MatchAllIPNetsStringFamily(isIPV6, advertisePrefixes)
622+
receivePrefixes := util.MatchAllIPNetsStringFamily(isIPV6, receivePrefixes)
607623
if len(advertisePrefixes) == 0 {
608624
continue
609625
}
626+
610627
neighbor.ToAdvertise = frrtypes.Advertise{
611628
Allowed: frrtypes.AllowedOutPrefixes{
612629
Mode: frrtypes.AllowRestricted,
@@ -686,7 +703,7 @@ func (c *Controller) generateFRRConfiguration(
686703
}
687704
if len(routers) == 0 {
688705
// we ended up with no routers, bail out
689-
return nil
706+
return nil, nil
690707
}
691708

692709
new := &frrtypes.FRRConfiguration{}
@@ -712,7 +729,7 @@ func (c *Controller) generateFRRConfiguration(
712729
},
713730
}
714731

715-
return new
732+
return new, nil
716733
}
717734

718735
// updateFRRConfigurations updates the FRRConfigurations that apply for a

go-controller/pkg/clustermanager/routeadvertisements/controller_test.go

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
ctesting "k8s.io/client-go/testing"
2323
"k8s.io/client-go/tools/cache"
2424
"k8s.io/client-go/util/workqueue"
25+
"k8s.io/utils/ptr"
2526

2627
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
2728
controllerutil "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/controller"
@@ -102,14 +103,16 @@ func (tn testNode) Node() *corev1.Node {
102103
type testNeighbor struct {
103104
ASN uint32
104105
Address string
106+
DisableMP *bool
105107
Receive []string
106108
Advertise []string
107109
}
108110

109111
func (tn testNeighbor) Neighbor() frrapi.Neighbor {
110112
n := frrapi.Neighbor{
111-
ASN: tn.ASN,
112-
Address: tn.Address,
113+
ASN: tn.ASN,
114+
Address: tn.Address,
115+
DisableMP: true,
113116
ToReceive: frrapi.Receive{
114117
Allowed: frrapi.AllowedInPrefixes{
115118
Mode: frrapi.AllowRestricted,
@@ -122,6 +125,9 @@ func (tn testNeighbor) Neighbor() frrapi.Neighbor {
122125
},
123126
},
124127
}
128+
if tn.DisableMP != nil {
129+
n.DisableMP = *tn.DisableMP
130+
}
125131
for _, receive := range tn.Receive {
126132
sep := strings.LastIndex(receive, "/")
127133
if sep == -1 {
@@ -365,6 +371,39 @@ func TestController_reconcile(t *testing.T) {
365371
},
366372
expectNADAnnotations: map[string]map[string]string{"default": {types.OvnRouteAdvertisementsKey: "[\"ra\"]"}},
367373
},
374+
{
375+
name: "reconciles dual-stack pod+eip RouteAdvertisement for a single FRR config, node and default network and target VRF",
376+
ra: &testRA{Name: "ra", AdvertisePods: true, AdvertiseEgressIPs: true},
377+
frrConfigs: []*testFRRConfig{
378+
{
379+
Name: "frrConfig",
380+
Namespace: frrNamespace,
381+
Routers: []*testRouter{
382+
{ASN: 1, Prefixes: []string{"1.1.1.0/24"}, Neighbors: []*testNeighbor{
383+
{ASN: 1, Address: "1.0.0.100"},
384+
{ASN: 1, Address: "fd02::ffff:100:64"},
385+
}},
386+
},
387+
},
388+
},
389+
nodes: []*testNode{{Name: "node", SubnetsAnnotation: "{\"default\":[\"1.1.0.0/24\",\"fd01::/64\"]}"}},
390+
eips: []*testEIP{{Name: "eipv4", EIPs: map[string]string{"node": "1.0.1.1"}}, {Name: "eipv6", EIPs: map[string]string{"node": "fd03::ffff:100:101"}}},
391+
reconcile: "ra",
392+
expectAcceptedStatus: metav1.ConditionTrue,
393+
expectFRRConfigs: []*testFRRConfig{
394+
{
395+
Labels: map[string]string{types.OvnRouteAdvertisementsKey: "ra"},
396+
Annotations: map[string]string{types.OvnRouteAdvertisementsKey: "ra/frrConfig/node"},
397+
NodeSelector: map[string]string{"kubernetes.io/hostname": "node"},
398+
Routers: []*testRouter{
399+
{ASN: 1, Prefixes: []string{"1.0.1.1/32", "1.1.0.0/24", "fd01::/64", "fd03::ffff:100:101/128"}, Neighbors: []*testNeighbor{
400+
{ASN: 1, Address: "1.0.0.100", Advertise: []string{"1.0.1.1/32", "1.1.0.0/24"}, Receive: []string{"1.1.0.0/16/24"}},
401+
{ASN: 1, Address: "fd02::ffff:100:64", Advertise: []string{"fd01::/64", "fd03::ffff:100:101/128"}, Receive: []string{"fd01::/48/64"}},
402+
}},
403+
}},
404+
},
405+
expectNADAnnotations: map[string]map[string]string{"default": {types.OvnRouteAdvertisementsKey: "[\"ra\"]"}},
406+
},
368407
{
369408
name: "reconciles pod RouteAdvertisement for a single FRR config, node, non default networks and default target VRF",
370409
ra: &testRA{Name: "ra", AdvertisePods: true, NetworkSelector: map[string]string{"selected": "true"}},
@@ -785,6 +824,24 @@ func TestController_reconcile(t *testing.T) {
785824
reconcile: "ra",
786825
expectAcceptedStatus: metav1.ConditionFalse,
787826
},
827+
{
828+
name: "fails to reconcile if DisableMP is unset",
829+
ra: &testRA{Name: "ra", AdvertisePods: true},
830+
frrConfigs: []*testFRRConfig{
831+
{
832+
Name: "frrConfig",
833+
Namespace: frrNamespace,
834+
Routers: []*testRouter{
835+
{ASN: 1, Prefixes: []string{"1.1.1.0/24"}, Neighbors: []*testNeighbor{
836+
{ASN: 1, Address: "1.0.0.100", DisableMP: ptr.To(false)},
837+
}},
838+
},
839+
},
840+
},
841+
nodes: []*testNode{{Name: "node", SubnetsAnnotation: "{\"default\":\"1.1.0.0/24\"}"}},
842+
reconcile: "ra",
843+
expectAcceptedStatus: metav1.ConditionFalse,
844+
},
788845
}
789846
for _, tt := range tests {
790847
t.Run(tt.name, func(t *testing.T) {
@@ -798,6 +855,10 @@ func TestController_reconcile(t *testing.T) {
798855
CIDR: ovntest.MustParseIPNet("1.1.0.0/16"),
799856
HostSubnetLength: 24,
800857
},
858+
{
859+
CIDR: ovntest.MustParseIPNet("fd01::/48"),
860+
HostSubnetLength: 64,
861+
},
801862
}
802863
config.OVNKubernetesFeature.EnableMultiNetwork = true
803864
config.OVNKubernetesFeature.EnableRouteAdvertisements = true
@@ -885,7 +946,7 @@ func TestController_reconcile(t *testing.T) {
885946
g.Expect(err).ToNot(gomega.HaveOccurred())
886947
accepted := meta.FindStatusCondition(ra.Status.Conditions, "Accepted")
887948
g.Expect(accepted).NotTo(gomega.BeNil())
888-
g.Expect(accepted.Status).To(gomega.Equal(tt.expectAcceptedStatus))
949+
g.Expect(accepted.Status).To(gomega.Equal(tt.expectAcceptedStatus), accepted.Message)
889950
}
890951

891952
// verify FRRConfigurations have been created/updated/deleted as expected

0 commit comments

Comments
 (0)