nat.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * Copyright (c) 2023, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package inproxy
  20. import (
  21. "fmt"
  22. )
  23. // NATMapping is a NAT mapping behavior defined in RFC 4787, section 4.1.
  24. type NATMapping int32
  25. const (
  26. NATMappingUnknown NATMapping = iota
  27. NATMappingEndpointIndependent
  28. NATMappingAddressDependent
  29. NATMappingAddressPortDependent
  30. )
  31. func (m NATMapping) String() string {
  32. switch m {
  33. case NATMappingUnknown:
  34. return "MappingUnknown"
  35. case NATMappingEndpointIndependent:
  36. return "MappingEndpointIndependent"
  37. case NATMappingAddressDependent:
  38. return "MappingAddressDependent"
  39. case NATMappingAddressPortDependent:
  40. return "MappingAddressPortDependent"
  41. }
  42. return ""
  43. }
  44. // MarshalText ensures the string representation of the value is logged in
  45. // JSON.
  46. func (m NATMapping) MarshalText() ([]byte, error) {
  47. return []byte(m.String()), nil
  48. }
  49. func (m NATMapping) IsValid() bool {
  50. return m.String() != ""
  51. }
  52. // NATMapping is a NAT filtering behavior defined in RFC 4787, section 5.
  53. type NATFiltering int32
  54. const (
  55. NATFilteringUnknown NATFiltering = iota
  56. NATFilteringEndpointIndependent
  57. NATFilteringAddressDependent
  58. NATFilteringAddressPortDependent
  59. )
  60. func (f NATFiltering) String() string {
  61. switch f {
  62. case NATFilteringUnknown:
  63. return "FilteringUnknown"
  64. case NATFilteringEndpointIndependent:
  65. return "FilteringEndpointIndependent"
  66. case NATFilteringAddressDependent:
  67. return "FilteringAddressDependent"
  68. case NATFilteringAddressPortDependent:
  69. return "FilteringAddressPortDependent"
  70. }
  71. return ""
  72. }
  73. // MarshalText ensures the string representation of the value is logged in
  74. // JSON.
  75. func (f NATFiltering) MarshalText() ([]byte, error) {
  76. return []byte(f.String()), nil
  77. }
  78. func (f NATFiltering) IsValid() bool {
  79. return f.String() != ""
  80. }
  81. // NATType specifies a network's NAT behavior and consists of a NATMapping and
  82. // a NATFiltering component.
  83. type NATType int32
  84. // MakeNATType creates a new NATType.
  85. func MakeNATType(mapping NATMapping, filtering NATFiltering) NATType {
  86. return (NATType(mapping) << 2) | NATType(filtering)
  87. }
  88. var (
  89. NATTypeUnknown = MakeNATType(NATMappingUnknown, NATFilteringUnknown)
  90. // NATTypePortMapping is a pseudo NATType, used in matching, that
  91. // represents the relevant NAT behavior of a port mapping (e.g., UPnP-IGD).
  92. NATTypePortMapping = MakeNATType(NATMappingEndpointIndependent, NATFilteringEndpointIndependent)
  93. // NATTypeMobileNetwork is a pseudo NATType, usied in matching, that
  94. // represents the assumed and relevent NAT behavior of clients on mobile
  95. // networks, presumed to be behind CGNAT when they report NATTypeUnknown.
  96. NATTypeMobileNetwork = MakeNATType(NATMappingAddressPortDependent, NATFilteringAddressPortDependent)
  97. // NATTypeNone and the following NATType constants are used in testing.
  98. // They are not entirely precise (a symmetric NAT may have a different
  99. // mix of mapping and filtering values). The matching logic does not use
  100. // specific NAT type definitions and instead considers the reported
  101. // mapping and filtering values.
  102. NATTypeNone = MakeNATType(NATMappingEndpointIndependent, NATFilteringEndpointIndependent)
  103. NATTypeFullCone = MakeNATType(NATMappingEndpointIndependent, NATFilteringEndpointIndependent)
  104. NATTypeRestrictedCone = MakeNATType(NATMappingEndpointIndependent, NATFilteringAddressDependent)
  105. NATTypePortRestrictedCone = MakeNATType(NATMappingEndpointIndependent, NATFilteringAddressPortDependent)
  106. NATTypeSymmetric = MakeNATType(NATMappingAddressPortDependent, NATFilteringAddressPortDependent)
  107. )
  108. // NeedsDiscovery indicates that the NATType is unknown and should be
  109. // discovered.
  110. func (t NATType) NeedsDiscovery() bool {
  111. return t == NATTypeUnknown
  112. }
  113. // Mapping extracts the NATMapping component of this NATType.
  114. func (t NATType) Mapping() NATMapping {
  115. return NATMapping(t >> 2)
  116. }
  117. // Filtering extracts the NATFiltering component of this NATType.
  118. func (t NATType) Filtering() NATFiltering {
  119. return NATFiltering(t & 0x3)
  120. }
  121. // Traversal returns the NATTraversal classification for this NATType.
  122. func (t NATType) Traversal() NATTraversal {
  123. return MakeTraversal(t)
  124. }
  125. // Compatible indicates whether the NATType NATTraversals are compatible.
  126. func (t NATType) Compatible(t1 NATType) bool {
  127. return t.Traversal().Compatible(t1.Traversal())
  128. }
  129. // IsPreferredMatch indicates whether the peer NATType's NATTraversal is
  130. // preferred.
  131. func (t NATType) IsPreferredMatch(t1 NATType) bool {
  132. return t.Traversal().IsPreferredMatch(t1.Traversal())
  133. }
  134. // ExistsPreferredMatch indicates whhether there exists a preferred match for
  135. // the NATType's NATTraversal.
  136. func (t NATType) ExistsPreferredMatch(unlimited, partiallyLimited, limited bool) bool {
  137. return t.Traversal().ExistsPreferredMatch(unlimited, partiallyLimited, limited)
  138. }
  139. func (t NATType) String() string {
  140. return fmt.Sprintf(
  141. "%s/%s", t.Mapping().String(), t.Filtering().String())
  142. }
  143. // MarshalText ensures the string representation of the value is logged in
  144. // JSON.
  145. func (t NATType) MarshalText() ([]byte, error) {
  146. return []byte(t.String()), nil
  147. }
  148. func (t NATType) IsValid() bool {
  149. return t.Mapping().IsValid() && t.Filtering().IsValid()
  150. }
  151. // NATTraversal classifies the NAT traversal potential for a NATType. NATTypes
  152. // are determined to be compatible -- that is, a connection between the
  153. // corresponding networks can be established via STUN hole punching -- based
  154. // on their respective NATTraversal classifications.
  155. type NATTraversal int32
  156. const (
  157. NATTraversalUnlimited NATTraversal = iota
  158. NATTraversalPartiallyLimited
  159. NATTraversalStrictlyLimited
  160. )
  161. // MakeTraversal returns the NATTraversal classification for the given
  162. // NATType.
  163. func MakeTraversal(t NATType) NATTraversal {
  164. mapping := t.Mapping()
  165. filtering := t.Filtering()
  166. if mapping == NATMappingEndpointIndependent {
  167. if filtering != NATFilteringAddressPortDependent {
  168. // NAT type is, e.g., none, full cone, or restricted cone.
  169. return NATTraversalUnlimited
  170. }
  171. // NAT type is, e.g., port restricted cone.
  172. return NATTraversalPartiallyLimited
  173. }
  174. // NAT type is, e.g., symmetric; or unknown -- where we assume the worst
  175. // case.
  176. return NATTraversalStrictlyLimited
  177. }
  178. // Compatible indicates whether the NATTraversals are compatible.
  179. func (t NATTraversal) Compatible(t1 NATTraversal) bool {
  180. // See the NAT compatibility matrix here:
  181. // https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/wikis/NAT-matching#nat-compatibility
  182. switch t {
  183. case NATTraversalUnlimited:
  184. // t1 can be any value when t is unlimited.
  185. return true
  186. case NATTraversalPartiallyLimited:
  187. // t1 can be unlimited or partially limited when t is partially limited.
  188. return t1 != NATTraversalStrictlyLimited
  189. case NATTraversalStrictlyLimited:
  190. // t1 must be unlimited when t is limited.
  191. return t1 == NATTraversalUnlimited
  192. }
  193. return false
  194. }
  195. // IsPreferredMatch indicates whether the peer NATTraversal is a preferred
  196. // match for this NATTraversal. A match is preferred, and so prioritized,
  197. // when one of the two NATTraversals is more limited, but the pair is still
  198. // compatible. This preference attempt to reserve less limited match
  199. // candidates for those peers that need them.
  200. func (t NATTraversal) IsPreferredMatch(t1 NATTraversal) bool {
  201. switch t {
  202. case NATTraversalUnlimited:
  203. // Prefer matching unlimited peers with strictly limited peers.
  204. // TODO: prefer matching unlimited with partially limited?
  205. return t1 == NATTraversalStrictlyLimited
  206. case NATTraversalPartiallyLimited:
  207. // Prefer matching partially limited peers with unlimited or other
  208. // partially limited peers.
  209. return t1 == NATTraversalUnlimited || t1 == NATTraversalPartiallyLimited
  210. case NATTraversalStrictlyLimited:
  211. // Prefer matching strictly limited peers with unlimited peers.
  212. return t1 == NATTraversalUnlimited
  213. }
  214. return false
  215. }
  216. // ExistsPreferredMatch indicates whether a preferred match exists, for this
  217. // NATTraversal, when there are unlimited/partiallyLimited/strictlyLimited candidates
  218. // available.
  219. func (t NATTraversal) ExistsPreferredMatch(unlimited, partiallyLimited, strictlyLimited bool) bool {
  220. switch t {
  221. case NATTraversalUnlimited:
  222. return strictlyLimited
  223. case NATTraversalPartiallyLimited:
  224. return unlimited || partiallyLimited
  225. case NATTraversalStrictlyLimited:
  226. return unlimited
  227. }
  228. return false
  229. }
  230. // PortMappingType is a port mapping protocol supported by a network. Values
  231. // include UPnP-IGD, NAT-PMP, and PCP.
  232. type PortMappingType int32
  233. const (
  234. PortMappingTypeNone PortMappingType = iota
  235. PortMappingTypeUPnP
  236. PortMappingTypePMP
  237. PortMappingTypePCP
  238. )
  239. func (t PortMappingType) String() string {
  240. switch t {
  241. case PortMappingTypeNone:
  242. return "None"
  243. case PortMappingTypeUPnP:
  244. return "UPnP-IGD"
  245. case PortMappingTypePMP:
  246. return "PMP"
  247. case PortMappingTypePCP:
  248. return "PCP"
  249. }
  250. return ""
  251. }
  252. // MarshalText ensures the string representation of the value is logged in
  253. // JSON.
  254. func (t PortMappingType) MarshalText() ([]byte, error) {
  255. return []byte(t.String()), nil
  256. }
  257. func (t PortMappingType) IsValid() bool {
  258. return t.String() != ""
  259. }
  260. // PortMappingTypes is a list of port mapping protocol supported by a
  261. // network.
  262. type PortMappingTypes []PortMappingType
  263. // NeedsDiscovery indicates that the list of port mapping types is empty and
  264. // should be discovered. If a network has no supported port mapping types,
  265. // its list will include PortMappingTypeNone.
  266. func (t PortMappingTypes) NeedsDiscovery() bool {
  267. return len(t) == 0
  268. }
  269. // Available indicates that at least one port mapping protocol is supported.
  270. func (t PortMappingTypes) Available() bool {
  271. for _, portMappingType := range t {
  272. if portMappingType > PortMappingTypeNone {
  273. return true
  274. }
  275. }
  276. return false
  277. }
  278. func (t PortMappingTypes) IsValid() bool {
  279. for _, portMappingType := range t {
  280. if !portMappingType.IsValid() {
  281. return false
  282. }
  283. }
  284. return true
  285. }
  286. // ICECandidateType is an ICE candidate type: host for public addresses, port
  287. // mapping for when a port mapping protocol was used to establish a public
  288. // address, or server reflexive when STUN hole punching was used to create a
  289. // public address. Peer reflexive candidates emerge during the ICE
  290. // negotiation process and are not SDP entries.
  291. type ICECandidateType int32
  292. const (
  293. ICECandidateUnknown ICECandidateType = iota
  294. ICECandidateHost
  295. ICECandidatePortMapping
  296. ICECandidateServerReflexive
  297. ICECandidatePeerReflexive
  298. )
  299. func (t ICECandidateType) String() string {
  300. switch t {
  301. case ICECandidateUnknown:
  302. return "Unknown"
  303. case ICECandidateHost:
  304. return "Host"
  305. case ICECandidatePortMapping:
  306. return "PortMapping"
  307. case ICECandidateServerReflexive:
  308. return "ServerReflexive"
  309. case ICECandidatePeerReflexive:
  310. return "PeerReflexive"
  311. }
  312. return ""
  313. }
  314. // MarshalText ensures the string representation of the value is logged in
  315. // JSON.
  316. func (t ICECandidateType) MarshalText() ([]byte, error) {
  317. return []byte(t.String()), nil
  318. }
  319. func (t ICECandidateType) IsValid() bool {
  320. return t.String() != ""
  321. }
  322. // ICECandidateTypes is a list of ICE candidate types.
  323. type ICECandidateTypes []ICECandidateType
  324. func (t ICECandidateTypes) IsValid() bool {
  325. for _, candidateType := range t {
  326. if !candidateType.IsValid() {
  327. return false
  328. }
  329. }
  330. return true
  331. }