|
|
|
|
@ -23,21 +23,23 @@ import ( |
|
|
|
|
"tailscale.com/types/logger" |
|
|
|
|
"tailscale.com/types/views" |
|
|
|
|
"tailscale.com/util/mak" |
|
|
|
|
"tailscale.com/util/slicesx" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// Filter is a stateful packet filter.
|
|
|
|
|
type Filter struct { |
|
|
|
|
logf logger.Logf |
|
|
|
|
// local is the set of IPs prefixes that we know to be "local" to
|
|
|
|
|
// this node. All packets coming in over tailscale must have a
|
|
|
|
|
// destination within local, regardless of the policy filter
|
|
|
|
|
// below.
|
|
|
|
|
local func(netip.Addr) bool |
|
|
|
|
// local4 and local6 report whether an IP is "local" to this node, for the
|
|
|
|
|
// respective address family. All packets coming in over tailscale must have
|
|
|
|
|
// a destination within local, regardless of the policy filter below.
|
|
|
|
|
local4 func(netip.Addr) bool |
|
|
|
|
local6 func(netip.Addr) bool |
|
|
|
|
|
|
|
|
|
// logIPs is the set of IPs that are allowed to appear in flow
|
|
|
|
|
// logs. If a packet is to or from an IP not in logIPs, it will
|
|
|
|
|
// never be logged.
|
|
|
|
|
logIPs func(netip.Addr) bool |
|
|
|
|
logIPs4 func(netip.Addr) bool |
|
|
|
|
logIPs6 func(netip.Addr) bool |
|
|
|
|
|
|
|
|
|
// matches4 and matches6 are lists of match->action rules
|
|
|
|
|
// applied to all packets arriving over tailscale
|
|
|
|
|
@ -184,23 +186,31 @@ func New(matches []Match, localNets, logIPs *netipx.IPSet, shareStateWith *Filte |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
containsFunc := func(s *netipx.IPSet) func(netip.Addr) bool { |
|
|
|
|
if s == nil { |
|
|
|
|
return tsaddr.FalseContainsIPFunc() |
|
|
|
|
} |
|
|
|
|
return tsaddr.NewContainsIPFunc(views.SliceOf(s.Prefixes())) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
f := &Filter{ |
|
|
|
|
logf: logf, |
|
|
|
|
matches4: matchesFamily(matches, netip.Addr.Is4), |
|
|
|
|
matches6: matchesFamily(matches, netip.Addr.Is6), |
|
|
|
|
cap4: capMatchesFunc(matches, netip.Addr.Is4), |
|
|
|
|
cap6: capMatchesFunc(matches, netip.Addr.Is6), |
|
|
|
|
local: containsFunc(localNets), |
|
|
|
|
logIPs: containsFunc(logIPs), |
|
|
|
|
local4: tsaddr.FalseContainsIPFunc(), |
|
|
|
|
local6: tsaddr.FalseContainsIPFunc(), |
|
|
|
|
logIPs4: tsaddr.FalseContainsIPFunc(), |
|
|
|
|
logIPs6: tsaddr.FalseContainsIPFunc(), |
|
|
|
|
state: state, |
|
|
|
|
} |
|
|
|
|
if localNets != nil { |
|
|
|
|
p := localNets.Prefixes() |
|
|
|
|
p4, p6 := slicesx.Partition(p, func(p netip.Prefix) bool { return p.Addr().Is4() }) |
|
|
|
|
f.local4 = tsaddr.NewContainsIPFunc(views.SliceOf(p4)) |
|
|
|
|
f.local6 = tsaddr.NewContainsIPFunc(views.SliceOf(p6)) |
|
|
|
|
} |
|
|
|
|
if logIPs != nil { |
|
|
|
|
p := logIPs.Prefixes() |
|
|
|
|
p4, p6 := slicesx.Partition(p, func(p netip.Prefix) bool { return p.Addr().Is4() }) |
|
|
|
|
f.logIPs4 = tsaddr.NewContainsIPFunc(views.SliceOf(p4)) |
|
|
|
|
f.logIPs6 = tsaddr.NewContainsIPFunc(views.SliceOf(p6)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return f |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -431,7 +441,7 @@ func (f *Filter) runIn4(q *packet.Parsed) (r Response, why string) { |
|
|
|
|
// A compromised peer could try to send us packets for
|
|
|
|
|
// destinations we didn't explicitly advertise. This check is to
|
|
|
|
|
// prevent that.
|
|
|
|
|
if !f.local(q.Dst.Addr()) { |
|
|
|
|
if !f.local4(q.Dst.Addr()) { |
|
|
|
|
return Drop, "destination not allowed" |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -491,7 +501,7 @@ func (f *Filter) runIn6(q *packet.Parsed) (r Response, why string) { |
|
|
|
|
// A compromised peer could try to send us packets for
|
|
|
|
|
// destinations we didn't explicitly advertise. This check is to
|
|
|
|
|
// prevent that.
|
|
|
|
|
if !f.local(q.Dst.Addr()) { |
|
|
|
|
if !f.local6(q.Dst.Addr()) { |
|
|
|
|
return Drop, "destination not allowed" |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -617,7 +627,13 @@ func (f *Filter) pre(q *packet.Parsed, rf RunFlags, dir direction) Response { |
|
|
|
|
|
|
|
|
|
// loggingAllowed reports whether p can appear in logs at all.
|
|
|
|
|
func (f *Filter) loggingAllowed(p *packet.Parsed) bool { |
|
|
|
|
return f.logIPs(p.Src.Addr()) && f.logIPs(p.Dst.Addr()) |
|
|
|
|
switch p.IPVersion { |
|
|
|
|
case 4: |
|
|
|
|
return f.logIPs4(p.Src.Addr()) && f.logIPs4(p.Dst.Addr()) |
|
|
|
|
case 6: |
|
|
|
|
return f.logIPs6(p.Src.Addr()) && f.logIPs6(p.Dst.Addr()) |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// omitDropLogging reports whether packet p, which has already been
|
|
|
|
|
|