Skip to content

Commit

Permalink
Implement ListSessions of BGP API
Browse files Browse the repository at this point in the history
VRF filtering and flaps + routes imported in the stats are still missing
here, but listing sessions does work.
  • Loading branch information
sebageek committed May 24, 2020
1 parent bfe8627 commit a1a2d12
Show file tree
Hide file tree
Showing 3 changed files with 345 additions and 1 deletion.
24 changes: 24 additions & 0 deletions protocols/bgp/metrics/bgp_peer_metrics.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package metrics

import (
"fmt"
"time"

bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/protocols/bgp/api"
)

const (
Expand Down Expand Up @@ -48,3 +50,25 @@ type BGPPeerMetrics struct {
// AddressFamilies provides metrics on AFI/SAFI level
AddressFamilies []*BGPAddressFamilyMetrics
}

// GetStateAsProto returns the state of this peer to be used by the BGP API
func (m *BGPPeerMetrics) GetStateAsProto() api.Session_State {
switch m.State {
case StateDown:
return api.Session_Active // substitution
case StateIdle:
return api.Session_Idle
case StateConnect:
return api.Session_Connect
case StateActive:
return api.Session_Active
case StateOpenSent:
return api.Session_OpenSent
case StateOpenConfirm:
return api.Session_OpenConfirmed
case StateEstablished:
return api.Session_Established
default:
panic(fmt.Sprintf("Unknown state: %v", m.State))
}
}
65 changes: 64 additions & 1 deletion protocols/bgp/server/bgp_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"fmt"

"github.com/bio-routing/bio-rd/protocols/bgp/api"
"github.com/bio-routing/bio-rd/protocols/bgp/metrics"
"github.com/bio-routing/bio-rd/route"
"github.com/pkg/errors"

bnet "github.com/bio-routing/bio-rd/net"
routeapi "github.com/bio-routing/bio-rd/route/api"
Expand All @@ -22,8 +24,69 @@ func NewBGPAPIServer(s BGPServer) *BGPAPIServer {
}
}

// ListSessions lists all sessions the BGP server currently has
func (s *BGPAPIServer) ListSessions(ctx context.Context, in *api.ListSessionsRequest) (*api.ListSessionsResponse, error) {
return nil, fmt.Errorf("Not implemented yet")
bgpMetrics, err := s.srv.Metrics()
if err != nil {
return nil, errors.Wrap(err, "Could not get peer metrics")
}

sessions := make([]*api.Session, 0)
for _, peerIP := range s.srv.GetPeers() {
peer := s.srv.GetPeerConfig(peerIP)
if in.Filter != nil {
if in.Filter.NeighborIp != nil {
filterNeighbor := bnet.IPFromProtoIP(in.Filter.NeighborIp)
if *filterNeighbor != *peerIP {
continue
}
}
}

// find metrics for peer
var peerMetrics *metrics.BGPPeerMetrics
for _, peerMetricsEntry := range bgpMetrics.Peers {
if *peerMetricsEntry.IP == *peer.PeerAddress {
peerMetrics = peerMetricsEntry
break
}
}
if peerMetrics == nil {
return nil, fmt.Errorf("Could not find metrics for neighbor %s", peer.PeerAddress)
}

estSince := peerMetrics.Since.Unix()
if estSince < 0 {
// time not set, peer probably not up
estSince = 0
}
var routesReceived, routesSent uint64
for _, afiPeerMetrics := range peerMetrics.AddressFamilies {
routesReceived += afiPeerMetrics.RoutesReceived
routesSent += afiPeerMetrics.RoutesSent
}

session := &api.Session{
LocalAddress: peer.LocalAddress.ToProto(),
NeighborAddress: peer.PeerAddress.ToProto(),
LocalAsn: peer.LocalAS,
PeerAsn: peer.PeerAS,
Status: peerMetrics.GetStateAsProto(),
Stats: &api.SessionStats{
MessagesIn: peerMetrics.UpdatesReceived,
MessagesOut: peerMetrics.UpdatesSent,
RoutesReceived: routesReceived,
RoutesExported: routesSent,
},
EstablishedSince: uint64(estSince),
}
sessions = append(sessions, session)
}

resp := &api.ListSessionsResponse{
Sessions: sessions,
}
return resp, nil
}

