all: add arbitrary capability support

Updates #4217

RELNOTE=start of WhoIsResponse capability support

Change-Id: I6522998a911fe49e2f003077dad6164c017eed9b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2022-03-18 11:48:40 -07:00
committed by Brad Fitzpatrick
parent c591c91653
commit 16f3520089
9 changed files with 218 additions and 5 deletions
+54
View File
@@ -27,10 +27,12 @@ type Filter struct {
// destination within local, regardless of the policy filter
// below.
local *netaddr.IPSet
// 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 *netaddr.IPSet
// matches4 and matches6 are lists of match->action rules
// applied to all packets arriving over tailscale
// tunnels. Matches are checked in order, and processing stops
@@ -38,6 +40,11 @@ type Filter struct {
// match is to drop the packet.
matches4 matches
matches6 matches
// cap4 and cap6 are the subsets of the matches that are about
// capability grants, partitioned by source IP address family.
cap4, cap6 matches
// state is the connection tracking state attached to this
// filter. It is used to allow incoming traffic that is a response
// to an outbound connection that this node made, even if those
@@ -174,6 +181,8 @@ func New(matches []Match, localNets *netaddr.IPSet, logIPs *netaddr.IPSet, share
logf: logf,
matches4: matchesFamily(matches, netaddr.IP.Is4),
matches6: matchesFamily(matches, netaddr.IP.Is6),
cap4: capMatchesFunc(matches, netaddr.IP.Is4),
cap6: capMatchesFunc(matches, netaddr.IP.Is6),
local: localNets,
logIPs: logIPs,
state: state,
@@ -205,6 +214,27 @@ func matchesFamily(ms matches, keep func(netaddr.IP) bool) matches {
return ret
}
// capMatchesFunc returns a copy of the subset of ms for which keep(srcNet.IP)
// and the match is a capability grant.
func capMatchesFunc(ms matches, keep func(netaddr.IP) bool) matches {
var ret matches
for _, m := range ms {
if len(m.Caps) == 0 {
continue
}
retm := Match{Caps: m.Caps}
for _, src := range m.Srcs {
if keep(src.IP()) {
retm.Srcs = append(retm.Srcs, src)
}
}
if len(retm.Srcs) > 0 {
ret = append(ret, retm)
}
}
return ret
}
func maybeHexdump(flag RunFlags, b []byte) string {
if flag == 0 {
return ""
@@ -291,6 +321,30 @@ func (f *Filter) CheckTCP(srcIP, dstIP netaddr.IP, dstPort uint16) Response {
return f.RunIn(pkt, 0)
}
// AppendCaps appends to base the capabilities that srcIP has talking
// to dstIP.
func (f *Filter) AppendCaps(base []string, srcIP, dstIP netaddr.IP) []string {
ret := base
var mm matches
switch {
case srcIP.Is4():
mm = f.cap4
case srcIP.Is6():
mm = f.cap6
}
for _, m := range mm {
if !ipInList(srcIP, m.Srcs) {
continue
}
for _, cm := range m.Caps {
if cm.Cap != "" && cm.Dst.Contains(dstIP) {
ret = append(ret, cm.Cap)
}
}
}
return ret
}
// ShieldsUp reports whether this is a "shields up" (block everything
// incoming) filter.
func (f *Filter) ShieldsUp() bool { return f.shieldsUp }