diff --git a/config/peer.go b/config/peer.go index fa963092ec84cf76d3db5ce05c45672131cd3f93..1736ac667e05eb81400b91d2e95d5f319ff3c9ed 100644 --- a/config/peer.go +++ b/config/peer.go @@ -11,21 +11,22 @@ import ( // Peer defines the configuration for a BGP session type Peer struct { - AdminEnabled bool - ReconnectInterval time.Duration - KeepAlive time.Duration - HoldTime time.Duration - LocalAddress bnet.IP - PeerAddress bnet.IP - LocalAS uint32 - PeerAS uint32 - Passive bool - RouterID uint32 - RouteServerClient bool - RouteReflectorClient bool - RouteReflectorClusterID uint32 - IPv4 *AddressFamilyConfig - IPv6 *AddressFamilyConfig + AdminEnabled bool + ReconnectInterval time.Duration + KeepAlive time.Duration + HoldTime time.Duration + LocalAddress bnet.IP + PeerAddress bnet.IP + LocalAS uint32 + PeerAS uint32 + Passive bool + RouterID uint32 + RouteServerClient bool + RouteReflectorClient bool + RouteReflectorClusterID uint32 + AdvertiseIPv4MultiProtocol bool + IPv4 *AddressFamilyConfig + IPv6 *AddressFamilyConfig } // AddressFamilyConfig represents all configuration parameters specific for an address family diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index 664e0d19399efd933fef5ee42330c7b5f2016a48..a276ef47ee0c1b57422df07be7b96a74c4a9c7d1 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -182,6 +182,10 @@ func (s *openSentState) processMultiProtocolCapability(cap packet.MultiProtocolC return } + if cap.AFI == packet.IPv4AFI && !s.fsm.peer.ipv4MultiProtocolAdvertised { + return + } + f := s.fsm.addressFamily(cap.AFI, cap.SAFI) if f != nil { f.multiProtocol = true diff --git a/protocols/bgp/server/fsm_open_sent_test.go b/protocols/bgp/server/fsm_open_sent_test.go index 03133879f5569c623a13f9f1fee49a3dcf11da59..708617b910775a28efcd89ec0866320905912455 100644 --- a/protocols/bgp/server/fsm_open_sent_test.go +++ b/protocols/bgp/server/fsm_open_sent_test.go @@ -87,3 +87,114 @@ func TestOpenMsgReceived(t *testing.T) { }) } } + +func TestProcessMultiProtocolCapability(t *testing.T) { + tests := []struct { + name string + peer *peer + caps []packet.MultiProtocolCapability + expectIPv4MultiProtocol bool + expectIPv6MultiProtocol bool + }{ + { + name: "IPv4 only without multi protocol configuration", + peer: &peer{ + ipv4: &peerAddressFamily{}, + }, + caps: []packet.MultiProtocolCapability{ + packet.MultiProtocolCapability{ + AFI: packet.IPv4AFI, + SAFI: packet.UnicastSAFI, + }, + }, + }, + { + name: "IPv4 only with multi protocol configuration", + peer: &peer{ + ipv4: &peerAddressFamily{}, + ipv4MultiProtocolAdvertised: true, + }, + caps: []packet.MultiProtocolCapability{ + packet.MultiProtocolCapability{ + AFI: packet.IPv4AFI, + SAFI: packet.UnicastSAFI, + }, + }, + expectIPv4MultiProtocol: true, + }, + { + name: "IPv6 only", + peer: &peer{ + ipv6: &peerAddressFamily{}, + }, + caps: []packet.MultiProtocolCapability{ + packet.MultiProtocolCapability{ + AFI: packet.IPv6AFI, + SAFI: packet.UnicastSAFI, + }, + }, + expectIPv6MultiProtocol: true, + }, + { + name: "IPv4 and IPv6, only IPv6 configured as multi protocol", + peer: &peer{ + ipv4: &peerAddressFamily{}, + ipv6: &peerAddressFamily{}, + }, + caps: []packet.MultiProtocolCapability{ + packet.MultiProtocolCapability{ + AFI: packet.IPv6AFI, + SAFI: packet.UnicastSAFI, + }, + packet.MultiProtocolCapability{ + AFI: packet.IPv4AFI, + SAFI: packet.UnicastSAFI, + }, + }, + expectIPv6MultiProtocol: true, + }, + { + name: "IPv4 and IPv6 configured as multi protocol", + peer: &peer{ + ipv4: &peerAddressFamily{}, + ipv6: &peerAddressFamily{}, + ipv4MultiProtocolAdvertised: true, + }, + caps: []packet.MultiProtocolCapability{ + packet.MultiProtocolCapability{ + AFI: packet.IPv6AFI, + SAFI: packet.UnicastSAFI, + }, + packet.MultiProtocolCapability{ + AFI: packet.IPv4AFI, + SAFI: packet.UnicastSAFI, + }, + }, + expectIPv4MultiProtocol: true, + expectIPv6MultiProtocol: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fsm := newFSM(test.peer) + fsm.con = &btesting.MockConn{} + + s := &openSentState{ + fsm: fsm, + } + + for _, cap := range test.caps { + s.processMultiProtocolCapability(cap) + } + + if fsm.ipv4Unicast != nil { + assert.Equal(t, test.expectIPv4MultiProtocol, fsm.ipv4Unicast.multiProtocol) + } + + if fsm.ipv6Unicast != nil { + assert.Equal(t, test.expectIPv6MultiProtocol, fsm.ipv6Unicast.multiProtocol) + } + }) + } +} diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index dd877d77f4eee02eb6a21578b9dbaf54a58336a9..d57b22d731d1e5bc394915e2ff19e6097e205722 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -29,14 +29,15 @@ type peer struct { fsms []*FSM fsmsMu sync.Mutex - routerID uint32 - reconnectInterval time.Duration - keepaliveTime time.Duration - holdTime time.Duration - optOpenParams []packet.OptParam - routeServerClient bool - routeReflectorClient bool - clusterID uint32 + routerID uint32 + reconnectInterval time.Duration + keepaliveTime time.Duration + holdTime time.Duration + optOpenParams []packet.OptParam + routeServerClient bool + routeReflectorClient bool + ipv4MultiProtocolAdvertised bool + clusterID uint32 ipv4 *peerAddressFamily ipv6 *peerAddressFamily @@ -162,6 +163,11 @@ func newPeer(c config.Peer, server *bgpServer) (*peer, error) { caps = append(caps, asn4Capability(c)) + if c.IPv4 != nil && c.AdvertiseIPv4MultiProtocol { + caps = append(caps, multiProtocolCapability(packet.IPv4AFI)) + p.ipv4MultiProtocolAdvertised = true + } + if c.IPv6 != nil { p.ipv6 = &peerAddressFamily{ rib: c.IPv6.RIB,