net/portmapper: move pcp code to separate file
This moves all the PCP code to a separate file in preparation for portmapping with PCP. Signed-off-by: julianknodt <julianknodt@gmail.com>
This commit is contained in:
parent
eee6b85b9b
commit
5c98b1b8d0
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package portmapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"inet.af/netaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// References:
|
||||||
|
//
|
||||||
|
// https://www.rfc-editor.org/rfc/pdfrfc/rfc6887.txt.pdf
|
||||||
|
// https://tools.ietf.org/html/rfc6887
|
||||||
|
|
||||||
|
// PCP constants
|
||||||
|
const (
|
||||||
|
pcpVersion = 2
|
||||||
|
pcpPort = 5351
|
||||||
|
|
||||||
|
pcpMapLifetimeSec = 7200 // TODO does the RFC recommend anything? This is taken from PMP.
|
||||||
|
|
||||||
|
pcpCodeOK = 0
|
||||||
|
pcpCodeNotAuthorized = 2
|
||||||
|
|
||||||
|
pcpOpReply = 0x80 // OR'd into request's op code on response
|
||||||
|
pcpOpAnnounce = 0
|
||||||
|
pcpOpMap = 1
|
||||||
|
|
||||||
|
pcpUDPMapping = 17 // portmap UDP
|
||||||
|
pcpTCPMapping = 6 // portmap TCP
|
||||||
|
)
|
||||||
|
|
||||||
|
// pcpMapRequest generates a PCP packet with a MAP opcode.
|
||||||
|
func pcpMapRequest(myIP netaddr.IP, mapToLocalPort int, delete bool) []byte {
|
||||||
|
const udpProtoNumber = 17
|
||||||
|
lifetimeSeconds := uint32(1)
|
||||||
|
if delete {
|
||||||
|
lifetimeSeconds = 0
|
||||||
|
}
|
||||||
|
const opMap = 1
|
||||||
|
|
||||||
|
// 24 byte header + 36 byte map opcode
|
||||||
|
pkt := make([]byte, (32+32+128)/8+(96+8+24+16+16+128)/8)
|
||||||
|
|
||||||
|
// The header (https://tools.ietf.org/html/rfc6887#section-7.1)
|
||||||
|
pkt[0] = 2 // version
|
||||||
|
pkt[1] = opMap
|
||||||
|
binary.BigEndian.PutUint32(pkt[4:8], lifetimeSeconds)
|
||||||
|
myIP16 := myIP.As16()
|
||||||
|
copy(pkt[8:], myIP16[:])
|
||||||
|
|
||||||
|
// The map opcode body (https://tools.ietf.org/html/rfc6887#section-11.1)
|
||||||
|
mapOp := pkt[24:]
|
||||||
|
rand.Read(mapOp[:12]) // 96 bit mappping nonce
|
||||||
|
mapOp[12] = udpProtoNumber
|
||||||
|
binary.BigEndian.PutUint16(mapOp[16:], uint16(mapToLocalPort))
|
||||||
|
v4unspec := netaddr.MustParseIP("0.0.0.0")
|
||||||
|
v4unspec16 := v4unspec.As16()
|
||||||
|
copy(mapOp[20:], v4unspec16[:])
|
||||||
|
return pkt
|
||||||
|
}
|
||||||
|
|
||||||
|
// pcpAnnounceRequest generates a PCP packet with an ANNOUNCE opcode.
|
||||||
|
func pcpAnnounceRequest(myIP netaddr.IP) []byte {
|
||||||
|
// See https://tools.ietf.org/html/rfc6887#section-7.1
|
||||||
|
pkt := make([]byte, 24)
|
||||||
|
pkt[0] = pcpVersion // version
|
||||||
|
pkt[1] = pcpOpAnnounce
|
||||||
|
myIP16 := myIP.As16()
|
||||||
|
copy(pkt[8:], myIP16[:])
|
||||||
|
return pkt
|
||||||
|
}
|
||||||
|
|
||||||
|
type pcpResponse struct {
|
||||||
|
OpCode uint8
|
||||||
|
ResultCode uint8
|
||||||
|
Lifetime uint32
|
||||||
|
Epoch uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePCPResponse(b []byte) (res pcpResponse, ok bool) {
|
||||||
|
if len(b) < 24 || b[0] != pcpVersion {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res.OpCode = b[1]
|
||||||
|
res.ResultCode = b[3]
|
||||||
|
res.Lifetime = binary.BigEndian.Uint32(b[4:])
|
||||||
|
res.Epoch = binary.BigEndian.Uint32(b[8:])
|
||||||
|
return res, true
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ package portmapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -36,7 +35,6 @@ var (
|
||||||
// References:
|
// References:
|
||||||
//
|
//
|
||||||
// NAT-PMP: https://tools.ietf.org/html/rfc6886
|
// NAT-PMP: https://tools.ietf.org/html/rfc6886
|
||||||
// PCP: https://tools.ietf.org/html/rfc6887
|
|
||||||
|
|
||||||
// portMapServiceTimeout is the time we wait for port mapping
|
// portMapServiceTimeout is the time we wait for port mapping
|
||||||
// services (UPnP, NAT-PMP, PCP) to respond before we give up and
|
// services (UPnP, NAT-PMP, PCP) to respond before we give up and
|
||||||
|
@ -669,77 +667,6 @@ func (c *Client) Probe(ctx context.Context) (res ProbeResult, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
pcpVersion = 2
|
|
||||||
pcpPort = 5351
|
|
||||||
|
|
||||||
pcpCodeOK = 0
|
|
||||||
pcpCodeNotAuthorized = 2
|
|
||||||
|
|
||||||
pcpOpReply = 0x80 // OR'd into request's op code on response
|
|
||||||
pcpOpAnnounce = 0
|
|
||||||
pcpOpMap = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// pcpAnnounceRequest generates a PCP packet with an ANNOUNCE opcode.
|
|
||||||
func pcpAnnounceRequest(myIP netaddr.IP) []byte {
|
|
||||||
// See https://tools.ietf.org/html/rfc6887#section-7.1
|
|
||||||
pkt := make([]byte, 24)
|
|
||||||
pkt[0] = pcpVersion // version
|
|
||||||
pkt[1] = pcpOpAnnounce
|
|
||||||
myIP16 := myIP.As16()
|
|
||||||
copy(pkt[8:], myIP16[:])
|
|
||||||
return pkt
|
|
||||||
}
|
|
||||||
|
|
||||||
// pcpMapRequest generates a PCP packet with a MAP opcode.
|
|
||||||
func pcpMapRequest(myIP netaddr.IP, mapToLocalPort int, delete bool) []byte {
|
|
||||||
const udpProtoNumber = 17
|
|
||||||
lifetimeSeconds := uint32(1)
|
|
||||||
if delete {
|
|
||||||
lifetimeSeconds = 0
|
|
||||||
}
|
|
||||||
const opMap = 1
|
|
||||||
|
|
||||||
// 24 byte header + 36 byte map opcode
|
|
||||||
pkt := make([]byte, (32+32+128)/8+(96+8+24+16+16+128)/8)
|
|
||||||
|
|
||||||
// The header (https://tools.ietf.org/html/rfc6887#section-7.1)
|
|
||||||
pkt[0] = 2 // version
|
|
||||||
pkt[1] = opMap
|
|
||||||
binary.BigEndian.PutUint32(pkt[4:8], lifetimeSeconds)
|
|
||||||
myIP16 := myIP.As16()
|
|
||||||
copy(pkt[8:], myIP16[:])
|
|
||||||
|
|
||||||
// The map opcode body (https://tools.ietf.org/html/rfc6887#section-11.1)
|
|
||||||
mapOp := pkt[24:]
|
|
||||||
rand.Read(mapOp[:12]) // 96 bit mappping nonce
|
|
||||||
mapOp[12] = udpProtoNumber
|
|
||||||
binary.BigEndian.PutUint16(mapOp[16:], uint16(mapToLocalPort))
|
|
||||||
v4unspec := netaddr.MustParseIP("0.0.0.0")
|
|
||||||
v4unspec16 := v4unspec.As16()
|
|
||||||
copy(mapOp[20:], v4unspec16[:])
|
|
||||||
return pkt
|
|
||||||
}
|
|
||||||
|
|
||||||
type pcpResponse struct {
|
|
||||||
OpCode uint8
|
|
||||||
ResultCode uint8
|
|
||||||
Lifetime uint32
|
|
||||||
Epoch uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func parsePCPResponse(b []byte) (res pcpResponse, ok bool) {
|
|
||||||
if len(b) < 24 || b[0] != pcpVersion {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res.OpCode = b[1]
|
|
||||||
res.ResultCode = b[3]
|
|
||||||
res.Lifetime = binary.BigEndian.Uint32(b[4:])
|
|
||||||
res.Epoch = binary.BigEndian.Uint32(b[8:])
|
|
||||||
return res, true
|
|
||||||
}
|
|
||||||
|
|
||||||
var pmpReqExternalAddrPacket = []byte{0, 0} // version 0, opcode 0 = "Public address request"
|
var pmpReqExternalAddrPacket = []byte{0, 0} // version 0, opcode 0 = "Public address request"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
Loading…
Reference in New Issue