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