nat.go 12 KB

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