Skip to content
Snippets Groups Projects
bgp_api_test.go 8.8 KiB
Newer Older
package server

import (
	"context"
	"log"
	"net"
	"testing"
	"time"

	"github.com/bio-routing/bio-rd/protocols/bgp/api"
	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
	"github.com/bio-routing/bio-rd/protocols/bgp/types"
	"github.com/bio-routing/bio-rd/route"
	routeapi "github.com/bio-routing/bio-rd/route/api"
	"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/stretchr/testify/assert"
	"google.golang.org/grpc"
	"google.golang.org/grpc/test/bufconn"

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

func TestDumpRIBInOut(t *testing.T) {
	tests := []struct {
		name      string
		apisrv    *BGPAPIServer
		addRoutes []*route.Route
		req       *api.DumpRIBRequest
		expected  []*routeapi.Route
		wantFail  bool
	}{
		{
			name: "Test #0: Non existent peer",
			apisrv: &BGPAPIServer{
				srv: &bgpServer{
					peers: &peerManager{
						peers: map[bnet.IP]*peer{},
					},
				},
			},
			addRoutes: []*route.Route{},
			req: &api.DumpRIBRequest{
				Peer: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
				Afi:  packet.IPv4AFI,
				Safi: packet.UnicastSAFI,
			},
			expected: []*routeapi.Route{},
			wantFail: false,
		},
		{
			name: "Test #1: No routes given",
			apisrv: &BGPAPIServer{
				srv: &bgpServer{
					peers: &peerManager{
						peers: map[bnet.IP]*peer{
							bnet.IPv4FromOctets(10, 0, 0, 0): {
								fsms: []*FSM{
									0: {
										ipv4Unicast: &fsmAddressFamily{
											adjRIBIn:  adjRIBIn.New(filter.NewAcceptAllFilter(), nil, 0, 0, true),
											adjRIBOut: adjRIBOut.New(&routingtable.Neighbor{Type: route.BGPPathType}, filter.NewAcceptAllFilter(), true),
										},
									},
								},
							},
						},
					},
				},
			},
			addRoutes: []*route.Route{},
			req: &api.DumpRIBRequest{
				Peer: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
				Afi:  packet.IPv4AFI,
				Safi: packet.UnicastSAFI,
			},
			expected: []*routeapi.Route{},
			wantFail: false,
		},
		{
			name: "Test #2: One simple routes given",
			apisrv: &BGPAPIServer{
				srv: &bgpServer{
					peers: &peerManager{
						peers: map[bnet.IP]*peer{
							bnet.IPv4FromOctets(10, 0, 0, 0): {
								fsms: []*FSM{
									0: {
										ipv4Unicast: &fsmAddressFamily{
											adjRIBIn:  adjRIBIn.New(filter.NewAcceptAllFilter(), nil, 0, 0, true),
											adjRIBOut: adjRIBOut.New(&routingtable.Neighbor{Type: route.BGPPathType, RouteServerClient: true}, filter.NewAcceptAllFilter(), false),
										},
									},
								},
							},
						},
					},
				},
			},
			addRoutes: []*route.Route{
				route.NewRoute(bnet.NewPfx(bnet.IPv4FromOctets(20, 0, 0, 0), 16), &route.Path{
					Type: route.BGPPathType,
					BGPPath: &route.BGPPath{
						OriginatorID: 1,
						NextHop:      bnet.IPv4FromOctets(100, 100, 100, 100),
						Source:       bnet.IPv4FromOctets(100, 100, 100, 100),
					},
				}),
			},
			req: &api.DumpRIBRequest{
				Peer: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
				Afi:  packet.IPv4AFI,
				Safi: packet.UnicastSAFI,
			},
			expected: []*routeapi.Route{
				{
					Pfx: bnet.NewPfx(bnet.IPv4FromOctets(20, 0, 0, 0), 16).ToProto(),
					Paths: []*routeapi.Path{
						{
							Type: routeapi.Path_BGP,
takt's avatar
takt committed
							BgpPath: &routeapi.BGPPath{
								OriginatorId:      1,
								NextHop:           bnet.IPv4FromOctets(100, 100, 100, 100).ToProto(),
								Source:            bnet.IPv4FromOctets(100, 100, 100, 100).ToProto(),
takt's avatar
takt committed
								AsPath:            nil,
								Communities:       nil,
								LargeCommunities:  nil,
								UnknownAttributes: nil,
								ClusterList:       nil,
							},
						},
					},
				},
			},
			wantFail: false,
		},
		{
			name: "Test #3: One complex routes given",
			apisrv: &BGPAPIServer{
				srv: &bgpServer{
					peers: &peerManager{
						peers: map[bnet.IP]*peer{
							bnet.IPv4FromOctets(10, 0, 0, 0): {
								fsms: []*FSM{
									0: {
										ipv4Unicast: &fsmAddressFamily{
											adjRIBIn:  adjRIBIn.New(filter.NewAcceptAllFilter(), routingtable.NewContributingASNs(), 0, 0, true),
											adjRIBOut: adjRIBOut.New(&routingtable.Neighbor{Type: route.BGPPathType, RouteServerClient: true}, filter.NewAcceptAllFilter(), false),
										},
									},
								},
							},
						},
					},
				},
			},
			addRoutes: []*route.Route{
				route.NewRoute(bnet.NewPfx(bnet.IPv4FromOctets(20, 0, 0, 0), 16), &route.Path{
					Type: route.BGPPathType,
					BGPPath: &route.BGPPath{
						OriginatorID: 1,
						NextHop:      bnet.IPv4FromOctets(100, 100, 100, 100),
						Source:       bnet.IPv4FromOctets(100, 100, 100, 100),
						ASPath: types.ASPath{
							types.ASPathSegment{
								Type: types.ASSequence,
								ASNs: []uint32{15169, 3320},
							},
						},
						Communities: []uint32{100, 200, 300},
						LargeCommunities: []types.LargeCommunity{
							{
								GlobalAdministrator: 1,
								DataPart1:           2,
								DataPart2:           3,
							},
						},
						LocalPref: 1000,
						MED:       2000,
						UnknownAttributes: []types.UnknownPathAttribute{
							{
								Optional:   true,
								Transitive: true,
								Partial:    true,
								TypeCode:   222,
								Value:      []byte{0xff, 0xff},
							},
						},
						ClusterList: []uint32{},
					},
				}),
			},
			req: &api.DumpRIBRequest{
				Peer: bnet.IPv4FromOctets(10, 0, 0, 0).ToProto(),
				Afi:  packet.IPv4AFI,
				Safi: packet.UnicastSAFI,
			},
			expected: []*routeapi.Route{
				{
					Pfx: bnet.NewPfx(bnet.IPv4FromOctets(20, 0, 0, 0), 16).ToProto(),
					Paths: []*routeapi.Path{
						{
							Type: routeapi.Path_BGP,
takt's avatar
takt committed
							BgpPath: &routeapi.BGPPath{
								OriginatorId: 1,
								LocalPref:    1000,
takt's avatar
takt committed
								Med:          2000,
								NextHop:      bnet.IPv4FromOctets(100, 100, 100, 100).ToProto(),
								Source:       bnet.IPv4FromOctets(100, 100, 100, 100).ToProto(),
takt's avatar
takt committed
								AsPath: []*routeapi.ASPathSegment{
takt's avatar
takt committed
										AsSequence: true,
										Asns:       []uint32{15169, 3320},
									},
								},
								Communities: []uint32{100, 200, 300},
								LargeCommunities: []*routeapi.LargeCommunity{
									{
										GlobalAdministrator: 1,
										DataPart1:           2,
										DataPart2:           3,
									},
								},
								ClusterList: nil,
								UnknownAttributes: []*routeapi.UnknownPathAttribute{
									{
										Optional:   true,
										Transitive: true,
										Partial:    true,
										TypeCode:   222,
										Value:      []byte{0xff, 0xff},
									},
								},
							},
						},
					},
				},
			},
			wantFail: false,
		},
	}

	// Test RIBin
	for _, test := range tests {
		for _, r := range test.addRoutes {
			for _, p := range r.Paths() {
				test.apisrv.srv.(*bgpServer).peers.peers[bnet.IPv4FromOctets(10, 0, 0, 0)].fsms[0].ipv4Unicast.adjRIBIn.AddPath(r.Prefix(), p)
			}
		}

		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)
		streamClient, err := client.DumpRIBIn(ctx, test.req)
		if err != nil {
			t.Fatalf("AdjRIBInStream client call failed: %v", err)
		}

		res := make([]*routeapi.Route, 0)
		for {
			r, err := streamClient.Recv()
			if err != nil {
				break
			}

			res = append(res, r)
		}

		assert.Equal(t, test.expected, res, test.name)
	}

	// Test RIBout
	for _, test := range tests {
		for _, r := range test.addRoutes {
			for _, p := range r.Paths() {
				test.apisrv.srv.(*bgpServer).peers.peers[bnet.IPv4FromOctets(10, 0, 0, 0)].fsms[0].ipv4Unicast.adjRIBOut.AddPath(r.Prefix(), p)
			}
		}

		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)
		streamClient, err := client.DumpRIBOut(ctx, test.req)
		if err != nil {
			t.Fatalf("AdjRIBInStream client call failed: %v", err)
		}

		res := make([]*routeapi.Route, 0)
		for {
			r, err := streamClient.Recv()
			if err != nil {
				break
			}

			res = append(res, r)
		}

		assert.Equal(t, test.expected, res, test.name)
	}
}