nat.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. package nat
  2. // nat is a convenience package for docker's manipulation of strings describing
  3. // network ports.
  4. import (
  5. "fmt"
  6. "net"
  7. "strconv"
  8. "strings"
  9. "github.com/docker/docker/pkg/parsers"
  10. )
  11. const (
  12. // portSpecTemplate is the expected format for port specifications
  13. portSpecTemplate = "ip:hostPort:containerPort"
  14. )
  15. // PortBinding represents a binding between a Host IP address and a Host Port
  16. type PortBinding struct {
  17. // HostIP is the host IP Address
  18. HostIP string `json:"HostIp"`
  19. // HostPort is the host port number
  20. HostPort string
  21. }
  22. // PortMap is a collection of PortBinding indexed by Port
  23. type PortMap map[Port][]PortBinding
  24. // PortSet is a collection of structs indexed by Port
  25. type PortSet map[Port]struct{}
  26. // Port is a string containing port number and protocol in the format "80/tcp"
  27. type Port string
  28. // NewPort creates a new instance of a Port given a protocol and port number or port range
  29. func NewPort(proto, port string) (Port, error) {
  30. // Check for parsing issues on "port" now so we can avoid having
  31. // to check it later on.
  32. portStartInt, portEndInt, err := ParsePortRange(port)
  33. if err != nil {
  34. return "", err
  35. }
  36. if portStartInt == portEndInt {
  37. return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil
  38. }
  39. return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil
  40. }
  41. // ParsePort parses the port number string and returns an int
  42. func ParsePort(rawPort string) (int, error) {
  43. if len(rawPort) == 0 {
  44. return 0, nil
  45. }
  46. port, err := strconv.ParseUint(rawPort, 10, 16)
  47. if err != nil {
  48. return 0, err
  49. }
  50. return int(port), nil
  51. }
  52. // ParsePortRange parses the port range string and returns start/end ints
  53. func ParsePortRange(rawPort string) (int, int, error) {
  54. if len(rawPort) == 0 {
  55. return 0, 0, nil
  56. }
  57. start, end, err := parsers.ParsePortRange(rawPort)
  58. if err != nil {
  59. return 0, 0, err
  60. }
  61. return int(start), int(end), nil
  62. }
  63. // Proto returns the protocol of a Port
  64. func (p Port) Proto() string {
  65. proto, _ := SplitProtoPort(string(p))
  66. return proto
  67. }
  68. // Port returns the port number of a Port
  69. func (p Port) Port() string {
  70. _, port := SplitProtoPort(string(p))
  71. return port
  72. }
  73. // Int returns the port number of a Port as an int
  74. func (p Port) Int() int {
  75. portStr := p.Port()
  76. if len(portStr) == 0 {
  77. return 0
  78. }
  79. // We don't need to check for an error because we're going to
  80. // assume that any error would have been found, and reported, in NewPort()
  81. port, _ := strconv.ParseUint(portStr, 10, 16)
  82. return int(port)
  83. }
  84. // Range returns the start/end port numbers of a Port range as ints
  85. func (p Port) Range() (int, int, error) {
  86. return ParsePortRange(p.Port())
  87. }
  88. // SplitProtoPort splits a port in the format of proto/port
  89. func SplitProtoPort(rawPort string) (string, string) {
  90. parts := strings.Split(rawPort, "/")
  91. l := len(parts)
  92. if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 {
  93. return "", ""
  94. }
  95. if l == 1 {
  96. return "tcp", rawPort
  97. }
  98. if len(parts[1]) == 0 {
  99. return "tcp", parts[0]
  100. }
  101. return parts[1], parts[0]
  102. }
  103. func validateProto(proto string) bool {
  104. for _, availableProto := range []string{"tcp", "udp"} {
  105. if availableProto == proto {
  106. return true
  107. }
  108. }
  109. return false
  110. }
  111. // ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses
  112. // these in to the internal types
  113. func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
  114. var (
  115. exposedPorts = make(map[Port]struct{}, len(ports))
  116. bindings = make(map[Port][]PortBinding)
  117. )
  118. for _, rawPort := range ports {
  119. proto := "tcp"
  120. if i := strings.LastIndex(rawPort, "/"); i != -1 {
  121. proto = rawPort[i+1:]
  122. rawPort = rawPort[:i]
  123. }
  124. if !strings.Contains(rawPort, ":") {
  125. rawPort = fmt.Sprintf("::%s", rawPort)
  126. } else if len(strings.Split(rawPort, ":")) == 2 {
  127. rawPort = fmt.Sprintf(":%s", rawPort)
  128. }
  129. parts, err := parsers.PartParser(portSpecTemplate, rawPort)
  130. if err != nil {
  131. return nil, nil, err
  132. }
  133. var (
  134. containerPort = parts["containerPort"]
  135. rawIP = parts["ip"]
  136. hostPort = parts["hostPort"]
  137. )
  138. if rawIP != "" && net.ParseIP(rawIP) == nil {
  139. return nil, nil, fmt.Errorf("Invalid ip address: %s", rawIP)
  140. }
  141. if containerPort == "" {
  142. return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
  143. }
  144. startPort, endPort, err := parsers.ParsePortRange(containerPort)
  145. if err != nil {
  146. return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
  147. }
  148. var startHostPort, endHostPort uint64 = 0, 0
  149. if len(hostPort) > 0 {
  150. startHostPort, endHostPort, err = parsers.ParsePortRange(hostPort)
  151. if err != nil {
  152. return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
  153. }
  154. }
  155. if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
  156. // Allow host port range iff containerPort is not a range.
  157. // In this case, use the host port range as the dynamic
  158. // host port range to allocate into.
  159. if endPort != startPort {
  160. return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
  161. }
  162. }
  163. if !validateProto(strings.ToLower(proto)) {
  164. return nil, nil, fmt.Errorf("Invalid proto: %s", proto)
  165. }
  166. for i := uint64(0); i <= (endPort - startPort); i++ {
  167. containerPort = strconv.FormatUint(startPort+i, 10)
  168. if len(hostPort) > 0 {
  169. hostPort = strconv.FormatUint(startHostPort+i, 10)
  170. }
  171. // Set hostPort to a range only if there is a single container port
  172. // and a dynamic host port.
  173. if startPort == endPort && startHostPort != endHostPort {
  174. hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10))
  175. }
  176. port, err := NewPort(strings.ToLower(proto), containerPort)
  177. if err != nil {
  178. return nil, nil, err
  179. }
  180. if _, exists := exposedPorts[port]; !exists {
  181. exposedPorts[port] = struct{}{}
  182. }
  183. binding := PortBinding{
  184. HostIP: rawIP,
  185. HostPort: hostPort,
  186. }
  187. bslice, exists := bindings[port]
  188. if !exists {
  189. bslice = []PortBinding{}
  190. }
  191. bindings[port] = append(bslice, binding)
  192. }
  193. }
  194. return exposedPorts, bindings, nil
  195. }