| 11 | 11 |
deleted file mode 100644 |
| ... | ... |
@@ -1,159 +0,0 @@ |
| 1 |
-package ipallocator |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/binary" |
|
| 5 |
- "errors" |
|
| 6 |
- "github.com/dotcloud/docker/networkdriver" |
|
| 7 |
- "github.com/dotcloud/docker/pkg/collections" |
|
| 8 |
- "net" |
|
| 9 |
- "sync" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-type networkSet map[string]*collections.OrderedIntSet |
|
| 13 |
- |
|
| 14 |
-var ( |
|
| 15 |
- ErrNoAvailableIPs = errors.New("no available ip addresses on network")
|
|
| 16 |
- ErrIPAlreadyAllocated = errors.New("ip already allocated")
|
|
| 17 |
-) |
|
| 18 |
- |
|
| 19 |
-var ( |
|
| 20 |
- lock = sync.Mutex{}
|
|
| 21 |
- allocatedIPs = networkSet{}
|
|
| 22 |
- availableIPS = networkSet{}
|
|
| 23 |
-) |
|
| 24 |
- |
|
| 25 |
-// RequestIP requests an available ip from the given network. It |
|
| 26 |
-// will return the next available ip if the ip provided is nil. If the |
|
| 27 |
-// ip provided is not nil it will validate that the provided ip is available |
|
| 28 |
-// for use or return an error |
|
| 29 |
-func RequestIP(address *net.IPNet, ip *net.IP) (*net.IP, error) {
|
|
| 30 |
- lock.Lock() |
|
| 31 |
- defer lock.Unlock() |
|
| 32 |
- |
|
| 33 |
- checkAddress(address) |
|
| 34 |
- |
|
| 35 |
- if ip == nil {
|
|
| 36 |
- next, err := getNextIp(address) |
|
| 37 |
- if err != nil {
|
|
| 38 |
- return nil, err |
|
| 39 |
- } |
|
| 40 |
- return next, nil |
|
| 41 |
- } |
|
| 42 |
- |
|
| 43 |
- if err := registerIP(address, ip); err != nil {
|
|
| 44 |
- return nil, err |
|
| 45 |
- } |
|
| 46 |
- return ip, nil |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-// ReleaseIP adds the provided ip back into the pool of |
|
| 50 |
-// available ips to be returned for use. |
|
| 51 |
-func ReleaseIP(address *net.IPNet, ip *net.IP) error {
|
|
| 52 |
- lock.Lock() |
|
| 53 |
- defer lock.Unlock() |
|
| 54 |
- |
|
| 55 |
- checkAddress(address) |
|
| 56 |
- |
|
| 57 |
- var ( |
|
| 58 |
- existing = allocatedIPs[address.String()] |
|
| 59 |
- available = availableIPS[address.String()] |
|
| 60 |
- pos = getPosition(address, ip) |
|
| 61 |
- ) |
|
| 62 |
- |
|
| 63 |
- existing.Remove(int(pos)) |
|
| 64 |
- available.Push(int(pos)) |
|
| 65 |
- |
|
| 66 |
- return nil |
|
| 67 |
-} |
|
| 68 |
- |
|
| 69 |
-// convert the ip into the position in the subnet. Only |
|
| 70 |
-// position are saved in the set |
|
| 71 |
-func getPosition(address *net.IPNet, ip *net.IP) int32 {
|
|
| 72 |
- var ( |
|
| 73 |
- first, _ = networkdriver.NetworkRange(address) |
|
| 74 |
- base = ipToInt(&first) |
|
| 75 |
- i = ipToInt(ip) |
|
| 76 |
- ) |
|
| 77 |
- return i - base |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-// return an available ip if one is currently available. If not, |
|
| 81 |
-// return the next available ip for the nextwork |
|
| 82 |
-func getNextIp(address *net.IPNet) (*net.IP, error) {
|
|
| 83 |
- var ( |
|
| 84 |
- ownIP = ipToInt(&address.IP) |
|
| 85 |
- available = availableIPS[address.String()] |
|
| 86 |
- allocated = allocatedIPs[address.String()] |
|
| 87 |
- first, _ = networkdriver.NetworkRange(address) |
|
| 88 |
- base = ipToInt(&first) |
|
| 89 |
- size = int(networkdriver.NetworkSize(address.Mask)) |
|
| 90 |
- max = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address |
|
| 91 |
- pos = int32(available.Pop()) |
|
| 92 |
- ) |
|
| 93 |
- |
|
| 94 |
- // We pop and push the position not the ip |
|
| 95 |
- if pos != 0 {
|
|
| 96 |
- ip := intToIP(int32(base + pos)) |
|
| 97 |
- allocated.Push(int(pos)) |
|
| 98 |
- |
|
| 99 |
- return ip, nil |
|
| 100 |
- } |
|
| 101 |
- |
|
| 102 |
- var ( |
|
| 103 |
- firstNetIP = address.IP.To4().Mask(address.Mask) |
|
| 104 |
- firstAsInt = ipToInt(&firstNetIP) + 1 |
|
| 105 |
- ) |
|
| 106 |
- |
|
| 107 |
- pos = int32(allocated.PullBack()) |
|
| 108 |
- for i := int32(0); i < max; i++ {
|
|
| 109 |
- pos = pos%max + 1 |
|
| 110 |
- next := int32(base + pos) |
|
| 111 |
- |
|
| 112 |
- if next == ownIP || next == firstAsInt {
|
|
| 113 |
- continue |
|
| 114 |
- } |
|
| 115 |
- |
|
| 116 |
- if !allocated.Exists(int(pos)) {
|
|
| 117 |
- ip := intToIP(next) |
|
| 118 |
- allocated.Push(int(pos)) |
|
| 119 |
- return ip, nil |
|
| 120 |
- } |
|
| 121 |
- } |
|
| 122 |
- return nil, ErrNoAvailableIPs |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-func registerIP(address *net.IPNet, ip *net.IP) error {
|
|
| 126 |
- var ( |
|
| 127 |
- existing = allocatedIPs[address.String()] |
|
| 128 |
- available = availableIPS[address.String()] |
|
| 129 |
- pos = getPosition(address, ip) |
|
| 130 |
- ) |
|
| 131 |
- |
|
| 132 |
- if existing.Exists(int(pos)) {
|
|
| 133 |
- return ErrIPAlreadyAllocated |
|
| 134 |
- } |
|
| 135 |
- available.Remove(int(pos)) |
|
| 136 |
- |
|
| 137 |
- return nil |
|
| 138 |
-} |
|
| 139 |
- |
|
| 140 |
-// Converts a 4 bytes IP into a 32 bit integer |
|
| 141 |
-func ipToInt(ip *net.IP) int32 {
|
|
| 142 |
- return int32(binary.BigEndian.Uint32(ip.To4())) |
|
| 143 |
-} |
|
| 144 |
- |
|
| 145 |
-// Converts 32 bit integer into a 4 bytes IP address |
|
| 146 |
-func intToIP(n int32) *net.IP {
|
|
| 147 |
- b := make([]byte, 4) |
|
| 148 |
- binary.BigEndian.PutUint32(b, uint32(n)) |
|
| 149 |
- ip := net.IP(b) |
|
| 150 |
- return &ip |
|
| 151 |
-} |
|
| 152 |
- |
|
| 153 |
-func checkAddress(address *net.IPNet) {
|
|
| 154 |
- key := address.String() |
|
| 155 |
- if _, exists := allocatedIPs[key]; !exists {
|
|
| 156 |
- allocatedIPs[key] = collections.NewOrderedIntSet() |
|
| 157 |
- availableIPS[key] = collections.NewOrderedIntSet() |
|
| 158 |
- } |
|
| 159 |
-} |
| 160 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,241 +0,0 @@ |
| 1 |
-package ipallocator |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "net" |
|
| 6 |
- "testing" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func reset() {
|
|
| 10 |
- allocatedIPs = networkSet{}
|
|
| 11 |
- availableIPS = networkSet{}
|
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-func TestRequestNewIps(t *testing.T) {
|
|
| 15 |
- defer reset() |
|
| 16 |
- network := &net.IPNet{
|
|
| 17 |
- IP: []byte{192, 168, 0, 1},
|
|
| 18 |
- Mask: []byte{255, 255, 255, 0},
|
|
| 19 |
- } |
|
| 20 |
- |
|
| 21 |
- for i := 2; i < 10; i++ {
|
|
| 22 |
- ip, err := RequestIP(network, nil) |
|
| 23 |
- if err != nil {
|
|
| 24 |
- t.Fatal(err) |
|
| 25 |
- } |
|
| 26 |
- |
|
| 27 |
- if expected := fmt.Sprintf("192.168.0.%d", i); ip.String() != expected {
|
|
| 28 |
- t.Fatalf("Expected ip %s got %s", expected, ip.String())
|
|
| 29 |
- } |
|
| 30 |
- } |
|
| 31 |
-} |
|
| 32 |
- |
|
| 33 |
-func TestReleaseIp(t *testing.T) {
|
|
| 34 |
- defer reset() |
|
| 35 |
- network := &net.IPNet{
|
|
| 36 |
- IP: []byte{192, 168, 0, 1},
|
|
| 37 |
- Mask: []byte{255, 255, 255, 0},
|
|
| 38 |
- } |
|
| 39 |
- |
|
| 40 |
- ip, err := RequestIP(network, nil) |
|
| 41 |
- if err != nil {
|
|
| 42 |
- t.Fatal(err) |
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- if err := ReleaseIP(network, ip); err != nil {
|
|
| 46 |
- t.Fatal(err) |
|
| 47 |
- } |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-func TestGetReleasedIp(t *testing.T) {
|
|
| 51 |
- defer reset() |
|
| 52 |
- network := &net.IPNet{
|
|
| 53 |
- IP: []byte{192, 168, 0, 1},
|
|
| 54 |
- Mask: []byte{255, 255, 255, 0},
|
|
| 55 |
- } |
|
| 56 |
- |
|
| 57 |
- ip, err := RequestIP(network, nil) |
|
| 58 |
- if err != nil {
|
|
| 59 |
- t.Fatal(err) |
|
| 60 |
- } |
|
| 61 |
- |
|
| 62 |
- value := ip.String() |
|
| 63 |
- if err := ReleaseIP(network, ip); err != nil {
|
|
| 64 |
- t.Fatal(err) |
|
| 65 |
- } |
|
| 66 |
- |
|
| 67 |
- ip, err = RequestIP(network, nil) |
|
| 68 |
- if err != nil {
|
|
| 69 |
- t.Fatal(err) |
|
| 70 |
- } |
|
| 71 |
- |
|
| 72 |
- if ip.String() != value {
|
|
| 73 |
- t.Fatalf("Expected to receive same ip %s got %s", value, ip.String())
|
|
| 74 |
- } |
|
| 75 |
-} |
|
| 76 |
- |
|
| 77 |
-func TestRequesetSpecificIp(t *testing.T) {
|
|
| 78 |
- defer reset() |
|
| 79 |
- network := &net.IPNet{
|
|
| 80 |
- IP: []byte{192, 168, 0, 1},
|
|
| 81 |
- Mask: []byte{255, 255, 255, 0},
|
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- ip := net.ParseIP("192.168.1.5")
|
|
| 85 |
- |
|
| 86 |
- if _, err := RequestIP(network, &ip); err != nil {
|
|
| 87 |
- t.Fatal(err) |
|
| 88 |
- } |
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func TestConversion(t *testing.T) {
|
|
| 92 |
- ip := net.ParseIP("127.0.0.1")
|
|
| 93 |
- i := ipToInt(&ip) |
|
| 94 |
- if i == 0 {
|
|
| 95 |
- t.Fatal("converted to zero")
|
|
| 96 |
- } |
|
| 97 |
- conv := intToIP(i) |
|
| 98 |
- if !ip.Equal(*conv) {
|
|
| 99 |
- t.Error(conv.String()) |
|
| 100 |
- } |
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 |
-func TestIPAllocator(t *testing.T) {
|
|
| 104 |
- expectedIPs := []net.IP{
|
|
| 105 |
- 0: net.IPv4(127, 0, 0, 2), |
|
| 106 |
- 1: net.IPv4(127, 0, 0, 3), |
|
| 107 |
- 2: net.IPv4(127, 0, 0, 4), |
|
| 108 |
- 3: net.IPv4(127, 0, 0, 5), |
|
| 109 |
- 4: net.IPv4(127, 0, 0, 6), |
|
| 110 |
- } |
|
| 111 |
- |
|
| 112 |
- gwIP, n, _ := net.ParseCIDR("127.0.0.1/29")
|
|
| 113 |
- network := &net.IPNet{IP: gwIP, Mask: n.Mask}
|
|
| 114 |
- // Pool after initialisation (f = free, u = used) |
|
| 115 |
- // 2(f) - 3(f) - 4(f) - 5(f) - 6(f) |
|
| 116 |
- // ↑ |
|
| 117 |
- |
|
| 118 |
- // Check that we get 5 IPs, from 127.0.0.2–127.0.0.6, in that |
|
| 119 |
- // order. |
|
| 120 |
- for i := 0; i < 5; i++ {
|
|
| 121 |
- ip, err := RequestIP(network, nil) |
|
| 122 |
- if err != nil {
|
|
| 123 |
- t.Fatal(err) |
|
| 124 |
- } |
|
| 125 |
- |
|
| 126 |
- assertIPEquals(t, &expectedIPs[i], ip) |
|
| 127 |
- } |
|
| 128 |
- // Before loop begin |
|
| 129 |
- // 2(f) - 3(f) - 4(f) - 5(f) - 6(f) |
|
| 130 |
- // ↑ |
|
| 131 |
- |
|
| 132 |
- // After i = 0 |
|
| 133 |
- // 2(u) - 3(f) - 4(f) - 5(f) - 6(f) |
|
| 134 |
- // ↑ |
|
| 135 |
- |
|
| 136 |
- // After i = 1 |
|
| 137 |
- // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) |
|
| 138 |
- // ↑ |
|
| 139 |
- |
|
| 140 |
- // After i = 2 |
|
| 141 |
- // 2(u) - 3(u) - 4(u) - 5(f) - 6(f) |
|
| 142 |
- // ↑ |
|
| 143 |
- |
|
| 144 |
- // After i = 3 |
|
| 145 |
- // 2(u) - 3(u) - 4(u) - 5(u) - 6(f) |
|
| 146 |
- // ↑ |
|
| 147 |
- |
|
| 148 |
- // After i = 4 |
|
| 149 |
- // 2(u) - 3(u) - 4(u) - 5(u) - 6(u) |
|
| 150 |
- // ↑ |
|
| 151 |
- |
|
| 152 |
- // Check that there are no more IPs |
|
| 153 |
- ip, err := RequestIP(network, nil) |
|
| 154 |
- if err == nil {
|
|
| 155 |
- t.Fatalf("There shouldn't be any IP addresses at this point, got %s\n", ip)
|
|
| 156 |
- } |
|
| 157 |
- |
|
| 158 |
- // Release some IPs in non-sequential order |
|
| 159 |
- if err := ReleaseIP(network, &expectedIPs[3]); err != nil {
|
|
| 160 |
- t.Fatal(err) |
|
| 161 |
- } |
|
| 162 |
- // 2(u) - 3(u) - 4(u) - 5(f) - 6(u) |
|
| 163 |
- // ↑ |
|
| 164 |
- |
|
| 165 |
- if err := ReleaseIP(network, &expectedIPs[2]); err != nil {
|
|
| 166 |
- t.Fatal(err) |
|
| 167 |
- } |
|
| 168 |
- // 2(u) - 3(u) - 4(f) - 5(f) - 6(u) |
|
| 169 |
- // ↑ |
|
| 170 |
- |
|
| 171 |
- if err := ReleaseIP(network, &expectedIPs[4]); err != nil {
|
|
| 172 |
- t.Fatal(err) |
|
| 173 |
- } |
|
| 174 |
- // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) |
|
| 175 |
- // ↑ |
|
| 176 |
- |
|
| 177 |
- // Make sure that IPs are reused in sequential order, starting |
|
| 178 |
- // with the first released IP |
|
| 179 |
- newIPs := make([]*net.IP, 3) |
|
| 180 |
- for i := 0; i < 3; i++ {
|
|
| 181 |
- ip, err := RequestIP(network, nil) |
|
| 182 |
- if err != nil {
|
|
| 183 |
- t.Fatal(err) |
|
| 184 |
- } |
|
| 185 |
- |
|
| 186 |
- newIPs[i] = ip |
|
| 187 |
- } |
|
| 188 |
- // Before loop begin |
|
| 189 |
- // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) |
|
| 190 |
- // ↑ |
|
| 191 |
- |
|
| 192 |
- // After i = 0 |
|
| 193 |
- // 2(u) - 3(u) - 4(f) - 5(u) - 6(f) |
|
| 194 |
- // ↑ |
|
| 195 |
- |
|
| 196 |
- // After i = 1 |
|
| 197 |
- // 2(u) - 3(u) - 4(f) - 5(u) - 6(u) |
|
| 198 |
- // ↑ |
|
| 199 |
- |
|
| 200 |
- // After i = 2 |
|
| 201 |
- // 2(u) - 3(u) - 4(u) - 5(u) - 6(u) |
|
| 202 |
- // ↑ |
|
| 203 |
- |
|
| 204 |
- // Reordered these because the new set will always return the |
|
| 205 |
- // lowest ips first and not in the order that they were released |
|
| 206 |
- assertIPEquals(t, &expectedIPs[2], newIPs[0]) |
|
| 207 |
- assertIPEquals(t, &expectedIPs[3], newIPs[1]) |
|
| 208 |
- assertIPEquals(t, &expectedIPs[4], newIPs[2]) |
|
| 209 |
- |
|
| 210 |
- _, err = RequestIP(network, nil) |
|
| 211 |
- if err == nil {
|
|
| 212 |
- t.Fatal("There shouldn't be any IP addresses at this point")
|
|
| 213 |
- } |
|
| 214 |
-} |
|
| 215 |
- |
|
| 216 |
-func TestAllocateFirstIP(t *testing.T) {
|
|
| 217 |
- defer reset() |
|
| 218 |
- network := &net.IPNet{
|
|
| 219 |
- IP: []byte{192, 168, 0, 0},
|
|
| 220 |
- Mask: []byte{255, 255, 255, 0},
|
|
| 221 |
- } |
|
| 222 |
- |
|
| 223 |
- firstIP := network.IP.To4().Mask(network.Mask) |
|
| 224 |
- first := ipToInt(&firstIP) + 1 |
|
| 225 |
- |
|
| 226 |
- ip, err := RequestIP(network, nil) |
|
| 227 |
- if err != nil {
|
|
| 228 |
- t.Fatal(err) |
|
| 229 |
- } |
|
| 230 |
- allocated := ipToInt(ip) |
|
| 231 |
- |
|
| 232 |
- if allocated == first {
|
|
| 233 |
- t.Fatalf("allocated ip should not equal first ip: %d == %d", first, allocated)
|
|
| 234 |
- } |
|
| 235 |
-} |
|
| 236 |
- |
|
| 237 |
-func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) {
|
|
| 238 |
- if !ip1.Equal(*ip2) {
|
|
| 239 |
- t.Fatalf("Expected IP %s, got %s", ip1, ip2)
|
|
| 240 |
- } |
|
| 241 |
-} |
| 242 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,482 +0,0 @@ |
| 1 |
-package lxc |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "github.com/dotcloud/docker/engine" |
|
| 6 |
- "github.com/dotcloud/docker/networkdriver" |
|
| 7 |
- "github.com/dotcloud/docker/networkdriver/ipallocator" |
|
| 8 |
- "github.com/dotcloud/docker/networkdriver/portallocator" |
|
| 9 |
- "github.com/dotcloud/docker/networkdriver/portmapper" |
|
| 10 |
- "github.com/dotcloud/docker/pkg/iptables" |
|
| 11 |
- "github.com/dotcloud/docker/pkg/netlink" |
|
| 12 |
- "github.com/dotcloud/docker/utils" |
|
| 13 |
- "io/ioutil" |
|
| 14 |
- "log" |
|
| 15 |
- "net" |
|
| 16 |
- "strings" |
|
| 17 |
- "syscall" |
|
| 18 |
- "unsafe" |
|
| 19 |
-) |
|
| 20 |
- |
|
| 21 |
-const ( |
|
| 22 |
- DefaultNetworkBridge = "docker0" |
|
| 23 |
- siocBRADDBR = 0x89a0 |
|
| 24 |
-) |
|
| 25 |
- |
|
| 26 |
-// Network interface represents the networking stack of a container |
|
| 27 |
-type networkInterface struct {
|
|
| 28 |
- IP net.IP |
|
| 29 |
- PortMappings []net.Addr // there are mappings to the host interfaces |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-var ( |
|
| 33 |
- addrs = []string{
|
|
| 34 |
- // Here we don't follow the convention of using the 1st IP of the range for the gateway. |
|
| 35 |
- // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. |
|
| 36 |
- // In theory this shouldn't matter - in practice there's bound to be a few scripts relying |
|
| 37 |
- // on the internal addressing or other stupid things like that. |
|
| 38 |
- // The shouldn't, but hey, let's not break them unless we really have to. |
|
| 39 |
- "172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 |
|
| 40 |
- "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive |
|
| 41 |
- "10.1.42.1/16", |
|
| 42 |
- "10.42.42.1/16", |
|
| 43 |
- "172.16.42.1/24", |
|
| 44 |
- "172.16.43.1/24", |
|
| 45 |
- "172.16.44.1/24", |
|
| 46 |
- "10.0.42.1/24", |
|
| 47 |
- "10.0.43.1/24", |
|
| 48 |
- "192.168.42.1/24", |
|
| 49 |
- "192.168.43.1/24", |
|
| 50 |
- "192.168.44.1/24", |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- bridgeIface string |
|
| 54 |
- bridgeNetwork *net.IPNet |
|
| 55 |
- |
|
| 56 |
- defaultBindingIP = net.ParseIP("0.0.0.0")
|
|
| 57 |
- currentInterfaces = make(map[string]*networkInterface) |
|
| 58 |
-) |
|
| 59 |
- |
|
| 60 |
-func InitDriver(job *engine.Job) engine.Status {
|
|
| 61 |
- var ( |
|
| 62 |
- network *net.IPNet |
|
| 63 |
- enableIPTables = job.GetenvBool("EnableIptables")
|
|
| 64 |
- icc = job.GetenvBool("InterContainerCommunication")
|
|
| 65 |
- ipForward = job.GetenvBool("EnableIpForward")
|
|
| 66 |
- bridgeIP = job.Getenv("BridgeIP")
|
|
| 67 |
- ) |
|
| 68 |
- |
|
| 69 |
- if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
|
|
| 70 |
- defaultBindingIP = net.ParseIP(defaultIP) |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- bridgeIface = job.Getenv("BridgeIface")
|
|
| 74 |
- if bridgeIface == "" {
|
|
| 75 |
- bridgeIface = DefaultNetworkBridge |
|
| 76 |
- } |
|
| 77 |
- |
|
| 78 |
- addr, err := networkdriver.GetIfaceAddr(bridgeIface) |
|
| 79 |
- if err != nil {
|
|
| 80 |
- // If the iface is not found, try to create it |
|
| 81 |
- job.Logf("creating new bridge for %s", bridgeIface)
|
|
| 82 |
- if err := createBridge(bridgeIP); err != nil {
|
|
| 83 |
- job.Error(err) |
|
| 84 |
- return engine.StatusErr |
|
| 85 |
- } |
|
| 86 |
- |
|
| 87 |
- job.Logf("getting iface addr")
|
|
| 88 |
- addr, err = networkdriver.GetIfaceAddr(bridgeIface) |
|
| 89 |
- if err != nil {
|
|
| 90 |
- job.Error(err) |
|
| 91 |
- return engine.StatusErr |
|
| 92 |
- } |
|
| 93 |
- network = addr.(*net.IPNet) |
|
| 94 |
- } else {
|
|
| 95 |
- network = addr.(*net.IPNet) |
|
| 96 |
- } |
|
| 97 |
- |
|
| 98 |
- // Configure iptables for link support |
|
| 99 |
- if enableIPTables {
|
|
| 100 |
- if err := setupIPTables(addr, icc); err != nil {
|
|
| 101 |
- job.Error(err) |
|
| 102 |
- return engine.StatusErr |
|
| 103 |
- } |
|
| 104 |
- } |
|
| 105 |
- |
|
| 106 |
- if ipForward {
|
|
| 107 |
- // Enable IPv4 forwarding |
|
| 108 |
- if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil {
|
|
| 109 |
- job.Logf("WARNING: unable to enable IPv4 forwarding: %s\n", err)
|
|
| 110 |
- } |
|
| 111 |
- } |
|
| 112 |
- |
|
| 113 |
- // We can always try removing the iptables |
|
| 114 |
- if err := iptables.RemoveExistingChain("DOCKER"); err != nil {
|
|
| 115 |
- job.Error(err) |
|
| 116 |
- return engine.StatusErr |
|
| 117 |
- } |
|
| 118 |
- |
|
| 119 |
- if enableIPTables {
|
|
| 120 |
- chain, err := iptables.NewChain("DOCKER", bridgeIface)
|
|
| 121 |
- if err != nil {
|
|
| 122 |
- job.Error(err) |
|
| 123 |
- return engine.StatusErr |
|
| 124 |
- } |
|
| 125 |
- portmapper.SetIptablesChain(chain) |
|
| 126 |
- } |
|
| 127 |
- |
|
| 128 |
- bridgeNetwork = network |
|
| 129 |
- |
|
| 130 |
- // https://github.com/dotcloud/docker/issues/2768 |
|
| 131 |
- job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeNetwork.IP)
|
|
| 132 |
- |
|
| 133 |
- for name, f := range map[string]engine.Handler{
|
|
| 134 |
- "allocate_interface": Allocate, |
|
| 135 |
- "release_interface": Release, |
|
| 136 |
- "allocate_port": AllocatePort, |
|
| 137 |
- "link": LinkContainers, |
|
| 138 |
- } {
|
|
| 139 |
- if err := job.Eng.Register(name, f); err != nil {
|
|
| 140 |
- job.Error(err) |
|
| 141 |
- return engine.StatusErr |
|
| 142 |
- } |
|
| 143 |
- } |
|
| 144 |
- return engine.StatusOK |
|
| 145 |
-} |
|
| 146 |
- |
|
| 147 |
-func setupIPTables(addr net.Addr, icc bool) error {
|
|
| 148 |
- // Enable NAT |
|
| 149 |
- natArgs := []string{"POSTROUTING", "-t", "nat", "-s", addr.String(), "!", "-d", addr.String(), "-j", "MASQUERADE"}
|
|
| 150 |
- |
|
| 151 |
- if !iptables.Exists(natArgs...) {
|
|
| 152 |
- if output, err := iptables.Raw(append([]string{"-I"}, natArgs...)...); err != nil {
|
|
| 153 |
- return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
|
|
| 154 |
- } else if len(output) != 0 {
|
|
| 155 |
- return fmt.Errorf("Error iptables postrouting: %s", output)
|
|
| 156 |
- } |
|
| 157 |
- } |
|
| 158 |
- |
|
| 159 |
- var ( |
|
| 160 |
- args = []string{"FORWARD", "-i", bridgeIface, "-o", bridgeIface, "-j"}
|
|
| 161 |
- acceptArgs = append(args, "ACCEPT") |
|
| 162 |
- dropArgs = append(args, "DROP") |
|
| 163 |
- ) |
|
| 164 |
- |
|
| 165 |
- if !icc {
|
|
| 166 |
- iptables.Raw(append([]string{"-D"}, acceptArgs...)...)
|
|
| 167 |
- |
|
| 168 |
- if !iptables.Exists(dropArgs...) {
|
|
| 169 |
- utils.Debugf("Disable inter-container communication")
|
|
| 170 |
- if output, err := iptables.Raw(append([]string{"-I"}, dropArgs...)...); err != nil {
|
|
| 171 |
- return fmt.Errorf("Unable to prevent intercontainer communication: %s", err)
|
|
| 172 |
- } else if len(output) != 0 {
|
|
| 173 |
- return fmt.Errorf("Error disabling intercontainer communication: %s", output)
|
|
| 174 |
- } |
|
| 175 |
- } |
|
| 176 |
- } else {
|
|
| 177 |
- iptables.Raw(append([]string{"-D"}, dropArgs...)...)
|
|
| 178 |
- |
|
| 179 |
- if !iptables.Exists(acceptArgs...) {
|
|
| 180 |
- utils.Debugf("Enable inter-container communication")
|
|
| 181 |
- if output, err := iptables.Raw(append([]string{"-I"}, acceptArgs...)...); err != nil {
|
|
| 182 |
- return fmt.Errorf("Unable to allow intercontainer communication: %s", err)
|
|
| 183 |
- } else if len(output) != 0 {
|
|
| 184 |
- return fmt.Errorf("Error enabling intercontainer communication: %s", output)
|
|
| 185 |
- } |
|
| 186 |
- } |
|
| 187 |
- } |
|
| 188 |
- |
|
| 189 |
- // Accept all non-intercontainer outgoing packets |
|
| 190 |
- outgoingArgs := []string{"FORWARD", "-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}
|
|
| 191 |
- if !iptables.Exists(outgoingArgs...) {
|
|
| 192 |
- if output, err := iptables.Raw(append([]string{"-I"}, outgoingArgs...)...); err != nil {
|
|
| 193 |
- return fmt.Errorf("Unable to allow outgoing packets: %s", err)
|
|
| 194 |
- } else if len(output) != 0 {
|
|
| 195 |
- return fmt.Errorf("Error iptables allow outgoing: %s", output)
|
|
| 196 |
- } |
|
| 197 |
- } |
|
| 198 |
- |
|
| 199 |
- // Accept incoming packets for existing connections |
|
| 200 |
- existingArgs := []string{"FORWARD", "-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}
|
|
| 201 |
- |
|
| 202 |
- if !iptables.Exists(existingArgs...) {
|
|
| 203 |
- if output, err := iptables.Raw(append([]string{"-I"}, existingArgs...)...); err != nil {
|
|
| 204 |
- return fmt.Errorf("Unable to allow incoming packets: %s", err)
|
|
| 205 |
- } else if len(output) != 0 {
|
|
| 206 |
- return fmt.Errorf("Error iptables allow incoming: %s", output)
|
|
| 207 |
- } |
|
| 208 |
- } |
|
| 209 |
- return nil |
|
| 210 |
-} |
|
| 211 |
- |
|
| 212 |
-// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, |
|
| 213 |
-// and attempts to configure it with an address which doesn't conflict with any other interface on the host. |
|
| 214 |
-// If it can't find an address which doesn't conflict, it will return an error. |
|
| 215 |
-func createBridge(bridgeIP string) error {
|
|
| 216 |
- nameservers := []string{}
|
|
| 217 |
- resolvConf, _ := utils.GetResolvConf() |
|
| 218 |
- // we don't check for an error here, because we don't really care |
|
| 219 |
- // if we can't read /etc/resolv.conf. So instead we skip the append |
|
| 220 |
- // if resolvConf is nil. It either doesn't exist, or we can't read it |
|
| 221 |
- // for some reason. |
|
| 222 |
- if resolvConf != nil {
|
|
| 223 |
- nameservers = append(nameservers, utils.GetNameserversAsCIDR(resolvConf)...) |
|
| 224 |
- } |
|
| 225 |
- |
|
| 226 |
- var ifaceAddr string |
|
| 227 |
- if len(bridgeIP) != 0 {
|
|
| 228 |
- _, _, err := net.ParseCIDR(bridgeIP) |
|
| 229 |
- if err != nil {
|
|
| 230 |
- return err |
|
| 231 |
- } |
|
| 232 |
- ifaceAddr = bridgeIP |
|
| 233 |
- } else {
|
|
| 234 |
- for _, addr := range addrs {
|
|
| 235 |
- _, dockerNetwork, err := net.ParseCIDR(addr) |
|
| 236 |
- if err != nil {
|
|
| 237 |
- return err |
|
| 238 |
- } |
|
| 239 |
- if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil {
|
|
| 240 |
- if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil {
|
|
| 241 |
- ifaceAddr = addr |
|
| 242 |
- break |
|
| 243 |
- } else {
|
|
| 244 |
- utils.Debugf("%s %s", addr, err)
|
|
| 245 |
- } |
|
| 246 |
- } |
|
| 247 |
- } |
|
| 248 |
- } |
|
| 249 |
- |
|
| 250 |
- if ifaceAddr == "" {
|
|
| 251 |
- return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", bridgeIface, bridgeIface)
|
|
| 252 |
- } |
|
| 253 |
- utils.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr)
|
|
| 254 |
- |
|
| 255 |
- if err := createBridgeIface(bridgeIface); err != nil {
|
|
| 256 |
- return err |
|
| 257 |
- } |
|
| 258 |
- |
|
| 259 |
- iface, err := net.InterfaceByName(bridgeIface) |
|
| 260 |
- if err != nil {
|
|
| 261 |
- return err |
|
| 262 |
- } |
|
| 263 |
- |
|
| 264 |
- ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) |
|
| 265 |
- if err != nil {
|
|
| 266 |
- return err |
|
| 267 |
- } |
|
| 268 |
- |
|
| 269 |
- if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
|
|
| 270 |
- return fmt.Errorf("Unable to add private network: %s", err)
|
|
| 271 |
- } |
|
| 272 |
- if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 273 |
- return fmt.Errorf("Unable to start network bridge: %s", err)
|
|
| 274 |
- } |
|
| 275 |
- return nil |
|
| 276 |
-} |
|
| 277 |
- |
|
| 278 |
-// Create the actual bridge device. This is more backward-compatible than |
|
| 279 |
-// netlink.NetworkLinkAdd and works on RHEL 6. |
|
| 280 |
-func createBridgeIface(name string) error {
|
|
| 281 |
- s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_IP) |
|
| 282 |
- if err != nil {
|
|
| 283 |
- utils.Debugf("Bridge socket creation failed IPv6 probably not enabled: %v", err)
|
|
| 284 |
- s, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_IP) |
|
| 285 |
- if err != nil {
|
|
| 286 |
- return fmt.Errorf("Error creating bridge creation socket: %s", err)
|
|
| 287 |
- } |
|
| 288 |
- } |
|
| 289 |
- defer syscall.Close(s) |
|
| 290 |
- |
|
| 291 |
- nameBytePtr, err := syscall.BytePtrFromString(name) |
|
| 292 |
- if err != nil {
|
|
| 293 |
- return fmt.Errorf("Error converting bridge name %s to byte array: %s", name, err)
|
|
| 294 |
- } |
|
| 295 |
- |
|
| 296 |
- if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), siocBRADDBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 {
|
|
| 297 |
- return fmt.Errorf("Error creating bridge: %s", err)
|
|
| 298 |
- } |
|
| 299 |
- return nil |
|
| 300 |
-} |
|
| 301 |
- |
|
| 302 |
-// Allocate a network interface |
|
| 303 |
-func Allocate(job *engine.Job) engine.Status {
|
|
| 304 |
- var ( |
|
| 305 |
- ip *net.IP |
|
| 306 |
- err error |
|
| 307 |
- id = job.Args[0] |
|
| 308 |
- requestedIP = net.ParseIP(job.Getenv("RequestedIP"))
|
|
| 309 |
- ) |
|
| 310 |
- |
|
| 311 |
- if requestedIP != nil {
|
|
| 312 |
- ip, err = ipallocator.RequestIP(bridgeNetwork, &requestedIP) |
|
| 313 |
- } else {
|
|
| 314 |
- ip, err = ipallocator.RequestIP(bridgeNetwork, nil) |
|
| 315 |
- } |
|
| 316 |
- if err != nil {
|
|
| 317 |
- job.Error(err) |
|
| 318 |
- return engine.StatusErr |
|
| 319 |
- } |
|
| 320 |
- |
|
| 321 |
- out := engine.Env{}
|
|
| 322 |
- out.Set("IP", ip.String())
|
|
| 323 |
- out.Set("Mask", bridgeNetwork.Mask.String())
|
|
| 324 |
- out.Set("Gateway", bridgeNetwork.IP.String())
|
|
| 325 |
- out.Set("Bridge", bridgeIface)
|
|
| 326 |
- |
|
| 327 |
- size, _ := bridgeNetwork.Mask.Size() |
|
| 328 |
- out.SetInt("IPPrefixLen", size)
|
|
| 329 |
- |
|
| 330 |
- currentInterfaces[id] = &networkInterface{
|
|
| 331 |
- IP: *ip, |
|
| 332 |
- } |
|
| 333 |
- |
|
| 334 |
- out.WriteTo(job.Stdout) |
|
| 335 |
- |
|
| 336 |
- return engine.StatusOK |
|
| 337 |
-} |
|
| 338 |
- |
|
| 339 |
-// release an interface for a select ip |
|
| 340 |
-func Release(job *engine.Job) engine.Status {
|
|
| 341 |
- var ( |
|
| 342 |
- id = job.Args[0] |
|
| 343 |
- containerInterface = currentInterfaces[id] |
|
| 344 |
- ip net.IP |
|
| 345 |
- port int |
|
| 346 |
- proto string |
|
| 347 |
- ) |
|
| 348 |
- |
|
| 349 |
- if containerInterface == nil {
|
|
| 350 |
- return job.Errorf("No network information to release for %s", id)
|
|
| 351 |
- } |
|
| 352 |
- |
|
| 353 |
- for _, nat := range containerInterface.PortMappings {
|
|
| 354 |
- if err := portmapper.Unmap(nat); err != nil {
|
|
| 355 |
- log.Printf("Unable to unmap port %s: %s", nat, err)
|
|
| 356 |
- } |
|
| 357 |
- |
|
| 358 |
- // this is host mappings |
|
| 359 |
- switch a := nat.(type) {
|
|
| 360 |
- case *net.TCPAddr: |
|
| 361 |
- proto = "tcp" |
|
| 362 |
- ip = a.IP |
|
| 363 |
- port = a.Port |
|
| 364 |
- case *net.UDPAddr: |
|
| 365 |
- proto = "udp" |
|
| 366 |
- ip = a.IP |
|
| 367 |
- port = a.Port |
|
| 368 |
- } |
|
| 369 |
- |
|
| 370 |
- if err := portallocator.ReleasePort(ip, proto, port); err != nil {
|
|
| 371 |
- log.Printf("Unable to release port %s", nat)
|
|
| 372 |
- } |
|
| 373 |
- } |
|
| 374 |
- |
|
| 375 |
- if err := ipallocator.ReleaseIP(bridgeNetwork, &containerInterface.IP); err != nil {
|
|
| 376 |
- log.Printf("Unable to release ip %s\n", err)
|
|
| 377 |
- } |
|
| 378 |
- return engine.StatusOK |
|
| 379 |
-} |
|
| 380 |
- |
|
| 381 |
-// Allocate an external port and map it to the interface |
|
| 382 |
-func AllocatePort(job *engine.Job) engine.Status {
|
|
| 383 |
- var ( |
|
| 384 |
- err error |
|
| 385 |
- |
|
| 386 |
- ip = defaultBindingIP |
|
| 387 |
- id = job.Args[0] |
|
| 388 |
- hostIP = job.Getenv("HostIP")
|
|
| 389 |
- hostPort = job.GetenvInt("HostPort")
|
|
| 390 |
- containerPort = job.GetenvInt("ContainerPort")
|
|
| 391 |
- proto = job.Getenv("Proto")
|
|
| 392 |
- network = currentInterfaces[id] |
|
| 393 |
- ) |
|
| 394 |
- |
|
| 395 |
- if hostIP != "" {
|
|
| 396 |
- ip = net.ParseIP(hostIP) |
|
| 397 |
- } |
|
| 398 |
- |
|
| 399 |
- // host ip, proto, and host port |
|
| 400 |
- hostPort, err = portallocator.RequestPort(ip, proto, hostPort) |
|
| 401 |
- if err != nil {
|
|
| 402 |
- job.Error(err) |
|
| 403 |
- return engine.StatusErr |
|
| 404 |
- } |
|
| 405 |
- |
|
| 406 |
- var ( |
|
| 407 |
- container net.Addr |
|
| 408 |
- host net.Addr |
|
| 409 |
- ) |
|
| 410 |
- |
|
| 411 |
- if proto == "tcp" {
|
|
| 412 |
- host = &net.TCPAddr{IP: ip, Port: hostPort}
|
|
| 413 |
- container = &net.TCPAddr{IP: network.IP, Port: containerPort}
|
|
| 414 |
- } else {
|
|
| 415 |
- host = &net.UDPAddr{IP: ip, Port: hostPort}
|
|
| 416 |
- container = &net.UDPAddr{IP: network.IP, Port: containerPort}
|
|
| 417 |
- } |
|
| 418 |
- |
|
| 419 |
- if err := portmapper.Map(container, ip, hostPort); err != nil {
|
|
| 420 |
- portallocator.ReleasePort(ip, proto, hostPort) |
|
| 421 |
- |
|
| 422 |
- job.Error(err) |
|
| 423 |
- return engine.StatusErr |
|
| 424 |
- } |
|
| 425 |
- network.PortMappings = append(network.PortMappings, host) |
|
| 426 |
- |
|
| 427 |
- out := engine.Env{}
|
|
| 428 |
- out.Set("HostIP", ip.String())
|
|
| 429 |
- out.SetInt("HostPort", hostPort)
|
|
| 430 |
- |
|
| 431 |
- if _, err := out.WriteTo(job.Stdout); err != nil {
|
|
| 432 |
- job.Error(err) |
|
| 433 |
- return engine.StatusErr |
|
| 434 |
- } |
|
| 435 |
- return engine.StatusOK |
|
| 436 |
-} |
|
| 437 |
- |
|
| 438 |
-func LinkContainers(job *engine.Job) engine.Status {
|
|
| 439 |
- var ( |
|
| 440 |
- action = job.Args[0] |
|
| 441 |
- childIP = job.Getenv("ChildIP")
|
|
| 442 |
- parentIP = job.Getenv("ParentIP")
|
|
| 443 |
- ignoreErrors = job.GetenvBool("IgnoreErrors")
|
|
| 444 |
- ports = job.GetenvList("Ports")
|
|
| 445 |
- ) |
|
| 446 |
- split := func(p string) (string, string) {
|
|
| 447 |
- parts := strings.Split(p, "/") |
|
| 448 |
- return parts[0], parts[1] |
|
| 449 |
- } |
|
| 450 |
- |
|
| 451 |
- for _, p := range ports {
|
|
| 452 |
- port, proto := split(p) |
|
| 453 |
- if output, err := iptables.Raw(action, "FORWARD", |
|
| 454 |
- "-i", bridgeIface, "-o", bridgeIface, |
|
| 455 |
- "-p", proto, |
|
| 456 |
- "-s", parentIP, |
|
| 457 |
- "--dport", port, |
|
| 458 |
- "-d", childIP, |
|
| 459 |
- "-j", "ACCEPT"); !ignoreErrors && err != nil {
|
|
| 460 |
- job.Error(err) |
|
| 461 |
- return engine.StatusErr |
|
| 462 |
- } else if len(output) != 0 {
|
|
| 463 |
- job.Errorf("Error toggle iptables forward: %s", output)
|
|
| 464 |
- return engine.StatusErr |
|
| 465 |
- } |
|
| 466 |
- |
|
| 467 |
- if output, err := iptables.Raw(action, "FORWARD", |
|
| 468 |
- "-i", bridgeIface, "-o", bridgeIface, |
|
| 469 |
- "-p", proto, |
|
| 470 |
- "-s", childIP, |
|
| 471 |
- "--sport", port, |
|
| 472 |
- "-d", parentIP, |
|
| 473 |
- "-j", "ACCEPT"); !ignoreErrors && err != nil {
|
|
| 474 |
- job.Error(err) |
|
| 475 |
- return engine.StatusErr |
|
| 476 |
- } else if len(output) != 0 {
|
|
| 477 |
- job.Errorf("Error toggle iptables forward: %s", output)
|
|
| 478 |
- return engine.StatusErr |
|
| 479 |
- } |
|
| 480 |
- } |
|
| 481 |
- return engine.StatusOK |
|
| 482 |
-} |
| 483 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,10 +0,0 @@ |
| 1 |
-package networkdriver |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-var ( |
|
| 8 |
- ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver")
|
|
| 9 |
- ErrNetworkOverlaps = errors.New("requested network overlaps with existing network")
|
|
| 10 |
-) |
| 11 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,190 +0,0 @@ |
| 1 |
-package networkdriver |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/pkg/netlink" |
|
| 5 |
- "net" |
|
| 6 |
- "testing" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func TestNonOverlapingNameservers(t *testing.T) {
|
|
| 10 |
- network := &net.IPNet{
|
|
| 11 |
- IP: []byte{192, 168, 0, 1},
|
|
| 12 |
- Mask: []byte{255, 255, 255, 0},
|
|
| 13 |
- } |
|
| 14 |
- nameservers := []string{
|
|
| 15 |
- "127.0.0.1/32", |
|
| 16 |
- } |
|
| 17 |
- |
|
| 18 |
- if err := CheckNameserverOverlaps(nameservers, network); err != nil {
|
|
| 19 |
- t.Fatal(err) |
|
| 20 |
- } |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-func TestOverlapingNameservers(t *testing.T) {
|
|
| 24 |
- network := &net.IPNet{
|
|
| 25 |
- IP: []byte{192, 168, 0, 1},
|
|
| 26 |
- Mask: []byte{255, 255, 255, 0},
|
|
| 27 |
- } |
|
| 28 |
- nameservers := []string{
|
|
| 29 |
- "192.168.0.1/32", |
|
| 30 |
- } |
|
| 31 |
- |
|
| 32 |
- if err := CheckNameserverOverlaps(nameservers, network); err == nil {
|
|
| 33 |
- t.Fatalf("Expected error %s got %s", ErrNetworkOverlapsWithNameservers, err)
|
|
| 34 |
- } |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-func TestCheckRouteOverlaps(t *testing.T) {
|
|
| 38 |
- orig := networkGetRoutesFct |
|
| 39 |
- defer func() {
|
|
| 40 |
- networkGetRoutesFct = orig |
|
| 41 |
- }() |
|
| 42 |
- networkGetRoutesFct = func() ([]netlink.Route, error) {
|
|
| 43 |
- routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"}
|
|
| 44 |
- |
|
| 45 |
- routes := []netlink.Route{}
|
|
| 46 |
- for _, addr := range routesData {
|
|
| 47 |
- _, netX, _ := net.ParseCIDR(addr) |
|
| 48 |
- routes = append(routes, netlink.Route{IPNet: netX})
|
|
| 49 |
- } |
|
| 50 |
- return routes, nil |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- _, netX, _ := net.ParseCIDR("172.16.0.1/24")
|
|
| 54 |
- if err := CheckRouteOverlaps(netX); err != nil {
|
|
| 55 |
- t.Fatal(err) |
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- _, netX, _ = net.ParseCIDR("10.0.2.0/24")
|
|
| 59 |
- if err := CheckRouteOverlaps(netX); err == nil {
|
|
| 60 |
- t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't")
|
|
| 61 |
- } |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-func TestCheckNameserverOverlaps(t *testing.T) {
|
|
| 65 |
- nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"}
|
|
| 66 |
- |
|
| 67 |
- _, netX, _ := net.ParseCIDR("10.0.2.3/32")
|
|
| 68 |
- |
|
| 69 |
- if err := CheckNameserverOverlaps(nameservers, netX); err == nil {
|
|
| 70 |
- t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX)
|
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- _, netX, _ = net.ParseCIDR("192.168.102.2/32")
|
|
| 74 |
- |
|
| 75 |
- if err := CheckNameserverOverlaps(nameservers, netX); err != nil {
|
|
| 76 |
- t.Fatalf("%s should not overlap %v but it does", netX, nameservers)
|
|
| 77 |
- } |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-func AssertOverlap(CIDRx string, CIDRy string, t *testing.T) {
|
|
| 81 |
- _, netX, _ := net.ParseCIDR(CIDRx) |
|
| 82 |
- _, netY, _ := net.ParseCIDR(CIDRy) |
|
| 83 |
- if !NetworkOverlaps(netX, netY) {
|
|
| 84 |
- t.Errorf("%v and %v should overlap", netX, netY)
|
|
| 85 |
- } |
|
| 86 |
-} |
|
| 87 |
- |
|
| 88 |
-func AssertNoOverlap(CIDRx string, CIDRy string, t *testing.T) {
|
|
| 89 |
- _, netX, _ := net.ParseCIDR(CIDRx) |
|
| 90 |
- _, netY, _ := net.ParseCIDR(CIDRy) |
|
| 91 |
- if NetworkOverlaps(netX, netY) {
|
|
| 92 |
- t.Errorf("%v and %v should not overlap", netX, netY)
|
|
| 93 |
- } |
|
| 94 |
-} |
|
| 95 |
- |
|
| 96 |
-func TestNetworkOverlaps(t *testing.T) {
|
|
| 97 |
- //netY starts at same IP and ends within netX |
|
| 98 |
- AssertOverlap("172.16.0.1/24", "172.16.0.1/25", t)
|
|
| 99 |
- //netY starts within netX and ends at same IP |
|
| 100 |
- AssertOverlap("172.16.0.1/24", "172.16.0.128/25", t)
|
|
| 101 |
- //netY starts and ends within netX |
|
| 102 |
- AssertOverlap("172.16.0.1/24", "172.16.0.64/25", t)
|
|
| 103 |
- //netY starts at same IP and ends outside of netX |
|
| 104 |
- AssertOverlap("172.16.0.1/24", "172.16.0.1/23", t)
|
|
| 105 |
- //netY starts before and ends at same IP of netX |
|
| 106 |
- AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t)
|
|
| 107 |
- //netY starts before and ends outside of netX |
|
| 108 |
- AssertOverlap("172.16.1.1/24", "172.16.0.1/22", t)
|
|
| 109 |
- //netY starts and ends before netX |
|
| 110 |
- AssertNoOverlap("172.16.1.1/25", "172.16.0.1/24", t)
|
|
| 111 |
- //netX starts and ends before netY |
|
| 112 |
- AssertNoOverlap("172.16.1.1/25", "172.16.2.1/24", t)
|
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-func TestNetworkRange(t *testing.T) {
|
|
| 116 |
- // Simple class C test |
|
| 117 |
- _, network, _ := net.ParseCIDR("192.168.0.1/24")
|
|
| 118 |
- first, last := NetworkRange(network) |
|
| 119 |
- if !first.Equal(net.ParseIP("192.168.0.0")) {
|
|
| 120 |
- t.Error(first.String()) |
|
| 121 |
- } |
|
| 122 |
- if !last.Equal(net.ParseIP("192.168.0.255")) {
|
|
| 123 |
- t.Error(last.String()) |
|
| 124 |
- } |
|
| 125 |
- if size := NetworkSize(network.Mask); size != 256 {
|
|
| 126 |
- t.Error(size) |
|
| 127 |
- } |
|
| 128 |
- |
|
| 129 |
- // Class A test |
|
| 130 |
- _, network, _ = net.ParseCIDR("10.0.0.1/8")
|
|
| 131 |
- first, last = NetworkRange(network) |
|
| 132 |
- if !first.Equal(net.ParseIP("10.0.0.0")) {
|
|
| 133 |
- t.Error(first.String()) |
|
| 134 |
- } |
|
| 135 |
- if !last.Equal(net.ParseIP("10.255.255.255")) {
|
|
| 136 |
- t.Error(last.String()) |
|
| 137 |
- } |
|
| 138 |
- if size := NetworkSize(network.Mask); size != 16777216 {
|
|
| 139 |
- t.Error(size) |
|
| 140 |
- } |
|
| 141 |
- |
|
| 142 |
- // Class A, random IP address |
|
| 143 |
- _, network, _ = net.ParseCIDR("10.1.2.3/8")
|
|
| 144 |
- first, last = NetworkRange(network) |
|
| 145 |
- if !first.Equal(net.ParseIP("10.0.0.0")) {
|
|
| 146 |
- t.Error(first.String()) |
|
| 147 |
- } |
|
| 148 |
- if !last.Equal(net.ParseIP("10.255.255.255")) {
|
|
| 149 |
- t.Error(last.String()) |
|
| 150 |
- } |
|
| 151 |
- |
|
| 152 |
- // 32bit mask |
|
| 153 |
- _, network, _ = net.ParseCIDR("10.1.2.3/32")
|
|
| 154 |
- first, last = NetworkRange(network) |
|
| 155 |
- if !first.Equal(net.ParseIP("10.1.2.3")) {
|
|
| 156 |
- t.Error(first.String()) |
|
| 157 |
- } |
|
| 158 |
- if !last.Equal(net.ParseIP("10.1.2.3")) {
|
|
| 159 |
- t.Error(last.String()) |
|
| 160 |
- } |
|
| 161 |
- if size := NetworkSize(network.Mask); size != 1 {
|
|
| 162 |
- t.Error(size) |
|
| 163 |
- } |
|
| 164 |
- |
|
| 165 |
- // 31bit mask |
|
| 166 |
- _, network, _ = net.ParseCIDR("10.1.2.3/31")
|
|
| 167 |
- first, last = NetworkRange(network) |
|
| 168 |
- if !first.Equal(net.ParseIP("10.1.2.2")) {
|
|
| 169 |
- t.Error(first.String()) |
|
| 170 |
- } |
|
| 171 |
- if !last.Equal(net.ParseIP("10.1.2.3")) {
|
|
| 172 |
- t.Error(last.String()) |
|
| 173 |
- } |
|
| 174 |
- if size := NetworkSize(network.Mask); size != 2 {
|
|
| 175 |
- t.Error(size) |
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- // 26bit mask |
|
| 179 |
- _, network, _ = net.ParseCIDR("10.1.2.3/26")
|
|
| 180 |
- first, last = NetworkRange(network) |
|
| 181 |
- if !first.Equal(net.ParseIP("10.1.2.0")) {
|
|
| 182 |
- t.Error(first.String()) |
|
| 183 |
- } |
|
| 184 |
- if !last.Equal(net.ParseIP("10.1.2.63")) {
|
|
| 185 |
- t.Error(last.String()) |
|
| 186 |
- } |
|
| 187 |
- if size := NetworkSize(network.Mask); size != 64 {
|
|
| 188 |
- t.Error(size) |
|
| 189 |
- } |
|
| 190 |
-} |
| 191 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,165 +0,0 @@ |
| 1 |
-package portallocator |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "github.com/dotcloud/docker/pkg/collections" |
|
| 6 |
- "net" |
|
| 7 |
- "sync" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-const ( |
|
| 11 |
- BeginPortRange = 49153 |
|
| 12 |
- EndPortRange = 65535 |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-type ( |
|
| 16 |
- portMappings map[string]*collections.OrderedIntSet |
|
| 17 |
- ipMapping map[string]portMappings |
|
| 18 |
-) |
|
| 19 |
- |
|
| 20 |
-var ( |
|
| 21 |
- ErrPortAlreadyAllocated = errors.New("port has already been allocated")
|
|
| 22 |
- ErrPortExceedsRange = errors.New("port exceeds upper range")
|
|
| 23 |
- ErrUnknownProtocol = errors.New("unknown protocol")
|
|
| 24 |
-) |
|
| 25 |
- |
|
| 26 |
-var ( |
|
| 27 |
- currentDynamicPort = map[string]int{
|
|
| 28 |
- "tcp": BeginPortRange - 1, |
|
| 29 |
- "udp": BeginPortRange - 1, |
|
| 30 |
- } |
|
| 31 |
- defaultIP = net.ParseIP("0.0.0.0")
|
|
| 32 |
- defaultAllocatedPorts = portMappings{}
|
|
| 33 |
- otherAllocatedPorts = ipMapping{}
|
|
| 34 |
- lock = sync.Mutex{}
|
|
| 35 |
-) |
|
| 36 |
- |
|
| 37 |
-func init() {
|
|
| 38 |
- defaultAllocatedPorts["tcp"] = collections.NewOrderedIntSet() |
|
| 39 |
- defaultAllocatedPorts["udp"] = collections.NewOrderedIntSet() |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 |
-// RequestPort returns an available port if the port is 0 |
|
| 43 |
-// If the provided port is not 0 then it will be checked if |
|
| 44 |
-// it is available for allocation |
|
| 45 |
-func RequestPort(ip net.IP, proto string, port int) (int, error) {
|
|
| 46 |
- lock.Lock() |
|
| 47 |
- defer lock.Unlock() |
|
| 48 |
- |
|
| 49 |
- if err := validateProtocol(proto); err != nil {
|
|
| 50 |
- return 0, err |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- // If the user requested a specific port to be allocated |
|
| 54 |
- if port > 0 {
|
|
| 55 |
- if err := registerSetPort(ip, proto, port); err != nil {
|
|
| 56 |
- return 0, err |
|
| 57 |
- } |
|
| 58 |
- return port, nil |
|
| 59 |
- } |
|
| 60 |
- return registerDynamicPort(ip, proto) |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-// ReleasePort will return the provided port back into the |
|
| 64 |
-// pool for reuse |
|
| 65 |
-func ReleasePort(ip net.IP, proto string, port int) error {
|
|
| 66 |
- lock.Lock() |
|
| 67 |
- defer lock.Unlock() |
|
| 68 |
- |
|
| 69 |
- if err := validateProtocol(proto); err != nil {
|
|
| 70 |
- return err |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- allocated := defaultAllocatedPorts[proto] |
|
| 74 |
- allocated.Remove(port) |
|
| 75 |
- |
|
| 76 |
- if !equalsDefault(ip) {
|
|
| 77 |
- registerIP(ip) |
|
| 78 |
- |
|
| 79 |
- // Remove the port for the specific ip address |
|
| 80 |
- allocated = otherAllocatedPorts[ip.String()][proto] |
|
| 81 |
- allocated.Remove(port) |
|
| 82 |
- } |
|
| 83 |
- return nil |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-func ReleaseAll() error {
|
|
| 87 |
- lock.Lock() |
|
| 88 |
- defer lock.Unlock() |
|
| 89 |
- |
|
| 90 |
- currentDynamicPort["tcp"] = BeginPortRange - 1 |
|
| 91 |
- currentDynamicPort["udp"] = BeginPortRange - 1 |
|
| 92 |
- |
|
| 93 |
- defaultAllocatedPorts = portMappings{}
|
|
| 94 |
- defaultAllocatedPorts["tcp"] = collections.NewOrderedIntSet() |
|
| 95 |
- defaultAllocatedPorts["udp"] = collections.NewOrderedIntSet() |
|
| 96 |
- |
|
| 97 |
- otherAllocatedPorts = ipMapping{}
|
|
| 98 |
- |
|
| 99 |
- return nil |
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-func registerDynamicPort(ip net.IP, proto string) (int, error) {
|
|
| 103 |
- allocated := defaultAllocatedPorts[proto] |
|
| 104 |
- |
|
| 105 |
- port := nextPort(proto) |
|
| 106 |
- if port > EndPortRange {
|
|
| 107 |
- return 0, ErrPortExceedsRange |
|
| 108 |
- } |
|
| 109 |
- |
|
| 110 |
- if !equalsDefault(ip) {
|
|
| 111 |
- registerIP(ip) |
|
| 112 |
- |
|
| 113 |
- ipAllocated := otherAllocatedPorts[ip.String()][proto] |
|
| 114 |
- ipAllocated.Push(port) |
|
| 115 |
- } else {
|
|
| 116 |
- allocated.Push(port) |
|
| 117 |
- } |
|
| 118 |
- return port, nil |
|
| 119 |
-} |
|
| 120 |
- |
|
| 121 |
-func registerSetPort(ip net.IP, proto string, port int) error {
|
|
| 122 |
- allocated := defaultAllocatedPorts[proto] |
|
| 123 |
- if allocated.Exists(port) {
|
|
| 124 |
- return ErrPortAlreadyAllocated |
|
| 125 |
- } |
|
| 126 |
- |
|
| 127 |
- if !equalsDefault(ip) {
|
|
| 128 |
- registerIP(ip) |
|
| 129 |
- |
|
| 130 |
- ipAllocated := otherAllocatedPorts[ip.String()][proto] |
|
| 131 |
- if ipAllocated.Exists(port) {
|
|
| 132 |
- return ErrPortAlreadyAllocated |
|
| 133 |
- } |
|
| 134 |
- ipAllocated.Push(port) |
|
| 135 |
- } else {
|
|
| 136 |
- allocated.Push(port) |
|
| 137 |
- } |
|
| 138 |
- return nil |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-func equalsDefault(ip net.IP) bool {
|
|
| 142 |
- return ip == nil || ip.Equal(defaultIP) |
|
| 143 |
-} |
|
| 144 |
- |
|
| 145 |
-func nextPort(proto string) int {
|
|
| 146 |
- c := currentDynamicPort[proto] + 1 |
|
| 147 |
- currentDynamicPort[proto] = c |
|
| 148 |
- return c |
|
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-func registerIP(ip net.IP) {
|
|
| 152 |
- if _, exists := otherAllocatedPorts[ip.String()]; !exists {
|
|
| 153 |
- otherAllocatedPorts[ip.String()] = portMappings{
|
|
| 154 |
- "tcp": collections.NewOrderedIntSet(), |
|
| 155 |
- "udp": collections.NewOrderedIntSet(), |
|
| 156 |
- } |
|
| 157 |
- } |
|
| 158 |
-} |
|
| 159 |
- |
|
| 160 |
-func validateProtocol(proto string) error {
|
|
| 161 |
- if _, exists := defaultAllocatedPorts[proto]; !exists {
|
|
| 162 |
- return ErrUnknownProtocol |
|
| 163 |
- } |
|
| 164 |
- return nil |
|
| 165 |
-} |
| 166 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,184 +0,0 @@ |
| 1 |
-package portallocator |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "net" |
|
| 5 |
- "testing" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-func reset() {
|
|
| 9 |
- ReleaseAll() |
|
| 10 |
-} |
|
| 11 |
- |
|
| 12 |
-func TestRequestNewPort(t *testing.T) {
|
|
| 13 |
- defer reset() |
|
| 14 |
- |
|
| 15 |
- port, err := RequestPort(defaultIP, "tcp", 0) |
|
| 16 |
- if err != nil {
|
|
| 17 |
- t.Fatal(err) |
|
| 18 |
- } |
|
| 19 |
- |
|
| 20 |
- if expected := BeginPortRange; port != expected {
|
|
| 21 |
- t.Fatalf("Expected port %d got %d", expected, port)
|
|
| 22 |
- } |
|
| 23 |
-} |
|
| 24 |
- |
|
| 25 |
-func TestRequestSpecificPort(t *testing.T) {
|
|
| 26 |
- defer reset() |
|
| 27 |
- |
|
| 28 |
- port, err := RequestPort(defaultIP, "tcp", 5000) |
|
| 29 |
- if err != nil {
|
|
| 30 |
- t.Fatal(err) |
|
| 31 |
- } |
|
| 32 |
- if port != 5000 {
|
|
| 33 |
- t.Fatalf("Expected port 5000 got %d", port)
|
|
| 34 |
- } |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-func TestReleasePort(t *testing.T) {
|
|
| 38 |
- defer reset() |
|
| 39 |
- |
|
| 40 |
- port, err := RequestPort(defaultIP, "tcp", 5000) |
|
| 41 |
- if err != nil {
|
|
| 42 |
- t.Fatal(err) |
|
| 43 |
- } |
|
| 44 |
- if port != 5000 {
|
|
| 45 |
- t.Fatalf("Expected port 5000 got %d", port)
|
|
| 46 |
- } |
|
| 47 |
- |
|
| 48 |
- if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
|
|
| 49 |
- t.Fatal(err) |
|
| 50 |
- } |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-func TestReuseReleasedPort(t *testing.T) {
|
|
| 54 |
- defer reset() |
|
| 55 |
- |
|
| 56 |
- port, err := RequestPort(defaultIP, "tcp", 5000) |
|
| 57 |
- if err != nil {
|
|
| 58 |
- t.Fatal(err) |
|
| 59 |
- } |
|
| 60 |
- if port != 5000 {
|
|
| 61 |
- t.Fatalf("Expected port 5000 got %d", port)
|
|
| 62 |
- } |
|
| 63 |
- |
|
| 64 |
- if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
|
|
| 65 |
- t.Fatal(err) |
|
| 66 |
- } |
|
| 67 |
- |
|
| 68 |
- port, err = RequestPort(defaultIP, "tcp", 5000) |
|
| 69 |
- if err != nil {
|
|
| 70 |
- t.Fatal(err) |
|
| 71 |
- } |
|
| 72 |
-} |
|
| 73 |
- |
|
| 74 |
-func TestReleaseUnreadledPort(t *testing.T) {
|
|
| 75 |
- defer reset() |
|
| 76 |
- |
|
| 77 |
- port, err := RequestPort(defaultIP, "tcp", 5000) |
|
| 78 |
- if err != nil {
|
|
| 79 |
- t.Fatal(err) |
|
| 80 |
- } |
|
| 81 |
- if port != 5000 {
|
|
| 82 |
- t.Fatalf("Expected port 5000 got %d", port)
|
|
| 83 |
- } |
|
| 84 |
- |
|
| 85 |
- port, err = RequestPort(defaultIP, "tcp", 5000) |
|
| 86 |
- if err != ErrPortAlreadyAllocated {
|
|
| 87 |
- t.Fatalf("Expected error %s got %s", ErrPortAlreadyAllocated, err)
|
|
| 88 |
- } |
|
| 89 |
-} |
|
| 90 |
- |
|
| 91 |
-func TestUnknowProtocol(t *testing.T) {
|
|
| 92 |
- defer reset() |
|
| 93 |
- |
|
| 94 |
- if _, err := RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol {
|
|
| 95 |
- t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err)
|
|
| 96 |
- } |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-func TestAllocateAllPorts(t *testing.T) {
|
|
| 100 |
- defer reset() |
|
| 101 |
- |
|
| 102 |
- for i := 0; i <= EndPortRange-BeginPortRange; i++ {
|
|
| 103 |
- port, err := RequestPort(defaultIP, "tcp", 0) |
|
| 104 |
- if err != nil {
|
|
| 105 |
- t.Fatal(err) |
|
| 106 |
- } |
|
| 107 |
- |
|
| 108 |
- if expected := BeginPortRange + i; port != expected {
|
|
| 109 |
- t.Fatalf("Expected port %d got %d", expected, port)
|
|
| 110 |
- } |
|
| 111 |
- } |
|
| 112 |
- |
|
| 113 |
- if _, err := RequestPort(defaultIP, "tcp", 0); err != ErrPortExceedsRange {
|
|
| 114 |
- t.Fatalf("Expected error %s got %s", ErrPortExceedsRange, err)
|
|
| 115 |
- } |
|
| 116 |
- |
|
| 117 |
- _, err := RequestPort(defaultIP, "udp", 0) |
|
| 118 |
- if err != nil {
|
|
| 119 |
- t.Fatal(err) |
|
| 120 |
- } |
|
| 121 |
-} |
|
| 122 |
- |
|
| 123 |
-func BenchmarkAllocatePorts(b *testing.B) {
|
|
| 124 |
- defer reset() |
|
| 125 |
- |
|
| 126 |
- for i := 0; i < b.N; i++ {
|
|
| 127 |
- for i := 0; i <= EndPortRange-BeginPortRange; i++ {
|
|
| 128 |
- port, err := RequestPort(defaultIP, "tcp", 0) |
|
| 129 |
- if err != nil {
|
|
| 130 |
- b.Fatal(err) |
|
| 131 |
- } |
|
| 132 |
- |
|
| 133 |
- if expected := BeginPortRange + i; port != expected {
|
|
| 134 |
- b.Fatalf("Expected port %d got %d", expected, port)
|
|
| 135 |
- } |
|
| 136 |
- } |
|
| 137 |
- reset() |
|
| 138 |
- } |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-func TestPortAllocation(t *testing.T) {
|
|
| 142 |
- defer reset() |
|
| 143 |
- |
|
| 144 |
- ip := net.ParseIP("192.168.0.1")
|
|
| 145 |
- ip2 := net.ParseIP("192.168.0.2")
|
|
| 146 |
- if port, err := RequestPort(ip, "tcp", 80); err != nil {
|
|
| 147 |
- t.Fatal(err) |
|
| 148 |
- } else if port != 80 {
|
|
| 149 |
- t.Fatalf("Acquire(80) should return 80, not %d", port)
|
|
| 150 |
- } |
|
| 151 |
- port, err := RequestPort(ip, "tcp", 0) |
|
| 152 |
- if err != nil {
|
|
| 153 |
- t.Fatal(err) |
|
| 154 |
- } |
|
| 155 |
- if port <= 0 {
|
|
| 156 |
- t.Fatalf("Acquire(0) should return a non-zero port")
|
|
| 157 |
- } |
|
| 158 |
- |
|
| 159 |
- if _, err := RequestPort(ip, "tcp", port); err == nil {
|
|
| 160 |
- t.Fatalf("Acquiring a port already in use should return an error")
|
|
| 161 |
- } |
|
| 162 |
- |
|
| 163 |
- if newPort, err := RequestPort(ip, "tcp", 0); err != nil {
|
|
| 164 |
- t.Fatal(err) |
|
| 165 |
- } else if newPort == port {
|
|
| 166 |
- t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
|
|
| 167 |
- } |
|
| 168 |
- |
|
| 169 |
- if _, err := RequestPort(ip, "tcp", 80); err == nil {
|
|
| 170 |
- t.Fatalf("Acquiring a port already in use should return an error")
|
|
| 171 |
- } |
|
| 172 |
- if _, err := RequestPort(ip2, "tcp", 80); err != nil {
|
|
| 173 |
- t.Fatalf("It should be possible to allocate the same port on a different interface")
|
|
| 174 |
- } |
|
| 175 |
- if _, err := RequestPort(ip2, "tcp", 80); err == nil {
|
|
| 176 |
- t.Fatalf("Acquiring a port already in use should return an error")
|
|
| 177 |
- } |
|
| 178 |
- if err := ReleasePort(ip, "tcp", 80); err != nil {
|
|
| 179 |
- t.Fatal(err) |
|
| 180 |
- } |
|
| 181 |
- if _, err := RequestPort(ip, "tcp", 80); err != nil {
|
|
| 182 |
- t.Fatal(err) |
|
| 183 |
- } |
|
| 184 |
-} |
| 185 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,131 +0,0 @@ |
| 1 |
-package portmapper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "fmt" |
|
| 6 |
- "github.com/dotcloud/docker/pkg/iptables" |
|
| 7 |
- "github.com/dotcloud/docker/pkg/proxy" |
|
| 8 |
- "net" |
|
| 9 |
- "sync" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-type mapping struct {
|
|
| 13 |
- proto string |
|
| 14 |
- userlandProxy proxy.Proxy |
|
| 15 |
- host net.Addr |
|
| 16 |
- container net.Addr |
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-var ( |
|
| 20 |
- chain *iptables.Chain |
|
| 21 |
- lock sync.Mutex |
|
| 22 |
- |
|
| 23 |
- // udp:ip:port |
|
| 24 |
- currentMappings = make(map[string]*mapping) |
|
| 25 |
- newProxy = proxy.NewProxy |
|
| 26 |
-) |
|
| 27 |
- |
|
| 28 |
-var ( |
|
| 29 |
- ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
|
|
| 30 |
- ErrPortMappedForIP = errors.New("port is already mapped to ip")
|
|
| 31 |
- ErrPortNotMapped = errors.New("port is not mapped")
|
|
| 32 |
-) |
|
| 33 |
- |
|
| 34 |
-func SetIptablesChain(c *iptables.Chain) {
|
|
| 35 |
- chain = c |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-func Map(container net.Addr, hostIP net.IP, hostPort int) error {
|
|
| 39 |
- lock.Lock() |
|
| 40 |
- defer lock.Unlock() |
|
| 41 |
- |
|
| 42 |
- var m *mapping |
|
| 43 |
- switch container.(type) {
|
|
| 44 |
- case *net.TCPAddr: |
|
| 45 |
- m = &mapping{
|
|
| 46 |
- proto: "tcp", |
|
| 47 |
- host: &net.TCPAddr{IP: hostIP, Port: hostPort},
|
|
| 48 |
- container: container, |
|
| 49 |
- } |
|
| 50 |
- case *net.UDPAddr: |
|
| 51 |
- m = &mapping{
|
|
| 52 |
- proto: "udp", |
|
| 53 |
- host: &net.UDPAddr{IP: hostIP, Port: hostPort},
|
|
| 54 |
- container: container, |
|
| 55 |
- } |
|
| 56 |
- default: |
|
| 57 |
- return ErrUnknownBackendAddressType |
|
| 58 |
- } |
|
| 59 |
- |
|
| 60 |
- key := getKey(m.host) |
|
| 61 |
- if _, exists := currentMappings[key]; exists {
|
|
| 62 |
- return ErrPortMappedForIP |
|
| 63 |
- } |
|
| 64 |
- |
|
| 65 |
- containerIP, containerPort := getIPAndPort(m.container) |
|
| 66 |
- if err := forward(iptables.Add, m.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
|
|
| 67 |
- return err |
|
| 68 |
- } |
|
| 69 |
- |
|
| 70 |
- p, err := newProxy(m.host, m.container) |
|
| 71 |
- if err != nil {
|
|
| 72 |
- // need to undo the iptables rules before we reutrn |
|
| 73 |
- forward(iptables.Delete, m.proto, hostIP, hostPort, containerIP.String(), containerPort) |
|
| 74 |
- return err |
|
| 75 |
- } |
|
| 76 |
- |
|
| 77 |
- m.userlandProxy = p |
|
| 78 |
- currentMappings[key] = m |
|
| 79 |
- |
|
| 80 |
- go p.Run() |
|
| 81 |
- |
|
| 82 |
- return nil |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 |
-func Unmap(host net.Addr) error {
|
|
| 86 |
- lock.Lock() |
|
| 87 |
- defer lock.Unlock() |
|
| 88 |
- |
|
| 89 |
- key := getKey(host) |
|
| 90 |
- data, exists := currentMappings[key] |
|
| 91 |
- if !exists {
|
|
| 92 |
- return ErrPortNotMapped |
|
| 93 |
- } |
|
| 94 |
- |
|
| 95 |
- data.userlandProxy.Close() |
|
| 96 |
- delete(currentMappings, key) |
|
| 97 |
- |
|
| 98 |
- containerIP, containerPort := getIPAndPort(data.container) |
|
| 99 |
- hostIP, hostPort := getIPAndPort(data.host) |
|
| 100 |
- if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
|
|
| 101 |
- return err |
|
| 102 |
- } |
|
| 103 |
- return nil |
|
| 104 |
-} |
|
| 105 |
- |
|
| 106 |
-func getKey(a net.Addr) string {
|
|
| 107 |
- switch t := a.(type) {
|
|
| 108 |
- case *net.TCPAddr: |
|
| 109 |
- return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
|
|
| 110 |
- case *net.UDPAddr: |
|
| 111 |
- return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
|
|
| 112 |
- } |
|
| 113 |
- return "" |
|
| 114 |
-} |
|
| 115 |
- |
|
| 116 |
-func getIPAndPort(a net.Addr) (net.IP, int) {
|
|
| 117 |
- switch t := a.(type) {
|
|
| 118 |
- case *net.TCPAddr: |
|
| 119 |
- return t.IP, t.Port |
|
| 120 |
- case *net.UDPAddr: |
|
| 121 |
- return t.IP, t.Port |
|
| 122 |
- } |
|
| 123 |
- return nil, 0 |
|
| 124 |
-} |
|
| 125 |
- |
|
| 126 |
-func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
|
|
| 127 |
- if chain == nil {
|
|
| 128 |
- return nil |
|
| 129 |
- } |
|
| 130 |
- return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort) |
|
| 131 |
-} |
| 132 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,107 +0,0 @@ |
| 1 |
-package portmapper |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/pkg/iptables" |
|
| 5 |
- "github.com/dotcloud/docker/pkg/proxy" |
|
| 6 |
- "net" |
|
| 7 |
- "testing" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func init() {
|
|
| 11 |
- // override this func to mock out the proxy server |
|
| 12 |
- newProxy = proxy.NewStubProxy |
|
| 13 |
-} |
|
| 14 |
- |
|
| 15 |
-func reset() {
|
|
| 16 |
- chain = nil |
|
| 17 |
- currentMappings = make(map[string]*mapping) |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-func TestSetIptablesChain(t *testing.T) {
|
|
| 21 |
- defer reset() |
|
| 22 |
- |
|
| 23 |
- c := &iptables.Chain{
|
|
| 24 |
- Name: "TEST", |
|
| 25 |
- Bridge: "192.168.1.1", |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- if chain != nil {
|
|
| 29 |
- t.Fatal("chain should be nil at init")
|
|
| 30 |
- } |
|
| 31 |
- |
|
| 32 |
- SetIptablesChain(c) |
|
| 33 |
- if chain == nil {
|
|
| 34 |
- t.Fatal("chain should not be nil after set")
|
|
| 35 |
- } |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-func TestMapPorts(t *testing.T) {
|
|
| 39 |
- dstIp1 := net.ParseIP("192.168.0.1")
|
|
| 40 |
- dstIp2 := net.ParseIP("192.168.0.2")
|
|
| 41 |
- dstAddr1 := &net.TCPAddr{IP: dstIp1, Port: 80}
|
|
| 42 |
- dstAddr2 := &net.TCPAddr{IP: dstIp2, Port: 80}
|
|
| 43 |
- |
|
| 44 |
- srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
|
|
| 45 |
- srcAddr2 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")}
|
|
| 46 |
- |
|
| 47 |
- if err := Map(srcAddr1, dstIp1, 80); err != nil {
|
|
| 48 |
- t.Fatalf("Failed to allocate port: %s", err)
|
|
| 49 |
- } |
|
| 50 |
- |
|
| 51 |
- if Map(srcAddr1, dstIp1, 80) == nil {
|
|
| 52 |
- t.Fatalf("Port is in use - mapping should have failed")
|
|
| 53 |
- } |
|
| 54 |
- |
|
| 55 |
- if Map(srcAddr2, dstIp1, 80) == nil {
|
|
| 56 |
- t.Fatalf("Port is in use - mapping should have failed")
|
|
| 57 |
- } |
|
| 58 |
- |
|
| 59 |
- if err := Map(srcAddr2, dstIp2, 80); err != nil {
|
|
| 60 |
- t.Fatalf("Failed to allocate port: %s", err)
|
|
| 61 |
- } |
|
| 62 |
- |
|
| 63 |
- if Unmap(dstAddr1) != nil {
|
|
| 64 |
- t.Fatalf("Failed to release port")
|
|
| 65 |
- } |
|
| 66 |
- |
|
| 67 |
- if Unmap(dstAddr2) != nil {
|
|
| 68 |
- t.Fatalf("Failed to release port")
|
|
| 69 |
- } |
|
| 70 |
- |
|
| 71 |
- if Unmap(dstAddr2) == nil {
|
|
| 72 |
- t.Fatalf("Port already released, but no error reported")
|
|
| 73 |
- } |
|
| 74 |
-} |
|
| 75 |
- |
|
| 76 |
-func TestGetUDPKey(t *testing.T) {
|
|
| 77 |
- addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53}
|
|
| 78 |
- |
|
| 79 |
- key := getKey(addr) |
|
| 80 |
- |
|
| 81 |
- if expected := "192.168.1.5:53/udp"; key != expected {
|
|
| 82 |
- t.Fatalf("expected key %s got %s", expected, key)
|
|
| 83 |
- } |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-func TestGetTCPKey(t *testing.T) {
|
|
| 87 |
- addr := &net.TCPAddr{IP: net.ParseIP("192.168.1.5"), Port: 80}
|
|
| 88 |
- |
|
| 89 |
- key := getKey(addr) |
|
| 90 |
- |
|
| 91 |
- if expected := "192.168.1.5:80/tcp"; key != expected {
|
|
| 92 |
- t.Fatalf("expected key %s got %s", expected, key)
|
|
| 93 |
- } |
|
| 94 |
-} |
|
| 95 |
- |
|
| 96 |
-func TestGetUDPIPAndPort(t *testing.T) {
|
|
| 97 |
- addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53}
|
|
| 98 |
- |
|
| 99 |
- ip, port := getIPAndPort(addr) |
|
| 100 |
- if expected := "192.168.1.5"; ip.String() != expected {
|
|
| 101 |
- t.Fatalf("expected ip %s got %s", expected, ip)
|
|
| 102 |
- } |
|
| 103 |
- |
|
| 104 |
- if ep := 53; port != ep {
|
|
| 105 |
- t.Fatalf("expected port %d got %d", ep, port)
|
|
| 106 |
- } |
|
| 107 |
-} |
| 108 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,118 +0,0 @@ |
| 1 |
-package networkdriver |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "encoding/binary" |
|
| 5 |
- "errors" |
|
| 6 |
- "fmt" |
|
| 7 |
- "net" |
|
| 8 |
- |
|
| 9 |
- "github.com/dotcloud/docker/pkg/netlink" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-var ( |
|
| 13 |
- networkGetRoutesFct = netlink.NetworkGetRoutes |
|
| 14 |
- ErrNoDefaultRoute = errors.New("no default route")
|
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
|
|
| 18 |
- if len(nameservers) > 0 {
|
|
| 19 |
- for _, ns := range nameservers {
|
|
| 20 |
- _, nsNetwork, err := net.ParseCIDR(ns) |
|
| 21 |
- if err != nil {
|
|
| 22 |
- return err |
|
| 23 |
- } |
|
| 24 |
- if NetworkOverlaps(toCheck, nsNetwork) {
|
|
| 25 |
- return ErrNetworkOverlapsWithNameservers |
|
| 26 |
- } |
|
| 27 |
- } |
|
| 28 |
- } |
|
| 29 |
- return nil |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func CheckRouteOverlaps(toCheck *net.IPNet) error {
|
|
| 33 |
- networks, err := networkGetRoutesFct() |
|
| 34 |
- if err != nil {
|
|
| 35 |
- return err |
|
| 36 |
- } |
|
| 37 |
- |
|
| 38 |
- for _, network := range networks {
|
|
| 39 |
- if network.IPNet != nil && NetworkOverlaps(toCheck, network.IPNet) {
|
|
| 40 |
- return ErrNetworkOverlaps |
|
| 41 |
- } |
|
| 42 |
- } |
|
| 43 |
- return nil |
|
| 44 |
-} |
|
| 45 |
- |
|
| 46 |
-// Detects overlap between one IPNet and another |
|
| 47 |
-func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool {
|
|
| 48 |
- if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) {
|
|
| 49 |
- return true |
|
| 50 |
- } |
|
| 51 |
- if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) {
|
|
| 52 |
- return true |
|
| 53 |
- } |
|
| 54 |
- return false |
|
| 55 |
-} |
|
| 56 |
- |
|
| 57 |
-// Calculates the first and last IP addresses in an IPNet |
|
| 58 |
-func NetworkRange(network *net.IPNet) (net.IP, net.IP) {
|
|
| 59 |
- var ( |
|
| 60 |
- netIP = network.IP.To4() |
|
| 61 |
- firstIP = netIP.Mask(network.Mask) |
|
| 62 |
- lastIP = net.IPv4(0, 0, 0, 0).To4() |
|
| 63 |
- ) |
|
| 64 |
- |
|
| 65 |
- for i := 0; i < len(lastIP); i++ {
|
|
| 66 |
- lastIP[i] = netIP[i] | ^network.Mask[i] |
|
| 67 |
- } |
|
| 68 |
- return firstIP, lastIP |
|
| 69 |
-} |
|
| 70 |
- |
|
| 71 |
-// Given a netmask, calculates the number of available hosts |
|
| 72 |
-func NetworkSize(mask net.IPMask) int32 {
|
|
| 73 |
- m := net.IPv4Mask(0, 0, 0, 0) |
|
| 74 |
- for i := 0; i < net.IPv4len; i++ {
|
|
| 75 |
- m[i] = ^mask[i] |
|
| 76 |
- } |
|
| 77 |
- return int32(binary.BigEndian.Uint32(m)) + 1 |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-// Return the IPv4 address of a network interface |
|
| 81 |
-func GetIfaceAddr(name string) (net.Addr, error) {
|
|
| 82 |
- iface, err := net.InterfaceByName(name) |
|
| 83 |
- if err != nil {
|
|
| 84 |
- return nil, err |
|
| 85 |
- } |
|
| 86 |
- addrs, err := iface.Addrs() |
|
| 87 |
- if err != nil {
|
|
| 88 |
- return nil, err |
|
| 89 |
- } |
|
| 90 |
- var addrs4 []net.Addr |
|
| 91 |
- for _, addr := range addrs {
|
|
| 92 |
- ip := (addr.(*net.IPNet)).IP |
|
| 93 |
- if ip4 := ip.To4(); len(ip4) == net.IPv4len {
|
|
| 94 |
- addrs4 = append(addrs4, addr) |
|
| 95 |
- } |
|
| 96 |
- } |
|
| 97 |
- switch {
|
|
| 98 |
- case len(addrs4) == 0: |
|
| 99 |
- return nil, fmt.Errorf("Interface %v has no IP addresses", name)
|
|
| 100 |
- case len(addrs4) > 1: |
|
| 101 |
- fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n",
|
|
| 102 |
- name, (addrs4[0].(*net.IPNet)).IP) |
|
| 103 |
- } |
|
| 104 |
- return addrs4[0], nil |
|
| 105 |
-} |
|
| 106 |
- |
|
| 107 |
-func GetDefaultRouteIface() (*net.Interface, error) {
|
|
| 108 |
- rs, err := networkGetRoutesFct() |
|
| 109 |
- if err != nil {
|
|
| 110 |
- return nil, fmt.Errorf("unable to get routes: %v", err)
|
|
| 111 |
- } |
|
| 112 |
- for _, r := range rs {
|
|
| 113 |
- if r.Default {
|
|
| 114 |
- return r.Iface, nil |
|
| 115 |
- } |
|
| 116 |
- } |
|
| 117 |
- return nil, ErrNoDefaultRoute |
|
| 118 |
-} |
| 119 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,159 @@ |
| 0 |
+package ipallocator |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/binary" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "github.com/dotcloud/docker/runtime/networkdriver" |
|
| 6 |
+ "github.com/dotcloud/docker/pkg/collections" |
|
| 7 |
+ "net" |
|
| 8 |
+ "sync" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+type networkSet map[string]*collections.OrderedIntSet |
|
| 12 |
+ |
|
| 13 |
+var ( |
|
| 14 |
+ ErrNoAvailableIPs = errors.New("no available ip addresses on network")
|
|
| 15 |
+ ErrIPAlreadyAllocated = errors.New("ip already allocated")
|
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+var ( |
|
| 19 |
+ lock = sync.Mutex{}
|
|
| 20 |
+ allocatedIPs = networkSet{}
|
|
| 21 |
+ availableIPS = networkSet{}
|
|
| 22 |
+) |
|
| 23 |
+ |
|
| 24 |
+// RequestIP requests an available ip from the given network. It |
|
| 25 |
+// will return the next available ip if the ip provided is nil. If the |
|
| 26 |
+// ip provided is not nil it will validate that the provided ip is available |
|
| 27 |
+// for use or return an error |
|
| 28 |
+func RequestIP(address *net.IPNet, ip *net.IP) (*net.IP, error) {
|
|
| 29 |
+ lock.Lock() |
|
| 30 |
+ defer lock.Unlock() |
|
| 31 |
+ |
|
| 32 |
+ checkAddress(address) |
|
| 33 |
+ |
|
| 34 |
+ if ip == nil {
|
|
| 35 |
+ next, err := getNextIp(address) |
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ return nil, err |
|
| 38 |
+ } |
|
| 39 |
+ return next, nil |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ if err := registerIP(address, ip); err != nil {
|
|
| 43 |
+ return nil, err |
|
| 44 |
+ } |
|
| 45 |
+ return ip, nil |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// ReleaseIP adds the provided ip back into the pool of |
|
| 49 |
+// available ips to be returned for use. |
|
| 50 |
+func ReleaseIP(address *net.IPNet, ip *net.IP) error {
|
|
| 51 |
+ lock.Lock() |
|
| 52 |
+ defer lock.Unlock() |
|
| 53 |
+ |
|
| 54 |
+ checkAddress(address) |
|
| 55 |
+ |
|
| 56 |
+ var ( |
|
| 57 |
+ existing = allocatedIPs[address.String()] |
|
| 58 |
+ available = availableIPS[address.String()] |
|
| 59 |
+ pos = getPosition(address, ip) |
|
| 60 |
+ ) |
|
| 61 |
+ |
|
| 62 |
+ existing.Remove(int(pos)) |
|
| 63 |
+ available.Push(int(pos)) |
|
| 64 |
+ |
|
| 65 |
+ return nil |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// convert the ip into the position in the subnet. Only |
|
| 69 |
+// position are saved in the set |
|
| 70 |
+func getPosition(address *net.IPNet, ip *net.IP) int32 {
|
|
| 71 |
+ var ( |
|
| 72 |
+ first, _ = networkdriver.NetworkRange(address) |
|
| 73 |
+ base = ipToInt(&first) |
|
| 74 |
+ i = ipToInt(ip) |
|
| 75 |
+ ) |
|
| 76 |
+ return i - base |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// return an available ip if one is currently available. If not, |
|
| 80 |
+// return the next available ip for the nextwork |
|
| 81 |
+func getNextIp(address *net.IPNet) (*net.IP, error) {
|
|
| 82 |
+ var ( |
|
| 83 |
+ ownIP = ipToInt(&address.IP) |
|
| 84 |
+ available = availableIPS[address.String()] |
|
| 85 |
+ allocated = allocatedIPs[address.String()] |
|
| 86 |
+ first, _ = networkdriver.NetworkRange(address) |
|
| 87 |
+ base = ipToInt(&first) |
|
| 88 |
+ size = int(networkdriver.NetworkSize(address.Mask)) |
|
| 89 |
+ max = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address |
|
| 90 |
+ pos = int32(available.Pop()) |
|
| 91 |
+ ) |
|
| 92 |
+ |
|
| 93 |
+ // We pop and push the position not the ip |
|
| 94 |
+ if pos != 0 {
|
|
| 95 |
+ ip := intToIP(int32(base + pos)) |
|
| 96 |
+ allocated.Push(int(pos)) |
|
| 97 |
+ |
|
| 98 |
+ return ip, nil |
|
| 99 |
+ } |
|
| 100 |
+ |
|
| 101 |
+ var ( |
|
| 102 |
+ firstNetIP = address.IP.To4().Mask(address.Mask) |
|
| 103 |
+ firstAsInt = ipToInt(&firstNetIP) + 1 |
|
| 104 |
+ ) |
|
| 105 |
+ |
|
| 106 |
+ pos = int32(allocated.PullBack()) |
|
| 107 |
+ for i := int32(0); i < max; i++ {
|
|
| 108 |
+ pos = pos%max + 1 |
|
| 109 |
+ next := int32(base + pos) |
|
| 110 |
+ |
|
| 111 |
+ if next == ownIP || next == firstAsInt {
|
|
| 112 |
+ continue |
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ if !allocated.Exists(int(pos)) {
|
|
| 116 |
+ ip := intToIP(next) |
|
| 117 |
+ allocated.Push(int(pos)) |
|
| 118 |
+ return ip, nil |
|
| 119 |
+ } |
|
| 120 |
+ } |
|
| 121 |
+ return nil, ErrNoAvailableIPs |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+func registerIP(address *net.IPNet, ip *net.IP) error {
|
|
| 125 |
+ var ( |
|
| 126 |
+ existing = allocatedIPs[address.String()] |
|
| 127 |
+ available = availableIPS[address.String()] |
|
| 128 |
+ pos = getPosition(address, ip) |
|
| 129 |
+ ) |
|
| 130 |
+ |
|
| 131 |
+ if existing.Exists(int(pos)) {
|
|
| 132 |
+ return ErrIPAlreadyAllocated |
|
| 133 |
+ } |
|
| 134 |
+ available.Remove(int(pos)) |
|
| 135 |
+ |
|
| 136 |
+ return nil |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+// Converts a 4 bytes IP into a 32 bit integer |
|
| 140 |
+func ipToInt(ip *net.IP) int32 {
|
|
| 141 |
+ return int32(binary.BigEndian.Uint32(ip.To4())) |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+// Converts 32 bit integer into a 4 bytes IP address |
|
| 145 |
+func intToIP(n int32) *net.IP {
|
|
| 146 |
+ b := make([]byte, 4) |
|
| 147 |
+ binary.BigEndian.PutUint32(b, uint32(n)) |
|
| 148 |
+ ip := net.IP(b) |
|
| 149 |
+ return &ip |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+func checkAddress(address *net.IPNet) {
|
|
| 153 |
+ key := address.String() |
|
| 154 |
+ if _, exists := allocatedIPs[key]; !exists {
|
|
| 155 |
+ allocatedIPs[key] = collections.NewOrderedIntSet() |
|
| 156 |
+ availableIPS[key] = collections.NewOrderedIntSet() |
|
| 157 |
+ } |
|
| 158 |
+} |
| 0 | 159 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,241 @@ |
| 0 |
+package ipallocator |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net" |
|
| 5 |
+ "testing" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func reset() {
|
|
| 9 |
+ allocatedIPs = networkSet{}
|
|
| 10 |
+ availableIPS = networkSet{}
|
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func TestRequestNewIps(t *testing.T) {
|
|
| 14 |
+ defer reset() |
|
| 15 |
+ network := &net.IPNet{
|
|
| 16 |
+ IP: []byte{192, 168, 0, 1},
|
|
| 17 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ for i := 2; i < 10; i++ {
|
|
| 21 |
+ ip, err := RequestIP(network, nil) |
|
| 22 |
+ if err != nil {
|
|
| 23 |
+ t.Fatal(err) |
|
| 24 |
+ } |
|
| 25 |
+ |
|
| 26 |
+ if expected := fmt.Sprintf("192.168.0.%d", i); ip.String() != expected {
|
|
| 27 |
+ t.Fatalf("Expected ip %s got %s", expected, ip.String())
|
|
| 28 |
+ } |
|
| 29 |
+ } |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func TestReleaseIp(t *testing.T) {
|
|
| 33 |
+ defer reset() |
|
| 34 |
+ network := &net.IPNet{
|
|
| 35 |
+ IP: []byte{192, 168, 0, 1},
|
|
| 36 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ ip, err := RequestIP(network, nil) |
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ t.Fatal(err) |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ if err := ReleaseIP(network, ip); err != nil {
|
|
| 45 |
+ t.Fatal(err) |
|
| 46 |
+ } |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func TestGetReleasedIp(t *testing.T) {
|
|
| 50 |
+ defer reset() |
|
| 51 |
+ network := &net.IPNet{
|
|
| 52 |
+ IP: []byte{192, 168, 0, 1},
|
|
| 53 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 56 |
+ ip, err := RequestIP(network, nil) |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ t.Fatal(err) |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ value := ip.String() |
|
| 62 |
+ if err := ReleaseIP(network, ip); err != nil {
|
|
| 63 |
+ t.Fatal(err) |
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ ip, err = RequestIP(network, nil) |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ t.Fatal(err) |
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ if ip.String() != value {
|
|
| 72 |
+ t.Fatalf("Expected to receive same ip %s got %s", value, ip.String())
|
|
| 73 |
+ } |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func TestRequesetSpecificIp(t *testing.T) {
|
|
| 77 |
+ defer reset() |
|
| 78 |
+ network := &net.IPNet{
|
|
| 79 |
+ IP: []byte{192, 168, 0, 1},
|
|
| 80 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ ip := net.ParseIP("192.168.1.5")
|
|
| 84 |
+ |
|
| 85 |
+ if _, err := RequestIP(network, &ip); err != nil {
|
|
| 86 |
+ t.Fatal(err) |
|
| 87 |
+ } |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func TestConversion(t *testing.T) {
|
|
| 91 |
+ ip := net.ParseIP("127.0.0.1")
|
|
| 92 |
+ i := ipToInt(&ip) |
|
| 93 |
+ if i == 0 {
|
|
| 94 |
+ t.Fatal("converted to zero")
|
|
| 95 |
+ } |
|
| 96 |
+ conv := intToIP(i) |
|
| 97 |
+ if !ip.Equal(*conv) {
|
|
| 98 |
+ t.Error(conv.String()) |
|
| 99 |
+ } |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+func TestIPAllocator(t *testing.T) {
|
|
| 103 |
+ expectedIPs := []net.IP{
|
|
| 104 |
+ 0: net.IPv4(127, 0, 0, 2), |
|
| 105 |
+ 1: net.IPv4(127, 0, 0, 3), |
|
| 106 |
+ 2: net.IPv4(127, 0, 0, 4), |
|
| 107 |
+ 3: net.IPv4(127, 0, 0, 5), |
|
| 108 |
+ 4: net.IPv4(127, 0, 0, 6), |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ gwIP, n, _ := net.ParseCIDR("127.0.0.1/29")
|
|
| 112 |
+ network := &net.IPNet{IP: gwIP, Mask: n.Mask}
|
|
| 113 |
+ // Pool after initialisation (f = free, u = used) |
|
| 114 |
+ // 2(f) - 3(f) - 4(f) - 5(f) - 6(f) |
|
| 115 |
+ // ↑ |
|
| 116 |
+ |
|
| 117 |
+ // Check that we get 5 IPs, from 127.0.0.2–127.0.0.6, in that |
|
| 118 |
+ // order. |
|
| 119 |
+ for i := 0; i < 5; i++ {
|
|
| 120 |
+ ip, err := RequestIP(network, nil) |
|
| 121 |
+ if err != nil {
|
|
| 122 |
+ t.Fatal(err) |
|
| 123 |
+ } |
|
| 124 |
+ |
|
| 125 |
+ assertIPEquals(t, &expectedIPs[i], ip) |
|
| 126 |
+ } |
|
| 127 |
+ // Before loop begin |
|
| 128 |
+ // 2(f) - 3(f) - 4(f) - 5(f) - 6(f) |
|
| 129 |
+ // ↑ |
|
| 130 |
+ |
|
| 131 |
+ // After i = 0 |
|
| 132 |
+ // 2(u) - 3(f) - 4(f) - 5(f) - 6(f) |
|
| 133 |
+ // ↑ |
|
| 134 |
+ |
|
| 135 |
+ // After i = 1 |
|
| 136 |
+ // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) |
|
| 137 |
+ // ↑ |
|
| 138 |
+ |
|
| 139 |
+ // After i = 2 |
|
| 140 |
+ // 2(u) - 3(u) - 4(u) - 5(f) - 6(f) |
|
| 141 |
+ // ↑ |
|
| 142 |
+ |
|
| 143 |
+ // After i = 3 |
|
| 144 |
+ // 2(u) - 3(u) - 4(u) - 5(u) - 6(f) |
|
| 145 |
+ // ↑ |
|
| 146 |
+ |
|
| 147 |
+ // After i = 4 |
|
| 148 |
+ // 2(u) - 3(u) - 4(u) - 5(u) - 6(u) |
|
| 149 |
+ // ↑ |
|
| 150 |
+ |
|
| 151 |
+ // Check that there are no more IPs |
|
| 152 |
+ ip, err := RequestIP(network, nil) |
|
| 153 |
+ if err == nil {
|
|
| 154 |
+ t.Fatalf("There shouldn't be any IP addresses at this point, got %s\n", ip)
|
|
| 155 |
+ } |
|
| 156 |
+ |
|
| 157 |
+ // Release some IPs in non-sequential order |
|
| 158 |
+ if err := ReleaseIP(network, &expectedIPs[3]); err != nil {
|
|
| 159 |
+ t.Fatal(err) |
|
| 160 |
+ } |
|
| 161 |
+ // 2(u) - 3(u) - 4(u) - 5(f) - 6(u) |
|
| 162 |
+ // ↑ |
|
| 163 |
+ |
|
| 164 |
+ if err := ReleaseIP(network, &expectedIPs[2]); err != nil {
|
|
| 165 |
+ t.Fatal(err) |
|
| 166 |
+ } |
|
| 167 |
+ // 2(u) - 3(u) - 4(f) - 5(f) - 6(u) |
|
| 168 |
+ // ↑ |
|
| 169 |
+ |
|
| 170 |
+ if err := ReleaseIP(network, &expectedIPs[4]); err != nil {
|
|
| 171 |
+ t.Fatal(err) |
|
| 172 |
+ } |
|
| 173 |
+ // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) |
|
| 174 |
+ // ↑ |
|
| 175 |
+ |
|
| 176 |
+ // Make sure that IPs are reused in sequential order, starting |
|
| 177 |
+ // with the first released IP |
|
| 178 |
+ newIPs := make([]*net.IP, 3) |
|
| 179 |
+ for i := 0; i < 3; i++ {
|
|
| 180 |
+ ip, err := RequestIP(network, nil) |
|
| 181 |
+ if err != nil {
|
|
| 182 |
+ t.Fatal(err) |
|
| 183 |
+ } |
|
| 184 |
+ |
|
| 185 |
+ newIPs[i] = ip |
|
| 186 |
+ } |
|
| 187 |
+ // Before loop begin |
|
| 188 |
+ // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) |
|
| 189 |
+ // ↑ |
|
| 190 |
+ |
|
| 191 |
+ // After i = 0 |
|
| 192 |
+ // 2(u) - 3(u) - 4(f) - 5(u) - 6(f) |
|
| 193 |
+ // ↑ |
|
| 194 |
+ |
|
| 195 |
+ // After i = 1 |
|
| 196 |
+ // 2(u) - 3(u) - 4(f) - 5(u) - 6(u) |
|
| 197 |
+ // ↑ |
|
| 198 |
+ |
|
| 199 |
+ // After i = 2 |
|
| 200 |
+ // 2(u) - 3(u) - 4(u) - 5(u) - 6(u) |
|
| 201 |
+ // ↑ |
|
| 202 |
+ |
|
| 203 |
+ // Reordered these because the new set will always return the |
|
| 204 |
+ // lowest ips first and not in the order that they were released |
|
| 205 |
+ assertIPEquals(t, &expectedIPs[2], newIPs[0]) |
|
| 206 |
+ assertIPEquals(t, &expectedIPs[3], newIPs[1]) |
|
| 207 |
+ assertIPEquals(t, &expectedIPs[4], newIPs[2]) |
|
| 208 |
+ |
|
| 209 |
+ _, err = RequestIP(network, nil) |
|
| 210 |
+ if err == nil {
|
|
| 211 |
+ t.Fatal("There shouldn't be any IP addresses at this point")
|
|
| 212 |
+ } |
|
| 213 |
+} |
|
| 214 |
+ |
|
| 215 |
+func TestAllocateFirstIP(t *testing.T) {
|
|
| 216 |
+ defer reset() |
|
| 217 |
+ network := &net.IPNet{
|
|
| 218 |
+ IP: []byte{192, 168, 0, 0},
|
|
| 219 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 220 |
+ } |
|
| 221 |
+ |
|
| 222 |
+ firstIP := network.IP.To4().Mask(network.Mask) |
|
| 223 |
+ first := ipToInt(&firstIP) + 1 |
|
| 224 |
+ |
|
| 225 |
+ ip, err := RequestIP(network, nil) |
|
| 226 |
+ if err != nil {
|
|
| 227 |
+ t.Fatal(err) |
|
| 228 |
+ } |
|
| 229 |
+ allocated := ipToInt(ip) |
|
| 230 |
+ |
|
| 231 |
+ if allocated == first {
|
|
| 232 |
+ t.Fatalf("allocated ip should not equal first ip: %d == %d", first, allocated)
|
|
| 233 |
+ } |
|
| 234 |
+} |
|
| 235 |
+ |
|
| 236 |
+func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) {
|
|
| 237 |
+ if !ip1.Equal(*ip2) {
|
|
| 238 |
+ t.Fatalf("Expected IP %s, got %s", ip1, ip2)
|
|
| 239 |
+ } |
|
| 240 |
+} |
| 0 | 241 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,482 @@ |
| 0 |
+package lxc |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/dotcloud/docker/engine" |
|
| 5 |
+ "github.com/dotcloud/docker/runtime/networkdriver" |
|
| 6 |
+ "github.com/dotcloud/docker/runtime/networkdriver/ipallocator" |
|
| 7 |
+ "github.com/dotcloud/docker/runtime/networkdriver/portallocator" |
|
| 8 |
+ "github.com/dotcloud/docker/runtime/networkdriver/portmapper" |
|
| 9 |
+ "github.com/dotcloud/docker/pkg/iptables" |
|
| 10 |
+ "github.com/dotcloud/docker/pkg/netlink" |
|
| 11 |
+ "github.com/dotcloud/docker/utils" |
|
| 12 |
+ "io/ioutil" |
|
| 13 |
+ "log" |
|
| 14 |
+ "net" |
|
| 15 |
+ "strings" |
|
| 16 |
+ "syscall" |
|
| 17 |
+ "unsafe" |
|
| 18 |
+) |
|
| 19 |
+ |
|
| 20 |
+const ( |
|
| 21 |
+ DefaultNetworkBridge = "docker0" |
|
| 22 |
+ siocBRADDBR = 0x89a0 |
|
| 23 |
+) |
|
| 24 |
+ |
|
| 25 |
+// Network interface represents the networking stack of a container |
|
| 26 |
+type networkInterface struct {
|
|
| 27 |
+ IP net.IP |
|
| 28 |
+ PortMappings []net.Addr // there are mappings to the host interfaces |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+var ( |
|
| 32 |
+ addrs = []string{
|
|
| 33 |
+ // Here we don't follow the convention of using the 1st IP of the range for the gateway. |
|
| 34 |
+ // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. |
|
| 35 |
+ // In theory this shouldn't matter - in practice there's bound to be a few scripts relying |
|
| 36 |
+ // on the internal addressing or other stupid things like that. |
|
| 37 |
+ // The shouldn't, but hey, let's not break them unless we really have to. |
|
| 38 |
+ "172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 |
|
| 39 |
+ "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive |
|
| 40 |
+ "10.1.42.1/16", |
|
| 41 |
+ "10.42.42.1/16", |
|
| 42 |
+ "172.16.42.1/24", |
|
| 43 |
+ "172.16.43.1/24", |
|
| 44 |
+ "172.16.44.1/24", |
|
| 45 |
+ "10.0.42.1/24", |
|
| 46 |
+ "10.0.43.1/24", |
|
| 47 |
+ "192.168.42.1/24", |
|
| 48 |
+ "192.168.43.1/24", |
|
| 49 |
+ "192.168.44.1/24", |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ bridgeIface string |
|
| 53 |
+ bridgeNetwork *net.IPNet |
|
| 54 |
+ |
|
| 55 |
+ defaultBindingIP = net.ParseIP("0.0.0.0")
|
|
| 56 |
+ currentInterfaces = make(map[string]*networkInterface) |
|
| 57 |
+) |
|
| 58 |
+ |
|
| 59 |
+func InitDriver(job *engine.Job) engine.Status {
|
|
| 60 |
+ var ( |
|
| 61 |
+ network *net.IPNet |
|
| 62 |
+ enableIPTables = job.GetenvBool("EnableIptables")
|
|
| 63 |
+ icc = job.GetenvBool("InterContainerCommunication")
|
|
| 64 |
+ ipForward = job.GetenvBool("EnableIpForward")
|
|
| 65 |
+ bridgeIP = job.Getenv("BridgeIP")
|
|
| 66 |
+ ) |
|
| 67 |
+ |
|
| 68 |
+ if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {
|
|
| 69 |
+ defaultBindingIP = net.ParseIP(defaultIP) |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ bridgeIface = job.Getenv("BridgeIface")
|
|
| 73 |
+ if bridgeIface == "" {
|
|
| 74 |
+ bridgeIface = DefaultNetworkBridge |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ addr, err := networkdriver.GetIfaceAddr(bridgeIface) |
|
| 78 |
+ if err != nil {
|
|
| 79 |
+ // If the iface is not found, try to create it |
|
| 80 |
+ job.Logf("creating new bridge for %s", bridgeIface)
|
|
| 81 |
+ if err := createBridge(bridgeIP); err != nil {
|
|
| 82 |
+ job.Error(err) |
|
| 83 |
+ return engine.StatusErr |
|
| 84 |
+ } |
|
| 85 |
+ |
|
| 86 |
+ job.Logf("getting iface addr")
|
|
| 87 |
+ addr, err = networkdriver.GetIfaceAddr(bridgeIface) |
|
| 88 |
+ if err != nil {
|
|
| 89 |
+ job.Error(err) |
|
| 90 |
+ return engine.StatusErr |
|
| 91 |
+ } |
|
| 92 |
+ network = addr.(*net.IPNet) |
|
| 93 |
+ } else {
|
|
| 94 |
+ network = addr.(*net.IPNet) |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ // Configure iptables for link support |
|
| 98 |
+ if enableIPTables {
|
|
| 99 |
+ if err := setupIPTables(addr, icc); err != nil {
|
|
| 100 |
+ job.Error(err) |
|
| 101 |
+ return engine.StatusErr |
|
| 102 |
+ } |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ if ipForward {
|
|
| 106 |
+ // Enable IPv4 forwarding |
|
| 107 |
+ if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil {
|
|
| 108 |
+ job.Logf("WARNING: unable to enable IPv4 forwarding: %s\n", err)
|
|
| 109 |
+ } |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ // We can always try removing the iptables |
|
| 113 |
+ if err := iptables.RemoveExistingChain("DOCKER"); err != nil {
|
|
| 114 |
+ job.Error(err) |
|
| 115 |
+ return engine.StatusErr |
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ if enableIPTables {
|
|
| 119 |
+ chain, err := iptables.NewChain("DOCKER", bridgeIface)
|
|
| 120 |
+ if err != nil {
|
|
| 121 |
+ job.Error(err) |
|
| 122 |
+ return engine.StatusErr |
|
| 123 |
+ } |
|
| 124 |
+ portmapper.SetIptablesChain(chain) |
|
| 125 |
+ } |
|
| 126 |
+ |
|
| 127 |
+ bridgeNetwork = network |
|
| 128 |
+ |
|
| 129 |
+ // https://github.com/dotcloud/docker/issues/2768 |
|
| 130 |
+ job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeNetwork.IP)
|
|
| 131 |
+ |
|
| 132 |
+ for name, f := range map[string]engine.Handler{
|
|
| 133 |
+ "allocate_interface": Allocate, |
|
| 134 |
+ "release_interface": Release, |
|
| 135 |
+ "allocate_port": AllocatePort, |
|
| 136 |
+ "link": LinkContainers, |
|
| 137 |
+ } {
|
|
| 138 |
+ if err := job.Eng.Register(name, f); err != nil {
|
|
| 139 |
+ job.Error(err) |
|
| 140 |
+ return engine.StatusErr |
|
| 141 |
+ } |
|
| 142 |
+ } |
|
| 143 |
+ return engine.StatusOK |
|
| 144 |
+} |
|
| 145 |
+ |
|
| 146 |
+func setupIPTables(addr net.Addr, icc bool) error {
|
|
| 147 |
+ // Enable NAT |
|
| 148 |
+ natArgs := []string{"POSTROUTING", "-t", "nat", "-s", addr.String(), "!", "-d", addr.String(), "-j", "MASQUERADE"}
|
|
| 149 |
+ |
|
| 150 |
+ if !iptables.Exists(natArgs...) {
|
|
| 151 |
+ if output, err := iptables.Raw(append([]string{"-I"}, natArgs...)...); err != nil {
|
|
| 152 |
+ return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
|
|
| 153 |
+ } else if len(output) != 0 {
|
|
| 154 |
+ return fmt.Errorf("Error iptables postrouting: %s", output)
|
|
| 155 |
+ } |
|
| 156 |
+ } |
|
| 157 |
+ |
|
| 158 |
+ var ( |
|
| 159 |
+ args = []string{"FORWARD", "-i", bridgeIface, "-o", bridgeIface, "-j"}
|
|
| 160 |
+ acceptArgs = append(args, "ACCEPT") |
|
| 161 |
+ dropArgs = append(args, "DROP") |
|
| 162 |
+ ) |
|
| 163 |
+ |
|
| 164 |
+ if !icc {
|
|
| 165 |
+ iptables.Raw(append([]string{"-D"}, acceptArgs...)...)
|
|
| 166 |
+ |
|
| 167 |
+ if !iptables.Exists(dropArgs...) {
|
|
| 168 |
+ utils.Debugf("Disable inter-container communication")
|
|
| 169 |
+ if output, err := iptables.Raw(append([]string{"-I"}, dropArgs...)...); err != nil {
|
|
| 170 |
+ return fmt.Errorf("Unable to prevent intercontainer communication: %s", err)
|
|
| 171 |
+ } else if len(output) != 0 {
|
|
| 172 |
+ return fmt.Errorf("Error disabling intercontainer communication: %s", output)
|
|
| 173 |
+ } |
|
| 174 |
+ } |
|
| 175 |
+ } else {
|
|
| 176 |
+ iptables.Raw(append([]string{"-D"}, dropArgs...)...)
|
|
| 177 |
+ |
|
| 178 |
+ if !iptables.Exists(acceptArgs...) {
|
|
| 179 |
+ utils.Debugf("Enable inter-container communication")
|
|
| 180 |
+ if output, err := iptables.Raw(append([]string{"-I"}, acceptArgs...)...); err != nil {
|
|
| 181 |
+ return fmt.Errorf("Unable to allow intercontainer communication: %s", err)
|
|
| 182 |
+ } else if len(output) != 0 {
|
|
| 183 |
+ return fmt.Errorf("Error enabling intercontainer communication: %s", output)
|
|
| 184 |
+ } |
|
| 185 |
+ } |
|
| 186 |
+ } |
|
| 187 |
+ |
|
| 188 |
+ // Accept all non-intercontainer outgoing packets |
|
| 189 |
+ outgoingArgs := []string{"FORWARD", "-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}
|
|
| 190 |
+ if !iptables.Exists(outgoingArgs...) {
|
|
| 191 |
+ if output, err := iptables.Raw(append([]string{"-I"}, outgoingArgs...)...); err != nil {
|
|
| 192 |
+ return fmt.Errorf("Unable to allow outgoing packets: %s", err)
|
|
| 193 |
+ } else if len(output) != 0 {
|
|
| 194 |
+ return fmt.Errorf("Error iptables allow outgoing: %s", output)
|
|
| 195 |
+ } |
|
| 196 |
+ } |
|
| 197 |
+ |
|
| 198 |
+ // Accept incoming packets for existing connections |
|
| 199 |
+ existingArgs := []string{"FORWARD", "-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}
|
|
| 200 |
+ |
|
| 201 |
+ if !iptables.Exists(existingArgs...) {
|
|
| 202 |
+ if output, err := iptables.Raw(append([]string{"-I"}, existingArgs...)...); err != nil {
|
|
| 203 |
+ return fmt.Errorf("Unable to allow incoming packets: %s", err)
|
|
| 204 |
+ } else if len(output) != 0 {
|
|
| 205 |
+ return fmt.Errorf("Error iptables allow incoming: %s", output)
|
|
| 206 |
+ } |
|
| 207 |
+ } |
|
| 208 |
+ return nil |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+// CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, |
|
| 212 |
+// and attempts to configure it with an address which doesn't conflict with any other interface on the host. |
|
| 213 |
+// If it can't find an address which doesn't conflict, it will return an error. |
|
| 214 |
+func createBridge(bridgeIP string) error {
|
|
| 215 |
+ nameservers := []string{}
|
|
| 216 |
+ resolvConf, _ := utils.GetResolvConf() |
|
| 217 |
+ // we don't check for an error here, because we don't really care |
|
| 218 |
+ // if we can't read /etc/resolv.conf. So instead we skip the append |
|
| 219 |
+ // if resolvConf is nil. It either doesn't exist, or we can't read it |
|
| 220 |
+ // for some reason. |
|
| 221 |
+ if resolvConf != nil {
|
|
| 222 |
+ nameservers = append(nameservers, utils.GetNameserversAsCIDR(resolvConf)...) |
|
| 223 |
+ } |
|
| 224 |
+ |
|
| 225 |
+ var ifaceAddr string |
|
| 226 |
+ if len(bridgeIP) != 0 {
|
|
| 227 |
+ _, _, err := net.ParseCIDR(bridgeIP) |
|
| 228 |
+ if err != nil {
|
|
| 229 |
+ return err |
|
| 230 |
+ } |
|
| 231 |
+ ifaceAddr = bridgeIP |
|
| 232 |
+ } else {
|
|
| 233 |
+ for _, addr := range addrs {
|
|
| 234 |
+ _, dockerNetwork, err := net.ParseCIDR(addr) |
|
| 235 |
+ if err != nil {
|
|
| 236 |
+ return err |
|
| 237 |
+ } |
|
| 238 |
+ if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil {
|
|
| 239 |
+ if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil {
|
|
| 240 |
+ ifaceAddr = addr |
|
| 241 |
+ break |
|
| 242 |
+ } else {
|
|
| 243 |
+ utils.Debugf("%s %s", addr, err)
|
|
| 244 |
+ } |
|
| 245 |
+ } |
|
| 246 |
+ } |
|
| 247 |
+ } |
|
| 248 |
+ |
|
| 249 |
+ if ifaceAddr == "" {
|
|
| 250 |
+ return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", bridgeIface, bridgeIface)
|
|
| 251 |
+ } |
|
| 252 |
+ utils.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr)
|
|
| 253 |
+ |
|
| 254 |
+ if err := createBridgeIface(bridgeIface); err != nil {
|
|
| 255 |
+ return err |
|
| 256 |
+ } |
|
| 257 |
+ |
|
| 258 |
+ iface, err := net.InterfaceByName(bridgeIface) |
|
| 259 |
+ if err != nil {
|
|
| 260 |
+ return err |
|
| 261 |
+ } |
|
| 262 |
+ |
|
| 263 |
+ ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) |
|
| 264 |
+ if err != nil {
|
|
| 265 |
+ return err |
|
| 266 |
+ } |
|
| 267 |
+ |
|
| 268 |
+ if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {
|
|
| 269 |
+ return fmt.Errorf("Unable to add private network: %s", err)
|
|
| 270 |
+ } |
|
| 271 |
+ if err := netlink.NetworkLinkUp(iface); err != nil {
|
|
| 272 |
+ return fmt.Errorf("Unable to start network bridge: %s", err)
|
|
| 273 |
+ } |
|
| 274 |
+ return nil |
|
| 275 |
+} |
|
| 276 |
+ |
|
| 277 |
+// Create the actual bridge device. This is more backward-compatible than |
|
| 278 |
+// netlink.NetworkLinkAdd and works on RHEL 6. |
|
| 279 |
+func createBridgeIface(name string) error {
|
|
| 280 |
+ s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_IP) |
|
| 281 |
+ if err != nil {
|
|
| 282 |
+ utils.Debugf("Bridge socket creation failed IPv6 probably not enabled: %v", err)
|
|
| 283 |
+ s, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_IP) |
|
| 284 |
+ if err != nil {
|
|
| 285 |
+ return fmt.Errorf("Error creating bridge creation socket: %s", err)
|
|
| 286 |
+ } |
|
| 287 |
+ } |
|
| 288 |
+ defer syscall.Close(s) |
|
| 289 |
+ |
|
| 290 |
+ nameBytePtr, err := syscall.BytePtrFromString(name) |
|
| 291 |
+ if err != nil {
|
|
| 292 |
+ return fmt.Errorf("Error converting bridge name %s to byte array: %s", name, err)
|
|
| 293 |
+ } |
|
| 294 |
+ |
|
| 295 |
+ if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), siocBRADDBR, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 {
|
|
| 296 |
+ return fmt.Errorf("Error creating bridge: %s", err)
|
|
| 297 |
+ } |
|
| 298 |
+ return nil |
|
| 299 |
+} |
|
| 300 |
+ |
|
| 301 |
+// Allocate a network interface |
|
| 302 |
+func Allocate(job *engine.Job) engine.Status {
|
|
| 303 |
+ var ( |
|
| 304 |
+ ip *net.IP |
|
| 305 |
+ err error |
|
| 306 |
+ id = job.Args[0] |
|
| 307 |
+ requestedIP = net.ParseIP(job.Getenv("RequestedIP"))
|
|
| 308 |
+ ) |
|
| 309 |
+ |
|
| 310 |
+ if requestedIP != nil {
|
|
| 311 |
+ ip, err = ipallocator.RequestIP(bridgeNetwork, &requestedIP) |
|
| 312 |
+ } else {
|
|
| 313 |
+ ip, err = ipallocator.RequestIP(bridgeNetwork, nil) |
|
| 314 |
+ } |
|
| 315 |
+ if err != nil {
|
|
| 316 |
+ job.Error(err) |
|
| 317 |
+ return engine.StatusErr |
|
| 318 |
+ } |
|
| 319 |
+ |
|
| 320 |
+ out := engine.Env{}
|
|
| 321 |
+ out.Set("IP", ip.String())
|
|
| 322 |
+ out.Set("Mask", bridgeNetwork.Mask.String())
|
|
| 323 |
+ out.Set("Gateway", bridgeNetwork.IP.String())
|
|
| 324 |
+ out.Set("Bridge", bridgeIface)
|
|
| 325 |
+ |
|
| 326 |
+ size, _ := bridgeNetwork.Mask.Size() |
|
| 327 |
+ out.SetInt("IPPrefixLen", size)
|
|
| 328 |
+ |
|
| 329 |
+ currentInterfaces[id] = &networkInterface{
|
|
| 330 |
+ IP: *ip, |
|
| 331 |
+ } |
|
| 332 |
+ |
|
| 333 |
+ out.WriteTo(job.Stdout) |
|
| 334 |
+ |
|
| 335 |
+ return engine.StatusOK |
|
| 336 |
+} |
|
| 337 |
+ |
|
| 338 |
+// release an interface for a select ip |
|
| 339 |
+func Release(job *engine.Job) engine.Status {
|
|
| 340 |
+ var ( |
|
| 341 |
+ id = job.Args[0] |
|
| 342 |
+ containerInterface = currentInterfaces[id] |
|
| 343 |
+ ip net.IP |
|
| 344 |
+ port int |
|
| 345 |
+ proto string |
|
| 346 |
+ ) |
|
| 347 |
+ |
|
| 348 |
+ if containerInterface == nil {
|
|
| 349 |
+ return job.Errorf("No network information to release for %s", id)
|
|
| 350 |
+ } |
|
| 351 |
+ |
|
| 352 |
+ for _, nat := range containerInterface.PortMappings {
|
|
| 353 |
+ if err := portmapper.Unmap(nat); err != nil {
|
|
| 354 |
+ log.Printf("Unable to unmap port %s: %s", nat, err)
|
|
| 355 |
+ } |
|
| 356 |
+ |
|
| 357 |
+ // this is host mappings |
|
| 358 |
+ switch a := nat.(type) {
|
|
| 359 |
+ case *net.TCPAddr: |
|
| 360 |
+ proto = "tcp" |
|
| 361 |
+ ip = a.IP |
|
| 362 |
+ port = a.Port |
|
| 363 |
+ case *net.UDPAddr: |
|
| 364 |
+ proto = "udp" |
|
| 365 |
+ ip = a.IP |
|
| 366 |
+ port = a.Port |
|
| 367 |
+ } |
|
| 368 |
+ |
|
| 369 |
+ if err := portallocator.ReleasePort(ip, proto, port); err != nil {
|
|
| 370 |
+ log.Printf("Unable to release port %s", nat)
|
|
| 371 |
+ } |
|
| 372 |
+ } |
|
| 373 |
+ |
|
| 374 |
+ if err := ipallocator.ReleaseIP(bridgeNetwork, &containerInterface.IP); err != nil {
|
|
| 375 |
+ log.Printf("Unable to release ip %s\n", err)
|
|
| 376 |
+ } |
|
| 377 |
+ return engine.StatusOK |
|
| 378 |
+} |
|
| 379 |
+ |
|
| 380 |
+// Allocate an external port and map it to the interface |
|
| 381 |
+func AllocatePort(job *engine.Job) engine.Status {
|
|
| 382 |
+ var ( |
|
| 383 |
+ err error |
|
| 384 |
+ |
|
| 385 |
+ ip = defaultBindingIP |
|
| 386 |
+ id = job.Args[0] |
|
| 387 |
+ hostIP = job.Getenv("HostIP")
|
|
| 388 |
+ hostPort = job.GetenvInt("HostPort")
|
|
| 389 |
+ containerPort = job.GetenvInt("ContainerPort")
|
|
| 390 |
+ proto = job.Getenv("Proto")
|
|
| 391 |
+ network = currentInterfaces[id] |
|
| 392 |
+ ) |
|
| 393 |
+ |
|
| 394 |
+ if hostIP != "" {
|
|
| 395 |
+ ip = net.ParseIP(hostIP) |
|
| 396 |
+ } |
|
| 397 |
+ |
|
| 398 |
+ // host ip, proto, and host port |
|
| 399 |
+ hostPort, err = portallocator.RequestPort(ip, proto, hostPort) |
|
| 400 |
+ if err != nil {
|
|
| 401 |
+ job.Error(err) |
|
| 402 |
+ return engine.StatusErr |
|
| 403 |
+ } |
|
| 404 |
+ |
|
| 405 |
+ var ( |
|
| 406 |
+ container net.Addr |
|
| 407 |
+ host net.Addr |
|
| 408 |
+ ) |
|
| 409 |
+ |
|
| 410 |
+ if proto == "tcp" {
|
|
| 411 |
+ host = &net.TCPAddr{IP: ip, Port: hostPort}
|
|
| 412 |
+ container = &net.TCPAddr{IP: network.IP, Port: containerPort}
|
|
| 413 |
+ } else {
|
|
| 414 |
+ host = &net.UDPAddr{IP: ip, Port: hostPort}
|
|
| 415 |
+ container = &net.UDPAddr{IP: network.IP, Port: containerPort}
|
|
| 416 |
+ } |
|
| 417 |
+ |
|
| 418 |
+ if err := portmapper.Map(container, ip, hostPort); err != nil {
|
|
| 419 |
+ portallocator.ReleasePort(ip, proto, hostPort) |
|
| 420 |
+ |
|
| 421 |
+ job.Error(err) |
|
| 422 |
+ return engine.StatusErr |
|
| 423 |
+ } |
|
| 424 |
+ network.PortMappings = append(network.PortMappings, host) |
|
| 425 |
+ |
|
| 426 |
+ out := engine.Env{}
|
|
| 427 |
+ out.Set("HostIP", ip.String())
|
|
| 428 |
+ out.SetInt("HostPort", hostPort)
|
|
| 429 |
+ |
|
| 430 |
+ if _, err := out.WriteTo(job.Stdout); err != nil {
|
|
| 431 |
+ job.Error(err) |
|
| 432 |
+ return engine.StatusErr |
|
| 433 |
+ } |
|
| 434 |
+ return engine.StatusOK |
|
| 435 |
+} |
|
| 436 |
+ |
|
| 437 |
+func LinkContainers(job *engine.Job) engine.Status {
|
|
| 438 |
+ var ( |
|
| 439 |
+ action = job.Args[0] |
|
| 440 |
+ childIP = job.Getenv("ChildIP")
|
|
| 441 |
+ parentIP = job.Getenv("ParentIP")
|
|
| 442 |
+ ignoreErrors = job.GetenvBool("IgnoreErrors")
|
|
| 443 |
+ ports = job.GetenvList("Ports")
|
|
| 444 |
+ ) |
|
| 445 |
+ split := func(p string) (string, string) {
|
|
| 446 |
+ parts := strings.Split(p, "/") |
|
| 447 |
+ return parts[0], parts[1] |
|
| 448 |
+ } |
|
| 449 |
+ |
|
| 450 |
+ for _, p := range ports {
|
|
| 451 |
+ port, proto := split(p) |
|
| 452 |
+ if output, err := iptables.Raw(action, "FORWARD", |
|
| 453 |
+ "-i", bridgeIface, "-o", bridgeIface, |
|
| 454 |
+ "-p", proto, |
|
| 455 |
+ "-s", parentIP, |
|
| 456 |
+ "--dport", port, |
|
| 457 |
+ "-d", childIP, |
|
| 458 |
+ "-j", "ACCEPT"); !ignoreErrors && err != nil {
|
|
| 459 |
+ job.Error(err) |
|
| 460 |
+ return engine.StatusErr |
|
| 461 |
+ } else if len(output) != 0 {
|
|
| 462 |
+ job.Errorf("Error toggle iptables forward: %s", output)
|
|
| 463 |
+ return engine.StatusErr |
|
| 464 |
+ } |
|
| 465 |
+ |
|
| 466 |
+ if output, err := iptables.Raw(action, "FORWARD", |
|
| 467 |
+ "-i", bridgeIface, "-o", bridgeIface, |
|
| 468 |
+ "-p", proto, |
|
| 469 |
+ "-s", childIP, |
|
| 470 |
+ "--sport", port, |
|
| 471 |
+ "-d", parentIP, |
|
| 472 |
+ "-j", "ACCEPT"); !ignoreErrors && err != nil {
|
|
| 473 |
+ job.Error(err) |
|
| 474 |
+ return engine.StatusErr |
|
| 475 |
+ } else if len(output) != 0 {
|
|
| 476 |
+ job.Errorf("Error toggle iptables forward: %s", output)
|
|
| 477 |
+ return engine.StatusErr |
|
| 478 |
+ } |
|
| 479 |
+ } |
|
| 480 |
+ return engine.StatusOK |
|
| 481 |
+} |
| 0 | 482 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,10 @@ |
| 0 |
+package networkdriver |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+var ( |
|
| 7 |
+ ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver")
|
|
| 8 |
+ ErrNetworkOverlaps = errors.New("requested network overlaps with existing network")
|
|
| 9 |
+) |
| 0 | 10 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,190 @@ |
| 0 |
+package networkdriver |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/pkg/netlink" |
|
| 4 |
+ "net" |
|
| 5 |
+ "testing" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func TestNonOverlapingNameservers(t *testing.T) {
|
|
| 9 |
+ network := &net.IPNet{
|
|
| 10 |
+ IP: []byte{192, 168, 0, 1},
|
|
| 11 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 12 |
+ } |
|
| 13 |
+ nameservers := []string{
|
|
| 14 |
+ "127.0.0.1/32", |
|
| 15 |
+ } |
|
| 16 |
+ |
|
| 17 |
+ if err := CheckNameserverOverlaps(nameservers, network); err != nil {
|
|
| 18 |
+ t.Fatal(err) |
|
| 19 |
+ } |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+func TestOverlapingNameservers(t *testing.T) {
|
|
| 23 |
+ network := &net.IPNet{
|
|
| 24 |
+ IP: []byte{192, 168, 0, 1},
|
|
| 25 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 26 |
+ } |
|
| 27 |
+ nameservers := []string{
|
|
| 28 |
+ "192.168.0.1/32", |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ if err := CheckNameserverOverlaps(nameservers, network); err == nil {
|
|
| 32 |
+ t.Fatalf("Expected error %s got %s", ErrNetworkOverlapsWithNameservers, err)
|
|
| 33 |
+ } |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func TestCheckRouteOverlaps(t *testing.T) {
|
|
| 37 |
+ orig := networkGetRoutesFct |
|
| 38 |
+ defer func() {
|
|
| 39 |
+ networkGetRoutesFct = orig |
|
| 40 |
+ }() |
|
| 41 |
+ networkGetRoutesFct = func() ([]netlink.Route, error) {
|
|
| 42 |
+ routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"}
|
|
| 43 |
+ |
|
| 44 |
+ routes := []netlink.Route{}
|
|
| 45 |
+ for _, addr := range routesData {
|
|
| 46 |
+ _, netX, _ := net.ParseCIDR(addr) |
|
| 47 |
+ routes = append(routes, netlink.Route{IPNet: netX})
|
|
| 48 |
+ } |
|
| 49 |
+ return routes, nil |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ _, netX, _ := net.ParseCIDR("172.16.0.1/24")
|
|
| 53 |
+ if err := CheckRouteOverlaps(netX); err != nil {
|
|
| 54 |
+ t.Fatal(err) |
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 57 |
+ _, netX, _ = net.ParseCIDR("10.0.2.0/24")
|
|
| 58 |
+ if err := CheckRouteOverlaps(netX); err == nil {
|
|
| 59 |
+ t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't")
|
|
| 60 |
+ } |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func TestCheckNameserverOverlaps(t *testing.T) {
|
|
| 64 |
+ nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"}
|
|
| 65 |
+ |
|
| 66 |
+ _, netX, _ := net.ParseCIDR("10.0.2.3/32")
|
|
| 67 |
+ |
|
| 68 |
+ if err := CheckNameserverOverlaps(nameservers, netX); err == nil {
|
|
| 69 |
+ t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX)
|
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ _, netX, _ = net.ParseCIDR("192.168.102.2/32")
|
|
| 73 |
+ |
|
| 74 |
+ if err := CheckNameserverOverlaps(nameservers, netX); err != nil {
|
|
| 75 |
+ t.Fatalf("%s should not overlap %v but it does", netX, nameservers)
|
|
| 76 |
+ } |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+func AssertOverlap(CIDRx string, CIDRy string, t *testing.T) {
|
|
| 80 |
+ _, netX, _ := net.ParseCIDR(CIDRx) |
|
| 81 |
+ _, netY, _ := net.ParseCIDR(CIDRy) |
|
| 82 |
+ if !NetworkOverlaps(netX, netY) {
|
|
| 83 |
+ t.Errorf("%v and %v should overlap", netX, netY)
|
|
| 84 |
+ } |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+func AssertNoOverlap(CIDRx string, CIDRy string, t *testing.T) {
|
|
| 88 |
+ _, netX, _ := net.ParseCIDR(CIDRx) |
|
| 89 |
+ _, netY, _ := net.ParseCIDR(CIDRy) |
|
| 90 |
+ if NetworkOverlaps(netX, netY) {
|
|
| 91 |
+ t.Errorf("%v and %v should not overlap", netX, netY)
|
|
| 92 |
+ } |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func TestNetworkOverlaps(t *testing.T) {
|
|
| 96 |
+ //netY starts at same IP and ends within netX |
|
| 97 |
+ AssertOverlap("172.16.0.1/24", "172.16.0.1/25", t)
|
|
| 98 |
+ //netY starts within netX and ends at same IP |
|
| 99 |
+ AssertOverlap("172.16.0.1/24", "172.16.0.128/25", t)
|
|
| 100 |
+ //netY starts and ends within netX |
|
| 101 |
+ AssertOverlap("172.16.0.1/24", "172.16.0.64/25", t)
|
|
| 102 |
+ //netY starts at same IP and ends outside of netX |
|
| 103 |
+ AssertOverlap("172.16.0.1/24", "172.16.0.1/23", t)
|
|
| 104 |
+ //netY starts before and ends at same IP of netX |
|
| 105 |
+ AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t)
|
|
| 106 |
+ //netY starts before and ends outside of netX |
|
| 107 |
+ AssertOverlap("172.16.1.1/24", "172.16.0.1/22", t)
|
|
| 108 |
+ //netY starts and ends before netX |
|
| 109 |
+ AssertNoOverlap("172.16.1.1/25", "172.16.0.1/24", t)
|
|
| 110 |
+ //netX starts and ends before netY |
|
| 111 |
+ AssertNoOverlap("172.16.1.1/25", "172.16.2.1/24", t)
|
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+func TestNetworkRange(t *testing.T) {
|
|
| 115 |
+ // Simple class C test |
|
| 116 |
+ _, network, _ := net.ParseCIDR("192.168.0.1/24")
|
|
| 117 |
+ first, last := NetworkRange(network) |
|
| 118 |
+ if !first.Equal(net.ParseIP("192.168.0.0")) {
|
|
| 119 |
+ t.Error(first.String()) |
|
| 120 |
+ } |
|
| 121 |
+ if !last.Equal(net.ParseIP("192.168.0.255")) {
|
|
| 122 |
+ t.Error(last.String()) |
|
| 123 |
+ } |
|
| 124 |
+ if size := NetworkSize(network.Mask); size != 256 {
|
|
| 125 |
+ t.Error(size) |
|
| 126 |
+ } |
|
| 127 |
+ |
|
| 128 |
+ // Class A test |
|
| 129 |
+ _, network, _ = net.ParseCIDR("10.0.0.1/8")
|
|
| 130 |
+ first, last = NetworkRange(network) |
|
| 131 |
+ if !first.Equal(net.ParseIP("10.0.0.0")) {
|
|
| 132 |
+ t.Error(first.String()) |
|
| 133 |
+ } |
|
| 134 |
+ if !last.Equal(net.ParseIP("10.255.255.255")) {
|
|
| 135 |
+ t.Error(last.String()) |
|
| 136 |
+ } |
|
| 137 |
+ if size := NetworkSize(network.Mask); size != 16777216 {
|
|
| 138 |
+ t.Error(size) |
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ // Class A, random IP address |
|
| 142 |
+ _, network, _ = net.ParseCIDR("10.1.2.3/8")
|
|
| 143 |
+ first, last = NetworkRange(network) |
|
| 144 |
+ if !first.Equal(net.ParseIP("10.0.0.0")) {
|
|
| 145 |
+ t.Error(first.String()) |
|
| 146 |
+ } |
|
| 147 |
+ if !last.Equal(net.ParseIP("10.255.255.255")) {
|
|
| 148 |
+ t.Error(last.String()) |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ // 32bit mask |
|
| 152 |
+ _, network, _ = net.ParseCIDR("10.1.2.3/32")
|
|
| 153 |
+ first, last = NetworkRange(network) |
|
| 154 |
+ if !first.Equal(net.ParseIP("10.1.2.3")) {
|
|
| 155 |
+ t.Error(first.String()) |
|
| 156 |
+ } |
|
| 157 |
+ if !last.Equal(net.ParseIP("10.1.2.3")) {
|
|
| 158 |
+ t.Error(last.String()) |
|
| 159 |
+ } |
|
| 160 |
+ if size := NetworkSize(network.Mask); size != 1 {
|
|
| 161 |
+ t.Error(size) |
|
| 162 |
+ } |
|
| 163 |
+ |
|
| 164 |
+ // 31bit mask |
|
| 165 |
+ _, network, _ = net.ParseCIDR("10.1.2.3/31")
|
|
| 166 |
+ first, last = NetworkRange(network) |
|
| 167 |
+ if !first.Equal(net.ParseIP("10.1.2.2")) {
|
|
| 168 |
+ t.Error(first.String()) |
|
| 169 |
+ } |
|
| 170 |
+ if !last.Equal(net.ParseIP("10.1.2.3")) {
|
|
| 171 |
+ t.Error(last.String()) |
|
| 172 |
+ } |
|
| 173 |
+ if size := NetworkSize(network.Mask); size != 2 {
|
|
| 174 |
+ t.Error(size) |
|
| 175 |
+ } |
|
| 176 |
+ |
|
| 177 |
+ // 26bit mask |
|
| 178 |
+ _, network, _ = net.ParseCIDR("10.1.2.3/26")
|
|
| 179 |
+ first, last = NetworkRange(network) |
|
| 180 |
+ if !first.Equal(net.ParseIP("10.1.2.0")) {
|
|
| 181 |
+ t.Error(first.String()) |
|
| 182 |
+ } |
|
| 183 |
+ if !last.Equal(net.ParseIP("10.1.2.63")) {
|
|
| 184 |
+ t.Error(last.String()) |
|
| 185 |
+ } |
|
| 186 |
+ if size := NetworkSize(network.Mask); size != 64 {
|
|
| 187 |
+ t.Error(size) |
|
| 188 |
+ } |
|
| 189 |
+} |
| 0 | 190 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,165 @@ |
| 0 |
+package portallocator |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "github.com/dotcloud/docker/pkg/collections" |
|
| 5 |
+ "net" |
|
| 6 |
+ "sync" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+const ( |
|
| 10 |
+ BeginPortRange = 49153 |
|
| 11 |
+ EndPortRange = 65535 |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+type ( |
|
| 15 |
+ portMappings map[string]*collections.OrderedIntSet |
|
| 16 |
+ ipMapping map[string]portMappings |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+var ( |
|
| 20 |
+ ErrPortAlreadyAllocated = errors.New("port has already been allocated")
|
|
| 21 |
+ ErrPortExceedsRange = errors.New("port exceeds upper range")
|
|
| 22 |
+ ErrUnknownProtocol = errors.New("unknown protocol")
|
|
| 23 |
+) |
|
| 24 |
+ |
|
| 25 |
+var ( |
|
| 26 |
+ currentDynamicPort = map[string]int{
|
|
| 27 |
+ "tcp": BeginPortRange - 1, |
|
| 28 |
+ "udp": BeginPortRange - 1, |
|
| 29 |
+ } |
|
| 30 |
+ defaultIP = net.ParseIP("0.0.0.0")
|
|
| 31 |
+ defaultAllocatedPorts = portMappings{}
|
|
| 32 |
+ otherAllocatedPorts = ipMapping{}
|
|
| 33 |
+ lock = sync.Mutex{}
|
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+func init() {
|
|
| 37 |
+ defaultAllocatedPorts["tcp"] = collections.NewOrderedIntSet() |
|
| 38 |
+ defaultAllocatedPorts["udp"] = collections.NewOrderedIntSet() |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// RequestPort returns an available port if the port is 0 |
|
| 42 |
+// If the provided port is not 0 then it will be checked if |
|
| 43 |
+// it is available for allocation |
|
| 44 |
+func RequestPort(ip net.IP, proto string, port int) (int, error) {
|
|
| 45 |
+ lock.Lock() |
|
| 46 |
+ defer lock.Unlock() |
|
| 47 |
+ |
|
| 48 |
+ if err := validateProtocol(proto); err != nil {
|
|
| 49 |
+ return 0, err |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ // If the user requested a specific port to be allocated |
|
| 53 |
+ if port > 0 {
|
|
| 54 |
+ if err := registerSetPort(ip, proto, port); err != nil {
|
|
| 55 |
+ return 0, err |
|
| 56 |
+ } |
|
| 57 |
+ return port, nil |
|
| 58 |
+ } |
|
| 59 |
+ return registerDynamicPort(ip, proto) |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+// ReleasePort will return the provided port back into the |
|
| 63 |
+// pool for reuse |
|
| 64 |
+func ReleasePort(ip net.IP, proto string, port int) error {
|
|
| 65 |
+ lock.Lock() |
|
| 66 |
+ defer lock.Unlock() |
|
| 67 |
+ |
|
| 68 |
+ if err := validateProtocol(proto); err != nil {
|
|
| 69 |
+ return err |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ allocated := defaultAllocatedPorts[proto] |
|
| 73 |
+ allocated.Remove(port) |
|
| 74 |
+ |
|
| 75 |
+ if !equalsDefault(ip) {
|
|
| 76 |
+ registerIP(ip) |
|
| 77 |
+ |
|
| 78 |
+ // Remove the port for the specific ip address |
|
| 79 |
+ allocated = otherAllocatedPorts[ip.String()][proto] |
|
| 80 |
+ allocated.Remove(port) |
|
| 81 |
+ } |
|
| 82 |
+ return nil |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func ReleaseAll() error {
|
|
| 86 |
+ lock.Lock() |
|
| 87 |
+ defer lock.Unlock() |
|
| 88 |
+ |
|
| 89 |
+ currentDynamicPort["tcp"] = BeginPortRange - 1 |
|
| 90 |
+ currentDynamicPort["udp"] = BeginPortRange - 1 |
|
| 91 |
+ |
|
| 92 |
+ defaultAllocatedPorts = portMappings{}
|
|
| 93 |
+ defaultAllocatedPorts["tcp"] = collections.NewOrderedIntSet() |
|
| 94 |
+ defaultAllocatedPorts["udp"] = collections.NewOrderedIntSet() |
|
| 95 |
+ |
|
| 96 |
+ otherAllocatedPorts = ipMapping{}
|
|
| 97 |
+ |
|
| 98 |
+ return nil |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+func registerDynamicPort(ip net.IP, proto string) (int, error) {
|
|
| 102 |
+ allocated := defaultAllocatedPorts[proto] |
|
| 103 |
+ |
|
| 104 |
+ port := nextPort(proto) |
|
| 105 |
+ if port > EndPortRange {
|
|
| 106 |
+ return 0, ErrPortExceedsRange |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ if !equalsDefault(ip) {
|
|
| 110 |
+ registerIP(ip) |
|
| 111 |
+ |
|
| 112 |
+ ipAllocated := otherAllocatedPorts[ip.String()][proto] |
|
| 113 |
+ ipAllocated.Push(port) |
|
| 114 |
+ } else {
|
|
| 115 |
+ allocated.Push(port) |
|
| 116 |
+ } |
|
| 117 |
+ return port, nil |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+func registerSetPort(ip net.IP, proto string, port int) error {
|
|
| 121 |
+ allocated := defaultAllocatedPorts[proto] |
|
| 122 |
+ if allocated.Exists(port) {
|
|
| 123 |
+ return ErrPortAlreadyAllocated |
|
| 124 |
+ } |
|
| 125 |
+ |
|
| 126 |
+ if !equalsDefault(ip) {
|
|
| 127 |
+ registerIP(ip) |
|
| 128 |
+ |
|
| 129 |
+ ipAllocated := otherAllocatedPorts[ip.String()][proto] |
|
| 130 |
+ if ipAllocated.Exists(port) {
|
|
| 131 |
+ return ErrPortAlreadyAllocated |
|
| 132 |
+ } |
|
| 133 |
+ ipAllocated.Push(port) |
|
| 134 |
+ } else {
|
|
| 135 |
+ allocated.Push(port) |
|
| 136 |
+ } |
|
| 137 |
+ return nil |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 140 |
+func equalsDefault(ip net.IP) bool {
|
|
| 141 |
+ return ip == nil || ip.Equal(defaultIP) |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+func nextPort(proto string) int {
|
|
| 145 |
+ c := currentDynamicPort[proto] + 1 |
|
| 146 |
+ currentDynamicPort[proto] = c |
|
| 147 |
+ return c |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+func registerIP(ip net.IP) {
|
|
| 151 |
+ if _, exists := otherAllocatedPorts[ip.String()]; !exists {
|
|
| 152 |
+ otherAllocatedPorts[ip.String()] = portMappings{
|
|
| 153 |
+ "tcp": collections.NewOrderedIntSet(), |
|
| 154 |
+ "udp": collections.NewOrderedIntSet(), |
|
| 155 |
+ } |
|
| 156 |
+ } |
|
| 157 |
+} |
|
| 158 |
+ |
|
| 159 |
+func validateProtocol(proto string) error {
|
|
| 160 |
+ if _, exists := defaultAllocatedPorts[proto]; !exists {
|
|
| 161 |
+ return ErrUnknownProtocol |
|
| 162 |
+ } |
|
| 163 |
+ return nil |
|
| 164 |
+} |
| 0 | 165 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,184 @@ |
| 0 |
+package portallocator |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net" |
|
| 4 |
+ "testing" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+func reset() {
|
|
| 8 |
+ ReleaseAll() |
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+func TestRequestNewPort(t *testing.T) {
|
|
| 12 |
+ defer reset() |
|
| 13 |
+ |
|
| 14 |
+ port, err := RequestPort(defaultIP, "tcp", 0) |
|
| 15 |
+ if err != nil {
|
|
| 16 |
+ t.Fatal(err) |
|
| 17 |
+ } |
|
| 18 |
+ |
|
| 19 |
+ if expected := BeginPortRange; port != expected {
|
|
| 20 |
+ t.Fatalf("Expected port %d got %d", expected, port)
|
|
| 21 |
+ } |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func TestRequestSpecificPort(t *testing.T) {
|
|
| 25 |
+ defer reset() |
|
| 26 |
+ |
|
| 27 |
+ port, err := RequestPort(defaultIP, "tcp", 5000) |
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ t.Fatal(err) |
|
| 30 |
+ } |
|
| 31 |
+ if port != 5000 {
|
|
| 32 |
+ t.Fatalf("Expected port 5000 got %d", port)
|
|
| 33 |
+ } |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func TestReleasePort(t *testing.T) {
|
|
| 37 |
+ defer reset() |
|
| 38 |
+ |
|
| 39 |
+ port, err := RequestPort(defaultIP, "tcp", 5000) |
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ t.Fatal(err) |
|
| 42 |
+ } |
|
| 43 |
+ if port != 5000 {
|
|
| 44 |
+ t.Fatalf("Expected port 5000 got %d", port)
|
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
|
|
| 48 |
+ t.Fatal(err) |
|
| 49 |
+ } |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func TestReuseReleasedPort(t *testing.T) {
|
|
| 53 |
+ defer reset() |
|
| 54 |
+ |
|
| 55 |
+ port, err := RequestPort(defaultIP, "tcp", 5000) |
|
| 56 |
+ if err != nil {
|
|
| 57 |
+ t.Fatal(err) |
|
| 58 |
+ } |
|
| 59 |
+ if port != 5000 {
|
|
| 60 |
+ t.Fatalf("Expected port 5000 got %d", port)
|
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ if err := ReleasePort(defaultIP, "tcp", 5000); err != nil {
|
|
| 64 |
+ t.Fatal(err) |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ port, err = RequestPort(defaultIP, "tcp", 5000) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ t.Fatal(err) |
|
| 70 |
+ } |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+func TestReleaseUnreadledPort(t *testing.T) {
|
|
| 74 |
+ defer reset() |
|
| 75 |
+ |
|
| 76 |
+ port, err := RequestPort(defaultIP, "tcp", 5000) |
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ t.Fatal(err) |
|
| 79 |
+ } |
|
| 80 |
+ if port != 5000 {
|
|
| 81 |
+ t.Fatalf("Expected port 5000 got %d", port)
|
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ port, err = RequestPort(defaultIP, "tcp", 5000) |
|
| 85 |
+ if err != ErrPortAlreadyAllocated {
|
|
| 86 |
+ t.Fatalf("Expected error %s got %s", ErrPortAlreadyAllocated, err)
|
|
| 87 |
+ } |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func TestUnknowProtocol(t *testing.T) {
|
|
| 91 |
+ defer reset() |
|
| 92 |
+ |
|
| 93 |
+ if _, err := RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol {
|
|
| 94 |
+ t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err)
|
|
| 95 |
+ } |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+func TestAllocateAllPorts(t *testing.T) {
|
|
| 99 |
+ defer reset() |
|
| 100 |
+ |
|
| 101 |
+ for i := 0; i <= EndPortRange-BeginPortRange; i++ {
|
|
| 102 |
+ port, err := RequestPort(defaultIP, "tcp", 0) |
|
| 103 |
+ if err != nil {
|
|
| 104 |
+ t.Fatal(err) |
|
| 105 |
+ } |
|
| 106 |
+ |
|
| 107 |
+ if expected := BeginPortRange + i; port != expected {
|
|
| 108 |
+ t.Fatalf("Expected port %d got %d", expected, port)
|
|
| 109 |
+ } |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ if _, err := RequestPort(defaultIP, "tcp", 0); err != ErrPortExceedsRange {
|
|
| 113 |
+ t.Fatalf("Expected error %s got %s", ErrPortExceedsRange, err)
|
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+ _, err := RequestPort(defaultIP, "udp", 0) |
|
| 117 |
+ if err != nil {
|
|
| 118 |
+ t.Fatal(err) |
|
| 119 |
+ } |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func BenchmarkAllocatePorts(b *testing.B) {
|
|
| 123 |
+ defer reset() |
|
| 124 |
+ |
|
| 125 |
+ for i := 0; i < b.N; i++ {
|
|
| 126 |
+ for i := 0; i <= EndPortRange-BeginPortRange; i++ {
|
|
| 127 |
+ port, err := RequestPort(defaultIP, "tcp", 0) |
|
| 128 |
+ if err != nil {
|
|
| 129 |
+ b.Fatal(err) |
|
| 130 |
+ } |
|
| 131 |
+ |
|
| 132 |
+ if expected := BeginPortRange + i; port != expected {
|
|
| 133 |
+ b.Fatalf("Expected port %d got %d", expected, port)
|
|
| 134 |
+ } |
|
| 135 |
+ } |
|
| 136 |
+ reset() |
|
| 137 |
+ } |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 140 |
+func TestPortAllocation(t *testing.T) {
|
|
| 141 |
+ defer reset() |
|
| 142 |
+ |
|
| 143 |
+ ip := net.ParseIP("192.168.0.1")
|
|
| 144 |
+ ip2 := net.ParseIP("192.168.0.2")
|
|
| 145 |
+ if port, err := RequestPort(ip, "tcp", 80); err != nil {
|
|
| 146 |
+ t.Fatal(err) |
|
| 147 |
+ } else if port != 80 {
|
|
| 148 |
+ t.Fatalf("Acquire(80) should return 80, not %d", port)
|
|
| 149 |
+ } |
|
| 150 |
+ port, err := RequestPort(ip, "tcp", 0) |
|
| 151 |
+ if err != nil {
|
|
| 152 |
+ t.Fatal(err) |
|
| 153 |
+ } |
|
| 154 |
+ if port <= 0 {
|
|
| 155 |
+ t.Fatalf("Acquire(0) should return a non-zero port")
|
|
| 156 |
+ } |
|
| 157 |
+ |
|
| 158 |
+ if _, err := RequestPort(ip, "tcp", port); err == nil {
|
|
| 159 |
+ t.Fatalf("Acquiring a port already in use should return an error")
|
|
| 160 |
+ } |
|
| 161 |
+ |
|
| 162 |
+ if newPort, err := RequestPort(ip, "tcp", 0); err != nil {
|
|
| 163 |
+ t.Fatal(err) |
|
| 164 |
+ } else if newPort == port {
|
|
| 165 |
+ t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
|
|
| 166 |
+ } |
|
| 167 |
+ |
|
| 168 |
+ if _, err := RequestPort(ip, "tcp", 80); err == nil {
|
|
| 169 |
+ t.Fatalf("Acquiring a port already in use should return an error")
|
|
| 170 |
+ } |
|
| 171 |
+ if _, err := RequestPort(ip2, "tcp", 80); err != nil {
|
|
| 172 |
+ t.Fatalf("It should be possible to allocate the same port on a different interface")
|
|
| 173 |
+ } |
|
| 174 |
+ if _, err := RequestPort(ip2, "tcp", 80); err == nil {
|
|
| 175 |
+ t.Fatalf("Acquiring a port already in use should return an error")
|
|
| 176 |
+ } |
|
| 177 |
+ if err := ReleasePort(ip, "tcp", 80); err != nil {
|
|
| 178 |
+ t.Fatal(err) |
|
| 179 |
+ } |
|
| 180 |
+ if _, err := RequestPort(ip, "tcp", 80); err != nil {
|
|
| 181 |
+ t.Fatal(err) |
|
| 182 |
+ } |
|
| 183 |
+} |
| 0 | 184 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,131 @@ |
| 0 |
+package portmapper |
|
| 1 |
+ |
|
| 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 |
+ |
|
| 11 |
+type mapping struct {
|
|
| 12 |
+ proto string |
|
| 13 |
+ userlandProxy proxy.Proxy |
|
| 14 |
+ host net.Addr |
|
| 15 |
+ container net.Addr |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+var ( |
|
| 19 |
+ chain *iptables.Chain |
|
| 20 |
+ lock sync.Mutex |
|
| 21 |
+ |
|
| 22 |
+ // udp:ip:port |
|
| 23 |
+ currentMappings = make(map[string]*mapping) |
|
| 24 |
+ newProxy = proxy.NewProxy |
|
| 25 |
+) |
|
| 26 |
+ |
|
| 27 |
+var ( |
|
| 28 |
+ ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
|
|
| 29 |
+ ErrPortMappedForIP = errors.New("port is already mapped to ip")
|
|
| 30 |
+ ErrPortNotMapped = errors.New("port is not mapped")
|
|
| 31 |
+) |
|
| 32 |
+ |
|
| 33 |
+func SetIptablesChain(c *iptables.Chain) {
|
|
| 34 |
+ chain = c |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func Map(container net.Addr, hostIP net.IP, hostPort int) error {
|
|
| 38 |
+ lock.Lock() |
|
| 39 |
+ defer lock.Unlock() |
|
| 40 |
+ |
|
| 41 |
+ var m *mapping |
|
| 42 |
+ switch container.(type) {
|
|
| 43 |
+ case *net.TCPAddr: |
|
| 44 |
+ m = &mapping{
|
|
| 45 |
+ proto: "tcp", |
|
| 46 |
+ host: &net.TCPAddr{IP: hostIP, Port: hostPort},
|
|
| 47 |
+ container: container, |
|
| 48 |
+ } |
|
| 49 |
+ case *net.UDPAddr: |
|
| 50 |
+ m = &mapping{
|
|
| 51 |
+ proto: "udp", |
|
| 52 |
+ host: &net.UDPAddr{IP: hostIP, Port: hostPort},
|
|
| 53 |
+ container: container, |
|
| 54 |
+ } |
|
| 55 |
+ default: |
|
| 56 |
+ return ErrUnknownBackendAddressType |
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ key := getKey(m.host) |
|
| 60 |
+ if _, exists := currentMappings[key]; exists {
|
|
| 61 |
+ return ErrPortMappedForIP |
|
| 62 |
+ } |
|
| 63 |
+ |
|
| 64 |
+ containerIP, containerPort := getIPAndPort(m.container) |
|
| 65 |
+ if err := forward(iptables.Add, m.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
|
|
| 66 |
+ return err |
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ p, err := newProxy(m.host, m.container) |
|
| 70 |
+ if err != nil {
|
|
| 71 |
+ // need to undo the iptables rules before we reutrn |
|
| 72 |
+ forward(iptables.Delete, m.proto, hostIP, hostPort, containerIP.String(), containerPort) |
|
| 73 |
+ return err |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ m.userlandProxy = p |
|
| 77 |
+ currentMappings[key] = m |
|
| 78 |
+ |
|
| 79 |
+ go p.Run() |
|
| 80 |
+ |
|
| 81 |
+ return nil |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+func Unmap(host net.Addr) error {
|
|
| 85 |
+ lock.Lock() |
|
| 86 |
+ defer lock.Unlock() |
|
| 87 |
+ |
|
| 88 |
+ key := getKey(host) |
|
| 89 |
+ data, exists := currentMappings[key] |
|
| 90 |
+ if !exists {
|
|
| 91 |
+ return ErrPortNotMapped |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ data.userlandProxy.Close() |
|
| 95 |
+ delete(currentMappings, key) |
|
| 96 |
+ |
|
| 97 |
+ containerIP, containerPort := getIPAndPort(data.container) |
|
| 98 |
+ hostIP, hostPort := getIPAndPort(data.host) |
|
| 99 |
+ if err := forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
|
|
| 100 |
+ return err |
|
| 101 |
+ } |
|
| 102 |
+ return nil |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+func getKey(a net.Addr) string {
|
|
| 106 |
+ switch t := a.(type) {
|
|
| 107 |
+ case *net.TCPAddr: |
|
| 108 |
+ return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
|
|
| 109 |
+ case *net.UDPAddr: |
|
| 110 |
+ return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
|
|
| 111 |
+ } |
|
| 112 |
+ return "" |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+func getIPAndPort(a net.Addr) (net.IP, int) {
|
|
| 116 |
+ switch t := a.(type) {
|
|
| 117 |
+ case *net.TCPAddr: |
|
| 118 |
+ return t.IP, t.Port |
|
| 119 |
+ case *net.UDPAddr: |
|
| 120 |
+ return t.IP, t.Port |
|
| 121 |
+ } |
|
| 122 |
+ return nil, 0 |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+func forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
|
|
| 126 |
+ if chain == nil {
|
|
| 127 |
+ return nil |
|
| 128 |
+ } |
|
| 129 |
+ return chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort) |
|
| 130 |
+} |
| 0 | 131 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,107 @@ |
| 0 |
+package portmapper |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/dotcloud/docker/pkg/iptables" |
|
| 4 |
+ "github.com/dotcloud/docker/pkg/proxy" |
|
| 5 |
+ "net" |
|
| 6 |
+ "testing" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func init() {
|
|
| 10 |
+ // override this func to mock out the proxy server |
|
| 11 |
+ newProxy = proxy.NewStubProxy |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+func reset() {
|
|
| 15 |
+ chain = nil |
|
| 16 |
+ currentMappings = make(map[string]*mapping) |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func TestSetIptablesChain(t *testing.T) {
|
|
| 20 |
+ defer reset() |
|
| 21 |
+ |
|
| 22 |
+ c := &iptables.Chain{
|
|
| 23 |
+ Name: "TEST", |
|
| 24 |
+ Bridge: "192.168.1.1", |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ if chain != nil {
|
|
| 28 |
+ t.Fatal("chain should be nil at init")
|
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ SetIptablesChain(c) |
|
| 32 |
+ if chain == nil {
|
|
| 33 |
+ t.Fatal("chain should not be nil after set")
|
|
| 34 |
+ } |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func TestMapPorts(t *testing.T) {
|
|
| 38 |
+ dstIp1 := net.ParseIP("192.168.0.1")
|
|
| 39 |
+ dstIp2 := net.ParseIP("192.168.0.2")
|
|
| 40 |
+ dstAddr1 := &net.TCPAddr{IP: dstIp1, Port: 80}
|
|
| 41 |
+ dstAddr2 := &net.TCPAddr{IP: dstIp2, Port: 80}
|
|
| 42 |
+ |
|
| 43 |
+ srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
|
|
| 44 |
+ srcAddr2 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")}
|
|
| 45 |
+ |
|
| 46 |
+ if err := Map(srcAddr1, dstIp1, 80); err != nil {
|
|
| 47 |
+ t.Fatalf("Failed to allocate port: %s", err)
|
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ if Map(srcAddr1, dstIp1, 80) == nil {
|
|
| 51 |
+ t.Fatalf("Port is in use - mapping should have failed")
|
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ if Map(srcAddr2, dstIp1, 80) == nil {
|
|
| 55 |
+ t.Fatalf("Port is in use - mapping should have failed")
|
|
| 56 |
+ } |
|
| 57 |
+ |
|
| 58 |
+ if err := Map(srcAddr2, dstIp2, 80); err != nil {
|
|
| 59 |
+ t.Fatalf("Failed to allocate port: %s", err)
|
|
| 60 |
+ } |
|
| 61 |
+ |
|
| 62 |
+ if Unmap(dstAddr1) != nil {
|
|
| 63 |
+ t.Fatalf("Failed to release port")
|
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ if Unmap(dstAddr2) != nil {
|
|
| 67 |
+ t.Fatalf("Failed to release port")
|
|
| 68 |
+ } |
|
| 69 |
+ |
|
| 70 |
+ if Unmap(dstAddr2) == nil {
|
|
| 71 |
+ t.Fatalf("Port already released, but no error reported")
|
|
| 72 |
+ } |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+func TestGetUDPKey(t *testing.T) {
|
|
| 76 |
+ addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53}
|
|
| 77 |
+ |
|
| 78 |
+ key := getKey(addr) |
|
| 79 |
+ |
|
| 80 |
+ if expected := "192.168.1.5:53/udp"; key != expected {
|
|
| 81 |
+ t.Fatalf("expected key %s got %s", expected, key)
|
|
| 82 |
+ } |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func TestGetTCPKey(t *testing.T) {
|
|
| 86 |
+ addr := &net.TCPAddr{IP: net.ParseIP("192.168.1.5"), Port: 80}
|
|
| 87 |
+ |
|
| 88 |
+ key := getKey(addr) |
|
| 89 |
+ |
|
| 90 |
+ if expected := "192.168.1.5:80/tcp"; key != expected {
|
|
| 91 |
+ t.Fatalf("expected key %s got %s", expected, key)
|
|
| 92 |
+ } |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func TestGetUDPIPAndPort(t *testing.T) {
|
|
| 96 |
+ addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53}
|
|
| 97 |
+ |
|
| 98 |
+ ip, port := getIPAndPort(addr) |
|
| 99 |
+ if expected := "192.168.1.5"; ip.String() != expected {
|
|
| 100 |
+ t.Fatalf("expected ip %s got %s", expected, ip)
|
|
| 101 |
+ } |
|
| 102 |
+ |
|
| 103 |
+ if ep := 53; port != ep {
|
|
| 104 |
+ t.Fatalf("expected port %d got %d", ep, port)
|
|
| 105 |
+ } |
|
| 106 |
+} |
| 0 | 107 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,118 @@ |
| 0 |
+package networkdriver |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/binary" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "net" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/dotcloud/docker/pkg/netlink" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var ( |
|
| 12 |
+ networkGetRoutesFct = netlink.NetworkGetRoutes |
|
| 13 |
+ ErrNoDefaultRoute = errors.New("no default route")
|
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
|
|
| 17 |
+ if len(nameservers) > 0 {
|
|
| 18 |
+ for _, ns := range nameservers {
|
|
| 19 |
+ _, nsNetwork, err := net.ParseCIDR(ns) |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ return err |
|
| 22 |
+ } |
|
| 23 |
+ if NetworkOverlaps(toCheck, nsNetwork) {
|
|
| 24 |
+ return ErrNetworkOverlapsWithNameservers |
|
| 25 |
+ } |
|
| 26 |
+ } |
|
| 27 |
+ } |
|
| 28 |
+ return nil |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+func CheckRouteOverlaps(toCheck *net.IPNet) error {
|
|
| 32 |
+ networks, err := networkGetRoutesFct() |
|
| 33 |
+ if err != nil {
|
|
| 34 |
+ return err |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ for _, network := range networks {
|
|
| 38 |
+ if network.IPNet != nil && NetworkOverlaps(toCheck, network.IPNet) {
|
|
| 39 |
+ return ErrNetworkOverlaps |
|
| 40 |
+ } |
|
| 41 |
+ } |
|
| 42 |
+ return nil |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// Detects overlap between one IPNet and another |
|
| 46 |
+func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool {
|
|
| 47 |
+ if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) {
|
|
| 48 |
+ return true |
|
| 49 |
+ } |
|
| 50 |
+ if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) {
|
|
| 51 |
+ return true |
|
| 52 |
+ } |
|
| 53 |
+ return false |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// Calculates the first and last IP addresses in an IPNet |
|
| 57 |
+func NetworkRange(network *net.IPNet) (net.IP, net.IP) {
|
|
| 58 |
+ var ( |
|
| 59 |
+ netIP = network.IP.To4() |
|
| 60 |
+ firstIP = netIP.Mask(network.Mask) |
|
| 61 |
+ lastIP = net.IPv4(0, 0, 0, 0).To4() |
|
| 62 |
+ ) |
|
| 63 |
+ |
|
| 64 |
+ for i := 0; i < len(lastIP); i++ {
|
|
| 65 |
+ lastIP[i] = netIP[i] | ^network.Mask[i] |
|
| 66 |
+ } |
|
| 67 |
+ return firstIP, lastIP |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+// Given a netmask, calculates the number of available hosts |
|
| 71 |
+func NetworkSize(mask net.IPMask) int32 {
|
|
| 72 |
+ m := net.IPv4Mask(0, 0, 0, 0) |
|
| 73 |
+ for i := 0; i < net.IPv4len; i++ {
|
|
| 74 |
+ m[i] = ^mask[i] |
|
| 75 |
+ } |
|
| 76 |
+ return int32(binary.BigEndian.Uint32(m)) + 1 |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Return the IPv4 address of a network interface |
|
| 80 |
+func GetIfaceAddr(name string) (net.Addr, error) {
|
|
| 81 |
+ iface, err := net.InterfaceByName(name) |
|
| 82 |
+ if err != nil {
|
|
| 83 |
+ return nil, err |
|
| 84 |
+ } |
|
| 85 |
+ addrs, err := iface.Addrs() |
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return nil, err |
|
| 88 |
+ } |
|
| 89 |
+ var addrs4 []net.Addr |
|
| 90 |
+ for _, addr := range addrs {
|
|
| 91 |
+ ip := (addr.(*net.IPNet)).IP |
|
| 92 |
+ if ip4 := ip.To4(); len(ip4) == net.IPv4len {
|
|
| 93 |
+ addrs4 = append(addrs4, addr) |
|
| 94 |
+ } |
|
| 95 |
+ } |
|
| 96 |
+ switch {
|
|
| 97 |
+ case len(addrs4) == 0: |
|
| 98 |
+ return nil, fmt.Errorf("Interface %v has no IP addresses", name)
|
|
| 99 |
+ case len(addrs4) > 1: |
|
| 100 |
+ fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n",
|
|
| 101 |
+ name, (addrs4[0].(*net.IPNet)).IP) |
|
| 102 |
+ } |
|
| 103 |
+ return addrs4[0], nil |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+func GetDefaultRouteIface() (*net.Interface, error) {
|
|
| 107 |
+ rs, err := networkGetRoutesFct() |
|
| 108 |
+ if err != nil {
|
|
| 109 |
+ return nil, fmt.Errorf("unable to get routes: %v", err)
|
|
| 110 |
+ } |
|
| 111 |
+ for _, r := range rs {
|
|
| 112 |
+ if r.Default {
|
|
| 113 |
+ return r.Iface, nil |
|
| 114 |
+ } |
|
| 115 |
+ } |
|
| 116 |
+ return nil, ErrNoDefaultRoute |
|
| 117 |
+} |
| ... | ... |
@@ -17,8 +17,8 @@ import ( |
| 17 | 17 |
_ "github.com/dotcloud/docker/graphdriver/devmapper" |
| 18 | 18 |
_ "github.com/dotcloud/docker/graphdriver/vfs" |
| 19 | 19 |
"github.com/dotcloud/docker/image" |
| 20 |
- _ "github.com/dotcloud/docker/networkdriver/lxc" |
|
| 21 |
- "github.com/dotcloud/docker/networkdriver/portallocator" |
|
| 20 |
+ _ "github.com/dotcloud/docker/runtime/networkdriver/lxc" |
|
| 21 |
+ "github.com/dotcloud/docker/runtime/networkdriver/portallocator" |
|
| 22 | 22 |
"github.com/dotcloud/docker/pkg/graphdb" |
| 23 | 23 |
"github.com/dotcloud/docker/pkg/sysinfo" |
| 24 | 24 |
"github.com/dotcloud/docker/runconfig" |