Skip to content

Commit

Permalink
Implement ListSessions of BGP API
Browse files Browse the repository at this point in the history
VRF filtering and some session stats are still missing here, but listing
sessions does work.
  • Loading branch information
sebageek committed May 24, 2020
1 parent bfe8627 commit 7f74a99
Show file tree
Hide file tree
Showing 3 changed files with 277 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))
}
}
58 changes: 57 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,62 @@ 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
}

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,
},
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
196 changes: 196 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,198 @@ 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,
},
}

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 7f74a99

Please sign in to comment.