mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-18 02:48:40 +00:00
net/portmapper: check disable flags
Signed-off-by: julianknodt <julianknodt@gmail.com>
This commit is contained in:
parent
777b711d96
commit
85304d7392
@ -45,6 +45,9 @@ type pcpMapping struct {
|
|||||||
|
|
||||||
renewAfter time.Time
|
renewAfter time.Time
|
||||||
goodUntil time.Time
|
goodUntil time.Time
|
||||||
|
|
||||||
|
// TODO should this also contain an epoch?
|
||||||
|
// Doesn't seem to be used elsewhere, but can use it for validation at some point.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pcpMapping) GoodUntil() time.Time { return p.goodUntil }
|
func (p *pcpMapping) GoodUntil() time.Time { return p.goodUntil }
|
||||||
@ -64,8 +67,6 @@ func (p *pcpMapping) Release(ctx context.Context) {
|
|||||||
// To create a packet which deletes a mapping, lifetimeSec should be set to 0.
|
// To create a packet which deletes a mapping, lifetimeSec should be set to 0.
|
||||||
// If prevPort is not known, it should be set to 0.
|
// If prevPort is not known, it should be set to 0.
|
||||||
func buildPCPRequestMappingPacket(myIP netaddr.IP, localPort, prevPort uint16, lifetimeSec uint32) (pkt []byte) {
|
func buildPCPRequestMappingPacket(myIP netaddr.IP, localPort, prevPort uint16, lifetimeSec uint32) (pkt []byte) {
|
||||||
// note: lifetimeSec = 0 implies delete the mapping, should that be special-cased here?
|
|
||||||
|
|
||||||
// 24 byte common PCP header + 36 bytes of MAP-specific fields
|
// 24 byte common PCP header + 36 bytes of MAP-specific fields
|
||||||
pkt = make([]byte, 24+36)
|
pkt = make([]byte, 24+36)
|
||||||
pkt[0] = pcpVersion
|
pkt[0] = pcpVersion
|
||||||
@ -77,12 +78,14 @@ func buildPCPRequestMappingPacket(myIP netaddr.IP, localPort, prevPort uint16, l
|
|||||||
mapOp := pkt[24:]
|
mapOp := pkt[24:]
|
||||||
rand.Read(mapOp[:12]) // 96 bit mapping nonce
|
rand.Read(mapOp[:12]) // 96 bit mapping nonce
|
||||||
|
|
||||||
// TODO should this be a UDP mapping? It looks like it supports "all protocols" with 0, but
|
// TODO: should this be a UDP mapping? It looks like it supports "all protocols" with 0, but
|
||||||
// also doesn't support a local port then.
|
// also doesn't support a local port then.
|
||||||
mapOp[12] = pcpUDPMapping
|
mapOp[12] = pcpUDPMapping
|
||||||
binary.BigEndian.PutUint16(mapOp[16:18], localPort)
|
binary.BigEndian.PutUint16(mapOp[16:18], localPort)
|
||||||
binary.BigEndian.PutUint16(mapOp[18:20], prevPort)
|
binary.BigEndian.PutUint16(mapOp[18:20], prevPort)
|
||||||
|
|
||||||
|
// TODO: This can also be the previous external IP similar to how PMP caches the
|
||||||
|
// last external IP, not sure what the benefits of that are.
|
||||||
v4unspec := netaddr.MustParseIP("0.0.0.0")
|
v4unspec := netaddr.MustParseIP("0.0.0.0")
|
||||||
v4unspec16 := v4unspec.As16()
|
v4unspec16 := v4unspec.As16()
|
||||||
copy(mapOp[20:], v4unspec16[:])
|
copy(mapOp[20:], v4unspec16[:])
|
||||||
@ -100,7 +103,7 @@ func parsePCPMapResponse(resp []byte) (*pcpMapping, error) {
|
|||||||
if res.ResultCode != pcpCodeOK {
|
if res.ResultCode != pcpCodeOK {
|
||||||
return nil, fmt.Errorf("PCP response not ok, code %d", res.ResultCode)
|
return nil, fmt.Errorf("PCP response not ok, code %d", res.ResultCode)
|
||||||
}
|
}
|
||||||
// TODO don't ignore the nonce and make sure it's the same?
|
// TODO: don't ignore the nonce and make sure it's the same?
|
||||||
externalPort := binary.BigEndian.Uint16(resp[42:44])
|
externalPort := binary.BigEndian.Uint16(resp[42:44])
|
||||||
externalIPBytes := [16]byte{}
|
externalIPBytes := [16]byte{}
|
||||||
copy(externalIPBytes[:], resp[44:])
|
copy(externalIPBytes[:], resp[44:])
|
||||||
|
@ -24,9 +24,12 @@ import (
|
|||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Debub knobs for "tailscaled debug --portmap".
|
// Debug knobs for "tailscaled debug --portmap".
|
||||||
var (
|
var (
|
||||||
VerboseLogs bool
|
VerboseLogs bool
|
||||||
|
|
||||||
|
// Disable* disables a specific service from mapping.
|
||||||
|
|
||||||
DisableUPnP bool
|
DisableUPnP bool
|
||||||
DisablePMP bool
|
DisablePMP bool
|
||||||
DisablePCP bool
|
DisablePCP bool
|
||||||
@ -345,6 +348,9 @@ func (c *Client) createMapping() {
|
|||||||
// If no mapping is available, the error will be of type
|
// If no mapping is available, the error will be of type
|
||||||
// NoMappingError; see IsNoMappingError.
|
// NoMappingError; see IsNoMappingError.
|
||||||
func (c *Client) createOrGetMapping(ctx context.Context) (external netaddr.IPPort, err error) {
|
func (c *Client) createOrGetMapping(ctx context.Context) (external netaddr.IPPort, err error) {
|
||||||
|
if DisableUPnP && DisablePCP && DisablePMP {
|
||||||
|
return
|
||||||
|
}
|
||||||
gw, myIP, ok := c.gatewayAndSelfIP()
|
gw, myIP, ok := c.gatewayAndSelfIP()
|
||||||
if !ok {
|
if !ok {
|
||||||
return netaddr.IPPort{}, NoMappingError{ErrGatewayRange}
|
return netaddr.IPPort{}, NoMappingError{ErrGatewayRange}
|
||||||
@ -353,10 +359,6 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netaddr.IPPor
|
|||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
localPort := c.localPort
|
localPort := c.localPort
|
||||||
internalAddr := netaddr.IPPortFrom(myIP, localPort)
|
internalAddr := netaddr.IPPortFrom(myIP, localPort)
|
||||||
m := &pmpMapping{
|
|
||||||
gw: gw,
|
|
||||||
internal: internalAddr,
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevPort is the port we had most previously, if any. We try
|
// prevPort is the port we had most previously, if any. We try
|
||||||
// to ask for the same port. 0 means to give us any port.
|
// to ask for the same port. 0 means to give us any port.
|
||||||
@ -373,12 +375,24 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netaddr.IPPor
|
|||||||
prevPort = m.External().Port()
|
prevPort = m.External().Port()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if DisablePCP && DisablePMP {
|
||||||
|
c.mu.Unlock()
|
||||||
|
if external, ok := c.getUPnPPortMapping(ctx, gw, internalAddr, prevPort); ok {
|
||||||
|
return external, nil
|
||||||
|
}
|
||||||
|
return netaddr.IPPort{}, NoMappingError{ErrNoPortMappingServices}
|
||||||
|
}
|
||||||
|
|
||||||
// If we just did a Probe (e.g. via netchecker) but didn't
|
// If we just did a Probe (e.g. via netchecker) but didn't
|
||||||
// find a PMP service, bail out early rather than probing
|
// find a PMP service, bail out early rather than probing
|
||||||
// again. Cuts down latency for most clients.
|
// again. Cuts down latency for most clients.
|
||||||
haveRecentPMP := c.sawPMPRecentlyLocked()
|
haveRecentPMP := c.sawPMPRecentlyLocked()
|
||||||
haveRecentPCP := c.sawPCPRecentlyLocked()
|
haveRecentPCP := c.sawPCPRecentlyLocked()
|
||||||
|
|
||||||
|
m := &pmpMapping{
|
||||||
|
gw: gw,
|
||||||
|
internal: internalAddr,
|
||||||
|
}
|
||||||
if haveRecentPMP {
|
if haveRecentPMP {
|
||||||
m.external = m.external.WithIP(c.pmpPubIP)
|
m.external = m.external.WithIP(c.pmpPubIP)
|
||||||
}
|
}
|
||||||
@ -390,7 +404,6 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netaddr.IPPor
|
|||||||
}
|
}
|
||||||
return netaddr.IPPort{}, NoMappingError{ErrNoPortMappingServices}
|
return netaddr.IPPort{}, NoMappingError{ErrNoPortMappingServices}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
|
||||||
uc, err := netns.Listener().ListenPacket(ctx, "udp4", ":0")
|
uc, err := netns.Listener().ListenPacket(ctx, "udp4", ":0")
|
||||||
@ -408,7 +421,7 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netaddr.IPPor
|
|||||||
pcpAddru := pcpAddr.UDPAddr()
|
pcpAddru := pcpAddr.UDPAddr()
|
||||||
|
|
||||||
// Create a mapping, defaulting to PMP unless only PCP was seen recently.
|
// Create a mapping, defaulting to PMP unless only PCP was seen recently.
|
||||||
if !haveRecentPMP && haveRecentPCP {
|
if DisablePMP || (!haveRecentPMP && haveRecentPCP) {
|
||||||
// Only do PCP mapping in the case when PMP did not appear to be available recently.
|
// Only do PCP mapping in the case when PMP did not appear to be available recently.
|
||||||
pkt := buildPCPRequestMappingPacket(myIP, localPort, prevPort, pcpMapLifetimeSec)
|
pkt := buildPCPRequestMappingPacket(myIP, localPort, prevPort, pcpMapLifetimeSec)
|
||||||
if _, err := uc.WriteTo(pkt, pcpAddru); err != nil {
|
if _, err := uc.WriteTo(pkt, pcpAddru); err != nil {
|
||||||
@ -473,15 +486,18 @@ func (c *Client) createOrGetMapping(ctx context.Context) (external netaddr.IPPor
|
|||||||
pcpMapping, err := parsePCPMapResponse(res[:n])
|
pcpMapping, err := parsePCPMapResponse(res[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("failed to get PCP mapping: %v", err)
|
c.logf("failed to get PCP mapping: %v", err)
|
||||||
continue
|
// PCP should only have a single packet response
|
||||||
|
return netaddr.IPPort{}, NoMappingError{ErrNoPortMappingServices}
|
||||||
}
|
}
|
||||||
pcpMapping.internal = m.internal
|
pcpMapping.internal = m.internal
|
||||||
|
pcpMapping.gw = gw
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
c.mapping = pcpMapping
|
c.mapping = pcpMapping
|
||||||
return pcpMapping.external, nil
|
return pcpMapping.external, nil
|
||||||
default:
|
default:
|
||||||
c.logf("unknown PMP/PCP version number: %d %v", version, res[:n])
|
c.logf("unknown PMP/PCP version number: %d %v", version, res[:n])
|
||||||
|
return netaddr.IPPort{}, NoMappingError{ErrNoPortMappingServices}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ func addAnyPortMapping(
|
|||||||
// The provided ctx is not retained in the returned upnpClient, but
|
// The provided ctx is not retained in the returned upnpClient, but
|
||||||
// its associated HTTP client is (if set via goupnp.WithHTTPClient).
|
// its associated HTTP client is (if set via goupnp.WithHTTPClient).
|
||||||
func getUPnPClient(ctx context.Context, logf logger.Logf, gw netaddr.IP, meta uPnPDiscoResponse) (client upnpClient, err error) {
|
func getUPnPClient(ctx context.Context, logf logger.Logf, gw netaddr.IP, meta uPnPDiscoResponse) (client upnpClient, err error) {
|
||||||
if controlknobs.DisableUPnP() {
|
if controlknobs.DisableUPnP() || DisableUPnP {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ func (c *Client) getUPnPPortMapping(
|
|||||||
internal netaddr.IPPort,
|
internal netaddr.IPPort,
|
||||||
prevPort uint16,
|
prevPort uint16,
|
||||||
) (external netaddr.IPPort, ok bool) {
|
) (external netaddr.IPPort, ok bool) {
|
||||||
if controlknobs.DisableUPnP() {
|
if controlknobs.DisableUPnP() || DisableUPnP {
|
||||||
return netaddr.IPPort{}, false
|
return netaddr.IPPort{}, false
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user