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