portmap fixes

Change-Id: Ia847580ba523acacadcb5fa8f87ccea98dc7ce41
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2024-08-08 10:20:11 -07:00
parent 2e90e9e9ef
commit e237e6ff79
3 changed files with 40 additions and 9 deletions

View File

@ -14,6 +14,7 @@
"time" "time"
"tailscale.com/tstest/natlab/vnet" "tailscale.com/tstest/natlab/vnet"
"tailscale.com/types/logger"
) )
var ( var (
@ -78,7 +79,7 @@ func main() {
log.Printf("NodeStatus: %v", err) log.Printf("NodeStatus: %v", err)
return return
} }
log.Printf("NodeStatus: %q", st) log.Printf("NodeStatus: %v", logger.AsJSON(st))
} }
for { for {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)

View File

@ -227,7 +227,7 @@ func ping(ctx context.Context, c *vnet.NodeAgentClient, target netip.Addr) (*ipn
n := 0 n := 0
var res *ipnstate.PingResult var res *ipnstate.PingResult
anyPong := false anyPong := false
for { for n < 10 {
n++ n++
pr, err := c.PingWithOpts(ctx, target, tailcfg.PingDisco, tailscale.PingOpts{}) pr, err := c.PingWithOpts(ctx, target, tailcfg.PingDisco, tailscale.PingOpts{})
if err != nil { if err != nil {
@ -242,9 +242,16 @@ func ping(ctx context.Context, c *vnet.NodeAgentClient, target netip.Addr) (*ipn
if pr.DERPRegionID == 0 { if pr.DERPRegionID == 0 {
return pr, nil return pr, nil
} }
time.Sleep(time.Second) select {
case <-ctx.Done():
case <-time.After(time.Second):
}
res = pr res = pr
} }
if res == nil {
return nil, errors.New("no ping response")
}
return res, nil
} }
func up(ctx context.Context, c *vnet.NodeAgentClient) error { func up(ctx context.Context, c *vnet.NodeAgentClient) error {
@ -270,7 +277,6 @@ func TestEasyEasy(t *testing.T) {
} }
func TestEasyHard(t *testing.T) { func TestEasyHard(t *testing.T) {
t.Skip()
nt := newNatTest(t) nt := newNatTest(t)
nt.runTest(easy, hard) nt.runTest(easy, hard)
} }

View File

@ -1214,11 +1214,16 @@ func (n *network) doNATIn(src, dst netip.AddrPort) (newDst netip.AddrPort) {
// First see if there's a port mapping, before doing NAT. // First see if there's a port mapping, before doing NAT.
if lanAP, ok := n.portMap[dst]; ok { if lanAP, ok := n.portMap[dst]; ok {
if now.Before(lanAP.expiry) { if now.Before(lanAP.expiry) {
log.Printf("XXX NAT: doNatIn: port mapping %v=>%v", dst, lanAP.dst)
return lanAP.dst return lanAP.dst
} }
log.Printf("XXX NAT: doNatIn: port mapping EXPIRED for %v=>%v", dst, lanAP.dst)
delete(n.portMap, dst) delete(n.portMap, dst)
return netip.AddrPort{} return netip.AddrPort{}
} }
if len(n.portMap) > 0 {
log.Printf("XXX NAT: doNatIn: no port mapping for %v; have %v", dst, n.portMap)
}
return n.natTable.PickIncomingDst(src, dst, now) return n.natTable.PickIncomingDst(src, dst, now)
} }
@ -1236,7 +1241,12 @@ func (n *network) doPortMap(src netip.Addr, dstLANPort, wantExtPort uint16, sec
n.natMu.Lock() n.natMu.Lock()
defer n.natMu.Unlock() defer n.natMu.Unlock()
if !n.portmap {
return 0, false
}
wanAP := netip.AddrPortFrom(n.wanIP, wantExtPort) wanAP := netip.AddrPortFrom(n.wanIP, wantExtPort)
dst := netip.AddrPortFrom(src, dstLANPort)
if sec == 0 { if sec == 0 {
lanAP, ok := n.portMap[wanAP] lanAP, ok := n.portMap[wanAP]
@ -1246,12 +1256,24 @@ func (n *network) doPortMap(src netip.Addr, dstLANPort, wantExtPort uint16, sec
return 0, false return 0, false
} }
// See if they already have a mapping and extend expiry if so.
for k, v := range n.portMap {
if v.dst == dst {
n.portMap[k] = portMapping{
dst: dst,
expiry: time.Now().Add(time.Duration(sec) * time.Second),
}
return k.Port(), true
}
}
for try := 0; try < 20_000; try++ { for try := 0; try < 20_000; try++ {
if !n.natTable.IsPublicPortUsed(wanAP) { if wanAP.Port() > 0 && !n.natTable.IsPublicPortUsed(wanAP) {
mak.Set(&n.portMap, wanAP, portMapping{ mak.Set(&n.portMap, wanAP, portMapping{
dst: netip.AddrPortFrom(src, dstLANPort), dst: dst,
expiry: time.Now().Add(time.Duration(sec) * time.Second), expiry: time.Now().Add(time.Duration(sec) * time.Second),
}) })
log.Printf("XXX allocated NAT mapping from %v to %v", wanAP, dst)
return wanAP.Port(), true return wanAP.Port(), true
} }
wantExtPort = rand.N(uint16(32<<10)) + 32<<10 wantExtPort = rand.N(uint16(32<<10)) + 32<<10
@ -1310,6 +1332,9 @@ func (n *network) createARPResponse(pkt gopacket.Packet) ([]byte, error) {
} }
func (n *network) handleNATPMPRequest(req UDPPacket) { func (n *network) handleNATPMPRequest(req UDPPacket) {
if !n.portmap {
return
}
if string(req.Payload) == "\x00\x00" { if string(req.Payload) == "\x00\x00" {
// https://www.rfc-editor.org/rfc/rfc6886#section-3.2 // https://www.rfc-editor.org/rfc/rfc6886#section-3.2
@ -1348,12 +1373,13 @@ func (n *network) handleNATPMPRequest(req UDPPacket) {
log.Printf("NAT-PMP map request for %v:%d failed", req.Src.Addr(), internalPort) log.Printf("NAT-PMP map request for %v:%d failed", req.Src.Addr(), internalPort)
return return
} }
res := make([]byte, 0, 12) res := make([]byte, 0, 16)
res = append(res, res = append(res,
0, // version 0 (NAT-PMP) 0, // version 0 (NAT-PMP)
1+128, // response to op 1 1+128, // response to op 1
0, 0, // result code success 0, 0, // result code success
) )
res = binary.BigEndian.AppendUint32(res, uint32(time.Now().Unix()))
res = binary.BigEndian.AppendUint16(res, internalPort) res = binary.BigEndian.AppendUint16(res, internalPort)
res = binary.BigEndian.AppendUint16(res, gotPort) res = binary.BigEndian.AppendUint16(res, gotPort)
res = binary.BigEndian.AppendUint32(res, lifetimeSec) res = binary.BigEndian.AppendUint32(res, lifetimeSec)
@ -1439,11 +1465,9 @@ func (s *Server) takeAgentConnOne(n *node) (_ *agentConn, ok bool) {
for ac := range s.agentConns { for ac := range s.agentConns {
if ac.node == n { if ac.node == n {
s.agentConns.Delete(ac) s.agentConns.Delete(ac)
log.Printf("XXX takeAgentConnOne HIT for %v", n.mac)
return ac, true return ac, true
} }
} }
log.Printf("XXX takeAgentConnOne MISS for %v", n.mac)
return nil, false return nil, false
} }