2022-11-25 14:05:48 +01:00

227 lines
5.7 KiB
Go

package gobgp
import (
"context"
"fmt"
"io"
"log"
"time"
gobgpapi "github.com/osrg/gobgp/api"
"github.com/osrg/gobgp/pkg/packet/bgp"
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/alice-lg/alice-lg/pkg/pools"
"github.com/alice-lg/alice-lg/pkg/sources/gobgp/apiutil"
)
var families []gobgpapi.Family = []gobgpapi.Family{{
Afi: gobgpapi.Family_AFI_IP,
Safi: gobgpapi.Family_SAFI_UNICAST,
}, {
Afi: gobgpapi.Family_AFI_IP6,
Safi: gobgpapi.Family_SAFI_UNICAST,
},
}
// NewRoutesResponse creates a new routes response
func NewRoutesResponse() api.RoutesResponse {
routes := api.RoutesResponse{}
routes.Imported = make(api.Routes, 0)
routes.Filtered = make(api.Routes, 0)
routes.NotExported = make(api.Routes, 0)
return routes
}
func (gobgp *GoBGP) lookupNeighbor(
ctx context.Context,
neighborID string,
) (*gobgpapi.Peer, error) {
peers, err := gobgp.GetNeighbors(ctx)
if err != nil {
return nil, err
}
for _, peer := range peers {
peerID := PeerHash(peer)
if neighborID == "" || peerID == neighborID {
return peer, nil
}
}
return nil, fmt.Errorf("could not lookup neighbor")
}
// GetNeighbors retrievs all neighbors and returns
// a list of peers.
func (gobgp *GoBGP) GetNeighbors(
ctx context.Context,
) ([]*gobgpapi.Peer, error) {
ctx, cancel := context.WithTimeout(
ctx, time.Second*time.Duration(gobgp.config.ProcessingTimeout))
defer cancel()
peerStream, err := gobgp.client.ListPeer(
ctx, &gobgpapi.ListPeerRequest{EnableAdvertised: true})
if err != nil {
return nil, err
}
peers := make([]*gobgpapi.Peer, 0)
for {
peer, err := peerStream.Recv()
if err == io.EOF {
break
}
peers = append(peers, peer.Peer)
}
return peers, nil
}
func (gobgp *GoBGP) parsePathIntoRoute(
path *gobgpapi.Path,
prefix string,
) (*api.Route, error) {
route := api.Route{}
// route.ID = fmt.Sprintf("%s_%s", path.SourceId, prefix)
route.NeighborID = pools.Neighbors.Acquire(
PeerHashWithASAndAddress(path.SourceAsn, path.NeighborIp))
route.Network = prefix
route.Interface = pools.Interfaces.Acquire("unknown")
route.Age = time.Since(time.Unix(path.Age.GetSeconds(), int64(path.Age.GetNanos())))
route.Primary = path.Best
attrs, err := apiutil.GetNativePathAttributes(path)
if err != nil {
return nil, err
}
route.BGP = &api.BGPInfo{}
route.BGP.Communities = make(api.Communities, 0)
route.BGP.LargeCommunities = make(api.Communities, 0)
route.BGP.ExtCommunities = make(api.ExtCommunities, 0)
for _, attr := range attrs {
switch attr := attr.(type) {
case *bgp.PathAttributeMultiExitDisc:
route.BGP.Med = int(attr.Value)
case *bgp.PathAttributeNextHop:
route.Gateway = pools.Gateways4.Acquire(attr.Value.String())
route.BGP.NextHop = pools.Gateways4.Acquire(attr.Value.String())
case *bgp.PathAttributeLocalPref:
route.BGP.LocalPref = int(attr.Value)
case *bgp.PathAttributeOrigin:
switch attr.Value {
case bgp.BGP_ORIGIN_ATTR_TYPE_IGP:
route.BGP.Origin = pools.Origins.Acquire("IGP")
case bgp.BGP_ORIGIN_ATTR_TYPE_EGP:
route.BGP.Origin = pools.Origins.Acquire("EGP")
case bgp.BGP_ORIGIN_ATTR_TYPE_INCOMPLETE:
route.BGP.Origin = pools.Origins.Acquire("Incomplete")
}
case *bgp.PathAttributeAsPath:
for _, aspth := range attr.Value {
for _, as := range aspth.GetAS() {
route.BGP.AsPath = append(route.BGP.AsPath, int(as))
}
}
case *bgp.PathAttributeCommunities:
for _, community := range attr.Value {
apiComm := api.Community{
int((0xffff0000 & community) >> 16),
int(0xffff & community)}
route.BGP.Communities = append(route.BGP.Communities, apiComm)
}
case *bgp.PathAttributeExtendedCommunities:
for _, community := range attr.Value {
if apiComm, ok := community.(*bgp.TwoOctetAsSpecificExtended); ok {
route.BGP.ExtCommunities = append(
route.BGP.ExtCommunities,
api.ExtCommunity{
apiComm.AS,
apiComm.LocalAdmin})
}
}
case *bgp.PathAttributeLargeCommunities:
for _, community := range attr.Values {
route.BGP.LargeCommunities = append(
route.BGP.LargeCommunities,
api.Community{
int(community.ASN),
int(community.LocalData1),
int(community.LocalData2)})
}
}
}
route.BGP.AsPath = pools.ASPaths.Acquire(route.BGP.AsPath)
route.BGP.Communities = pools.Communities.Acquire(route.BGP.Communities)
route.BGP.ExtCommunities = pools.ExtCommunities.AcquireExt(route.BGP.ExtCommunities)
route.BGP.LargeCommunities = pools.LargeCommunities.Acquire(route.BGP.LargeCommunities)
route.Metric = (route.BGP.LocalPref + route.BGP.Med)
return &route, nil
}
// GetRoutes retrieves all routes from a peer
// for a table type.
func (gobgp *GoBGP) GetRoutes(
ctx context.Context,
peer *gobgpapi.Peer,
tableType gobgpapi.TableType,
response *api.RoutesResponse,
) error {
ctx, cancel := context.WithTimeout(
ctx, time.Second*time.Duration(gobgp.config.ProcessingTimeout))
defer cancel()
for _, family := range families {
pathStream, err := gobgp.client.ListPath(ctx, &gobgpapi.ListPathRequest{
Name: peer.State.NeighborAddress,
TableType: tableType,
Family: &family,
EnableFiltered: true,
})
if err != nil {
log.Print(err)
continue
}
rib := make([]*gobgpapi.Destination, 0)
for {
_path, err := pathStream.Recv()
if err == io.EOF {
break
} else if err != nil {
log.Print(err)
return err
}
rib = append(rib, _path.Destination)
}
for _, destination := range rib {
for _, path := range destination.Paths {
route, err := gobgp.parsePathIntoRoute(path, destination.Prefix)
if err != nil {
log.Println(err)
continue
}
if path.Filtered {
response.Filtered = append(response.Filtered, route)
} else {
response.Imported = append(response.Imported, route)
}
}
}
}
return nil
}