// DumpRIBIn dumps the RIB in of a peer for a given AFI/SAFI
Expand Down
257 changes: 257 additions & 0 deletions protocols/bgp/server/bgp_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/bio-routing/bio-rd/routingtable/adjRIBIn"
"github.com/bio-routing/bio-rd/routingtable/adjRIBOut"
"github.com/bio-routing/bio-rd/routingtable/filter"
"github.com/bio-routing/bio-rd/routingtable/vrf"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
"google.golang.org/grpc/test/bufconn"
Expand Down Expand Up @@ -358,3 +359,259 @@ func TestDumpRIBInOut(t *testing.T) {
assert.Equal(t, expected, results, test.name)
}
}

func TestListSessions(t *testing.T) {
vrf, _ := vrf.New("inet.0", 0)
//establishedTime := time.Now()

tests := []struct {
name string
apisrv *BGPAPIServer
req *api.ListSessionsRequest
expected *api.ListSessionsResponse
wantFail bool
}{
{
name: "Simple ListSessions, without filter",
apisrv: &BGPAPIServer{
srv: &bgpServer{
peers: &peerManager{
peers: map[bnet.IP]*peer{
bnet.IPv4FromOctets(10, 0, 0, 0): {
config: &PeerConfig{
PeerAS: 65100,
LocalAS: 65000,
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).Ptr(),
PeerAddress: bnet.IPv4FromOctets(10, 0, 0, 0).Ptr(),
},
peerASN: 65100,
localASN: 65000,
addr: bnet.IPv4FromOctets(10, 0, 0, 0).Ptr(),
vrf: vrf,
},
},
},
},
},
req: &api.ListSessionsRequest{},
expected: &api.ListSessionsResponse{
Sessions: []*api.Session{
{
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).ToProto(),
NeighborAddress: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
PeerAsn: 65100,
LocalAsn: 65000,
Status: api.Session_Active,
Stats: &api.SessionStats{},
},
},
},
wantFail: false,
},
{
name: "ListSessions with two peers without filter",
apisrv: &BGPAPIServer{
srv: &bgpServer{
peers: &peerManager{
peers: map[bnet.IP]*peer{
bnet.IPv4FromOctets(10, 0, 0, 0): {
config: &PeerConfig{
PeerAS: 65100,
LocalAS: 65000,
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).Ptr(),
PeerAddress: bnet.IPv4FromOctets(10, 0, 0, 0).Ptr(),
},
peerASN: 65100,
localASN: 65000,
addr: bnet.IPv4FromOctets(10, 0, 0, 0).Ptr(),
vrf: vrf,
},
bnet.IPv4FromOctets(192, 168, 0, 0): {
config: &PeerConfig{
PeerAS: 64999,
LocalAS: 65000,
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).Ptr(),
PeerAddress: bnet.IPv4FromOctets(192, 168, 0, 0).Ptr(),
},
peerASN: 64999,
localASN: 65000,
addr: bnet.IPv4FromOctets(192, 168, 0, 0).Ptr(),
vrf: vrf,
},
},
},
},
},
req: &api.ListSessionsRequest{},
expected: &api.ListSessionsResponse{
Sessions: []*api.Session{
{
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).ToProto(),
NeighborAddress: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
PeerAsn: 65100,
LocalAsn: 65000,
Status: api.Session_Active,
Stats: &api.SessionStats{},
},
{
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).ToProto(),
NeighborAddress: bnet.IPv4FromOctets(192, 168, 0, 0).ToProto(),
PeerAsn: 64999,
LocalAsn: 65000,
Status: api.Session_Active,
Stats: &api.SessionStats{},
},
},
},
wantFail: false,
},
{
name: "ListSession with two peers and filter",
apisrv: &BGPAPIServer{
srv: &bgpServer{
peers: &peerManager{
peers: map[bnet.IP]*peer{
bnet.IPv4FromOctets(10, 0, 0, 0): {
config: &PeerConfig{
PeerAS: 65100,
LocalAS: 65000,
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).Ptr(),
PeerAddress: bnet.IPv4FromOctets(10, 0, 0, 0).Ptr(),
},
peerASN: 65100,
localASN: 65000,
addr: bnet.IPv4FromOctets(10, 0, 0, 0).Ptr(),
vrf: vrf,
},
bnet.IPv4FromOctets(192, 168, 0, 0): {
config: &PeerConfig{
PeerAS: 64999,
LocalAS: 65000,
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).Ptr(),
PeerAddress: bnet.IPv4FromOctets(192, 168, 0, 0).Ptr(),
},
peerASN: 64999,
localASN: 65000,
addr: bnet.IPv4FromOctets(192, 168, 0, 0).Ptr(),
vrf: vrf,
},
},
},
},
},
req: &api.ListSessionsRequest{
Filter: &api.SessionFilter{
NeighborIp: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
},
},
expected: &api.ListSessionsResponse{
Sessions: []*api.Session{
{
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).ToProto(),
NeighborAddress: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
PeerAsn: 65100,
LocalAsn: 65000,
Status: api.Session_Active,
Stats: &api.SessionStats{},
},
},
},
wantFail: false,
},
{
name: "ListSession with routes for stats",
apisrv: &BGPAPIServer{
srv: &bgpServer{
peers: &peerManager{
peers: map[bnet.IP]*peer{
bnet.IPv4FromOctets(10, 0, 0, 0): {
ipv4: &peerAddressFamily{},
ipv6: &peerAddressFamily{},
fsms: []*FSM{
0: {
ribsInitialized: true,
ipv4Unicast: &fsmAddressFamily{
adjRIBIn: &routingtable.RTMockClient{FakeRouteCount: 3},
adjRIBOut: &routingtable.RTMockClient{FakeRouteCount: 2},
},
ipv6Unicast: &fsmAddressFamily{
adjRIBIn: &routingtable.RTMockClient{FakeRouteCount: 10},
adjRIBOut: &routingtable.RTMockClient{FakeRouteCount: 12},
},
counters: fsmCounters{
updatesReceived: 23,
updatesSent: 42,
},
},
},
config: &PeerConfig{
PeerAS: 65100,
LocalAS: 65000,
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).Ptr(),
PeerAddress: bnet.IPv4FromOctets(10, 0, 0, 0).Ptr(),
},
peerASN: 65100,
localASN: 65000,
addr: bnet.IPv4FromOctets(10, 0, 0, 0).Ptr(),
vrf: vrf,
},
},
},
},
},
req: &api.ListSessionsRequest{},
expected: &api.ListSessionsResponse{
Sessions: []*api.Session{
{
LocalAddress: bnet.IPv4FromOctets(172, 0, 0, 0).ToProto(),
NeighborAddress: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
PeerAsn: 65100,
LocalAsn: 65000,
Status: api.Session_Active,
Stats: &api.SessionStats{
RoutesReceived: 13,
RoutesExported: 14,
MessagesIn: 23,
MessagesOut: 42,
},
},
},
},
wantFail: false,
},
}

for _, test := range tests {
testSrv := test.apisrv.srv.(*bgpServer)
testSrv.metrics = &metricsService{testSrv}
bufSize := 1024 * 1024
lis := bufconn.Listen(bufSize)
s := grpc.NewServer()
api.RegisterBgpServiceServer(s, test.apisrv)
go func() {
if err := s.Serve(lis); err != nil {
log.Fatalf("Server exited with error: %v", err)
}
}()

ctx := context.Background()
conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithDialer(func(string, time.Duration) (net.Conn, error) {
return lis.Dial()
}), grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to dial bufnet: %v", err)
}
defer conn.Close()

client := api.NewBgpServiceClient(conn)
neighborResp, err := client.ListSessions(ctx, test.req)
if err != nil {
t.Fatalf("ListSessions call failed: %v", err)
}
assert.Equal(t, test.expected, neighborResp)
}

// As tests seem to share state we need to clean up the vrf here
vrf.Unregister()
vrf.Dispose()
}

0 comments on commit a1a2d12

Please sign in to comment.