mapper.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. package portmapper
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "github.com/containerd/log"
  8. "github.com/docker/docker/libnetwork/portallocator"
  9. "github.com/ishidawataru/sctp"
  10. )
  11. type mapping struct {
  12. proto string
  13. userlandProxy userlandProxy
  14. host net.Addr
  15. container net.Addr
  16. }
  17. // newProxy is used to mock out the proxy server in tests
  18. var newProxy = newProxyCommand
  19. var (
  20. // ErrUnknownBackendAddressType refers to an unknown container or unsupported address type
  21. ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
  22. // ErrPortMappedForIP refers to a port already mapped to an ip address
  23. ErrPortMappedForIP = errors.New("port is already mapped to ip")
  24. // ErrPortNotMapped refers to an unmapped port
  25. ErrPortNotMapped = errors.New("port is not mapped")
  26. // ErrSCTPAddrNoIP refers to a SCTP address without IP address.
  27. ErrSCTPAddrNoIP = errors.New("sctp address does not contain any IP address")
  28. )
  29. // New returns a new instance of PortMapper
  30. func New() *PortMapper {
  31. return NewWithPortAllocator(portallocator.Get(), "")
  32. }
  33. // NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator
  34. func NewWithPortAllocator(allocator *portallocator.PortAllocator, proxyPath string) *PortMapper {
  35. return &PortMapper{
  36. currentMappings: make(map[string]*mapping),
  37. allocator: allocator,
  38. proxyPath: proxyPath,
  39. }
  40. }
  41. // Map maps the specified container transport address to the host's network address and transport port
  42. func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) {
  43. return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy)
  44. }
  45. // MapRange maps the specified container transport address to the host's network address and transport port range
  46. func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) {
  47. pm.lock.Lock()
  48. defer pm.lock.Unlock()
  49. var (
  50. m *mapping
  51. proto string
  52. allocatedHostPort int
  53. )
  54. switch t := container.(type) {
  55. case *net.TCPAddr:
  56. proto = "tcp"
  57. if allocatedHostPort, err = pm.allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
  58. return nil, err
  59. }
  60. m = &mapping{
  61. proto: proto,
  62. host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort},
  63. container: container,
  64. }
  65. if useProxy {
  66. m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, t.IP, t.Port, pm.proxyPath)
  67. if err != nil {
  68. return nil, err
  69. }
  70. } else {
  71. m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort)
  72. if err != nil {
  73. return nil, err
  74. }
  75. }
  76. case *net.UDPAddr:
  77. proto = "udp"
  78. if allocatedHostPort, err = pm.allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
  79. return nil, err
  80. }
  81. m = &mapping{
  82. proto: proto,
  83. host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort},
  84. container: container,
  85. }
  86. if useProxy {
  87. m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, t.IP, t.Port, pm.proxyPath)
  88. if err != nil {
  89. return nil, err
  90. }
  91. } else {
  92. m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort)
  93. if err != nil {
  94. return nil, err
  95. }
  96. }
  97. case *sctp.SCTPAddr:
  98. proto = "sctp"
  99. if allocatedHostPort, err = pm.allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
  100. return nil, err
  101. }
  102. m = &mapping{
  103. proto: proto,
  104. host: &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: allocatedHostPort},
  105. container: container,
  106. }
  107. if useProxy {
  108. sctpAddr := container.(*sctp.SCTPAddr)
  109. if len(sctpAddr.IPAddrs) == 0 {
  110. return nil, ErrSCTPAddrNoIP
  111. }
  112. m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IPAddrs[0].IP, sctpAddr.Port, pm.proxyPath)
  113. if err != nil {
  114. return nil, err
  115. }
  116. } else {
  117. m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort)
  118. if err != nil {
  119. return nil, err
  120. }
  121. }
  122. default:
  123. return nil, ErrUnknownBackendAddressType
  124. }
  125. // release the allocated port on any further error during return.
  126. defer func() {
  127. if err != nil {
  128. pm.allocator.ReleasePort(hostIP, proto, allocatedHostPort)
  129. }
  130. }()
  131. key := getKey(m.host)
  132. if _, exists := pm.currentMappings[key]; exists {
  133. return nil, ErrPortMappedForIP
  134. }
  135. containerIP, containerPort := getIPAndPort(m.container)
  136. if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
  137. return nil, err
  138. }
  139. cleanup := func() error {
  140. // need to undo the iptables rules before we return
  141. m.userlandProxy.Stop()
  142. pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
  143. if err := pm.allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
  144. return err
  145. }
  146. return nil
  147. }
  148. if err := m.userlandProxy.Start(); err != nil {
  149. if err := cleanup(); err != nil {
  150. return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
  151. }
  152. return nil, err
  153. }
  154. pm.currentMappings[key] = m
  155. return m.host, nil
  156. }
  157. // Unmap removes stored mapping for the specified host transport address
  158. func (pm *PortMapper) Unmap(host net.Addr) error {
  159. pm.lock.Lock()
  160. defer pm.lock.Unlock()
  161. key := getKey(host)
  162. data, exists := pm.currentMappings[key]
  163. if !exists {
  164. return ErrPortNotMapped
  165. }
  166. if data.userlandProxy != nil {
  167. data.userlandProxy.Stop()
  168. }
  169. delete(pm.currentMappings, key)
  170. containerIP, containerPort := getIPAndPort(data.container)
  171. hostIP, hostPort := getIPAndPort(data.host)
  172. if err := pm.DeleteForwardingTableEntry(data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
  173. log.G(context.TODO()).Errorf("Error on iptables delete: %s", err)
  174. }
  175. switch a := host.(type) {
  176. case *net.TCPAddr:
  177. return pm.allocator.ReleasePort(a.IP, "tcp", a.Port)
  178. case *net.UDPAddr:
  179. return pm.allocator.ReleasePort(a.IP, "udp", a.Port)
  180. case *sctp.SCTPAddr:
  181. if len(a.IPAddrs) == 0 {
  182. return ErrSCTPAddrNoIP
  183. }
  184. return pm.allocator.ReleasePort(a.IPAddrs[0].IP, "sctp", a.Port)
  185. }
  186. return ErrUnknownBackendAddressType
  187. }
  188. // ReMapAll re-applies all port mappings
  189. func (pm *PortMapper) ReMapAll() {
  190. pm.lock.Lock()
  191. defer pm.lock.Unlock()
  192. log.G(context.TODO()).Debugln("Re-applying all port mappings.")
  193. for _, data := range pm.currentMappings {
  194. containerIP, containerPort := getIPAndPort(data.container)
  195. hostIP, hostPort := getIPAndPort(data.host)
  196. if err := pm.AppendForwardingTableEntry(data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
  197. log.G(context.TODO()).Errorf("Error on iptables add: %s", err)
  198. }
  199. }
  200. }
  201. func getKey(a net.Addr) string {
  202. switch t := a.(type) {
  203. case *net.TCPAddr:
  204. return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
  205. case *net.UDPAddr:
  206. return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
  207. case *sctp.SCTPAddr:
  208. if len(t.IPAddrs) == 0 {
  209. log.G(context.TODO()).Error(ErrSCTPAddrNoIP)
  210. return ""
  211. }
  212. return fmt.Sprintf("%s:%d/%s", t.IPAddrs[0].IP.String(), t.Port, "sctp")
  213. }
  214. return ""
  215. }
  216. func getIPAndPort(a net.Addr) (net.IP, int) {
  217. switch t := a.(type) {
  218. case *net.TCPAddr:
  219. return t.IP, t.Port
  220. case *net.UDPAddr:
  221. return t.IP, t.Port
  222. case *sctp.SCTPAddr:
  223. if len(t.IPAddrs) == 0 {
  224. log.G(context.TODO()).Error(ErrSCTPAddrNoIP)
  225. return nil, 0
  226. }
  227. return t.IPAddrs[0].IP, t.Port
  228. }
  229. return nil, 0
  230. }