github.com/cilium/cilium@v1.16.2/pkg/bgpv1/gobgp/peer_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package gobgp 5 6 import ( 7 "context" 8 "fmt" 9 "net/netip" 10 "testing" 11 12 gobgp "github.com/osrg/gobgp/v3/api" 13 "github.com/stretchr/testify/require" 14 "k8s.io/utils/ptr" 15 16 "github.com/cilium/cilium/pkg/bgpv1/types" 17 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 18 ) 19 20 var ( 21 defaultConf = neighborConf{ 22 address: "1.2.3.4", 23 port: ptr.To[int32](179), 24 asn: 65001, 25 } 26 27 invalidIPConf = neighborConf{ 28 address: "1.2.3.x", 29 port: ptr.To[int32](179), 30 asn: 65001, 31 } 32 33 afiConf = func() neighborConf { 34 d := defaultConf.DeepCopy() 35 d.families = []v2alpha1.CiliumBGPFamily{ 36 { 37 Afi: "l2vpn", 38 Safi: "multicast", 39 }, 40 } 41 return d 42 } 43 44 invalidAfiConf = func() neighborConf { 45 d := defaultConf.DeepCopy() 46 d.families = []v2alpha1.CiliumBGPFamily{ 47 { 48 Afi: "foo", 49 Safi: "bar", 50 }, 51 } 52 return d 53 } 54 55 multiHopConf = func() neighborConf { 56 d := defaultConf.DeepCopy() 57 d.multihop = ptr.To[int32](3) 58 return d 59 } 60 61 timersConf = func() neighborConf { 62 d := defaultConf.DeepCopy() 63 d.timers = &timersConfig{ 64 connect: 60, 65 hold: 30, 66 keepalive: 15, 67 } 68 return d 69 } 70 71 restartConf = func() neighborConf { 72 d := defaultConf.DeepCopy() 73 d.restart = &restartConfig{ 74 enabled: true, 75 time: ptr.To[int32](90), 76 } 77 return d 78 } 79 ) 80 81 type restartConfig struct { 82 enabled bool 83 time *int32 84 } 85 86 type timersConfig struct { 87 connect int32 88 hold int32 89 keepalive int32 90 } 91 92 type neighborConf struct { 93 address string 94 port *int32 95 asn int64 96 families []v2alpha1.CiliumBGPFamily 97 multihop *int32 98 timers *timersConfig 99 restart *restartConfig 100 } 101 102 func (n neighborConf) DeepCopy() neighborConf { 103 neighCopy := neighborConf{ 104 address: n.address, 105 port: ptr.To[int32](*n.port), 106 asn: n.asn, 107 multihop: n.multihop, 108 } 109 110 neighCopy.families = make([]v2alpha1.CiliumBGPFamily, len(n.families)) 111 for i, f := range n.families { 112 neighCopy.families[i] = *f.DeepCopy() 113 } 114 115 if n.timers != nil { 116 neighCopy.timers = &timersConfig{ 117 connect: n.timers.connect, 118 hold: n.timers.hold, 119 keepalive: n.timers.keepalive, 120 } 121 } 122 123 if n.restart != nil { 124 neighCopy.restart = &restartConfig{ 125 enabled: n.restart.enabled, 126 time: ptr.To[int32](*n.restart.time), 127 } 128 } 129 130 return neighCopy 131 } 132 133 func neighborFromTestConf(c neighborConf) *v2alpha1.CiliumBGPNeighbor { 134 n := &v2alpha1.CiliumBGPNeighbor{ 135 PeerAddress: fmt.Sprintf("%s/32", c.address), 136 PeerPort: ptr.To[int32](179), 137 PeerASN: c.asn, 138 EBGPMultihopTTL: c.multihop, 139 GracefulRestart: &v2alpha1.CiliumBGPNeighborGracefulRestart{}, 140 Families: c.families, 141 AdvertisedPathAttributes: []v2alpha1.CiliumBGPPathAttributes{}, 142 ConnectRetryTimeSeconds: ptr.To[int32](120), 143 HoldTimeSeconds: ptr.To[int32](90), 144 KeepAliveTimeSeconds: ptr.To[int32](30), 145 } 146 147 if c.port != nil { 148 n.PeerPort = c.port 149 } 150 151 if c.timers != nil { 152 n.ConnectRetryTimeSeconds = &c.timers.connect 153 n.HoldTimeSeconds = &c.timers.hold 154 n.KeepAliveTimeSeconds = &c.timers.keepalive 155 } 156 157 if c.restart != nil { 158 n.GracefulRestart.Enabled = c.restart.enabled 159 n.GracefulRestart.RestartTimeSeconds = c.restart.time 160 } 161 162 return n 163 } 164 165 func bgpNodePeerFromTestConf(c neighborConf) *v2alpha1.CiliumBGPNodePeer { 166 p := &v2alpha1.CiliumBGPNodePeer{ 167 Name: "peer-1", 168 PeerAddress: ptr.To[string](c.address), 169 PeerASN: ptr.To[int64](c.asn), 170 } 171 172 return p 173 } 174 175 func bgpPeerConfigFromTestConf(c neighborConf) *v2alpha1.CiliumBGPPeerConfigSpec { 176 p := &v2alpha1.CiliumBGPPeerConfigSpec{} 177 p.SetDefaults() 178 179 p.Families = []v2alpha1.CiliumBGPFamilyWithAdverts{} 180 for _, fam := range c.families { 181 p.Families = append(p.Families, v2alpha1.CiliumBGPFamilyWithAdverts{ 182 CiliumBGPFamily: fam, 183 }) 184 } 185 186 if c.multihop != nil { 187 p.EBGPMultihop = c.multihop 188 } 189 190 if c.port != nil { 191 p.Transport.PeerPort = c.port 192 } 193 194 if c.timers != nil { 195 p.Timers.ConnectRetryTimeSeconds = &c.timers.connect 196 p.Timers.HoldTimeSeconds = &c.timers.hold 197 p.Timers.KeepAliveTimeSeconds = &c.timers.keepalive 198 } 199 200 if c.restart != nil { 201 p.GracefulRestart.Enabled = c.restart.enabled 202 p.GracefulRestart.RestartTimeSeconds = c.restart.time 203 } 204 205 return p 206 } 207 208 func gobgpPeerFromTestConf(c neighborConf) *gobgp.Peer { 209 p := &gobgp.Peer{ 210 Conf: &gobgp.PeerConf{ 211 NeighborAddress: c.address, 212 PeerAsn: uint32(c.asn), 213 }, 214 Transport: &gobgp.Transport{ 215 RemotePort: uint32(*c.port), 216 }, 217 Timers: &gobgp.Timers{}, 218 GracefulRestart: &gobgp.GracefulRestart{}, 219 } 220 221 addr, err := netip.ParseAddr(c.address) 222 if err != nil { 223 return nil 224 } 225 if addr.Is4() { 226 p.Transport.LocalAddress = wildcardIPv4Addr 227 } else { 228 p.Transport.LocalAddress = wildcardIPv6Addr 229 } 230 231 p.AfiSafis, err = convertBGPNeighborSAFI(c.families) 232 if err != nil { 233 return nil 234 } 235 236 if testServerParameters.Global.ASN != uint32(c.asn) && c.multihop != nil && *c.multihop > 1 { 237 p.EbgpMultihop = &gobgp.EbgpMultihop{ 238 Enabled: true, 239 MultihopTtl: uint32(*c.multihop), 240 } 241 } 242 243 if c.timers != nil { 244 p.Timers.Config = &gobgp.TimersConfig{ 245 ConnectRetry: uint64(c.timers.connect), 246 HoldTime: uint64(c.timers.hold), 247 KeepaliveInterval: uint64(c.timers.keepalive), 248 } 249 } else { 250 p.Timers.Config = &gobgp.TimersConfig{ 251 ConnectRetry: uint64(120), 252 HoldTime: uint64(90), 253 KeepaliveInterval: uint64(30), 254 } 255 } 256 p.Timers.Config.IdleHoldTimeAfterReset = idleHoldTimeAfterResetSeconds 257 258 if c.restart != nil { 259 p.GracefulRestart.Enabled = true 260 p.GracefulRestart.NotificationEnabled = true 261 if c.restart.time != nil { 262 p.GracefulRestart.RestartTime = uint32(*c.restart.time) 263 } else { 264 p.GracefulRestart.RestartTime = uint32(120) 265 } 266 } 267 268 return p 269 } 270 271 func TestGetPeerConfigV1(t *testing.T) { 272 table := []struct { 273 name string 274 neighbor *v2alpha1.CiliumBGPNeighbor 275 expected *gobgp.Peer 276 expect bool 277 }{ 278 { 279 name: "test nil neighbor", 280 neighbor: nil, 281 expected: nil, 282 expect: false, 283 }, 284 { 285 name: "test default neighbor config", 286 neighbor: neighborFromTestConf(defaultConf), 287 expected: gobgpPeerFromTestConf(defaultConf), 288 expect: true, 289 }, 290 { 291 name: "test default invalid IP neighbor config", 292 neighbor: neighborFromTestConf(invalidIPConf), 293 expected: gobgpPeerFromTestConf(invalidIPConf), 294 expect: false, 295 }, 296 { 297 name: "test neighbor afi safi config", 298 neighbor: neighborFromTestConf(afiConf()), 299 expected: gobgpPeerFromTestConf(afiConf()), 300 expect: true, 301 }, 302 { 303 name: "test neighbor invalid afi safi config", 304 neighbor: neighborFromTestConf(invalidAfiConf()), 305 expected: gobgpPeerFromTestConf(invalidAfiConf()), 306 expect: false, 307 }, 308 { 309 name: "test neighbor ebgp multihop ttl config", 310 neighbor: neighborFromTestConf(multiHopConf()), 311 expected: gobgpPeerFromTestConf(multiHopConf()), 312 expect: true, 313 }, 314 { 315 name: "test neighbor ebgp multihop timers config", 316 neighbor: neighborFromTestConf(timersConf()), 317 expected: gobgpPeerFromTestConf(timersConf()), 318 expect: true, 319 }, 320 { 321 name: "test neighbor graceful restart timers config", 322 neighbor: neighborFromTestConf(restartConf()), 323 expected: gobgpPeerFromTestConf(restartConf()), 324 expect: true, 325 }, 326 } 327 for _, tt := range table { 328 t.Run(tt.name, func(t *testing.T) { 329 svr, err := NewGoBGPServer(context.Background(), log, testServerParameters) 330 require.NoError(t, err) 331 332 t.Cleanup(func() { 333 svr.Stop() 334 }) 335 336 req := types.NeighborRequest{ 337 Neighbor: tt.neighbor, 338 } 339 340 peer, reset, err := svr.(*GoBGPServer).getPeerConfig(context.Background(), req, false) 341 if tt.expect { 342 require.NoError(t, err) 343 require.Equal(t, tt.expected.Conf, peer.Conf) 344 require.Equal(t, tt.expected.Transport, peer.Transport) 345 require.Equal(t, tt.expected.Timers, peer.Timers) 346 require.Equal(t, tt.expected.EbgpMultihop, peer.EbgpMultihop) 347 require.Equal(t, reset, false) 348 if len(tt.expected.AfiSafis) > 0 { 349 for i, safi := range tt.expected.AfiSafis { 350 require.Equal(t, safi.Config, peer.AfiSafis[i].Config) 351 } 352 } 353 } else { 354 require.Error(t, err) 355 } 356 }) 357 } 358 } 359 360 func TestGetPeerConfigV2(t *testing.T) { 361 table := []struct { 362 name string 363 peer *v2alpha1.CiliumBGPNodePeer 364 peerConfig *v2alpha1.CiliumBGPPeerConfigSpec 365 expected *gobgp.Peer 366 expect bool 367 }{ 368 { 369 name: "test nil peer and config", 370 peer: nil, 371 peerConfig: nil, 372 expected: nil, 373 expect: false, 374 }, 375 { 376 name: "test default neighbor config", 377 peer: bgpNodePeerFromTestConf(defaultConf), 378 peerConfig: bgpPeerConfigFromTestConf(defaultConf), 379 expected: gobgpPeerFromTestConf(defaultConf), 380 expect: true, 381 }, 382 { 383 name: "test default invalid IP neighbor config", 384 peer: bgpNodePeerFromTestConf(invalidIPConf), 385 peerConfig: bgpPeerConfigFromTestConf(invalidIPConf), 386 expected: gobgpPeerFromTestConf(invalidIPConf), 387 expect: false, 388 }, 389 { 390 name: "test neighbor afi safi config", 391 peer: bgpNodePeerFromTestConf(afiConf()), 392 peerConfig: bgpPeerConfigFromTestConf(afiConf()), 393 expected: gobgpPeerFromTestConf(afiConf()), 394 expect: true, 395 }, 396 { 397 name: "test neighbor invalid afi safi config", 398 peer: bgpNodePeerFromTestConf(invalidAfiConf()), 399 peerConfig: bgpPeerConfigFromTestConf(invalidAfiConf()), 400 expected: gobgpPeerFromTestConf(invalidAfiConf()), 401 expect: false, 402 }, 403 { 404 name: "test neighbor ebgp multihop ttl config", 405 peer: bgpNodePeerFromTestConf(multiHopConf()), 406 peerConfig: bgpPeerConfigFromTestConf(multiHopConf()), 407 expected: gobgpPeerFromTestConf(multiHopConf()), 408 expect: true, 409 }, 410 { 411 name: "test neighbor ebgp multihop timers config", 412 peer: bgpNodePeerFromTestConf(timersConf()), 413 peerConfig: bgpPeerConfigFromTestConf(timersConf()), 414 expected: gobgpPeerFromTestConf(timersConf()), 415 expect: true, 416 }, 417 { 418 name: "test neighbor graceful restart timers config", 419 peer: bgpNodePeerFromTestConf(restartConf()), 420 peerConfig: bgpPeerConfigFromTestConf(restartConf()), 421 expected: gobgpPeerFromTestConf(restartConf()), 422 expect: true, 423 }, 424 } 425 for _, tt := range table { 426 t.Run(tt.name, func(t *testing.T) { 427 svr, err := NewGoBGPServer(context.Background(), log, testServerParameters) 428 require.NoError(t, err) 429 430 t.Cleanup(func() { 431 svr.Stop() 432 }) 433 434 req := types.NeighborRequest{ 435 Peer: tt.peer, 436 PeerConfig: tt.peerConfig, 437 } 438 439 peer, reset, err := svr.(*GoBGPServer).getPeerConfig(context.Background(), req, false) 440 if tt.expect { 441 require.NoError(t, err) 442 require.Equal(t, tt.expected.Conf, peer.Conf) 443 require.Equal(t, tt.expected.Transport, peer.Transport) 444 require.Equal(t, tt.expected.Timers, peer.Timers) 445 require.Equal(t, tt.expected.EbgpMultihop, peer.EbgpMultihop) 446 require.Equal(t, reset, false) 447 if len(tt.expected.AfiSafis) > 0 { 448 for i, safi := range tt.expected.AfiSafis { 449 require.Equal(t, safi.Config, peer.AfiSafis[i].Config) 450 } 451 } 452 } else { 453 require.Error(t, err) 454 } 455 }) 456 } 457 }