mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 11:05:45 +00:00
net/portmapper: check returned epoch from PMP and PCP protocols
If the epoch that we see during a Probe is less than the existing epoch, it means that the gateway has either restarted or reset its configuration, and an existing mapping is no longer valid. Reset any saved mapping(s) if we detect this case so that a future createOrGetMapping will not attempt to re-use it. Updates #10597 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Ie3cddaf625cb94a29885f7a1eeea25dbf6b97b47
This commit is contained in:
parent
b084888e4d
commit
fa3639783c
@ -54,8 +54,7 @@ type pcpMapping struct {
|
||||
renewAfter 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.
|
||||
epoch uint32
|
||||
}
|
||||
|
||||
func (p *pcpMapping) MappingType() string { return "pcp" }
|
||||
@ -140,6 +139,7 @@ func parsePCPMapResponse(resp []byte) (*pcpMapping, error) {
|
||||
external: external,
|
||||
renewAfter: now.Add(lifetime / 2),
|
||||
goodUntil: now.Add(lifetime),
|
||||
epoch: res.Epoch,
|
||||
}
|
||||
|
||||
return mapping, nil
|
||||
|
@ -90,11 +90,14 @@ type Client struct {
|
||||
|
||||
lastProbe time.Time
|
||||
|
||||
// The following PMP fields are populated during Probe
|
||||
pmpPubIP netip.Addr // non-zero if known
|
||||
pmpPubIPTime time.Time // time pmpPubIP last verified
|
||||
pmpLastEpoch uint32
|
||||
|
||||
pcpSawTime time.Time // time we last saw PCP was available
|
||||
// The following PCP fields are populated during Probe
|
||||
pcpSawTime time.Time // time we last saw PCP was available
|
||||
pcpLastEpoch uint32
|
||||
|
||||
uPnPSawTime time.Time // time we last saw UPnP was available
|
||||
uPnPMetas []uPnPDiscoResponse // UPnP UDP discovery responses
|
||||
@ -324,9 +327,14 @@ func (c *Client) invalidateMappingsLocked(releaseOld bool) {
|
||||
}
|
||||
c.mapping = nil
|
||||
}
|
||||
|
||||
c.pmpPubIP = netip.Addr{}
|
||||
c.pmpPubIPTime = time.Time{}
|
||||
c.pmpLastEpoch = 0
|
||||
|
||||
c.pcpSawTime = time.Time{}
|
||||
c.pcpLastEpoch = 0
|
||||
|
||||
c.uPnPSawTime = time.Time{}
|
||||
c.uPnPMetas = nil
|
||||
}
|
||||
@ -988,7 +996,9 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
|
||||
if pres.OpCode == pcpOpReply|pcpOpAnnounce {
|
||||
pcpHeard = true
|
||||
c.mu.Lock()
|
||||
c.maybeInvalidatePCPMappingLocked(pres.Epoch) // must be before we write to c.pcp*
|
||||
c.pcpSawTime = time.Now()
|
||||
c.pcpLastEpoch = pres.Epoch
|
||||
c.mu.Unlock()
|
||||
switch pres.ResultCode {
|
||||
case pcpCodeOK:
|
||||
@ -1026,6 +1036,7 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
|
||||
c.logf("[v1] Got PMP response; IP: %v, epoch: %v", pres.PublicAddr, pres.SecondsSinceEpoch)
|
||||
res.PMP = true
|
||||
c.mu.Lock()
|
||||
c.maybeInvalidatePMPMappingLocked(pres.SecondsSinceEpoch) // must be before we write to c.pmp*
|
||||
c.pmpPubIP = pres.PublicAddr
|
||||
c.pmpPubIPTime = time.Now()
|
||||
c.pmpLastEpoch = pres.SecondsSinceEpoch
|
||||
@ -1051,6 +1062,57 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) maybeInvalidatePMPMappingLocked(epoch uint32) {
|
||||
if epoch == 0 || c.mapping == nil {
|
||||
return
|
||||
}
|
||||
m, ok := c.mapping.(*pmpMapping)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if epoch >= m.epoch {
|
||||
// Epoch increased, which is fine.
|
||||
//
|
||||
// TODO: we should more closely follow RFC6887 § 8.5 which also
|
||||
// requires us to check the current time and the time that this
|
||||
// epoch was received at.
|
||||
return
|
||||
}
|
||||
|
||||
// Epoch decreased, so invalidate the mapping and clear PMP fields.
|
||||
c.logf("invalidating PMP mappings since returned epoch %d < stored epoch %d", epoch, m.epoch)
|
||||
c.mapping = nil
|
||||
c.pmpPubIP = netip.Addr{}
|
||||
c.pmpPubIPTime = time.Time{}
|
||||
c.pmpLastEpoch = 0
|
||||
}
|
||||
|
||||
func (c *Client) maybeInvalidatePCPMappingLocked(epoch uint32) {
|
||||
if epoch == 0 || c.mapping == nil {
|
||||
return
|
||||
}
|
||||
m, ok := c.mapping.(*pcpMapping)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if epoch >= m.epoch {
|
||||
// Epoch increased, which is fine.
|
||||
//
|
||||
// TODO: we should more closely follow RFC6887 § 8.5 which also
|
||||
// requires us to check the current time and the time that this
|
||||
// epoch was received at.
|
||||
return
|
||||
}
|
||||
|
||||
// Epoch decreased, so invalidate the mapping and clear PCP fields.
|
||||
c.logf("invalidating PCP mappings since returned epoch %d < stored epoch %d", epoch, m.epoch)
|
||||
c.mapping = nil
|
||||
c.pcpSawTime = time.Time{}
|
||||
c.pcpLastEpoch = 0
|
||||
}
|
||||
|
||||
var pmpReqExternalAddrPacket = []byte{pmpVersion, pmpOpMapPublicAddr} // 0, 0
|
||||
|
||||
const (
|
||||
|
Loading…
Reference in New Issue
Block a user