Skip to content

Commit

Permalink
bio-rd BGP configuration refactoring (#455)
Browse files Browse the repository at this point in the history
This PR improves the bio-rd BGP configuration.
It aims to provide the basic capability to run bio-rd in the current state after checking out supporting most out of the current feature set.

In detail:
 * adds IPv6 support
 * improve readability
 * encapsulate BGP server configuration
 * allow to change BGP listen address
 * respect configured VRF
 * add group / neighbor inheritance for AFI specific configurations
 * add tests for BGP config loading
  • Loading branch information
czerwonk authored Dec 10, 2023
1 parent 6bea7c1 commit c567987
Show file tree
Hide file tree
Showing 5 changed files with 538 additions and 166 deletions.
222 changes: 222 additions & 0 deletions cmd/bio-rd/bgp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package main

import (
"fmt"
"time"

"github.com/bio-routing/bio-rd/cmd/bio-rd/config"
bgpserver "github.com/bio-routing/bio-rd/protocols/bgp/server"
"github.com/bio-routing/bio-rd/routingtable"
"github.com/bio-routing/bio-rd/routingtable/vrf"
)

const (
DefaultReconnectInterval = time.Second * 15
)

type bgpConfigurator struct {
srv bgpserver.BGPServer
vrfReg *vrf.VRFRegistry
}

func (c *bgpConfigurator) configure(cfg *config.BGP) error {
for _, bg := range cfg.Groups {
for _, bn := range bg.Neighbors {
err := c.configureSession(bn, bg)
if err != nil {
return fmt.Errorf("could not configure session for neighbor %s: %w", bn.PeerAddress, err)
}
}
}

c.deconfigureRemovedSessions(cfg)

return nil
}

func (c *bgpConfigurator) configureSession(bn *config.BGPNeighbor, bg *config.BGPGroup) error {
v, err := c.determineVRF(bn, bg)
if err != nil {
return fmt.Errorf("could not determine VRF: %w", err)
}

newCfg := c.newPeerConfig(bn, bg, v)
oldCfg := c.srv.GetPeerConfig(v, bn.PeerAddressIP)
if oldCfg != nil {
return c.reconfigureModifiedSession(bn, bg, newCfg, oldCfg)
}

err = c.srv.AddPeer(*newCfg)
if err != nil {
return fmt.Errorf("unable to add BGP peer: %w", err)
}

return nil
}

func (c *bgpConfigurator) deconfigureRemovedSessions(cfg *config.BGP) {
for _, p := range c.srv.GetPeers() {
if !c.peerExistsInConfig(cfg, p) {
c.srv.DisposePeer(p.VRF(), p.Addr())
}
}
}

func (c *bgpConfigurator) peerExistsInConfig(cfg *config.BGP, p bgpserver.PeerKey) bool {
for _, bg := range cfg.Groups {
for _, bn := range bg.Neighbors {
v, _ := c.determineVRF(bn, bg)
if bn.PeerAddressIP == p.Addr() && p.VRF() == v {
return true
}
}
}

return false
}

func (c *bgpConfigurator) reconfigureModifiedSession(bn *config.BGPNeighbor, bg *config.BGPGroup, newCfg, oldCfg *bgpserver.PeerConfig) error {
if oldCfg.NeedsRestart(newCfg) {
return c.replaceSession(newCfg, oldCfg)
}

err := c.srv.ReplaceImportFilterChain(
newCfg.VRF,
bn.PeerAddressIP,
bn.ImportFilterChain)
if err != nil {
return fmt.Errorf("could not replace import filter: %w", err)
}

err = c.srv.ReplaceExportFilterChain(
newCfg.VRF,
bn.PeerAddressIP,
bn.ExportFilterChain)
if err != nil {
return fmt.Errorf("could not replace export filter: %w", err)
}

return nil
}

func (c *bgpConfigurator) replaceSession(newCfg, oldCfg *bgpserver.PeerConfig) error {
c.srv.DisposePeer(oldCfg.VRF, oldCfg.PeerAddress)
err := c.srv.AddPeer(*newCfg)
if err != nil {
return fmt.Errorf("unable to reconfigure BGP peer: %w", err)
}

return nil
}

func (c *bgpConfigurator) determineVRF(bn *config.BGPNeighbor, bg *config.BGPGroup) (*vrf.VRF, error) {
if len(bn.RoutingInstance) > 0 {
return c.vrfByName(bn.RoutingInstance)
}

if len(bg.RoutingInstance) > 0 {
return c.vrfByName(bg.RoutingInstance)
}

return bgpSrv.GetDefaultVRF(), nil
}

func (c *bgpConfigurator) vrfByName(name string) (*vrf.VRF, error) {
v := c.vrfReg.GetVRFByName(name)
if v == nil {
return nil, fmt.Errorf("could not find VRF for name %s", name)
}

return v, nil
}

func (c *bgpConfigurator) newPeerConfig(bn *config.BGPNeighbor, bg *config.BGPGroup, vrf *vrf.VRF) *bgpserver.PeerConfig {
p := &bgpserver.PeerConfig{
AdminEnabled: !bn.Disabled,
AuthenticationKey: bn.AuthenticationKey,
LocalAS: bn.LocalAS,
PeerAS: bn.PeerAS,
PeerAddress: bn.PeerAddressIP,
LocalAddress: bn.LocalAddressIP,
TTL: bn.TTL,
ReconnectInterval: DefaultReconnectInterval,
HoldTime: bn.HoldTimeDuration,
KeepAlive: bn.HoldTimeDuration / 3,
RouterID: c.srv.RouterID(),
VRF: vrf,
AdvertiseIPv4MultiProtocol: bn.AdvertiseIPv4MultiProtocol,
}

c.configureIPv4(bn, bg, p)
c.configureIPv6(bn, bg, p)

if bn.Passive != nil {
p.Passive = *bn.Passive
}

if bn.RouteServerClient != nil {
p.RouteServerClient = *bn.RouteServerClient
}

if bn.RouteReflectorClient != nil {
p.RouteReflectorClient = *bn.RouteReflectorClient
}

if bn.ClusterIDIP != nil {
p.RouteReflectorClusterID = bn.ClusterIDIP.ToUint32()
}

return p
}

func (c *bgpConfigurator) configureIPv4(bn *config.BGPNeighbor, bg *config.BGPGroup, p *bgpserver.PeerConfig) {
if !bn.PeerAddressIP.IsIPv4() && bn.IPv4 == nil {
return
}

p.IPv4 = c.newAFIConfig(bn, bg)

if bn.IPv4 != nil {
c.configureAddressFamily(bn.IPv4, p.IPv4)
}
}

func (c *bgpConfigurator) configureIPv6(bn *config.BGPNeighbor, bg *config.BGPGroup, p *bgpserver.PeerConfig) {
if bn.PeerAddressIP.IsIPv4() && bn.IPv6 == nil {
return
}

p.IPv6 = c.newAFIConfig(bn, bg)

if bn.IPv6 != nil {
c.configureAddressFamily(bn.IPv6, p.IPv6)
}
}

func (c *bgpConfigurator) newAFIConfig(bn *config.BGPNeighbor, bg *config.BGPGroup) *bgpserver.AddressFamilyConfig {
return &bgpserver.AddressFamilyConfig{
ImportFilterChain: bn.ImportFilterChain,
ExportFilterChain: bn.ExportFilterChain,
AddPathSend: routingtable.ClientOptions{
BestOnly: true,
},
AddPathRecv: false,
}
}

func (c *bgpConfigurator) configureAddressFamily(baf *config.AddressFamilyConfig, af *bgpserver.AddressFamilyConfig) {
if baf.AddPath != nil {
c.configureAddPath(baf.AddPath, af)
}
}

func (c *bgpConfigurator) configureAddPath(bac *config.AddPathConfig, af *bgpserver.AddressFamilyConfig) {
af.AddPathRecv = bac.Receive

if bac.Send == nil {
return
}

af.AddPathSend.BestOnly = !bac.Send.Multipath
af.AddPathSend.MaxPaths = uint(bac.Send.PathCount)
}
Loading

0 comments on commit c567987

Please sign in to comment.