mapper.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package portmapper
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/dotcloud/docker/pkg/iptables"
  6. "github.com/dotcloud/docker/pkg/proxy"
  7. "net"
  8. "sync"
  9. )
  10. type mapping struct {
  11. proto string
  12. userlandProxy proxy.Proxy
  13. host net.Addr
  14. container net.Addr
  15. }
  16. var (
  17. chain *iptables.Chain
  18. lock sync.Mutex
  19. // udp:ip:port
  20. currentMappings = make(map[string]*mapping)
  21. newProxy = proxy.NewProxy
  22. )
  23. var (
  24. ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
  25. ErrPortMappedForIP = errors.New("port is already mapped to ip")
  26. ErrPortNotMapped = errors.New("port is not mapped")
  27. )
  28. func SetIptablesChain(c *iptables.Chain) {
  29. chain = c
  30. }
  31. func Map(container net.Addr, hostIP net.IP, hostPort int) error {
  32. lock.Lock()
  33. defer lock.Unlock()
  34. var m *mapping
  35. switch container.(type) {
  36. case *net.TCPAddr:
  37. m = &mapping{
  38. proto: "tcp",
  39. host: &net.TCPAddr{IP: hostIP, Port: hostPort},
  40. container: container,
  41. }
  42. case *net.UDPAddr:
  43. m = &mapping{
  44. proto: "udp",
  45. host: &net.UDPAddr{IP: hostIP, Port: hostPort},
  46. container: container,
  47. }
  48. default:
  49. return ErrUnknownBackendAddressType
  50. }
  51. key := getKey(m.host)
  52. if _, exists := currentMappings[key]; exists {
  53. return ErrPortMappedForIP
  54. }
  55. containerIP, containerPort := getIPAndPort(m.container)
  56. if err := forward(iptables.Add, m.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
  57. return err
  58. }
  59. p, err := newProxy(m.host, m.container)
  60. if err != nil {
  61. // need to undo the iptables rules before we reutrn
  62. forward(iptables.Delete, m.proto, hostIP, hostPort, containerIP.String(), containerPort)
  63. return err
  64. }
  65. m.userlandProxy = p
  66. currentMappings[key] = m
  67. go p.Run()
  68. return nil
  69. }
  70. func Unmap(host net.Addr) error {
  71. lock.Lock()
  72. defer lock.Unlock()
  73. key := getKey(host)
  74. data, exists := currentMappings[key]
  75. if !exists {
  76. return ErrPortNotMapped
  77. }
  78. data.userlandProxy.Close()
  79. delete(currentMappings, key)
  80. containerIP, containerPort := getIPAndPort(data.container)
  81. hostIP, hostPort := getIPAndPort(data.host)
  82. if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
  83. return err
  84. }
  85. return nil
  86. }
  87. func getKey(a net.Addr) string {
  88. switch t := a.(type) {
  89. case *net.TCPAddr:
  90. return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
  91. case *net.UDPAddr:
  92. return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
  93. }
  94. return ""
  95. }
  96. func getIPAndPort(a net.Addr) (net.IP, int) {
  97. switch t := a.(type) {
  98. case *net.TCPAddr:
  99. return t.IP, t.Port
  100. case *net.UDPAddr:
  101. return t.IP, t.Port
  102. }
  103. return nil, 0
  104. }
  105. func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
  106. if chain == nil {
  107. return nil
  108. }
  109. return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort)
  110. }