mapper.go 7.3 KB

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