diff --git a/protocols/bgp/server/bmp_router.go b/protocols/bgp/server/bmp_router.go index 51a91eea..330b6b87 100644 --- a/protocols/bgp/server/bmp_router.go +++ b/protocols/bgp/server/bmp_router.go @@ -12,9 +12,12 @@ import ( log "github.com/sirupsen/logrus" bnet "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/protocols/bgp/api" "github.com/bio-routing/bio-rd/protocols/bgp/packet" bmppkt "github.com/bio-routing/bio-rd/protocols/bmp/packet" "github.com/bio-routing/bio-rd/routingtable" + "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/bio-routing/tflow2/convert" @@ -126,6 +129,48 @@ func (r *Router) serve(con net.Conn) { } } +// GetNeighborSessions returns all neighbors as API session objects +func (r *Router) GetNeighborSessions() []*api.Session { + sessions := make([]*api.Session, 0) + + for _, neigh := range r.neighborManager.list() { + estSince := neigh.fsm.establishedTime.Unix() + if estSince < 0 { + estSince = 0 + } + + // for now get this from adjRibIn/adjRibOut, can be replaced when we + // bmp gets its own bgpSrv or Router gets the bmpMetricsService + var routesReceived, routesSent uint64 + for _, afi := range []uint16{packet.IPv4AFI, packet.IPv6AFI} { + ribIn, err1 := r.GetNeighborRIBIn(neigh.fsm.peer.addr, afi, packet.UnicastSAFI) + if err1 == nil { + routesReceived += uint64(ribIn.RouteCount()) + } + + // adjRIBOut might not work properly with BMP, keeping it here for when it will + ribOut, err2 := r.GetNeighborRIBOut(neigh.fsm.peer.addr, afi, packet.UnicastSAFI) + if err2 == nil { + routesSent += uint64(ribOut.RouteCount()) + } + } + session := &api.Session{ + LocalAddress: neigh.fsm.peer.localAddr.ToProto(), + NeighborAddress: neigh.fsm.peer.addr.ToProto(), + LocalAsn: neigh.localAS, + PeerAsn: neigh.peerAS, + Status: stateToProto(neigh.fsm.state), + Stats: &api.SessionStats{ + RoutesReceived: routesReceived, + RoutesExported: routesSent, + }, + EstablishedSince: uint64(estSince), + } + sessions = append(sessions, session) + } + return sessions +} + func (r *Router) processMsg(msg []byte) { bmpMsg, err := bmppkt.Decode(msg) if err != nil { @@ -197,6 +242,48 @@ func (r *Router) processInitiationMsg(msg *bmppkt.InitiationMessage) { r.logger.Info(logMsg) } +func (r *Router) getNeighborAddressFamily(addr *bnet.IP, afi uint16, safi uint8) (*fsmAddressFamily, error) { + if safi != packet.UnicastSAFI { + return nil, fmt.Errorf("Unsupported safi, only unicast is supported") + } + + for _, neigh := range r.neighborManager.list() { + if *neigh.fsm.peer.addr == *addr { + af := neigh.fsm.addressFamily(afi, safi) + if af == nil { + return nil, fmt.Errorf("Address family not available") + } + return af, nil + } + } + + return nil, fmt.Errorf("Could not find neighbor with ip %s", addr.String()) +} + +// GetNeighborRIBIn returns the AdjRIBIn of a BMP neighbor +func (r *Router) GetNeighborRIBIn(addr *bnet.IP, afi uint16, safi uint8) (*adjRIBIn.AdjRIBIn, error) { + neighAF, err := r.getNeighborAddressFamily(addr, afi, safi) + if err != nil { + return nil, errors.Wrap(err, "Could not get RIBIn") + } + if neighAF.adjRIBIn == nil { + return nil, fmt.Errorf("RIBIn not available") + } + return neighAF.adjRIBIn.(*adjRIBIn.AdjRIBIn), nil +} + +// GetNeighborRIBOut returns the AdjRIBOut of a BMP neighbor +func (r *Router) GetNeighborRIBOut(addr *bnet.IP, afi uint16, safi uint8) (*adjRIBOut.AdjRIBOut, error) { + neighAF, err := r.getNeighborAddressFamily(addr, afi, safi) + if err != nil { + return nil, errors.Wrap(err, "Could not get RIBIn") + } + if neighAF.adjRIBOut == nil { + return nil, fmt.Errorf("RIBOut not available") + } + return neighAF.adjRIBOut.(*adjRIBOut.AdjRIBOut), nil +} + func (r *Router) processTerminationMsg(msg *bmppkt.TerminationMessage) { const ( stringType = 0 diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index 18017bbb..26f5daff 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -9,6 +9,7 @@ import ( "time" "github.com/bio-routing/bio-rd/net/tcp" + "github.com/bio-routing/bio-rd/protocols/bgp/api" "github.com/bio-routing/bio-rd/protocols/bgp/packet" "github.com/bio-routing/bio-rd/routingtable/filter" "github.com/pkg/errors" @@ -239,6 +240,27 @@ func stateName(s state) string { } } +func stateToProto(s state) api.Session_State { + switch s.(type) { + case *idleState: + return api.Session_Idle + case *connectState: + return api.Session_Connect + case *activeState: + return api.Session_Active + case *openSentState: + return api.Session_OpenSent + case *openConfirmState: + return api.Session_OpenConfirmed + case *establishedState: + return api.Session_Established + case *ceaseState: + return api.Session_Active // substitution + default: + panic(fmt.Sprintf("Unknown state: %v", s)) + } +} + func (fsm *FSM) cease() { fsm.eventCh <- Cease }