mapper.go 6.7 KB

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