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>
main
Fran Bull 4 weeks ago committed by franbull
parent 67496e14c6
commit d3626c51f1
  1. 47
      feature/conn25/conn25.go
  2. 71
      feature/conn25/conn25_test.go

@ -27,6 +27,7 @@ import (
"tailscale.com/ipn/ipnext"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/net/dns"
"tailscale.com/net/packet"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/appctype"
@ -499,6 +500,26 @@ func (c *client) ClientTransitIPForMagicIP(magicIP netip.Addr) (netip.Addr, erro
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 {
c.mu.Lock()
defer c.mu.Unlock()
@ -863,6 +884,20 @@ func (c *connector) ConnectorRealIPForTransitIPConnection(srcIP netip.Addr, tran
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) {
m, ok := c.transitIPs[srcIP]
if !ok || m == nil {
@ -899,9 +934,10 @@ type domainDst struct {
}
// 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 {
byMagicIP map[netip.Addr]addrs
byTransitIP map[netip.Addr]addrs
byDomainDst map[domainDst]addrs
}
@ -915,7 +951,11 @@ func (a *addrAssignments) insert(as addrs) error {
if _, ok := a.byDomainDst[ddst]; ok {
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.byTransitIP, as.transit, as)
mak.Set(&a.byDomainDst, ddst, as)
return nil
}
@ -929,3 +969,8 @@ func (a *addrAssignments) lookupByMagicIP(mip netip.Addr) (addrs, bool) {
v, ok := a.byMagicIP[mip]
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"
"golang.org/x/net/dns/dnsmessage"
"tailscale.com/ipn/ipnext"
"tailscale.com/net/packet"
"tailscale.com/net/tsdial"
"tailscale.com/tailcfg"
"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")
}
}

Loading…
Cancel
Save