@ -10,11 +10,47 @@ import (
"github.com/gaissmai/bart"
"tailscale.com/types/views"
"tailscale.com/util/set"
)
// FalseContainsIPFunc is shorthand for NewContainsIPFunc(views.Slice[netip.Prefix]{}).
func FalseContainsIPFunc ( ) func ( ip netip . Addr ) bool {
return func ( ip netip . Addr ) bool { return false }
return emptySet
}
func emptySet ( ip netip . Addr ) bool { return false }
func bartLookup ( t * bart . Table [ struct { } ] ) func ( netip . Addr ) bool {
return func ( ip netip . Addr ) bool {
_ , ok := t . Get ( ip )
return ok
}
}
func prefixContainsLoop ( addrs [ ] netip . Prefix ) func ( netip . Addr ) bool {
return func ( ip netip . Addr ) bool {
for _ , p := range addrs {
if p . Contains ( ip ) {
return true
}
}
return false
}
}
func oneIP ( ip1 netip . Addr ) func ( netip . Addr ) bool {
return func ( ip netip . Addr ) bool { return ip == ip1 }
}
func twoIP ( ip1 , ip2 netip . Addr ) func ( netip . Addr ) bool {
return func ( ip netip . Addr ) bool { return ip == ip1 || ip == ip2 }
}
func ipInMap ( m set . Set [ netip . Addr ] ) func ( netip . Addr ) bool {
return func ( ip netip . Addr ) bool {
_ , ok := m [ ip ]
return ok
}
}
// pathForTest is a test hook for NewContainsIPFunc, to test that it took the
@ -29,7 +65,7 @@ func NewContainsIPFunc(addrs views.Slice[netip.Prefix]) func(ip netip.Addr) bool
// (or just IPv6), and both IPv4 and IPv6.
if addrs . Len ( ) == 0 {
pathForTest ( "empty" )
return func ( netip . Addr ) bool { return false }
return emptySet
}
// If any addr is a prefix with more than a single IP, then do either a
// linear scan or a bart table, depending on the number of addrs.
@ -41,40 +77,27 @@ func NewContainsIPFunc(addrs views.Slice[netip.Prefix]) func(ip netip.Addr) bool
for i := range addrs . Len ( ) {
t . Insert ( addrs . At ( i ) , struct { } { } )
}
return func ( ip netip . Addr ) bool {
_ , ok := t . Get ( ip )
return ok
}
return bartLookup ( t )
} else {
pathForTest ( "linear-contains" )
// Small enough to do a linear search.
acopy := addrs . AsSlice ( )
return func ( ip netip . Addr ) bool {
for _ , a := range acopy {
if a . Contains ( ip ) {
return true
}
}
return false
}
return prefixContainsLoop ( addrs . AsSlice ( ) )
}
}
// Fast paths for 1 and 2 IPs:
if addrs . Len ( ) == 1 {
pathForTest ( "one-ip" )
a := addrs . At ( 0 )
return func ( ip netip . Addr ) bool { return ip == a . Addr ( ) }
return oneIP ( addrs . At ( 0 ) . Addr ( ) )
}
if addrs . Len ( ) == 2 {
pathForTest ( "two-ip" )
a , b := addrs . At ( 0 ) , addrs . At ( 1 )
return func ( ip netip . Addr ) bool { return ip == a . Addr ( ) || ip == b . Addr ( ) }
return twoIP ( addrs . At ( 0 ) . Addr ( ) , addrs . At ( 1 ) . Addr ( ) )
}
// General case:
pathForTest ( "ip-map" )
m := map [ netip . Addr ] bool { }
m := set . Set [ netip . Addr ] { }
for i := range addrs . Len ( ) {
m [ addrs . At ( i ) . Addr ( ) ] = true
m .Add ( addrs . At ( i ) . Addr ( ) )
}
return func ( ip netip . Addr ) bool { return m [ ip ] }
return ipInMap ( m )
}