diff --git a/routingtable/filter/route_filter.go b/routingtable/filter/route_filter.go new file mode 100644 index 0000000000000000000000000000000000000000..78a922fef4792f321d1563689a2b38ac5391f859 --- /dev/null +++ b/routingtable/filter/route_filter.go @@ -0,0 +1,48 @@ +package filter + +import ( + "github.com/bio-routing/bio-rd/net" +) + +type RouteFilter struct { + pattern net.Prefix + matcher PrefixMatcher +} + +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() + } +} + +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)) + }) + } +}