Skip to content

Commit

Permalink
add tests using inmemory listener
Browse files Browse the repository at this point in the history
  • Loading branch information
tobikris committed Dec 6, 2022
1 parent b554c27 commit c8dd0e7
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 66 deletions.
80 changes: 49 additions & 31 deletions protocols/bgp/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ const (
)

type bgpServer struct {
listeners []listener
acceptCh chan net.Conn
peers *peerManager
routerID uint32
metrics *metricsService
logger log.LoggerInterface
addListeners chan listener
acceptedListeners []listener
acceptCh chan net.Conn
peers *peerManager
routerID uint32
metrics *metricsService
logger log.LoggerInterface
}

type BGPServer interface {
Expand All @@ -37,6 +38,7 @@ type BGPServer interface {
GetPeerConfig(*bnet.IP) *PeerConfig
DisposePeer(*bnet.IP)
GetPeers() []*bnet.IP
GetPeerStatus(*bnet.IP) string
Metrics() (*metrics.BGPMetrics, error)
GetRIBIn(peerIP *bnet.IP, afi uint16, safi uint8) *adjRIBIn.AdjRIBIn
GetRIBOut(peerIP *bnet.IP, afi uint16, safi uint8) *adjRIBOut.AdjRIBOut
Expand All @@ -52,9 +54,10 @@ func NewBGPServer(routerID uint32) BGPServer {

func newBGPServer(routerID uint32) *bgpServer {
server := &bgpServer{
peers: newPeerManager(),
routerID: routerID,
listeners: make([]listener, 0),
peers: newPeerManager(),
routerID: routerID,
addListeners: make(chan listener, 256),
acceptedListeners: make([]listener, 0),
logger: log.GetLogger().WithFields(log.Fields{
"router_id": bnet.IPv4(routerID).String(),
}),
Expand Down Expand Up @@ -99,12 +102,12 @@ func (d *dummyListener) setTCPMD5(net.IP, string) error {
func (b *bgpServer) AddListener(l ...net.Listener) error {
for _, l := range l {
if ll, ok := l.(listener); ok {
b.listeners = append(b.listeners, ll)
b.addListeners <- ll
} else {
d := &dummyListener{
Listener: l,
logger: b.logger}
b.listeners = append(b.listeners, d)
b.addListeners <- d
}
}

Expand All @@ -126,28 +129,30 @@ func (b *bgpServer) AddListenerFromAddrString(addrs ...string) error {
return nil
}

func (b *bgpServer) Start() error {
if len(b.listeners) > 0 {
acceptCh := make(chan net.Conn, 4096)

for _, addr := range b.listeners {
go func(addr listener) {
for {
conn, err := addr.Accept()

if err != nil {
b.logger.Errorf("failed to accept connection: %v", err)
continue
}

acceptCh <- conn
}
}(addr)
func (b *bgpServer) accept(addr listener, acceptCh chan net.Conn) {
for {
conn, err := addr.Accept()

if err != nil {
b.logger.Errorf("failed to accept connection: %v", err)
continue
}
b.acceptCh = acceptCh

go b.incomingConnectionWorker()
acceptCh <- conn
}
}

func (b *bgpServer) Start() error {
b.acceptCh = make(chan net.Conn, 4096)

go b.incomingConnectionWorker()

go func() {
for addr := range b.addListeners {
go b.accept(addr, b.acceptCh)
b.acceptedListeners = append(b.acceptedListeners, addr)
}
}()

return nil
}
Expand Down Expand Up @@ -264,7 +269,7 @@ func (b *bgpServer) AddPeer(c PeerConfig) error {
}

if c.AuthenticationKey != "" {
for _, l := range b.listeners {
for _, l := range b.acceptedListeners {
err = l.setTCPMD5(c.PeerAddress.ToNetIP(), c.AuthenticationKey)
if err != nil {
return fmt.Errorf("unable to set TCP MD5 secret: %w", err)
Expand Down Expand Up @@ -298,6 +303,19 @@ func (b *bgpServer) GetPeerConfig(addr *bnet.IP) *PeerConfig {
return nil
}

// GetPeerStatus gets the status of a BGP peer by its address
func (b *bgpServer) GetPeerStatus(addr *bnet.IP) string {
p := b.peers.get(addr)
if p != nil {
p.fsms[0].stateMu.RLock()
defer p.fsms[0].stateMu.RUnlock()

return stateName(p.fsms[0].state)
}

return ""
}

func (b *bgpServer) DisposePeer(addr *bnet.IP) {
p := b.peers.get(addr)
if p == nil {
Expand Down
173 changes: 138 additions & 35 deletions protocols/bgp/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,32 @@ import (
"github.com/valyala/fasthttp/fasthttputil"

bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/packet"
bgpserver "github.com/bio-routing/bio-rd/protocols/bgp/server"
"github.com/bio-routing/bio-rd/protocols/bgp/types"
"github.com/bio-routing/bio-rd/route"
"github.com/bio-routing/bio-rd/routingtable"
"github.com/bio-routing/bio-rd/routingtable/filter"
"github.com/bio-routing/bio-rd/routingtable/vrf"
)

func TestServerInMemory(t *testing.T) {
ip1 := bnet.IPv4FromOctets(172, 17, 0, 3)
ip2 := bnet.IPv4FromOctets(169, 254, 200, 0)
func TestInMemoryServerMinimalConnection(t *testing.T) {
ip1 := bnet.IPv4FromOctets(172, 16, 0, 1)
as1 := uint32(65101)
ip2 := bnet.IPv4FromOctets(172, 16, 0, 2)
as2 := uint32(65102)

s1, ml1, vrfs1, err := startServer(ip1)
s1, vrfs1, err := startServer(ip1)
if err != nil {
t.Fatalf("Unable to start server 1: %v", err)
t.Fatalf("Unable to start server: %v", err)
}
s2, ml2, vrfs2, err := startServer(ip2)

s2, vrfs2, err := startServer(ip2)
if err != nil {
t.Fatalf("Unable to start server 2: %v", err)
t.Fatalf("Unable to start server: %v", err)
}

as1 := uint32(65100)
as2 := uint32(65101)
ml1 := addListener(s1, ip1)
ml2 := addListener(s2, ip2)

if err := s1.AddPeer(
bgpPeerConfig(s1, ip1.Ptr(), ip2.Ptr(), as1, as2,
Expand All @@ -54,49 +57,155 @@ func TestServerInMemory(t *testing.T) {
t.Fatalf("Unable to add peer: %v", err)
}

master := vrfs1.GetVRFByName("master")
locRIB := master.IPv4UnicastRIB()
// Wait for connection to be established
assert.Eventually(t, func() bool {
return s1.GetPeerStatus(ip2.Ptr()) == "established" && s2.GetPeerStatus(ip1.Ptr()) == "established"
}, time.Second*10, time.Millisecond*100, "Peer not established")

assert.Zero(t, s1.GetRIBIn(ip2.Ptr(), packet.AFIIPv4, packet.SAFIUnicast).RouteCount())
assert.Zero(t, s2.GetRIBIn(ip1.Ptr(), packet.AFIIPv4, packet.SAFIUnicast).RouteCount())
}

func TestInMemoryServerMinimalPaths(t *testing.T) {
ip1 := bnet.IPv4FromOctets(172, 16, 0, 1)
as1 := uint32(65101)
ip2 := bnet.IPv4FromOctets(172, 16, 0, 2)
as2 := uint32(65102)
ip3 := bnet.IPv4FromOctets(172, 16, 0, 3)
as3 := uint32(65103)

s1, vrfs1, err := startServer(ip1)
if err != nil {
t.Fatalf("Unable to start server: %v", err)
}

s2, vrfs2, err := startServer(ip2)
if err != nil {
t.Fatalf("Unable to start server: %v", err)
}

s3, vrfs3, err := startServer(ip3)
if err != nil {
t.Fatalf("Unable to start server: %v", err)
}

ml1 := addListener(s1, ip1)
ml2 := addListener(s2, ip2)
ml3 := addListener(s3, ip3)

if err := connect(s1, s2, ip1, ip2, vrfs1, vrfs2, as1, as2, ml1, ml2); err != nil {
t.Fatalf("Unable to connect s1 and s2: %v", err)
}

if err := connect(s2, s3, ip2, ip3, vrfs2, vrfs3, as2, as3, ml2, ml3); err != nil {
t.Fatalf("Unable to connect s2 and s3: %v", err)
}

pfx := bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8)

if err := locRIB.AddPath(pfx.Ptr(), generatePath(ip1.Ptr(), ip1.Ptr())); err != nil {
if err := vrfs1.GetVRFByName("master").IPv4UnicastRIB().AddPath(pfx.Ptr(), generatePath(ip1.Ptr(), ip1.Ptr())); err != nil {
t.Fatalf("Unable to add path: %v", err)
}

// Wait for connection to be established
assert.Eventually(t, func() bool {
return s1.GetPeerStatus(ip2.Ptr()) == "established" &&
s2.GetPeerStatus(ip1.Ptr()) == "established" &&
s2.GetPeerStatus(ip3.Ptr()) == "established" &&
s3.GetPeerStatus(ip2.Ptr()) == "established"
}, time.Second*10, time.Millisecond*100, "Peer not established")

assert.Eventually(t, func() bool {
routes := vrfs2.GetVRFByName("master").IPv4UnicastRIB().LPM(pfx.Ptr())

if len(routes) != 1 {
t.Logf("Expected 1 route, got %d", len(routes))

return false
}

if len(routes[0].Paths()) != 1 {
t.Logf("Expected 1 path, got %d", len(routes[0].Paths()))

return false
}

path := routes[0].Paths()[0]

return path.NextHop().Equal(ip1) &&
path.BGPPath.BGPPathA.LocalPref == 0 &&
path.BGPPath.BGPPathA.MED == 200
}, time.Second*15, time.Millisecond*100, "Route not received")
return path.NextHop().Equal(ip1)
}, time.Second*3, time.Millisecond*500, "Route not received")

assert.Eventually(t, func() bool {
routes := vrfs3.GetVRFByName("master").IPv4UnicastRIB().LPM(pfx.Ptr())

if len(routes) != 1 {
t.Logf("Expected 1 route, got %d", len(routes))

return false
}

if len(routes[0].Paths()) != 1 {
t.Logf("Expected 1 path, got %d", len(routes[0].Paths()))

return false
}

path := routes[0].Paths()[0]

return path.NextHop().Equal(ip2)
}, time.Second*3, time.Millisecond*500, "Route not received")
}

func startServer(routerID bnet.IP) (bgpserver.BGPServer, *vrf.VRFRegistry, error) {
s := bgpserver.NewBGPServer(uint32(routerID.Lower()))

if err := s.Start(); err != nil {
return nil, nil, fmt.Errorf("Unable to start BGP server: %v", err)
}

vrfs := vrf.NewVRFRegistry()

return s, vrfs, nil
}

func startServer(routerID bnet.IP) (bgpserver.BGPServer, *fasthttputil.InmemoryListener, *vrf.VRFRegistry, error) {
func addListener(s bgpserver.BGPServer, routerID bnet.IP) *fasthttputil.InmemoryListener {
ml := fasthttputil.NewInmemoryListener()
ml.SetLocalAddr(&net.TCPAddr{IP: routerID.ToNetIP()})

s := bgpserver.NewBGPServer(uint32(routerID.Lower()))

s.AddListener(ml)

if err := s.Start(); err != nil {
return nil, nil, nil, fmt.Errorf("Unable to start BGP server: %v", err)
return ml
}

func connect(r1, r2 bgpserver.BGPServer, ip1, ip2 bnet.IP, vrfs1, vrfs2 *vrf.VRFRegistry, as1, as2 uint32, ml1, ml2 *fasthttputil.InmemoryListener) error {
if err := r1.AddPeer(
bgpPeerConfig(r1,
bnet.IPv4FromBytes(ml1.Addr().(*net.TCPAddr).IP.To4()).Ptr(),
bnet.IPv4FromBytes(ml2.Addr().(*net.TCPAddr).IP.To4()).Ptr(),
as1,
as2,
vrfs1.CreateVRFIfNotExists("master", 0),
ml2,
),
); err != nil {
return fmt.Errorf("Unable to add peer: %v", err)
}

vrfs := vrf.NewVRFRegistry()
pc := bgpPeerConfig(r2,
bnet.IPv4FromBytes(ml2.Addr().(*net.TCPAddr).IP.To4()).Ptr(),
bnet.IPv4FromBytes(ml1.Addr().(*net.TCPAddr).IP.To4()).Ptr(),
as2,
as1,
vrfs2.CreateVRFIfNotExists("master", 0),
ml1,
)
// pc.Passive = true

return s, ml, vrfs, nil
if err := r2.AddPeer(pc); err != nil {
return fmt.Errorf("Unable to add peer: %v", err)
}

return nil
}

func bgpPeerConfig(s bgpserver.BGPServer, localAddr, peerAddr *bnet.IP, localAS, peerAS uint32, vrf *vrf.VRF, ml *fasthttputil.InmemoryListener) bgpserver.PeerConfig {
Expand All @@ -112,13 +221,9 @@ func bgpPeerConfig(s bgpserver.BGPServer, localAddr, peerAddr *bnet.IP, localAS,
IPv4: &bgpserver.AddressFamilyConfig{
ImportFilterChain: filter.NewAcceptAllFilterChain(),
ExportFilterChain: filter.NewAcceptAllFilterChain(),
AddPathSend: routingtable.ClientOptions{
MaxPaths: 10,
},
},
VRF: vrf,
TCPDialer: ml,
AuthenticationKey: "test",
VRF: vrf,
TCPDialer: ml,
}
}

Expand All @@ -127,11 +232,9 @@ func generatePath(source, nh *bnet.IP) *route.Path {
Type: route.BGPPathType,
BGPPath: &route.BGPPath{
BGPPathA: &route.BGPPathA{
Source: source,
NextHop: nh,
LocalPref: 100,
MED: 200,
EBGP: true,
Source: source,
NextHop: nh,
EBGP: true,
},
ASPath: &types.ASPath{
types.ASPathSegment{
Expand Down

0 comments on commit c8dd0e7

Please sign in to comment.