Skip to content

Commit db83b98

Browse files
authored
lxd/bgp: Rework start/stop logic (from Incus) (#15173)
Includes cherry-pick from lxc/incus#1761. **Changes:** - BGP listener is now started after all the networks are set up. - Predefined network forwards are now added correctly during the BGP listener start up. - Extended test/suites/network_forward to test the BGP listener start up and subsequent restart of LXD daemon.
2 parents 835fe31 + 9b1d770 commit db83b98

File tree

4 files changed

+78
-70
lines changed

4 files changed

+78
-70
lines changed

lxd/api_1.0.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,7 @@ func doAPI10UpdateTriggers(d *Daemon, nodeChanged, clusterChanged map[string]str
996996
return fmt.Errorf("Cannot convert BGP ASN to uint32: Upper bound exceeded")
997997
}
998998

999-
err := s.BGP.Reconfigure(address, uint32(asn), net.ParseIP(routerid))
999+
err := s.BGP.Configure(address, uint32(asn), net.ParseIP(routerid))
10001000
if err != nil {
10011001
return fmt.Errorf("Failed reconfiguring BGP: %w", err)
10021002
}

lxd/bgp/server.go

+51-57
Original file line numberDiff line numberDiff line change
@@ -56,45 +56,21 @@ func NewServer() *Server {
5656
return s
5757
}
5858

59-
func (s *Server) setup() {
60-
if s.bgp != nil {
61-
return
62-
}
63-
64-
// Spawn the BGP goroutines.
65-
s.bgp = bgpServer.NewBgpServer()
66-
go s.bgp.Serve()
67-
68-
// Insert any path that's already defined.
69-
if len(s.paths) > 0 {
70-
// Reset the path list.
71-
paths := s.paths
72-
s.paths = map[string]path{}
73-
74-
for _, path := range paths {
75-
err := s.addPrefix(path.prefix, path.nexthop, path.owner)
76-
logger.Warn("Unable to add prefix to BGP server", logger.Ctx{"prefix": path.prefix.String(), "err": err})
77-
}
78-
}
79-
}
80-
8159
// Start sets up the BGP listener.
82-
func (s *Server) Start(address string, asn uint32, routerID net.IP) error {
83-
// Locking.
84-
s.mu.Lock()
85-
defer s.mu.Unlock()
86-
87-
return s.start(address, asn, routerID)
88-
}
89-
9060
func (s *Server) start(address string, asn uint32, routerID net.IP) error {
9161
// If routerID is nil, fill with our best guess.
9262
if routerID == nil || routerID.To4() == nil {
9363
return ErrBadRouterID
9464
}
9565

96-
// Make sure we have a BGP instance.
97-
s.setup()
66+
// Check if already running
67+
if s.bgp != nil {
68+
return fmt.Errorf("BGP listener is already running")
69+
}
70+
71+
// Spawn the BGP goroutines.
72+
s.bgp = bgpServer.NewBgpServer()
73+
go s.bgp.Serve()
9874

9975
// Get the address and port.
10076
addrHost, addrPort, err := net.SplitHostPort(address)
@@ -131,8 +107,30 @@ func (s *Server) start(address string, asn uint32, routerID net.IP) error {
131107
return err
132108
}
133109

134-
// Add any existing peers.
135-
for _, peer := range s.peers {
110+
// Copy the path list
111+
oldPaths := map[string]path{}
112+
for pathUUID, path := range s.paths {
113+
oldPaths[pathUUID] = path
114+
}
115+
116+
// Add existing paths.
117+
s.paths = map[string]path{}
118+
for _, path := range oldPaths {
119+
err := s.addPrefix(path.prefix, path.nexthop, path.owner)
120+
if err != nil {
121+
logger.Warn("Unable to add prefix to BGP server", logger.Ctx{"prefix": path.prefix.String(), "err": err})
122+
}
123+
}
124+
125+
// Copy the peer list.
126+
oldPeers := map[string]peer{}
127+
for peerUUID, peer := range s.peers {
128+
oldPeers[peerUUID] = peer
129+
}
130+
131+
// Add existing peers.
132+
s.peers = map[string]peer{}
133+
for _, peer := range oldPeers {
136134
err := s.addPeer(peer.address, peer.asn, peer.password, peer.holdtime)
137135
if err != nil {
138136
return err
@@ -148,20 +146,18 @@ func (s *Server) start(address string, asn uint32, routerID net.IP) error {
148146
}
149147

150148
// Stop tears down the BGP listener.
151-
func (s *Server) Stop() error {
152-
// Locking.
153-
s.mu.Lock()
154-
defer s.mu.Unlock()
155-
156-
return s.stop()
157-
}
158-
159149
func (s *Server) stop() error {
160150
// Skip if no instance.
161151
if s.bgp == nil {
162152
return nil
163153
}
164154

155+
// Save the peer list.
156+
oldPeers := map[string]peer{}
157+
for peerUUID, peer := range s.peers {
158+
oldPeers[peerUUID] = peer
159+
}
160+
165161
// Remove all the peers (ignore failures).
166162
for _, peer := range s.peers {
167163
err := s.removePeer(peer.address)
@@ -170,37 +166,38 @@ func (s *Server) stop() error {
170166
}
171167
}
172168

169+
// Restore peer list.
170+
s.peers = oldPeers
171+
173172
// Stop the listener.
174173
err := s.bgp.StopBgp(context.Background(), &bgpAPI.StopBgpRequest{})
175174
if err != nil {
176175
return err
177176
}
178177

179-
// Unset the address
178+
// Mark the daemon as down.
180179
s.address = ""
181180
s.asn = 0
182181
s.routerID = nil
182+
s.bgp = nil
183+
183184
return nil
184185
}
185186

186-
// Reconfigure updates the listener with a new configuration..
187-
func (s *Server) Reconfigure(address string, asn uint32, routerID net.IP) error {
187+
// Configure updates the listener with a new configuration..
188+
func (s *Server) Configure(address string, asn uint32, routerID net.IP) error {
188189
// Locking.
189190
s.mu.Lock()
190191
defer s.mu.Unlock()
191192

192-
return s.reconfigure(address, asn, routerID)
193+
return s.configure(address, asn, routerID)
193194
}
194195

195-
func (s *Server) reconfigure(address string, asn uint32, routerID net.IP) error {
196-
// Get the old address.
196+
func (s *Server) configure(address string, asn uint32, routerID net.IP) error {
197+
// Store current configuration for reverting.
197198
oldAddress := s.address
198199
oldASN := s.asn
199200
oldRouterID := s.routerID
200-
oldPeers := map[string]peer{}
201-
for peerUUID, peer := range s.peers {
202-
oldPeers[peerUUID] = peer
203-
}
204201

205202
// Setup reverter.
206203
revert := revert.New()
@@ -209,12 +206,9 @@ func (s *Server) reconfigure(address string, asn uint32, routerID net.IP) error
209206
// Stop the listener.
210207
err := s.stop()
211208
if err != nil {
212-
return err
209+
return fmt.Errorf("Failed to stop current listener: %w", err)
213210
}
214211

215-
// Restore peer list.
216-
s.peers = oldPeers
217-
218212
// Check if we should start.
219213
if address != "" && asn > 0 && routerID != nil {
220214
// Restore old address on failure.
@@ -223,7 +217,7 @@ func (s *Server) reconfigure(address string, asn uint32, routerID net.IP) error
223217
// Start the listener with the new address.
224218
err = s.start(address, asn, routerID)
225219
if err != nil {
226-
return err
220+
return fmt.Errorf("Failed to start new listener: %w", err)
227221
}
228222
}
229223

lxd/daemon.go

+13-12
Original file line numberDiff line numberDiff line change
@@ -1708,18 +1708,6 @@ func (d *Daemon) init() error {
17081708

17091709
// Setup BGP listener.
17101710
d.bgp = bgp.NewServer()
1711-
if bgpAddress != "" && bgpASN != 0 && bgpRouterID != "" {
1712-
if bgpASN > math.MaxUint32 {
1713-
return fmt.Errorf("Cannot convert BGP ASN to uint32: Upper bound exceeded")
1714-
}
1715-
1716-
err := d.bgp.Start(bgpAddress, uint32(bgpASN), net.ParseIP(bgpRouterID))
1717-
if err != nil {
1718-
return err
1719-
}
1720-
1721-
logger.Info("Started BGP server")
1722-
}
17231711

17241712
// Setup DNS listener.
17251713
d.dns = dns.NewServer(d.db.Cluster, func(name string, full bool) (*dns.Zone, error) {
@@ -1769,6 +1757,19 @@ func (d *Daemon) init() error {
17691757
}
17701758

17711759
// Setup tertiary listeners that may use managed network addresses and must be started after networks.
1760+
if bgpAddress != "" && bgpASN != 0 && bgpRouterID != "" {
1761+
if bgpASN > math.MaxUint32 {
1762+
return fmt.Errorf("Cannot convert BGP ASN to uint32: Upper bound exceeded")
1763+
}
1764+
1765+
err := d.bgp.Configure(bgpAddress, uint32(bgpASN), net.ParseIP(bgpRouterID))
1766+
if err != nil {
1767+
return err
1768+
}
1769+
1770+
logger.Info("Started BGP server")
1771+
}
1772+
17721773
dnsAddress := d.localConfig.DNSAddress()
17731774
if dnsAddress != "" {
17741775
err = d.dns.Start(dnsAddress)

test/suites/network_forward.sh

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ test_network_forward() {
55
firewallDriver=$(lxc info | awk -F ":" '/firewall:/{gsub(/ /, "", $0); print $2}')
66
netName=lxdt$$
77

8+
lxc network create bgpbr # Bridge to start BGP listener on.
9+
10+
bgpIP=$(lxc network get bgpbr ipv4.address | cut -d/ -f1)
11+
812
lxc network create "${netName}" \
913
ipv4.address=192.0.2.1/24 \
1014
ipv6.address=fd42:4242:4242:1010::1/64
@@ -28,6 +32,15 @@ test_network_forward() {
2832
# Check forward is exported via BGP prefixes.
2933
lxc query /internal/testing/bgp | grep "198.51.100.1/32"
3034

35+
# Enable the BGP listener
36+
lxc config set core.bgp_address="${bgpIP}:8874"
37+
lxc config set core.bgp_asn=65536
38+
lxc config set core.bgp_routerid="${bgpIP}"
39+
40+
# Check that the listener survives a restart of LXD
41+
shutdown_lxd "${LXD_DIR}"
42+
respawn_lxd "${LXD_DIR}" true
43+
3144
lxc network forward delete "${netName}" 198.51.100.1
3245

3346
# Check deleting network forward removes forward BGP prefix.

0 commit comments

Comments
 (0)