pcp.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package portmapper
  4. import (
  5. "context"
  6. "crypto/rand"
  7. "encoding/binary"
  8. "fmt"
  9. "net/netip"
  10. "time"
  11. )
  12. // References:
  13. //
  14. // https://www.rfc-editor.org/rfc/pdfrfc/rfc6887.txt.pdf
  15. // https://tools.ietf.org/html/rfc6887
  16. //go:generate go run tailscale.com/cmd/addlicense -file pcpresultcode_string.go go run golang.org/x/tools/cmd/stringer -type=pcpResultCode -trimprefix=pcpCode
  17. type pcpResultCode uint8
  18. // PCP constants
  19. const (
  20. pcpVersion = 2
  21. pcpDefaultPort = 5351
  22. pcpMapLifetimeSec = 7200 // TODO does the RFC recommend anything? This is taken from PMP.
  23. pcpCodeOK pcpResultCode = 0
  24. pcpCodeNotAuthorized pcpResultCode = 2
  25. // From RFC 6887:
  26. // ADDRESS_MISMATCH: The source IP address of the request packet does
  27. // not match the contents of the PCP Client's IP Address field, due
  28. // to an unexpected NAT on the path between the PCP client and the
  29. // PCP-controlled NAT or firewall.
  30. pcpCodeAddressMismatch pcpResultCode = 12
  31. pcpOpReply = 0x80 // OR'd into request's op code on response
  32. pcpOpAnnounce = 0
  33. pcpOpMap = 1
  34. pcpUDPMapping = 17 // portmap UDP
  35. pcpTCPMapping = 6 // portmap TCP
  36. )
  37. type pcpMapping struct {
  38. c *Client
  39. gw netip.AddrPort
  40. internal netip.AddrPort
  41. external netip.AddrPort
  42. renewAfter time.Time
  43. goodUntil time.Time
  44. epoch uint32
  45. }
  46. func (p *pcpMapping) MappingType() string { return "pcp" }
  47. func (p *pcpMapping) GoodUntil() time.Time { return p.goodUntil }
  48. func (p *pcpMapping) RenewAfter() time.Time { return p.renewAfter }
  49. func (p *pcpMapping) External() netip.AddrPort { return p.external }
  50. func (p *pcpMapping) MappingDebug() string {
  51. return fmt.Sprintf("pcpMapping{gw:%v, external:%v, internal:%v, renewAfter:%d, goodUntil:%d}",
  52. p.gw, p.external, p.internal,
  53. p.renewAfter.Unix(), p.goodUntil.Unix())
  54. }
  55. func (p *pcpMapping) Release(ctx context.Context) {
  56. uc, err := p.c.listenPacket(ctx, "udp4", ":0")
  57. if err != nil {
  58. return
  59. }
  60. defer uc.Close()
  61. pkt := buildPCPRequestMappingPacket(p.internal.Addr(), p.internal.Port(), p.external.Port(), 0, p.external.Addr())
  62. uc.WriteToUDPAddrPort(pkt, p.gw)
  63. }
  64. // buildPCPRequestMappingPacket generates a PCP packet with a MAP opcode.
  65. // To create a packet which deletes a mapping, lifetimeSec should be set to 0.
  66. // If prevPort is not known, it should be set to 0.
  67. // If prevExternalIP is not known, it should be set to 0.0.0.0.
  68. func buildPCPRequestMappingPacket(
  69. myIP netip.Addr,
  70. localPort, prevPort uint16,
  71. lifetimeSec uint32,
  72. prevExternalIP netip.Addr,
  73. ) (pkt []byte) {
  74. // 24 byte common PCP header + 36 bytes of MAP-specific fields
  75. pkt = make([]byte, 24+36)
  76. pkt[0] = pcpVersion
  77. pkt[1] = pcpOpMap
  78. binary.BigEndian.PutUint32(pkt[4:8], lifetimeSec)
  79. myIP16 := myIP.As16()
  80. copy(pkt[8:24], myIP16[:])
  81. mapOp := pkt[24:]
  82. rand.Read(mapOp[:12]) // 96 bit mapping nonce
  83. // TODO: should this be a UDP mapping? It looks like it supports "all protocols" with 0, but
  84. // also doesn't support a local port then.
  85. mapOp[12] = pcpUDPMapping
  86. binary.BigEndian.PutUint16(mapOp[16:18], localPort)
  87. binary.BigEndian.PutUint16(mapOp[18:20], prevPort)
  88. prevExternalIP16 := prevExternalIP.As16()
  89. copy(mapOp[20:], prevExternalIP16[:])
  90. return pkt
  91. }
  92. // parsePCPMapResponse parses resp into a partially populated pcpMapping.
  93. // In particular, its Client is not populated.
  94. func parsePCPMapResponse(resp []byte) (*pcpMapping, error) {
  95. if len(resp) < 60 {
  96. return nil, fmt.Errorf("Does not appear to be PCP MAP response")
  97. }
  98. res, ok := parsePCPResponse(resp[:24])
  99. if !ok {
  100. return nil, fmt.Errorf("Invalid PCP common header")
  101. }
  102. if res.ResultCode == pcpCodeNotAuthorized {
  103. return nil, fmt.Errorf("PCP is implemented but not enabled in the router")
  104. }
  105. if res.ResultCode != pcpCodeOK {
  106. return nil, fmt.Errorf("PCP response not ok, code %d", res.ResultCode)
  107. }
  108. // TODO: don't ignore the nonce and make sure it's the same?
  109. externalPort := binary.BigEndian.Uint16(resp[42:44])
  110. externalIPBytes := [16]byte{}
  111. copy(externalIPBytes[:], resp[44:])
  112. externalIP := netip.AddrFrom16(externalIPBytes).Unmap()
  113. external := netip.AddrPortFrom(externalIP, externalPort)
  114. lifetime := time.Second * time.Duration(res.Lifetime)
  115. now := time.Now()
  116. mapping := &pcpMapping{
  117. external: external,
  118. renewAfter: now.Add(lifetime / 2),
  119. goodUntil: now.Add(lifetime),
  120. epoch: res.Epoch,
  121. }
  122. return mapping, nil
  123. }
  124. // pcpAnnounceRequest generates a PCP packet with an ANNOUNCE opcode.
  125. func pcpAnnounceRequest(myIP netip.Addr) []byte {
  126. // See https://tools.ietf.org/html/rfc6887#section-7.1
  127. pkt := make([]byte, 24)
  128. pkt[0] = pcpVersion
  129. pkt[1] = pcpOpAnnounce
  130. myIP16 := myIP.As16()
  131. copy(pkt[8:], myIP16[:])
  132. return pkt
  133. }
  134. type pcpResponse struct {
  135. OpCode uint8
  136. ResultCode pcpResultCode
  137. Lifetime uint32
  138. Epoch uint32
  139. }
  140. func parsePCPResponse(b []byte) (res pcpResponse, ok bool) {
  141. if len(b) < 24 || b[0] != pcpVersion {
  142. return
  143. }
  144. res.OpCode = b[1]
  145. res.ResultCode = pcpResultCode(b[3])
  146. res.Lifetime = binary.BigEndian.Uint32(b[4:])
  147. res.Epoch = binary.BigEndian.Uint32(b[8:])
  148. return res, true
  149. }