diff --git a/routingtable/filter/filter_test.go b/routingtable/filter/filter_test.go index 8b33f125e0824a1e44b5dad7f4b8daf9ec323247..ce9b733e08a0b32959eb23b3230ffeb7cb5c3041 100644 --- a/routingtable/filter/filter_test.go +++ b/routingtable/filter/filter_test.go @@ -54,7 +54,7 @@ func TestAddPath(t *testing.T) { prefix: net.NewPfx(0, 0), path: &route.Path{}, term: &Term{ - then: []Then{ + then: []FilterAction{ &actions.AcceptAction{}, }, }, @@ -66,7 +66,7 @@ func TestAddPath(t *testing.T) { prefix: net.NewPfx(0, 0), path: &route.Path{}, term: &Term{ - then: []Then{ + then: []FilterAction{ &actions.RejectAction{}, }, }, @@ -78,7 +78,7 @@ func TestAddPath(t *testing.T) { prefix: net.NewPfx(0, 0), path: &route.Path{}, term: &Term{ - then: []Then{ + then: []FilterAction{ &mockAction{}, &actions.AcceptAction{}, }, @@ -127,7 +127,7 @@ func TestRemovePath(t *testing.T) { prefix: net.NewPfx(0, 0), path: &route.Path{}, term: &Term{ - then: []Then{ + then: []FilterAction{ &actions.AcceptAction{}, }, }, @@ -139,7 +139,7 @@ func TestRemovePath(t *testing.T) { prefix: net.NewPfx(0, 0), path: &route.Path{}, term: &Term{ - then: []Then{ + then: []FilterAction{ &actions.RejectAction{}, }, }, @@ -151,7 +151,7 @@ func TestRemovePath(t *testing.T) { prefix: net.NewPfx(0, 0), path: &route.Path{}, term: &Term{ - then: []Then{ + then: []FilterAction{ &mockAction{}, &actions.AcceptAction{}, }, diff --git a/routingtable/filter/from.go b/routingtable/filter/from.go deleted file mode 100644 index 2237fe4ea9fcc61e65f2ec8c59a400e619fc092f..0000000000000000000000000000000000000000 --- a/routingtable/filter/from.go +++ /dev/null @@ -1,14 +0,0 @@ -package filter - -import ( - "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/route" -) - -type From struct { - prefixList *PrefixList -} - -func (f *From) Matches(p net.Prefix, pa *route.Path) bool { - return f.prefixList.Matches(p) -} diff --git a/routingtable/filter/prefix_list.go b/routingtable/filter/prefix_list.go index b783a07a540b67acc7fd3384b582490f0bb5aea2..91668160879d5416265ffe5304cf612c753f0819 100644 --- a/routingtable/filter/prefix_list.go +++ b/routingtable/filter/prefix_list.go @@ -4,14 +4,31 @@ import "github.com/bio-routing/bio-rd/net" type PrefixList struct { allowed []net.Prefix + matcher PrefixMatcher } -func (f *PrefixList) Matches(p net.Prefix) bool { - for _, a := range f.allowed { - if !a.Contains(p) { - return false +func NewPrefixList(pfxs ...net.Prefix) *PrefixList { + l := &PrefixList{ + allowed: pfxs, + matcher: Exact(), + } + return l +} + +func NewPrefixListWithMatcher(matcher PrefixMatcher, pfxs ...net.Prefix) *PrefixList { + l := &PrefixList{ + allowed: pfxs, + matcher: matcher, + } + return l +} + +func (l *PrefixList) Matches(p net.Prefix) bool { + for _, a := range l.allowed { + if a.Equal(p) { + return true } } - return true + return false } diff --git a/routingtable/filter/prefix_matcher.go b/routingtable/filter/prefix_matcher.go new file mode 100644 index 0000000000000000000000000000000000000000..8b6136b57bfadfe744cf151bae7454e5adb6f935 --- /dev/null +++ b/routingtable/filter/prefix_matcher.go @@ -0,0 +1,30 @@ +package filter + +import "github.com/bio-routing/bio-rd/net" + +type PrefixMatcher func(pattern, prefix net.Prefix) bool + +func InRange(min, max uint8) PrefixMatcher { + return func(pattern, prefix net.Prefix) bool { + contains := pattern.Equal(prefix) || pattern.Contains(prefix) + return contains && prefix.Pfxlen() >= min && prefix.Pfxlen() <= max + } +} + +func Exact() PrefixMatcher { + return func(pattern, prefix net.Prefix) bool { + return pattern.Equal(prefix) + } +} + +func OrLonger() PrefixMatcher { + return func(pattern, prefix net.Prefix) bool { + return pattern.Equal(prefix) || pattern.Contains(prefix) + } +} + +func Longer() PrefixMatcher { + return func(pattern, prefix net.Prefix) bool { + return pattern.Contains(prefix) && prefix.Pfxlen() > pattern.Pfxlen() + } +} diff --git a/routingtable/filter/route_filter.go b/routingtable/filter/route_filter.go new file mode 100644 index 0000000000000000000000000000000000000000..d6716b5100166e755cc0950e7ba9bc6e8f0e1ae6 --- /dev/null +++ b/routingtable/filter/route_filter.go @@ -0,0 +1,21 @@ +package filter + +import ( + "github.com/bio-routing/bio-rd/net" +) + +type RouteFilter struct { + pattern net.Prefix + matcher PrefixMatcher +} + +func NewRouteFilter(pattern net.Prefix, matcher PrefixMatcher) *RouteFilter { + return &RouteFilter{ + pattern: pattern, + matcher: matcher, + } +} + +func (f *RouteFilter) Matches(prefix net.Prefix) bool { + return f.matcher(f.pattern, prefix) +} diff --git a/routingtable/filter/route_filter_test.go b/routingtable/filter/route_filter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7595df4dda19f8b4af5b668f972119878f5ab758 --- /dev/null +++ b/routingtable/filter/route_filter_test.go @@ -0,0 +1,198 @@ +package filter + +import ( + "testing" + + "github.com/bio-routing/bio-rd/net" + "github.com/stretchr/testify/assert" +) + +func TestInRange(t *testing.T) { + tests := []struct { + name string + prefix net.Prefix + pattern net.Prefix + begin uint8 + end uint8 + expected bool + }{ + { + name: "matches and in range (22-24)", + prefix: net.NewPfx(strAddr("1.2.1.0"), 23), + pattern: net.NewPfx(strAddr("1.2.0.0"), 22), + begin: 22, + end: 24, + expected: true, + }, + { + name: "matches begin of range (22-24)", + prefix: net.NewPfx(strAddr("1.2.0.0"), 22), + pattern: net.NewPfx(strAddr("1.2.0.0"), 22), + begin: 22, + end: 24, + expected: true, + }, + { + name: "matches end of range (22-24)", + prefix: net.NewPfx(strAddr("1.2.128.0"), 24), + pattern: net.NewPfx(strAddr("1.2.0.0"), 22), + begin: 22, + end: 24, + expected: true, + }, + { + name: "matches begin and end of range (24-24)", + prefix: net.NewPfx(strAddr("1.2.0.0"), 24), + pattern: net.NewPfx(strAddr("1.2.0.0"), 24), + begin: 24, + end: 24, + expected: true, + }, + { + name: "smaller (22-24)", + prefix: net.NewPfx(strAddr("1.2.0.0"), 16), + pattern: net.NewPfx(strAddr("1.2.4.0"), 22), + begin: 22, + end: 24, + expected: false, + }, + { + name: "longer (22-24)", + prefix: net.NewPfx(strAddr("1.2.0.128"), 25), + pattern: net.NewPfx(strAddr("1.2.0.0"), 22), + begin: 22, + end: 24, + expected: false, + }, + { + name: "does not match", + prefix: net.NewPfx(strAddr("2.0.0.0"), 23), + pattern: net.NewPfx(strAddr("1.2.0.0"), 22), + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + f := NewRouteFilter(test.pattern, InRange(test.begin, test.end)) + assert.Equal(te, test.expected, f.Matches(test.prefix)) + }) + } +} + +func TestExact(t *testing.T) { + tests := []struct { + name string + prefix net.Prefix + pattern net.Prefix + expected bool + }{ + { + name: "matches (0.0.0.0/0)", + prefix: net.NewPfx(strAddr("0.0.0.0"), 0), + pattern: net.NewPfx(strAddr("0.0.0.0"), 0), + expected: true, + }, + { + name: "matches (192.168.0.0)", + prefix: net.NewPfx(strAddr("192.168.1.1"), 24), + pattern: net.NewPfx(strAddr("192.168.1.1"), 24), + expected: true, + }, + { + name: "does not match", + prefix: net.NewPfx(strAddr("1.0.0.0"), 8), + pattern: net.NewPfx(strAddr("0.0.0.0"), 0), + expected: false, + }, + { + name: "longer", + prefix: net.NewPfx(strAddr("1.0.0.0"), 8), + pattern: net.NewPfx(strAddr("1.0.0.0"), 7), + expected: false, + }, + { + name: "lesser", + prefix: net.NewPfx(strAddr("1.0.0.0"), 7), + pattern: net.NewPfx(strAddr("1.0.0.0"), 8), + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + f := NewRouteFilter(test.pattern, Exact()) + assert.Equal(te, test.expected, f.Matches(test.prefix)) + }) + } +} + +func TestOrLonger(t *testing.T) { + tests := []struct { + name string + prefix net.Prefix + pattern net.Prefix + expected bool + }{ + { + name: "longer", + prefix: net.NewPfx(strAddr("1.2.3.128"), 25), + pattern: net.NewPfx(strAddr("1.2.3.0"), 24), + expected: true, + }, + { + name: "exact", + prefix: net.NewPfx(strAddr("1.2.3.0"), 24), + pattern: net.NewPfx(strAddr("1.2.3.0"), 24), + expected: true, + }, + { + name: "lesser", + prefix: net.NewPfx(strAddr("1.2.3.0"), 23), + pattern: net.NewPfx(strAddr("1.2.3.0"), 24), + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + f := NewRouteFilter(test.pattern, OrLonger()) + assert.Equal(te, test.expected, f.Matches(test.prefix)) + }) + } +} + +func TestLonger(t *testing.T) { + tests := []struct { + name string + prefix net.Prefix + pattern net.Prefix + expected bool + }{ + { + name: "longer", + prefix: net.NewPfx(strAddr("1.2.3.128"), 25), + pattern: net.NewPfx(strAddr("1.2.3.0"), 24), + expected: true, + }, + { + name: "exact", + prefix: net.NewPfx(strAddr("1.2.3.0"), 24), + pattern: net.NewPfx(strAddr("1.2.3.0"), 24), + expected: false, + }, + { + name: "lesser", + prefix: net.NewPfx(strAddr("1.2.3.0"), 23), + pattern: net.NewPfx(strAddr("1.2.3.0"), 24), + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + f := NewRouteFilter(test.pattern, Longer()) + assert.Equal(te, test.expected, f.Matches(test.prefix)) + }) + } +} diff --git a/routingtable/filter/term.go b/routingtable/filter/term.go index 85a88eb88945581c388f790bcf17631b7f1da9db..0d546be8bc7ceaf07e12a85453ed5cce741ea3cd 100644 --- a/routingtable/filter/term.go +++ b/routingtable/filter/term.go @@ -5,14 +5,18 @@ import ( "github.com/bio-routing/bio-rd/route" ) +type FilterAction interface { + Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) +} + // Term matches a path against a list of conditions and performs actions if it matches type Term struct { - from []*From - then []Then + from []*TermCondition + then []FilterAction } // NewTerm creates a new term -func NewTerm(from []*From, then []Then) *Term { +func NewTerm(from []*TermCondition, then []FilterAction) *Term { t := &Term{ from: from, then: then, diff --git a/routingtable/filter/term_condition.go b/routingtable/filter/term_condition.go new file mode 100644 index 0000000000000000000000000000000000000000..92c3616a8cc55f21c73696b2f87a1b2a9cfbcbd4 --- /dev/null +++ b/routingtable/filter/term_condition.go @@ -0,0 +1,35 @@ +package filter + +import ( + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" +) + +type TermCondition struct { + prefixLists []*PrefixList + routeFilters []*RouteFilter +} + +func (f *TermCondition) Matches(p net.Prefix, pa *route.Path) bool { + return f.matchesAnyPrefixList(p) || f.machtchesAnyRouteFilter(p) +} + +func (t *TermCondition) matchesAnyPrefixList(p net.Prefix) bool { + for _, l := range t.prefixLists { + if l.Matches(p) { + return true + } + } + + return false +} + +func (t *TermCondition) machtchesAnyRouteFilter(p net.Prefix) bool { + for _, l := range t.routeFilters { + if l.Matches(p) { + return true + } + } + + return false +} diff --git a/routingtable/filter/term_condition_test.go b/routingtable/filter/term_condition_test.go new file mode 100644 index 0000000000000000000000000000000000000000..876746a240d195ee2a6612636a888a2cf41a9d25 --- /dev/null +++ b/routingtable/filter/term_condition_test.go @@ -0,0 +1,120 @@ +package filter + +import ( + "testing" + + "github.com/bio-routing/bio-rd/net" + "github.com/bio-routing/bio-rd/route" + "github.com/stretchr/testify/assert" +) + +func TestMatches(t *testing.T) { + tests := []struct { + name string + prefix net.Prefix + prefixLists []*PrefixList + routeFilters []*RouteFilter + expected bool + }{ + { + name: "one prefix matches in prefix list, no route filters set", + prefix: net.NewPfx(strAddr("127.0.0.1"), 8), + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(strAddr("127.0.0.1"), 8)), + }, + routeFilters: []*RouteFilter{}, + expected: true, + }, + { + name: "one prefix in prefix list and no match, no route filters set", + prefix: net.NewPfx(strAddr("127.0.0.1"), 8), + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(0, 32)), + }, + routeFilters: []*RouteFilter{}, + expected: false, + }, + { + name: "one prefix of 2 matches in prefix list, no route filters set", + prefix: net.NewPfx(strAddr("127.0.0.1"), 8), + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(strAddr("10.0.0.0"), 8)), + NewPrefixList(net.NewPfx(strAddr("127.0.0.1"), 8)), + }, + routeFilters: []*RouteFilter{}, + expected: true, + }, + { + name: "no prefixes in prefix list, only route filter matches", + prefix: net.NewPfx(strAddr("10.0.0.0"), 24), + prefixLists: []*PrefixList{}, + routeFilters: []*RouteFilter{ + NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()), + }, + expected: true, + }, + { + name: "no prefixes in prefix list, one route filter matches", + prefix: net.NewPfx(strAddr("10.0.0.0"), 24), + prefixLists: []*PrefixList{}, + routeFilters: []*RouteFilter{ + NewRouteFilter(net.NewPfx(strAddr("8.0.0.0"), 8), Longer()), + NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()), + }, + expected: true, + }, + { + name: "no prefixes in prefix list, one of many route filters matches", + prefix: net.NewPfx(strAddr("127.0.0.1"), 8), + prefixLists: []*PrefixList{}, + routeFilters: []*RouteFilter{ + NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()), + }, + expected: false, + }, + { + name: "no match in prefix list, no macht in route filter", + prefix: net.NewPfx(strAddr("9.9.9.0"), 24), + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(strAddr("8.0.0.0"), 8)), + }, + routeFilters: []*RouteFilter{ + NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()), + }, + expected: false, + }, + { + name: "one prefix in prefixlist, one route fitler, only prefix list matches", + prefix: net.NewPfx(strAddr("8.8.8.0"), 24), + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(strAddr("8.0.0.0"), 8)), + }, + routeFilters: []*RouteFilter{ + NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()), + }, + expected: true, + }, + { + name: "one prefix in prefixlist, one route fitler, only route filter matches", + prefix: net.NewPfx(strAddr("10.0.0.0"), 24), + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(strAddr("8.0.0.0"), 8)), + }, + routeFilters: []*RouteFilter{ + NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()), + }, + expected: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(te *testing.T) { + f := &TermCondition{ + prefixLists: test.prefixLists, + routeFilters: test.routeFilters, + } + + assert.Equal(te, test.expected, f.Matches(test.prefix, &route.Path{})) + }) + } +} diff --git a/routingtable/filter/term_test.go b/routingtable/filter/term_test.go index ee55c712c21a055d75dc61468751e1c80b92f81b..967480e76cdaa8a81f7594651b83fa8405fe02f9 100644 --- a/routingtable/filter/term_test.go +++ b/routingtable/filter/term_test.go @@ -24,8 +24,8 @@ func TestProcess(t *testing.T) { name string prefix net.Prefix path *route.Path - from []*From - then []Then + from []*TermCondition + then []FilterAction expectReject bool expectModified bool }{ @@ -33,8 +33,8 @@ func TestProcess(t *testing.T) { name: "empty from", prefix: net.NewPfx(strAddr("100.64.0.1"), 8), path: &route.Path{}, - from: []*From{}, - then: []Then{ + from: []*TermCondition{}, + then: []FilterAction{ &actions.AcceptAction{}, }, expectReject: false, @@ -44,16 +44,14 @@ func TestProcess(t *testing.T) { name: "from matches", prefix: net.NewPfx(strAddr("100.64.0.1"), 8), path: &route.Path{}, - from: []*From{ + from: []*TermCondition{ { - &PrefixList{ - allowed: []net.Prefix{ - net.NewPfx(0, 0), - }, + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(strAddr("100.64.0.1"), 8)), }, }, }, - then: []Then{ + then: []FilterAction{ &actions.AcceptAction{}, }, expectReject: false, @@ -63,16 +61,14 @@ func TestProcess(t *testing.T) { name: "from does not match", prefix: net.NewPfx(strAddr("100.64.0.1"), 8), path: &route.Path{}, - from: []*From{ + from: []*TermCondition{ { - &PrefixList{ - allowed: []net.Prefix{ - net.NewPfx(0, 32), - }, + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(0, 32)), }, }, }, - then: []Then{ + then: []FilterAction{ &actions.AcceptAction{}, }, expectReject: true, @@ -82,16 +78,14 @@ func TestProcess(t *testing.T) { name: "modified", prefix: net.NewPfx(strAddr("100.64.0.1"), 8), path: &route.Path{}, - from: []*From{ + from: []*TermCondition{ { - &PrefixList{ - allowed: []net.Prefix{ - net.NewPfx(0, 0), - }, + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(strAddr("100.64.0.1"), 8)), }, }, }, - then: []Then{ + then: []FilterAction{ &mockAction{}, }, expectReject: false, @@ -101,16 +95,14 @@ func TestProcess(t *testing.T) { name: "modified and accepted (2 actions)", prefix: net.NewPfx(strAddr("100.64.0.1"), 8), path: &route.Path{}, - from: []*From{ + from: []*TermCondition{ { - &PrefixList{ - allowed: []net.Prefix{ - net.NewPfx(0, 0), - }, + prefixLists: []*PrefixList{ + NewPrefixList(net.NewPfx(strAddr("100.64.0.1"), 8)), }, }, }, - then: []Then{ + then: []FilterAction{ &mockAction{}, &actions.AcceptAction{}, }, @@ -121,23 +113,15 @@ func TestProcess(t *testing.T) { name: "one of the prefix filters matches", prefix: net.NewPfx(strAddr("100.64.0.1"), 8), path: &route.Path{}, - from: []*From{ + from: []*TermCondition{ { - &PrefixList{ - allowed: []net.Prefix{ - net.NewPfx(0, 32), - }, - }, - }, - { - &PrefixList{ - allowed: []net.Prefix{ - net.NewPfx(0, 0), - }, + prefixLists: []*PrefixList{ + NewPrefixListWithMatcher(Exact(), net.NewPfx(0, 32)), + NewPrefixList(net.NewPfx(strAddr("100.64.0.1"), 8)), }, }, }, - then: []Then{ + then: []FilterAction{ &actions.AcceptAction{}, }, expectReject: false, diff --git a/routingtable/filter/then.go b/routingtable/filter/then.go deleted file mode 100644 index 920daf43c1cdb121400168cc0ac17460bff42350..0000000000000000000000000000000000000000 --- a/routingtable/filter/then.go +++ /dev/null @@ -1,10 +0,0 @@ -package filter - -import ( - "github.com/bio-routing/bio-rd/net" - "github.com/bio-routing/bio-rd/route" -) - -type Then interface { - Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) -}