github.com/clcy1243/docker@v1.6.0-rc3/daemon/networkdriver/bridge/driver.go (about) 1 package bridge 2 3 import ( 4 "encoding/hex" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "net" 9 "os" 10 "strings" 11 "sync" 12 13 log "github.com/Sirupsen/logrus" 14 "github.com/docker/docker/daemon/networkdriver" 15 "github.com/docker/docker/daemon/networkdriver/ipallocator" 16 "github.com/docker/docker/daemon/networkdriver/portmapper" 17 "github.com/docker/docker/engine" 18 "github.com/docker/docker/nat" 19 "github.com/docker/docker/pkg/iptables" 20 "github.com/docker/docker/pkg/networkfs/resolvconf" 21 "github.com/docker/docker/pkg/parsers/kernel" 22 "github.com/docker/libcontainer/netlink" 23 ) 24 25 const ( 26 DefaultNetworkBridge = "docker0" 27 MaxAllocatedPortAttempts = 10 28 ) 29 30 // Network interface represents the networking stack of a container 31 type networkInterface struct { 32 IP net.IP 33 IPv6 net.IP 34 PortMappings []net.Addr // There are mappings to the host interfaces 35 } 36 37 type ifaces struct { 38 c map[string]*networkInterface 39 sync.Mutex 40 } 41 42 func (i *ifaces) Set(key string, n *networkInterface) { 43 i.Lock() 44 i.c[key] = n 45 i.Unlock() 46 } 47 48 func (i *ifaces) Get(key string) *networkInterface { 49 i.Lock() 50 res := i.c[key] 51 i.Unlock() 52 return res 53 } 54 55 var ( 56 addrs = []string{ 57 // Here we don't follow the convention of using the 1st IP of the range for the gateway. 58 // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. 59 // In theory this shouldn't matter - in practice there's bound to be a few scripts relying 60 // on the internal addressing or other stupid things like that. 61 // They shouldn't, but hey, let's not break them unless we really have to. 62 "172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 63 "10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive 64 "10.1.42.1/16", 65 "10.42.42.1/16", 66 "172.16.42.1/24", 67 "172.16.43.1/24", 68 "172.16.44.1/24", 69 "10.0.42.1/24", 70 "10.0.43.1/24", 71 "192.168.42.1/24", 72 "192.168.43.1/24", 73 "192.168.44.1/24", 74 } 75 76 bridgeIface string 77 bridgeIPv4Network *net.IPNet 78 bridgeIPv6Addr net.IP 79 globalIPv6Network *net.IPNet 80 portMapper *portmapper.PortMapper 81 once sync.Once 82 83 defaultBindingIP = net.ParseIP("0.0.0.0") 84 currentInterfaces = ifaces{c: make(map[string]*networkInterface)} 85 ) 86 87 func initPortMapper() { 88 once.Do(func() { 89 portMapper = portmapper.New() 90 }) 91 } 92 93 func InitDriver(job *engine.Job) engine.Status { 94 var ( 95 networkv4 *net.IPNet 96 networkv6 *net.IPNet 97 addrv4 net.Addr 98 addrsv6 []net.Addr 99 enableIPTables = job.GetenvBool("EnableIptables") 100 enableIPv6 = job.GetenvBool("EnableIPv6") 101 icc = job.GetenvBool("InterContainerCommunication") 102 ipMasq = job.GetenvBool("EnableIpMasq") 103 ipForward = job.GetenvBool("EnableIpForward") 104 bridgeIP = job.Getenv("BridgeIP") 105 bridgeIPv6 = "fe80::1/64" 106 fixedCIDR = job.Getenv("FixedCIDR") 107 fixedCIDRv6 = job.Getenv("FixedCIDRv6") 108 ) 109 initPortMapper() 110 111 if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" { 112 defaultBindingIP = net.ParseIP(defaultIP) 113 } 114 115 bridgeIface = job.Getenv("BridgeIface") 116 usingDefaultBridge := false 117 if bridgeIface == "" { 118 usingDefaultBridge = true 119 bridgeIface = DefaultNetworkBridge 120 } 121 122 addrv4, addrsv6, err := networkdriver.GetIfaceAddr(bridgeIface) 123 124 if err != nil { 125 // No Bridge existent, create one 126 // If we're not using the default bridge, fail without trying to create it 127 if !usingDefaultBridge { 128 return job.Error(err) 129 } 130 131 // If the iface is not found, try to create it 132 if err := configureBridge(bridgeIP, bridgeIPv6, enableIPv6); err != nil { 133 return job.Error(err) 134 } 135 136 addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface) 137 if err != nil { 138 return job.Error(err) 139 } 140 141 if fixedCIDRv6 != "" { 142 // Setting route to global IPv6 subnet 143 log.Infof("Adding route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface) 144 if err := netlink.AddRoute(fixedCIDRv6, "", "", bridgeIface); err != nil { 145 log.Fatalf("Could not add route to IPv6 network %q via device %q", fixedCIDRv6, bridgeIface) 146 } 147 } 148 } else { 149 // Bridge exists already, getting info... 150 // Validate that the bridge ip matches the ip specified by BridgeIP 151 if bridgeIP != "" { 152 networkv4 = addrv4.(*net.IPNet) 153 bip, _, err := net.ParseCIDR(bridgeIP) 154 if err != nil { 155 return job.Error(err) 156 } 157 if !networkv4.IP.Equal(bip) { 158 return job.Errorf("Bridge ip (%s) does not match existing bridge configuration %s", networkv4.IP, bip) 159 } 160 } 161 162 // A bridge might exist but not have any IPv6 addr associated with it yet 163 // (for example, an existing Docker installation that has only been used 164 // with IPv4 and docker0 already is set up) In that case, we can perform 165 // the bridge init for IPv6 here, else we will error out below if --ipv6=true 166 if len(addrsv6) == 0 && enableIPv6 { 167 if err := setupIPv6Bridge(bridgeIPv6); err != nil { 168 return job.Error(err) 169 } 170 // Recheck addresses now that IPv6 is setup on the bridge 171 addrv4, addrsv6, err = networkdriver.GetIfaceAddr(bridgeIface) 172 if err != nil { 173 return job.Error(err) 174 } 175 } 176 177 // TODO: Check if route to fixedCIDRv6 is set 178 } 179 180 if enableIPv6 { 181 bip6, _, err := net.ParseCIDR(bridgeIPv6) 182 if err != nil { 183 return job.Error(err) 184 } 185 found := false 186 for _, addrv6 := range addrsv6 { 187 networkv6 = addrv6.(*net.IPNet) 188 if networkv6.IP.Equal(bip6) { 189 found = true 190 break 191 } 192 } 193 if !found { 194 return job.Errorf("Bridge IPv6 does not match existing bridge configuration %s", bip6) 195 } 196 } 197 198 networkv4 = addrv4.(*net.IPNet) 199 200 if enableIPv6 { 201 if len(addrsv6) == 0 { 202 return job.Error(errors.New("IPv6 enabled but no IPv6 detected")) 203 } 204 bridgeIPv6Addr = networkv6.IP 205 } 206 207 // Configure iptables for link support 208 if enableIPTables { 209 if err := setupIPTables(addrv4, icc, ipMasq); err != nil { 210 return job.Error(err) 211 } 212 213 } 214 215 if ipForward { 216 // Enable IPv4 forwarding 217 if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil { 218 job.Logf("WARNING: unable to enable IPv4 forwarding: %s\n", err) 219 } 220 221 if fixedCIDRv6 != "" { 222 // Enable IPv6 forwarding 223 if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil { 224 job.Logf("WARNING: unable to enable IPv6 default forwarding: %s\n", err) 225 } 226 if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, 0644); err != nil { 227 job.Logf("WARNING: unable to enable IPv6 all forwarding: %s\n", err) 228 } 229 } 230 } 231 232 // We can always try removing the iptables 233 if err := iptables.RemoveExistingChain("DOCKER", iptables.Nat); err != nil { 234 return job.Error(err) 235 } 236 237 if enableIPTables { 238 _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat) 239 if err != nil { 240 return job.Error(err) 241 } 242 chain, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter) 243 if err != nil { 244 return job.Error(err) 245 } 246 portMapper.SetIptablesChain(chain) 247 } 248 249 bridgeIPv4Network = networkv4 250 if fixedCIDR != "" { 251 _, subnet, err := net.ParseCIDR(fixedCIDR) 252 if err != nil { 253 return job.Error(err) 254 } 255 log.Debugf("Subnet: %v", subnet) 256 if err := ipallocator.RegisterSubnet(bridgeIPv4Network, subnet); err != nil { 257 return job.Error(err) 258 } 259 } 260 261 if fixedCIDRv6 != "" { 262 _, subnet, err := net.ParseCIDR(fixedCIDRv6) 263 if err != nil { 264 return job.Error(err) 265 } 266 log.Debugf("Subnet: %v", subnet) 267 if err := ipallocator.RegisterSubnet(subnet, subnet); err != nil { 268 return job.Error(err) 269 } 270 globalIPv6Network = subnet 271 } 272 273 // Block BridgeIP in IP allocator 274 ipallocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP) 275 276 // https://github.com/docker/docker/issues/2768 277 job.Eng.Hack_SetGlobalVar("httpapi.bridgeIP", bridgeIPv4Network.IP) 278 279 for name, f := range map[string]engine.Handler{ 280 "allocate_interface": Allocate, 281 "release_interface": Release, 282 "allocate_port": AllocatePort, 283 "link": LinkContainers, 284 } { 285 if err := job.Eng.Register(name, f); err != nil { 286 return job.Error(err) 287 } 288 } 289 return engine.StatusOK 290 } 291 292 func setupIPTables(addr net.Addr, icc, ipmasq bool) error { 293 // Enable NAT 294 295 if ipmasq { 296 natArgs := []string{"-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"} 297 298 if !iptables.Exists(iptables.Nat, "POSTROUTING", natArgs...) { 299 if output, err := iptables.Raw(append([]string{ 300 "-t", string(iptables.Nat), "-I", "POSTROUTING"}, natArgs...)...); err != nil { 301 return fmt.Errorf("Unable to enable network bridge NAT: %s", err) 302 } else if len(output) != 0 { 303 return &iptables.ChainError{Chain: "POSTROUTING", Output: output} 304 } 305 } 306 } 307 308 var ( 309 args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"} 310 acceptArgs = append(args, "ACCEPT") 311 dropArgs = append(args, "DROP") 312 ) 313 314 if !icc { 315 iptables.Raw(append([]string{"-D", "FORWARD"}, acceptArgs...)...) 316 317 if !iptables.Exists(iptables.Filter, "FORWARD", dropArgs...) { 318 log.Debugf("Disable inter-container communication") 319 if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, dropArgs...)...); err != nil { 320 return fmt.Errorf("Unable to prevent intercontainer communication: %s", err) 321 } else if len(output) != 0 { 322 return fmt.Errorf("Error disabling intercontainer communication: %s", output) 323 } 324 } 325 } else { 326 iptables.Raw(append([]string{"-D", "FORWARD"}, dropArgs...)...) 327 328 if !iptables.Exists(iptables.Filter, "FORWARD", acceptArgs...) { 329 log.Debugf("Enable inter-container communication") 330 if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, acceptArgs...)...); err != nil { 331 return fmt.Errorf("Unable to allow intercontainer communication: %s", err) 332 } else if len(output) != 0 { 333 return fmt.Errorf("Error enabling intercontainer communication: %s", output) 334 } 335 } 336 } 337 338 // Accept all non-intercontainer outgoing packets 339 outgoingArgs := []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"} 340 if !iptables.Exists(iptables.Filter, "FORWARD", outgoingArgs...) { 341 if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, outgoingArgs...)...); err != nil { 342 return fmt.Errorf("Unable to allow outgoing packets: %s", err) 343 } else if len(output) != 0 { 344 return &iptables.ChainError{Chain: "FORWARD outgoing", Output: output} 345 } 346 } 347 348 // Accept incoming packets for existing connections 349 existingArgs := []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"} 350 351 if !iptables.Exists(iptables.Filter, "FORWARD", existingArgs...) { 352 if output, err := iptables.Raw(append([]string{"-I", "FORWARD"}, existingArgs...)...); err != nil { 353 return fmt.Errorf("Unable to allow incoming packets: %s", err) 354 } else if len(output) != 0 { 355 return &iptables.ChainError{Chain: "FORWARD incoming", Output: output} 356 } 357 } 358 return nil 359 } 360 361 func RequestPort(ip net.IP, proto string, port int) (int, error) { 362 initPortMapper() 363 return portMapper.Allocator.RequestPort(ip, proto, port) 364 } 365 366 // configureBridge attempts to create and configure a network bridge interface named `bridgeIface` on the host 367 // If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges 368 // If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing 369 // bridge (fixes issue #8444) 370 // If an address which doesn't conflict with existing interfaces can't be found, an error is returned. 371 func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error { 372 nameservers := []string{} 373 resolvConf, _ := resolvconf.Get() 374 // We don't check for an error here, because we don't really care 375 // if we can't read /etc/resolv.conf. So instead we skip the append 376 // if resolvConf is nil. It either doesn't exist, or we can't read it 377 // for some reason. 378 if resolvConf != nil { 379 nameservers = append(nameservers, resolvconf.GetNameserversAsCIDR(resolvConf)...) 380 } 381 382 var ifaceAddr string 383 if len(bridgeIP) != 0 { 384 _, _, err := net.ParseCIDR(bridgeIP) 385 if err != nil { 386 return err 387 } 388 ifaceAddr = bridgeIP 389 } else { 390 for _, addr := range addrs { 391 _, dockerNetwork, err := net.ParseCIDR(addr) 392 if err != nil { 393 return err 394 } 395 if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil { 396 if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil { 397 ifaceAddr = addr 398 break 399 } else { 400 log.Debugf("%s %s", addr, err) 401 } 402 } 403 } 404 } 405 406 if ifaceAddr == "" { 407 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) 408 } 409 log.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr) 410 411 if err := createBridgeIface(bridgeIface); err != nil { 412 // The bridge may already exist, therefore we can ignore an "exists" error 413 if !os.IsExist(err) { 414 return err 415 } 416 } 417 418 iface, err := net.InterfaceByName(bridgeIface) 419 if err != nil { 420 return err 421 } 422 423 ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) 424 if err != nil { 425 return err 426 } 427 428 if err := netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil { 429 return fmt.Errorf("Unable to add private network: %s", err) 430 } 431 432 if enableIPv6 { 433 if err := setupIPv6Bridge(bridgeIPv6); err != nil { 434 return err 435 } 436 } 437 438 if err := netlink.NetworkLinkUp(iface); err != nil { 439 return fmt.Errorf("Unable to start network bridge: %s", err) 440 } 441 return nil 442 } 443 444 func setupIPv6Bridge(bridgeIPv6 string) error { 445 446 iface, err := net.InterfaceByName(bridgeIface) 447 if err != nil { 448 return err 449 } 450 // Enable IPv6 on the bridge 451 procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6" 452 if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil { 453 return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err) 454 } 455 456 ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6) 457 if err != nil { 458 return fmt.Errorf("Unable to parse bridge IPv6 address: %q, error: %v", bridgeIPv6, err) 459 } 460 461 if err := netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil { 462 return fmt.Errorf("Unable to add private IPv6 network: %v", err) 463 } 464 465 return nil 466 } 467 468 func createBridgeIface(name string) error { 469 kv, err := kernel.GetKernelVersion() 470 // Only set the bridge's mac address if the kernel version is > 3.3 471 // before that it was not supported 472 setBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3) 473 log.Debugf("setting bridge mac address = %v", setBridgeMacAddr) 474 return netlink.CreateBridge(name, setBridgeMacAddr) 475 } 476 477 // Generate a IEEE802 compliant MAC address from the given IP address. 478 // 479 // The generator is guaranteed to be consistent: the same IP will always yield the same 480 // MAC address. This is to avoid ARP cache issues. 481 func generateMacAddr(ip net.IP) net.HardwareAddr { 482 hw := make(net.HardwareAddr, 6) 483 484 // The first byte of the MAC address has to comply with these rules: 485 // 1. Unicast: Set the least-significant bit to 0. 486 // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. 487 // 3. As "small" as possible: The veth address has to be "smaller" than the bridge address. 488 hw[0] = 0x02 489 490 // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). 491 // Since this address is locally administered, we can do whatever we want as long as 492 // it doesn't conflict with other addresses. 493 hw[1] = 0x42 494 495 // Insert the IP address into the last 32 bits of the MAC address. 496 // This is a simple way to guarantee the address will be consistent and unique. 497 copy(hw[2:], ip.To4()) 498 499 return hw 500 } 501 502 func linkLocalIPv6FromMac(mac string) (string, error) { 503 hx := strings.Replace(mac, ":", "", -1) 504 hw, err := hex.DecodeString(hx) 505 if err != nil { 506 return "", errors.New("Could not parse MAC address " + mac) 507 } 508 509 hw[0] ^= 0x2 510 511 return fmt.Sprintf("fe80::%x%x:%xff:fe%x:%x%x/64", hw[0], hw[1], hw[2], hw[3], hw[4], hw[5]), nil 512 } 513 514 // Allocate a network interface 515 func Allocate(job *engine.Job) engine.Status { 516 var ( 517 ip net.IP 518 mac net.HardwareAddr 519 err error 520 id = job.Args[0] 521 requestedIP = net.ParseIP(job.Getenv("RequestedIP")) 522 requestedIPv6 = net.ParseIP(job.Getenv("RequestedIPv6")) 523 globalIPv6 net.IP 524 ) 525 526 ip, err = ipallocator.RequestIP(bridgeIPv4Network, requestedIP) 527 if err != nil { 528 return job.Error(err) 529 } 530 531 // If no explicit mac address was given, generate a random one. 532 if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil { 533 mac = generateMacAddr(ip) 534 } 535 536 if globalIPv6Network != nil { 537 // If globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address 538 netmask_ones, _ := globalIPv6Network.Mask.Size() 539 if requestedIPv6 == nil && netmask_ones <= 80 { 540 requestedIPv6 = make(net.IP, len(globalIPv6Network.IP)) 541 copy(requestedIPv6, globalIPv6Network.IP) 542 for i, h := range mac { 543 requestedIPv6[i+10] = h 544 } 545 } 546 547 globalIPv6, err = ipallocator.RequestIP(globalIPv6Network, requestedIPv6) 548 if err != nil { 549 log.Errorf("Allocator: RequestIP v6: %v", err) 550 return job.Error(err) 551 } 552 log.Infof("Allocated IPv6 %s", globalIPv6) 553 } 554 555 out := engine.Env{} 556 out.Set("IP", ip.String()) 557 out.Set("Mask", bridgeIPv4Network.Mask.String()) 558 out.Set("Gateway", bridgeIPv4Network.IP.String()) 559 out.Set("MacAddress", mac.String()) 560 out.Set("Bridge", bridgeIface) 561 562 size, _ := bridgeIPv4Network.Mask.Size() 563 out.SetInt("IPPrefixLen", size) 564 565 // If linklocal IPv6 566 localIPv6Net, err := linkLocalIPv6FromMac(mac.String()) 567 if err != nil { 568 return job.Error(err) 569 } 570 localIPv6, _, _ := net.ParseCIDR(localIPv6Net) 571 out.Set("LinkLocalIPv6", localIPv6.String()) 572 out.Set("MacAddress", mac.String()) 573 574 if globalIPv6Network != nil { 575 out.Set("GlobalIPv6", globalIPv6.String()) 576 sizev6, _ := globalIPv6Network.Mask.Size() 577 out.SetInt("GlobalIPv6PrefixLen", sizev6) 578 out.Set("IPv6Gateway", bridgeIPv6Addr.String()) 579 } 580 581 currentInterfaces.Set(id, &networkInterface{ 582 IP: ip, 583 IPv6: globalIPv6, 584 }) 585 586 out.WriteTo(job.Stdout) 587 588 return engine.StatusOK 589 } 590 591 // Release an interface for a select ip 592 func Release(job *engine.Job) engine.Status { 593 var ( 594 id = job.Args[0] 595 containerInterface = currentInterfaces.Get(id) 596 ) 597 598 if containerInterface == nil { 599 return job.Errorf("No network information to release for %s", id) 600 } 601 602 for _, nat := range containerInterface.PortMappings { 603 if err := portMapper.Unmap(nat); err != nil { 604 log.Infof("Unable to unmap port %s: %s", nat, err) 605 } 606 } 607 608 if err := ipallocator.ReleaseIP(bridgeIPv4Network, containerInterface.IP); err != nil { 609 log.Infof("Unable to release IPv4 %s", err) 610 } 611 if globalIPv6Network != nil { 612 if err := ipallocator.ReleaseIP(globalIPv6Network, containerInterface.IPv6); err != nil { 613 log.Infof("Unable to release IPv6 %s", err) 614 } 615 } 616 return engine.StatusOK 617 } 618 619 // Allocate an external port and map it to the interface 620 func AllocatePort(job *engine.Job) engine.Status { 621 var ( 622 err error 623 624 ip = defaultBindingIP 625 id = job.Args[0] 626 hostIP = job.Getenv("HostIP") 627 hostPort = job.GetenvInt("HostPort") 628 containerPort = job.GetenvInt("ContainerPort") 629 proto = job.Getenv("Proto") 630 network = currentInterfaces.Get(id) 631 ) 632 633 if hostIP != "" { 634 ip = net.ParseIP(hostIP) 635 if ip == nil { 636 return job.Errorf("Bad parameter: invalid host ip %s", hostIP) 637 } 638 } 639 640 // host ip, proto, and host port 641 var container net.Addr 642 switch proto { 643 case "tcp": 644 container = &net.TCPAddr{IP: network.IP, Port: containerPort} 645 case "udp": 646 container = &net.UDPAddr{IP: network.IP, Port: containerPort} 647 default: 648 return job.Errorf("unsupported address type %s", proto) 649 } 650 651 // 652 // Try up to 10 times to get a port that's not already allocated. 653 // 654 // In the event of failure to bind, return the error that portmapper.Map 655 // yields. 656 // 657 658 var host net.Addr 659 for i := 0; i < MaxAllocatedPortAttempts; i++ { 660 if host, err = portMapper.Map(container, ip, hostPort); err == nil { 661 break 662 } 663 // There is no point in immediately retrying to map an explicitly 664 // chosen port. 665 if hostPort != 0 { 666 job.Logf("Failed to allocate and map port %d: %s", hostPort, err) 667 break 668 } 669 job.Logf("Failed to allocate and map port: %s, retry: %d", err, i+1) 670 } 671 672 if err != nil { 673 return job.Error(err) 674 } 675 676 network.PortMappings = append(network.PortMappings, host) 677 678 out := engine.Env{} 679 switch netAddr := host.(type) { 680 case *net.TCPAddr: 681 out.Set("HostIP", netAddr.IP.String()) 682 out.SetInt("HostPort", netAddr.Port) 683 case *net.UDPAddr: 684 out.Set("HostIP", netAddr.IP.String()) 685 out.SetInt("HostPort", netAddr.Port) 686 } 687 if _, err := out.WriteTo(job.Stdout); err != nil { 688 return job.Error(err) 689 } 690 691 return engine.StatusOK 692 } 693 694 func LinkContainers(job *engine.Job) engine.Status { 695 var ( 696 action = job.Args[0] 697 nfAction iptables.Action 698 childIP = job.Getenv("ChildIP") 699 parentIP = job.Getenv("ParentIP") 700 ignoreErrors = job.GetenvBool("IgnoreErrors") 701 ports = job.GetenvList("Ports") 702 ) 703 704 switch action { 705 case "-A": 706 nfAction = iptables.Append 707 case "-I": 708 nfAction = iptables.Insert 709 case "-D": 710 nfAction = iptables.Delete 711 default: 712 return job.Errorf("Invalid action '%s' specified", action) 713 } 714 715 ip1 := net.ParseIP(parentIP) 716 if ip1 == nil { 717 return job.Errorf("Parent IP '%s' is invalid", parentIP) 718 } 719 ip2 := net.ParseIP(childIP) 720 if ip2 == nil { 721 return job.Errorf("Child IP '%s' is invalid", childIP) 722 } 723 724 chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface} 725 for _, p := range ports { 726 port := nat.Port(p) 727 if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil { 728 return job.Error(err) 729 } 730 } 731 return engine.StatusOK 732 }