feature/conn25: add packet filter allow functions
That will be able to be plugged into the hooks in wgengine/filter/filter.go to let connector packets flow. Fixes tailscale/corp#37144 Fixes tailscale/corp#37145 Signed-off-by: Fran Bull <fran@tailscale.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
|||||||
"tailscale.com/ipn/ipnext"
|
"tailscale.com/ipn/ipnext"
|
||||||
"tailscale.com/ipn/ipnlocal"
|
"tailscale.com/ipn/ipnlocal"
|
||||||
"tailscale.com/net/dns"
|
"tailscale.com/net/dns"
|
||||||
|
"tailscale.com/net/packet"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/appctype"
|
"tailscale.com/types/appctype"
|
||||||
@@ -499,6 +500,26 @@ func (c *client) ClientTransitIPForMagicIP(magicIP netip.Addr) (netip.Addr, erro
|
|||||||
return netip.Addr{}, ErrUnmappedMagicIP
|
return netip.Addr{}, ErrUnmappedMagicIP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// linkLocalAllow returns true if the provided packet with a link-local Dst address has a
|
||||||
|
// Dst that is one of our transit IPs, and false otherwise.
|
||||||
|
// Tailscale's wireguard filters drop link-local unicast packets (see [wgengine/filter/filter.go])
|
||||||
|
// but conn25 uses link-local addresses for transit IPs.
|
||||||
|
// Let the filter know if this is one of our addresses and should be allowed.
|
||||||
|
func (c *client) linkLocalAllow(p packet.Parsed) (bool, string) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
ok := c.isKnownTransitIP(p.Dst.Addr())
|
||||||
|
if ok {
|
||||||
|
return true, packetFilterAllowReason
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) isKnownTransitIP(tip netip.Addr) bool {
|
||||||
|
_, ok := c.assignments.lookupByTransitIP(tip)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func (c *client) isConfigured() bool {
|
func (c *client) isConfigured() bool {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
@@ -863,6 +884,20 @@ func (c *connector) ConnectorRealIPForTransitIPConnection(srcIP netip.Addr, tran
|
|||||||
return netip.Addr{}, ErrUnmappedSrcAndTransitIP
|
return netip.Addr{}, ErrUnmappedSrcAndTransitIP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const packetFilterAllowReason = "app connector transit IP"
|
||||||
|
|
||||||
|
// packetFilterAllow returns true if the provided packet has a Src that maps to a peer
|
||||||
|
// that has a transit IP with us that is the packet Dst, and false otherwise.
|
||||||
|
func (c *connector) packetFilterAllow(p packet.Parsed) (bool, string) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
_, ok := c.lookupBySrcIPAndTransitIP(p.Src.Addr(), p.Dst.Addr())
|
||||||
|
if ok {
|
||||||
|
return true, packetFilterAllowReason
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
func (c *connector) lookupBySrcIPAndTransitIP(srcIP, transitIP netip.Addr) (appAddr, bool) {
|
func (c *connector) lookupBySrcIPAndTransitIP(srcIP, transitIP netip.Addr) (appAddr, bool) {
|
||||||
m, ok := c.transitIPs[srcIP]
|
m, ok := c.transitIPs[srcIP]
|
||||||
if !ok || m == nil {
|
if !ok || m == nil {
|
||||||
@@ -899,9 +934,10 @@ type domainDst struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addrAssignments is the collection of addrs assigned by this client
|
// addrAssignments is the collection of addrs assigned by this client
|
||||||
// supporting lookup by magicip or domain+dst
|
// supporting lookup by magic IP, transit IP or domain+dst
|
||||||
type addrAssignments struct {
|
type addrAssignments struct {
|
||||||
byMagicIP map[netip.Addr]addrs
|
byMagicIP map[netip.Addr]addrs
|
||||||
|
byTransitIP map[netip.Addr]addrs
|
||||||
byDomainDst map[domainDst]addrs
|
byDomainDst map[domainDst]addrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,7 +951,11 @@ func (a *addrAssignments) insert(as addrs) error {
|
|||||||
if _, ok := a.byDomainDst[ddst]; ok {
|
if _, ok := a.byDomainDst[ddst]; ok {
|
||||||
return errors.New("byDomainDst key exists")
|
return errors.New("byDomainDst key exists")
|
||||||
}
|
}
|
||||||
|
if _, ok := a.byTransitIP[as.transit]; ok {
|
||||||
|
return errors.New("byTransitIP key exists")
|
||||||
|
}
|
||||||
mak.Set(&a.byMagicIP, as.magic, as)
|
mak.Set(&a.byMagicIP, as.magic, as)
|
||||||
|
mak.Set(&a.byTransitIP, as.transit, as)
|
||||||
mak.Set(&a.byDomainDst, ddst, as)
|
mak.Set(&a.byDomainDst, ddst, as)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -929,3 +969,8 @@ func (a *addrAssignments) lookupByMagicIP(mip netip.Addr) (addrs, bool) {
|
|||||||
v, ok := a.byMagicIP[mip]
|
v, ok := a.byMagicIP[mip]
|
||||||
return v, ok
|
return v, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *addrAssignments) lookupByTransitIP(tip netip.Addr) (addrs, bool) {
|
||||||
|
v, ok := a.byTransitIP[tip]
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
"tailscale.com/ipn/ipnext"
|
"tailscale.com/ipn/ipnext"
|
||||||
|
"tailscale.com/net/packet"
|
||||||
"tailscale.com/net/tsdial"
|
"tailscale.com/net/tsdial"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/tsd"
|
"tailscale.com/tsd"
|
||||||
@@ -1309,3 +1310,73 @@ func TestConnectorRealIPForTransitIPConnection(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsKnownTransitIP(t *testing.T) {
|
||||||
|
knownTip := netip.MustParseAddr("100.64.0.41")
|
||||||
|
unknownTip := netip.MustParseAddr("100.64.0.42")
|
||||||
|
|
||||||
|
c := newConn25(t.Logf)
|
||||||
|
c.client.assignments.insert(addrs{
|
||||||
|
transit: knownTip,
|
||||||
|
})
|
||||||
|
|
||||||
|
if !c.client.isKnownTransitIP(knownTip) {
|
||||||
|
t.Fatal("knownTip: should have been known")
|
||||||
|
}
|
||||||
|
if c.client.isKnownTransitIP(unknownTip) {
|
||||||
|
t.Fatal("unknownTip: should not have been known")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLinkLocalAllow(t *testing.T) {
|
||||||
|
knownTip := netip.MustParseAddr("100.64.0.41")
|
||||||
|
|
||||||
|
c := newConn25(t.Logf)
|
||||||
|
c.client.assignments.insert(addrs{
|
||||||
|
transit: knownTip,
|
||||||
|
})
|
||||||
|
|
||||||
|
if allow, _ := c.client.linkLocalAllow(packet.Parsed{
|
||||||
|
Dst: netip.AddrPortFrom(knownTip, 1234),
|
||||||
|
}); !allow {
|
||||||
|
t.Fatal("knownTip: should have been allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if allow, _ := c.client.linkLocalAllow(packet.Parsed{
|
||||||
|
Dst: netip.AddrPort{},
|
||||||
|
}); allow {
|
||||||
|
t.Fatal("unknownTip: should not have been allowed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnectorPacketFilterAllow(t *testing.T) {
|
||||||
|
knownTip := netip.MustParseAddr("100.64.0.41")
|
||||||
|
knownSrc := netip.MustParseAddr("100.64.0.1")
|
||||||
|
unknownTip := netip.MustParseAddr("100.64.0.42")
|
||||||
|
unknownSrc := netip.MustParseAddr("100.64.0.42")
|
||||||
|
|
||||||
|
c := newConn25(t.Logf)
|
||||||
|
c.connector.transitIPs = map[netip.Addr]map[netip.Addr]appAddr{}
|
||||||
|
c.connector.transitIPs[knownSrc] = map[netip.Addr]appAddr{}
|
||||||
|
c.connector.transitIPs[knownSrc][knownTip] = appAddr{}
|
||||||
|
|
||||||
|
if allow, _ := c.connector.packetFilterAllow(packet.Parsed{
|
||||||
|
Src: netip.AddrPortFrom(knownSrc, 1234),
|
||||||
|
Dst: netip.AddrPortFrom(knownTip, 1234),
|
||||||
|
}); !allow {
|
||||||
|
t.Fatal("knownTip: should have been allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if allow, _ := c.connector.packetFilterAllow(packet.Parsed{
|
||||||
|
Src: netip.AddrPortFrom(unknownSrc, 1234),
|
||||||
|
Dst: netip.AddrPortFrom(knownTip, 1234),
|
||||||
|
}); allow {
|
||||||
|
t.Fatal("unknownSrc: should not have been allowed")
|
||||||
|
}
|
||||||
|
if allow, _ := c.connector.packetFilterAllow(packet.Parsed{
|
||||||
|
Src: netip.AddrPortFrom(knownSrc, 1234),
|
||||||
|
Dst: netip.AddrPortFrom(unknownTip, 1234),
|
||||||
|
}); allow {
|
||||||
|
t.Fatal("unknownTip: should not have been allowed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user