mapper.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package portmapper
  2. import (
  3. "errors"
  4. "fmt"
  5. "net"
  6. "sync"
  7. log "github.com/Sirupsen/logrus"
  8. "github.com/docker/docker/daemon/networkdriver/portallocator"
  9. "github.com/docker/docker/pkg/iptables"
  10. )
  11. type mapping struct {
  12. proto string
  13. userlandProxy UserlandProxy
  14. host net.Addr
  15. container net.Addr
  16. }
  17. var (
  18. chain *iptables.Chain
  19. lock sync.Mutex
  20. // udp:ip:port
  21. currentMappings = make(map[string]*mapping)
  22. NewProxy = NewProxyCommand
  23. )
  24. var (
  25. ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
  26. ErrPortMappedForIP = errors.New("port is already mapped to ip")
  27. ErrPortNotMapped = errors.New("port is not mapped")
  28. )
  29. func SetIptablesChain(c *iptables.Chain) {
  30. chain = c
  31. }
  32. func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) {
  33. lock.Lock()
  34. defer lock.Unlock()
  35. var (
  36. m *mapping
  37. proto string
  38. allocatedHostPort int
  39. proxy UserlandProxy
  40. )
  41. switch container.(type) {
  42. case *net.TCPAddr:
  43. proto = "tcp"
  44. if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
  45. return nil, err
  46. }
  47. m = &mapping{
  48. proto: proto,
  49. host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort},
  50. container: container,
  51. }
  52. proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port)
  53. case *net.UDPAddr:
  54. proto = "udp"
  55. if allocatedHostPort, err = portallocator.RequestPort(hostIP, proto, hostPort); err != nil {
  56. return nil, err
  57. }
  58. m = &mapping{
  59. proto: proto,
  60. host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort},
  61. container: container,
  62. }
  63. proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port)
  64. default:
  65. return nil, ErrUnknownBackendAddressType
  66. }
  67. // release the allocated port on any further error during return.
  68. defer func() {
  69. if err != nil {
  70. portallocator.ReleasePort(hostIP, proto, allocatedHostPort)
  71. }
  72. }()
  73. key := getKey(m.host)
  74. if _, exists := currentMappings[key]; exists {
  75. return nil, ErrPortMappedForIP
  76. }
  77. containerIP, containerPort := getIPAndPort(m.container)
  78. if err := forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
  79. return nil, err
  80. }
  81. cleanup := func() error {
  82. // need to undo the iptables rules before we return
  83. proxy.Stop()
  84. forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
  85. if err := portallocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
  86. return err
  87. }
  88. return nil
  89. }
  90. if err := proxy.Start(); err != nil {
  91. if err := cleanup(); err != nil {
  92. return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
  93. }
  94. return nil, err
  95. }
  96. m.userlandProxy = proxy
  97. currentMappings[key] = m
  98. return m.host, nil
  99. }
  100. func Unmap(host net.Addr) error {
  101. lock.Lock()
  102. defer lock.Unlock()
  103. key := getKey(host)
  104. data, exists := currentMappings[key]
  105. if !exists {
  106. return ErrPortNotMapped
  107. }
  108. data.userlandProxy.Stop()
  109. delete(currentMappings, key)
  110. containerIP, containerPort := getIPAndPort(data.container)
  111. hostIP, hostPort := getIPAndPort(data.host)
  112. if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
  113. log.Errorf("Error on iptables delete: %s", err)
  114. }
  115. switch a := host.(type) {
  116. case *net.TCPAddr:
  117. return portallocator.ReleasePort(a.IP, "tcp", a.Port)
  118. case *net.UDPAddr:
  119. return portallocator.ReleasePort(a.IP, "udp", a.Port)
  120. }
  121. return nil
  122. }
  123. func getKey(a net.Addr) string {
  124. switch t := a.(type) {
  125. case *net.TCPAddr:
  126. return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
  127. case *net.UDPAddr:
  128. return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
  129. }
  130. return ""
  131. }
  132. func getIPAndPort(a net.Addr) (net.IP, int) {
  133. switch t := a.(type) {
  134. case *net.TCPAddr:
  135. return t.IP, t.Port
  136. case *net.UDPAddr:
  137. return t.IP, t.Port
  138. }
  139. return nil, 0
  140. }
  141. func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
  142. if chain == nil {
  143. return nil
  144. }
  145. return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
  146. }