From b109345092c4976e093805eb7879b5dfe61bae99 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Wed, 29 Nov 2023 12:21:24 +0100 Subject: [PATCH 01/22] add dockerfile --- docker/bio-rd/Dockerfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docker/bio-rd/Dockerfile diff --git a/docker/bio-rd/Dockerfile b/docker/bio-rd/Dockerfile new file mode 100644 index 00000000..b08a6070 --- /dev/null +++ b/docker/bio-rd/Dockerfile @@ -0,0 +1,14 @@ +FROM golang as builder +ADD . /go/bio +WORKDIR /go/bio/cmd/bio-rd +RUN GOOS=linux go build -o /go/bin/bio-rd + +FROM debian:stable +WORKDIR /app +ENV CMD_ARGS="--config.file=/config/bio-rd.yml" +COPY --from=builder /go/bin/bio-rd . +CMD /app/bio-rd ${CMD_ARGS} +VOLUME /config +EXPOSE 179 +EXPOSE 5566 +EXPOSE 55667 From 5d8f84d9a8e748b9bf71082859d735a7f66b509f Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Mon, 4 Dec 2023 11:29:00 +0100 Subject: [PATCH 02/22] add dockerfile --- docker/bio-rd/Dockerfile | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docker/bio-rd/Dockerfile b/docker/bio-rd/Dockerfile index b08a6070..3652d864 100644 --- a/docker/bio-rd/Dockerfile +++ b/docker/bio-rd/Dockerfile @@ -1,13 +1,17 @@ FROM golang as builder -ADD . /go/bio -WORKDIR /go/bio/cmd/bio-rd +ADD . /go/bio-rd +WORKDIR /go/bio-rd/cmd/bio-rd RUN GOOS=linux go build -o /go/bin/bio-rd FROM debian:stable WORKDIR /app -ENV CMD_ARGS="--config.file=/config/bio-rd.yml" COPY --from=builder /go/bin/bio-rd . -CMD /app/bio-rd ${CMD_ARGS} +CMD /app/bio-rd --config.file=/config/bio-rd.yml ${CMD_ARGS} +#RUN apt update && \ +# apt install -y libcap2-bin && \ +# setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip /app/bio-rd && \ +# useradd --system bio-rd +#USER bio-rd VOLUME /config EXPOSE 179 EXPOSE 5566 From 0a2329d1aa3485c115b426e658161da316b8fecd Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Mon, 4 Dec 2023 11:31:22 +0100 Subject: [PATCH 03/22] prepare cleanup of daemon config --- cmd/bio-rd/bgp.go | 103 +++++++++++++++++++++++++++++++++++++++ cmd/bio-rd/config/bgp.go | 2 +- cmd/bio-rd/main.go | 101 ++++---------------------------------- 3 files changed, 113 insertions(+), 93 deletions(-) create mode 100644 cmd/bio-rd/bgp.go diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go new file mode 100644 index 00000000..ec083405 --- /dev/null +++ b/cmd/bio-rd/bgp.go @@ -0,0 +1,103 @@ +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" +) + +func configureProtocolsBGP(bgp *config.BGP) error { + // Tear down peers that are to be removed + for _, p := range bgpSrv.GetPeers() { + found := false + for _, g := range bgp.Groups { + for _, n := range g.Neighbors { + if n.PeerAddressIP == p.Addr() && p.VRF() == bgpSrv.GetDefaultVRF() { + found = true + break + } + } + } + + if !found { + bgpSrv.DisposePeer(bgpSrv.GetDefaultVRF(), p.Addr()) + } + } + + // Tear down peers that need new sessions as they changed too significantly + for _, g := range bgp.Groups { + for _, n := range g.Neighbors { + newCfg := toBGPPeerConfig(n, bgpSrv.GetDefaultVRF()) + oldCfg := bgpSrv.GetPeerConfig(bgpSrv.GetDefaultVRF(), n.PeerAddressIP) + if oldCfg == nil { + continue + } + + if !oldCfg.NeedsRestart(newCfg) { + bgpSrv.ReplaceImportFilterChain(bgpSrv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ImportFilterChain) + bgpSrv.ReplaceExportFilterChain(bgpSrv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ExportFilterChain) + continue + } + + bgpSrv.DisposePeer(bgpSrv.GetDefaultVRF(), oldCfg.PeerAddress) + } + } + + // Turn up all sessions that are missing + for _, g := range bgp.Groups { + for _, n := range g.Neighbors { + if bgpSrv.GetPeerConfig(bgpSrv.GetDefaultVRF(), n.PeerAddressIP) != nil { + continue + } + + newCfg := toBGPPeerConfig(n, vrfReg.GetVRFByName(vrf.DefaultVRFName)) + err := bgpSrv.AddPeer(*newCfg) + if err != nil { + return fmt.Errorf("unable to add BGP peer: %w", err) + } + } + } + + return nil +} + +// BGPPeerConfig converts a BGPNeighbor config into a PeerConfig +func toBGPPeerConfig(n *config.BGPNeighbor, vrf *vrf.VRF) *bgpserver.PeerConfig { + r := &bgpserver.PeerConfig{ + AdminEnabled: !n.Disabled, + AuthenticationKey: n.AuthenticationKey, + LocalAS: n.LocalAS, + PeerAS: n.PeerAS, + PeerAddress: n.PeerAddressIP, + LocalAddress: n.LocalAddressIP, + TTL: n.TTL, + ReconnectInterval: time.Second * 15, + HoldTime: n.HoldTimeDuration, + KeepAlive: n.HoldTimeDuration / 3, + RouterID: bgpSrv.RouterID(), + IPv4: &bgpserver.AddressFamilyConfig{ + ImportFilterChain: n.ImportFilterChain, + ExportFilterChain: n.ExportFilterChain, + AddPathSend: routingtable.ClientOptions{ + MaxPaths: 10, + }, + }, + VRF: vrf, + } + + // TODO: configureAFIsForBGPPeer(n, r) + + if n.Passive != nil { + r.Passive = *n.Passive + } + + if n.RouteServerClient != nil { + r.RouteServerClient = *n.RouteServerClient + } + + return r +} diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 6fd5ca73..bf9de14d 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -119,6 +119,7 @@ type BGPNeighbor struct { PeerAddressIP *bnet.IP LocalAddress string `yaml:"local_address"` LocalAddressIP *bnet.IP + Disabled bool `yaml:"disabled"` TTL uint8 `yaml:"ttl"` AuthenticationKey string `yaml:"authentication_key"` PeerAS uint32 `yaml:"peer_as"` @@ -134,7 +135,6 @@ type BGPNeighbor struct { Passive *bool `yaml:"passive"` ClusterID string `yaml:"cluster_id"` ClusterIDIP *bnet.IP - AFIs []*AFI `yaml:"afi"` } func (bn *BGPNeighbor) load(po *PolicyOptions) error { diff --git a/cmd/bio-rd/main.go b/cmd/bio-rd/main.go index 3f209b02..829b75df 100644 --- a/cmd/bio-rd/main.go +++ b/cmd/bio-rd/main.go @@ -14,7 +14,6 @@ import ( "github.com/bio-routing/bio-rd/protocols/device" isisapi "github.com/bio-routing/bio-rd/protocols/isis/api" isisserver "github.com/bio-routing/bio-rd/protocols/isis/server" - "github.com/bio-routing/bio-rd/routingtable" "github.com/bio-routing/bio-rd/routingtable/vrf" "github.com/bio-routing/bio-rd/util/log" "github.com/bio-routing/bio-rd/util/servicewrapper" @@ -23,11 +22,18 @@ import ( "google.golang.org/grpc/keepalive" ) +const ( + DefaultBGPListenAddrIPv4 = "0.0.0.0:179" + DefaultBGPListenAddrIPv6 = "[::]:179" +) + var ( configFilePath = flag.String("config.file", "bio-rd.yml", "bio-rd config file") grpcPort = flag.Uint("grpc_port", 5566, "GRPC API server port") grpcKeepaliveMinTime = flag.Uint("grpc_keepalive_min_time", 1, "Minimum time (seconds) for a client to wait between GRPC keepalive pings") metricsPort = flag.Uint("metrics_port", 55667, "Metrics HTTP server port") + bgpListenAddrIPv4 = flag.String("bgp.listen-addr-ipv4", DefaultBGPListenAddrIPv4, "BGP listen address for IPv4 AFI") + bgpListenAddrIPv6 = flag.String("bgp.listen-addr-ipv6", DefaultBGPListenAddrIPv6, "BGP listen address for IPv6 AFI") sigHUP = make(chan os.Signal) vrfReg = vrf.NewVRFRegistry() bgpSrv bgpserver.BGPServer @@ -63,8 +69,8 @@ func main() { listenAddrsByVRF := map[string][]string{ vrf.DefaultVRFName: { - "[::]:179", - "0.0.0.0:179", + fmt.Sprintf(*bgpListenAddrIPv6), + fmt.Sprintf(*bgpListenAddrIPv4), }, } @@ -159,95 +165,6 @@ func loadConfig(cfg *config.Config) error { return nil } -func configureProtocolsBGP(bgp *config.BGP) error { - // Tear down peers that are to be removed - for _, p := range bgpSrv.GetPeers() { - found := false - for _, g := range bgp.Groups { - for _, n := range g.Neighbors { - if n.PeerAddressIP == p.Addr() && p.VRF() == bgpSrv.GetDefaultVRF() { - found = true - break - } - } - } - - if !found { - bgpSrv.DisposePeer(bgpSrv.GetDefaultVRF(), p.Addr()) - } - } - - // Tear down peers that need new sessions as they changed too significantly - for _, g := range bgp.Groups { - for _, n := range g.Neighbors { - newCfg := BGPPeerConfig(n, bgpSrv.GetDefaultVRF()) - oldCfg := bgpSrv.GetPeerConfig(bgpSrv.GetDefaultVRF(), n.PeerAddressIP) - if oldCfg == nil { - continue - } - - if !oldCfg.NeedsRestart(newCfg) { - bgpSrv.ReplaceImportFilterChain(bgpSrv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ImportFilterChain) - bgpSrv.ReplaceExportFilterChain(bgpSrv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ExportFilterChain) - continue - } - - bgpSrv.DisposePeer(bgpSrv.GetDefaultVRF(), oldCfg.PeerAddress) - } - } - - // Turn up all sessions that are missing - for _, g := range bgp.Groups { - for _, n := range g.Neighbors { - if bgpSrv.GetPeerConfig(bgpSrv.GetDefaultVRF(), n.PeerAddressIP) != nil { - continue - } - - newCfg := BGPPeerConfig(n, vrfReg.GetVRFByName(vrf.DefaultVRFName)) - err := bgpSrv.AddPeer(*newCfg) - if err != nil { - return fmt.Errorf("unable to add BGP peer: %w", err) - } - } - } - - return nil -} - -// BGPPeerConfig converts a BGPNeighbor config into a PeerConfig -func BGPPeerConfig(n *config.BGPNeighbor, vrf *vrf.VRF) *bgpserver.PeerConfig { - r := &bgpserver.PeerConfig{ - AuthenticationKey: n.AuthenticationKey, - LocalAS: n.LocalAS, - PeerAS: n.PeerAS, - PeerAddress: n.PeerAddressIP, - LocalAddress: n.LocalAddressIP, - TTL: n.TTL, - ReconnectInterval: time.Second * 15, - HoldTime: n.HoldTimeDuration, - KeepAlive: n.HoldTimeDuration / 3, - RouterID: bgpSrv.RouterID(), - IPv4: &bgpserver.AddressFamilyConfig{ - ImportFilterChain: n.ImportFilterChain, - ExportFilterChain: n.ExportFilterChain, - AddPathSend: routingtable.ClientOptions{ - MaxPaths: 10, - }, - }, - VRF: vrf, - } - - if n.Passive != nil { - r.Passive = *n.Passive - } - - if n.RouteServerClient != nil { - r.RouteServerClient = *n.RouteServerClient - } - - return r -} - func configureRoutingInstance(ri *config.RoutingInstance) error { vrf := vrfReg.GetVRFByName(ri.Name) From bdcc3a00109d4432d68215a34a64ec0e1a4f86b0 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Tue, 5 Dec 2023 12:54:47 +0100 Subject: [PATCH 04/22] improve readability in bgp.go --- cmd/bio-rd/bgp.go | 66 +++++++++++++++++++++++++++++++--------------- cmd/bio-rd/main.go | 5 +++- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index ec083405..fca3594e 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -10,13 +10,32 @@ import ( "github.com/bio-routing/bio-rd/routingtable/vrf" ) -func configureProtocolsBGP(bgp *config.BGP) error { - // Tear down peers that are to be removed - for _, p := range bgpSrv.GetPeers() { +type bgpConfigurator struct { + srv bgpserver.BGPServer +} + +func (c *bgpConfigurator) configure(cfg *config.BGP) error { + c.deconfigureRemovedSessions(cfg) + + err := c.reconfigureModifiedSessions(cfg) + if err != nil { + return fmt.Errorf("could not reconfigure session: %w", err) + } + + err = c.configureNewSessions(cfg) + if err != nil { + return fmt.Errorf("could not configure session: %w", err) + } + + return nil +} + +func (c *bgpConfigurator) deconfigureRemovedSessions(cfg *config.BGP) { + for _, p := range c.srv.GetPeers() { found := false - for _, g := range bgp.Groups { + for _, g := range cfg.Groups { for _, n := range g.Neighbors { - if n.PeerAddressIP == p.Addr() && p.VRF() == bgpSrv.GetDefaultVRF() { + if n.PeerAddressIP == p.Addr() && p.VRF() == c.srv.GetDefaultVRF() { found = true break } @@ -24,38 +43,44 @@ func configureProtocolsBGP(bgp *config.BGP) error { } if !found { - bgpSrv.DisposePeer(bgpSrv.GetDefaultVRF(), p.Addr()) + c.srv.DisposePeer(c.srv.GetDefaultVRF(), p.Addr()) } } +} - // Tear down peers that need new sessions as they changed too significantly - for _, g := range bgp.Groups { +func (c *bgpConfigurator) reconfigureModifiedSessions(cfg *config.BGP) error { + for _, g := range cfg.Groups { for _, n := range g.Neighbors { - newCfg := toBGPPeerConfig(n, bgpSrv.GetDefaultVRF()) - oldCfg := bgpSrv.GetPeerConfig(bgpSrv.GetDefaultVRF(), n.PeerAddressIP) + newCfg := c.toPeerConfig(n, c.srv.GetDefaultVRF()) + oldCfg := c.srv.GetPeerConfig(c.srv.GetDefaultVRF(), n.PeerAddressIP) if oldCfg == nil { continue } if !oldCfg.NeedsRestart(newCfg) { - bgpSrv.ReplaceImportFilterChain(bgpSrv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ImportFilterChain) - bgpSrv.ReplaceExportFilterChain(bgpSrv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ExportFilterChain) + c.srv.ReplaceImportFilterChain(c.srv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ImportFilterChain) + c.srv.ReplaceExportFilterChain(c.srv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ExportFilterChain) continue } - bgpSrv.DisposePeer(bgpSrv.GetDefaultVRF(), oldCfg.PeerAddress) + c.srv.DisposePeer(c.srv.GetDefaultVRF(), oldCfg.PeerAddress) + return c.srv.AddPeer(*newCfg) } + } - // Turn up all sessions that are missing - for _, g := range bgp.Groups { + return nil +} + +func (c *bgpConfigurator) configureNewSessions(cfg *config.BGP) error { + for _, g := range cfg.Groups { for _, n := range g.Neighbors { - if bgpSrv.GetPeerConfig(bgpSrv.GetDefaultVRF(), n.PeerAddressIP) != nil { + if c.srv.GetPeerConfig(c.srv.GetDefaultVRF(), n.PeerAddressIP) != nil { continue } - newCfg := toBGPPeerConfig(n, vrfReg.GetVRFByName(vrf.DefaultVRFName)) - err := bgpSrv.AddPeer(*newCfg) + newCfg := c.toPeerConfig(n, vrfReg.GetVRFByName(vrf.DefaultVRFName)) + err := c.srv.AddPeer(*newCfg) if err != nil { return fmt.Errorf("unable to add BGP peer: %w", err) } @@ -65,8 +90,7 @@ func configureProtocolsBGP(bgp *config.BGP) error { return nil } -// BGPPeerConfig converts a BGPNeighbor config into a PeerConfig -func toBGPPeerConfig(n *config.BGPNeighbor, vrf *vrf.VRF) *bgpserver.PeerConfig { +func (c *bgpConfigurator) toPeerConfig(n *config.BGPNeighbor, vrf *vrf.VRF) *bgpserver.PeerConfig { r := &bgpserver.PeerConfig{ AdminEnabled: !n.Disabled, AuthenticationKey: n.AuthenticationKey, @@ -78,7 +102,7 @@ func toBGPPeerConfig(n *config.BGPNeighbor, vrf *vrf.VRF) *bgpserver.PeerConfig ReconnectInterval: time.Second * 15, HoldTime: n.HoldTimeDuration, KeepAlive: n.HoldTimeDuration / 3, - RouterID: bgpSrv.RouterID(), + RouterID: c.srv.RouterID(), IPv4: &bgpserver.AddressFamilyConfig{ ImportFilterChain: n.ImportFilterChain, ExportFilterChain: n.ExportFilterChain, diff --git a/cmd/bio-rd/main.go b/cmd/bio-rd/main.go index 829b75df..912f897e 100644 --- a/cmd/bio-rd/main.go +++ b/cmd/bio-rd/main.go @@ -148,7 +148,10 @@ func loadConfig(cfg *config.Config) error { if cfg.Protocols != nil { if cfg.Protocols.BGP != nil { - err := configureProtocolsBGP(cfg.Protocols.BGP) + bgpCfgtr := &bgpConfigurator{ + srv: bgpSrv, + } + err := bgpCfgtr.configure(cfg.Protocols.BGP) if err != nil { return fmt.Errorf("unable to configure BGP: %w", err) } From 1330851f2c1854d663e46b23c7a7cf8888f2304d Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Tue, 5 Dec 2023 15:05:10 +0100 Subject: [PATCH 05/22] add AFI configuration following the internal structure --- cmd/bio-rd/bgp.go | 124 ++++++++++++++++++++++++++++----------- cmd/bio-rd/config/bgp.go | 67 +++++++++++++-------- 2 files changed, 130 insertions(+), 61 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index fca3594e..7f3346c4 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -7,6 +7,7 @@ import ( "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/filter" "github.com/bio-routing/bio-rd/routingtable/vrf" ) @@ -49,37 +50,36 @@ func (c *bgpConfigurator) deconfigureRemovedSessions(cfg *config.BGP) { } func (c *bgpConfigurator) reconfigureModifiedSessions(cfg *config.BGP) error { - for _, g := range cfg.Groups { - for _, n := range g.Neighbors { - newCfg := c.toPeerConfig(n, c.srv.GetDefaultVRF()) - oldCfg := c.srv.GetPeerConfig(c.srv.GetDefaultVRF(), n.PeerAddressIP) + for _, bg := range cfg.Groups { + for _, bn := range bg.Neighbors { + newCfg := c.newPeerConfig(bn, bg, c.srv.GetDefaultVRF()) + oldCfg := c.srv.GetPeerConfig(c.srv.GetDefaultVRF(), bn.PeerAddressIP) if oldCfg == nil { continue } if !oldCfg.NeedsRestart(newCfg) { - c.srv.ReplaceImportFilterChain(c.srv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ImportFilterChain) - c.srv.ReplaceExportFilterChain(c.srv.GetDefaultVRF(), n.PeerAddressIP, newCfg.IPv4.ExportFilterChain) + c.srv.ReplaceImportFilterChain(c.srv.GetDefaultVRF(), bn.PeerAddressIP, newCfg.IPv4.ImportFilterChain) + c.srv.ReplaceExportFilterChain(c.srv.GetDefaultVRF(), bn.PeerAddressIP, newCfg.IPv4.ExportFilterChain) continue } c.srv.DisposePeer(c.srv.GetDefaultVRF(), oldCfg.PeerAddress) return c.srv.AddPeer(*newCfg) } - } return nil } func (c *bgpConfigurator) configureNewSessions(cfg *config.BGP) error { - for _, g := range cfg.Groups { - for _, n := range g.Neighbors { - if c.srv.GetPeerConfig(c.srv.GetDefaultVRF(), n.PeerAddressIP) != nil { + for _, bg := range cfg.Groups { + for _, bn := range bg.Neighbors { + if c.srv.GetPeerConfig(c.srv.GetDefaultVRF(), bn.PeerAddressIP) != nil { continue } - newCfg := c.toPeerConfig(n, vrfReg.GetVRFByName(vrf.DefaultVRFName)) + newCfg := c.newPeerConfig(bn, bg, vrfReg.GetVRFByName(vrf.DefaultVRFName)) err := c.srv.AddPeer(*newCfg) if err != nil { return fmt.Errorf("unable to add BGP peer: %w", err) @@ -90,38 +90,92 @@ func (c *bgpConfigurator) configureNewSessions(cfg *config.BGP) error { return nil } -func (c *bgpConfigurator) toPeerConfig(n *config.BGPNeighbor, vrf *vrf.VRF) *bgpserver.PeerConfig { - r := &bgpserver.PeerConfig{ - AdminEnabled: !n.Disabled, - AuthenticationKey: n.AuthenticationKey, - LocalAS: n.LocalAS, - PeerAS: n.PeerAS, - PeerAddress: n.PeerAddressIP, - LocalAddress: n.LocalAddressIP, - TTL: n.TTL, +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: time.Second * 15, - HoldTime: n.HoldTimeDuration, - KeepAlive: n.HoldTimeDuration / 3, + HoldTime: bn.HoldTimeDuration, + KeepAlive: bn.HoldTimeDuration / 3, RouterID: c.srv.RouterID(), - IPv4: &bgpserver.AddressFamilyConfig{ - ImportFilterChain: n.ImportFilterChain, - ExportFilterChain: n.ExportFilterChain, - AddPathSend: routingtable.ClientOptions{ - MaxPaths: 10, - }, + VRF: vrf, + } + + 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 + } + + 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: c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain), + ExportFilterChain: c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain), + AddPathSend: routingtable.ClientOptions{ + BestOnly: true, }, - VRF: vrf, + AddPathRecv: false, } +} - // TODO: configureAFIsForBGPPeer(n, r) +func (c *bgpConfigurator) determineFilterChain(groupChain filter.Chain, neighChain filter.Chain) filter.Chain { + if len(neighChain) > 0 { + return neighChain + } - if n.Passive != nil { - r.Passive = *n.Passive + return groupChain +} + +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 n.RouteServerClient != nil { - r.RouteServerClient = *n.RouteServerClient + if bac.Send == nil { + return } - return r + af.AddPathSend.BestOnly = !bac.Send.Multipath + af.AddPathSend.MaxPaths = uint(bac.Send.PathCount) } diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index bf9de14d..506dafc0 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -27,18 +27,19 @@ type BGPGroup struct { Name string `yaml:"name"` LocalAddress string `yaml:"local_address"` LocalAddressIP *bnet.IP - TTL uint8 `yaml:"ttl"` - AuthenticationKey string `yaml:"authentication_key"` - PeerAS uint32 `yaml:"peer_as"` - LocalAS uint32 `yaml:"local_as"` - HoldTime uint16 `yaml:"hold_time"` - Multipath *Multipath `yaml:"multipath"` - Import []string `yaml:"import"` - Export []string `yaml:"export"` + TTL uint8 `yaml:"ttl"` + AuthenticationKey string `yaml:"authentication_key"` + PeerAS uint32 `yaml:"peer_as"` + LocalAS uint32 `yaml:"local_as"` + HoldTime uint16 `yaml:"hold_time"` + Multipath *Multipath `yaml:"multipath"` + Import []string `yaml:"import"` + ImportFilterChain filter.Chain + Export []string `yaml:"export"` + ExportFilterChain filter.Chain RouteServerClient bool `yaml:"route_server_client"` Passive bool `yaml:"passive"` Neighbors []*BGPNeighbor `yaml:"neighbors"` - AFIs []*AFI `yaml:"afi"` } func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { @@ -59,6 +60,24 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { bg.HoldTime = 90 } + for i := range bg.Import { + f := policyOptions.getPolicyStatementFilter(bg.Import[i]) + if f == nil { + return fmt.Errorf("policy statement %q undefined", bg.Import[i]) + } + + bg.ImportFilterChain = append(bg.ImportFilterChain, f) + } + + for i := range bg.Export { + f := policyOptions.getPolicyStatementFilter(bg.Export[i]) + if f == nil { + return fmt.Errorf("policy statement %q undefined", bg.Export[i]) + } + + bg.ExportFilterChain = append(bg.ExportFilterChain, f) + } + for _, n := range bg.Neighbors { if n.RouteServerClient == nil { n.RouteServerClient = &bg.RouteServerClient @@ -135,15 +154,17 @@ type BGPNeighbor struct { Passive *bool `yaml:"passive"` ClusterID string `yaml:"cluster_id"` ClusterIDIP *bnet.IP + IPv4 *AddressFamilyConfig + IPv6 *AddressFamilyConfig } -func (bn *BGPNeighbor) load(po *PolicyOptions) error { +func (bn *BGPNeighbor) load(policyOptions *PolicyOptions) error { if bn.PeerAS == 0 { - return fmt.Errorf("Peer %q is lacking peer as number", bn.PeerAddress) + return fmt.Errorf("peer %q is lacking peer as number", bn.PeerAddress) } if bn.PeerAddress == "" { - return fmt.Errorf("Mandatory parameter BGP peer address is empty") + return fmt.Errorf("mandatory parameter BGP peer address is empty") } if bn.LocalAddress != "" { @@ -164,7 +185,7 @@ func (bn *BGPNeighbor) load(po *PolicyOptions) error { bn.HoldTimeDuration = time.Second * time.Duration(bn.HoldTime) for i := range bn.Import { - f := po.getPolicyStatementFilter(bn.Import[i]) + f := policyOptions.getPolicyStatementFilter(bn.Import[i]) if f == nil { return fmt.Errorf("policy statement %q undefined", bn.Import[i]) } @@ -173,7 +194,7 @@ func (bn *BGPNeighbor) load(po *PolicyOptions) error { } for i := range bn.Export { - f := po.getPolicyStatementFilter(bn.Export[i]) + f := policyOptions.getPolicyStatementFilter(bn.Export[i]) if f == nil { return fmt.Errorf("policy statement %q undefined", bn.Export[i]) } @@ -183,22 +204,16 @@ func (bn *BGPNeighbor) load(po *PolicyOptions) error { return nil } -type AFI struct { - Name string `yaml:"name"` - SAFI SAFI `yaml:"safi"` -} - -type SAFI struct { - Name string `yaml:"name"` - AddPath *AddPath `yaml:"add_path"` +type AddressFamilyConfig struct { + AddPath *AddPathConfig } -type AddPath struct { - Receive bool `yaml:"receive"` - Send *AddPathSend `yaml:"send"` +type AddPathConfig struct { + Receive bool `yaml:"receive"` + Send *AddPathSendConfig `yaml:"send"` } -type AddPathSend struct { +type AddPathSendConfig struct { Multipath bool `yaml:"multipath"` PathCount uint8 `yaml:"path_count"` } From 8031d549827a8846e22bfdc79f6d945ab50ebc9c Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Tue, 5 Dec 2023 15:28:53 +0100 Subject: [PATCH 06/22] do not assume legacy IP session --- cmd/bio-rd/bgp.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index 7f3346c4..343a2413 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -59,8 +59,12 @@ func (c *bgpConfigurator) reconfigureModifiedSessions(cfg *config.BGP) error { } if !oldCfg.NeedsRestart(newCfg) { - c.srv.ReplaceImportFilterChain(c.srv.GetDefaultVRF(), bn.PeerAddressIP, newCfg.IPv4.ImportFilterChain) - c.srv.ReplaceExportFilterChain(c.srv.GetDefaultVRF(), bn.PeerAddressIP, newCfg.IPv4.ExportFilterChain) + c.srv.ReplaceImportFilterChain(c.srv.GetDefaultVRF(), + bn.PeerAddressIP, + c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain)) + c.srv.ReplaceExportFilterChain(c.srv.GetDefaultVRF(), + bn.PeerAddressIP, + c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain)) continue } From 1cf35b80a0bed272110197da240d6c22c504ce47 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Tue, 5 Dec 2023 15:30:22 +0100 Subject: [PATCH 07/22] improve readability of filter replacements --- cmd/bio-rd/bgp.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index 343a2413..f672df7f 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -59,12 +59,16 @@ func (c *bgpConfigurator) reconfigureModifiedSessions(cfg *config.BGP) error { } if !oldCfg.NeedsRestart(newCfg) { - c.srv.ReplaceImportFilterChain(c.srv.GetDefaultVRF(), + c.srv.ReplaceImportFilterChain( + c.srv.GetDefaultVRF(), bn.PeerAddressIP, - c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain)) - c.srv.ReplaceExportFilterChain(c.srv.GetDefaultVRF(), + c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain), + ) + c.srv.ReplaceExportFilterChain( + c.srv.GetDefaultVRF(), bn.PeerAddressIP, - c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain)) + c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain), + ) continue } From 00111eaeb71d7066301814e7294678d1f108e79f Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Wed, 6 Dec 2023 09:51:42 +0100 Subject: [PATCH 08/22] extract magic figure DefaultReconnectInterval --- cmd/bio-rd/bgp.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index f672df7f..40e566be 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -11,6 +11,10 @@ import ( "github.com/bio-routing/bio-rd/routingtable/vrf" ) +const ( + DefaultReconnectInterval = time.Second * 15 +) + type bgpConfigurator struct { srv bgpserver.BGPServer } @@ -107,7 +111,7 @@ func (c *bgpConfigurator) newPeerConfig(bn *config.BGPNeighbor, bg *config.BGPGr PeerAddress: bn.PeerAddressIP, LocalAddress: bn.LocalAddressIP, TTL: bn.TTL, - ReconnectInterval: time.Second * 15, + ReconnectInterval: DefaultReconnectInterval, HoldTime: bn.HoldTimeDuration, KeepAlive: bn.HoldTimeDuration / 3, RouterID: c.srv.RouterID(), From c7e388ba414370af73214f1e31050bedfe331e75 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Wed, 6 Dec 2023 11:45:58 +0100 Subject: [PATCH 09/22] support AdvertiseIPv4MultiProtocol setting in config --- cmd/bio-rd/bgp.go | 25 ++++++------ cmd/bio-rd/config/bgp.go | 85 ++++++++++++++++++++-------------------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index 40e566be..b7e80a01 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -104,18 +104,19 @@ func (c *bgpConfigurator) configureNewSessions(cfg *config.BGP) error { 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, + 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) diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 506dafc0..167fba10 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -78,48 +78,48 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { bg.ExportFilterChain = append(bg.ExportFilterChain, f) } - for _, n := range bg.Neighbors { - if n.RouteServerClient == nil { - n.RouteServerClient = &bg.RouteServerClient + for _, bn := range bg.Neighbors { + if bn.RouteServerClient == nil { + bn.RouteServerClient = &bg.RouteServerClient } - if n.Passive == nil { - n.Passive = &bg.Passive + if bn.Passive == nil { + bn.Passive = &bg.Passive } - if n.LocalAddress == "" { - n.LocalAddressIP = bg.LocalAddressIP + if bn.LocalAddress == "" { + bn.LocalAddressIP = bg.LocalAddressIP } - if n.TTL == 0 { - n.TTL = bg.TTL + if bn.TTL == 0 { + bn.TTL = bg.TTL } - if n.AuthenticationKey == "" { - n.AuthenticationKey = bg.AuthenticationKey + if bn.AuthenticationKey == "" { + bn.AuthenticationKey = bg.AuthenticationKey } - if n.LocalAS == 0 { - n.LocalAS = localAS + if bn.LocalAS == 0 { + bn.LocalAS = localAS } - if n.LocalAS == 0 { + if bn.LocalAS == 0 { return fmt.Errorf("local_as 0 is invalid") } - if n.PeerAS == 0 { - n.PeerAS = bg.PeerAS + if bn.PeerAS == 0 { + bn.PeerAS = bg.PeerAS } - if n.PeerAS == 0 { + if bn.PeerAS == 0 { return fmt.Errorf("peer_as 0 is invalid") } - if n.HoldTime == 0 { - n.HoldTime = bg.HoldTime + if bn.HoldTime == 0 { + bn.HoldTime = bg.HoldTime } - err := n.load(policyOptions) + err := bn.load(policyOptions) if err != nil { return err } @@ -134,28 +134,29 @@ type Multipath struct { } type BGPNeighbor struct { - PeerAddress string `yaml:"peer_address"` - PeerAddressIP *bnet.IP - LocalAddress string `yaml:"local_address"` - LocalAddressIP *bnet.IP - Disabled bool `yaml:"disabled"` - TTL uint8 `yaml:"ttl"` - AuthenticationKey string `yaml:"authentication_key"` - PeerAS uint32 `yaml:"peer_as"` - LocalAS uint32 `yaml:"local_as"` - HoldTime uint16 `yaml:"hold_time"` - HoldTimeDuration time.Duration - Multipath *Multipath `yaml:"multipath"` - Import []string `yaml:"import"` - ImportFilterChain filter.Chain - Export []string `yaml:"export"` - ExportFilterChain filter.Chain - RouteServerClient *bool `yaml:"route_server_client"` - Passive *bool `yaml:"passive"` - ClusterID string `yaml:"cluster_id"` - ClusterIDIP *bnet.IP - IPv4 *AddressFamilyConfig - IPv6 *AddressFamilyConfig + PeerAddress string `yaml:"peer_address"` + PeerAddressIP *bnet.IP + LocalAddress string `yaml:"local_address"` + LocalAddressIP *bnet.IP + Disabled bool `yaml:"disabled"` + TTL uint8 `yaml:"ttl"` + AuthenticationKey string `yaml:"authentication_key"` + PeerAS uint32 `yaml:"peer_as"` + LocalAS uint32 `yaml:"local_as"` + HoldTime uint16 `yaml:"hold_time"` + HoldTimeDuration time.Duration + Multipath *Multipath `yaml:"multipath"` + Import []string `yaml:"import"` + ImportFilterChain filter.Chain + Export []string `yaml:"export"` + ExportFilterChain filter.Chain + RouteServerClient *bool `yaml:"route_server_client"` + Passive *bool `yaml:"passive"` + ClusterID string `yaml:"cluster_id"` + ClusterIDIP *bnet.IP + IPv4 *AddressFamilyConfig `yaml:"ipv4"` + IPv6 *AddressFamilyConfig `yaml:"ipv6"` + AdvertiseIPv4MultiProtocol bool `yaml:"advertise_ipv4_multiprotocol"` } func (bn *BGPNeighbor) load(policyOptions *PolicyOptions) error { From 8b1ccf3e572023fe8b5161855427075be451afde Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Thu, 7 Dec 2023 10:41:20 +0100 Subject: [PATCH 10/22] simplify BGP session configuration code, respect configured VRF --- cmd/bio-rd/bgp.go | 121 ++++++++++++++++++++++++--------------- cmd/bio-rd/config/bgp.go | 2 + cmd/bio-rd/main.go | 3 +- 3 files changed, 79 insertions(+), 47 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index b7e80a01..86f5a164 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -16,20 +16,40 @@ const ( ) type bgpConfigurator struct { - srv bgpserver.BGPServer + srv bgpserver.BGPServer + vrfReg *vrf.VRFRegistry } func (c *bgpConfigurator) configure(cfg *config.BGP) error { c.deconfigureRemovedSessions(cfg) - err := c.reconfigureModifiedSessions(cfg) + 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: %w", err) + } + } + } + + 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 reconfigure session: %w", err) + return fmt.Errorf("could not determine VRF for neighbor %s: %w", bn.PeerAddress, err) } - err = c.configureNewSessions(cfg) + 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("could not configure session: %w", err) + return fmt.Errorf("unable to add BGP peer %s: %w", bn.PeerAddress, err) } return nil @@ -38,9 +58,10 @@ func (c *bgpConfigurator) configure(cfg *config.BGP) error { func (c *bgpConfigurator) deconfigureRemovedSessions(cfg *config.BGP) { for _, p := range c.srv.GetPeers() { found := false - for _, g := range cfg.Groups { - for _, n := range g.Neighbors { - if n.PeerAddressIP == p.Addr() && p.VRF() == c.srv.GetDefaultVRF() { + for _, bg := range cfg.Groups { + for _, bn := range bg.Neighbors { + v, _ := c.determineVRF(bn, bg) + if bn.PeerAddressIP == p.Addr() && p.VRF() == v { found = true break } @@ -48,60 +69,68 @@ func (c *bgpConfigurator) deconfigureRemovedSessions(cfg *config.BGP) { } if !found { - c.srv.DisposePeer(c.srv.GetDefaultVRF(), p.Addr()) + c.srv.DisposePeer(p.VRF(), p.Addr()) } } } -func (c *bgpConfigurator) reconfigureModifiedSessions(cfg *config.BGP) error { - for _, bg := range cfg.Groups { - for _, bn := range bg.Neighbors { - newCfg := c.newPeerConfig(bn, bg, c.srv.GetDefaultVRF()) - oldCfg := c.srv.GetPeerConfig(c.srv.GetDefaultVRF(), bn.PeerAddressIP) - if oldCfg == nil { - continue - } +func (c *bgpConfigurator) reconfigureModifiedSession(bn *config.BGPNeighbor, bg *config.BGPGroup, newCfg, oldCfg *bgpserver.PeerConfig) error { + if oldCfg.NeedsRestart(newCfg) { + return c.replaceSession(newCfg, oldCfg) + } - if !oldCfg.NeedsRestart(newCfg) { - c.srv.ReplaceImportFilterChain( - c.srv.GetDefaultVRF(), - bn.PeerAddressIP, - c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain), - ) - c.srv.ReplaceExportFilterChain( - c.srv.GetDefaultVRF(), - bn.PeerAddressIP, - c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain), - ) - continue - } + err := c.srv.ReplaceImportFilterChain( + newCfg.VRF, + bn.PeerAddressIP, + c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain), + ) + if err != nil { + return fmt.Errorf("could not replace import filter for neighbor %s: %w", bn.PeerAddress, err) + } - c.srv.DisposePeer(c.srv.GetDefaultVRF(), oldCfg.PeerAddress) - return c.srv.AddPeer(*newCfg) - } + err = c.srv.ReplaceExportFilterChain( + newCfg.VRF, + bn.PeerAddressIP, + c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain), + ) + if err != nil { + return fmt.Errorf("could not replace export filter for neighbor %s: %w", bn.PeerAddress, err) } return nil } -func (c *bgpConfigurator) configureNewSessions(cfg *config.BGP) error { - for _, bg := range cfg.Groups { - for _, bn := range bg.Neighbors { - if c.srv.GetPeerConfig(c.srv.GetDefaultVRF(), bn.PeerAddressIP) != nil { - continue - } - - newCfg := c.newPeerConfig(bn, bg, vrfReg.GetVRFByName(vrf.DefaultVRFName)) - err := c.srv.AddPeer(*newCfg) - if err != nil { - return fmt.Errorf("unable to add BGP peer: %w", err) - } - } +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 %q: %w", newCfg.PeerAddress, 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 routing instance %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, diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 167fba10..44f469f3 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -40,6 +40,7 @@ type BGPGroup struct { RouteServerClient bool `yaml:"route_server_client"` Passive bool `yaml:"passive"` Neighbors []*BGPNeighbor `yaml:"neighbors"` + RoutingInstance string `yaml:"routing_instance"` } func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { @@ -157,6 +158,7 @@ type BGPNeighbor struct { IPv4 *AddressFamilyConfig `yaml:"ipv4"` IPv6 *AddressFamilyConfig `yaml:"ipv6"` AdvertiseIPv4MultiProtocol bool `yaml:"advertise_ipv4_multiprotocol"` + RoutingInstance string `yaml:"routing_instance"` } func (bn *BGPNeighbor) load(policyOptions *PolicyOptions) error { diff --git a/cmd/bio-rd/main.go b/cmd/bio-rd/main.go index 912f897e..79454007 100644 --- a/cmd/bio-rd/main.go +++ b/cmd/bio-rd/main.go @@ -149,7 +149,8 @@ func loadConfig(cfg *config.Config) error { if cfg.Protocols != nil { if cfg.Protocols.BGP != nil { bgpCfgtr := &bgpConfigurator{ - srv: bgpSrv, + srv: bgpSrv, + vrfReg: vrfReg, } err := bgpCfgtr.configure(cfg.Protocols.BGP) if err != nil { From 6b09b9acb2b833fb825b5ed9367efc48410aa621 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Thu, 7 Dec 2023 10:46:21 +0100 Subject: [PATCH 11/22] improve configuration error messages --- cmd/bio-rd/bgp.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index 86f5a164..d774cb17 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -27,7 +27,7 @@ func (c *bgpConfigurator) configure(cfg *config.BGP) error { for _, bn := range bg.Neighbors { err := c.configureSession(bn, bg) if err != nil { - return fmt.Errorf("could not configure session: %w", err) + return fmt.Errorf("could not configure session for neighbor %s: %w", bn.PeerAddress, err) } } } @@ -38,7 +38,7 @@ func (c *bgpConfigurator) configure(cfg *config.BGP) error { 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 for neighbor %s: %w", bn.PeerAddress, err) + return fmt.Errorf("could not determine VRF: %w", err) } newCfg := c.newPeerConfig(bn, bg, v) @@ -49,7 +49,7 @@ func (c *bgpConfigurator) configureSession(bn *config.BGPNeighbor, bg *config.BG err = c.srv.AddPeer(*newCfg) if err != nil { - return fmt.Errorf("unable to add BGP peer %s: %w", bn.PeerAddress, err) + return fmt.Errorf("unable to add BGP peer: %w", err) } return nil @@ -85,7 +85,7 @@ func (c *bgpConfigurator) reconfigureModifiedSession(bn *config.BGPNeighbor, bg c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain), ) if err != nil { - return fmt.Errorf("could not replace import filter for neighbor %s: %w", bn.PeerAddress, err) + return fmt.Errorf("could not replace import filter: %w", err) } err = c.srv.ReplaceExportFilterChain( @@ -94,7 +94,7 @@ func (c *bgpConfigurator) reconfigureModifiedSession(bn *config.BGPNeighbor, bg c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain), ) if err != nil { - return fmt.Errorf("could not replace export filter for neighbor %s: %w", bn.PeerAddress, err) + return fmt.Errorf("could not replace export filter: %w", err) } return nil @@ -104,7 +104,7 @@ func (c *bgpConfigurator) replaceSession(newCfg, oldCfg *bgpserver.PeerConfig) e c.srv.DisposePeer(oldCfg.VRF, oldCfg.PeerAddress) err := c.srv.AddPeer(*newCfg) if err != nil { - return fmt.Errorf("unable to reconfigure BGP peer %q: %w", newCfg.PeerAddress, err) + return fmt.Errorf("unable to reconfigure BGP peer: %w", err) } return nil @@ -125,7 +125,7 @@ func (c *bgpConfigurator) determineVRF(bn *config.BGPNeighbor, bg *config.BGPGro func (c *bgpConfigurator) vrfByName(name string) (*vrf.VRF, error) { v := c.vrfReg.GetVRFByName(name) if v == nil { - return nil, fmt.Errorf("could not find routing instance %s", name) + return nil, fmt.Errorf("could not find VRF for name %s", name) } return v, nil From fc7c2033fe0890c877ac20475ecf3ae89afb725b Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Thu, 7 Dec 2023 12:52:39 +0100 Subject: [PATCH 12/22] inherit multipath and routing instance --- cmd/bio-rd/config/bgp.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 44f469f3..3d5a5de5 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -8,6 +8,10 @@ import ( "github.com/bio-routing/bio-rd/routingtable/filter" ) +const ( + DefaultHoldTimeSeconds = 90 +) + type BGP struct { Groups []*BGPGroup `yaml:"groups"` } @@ -58,7 +62,7 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { } if bg.HoldTime == 0 { - bg.HoldTime = 90 + bg.HoldTime = DefaultHoldTimeSeconds } for i := range bg.Import { @@ -120,6 +124,14 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { bn.HoldTime = bg.HoldTime } + if bn.Multipath == nil { + bn.Multipath = bg.Multipath + } + + if len(bn.RoutingInstance) == 0 { + bn.RoutingInstance = bg.RoutingInstance + } + err := bn.load(policyOptions) if err != nil { return err From 310eaa331c2a15243ad96a86949c2d9a7190a9f4 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Thu, 7 Dec 2023 13:03:20 +0100 Subject: [PATCH 13/22] move responsibility to inherit filter chain to config package, add inheritance for API specific configs --- cmd/bio-rd/bgp.go | 19 ++++--------------- cmd/bio-rd/config/bgp.go | 31 +++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index d774cb17..a2cb38a0 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -7,7 +7,6 @@ import ( "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/filter" "github.com/bio-routing/bio-rd/routingtable/vrf" ) @@ -82,8 +81,7 @@ func (c *bgpConfigurator) reconfigureModifiedSession(bn *config.BGPNeighbor, bg err := c.srv.ReplaceImportFilterChain( newCfg.VRF, bn.PeerAddressIP, - c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain), - ) + bn.ImportFilterChain) if err != nil { return fmt.Errorf("could not replace import filter: %w", err) } @@ -91,8 +89,7 @@ func (c *bgpConfigurator) reconfigureModifiedSession(bn *config.BGPNeighbor, bg err = c.srv.ReplaceExportFilterChain( newCfg.VRF, bn.PeerAddressIP, - c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain), - ) + bn.ExportFilterChain) if err != nil { return fmt.Errorf("could not replace export filter: %w", err) } @@ -188,8 +185,8 @@ func (c *bgpConfigurator) configureIPv6(bn *config.BGPNeighbor, bg *config.BGPGr func (c *bgpConfigurator) newAFIConfig(bn *config.BGPNeighbor, bg *config.BGPGroup) *bgpserver.AddressFamilyConfig { return &bgpserver.AddressFamilyConfig{ - ImportFilterChain: c.determineFilterChain(bg.ImportFilterChain, bn.ImportFilterChain), - ExportFilterChain: c.determineFilterChain(bg.ExportFilterChain, bn.ExportFilterChain), + ImportFilterChain: bn.ImportFilterChain, + ExportFilterChain: bn.ExportFilterChain, AddPathSend: routingtable.ClientOptions{ BestOnly: true, }, @@ -197,14 +194,6 @@ func (c *bgpConfigurator) newAFIConfig(bn *config.BGPNeighbor, bg *config.BGPGro } } -func (c *bgpConfigurator) determineFilterChain(groupChain filter.Chain, neighChain filter.Chain) filter.Chain { - if len(neighChain) > 0 { - return neighChain - } - - return groupChain -} - func (c *bgpConfigurator) configureAddressFamily(baf *config.AddressFamilyConfig, af *bgpserver.AddressFamilyConfig) { if baf.AddPath != nil { c.configureAddPath(baf.AddPath, af) diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 3d5a5de5..6f31e7fa 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -41,10 +41,12 @@ type BGPGroup struct { ImportFilterChain filter.Chain Export []string `yaml:"export"` ExportFilterChain filter.Chain - RouteServerClient bool `yaml:"route_server_client"` - Passive bool `yaml:"passive"` - Neighbors []*BGPNeighbor `yaml:"neighbors"` - RoutingInstance string `yaml:"routing_instance"` + RouteServerClient *bool `yaml:"route_server_client"` + Passive *bool `yaml:"passive"` + Neighbors []*BGPNeighbor `yaml:"neighbors"` + IPv4 *AddressFamilyConfig `yaml:"ipv4"` + IPv6 *AddressFamilyConfig `yaml:"ipv6"` + RoutingInstance string `yaml:"routing_instance"` } func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { @@ -85,11 +87,11 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { for _, bn := range bg.Neighbors { if bn.RouteServerClient == nil { - bn.RouteServerClient = &bg.RouteServerClient + bn.RouteServerClient = bg.RouteServerClient } if bn.Passive == nil { - bn.Passive = &bg.Passive + bn.Passive = bg.Passive } if bn.LocalAddress == "" { @@ -132,6 +134,17 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { bn.RoutingInstance = bg.RoutingInstance } + if bn.IPv4 == nil { + bn.IPv4 = bg.IPv4 + } + + if bn.IPv6 == nil { + bn.IPv6 = bg.IPv6 + } + + bn.ImportFilterChain = bg.ImportFilterChain + bn.ExportFilterChain = bg.ExportFilterChain + err := bn.load(policyOptions) if err != nil { return err @@ -199,6 +212,9 @@ func (bn *BGPNeighbor) load(policyOptions *PolicyOptions) error { bn.PeerAddressIP = b.Dedup() bn.HoldTimeDuration = time.Second * time.Duration(bn.HoldTime) + if len(bn.Import) > 0 { + bn.ImportFilterChain = filter.Chain{} + } for i := range bn.Import { f := policyOptions.getPolicyStatementFilter(bn.Import[i]) if f == nil { @@ -208,6 +224,9 @@ func (bn *BGPNeighbor) load(policyOptions *PolicyOptions) error { bn.ImportFilterChain = append(bn.ImportFilterChain, f) } + if len(bn.Export) > 0 { + bn.ExportFilterChain = filter.Chain{} + } for i := range bn.Export { f := policyOptions.getPolicyStatementFilter(bn.Export[i]) if f == nil { From d50607aa1b2422baf445c9c973dbf3845ef13942 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Thu, 7 Dec 2023 13:47:16 +0100 Subject: [PATCH 14/22] run unprivileged --- cmd/bio-rd/config/bgp.go | 1 + docker/bio-rd/Dockerfile | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 6f31e7fa..cfee19ed 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -235,6 +235,7 @@ func (bn *BGPNeighbor) load(policyOptions *PolicyOptions) error { bn.ExportFilterChain = append(bn.ExportFilterChain, f) } + return nil } diff --git a/docker/bio-rd/Dockerfile b/docker/bio-rd/Dockerfile index 3652d864..921e2bd8 100644 --- a/docker/bio-rd/Dockerfile +++ b/docker/bio-rd/Dockerfile @@ -7,11 +7,11 @@ FROM debian:stable WORKDIR /app COPY --from=builder /go/bin/bio-rd . CMD /app/bio-rd --config.file=/config/bio-rd.yml ${CMD_ARGS} -#RUN apt update && \ -# apt install -y libcap2-bin && \ -# setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip /app/bio-rd && \ -# useradd --system bio-rd -#USER bio-rd +RUN apt update && \ + apt install -y libcap2-bin && \ + setcap cap_net_bind_service+eip /app/bio-rd && \ + useradd --system bio-rd +USER bio-rd VOLUME /config EXPOSE 179 EXPOSE 5566 From 291c87fb94d2f8a146e063a0c05840c8c84ae48a Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Thu, 7 Dec 2023 14:38:57 +0100 Subject: [PATCH 15/22] add missing configuration for route reflection --- cmd/bio-rd/bgp.go | 8 ++++++ cmd/bio-rd/config/bgp.go | 53 ++++++++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index a2cb38a0..76099f21 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -156,6 +156,14 @@ func (c *bgpConfigurator) newPeerConfig(bn *config.BGPNeighbor, bg *config.BGPGr p.RouteServerClient = *bn.RouteServerClient } + if bn.RouteReflectorClient != nil { + p.RouteReflectorClient = *bn.RouteReflectorClient + } + + if bn.ClusterIDIP != nil { + p.RouteReflectorClusterID = bn.ClusterIDIP.ToUint32() + } + return p } diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index cfee19ed..7399a217 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -28,25 +28,26 @@ func (b *BGP) load(localAS uint32, policyOptions *PolicyOptions) error { } type BGPGroup struct { - Name string `yaml:"name"` - LocalAddress string `yaml:"local_address"` - LocalAddressIP *bnet.IP - TTL uint8 `yaml:"ttl"` - AuthenticationKey string `yaml:"authentication_key"` - PeerAS uint32 `yaml:"peer_as"` - LocalAS uint32 `yaml:"local_as"` - HoldTime uint16 `yaml:"hold_time"` - Multipath *Multipath `yaml:"multipath"` - Import []string `yaml:"import"` - ImportFilterChain filter.Chain - Export []string `yaml:"export"` - ExportFilterChain filter.Chain - RouteServerClient *bool `yaml:"route_server_client"` - Passive *bool `yaml:"passive"` - Neighbors []*BGPNeighbor `yaml:"neighbors"` - IPv4 *AddressFamilyConfig `yaml:"ipv4"` - IPv6 *AddressFamilyConfig `yaml:"ipv6"` - RoutingInstance string `yaml:"routing_instance"` + Name string `yaml:"name"` + LocalAddress string `yaml:"local_address"` + LocalAddressIP *bnet.IP + TTL uint8 `yaml:"ttl"` + AuthenticationKey string `yaml:"authentication_key"` + PeerAS uint32 `yaml:"peer_as"` + LocalAS uint32 `yaml:"local_as"` + HoldTime uint16 `yaml:"hold_time"` + Multipath *Multipath `yaml:"multipath"` + Import []string `yaml:"import"` + ImportFilterChain filter.Chain + Export []string `yaml:"export"` + ExportFilterChain filter.Chain + RouteServerClient *bool `yaml:"route_server_client"` + RouteReflectorClient *bool `yaml:"route_reflector_client"` + Passive *bool `yaml:"passive"` + Neighbors []*BGPNeighbor `yaml:"neighbors"` + IPv4 *AddressFamilyConfig `yaml:"ipv4"` + IPv6 *AddressFamilyConfig `yaml:"ipv6"` + RoutingInstance string `yaml:"routing_instance"` } func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { @@ -142,6 +143,10 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { bn.IPv6 = bg.IPv6 } + if bn.RouteReflectorClient == nil { + bn.RouteReflectorClient = bg.RouteReflectorClient + } + bn.ImportFilterChain = bg.ImportFilterChain bn.ExportFilterChain = bg.ExportFilterChain @@ -177,6 +182,7 @@ type BGPNeighbor struct { Export []string `yaml:"export"` ExportFilterChain filter.Chain RouteServerClient *bool `yaml:"route_server_client"` + RouteReflectorClient *bool `yaml:"route_reflector_client"` Passive *bool `yaml:"passive"` ClusterID string `yaml:"cluster_id"` ClusterIDIP *bnet.IP @@ -204,6 +210,15 @@ func (bn *BGPNeighbor) load(policyOptions *PolicyOptions) error { bn.LocalAddressIP = a.Dedup() } + if bn.ClusterID != "" { + a, err := bnet.IPFromString(bn.ClusterID) + if err != nil { + return fmt.Errorf("unable to parse BGP cluster identifier: %w", err) + } + + bn.ClusterIDIP = a.Dedup() + } + b, err := bnet.IPFromString(bn.PeerAddress) if err != nil { return fmt.Errorf("unable to parse BGP peer address: %w", err) From 9e8742ca9c2b0357dc6b2abb390b512d60edc5f8 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Thu, 7 Dec 2023 18:35:51 +0100 Subject: [PATCH 16/22] add tests for loading group --- cmd/bio-rd/config/bgp.go | 2 +- cmd/bio-rd/config/bgp_test.go | 102 ++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 cmd/bio-rd/config/bgp_test.go diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 7399a217..5dd71b85 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -108,7 +108,7 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { } if bn.LocalAS == 0 { - bn.LocalAS = localAS + bn.LocalAS = bg.LocalAS } if bn.LocalAS == 0 { diff --git a/cmd/bio-rd/config/bgp_test.go b/cmd/bio-rd/config/bgp_test.go new file mode 100644 index 00000000..0fd73cf0 --- /dev/null +++ b/cmd/bio-rd/config/bgp_test.go @@ -0,0 +1,102 @@ +package config + +import ( + "testing" + "time" + + bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/routingtable/filter" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v2" +) + +const ( + BGPGroupTestFile = ` +name: Group1 +local_address: 100.64.0.1 +route_server_client: true +route_reflector_client: true +passive: true +ttl: 10 +local_as: 65200 +peer_as: 65300 +authentication_key: secret +hold_time: 180 +routing_instance: main +import: ["ACCEPT_ALL"] +export: ["REJECT_ALL"] +neighbors: + - peer_address: 100.64.0.2 + cluster_id: 100.64.0.0 + - peer_address: 100.64.1.2 + local_address: 100.64.1.1 + local_as: 65400 + peer_as: 65401 + hold_time: 90 + ttl: 1 + routing_instance: test + export: ["ACCEPT_ALL"] + import: ["REJECT_ALL"] + authentication_key: top-secret + passive: false + route_reflector_client: false + route_server_client: false + ` +) + +func TestBGPGroupConfigInheritance(t *testing.T) { + policyOptions := &PolicyOptions{} + accept_all := filter.NewFilter("ACCEPT_ALL", nil) + reject_all := filter.NewFilter("REJECT_ALL", nil) + policyOptions.PolicyStatementsFilter = []*filter.Filter{ + accept_all, + reject_all, + } + + b := []byte(BGPGroupTestFile) + var group *BGPGroup + err := yaml.Unmarshal(b, &group) + if err != nil { + t.Fatalf("unexpected error while parsing: %s", err) + } + + assert.Equal(t, 2, len(group.Neighbors), "neighbor count") + + err = group.load(64900, policyOptions) + if err != nil { + t.Fatalf("unexpected error while loading group config: %s", err) + } + + n1 := group.Neighbors[0] + assert.Equal(t, bnet.IPv4FromOctets(100, 64, 0, 1).Dedup(), n1.LocalAddressIP, "neighbor 1 local address") + assert.Equal(t, bnet.IPv4FromOctets(100, 64, 0, 2).Dedup(), n1.PeerAddressIP, "neighbor 1 peer address") + assert.True(t, *n1.RouteServerClient, "neighbor 1 route server client") + assert.True(t, *n1.RouteReflectorClient, "neighbor 1 route reflector client") + assert.True(t, *n1.Passive, "neighbor 1 passive") + assert.Equal(t, uint8(10), n1.TTL, "neighbor 1 TTL") + assert.Equal(t, uint32(65200), n1.LocalAS, "neighbor 1 local ASN") + assert.Equal(t, uint32(65300), n1.PeerAS, "neighbor 1 peer ASN") + assert.Equal(t, "secret", n1.AuthenticationKey, "neighbor 1 auth") + assert.Equal(t, 180*time.Second, n1.HoldTimeDuration, "neighbor 1 hold") + assert.Equal(t, "main", n1.RoutingInstance, "neighbor 1 VRF") + assert.Equal(t, filter.Chain{accept_all}, n1.ImportFilterChain, "neighbor 1 import") + assert.Equal(t, filter.Chain{reject_all}, n1.ExportFilterChain, "neighbor 1 export") + assert.Equal(t, bnet.IPv4FromOctets(100, 64, 0, 0).Dedup(), n1.ClusterIDIP, "neighbor 1 cluster ID") + + n2 := group.Neighbors[1] + assert.Equal(t, bnet.IPv4FromOctets(100, 64, 1, 1).Dedup(), n2.LocalAddressIP, "neighbor 2 local address") + assert.Equal(t, bnet.IPv4FromOctets(100, 64, 1, 2).Dedup(), n2.PeerAddressIP, "neighbor 2 peer address") + assert.False(t, *n2.RouteServerClient, "neighbor 2 route server client") + assert.False(t, *n2.RouteReflectorClient, "neighbor 2 route reflector client") + assert.False(t, *n2.Passive, "neighbor 2 passive") + assert.Equal(t, uint8(1), n2.TTL, "neighbor 2 TTL") + assert.Equal(t, uint32(65400), n2.LocalAS, "neighbor 2 local ASN") + assert.Equal(t, uint32(65401), n2.PeerAS, "neighbor 2 peer ASN") + assert.Equal(t, "top-secret", n2.AuthenticationKey, "neighbor 2 auth") + assert.Equal(t, 90*time.Second, n2.HoldTimeDuration, "neighbor 2 hold") + assert.Equal(t, "test", n2.RoutingInstance, "neighbor 2 VRF") + assert.Equal(t, filter.Chain{reject_all}, n2.ImportFilterChain, "neighbor 2 import") + assert.Equal(t, filter.Chain{accept_all}, n2.ExportFilterChain, "neighbor 2 export") + assert.Nil(t, n2.ClusterIDIP, "neighbor 2 cluster ID") +} From 87853b54bdf87dbd79cf7f51536e9ae10e867593 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Fri, 8 Dec 2023 07:34:34 +0100 Subject: [PATCH 17/22] parse BGP instead of BGPGroup in test to increase coverage --- cmd/bio-rd/config/bgp_test.go | 70 ++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/cmd/bio-rd/config/bgp_test.go b/cmd/bio-rd/config/bgp_test.go index 0fd73cf0..20846e41 100644 --- a/cmd/bio-rd/config/bgp_test.go +++ b/cmd/bio-rd/config/bgp_test.go @@ -13,39 +13,40 @@ import ( const ( BGPGroupTestFile = ` -name: Group1 -local_address: 100.64.0.1 -route_server_client: true -route_reflector_client: true -passive: true -ttl: 10 -local_as: 65200 -peer_as: 65300 -authentication_key: secret -hold_time: 180 -routing_instance: main -import: ["ACCEPT_ALL"] -export: ["REJECT_ALL"] -neighbors: - - peer_address: 100.64.0.2 - cluster_id: 100.64.0.0 - - peer_address: 100.64.1.2 - local_address: 100.64.1.1 - local_as: 65400 - peer_as: 65401 - hold_time: 90 - ttl: 1 - routing_instance: test - export: ["ACCEPT_ALL"] - import: ["REJECT_ALL"] - authentication_key: top-secret - passive: false - route_reflector_client: false - route_server_client: false +groups: + - name: Group1 + local_address: 100.64.0.1 + route_server_client: true + route_reflector_client: true + passive: true + ttl: 10 + local_as: 65200 + peer_as: 65300 + authentication_key: secret + hold_time: 180 + routing_instance: main + import: ["ACCEPT_ALL"] + export: ["REJECT_ALL"] + neighbors: + - peer_address: 100.64.0.2 + cluster_id: 100.64.0.0 + - peer_address: 100.64.1.2 + local_address: 100.64.1.1 + local_as: 65400 + peer_as: 65401 + hold_time: 90 + ttl: 1 + routing_instance: test + export: ["ACCEPT_ALL"] + import: ["REJECT_ALL"] + authentication_key: top-secret + passive: false + route_reflector_client: false + route_server_client: false ` ) -func TestBGPGroupConfigInheritance(t *testing.T) { +func TestBGPLoad(t *testing.T) { policyOptions := &PolicyOptions{} accept_all := filter.NewFilter("ACCEPT_ALL", nil) reject_all := filter.NewFilter("REJECT_ALL", nil) @@ -55,15 +56,18 @@ func TestBGPGroupConfigInheritance(t *testing.T) { } b := []byte(BGPGroupTestFile) - var group *BGPGroup - err := yaml.Unmarshal(b, &group) + var bgp *BGP + err := yaml.Unmarshal(b, &bgp) if err != nil { t.Fatalf("unexpected error while parsing: %s", err) } + assert.Equal(t, 1, len(bgp.Groups), "group count") + group := bgp.Groups[0] + assert.Equal(t, 2, len(group.Neighbors), "neighbor count") - err = group.load(64900, policyOptions) + err = bgp.load(64900, policyOptions) if err != nil { t.Fatalf("unexpected error while loading group config: %s", err) } From 41052840783d5f38cc14b14cec2bccf6de9d8306 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Fri, 8 Dec 2023 11:23:30 +0100 Subject: [PATCH 18/22] cover add_path configuration --- cmd/bio-rd/config/bgp.go | 2 +- cmd/bio-rd/config/bgp_test.go | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 5dd71b85..2b1d6096 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -255,7 +255,7 @@ func (bn *BGPNeighbor) load(policyOptions *PolicyOptions) error { } type AddressFamilyConfig struct { - AddPath *AddPathConfig + AddPath *AddPathConfig `yaml:"add_path"` } type AddPathConfig struct { diff --git a/cmd/bio-rd/config/bgp_test.go b/cmd/bio-rd/config/bgp_test.go index 20846e41..20363449 100644 --- a/cmd/bio-rd/config/bgp_test.go +++ b/cmd/bio-rd/config/bgp_test.go @@ -30,6 +30,11 @@ groups: neighbors: - peer_address: 100.64.0.2 cluster_id: 100.64.0.0 + ipv4: + add_path: + receive: true + send: + path_count: 5 - peer_address: 100.64.1.2 local_address: 100.64.1.1 local_as: 65400 @@ -43,6 +48,16 @@ groups: passive: false route_reflector_client: false route_server_client: false + ipv6: + add_path: + receive: true + send: + path_count: 2 + ipv6: + add_path: + receive: false + send: + path_count: 10 ` ) @@ -87,6 +102,10 @@ func TestBGPLoad(t *testing.T) { assert.Equal(t, filter.Chain{accept_all}, n1.ImportFilterChain, "neighbor 1 import") assert.Equal(t, filter.Chain{reject_all}, n1.ExportFilterChain, "neighbor 1 export") assert.Equal(t, bnet.IPv4FromOctets(100, 64, 0, 0).Dedup(), n1.ClusterIDIP, "neighbor 1 cluster ID") + assert.True(t, n1.IPv4.AddPath.Receive, "neighbor 1 IPv4 add path receive") + assert.False(t, n1.IPv6.AddPath.Receive, "neighbor 1 IPv6 add path receive") + assert.Equal(t, uint8(5), n1.IPv4.AddPath.Send.PathCount, "neighbor 1 IPv4 add path send count") + assert.Equal(t, uint8(10), n1.IPv6.AddPath.Send.PathCount, "neighbor 1 IPv6 add path send count") n2 := group.Neighbors[1] assert.Equal(t, bnet.IPv4FromOctets(100, 64, 1, 1).Dedup(), n2.LocalAddressIP, "neighbor 2 local address") @@ -103,4 +122,7 @@ func TestBGPLoad(t *testing.T) { assert.Equal(t, filter.Chain{reject_all}, n2.ImportFilterChain, "neighbor 2 import") assert.Equal(t, filter.Chain{accept_all}, n2.ExportFilterChain, "neighbor 2 export") assert.Nil(t, n2.ClusterIDIP, "neighbor 2 cluster ID") + assert.Nil(t, n2.IPv4, "neighbor 2 IPv4") + assert.True(t, n2.IPv6.AddPath.Receive, "neighbor 2 IPv6 add path receive") + assert.Equal(t, uint8(2), n2.IPv6.AddPath.Send.PathCount, "neighbor 2 IPv6 add path send count") } From dbd29e37e034e544e16deacfc100ed7468f45e32 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Fri, 8 Dec 2023 11:25:29 +0100 Subject: [PATCH 19/22] cover disabled --- cmd/bio-rd/config/bgp_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/bio-rd/config/bgp_test.go b/cmd/bio-rd/config/bgp_test.go index 20363449..ba62dd01 100644 --- a/cmd/bio-rd/config/bgp_test.go +++ b/cmd/bio-rd/config/bgp_test.go @@ -30,6 +30,7 @@ groups: neighbors: - peer_address: 100.64.0.2 cluster_id: 100.64.0.0 + disabled: true ipv4: add_path: receive: true @@ -106,6 +107,7 @@ func TestBGPLoad(t *testing.T) { assert.False(t, n1.IPv6.AddPath.Receive, "neighbor 1 IPv6 add path receive") assert.Equal(t, uint8(5), n1.IPv4.AddPath.Send.PathCount, "neighbor 1 IPv4 add path send count") assert.Equal(t, uint8(10), n1.IPv6.AddPath.Send.PathCount, "neighbor 1 IPv6 add path send count") + assert.True(t, n1.Disabled, "neighbor 1 disabled") n2 := group.Neighbors[1] assert.Equal(t, bnet.IPv4FromOctets(100, 64, 1, 1).Dedup(), n2.LocalAddressIP, "neighbor 2 local address") From 0b33ddd89d9ad4d299a6471a00d97b77e5c007fe Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Fri, 8 Dec 2023 12:51:11 +0100 Subject: [PATCH 20/22] improve readability for deconfigureRemovedSessions --- cmd/bio-rd/bgp.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index 76099f21..1009c78e 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -56,21 +56,23 @@ func (c *bgpConfigurator) configureSession(bn *config.BGPNeighbor, bg *config.BG func (c *bgpConfigurator) deconfigureRemovedSessions(cfg *config.BGP) { for _, p := range c.srv.GetPeers() { - found := false - for _, bg := range cfg.Groups { - for _, bn := range bg.Neighbors { - v, _ := c.determineVRF(bn, bg) - if bn.PeerAddressIP == p.Addr() && p.VRF() == v { - found = true - break - } - } + if !c.peerExistsInConfig(cfg, p) { + c.srv.DisposePeer(p.VRF(), p.Addr()) } + } +} - if !found { - 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 { From 5cc0ae0376baaf3feef38b436cd27e750baad1d8 Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Fri, 8 Dec 2023 12:52:57 +0100 Subject: [PATCH 21/22] move deconfigure to last step --- cmd/bio-rd/bgp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/bio-rd/bgp.go b/cmd/bio-rd/bgp.go index 1009c78e..ccf49d18 100644 --- a/cmd/bio-rd/bgp.go +++ b/cmd/bio-rd/bgp.go @@ -20,8 +20,6 @@ type bgpConfigurator struct { } func (c *bgpConfigurator) configure(cfg *config.BGP) error { - c.deconfigureRemovedSessions(cfg) - for _, bg := range cfg.Groups { for _, bn := range bg.Neighbors { err := c.configureSession(bn, bg) @@ -31,6 +29,8 @@ func (c *bgpConfigurator) configure(cfg *config.BGP) error { } } + c.deconfigureRemovedSessions(cfg) + return nil } From fe6445cc7ead4491994e096594d39e772b8558da Mon Sep 17 00:00:00 2001 From: Daniel Czerwonk Date: Sun, 10 Dec 2023 10:55:06 +0100 Subject: [PATCH 22/22] add cluster id to group, test overriding group cluster id --- cmd/bio-rd/config/bgp.go | 19 +++++++++++++++++-- cmd/bio-rd/config/bgp_test.go | 3 ++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cmd/bio-rd/config/bgp.go b/cmd/bio-rd/config/bgp.go index 2b1d6096..f28b1fa2 100644 --- a/cmd/bio-rd/config/bgp.go +++ b/cmd/bio-rd/config/bgp.go @@ -41,8 +41,10 @@ type BGPGroup struct { ImportFilterChain filter.Chain Export []string `yaml:"export"` ExportFilterChain filter.Chain - RouteServerClient *bool `yaml:"route_server_client"` - RouteReflectorClient *bool `yaml:"route_reflector_client"` + RouteServerClient *bool `yaml:"route_server_client"` + RouteReflectorClient *bool `yaml:"route_reflector_client"` + ClusterID string `yaml:"cluster_id"` + ClusterIDIP *bnet.IP Passive *bool `yaml:"passive"` Neighbors []*BGPNeighbor `yaml:"neighbors"` IPv4 *AddressFamilyConfig `yaml:"ipv4"` @@ -64,6 +66,15 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { bg.LocalAddressIP = a.Dedup() } + if bg.ClusterID != "" { + a, err := bnet.IPFromString(bg.ClusterID) + if err != nil { + return fmt.Errorf("unable to parse BGP cluster identifier: %w", err) + } + + bg.ClusterIDIP = a.Dedup() + } + if bg.HoldTime == 0 { bg.HoldTime = DefaultHoldTimeSeconds } @@ -99,6 +110,10 @@ func (bg *BGPGroup) load(localAS uint32, policyOptions *PolicyOptions) error { bn.LocalAddressIP = bg.LocalAddressIP } + if bn.ClusterID == "" { + bn.ClusterIDIP = bg.ClusterIDIP + } + if bn.TTL == 0 { bn.TTL = bg.TTL } diff --git a/cmd/bio-rd/config/bgp_test.go b/cmd/bio-rd/config/bgp_test.go index ba62dd01..d460923e 100644 --- a/cmd/bio-rd/config/bgp_test.go +++ b/cmd/bio-rd/config/bgp_test.go @@ -27,6 +27,7 @@ groups: routing_instance: main import: ["ACCEPT_ALL"] export: ["REJECT_ALL"] + cluster_id: 100.65.1.1 neighbors: - peer_address: 100.64.0.2 cluster_id: 100.64.0.0 @@ -123,7 +124,7 @@ func TestBGPLoad(t *testing.T) { assert.Equal(t, "test", n2.RoutingInstance, "neighbor 2 VRF") assert.Equal(t, filter.Chain{reject_all}, n2.ImportFilterChain, "neighbor 2 import") assert.Equal(t, filter.Chain{accept_all}, n2.ExportFilterChain, "neighbor 2 export") - assert.Nil(t, n2.ClusterIDIP, "neighbor 2 cluster ID") + assert.Equal(t, n2.ClusterIDIP, bnet.IPv4FromOctets(100, 65, 1, 1).Dedup(), "neighbor 2 cluster ID") assert.Nil(t, n2.IPv4, "neighbor 2 IPv4") assert.True(t, n2.IPv6.AddPath.Receive, "neighbor 2 IPv6 add path receive") assert.Equal(t, uint8(2), n2.IPv6.AddPath.Send.PathCount, "neighbor 2 IPv6 add path send count")