diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index 1527f8cf6e7d3d3ff9365068272aa3fad52e41d0..248b731714389096a62e2381a548780beb8318e7 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -1580,6 +1580,212 @@ func TestSerialize(t *testing.T) { } } +func TestSerializeAddPath(t *testing.T) { + tests := []struct { + name string + msg *BGPUpdateAddPath + expected []byte + wantFail bool + }{ + { + name: "Withdraw only", + msg: &BGPUpdateAddPath{ + WithdrawnRoutes: &NLRIAddPath{ + PathIdentifier: 257, + IP: strAddr("100.110.120.0"), + Pfxlen: 24, + }, + }, + expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 31, // Length + 2, // Msg Type + 0, 8, // Withdrawn Routes Length + 0, 0, 1, 1, // Path Identifier + 24, 100, 110, 120, // NLRI + 0, 0, // Total Path Attribute Length + }, + }, + { + name: "NLRI only", + msg: &BGPUpdateAddPath{ + NLRI: &NLRIAddPath{ + PathIdentifier: 257, + IP: strAddr("100.110.128.0"), + Pfxlen: 17, + }, + }, + expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 31, // Length + 2, // Msg Type + 0, 0, // Withdrawn Routes Length + 0, 0, // Total Path Attribute Length + 0, 0, 1, 1, // Path Identifier + 17, 100, 110, 128, // NLRI + }, + }, + { + name: "Path Attributes only", + msg: &BGPUpdateAddPath{ + PathAttributes: &PathAttribute{ + Optional: true, + Transitive: true, + TypeCode: OriginAttr, + Value: uint8(0), // IGP + }, + }, + expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 27, // Length + 2, // Msg Type + 0, 0, // Withdrawn Routes Length + 0, 4, // Total Path Attribute Length + 64, // Attr. Flags + 1, // Attr. Type Code + 1, // Length + 0, // Value + }, + }, + { + name: "Full test", + msg: &BGPUpdateAddPath{ + WithdrawnRoutes: &NLRIAddPath{ + IP: strAddr("10.0.0.0"), + Pfxlen: 8, + Next: &NLRIAddPath{ + IP: strAddr("192.168.0.0"), + Pfxlen: 16, + }, + }, + PathAttributes: &PathAttribute{ + TypeCode: OriginAttr, + Value: uint8(0), + Next: &PathAttribute{ + TypeCode: ASPathAttr, + Value: ASPath{ + { + Type: 2, + ASNs: []uint32{100, 155, 200}, + }, + { + Type: 1, + ASNs: []uint32{10, 20}, + }, + }, + Next: &PathAttribute{ + TypeCode: NextHopAttr, + Value: strAddr("10.20.30.40"), + Next: &PathAttribute{ + TypeCode: MEDAttr, + Value: uint32(100), + Next: &PathAttribute{ + TypeCode: LocalPrefAttr, + Value: uint32(500), + Next: &PathAttribute{ + TypeCode: AtomicAggrAttr, + Next: &PathAttribute{ + TypeCode: AggregatorAttr, + Value: uint16(200), + }, + }, + }, + }, + }, + }, + }, + NLRI: &NLRIAddPath{ + IP: strAddr("8.8.8.0"), + Pfxlen: 24, + Next: &NLRIAddPath{ + IP: strAddr("185.65.240.0"), + Pfxlen: 22, + }, + }, + }, + expected: []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 102, // Length + 2, // Msg Type + + // Withdraws + 0, 13, // Withdrawn Routes Length + 0, 0, 0, 0, // Path Identifier + 8, 10, // Withdraw 10/8 + 0, 0, 0, 0, // Path Identifier + 16, 192, 168, // Withdraw 192.168/16 + + 0, 50, // Total Path Attribute Length + + // ORIGIN + 64, // Attr. Flags + 1, // Attr. Type Code + 1, // Length + 0, // Value + // ASPath + 64, // Attr. Flags + 2, // Attr. Type Code + 14, // Attr. Length + 2, // Path Segment Type = AS_SEQUENCE + 3, // Path Segment Length + 0, 100, 0, 155, 0, 200, // ASNs + 1, // Path Segment Type = AS_SET + 2, // Path Segment Type = AS_SET + 0, 10, 0, 20, // ASNs + // Next Hop + 64, // Attr. Flags + 3, // Attr. Type Code + 4, // Length + 10, 20, 30, 40, // Next Hop Address + // MED + 128, // Attr. Flags + 4, // Attr Type Code + 4, // Length + 0, 0, 0, 100, // MED = 100 + // LocalPref + 64, // Attr. Flags + 5, // Attr. Type Code + 4, // Length + 0, 0, 1, 244, // Localpref + // Atomic Aggregate + 64, // Attr. Flags + 6, // Attr. Type Code + 0, // Length + // Aggregator + 192, // Attr. Flags + 7, // Attr. Type Code + 2, // Length + 0, 200, // Aggregator ASN = 200 + + // NLRI + 0, 0, 0, 0, // Path Identifier + 24, 8, 8, 8, // 8.8.8.0/24 + 0, 0, 0, 0, // Path Identifier + 22, 185, 65, 240, // 185.65.240.0/22 + }, + }, + } + + for _, test := range tests { + res, err := test.msg.SerializeUpdate() + if err != nil { + if test.wantFail { + continue + } + + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + continue + } + + if test.wantFail { + t.Errorf("Unexpected success for test %q", test.name) + continue + } + + assert.Equalf(t, test.expected, res, "%s", test.name) + } +} + func TestParseASPathStr(t *testing.T) { tests := []struct { name string diff --git a/routingtable/table_test.go b/routingtable/table_test.go index a2eb10f76f93046fe71e98e59998ff52e15283b5..ef18c5175533a649504f4d6ade2a43edcda102ff 100644 --- a/routingtable/table_test.go +++ b/routingtable/table_test.go @@ -262,6 +262,12 @@ func TestGetLonger(t *testing.T) { route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 12), nil), }, }, + { + name: "Test 2: Empty root", + routes: nil, + needle: net.NewPfx(strAddr("10.0.0.0"), 8), + expected: []*route.Route{}, + }, } for _, test := range tests { @@ -445,6 +451,372 @@ func TestRemovePath(t *testing.T) { } } +func TestReplacePath(t *testing.T) { + tests := []struct { + name string + routes []*route.Route + replacePfx net.Prefix + replacePath *route.Path + expected []*route.Route + expectedOld []*route.Path + }{ + { + name: "replace in empty table", + replacePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + replacePath: &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{}, + }), + }, + expectedOld: nil, + }, + { + name: "replace not existing prefix with multiple paths", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1001, + NextHop: 101, + }, + }), + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + replacePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + replacePath: &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1000, + }, + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1000, + }, + }), + newMultiPathRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1001, + NextHop: 101, + }, + }, &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: []*route.Path{}, + }, + { + name: "replace existing prefix with multiple paths", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 2, + }, + }), + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1001, + NextHop: 101, + }, + }), + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + replacePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + replacePath: &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1000, + }, + }, + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1000, + }, + }), + newMultiPathRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1001, + NextHop: 101, + }, + }, &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: []*route.Path{ + { + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }, + { + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 2, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + rt := NewRoutingTable() + for _, route := range test.routes { + for _, p := range route.Paths() { + rt.AddPath(route.Prefix(), p) + } + } + + old := rt.ReplacePath(test.replacePfx, test.replacePath) + assert.ElementsMatch(t, test.expectedOld, old) + assert.ElementsMatch(t, test.expected, rt.Dump()) + }) + + } +} + +func TestRemovePrefix(t *testing.T) { + tests := []struct { + name string + routes []*route.Route + removePfx net.Prefix + expected []*route.Route + expectedOld []*route.Path + }{ + { + name: "remove in empty table", + removePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + expected: []*route.Route{}, + expectedOld: nil, + }, + { + name: "remove not exist prefix", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + removePfx: net.NewPfx(strAddr("12.0.0.0"), 8), + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: nil, + }, + { + name: "remove not existing more specific prefix", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + removePfx: net.NewPfx(strAddr("10.0.0.0"), 9), + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: nil, + }, + { + name: "remove not existing more less prefix", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + removePfx: net.NewPfx(strAddr("10.0.0.0"), 7), + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: nil, + }, + { + name: "remove existing prefix", + routes: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }), + route.NewRoute(net.NewPfx(strAddr("10.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 2, + }, + }), + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + removePfx: net.NewPfx(strAddr("10.0.0.0"), 8), + expected: []*route.Route{ + route.NewRoute(net.NewPfx(strAddr("11.0.0.0"), 8), &route.Path{ + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1002, + NextHop: 100, + }, + }), + }, + expectedOld: []*route.Path{ + { + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 1, + }, + }, + { + Type: route.BGPPathType, + BGPPath: &route.BGPPath{ + LocalPref: 2, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + rt := NewRoutingTable() + for _, route := range test.routes { + for _, p := range route.Paths() { + rt.AddPath(route.Prefix(), p) + } + } + + old := rt.RemovePfx(test.removePfx) + assert.ElementsMatch(t, test.expectedOld, old) + assert.ElementsMatch(t, test.expected, rt.Dump()) + }) + + } +} + +func newMultiPathRoute(pfx net.Prefix, paths ...*route.Path) *route.Route { + if len(paths) == 0 { + return route.NewRoute(pfx, nil) + } + r := route.NewRoute(pfx, paths[0]) + for i := 1; i < len(paths); i++ { + r.AddPath(paths[i]) + } + return r + +} + func strAddr(s string) uint32 { ret, _ := net.StrToAddr(s) return ret