The BGP controller now supports announcing routes to multiple BGP peers for redundancy and resilience. If one peer fails, route announcements continue to succeed for other healthy peers.
```yaml
bgp:
local_as: 12345
local_ip: 192.168.1.100 # optional
peers:
- peer_ip: 10.10.10.1
peer_as: 6789
communities: # per-peer communities (optional)
- 100:100
- peer_ip: 10.10.10.2
peer_as: 6789
communities:
- 100:101
multi_hop: true # optional, defaults to true for eBGP
communities: # global communities applied to all peers
- 1000:1000
origin: igp
```
```yaml
bgp:
local_as: 12345
peer_as: 6789
peer_ip: 10.10.10.1
communities:
- 100:100
origin: igp
```
Legacy configurations are automatically converted to the new format internally, ensuring backward compatibility.
Routes are announced to all configured peers. If announcement to one peer fails, the operation continues for other peers. Errors are aggregated and returned, but partial success is allowed.
Communities are merged in the following order:
1. **Global communities** (defined at `bgp.communities`)
2. **Per-peer communities** (defined at `bgp.peers[].communities`)
3. **Per-route communities** (defined at `apps[].vip_config.bgp_communities`)
Example: If global communities are `[1000:1000]`, peer communities are `[100:100]`, and route communities are `[5000:5000]`, the announced route will have all three: `[1000:1000, 100:100, 5000:5000]`.
- **Default behavior**: Multi-hop is disabled by default
- **Enable**: Set `multi_hop: true` per peer to explicitly enable multi-hop BGP
The `/info` endpoint now returns an array of peer information instead of a single peer object:
**Before:**
```json
{
"conf": {
"neighbor_address": "10.10.10.1",
"peer_as": 6789
},
"state": {...}
}
```
**After:**
```json
[
{
"conf": {
"neighbor_address": "10.10.10.1",
"peer_as": 6789
},
"state": {...}
},
{
"conf": {
"neighbor_address": "10.10.10.2",
"peer_as": 6789
},
"state": {...}
}
]
```
- `config/config.go`: Added `PeerConfig` struct and `Peers` slice to `BgpConfig`
- `controller/bgp.go`: Refactored to support multiple peers with best-effort semantics
- `controller/monitor.go`: Updated `GetInfo()` to return slice of peers
- `server/server.go`: Updated info handler to return array of peers
1. **Controller struct** now stores `[]PeerConfig` instead of single peer fields
2. **Announce/Withdraw** methods loop through all peers with error aggregation
3. **getApiPath** accepts a `PeerConfig` parameter for per-peer community merging
4. **addPeer** determines multi-hop settings per peer
5. **PeerInfo** returns information for all configured peers
6. **Shutdown** gracefully shuts down all peer sessions
The implementation includes comprehensive test coverage:
1. **TestLegacyConfigConversion** - Verifies backward compatibility by testing that legacy single-peer configs are automatically converted to multi-peer format
2. **TestMultiPeerConfig** - Tests that new multi-peer configurations are properly loaded with multiple peers
3. **TestNoPeersConfigError** - Ensures proper error handling when no peers are configured
4. **TestCommunityMerging** - Validates that global, per-peer, and per-route communities are correctly merged in order
5. **TestMultiHopConfiguration** - Tests multi-hop BGP settings with various scenarios:
- Default behavior (multi-hop disabled)
- Explicit multi-hop disable
- Explicit multi-hop enable
6. **TestBestEffortAnnouncement** - Verifies that announcements succeed even when individual peers may have issues
7. **TestWithdrawMultiplePeers** - Tests route withdrawal across multiple peers
8. **TestPeerInfoMultiplePeers** - Validates that peer information is correctly returned for all configured peers
- **TestBgpNew** - Full integration test with actual BGP listeners (requires root, skipped in CI)
- **TestMultiPeerAnnouncement** - Tests actual route announcements to multiple BGP listeners (requires root, skipped in CI)
Existing configurations using `peer_ip` and `peer_as` continue to work without modification.
To add a second peer for resilience:
```yaml
bgp:
local_as: 12345
# Keep existing config for backward compatibility, or remove these lines
# peer_as: 6789
# peer_ip: 10.10.10.1
# Add new multi-peer config
peers:
- peer_ip: 10.10.10.1
peer_as: 6789
- peer_ip: 10.10.10.2 # redundant peer
peer_as: 6789
communities:
- 100:100
origin: igp
```
All operations (Announce, Withdraw, Shutdown) use best-effort error handling:
- Operations continue even if individual peers fail
- Errors are collected and returned as aggregated error messages
- Format: `"announcement errors: [peer 10.10.10.1: error message, peer 10.10.10.2: error message]"`
These changes were authored via AI LLM.
Authored-By: Claude Code (Sonnet 4.5)
1.1 KiB
1.1 KiB