Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unnumbered BGP peering for frrk8s backend #2605

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

karampok
Copy link
Contributor

/kind feature

What this PR does / why we need it:

Special notes for your reviewer:

Release note:

Add unnumbered BGP peering for frrk8s backend 

@karampok karampok force-pushed the unnumbered-frrk8s branch 7 times, most recently from 2609f31 to e5b79ad Compare November 26, 2024 09:07
@karampok karampok force-pushed the unnumbered-frrk8s branch 5 times, most recently from eaa72e2 to 0591413 Compare December 19, 2024 15:03
@karampok karampok marked this pull request as ready for review December 20, 2024 08:18
Copy link
Member

@oribon oribon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you update the DiscardFRROnly func to block interface?
also, if we decide this is only for frr-k8s I guess we should block it in frr mode only as well (currently they share the validation)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these changes related to unnumbered? if not, can you put it in a different commit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it is a refactor on host:port (split out the port) that forced to do the change in native.go

@@ -100,7 +100,8 @@ func (sm *sessionManager) SetEventCallback(func(interface{})) {}

// run tries to stay connected to the peer, and pumps route updates to it.
func (s *session) run() {
defer stats.DeleteSession(s.PeerAddress)
label := fmt.Sprintf("%s:%d", s.PeerAddress, s.PeerPort)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: label->peerLabel, have this string returned from a function (peerLabelFor)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done I think, at lease I add a method (so I suppose the For in the name looks not great)

},
},
{
desc: "unnumbered peer with address without interface ok",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like the previous one (interface only), also I guess this scenario can be removed as it should be all over the tests by now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean this is the normal way, so it does not add value?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep

@@ -226,14 +225,20 @@ func (c *bgpController) syncPeers(l log.Logger) error {
} else if p.session == nil && shouldRun {
// Session doesn't exist, but should be running. Create
// it.
level.Info(l).Log("event", "peerAdded", "peer", p.cfg.Addr, "msg", "peer configured, starting BGP session")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to change the p.cfg.Addr in the rest of the places (+wrap this id in a function)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

host, port, err := net.SplitHostPort(s.PeerAddress)
if err != nil {
return nil, err
if s.PeerPort == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we block this sooner when the config is parsed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think not,

func sessionName(s session) string {
baseName := fmt.Sprintf("%d@%s-%d@%s", s.PeerASN, s.PeerAddress, s.MyASN, s.SourceAddress)
f := func(ASN uint32, dynamicASN string) string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just use the contents of this function to set asn? this is a bit confusing, especially since it's used once

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if !reflect.DeepEqual(s.PasswordRef, corev1.SecretReference{}) && s.Password != "" {
return fmt.Errorf("invalid session with password and secret set: %s", sessionName(*s))
}

if s.PeerPort == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean validate eariler?

I think we should change api validation

// Port to dial when establishing the session.
	// +optional
	// +kubebuilder:validation:Minimum=0
	// +kubebuilder:validation:Maximum=16384
	// +kubebuilder:default:=179
	Port uint16 `json:"peerPort,omitempty"`

and remove validation,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I mean block it from ever getting to this situation (blocking the bad config earlier, not calling the session code with port 0).

"go.universe.tf/e2etest/pkg/metallb"
clientset "k8s.io/client-go/kubernetes"
)

func dumpBGPPeers(basePath, testName string, containers []*frrcontainer.FRR) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this function (what's the difference between this and dumpBGPInfo)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dupBGPPeers is doing the same as bumpBGPInfo but is not dumping the standard/hardcoded FRRContainers and is not dumping the speakerPods.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so when do we want to use this and not the other?

)

var FRRImage string
var _ = ginkgo.Describe("IPV4 FRRK8S-MODE Unnumbered BGP", func() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this v4 only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am working towards on all stacks

Comment on lines 132 to 133
// peerLLA, err := netdev.LinkLocalAddressForDevice(externalP2PContainer, iface)
// Expect(err).NotTo(HaveOccurred())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of, might be needed for the "learning route" part == if the external router adv routes I should check the local route for that.

(keep working on that)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given I configure using the BGPPeer CR with Interface, adding the route learning is not possible (I think)
I clean up all related code.


ginkgo.By(fmt.Sprintf("validating the p2p peer %s received the routes from node", externalP2PContainer.Name))
validatePrefixViaFor(externalP2PContainer, iface, nodeLLA, prefixSend)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to check that the external client can reach the service?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could investigate but why do you think it worth? I mean having the route on the peer is all what metallb does, if works or not is not something that depends on our changes. Is not trivial because I will need to modify the routes in the host to return traffic in the right secondary interface

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough, we can keep it this way

Comment on lines 78 to 79
ToAdvertiseV4: []string{"1.1.1.0/24"},
ToAdvertiseV6: []string{"1111::/64"},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this do something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was supposed to be part of the route learning but removed.

return err
}
for _, n := range neighbors {
if n.BGPNeighborAddr == nodeLLA && n.Connected { //&& n.BFDInfo.Status == "Up" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add bfd? 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i willl add it, we have the e2e in frr-k8s doing BFD

ingressIP := jigservice.GetIngressPoint(&i)
err = config.ValidateIPInRange(resources.Pools, ingressIP)
Expect(err).NotTo(HaveOccurred())
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's verify that the service got the prefixSend (it makes the test clearer)

Copy link
Contributor Author

@karampok karampok Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that prefixSend includes mask /32, /128, doing that check the result does not look cleaner.

IMO we can let that as is because the real check is following. I take another look once I have the per stack test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the check we can keep that if you like it more (or revert)

@karampok karampok marked this pull request as draft January 17, 2025 17:13
@karampok
Copy link
Contributor Author

can you update the DiscardFRROnly func to block interface? also, if we decide this is only for frr-k8s I guess we should block it in frr mode only as well (currently they share the validation)

The plan is to proceed next to support that in the frr backend AFAIK, not sure worth spliting the validation.

@karampok karampok force-pushed the unnumbered-frrk8s branch 3 times, most recently from 6ea524d to 29aa697 Compare January 21, 2025 19:01
@karampok karampok marked this pull request as ready for review January 22, 2025 06:59
@karampok karampok force-pushed the unnumbered-frrk8s branch 4 times, most recently from a617ae6 to 4ef8d5b Compare January 24, 2025 11:58
"go.universe.tf/e2etest/pkg/metallb"
clientset "k8s.io/client-go/kubernetes"
)

func dumpBGPPeers(basePath, testName string, containers []*frrcontainer.FRR) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so when do we want to use this and not the other?

@@ -54,6 +54,9 @@ func DiscardFRROnly(c ClusterResources) error {
if p.Spec.DynamicASN != "" {
return fmt.Errorf("peer %s has dynamicASN set on native bgp mode", p.Spec.Address)
}
if p.Spec.Interface != "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please add a case for this in TestValidate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -60,6 +59,15 @@ type peer struct {
session bgp.Session
}

func (p peer) id() string {
ret := ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be ret := p.cfg.Iface, if the addr is not nil then ret = p.cfg.Addr.String() and return ret?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if p.cfg.Addr != nil {
peerAddr = p.cfg.Addr.String()
}
level.Info(l).Log("event", "peerAdded", "peer", peerAddr+p.cfg.Iface, "msg", "peer configured, starting BGP session")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this back up (using the id func)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -98,9 +98,14 @@ func (sm *sessionManager) SyncExtraInfo(extras string) error {

func (sm *sessionManager) SetEventCallback(func(interface{})) {}

func (s *session) PeerLabelFor() string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah the "for" can be removed (in response to your comment 😅)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 53 to 55
if v := s.DynamicASN; v != "" {
asn = v
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if s.DynamicASN != "" and asn = s.DynamicASN looks cleaner

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(not sure) but done

}

peer := s.PeerAddress
if v := s.PeerInterface; v != "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@karampok karampok force-pushed the unnumbered-frrk8s branch 3 times, most recently from 57ddd7b to d0b0af2 Compare January 27, 2025 15:07
Signed-off-by: karampok <karampok@gmail.com>
- Add 'interface' field to BGPPeer CRD that specify network interface
  for unnumbered peering
- Make 'peerAddress' and 'interface' mutually exclusive

Signed-off-by: karampok <karampok@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants