github.com/daaku/docker@v1.5.0/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{"POSTROUTING", "-t", "nat", "-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"} 288 289 if !iptables.Exists(natArgs...) { 290 if output, err := iptables.Raw(append([]string{"-I"}, natArgs...)...); err != nil { 291 return fmt.Errorf("Unable to enable network bridge NAT: %s", err) 292 } else if len(output) != 0 { 293 return &iptables.ChainError{Chain: "POSTROUTING", Output: output} 294 } 295 } 296 } 297 298 var ( 299 args = []string{"FORWARD", "-i", bridgeIface, "-o", bridgeIface, "-j"} 300 acceptArgs = append(args, "ACCEPT") 301 dropArgs = append(args, "DROP") 302 ) 303 304 if !icc { 305 iptables.Raw(append([]string{"-D"}, acceptArgs...)...) 306 307 if !iptables.Exists(dropArgs...) { 308 log.Debugf("Disable inter-container communication") 309 if output, err := iptables.Raw(append([]string{"-I"}, dropArgs...)...); err != nil { 310 return fmt.Errorf("Unable to prevent intercontainer communication: %s", err) 311 } else if len(output) != 0 { 312 return fmt.Errorf("Error disabling intercontainer communication: %s", output) 313 } 314 } 315 } else { 316 iptables.Raw(append([]string{"-D"}, dropArgs...)...) 317 318 if !iptables.Exists(acceptArgs...) { 319 log.Debugf("Enable inter-container communication") 320 if output, err := iptables.Raw(append([]string{"-I"}, acceptArgs...)...); err != nil { 321 return fmt.Errorf("Unable to allow intercontainer communication: %s", err) 322 } else if len(output) != 0 { 323 return fmt.Errorf("Error enabling intercontainer communication: %s", output) 324 } 325 } 326 } 327 328 // Accept all non-intercontainer outgoing packets 329 outgoingArgs := []string{"FORWARD", "-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"} 330 if !iptables.Exists(outgoingArgs...) { 331 if output, err := iptables.Raw(append([]string{"-I"}, outgoingArgs...)...); err != nil { 332 return fmt.Errorf("Unable to allow outgoing packets: %s", err) 333 } else if len(output) != 0 { 334 return &iptables.ChainError{Chain: "FORWARD outgoing", Output: output} 335 } 336 } 337 338 // Accept incoming packets for existing connections 339 existingArgs := []string{"FORWARD", "-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"} 340 341 if !iptables.Exists(existingArgs...) { 342 if output, err := iptables.Raw(append([]string{"-I"}, existingArgs...)...); err != nil { 343 return fmt.Errorf("Unable to allow incoming packets: %s", err) 344 } else if len(output) != 0 { 345 return &iptables.ChainError{Chain: "FORWARD incoming", Output: output} 346 } 347 } 348 return nil 349 } 350 351 // configureBridge attempts to create and configure a network bridge interface named `bridgeIface` on the host 352 // If bridgeIP is empty, it will try to find a non-conflicting IP from the Docker-specified private ranges 353 // If the bridge `bridgeIface` already exists, it will only perform the IP address association with the existing 354 // bridge (fixes issue #8444) 355 // If an address which doesn't conflict with existing interfaces can't be found, an error is returned. 356 func configureBridge(bridgeIP string, bridgeIPv6 string, enableIPv6 bool) error { 357 nameservers := []string{} 358 resolvConf, _ := resolvconf.Get() 359 // we don't check for an error here, because we don't really care 360 // if we can't read /etc/resolv.conf. So instead we skip the append 361 // if resolvConf is nil. It either doesn't exist, or we can't read it 362 // for some reason. 363 if resolvConf != nil { 364 nameservers = append(nameservers, resolvconf.GetNameserversAsCIDR(resolvConf)...) 365 } 366 367 var ifaceAddr string 368 if len(bridgeIP) != 0 { 369 _, _, err := net.ParseCIDR(bridgeIP) 370 if err != nil { 371 return err 372 } 373 ifaceAddr = bridgeIP 374 } else { 375 for _, addr := range addrs { 376 _, dockerNetwork, err := net.ParseCIDR(addr) 377 if err != nil { 378 return err 379 } 380 if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil { 381 if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil { 382 ifaceAddr = addr 383 break 384 } else { 385 log.Debugf("%s %s", addr, err) 386 } 387 } 388 } 389 } 390 391 if ifaceAddr == "" { 392 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) 393 } 394 log.Debugf("Creating bridge %s with network %s", bridgeIface, ifaceAddr) 395 396 if err := createBridgeIface(bridgeIface); err != nil { 397 // the bridge may already exist, therefore we can ignore an "exists" error 398 if !os.IsExist(err) { 399 return err 400 } 401 } 402 403 iface, err := net.InterfaceByName(bridgeIface) 404 if err != nil { 405 return err 406 } 407 408 ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) 409 if err != nil { 410 return err 411 } 412 413 if err := netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil { 414 return fmt.Errorf("Unable to add private network: %s", err) 415 } 416 417 if enableIPv6 { 418 if err := setupIPv6Bridge(bridgeIPv6); err != nil { 419 return err 420 } 421 } 422 423 if err := netlink.NetworkLinkUp(iface); err != nil { 424 return fmt.Errorf("Unable to start network bridge: %s", err) 425 } 426 return nil 427 } 428 429 func setupIPv6Bridge(bridgeIPv6 string) error { 430 431 iface, err := net.InterfaceByName(bridgeIface) 432 if err != nil { 433 return err 434 } 435 // Enable IPv6 on the bridge 436 procFile := "/proc/sys/net/ipv6/conf/" + iface.Name + "/disable_ipv6" 437 if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil { 438 return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err) 439 } 440 441 ipAddr6, ipNet6, err := net.ParseCIDR(bridgeIPv6) 442 if err != nil { 443 return fmt.Errorf("Unable to parse bridge IPv6 address: %q, error: %v", bridgeIPv6, err) 444 } 445 446 if err := netlink.NetworkLinkAddIp(iface, ipAddr6, ipNet6); err != nil { 447 return fmt.Errorf("Unable to add private IPv6 network: %v", err) 448 } 449 450 return nil 451 } 452 453 func createBridgeIface(name string) error { 454 kv, err := kernel.GetKernelVersion() 455 // only set the bridge's mac address if the kernel version is > 3.3 456 // before that it was not supported 457 setBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3) 458 log.Debugf("setting bridge mac address = %v", setBridgeMacAddr) 459 return netlink.CreateBridge(name, setBridgeMacAddr) 460 } 461 462 // Generate a IEEE802 compliant MAC address from the given IP address. 463 // 464 // The generator is guaranteed to be consistent: the same IP will always yield the same 465 // MAC address. This is to avoid ARP cache issues. 466 func generateMacAddr(ip net.IP) net.HardwareAddr { 467 hw := make(net.HardwareAddr, 6) 468 469 // The first byte of the MAC address has to comply with these rules: 470 // 1. Unicast: Set the least-significant bit to 0. 471 // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. 472 // 3. As "small" as possible: The veth address has to be "smaller" than the bridge address. 473 hw[0] = 0x02 474 475 // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). 476 // Since this address is locally administered, we can do whatever we want as long as 477 // it doesn't conflict with other addresses. 478 hw[1] = 0x42 479 480 // Insert the IP address into the last 32 bits of the MAC address. 481 // This is a simple way to guarantee the address will be consistent and unique. 482 copy(hw[2:], ip.To4()) 483 484 return hw 485 } 486 487 func linkLocalIPv6FromMac(mac string) (string, error) { 488 hx := strings.Replace(mac, ":", "", -1) 489 hw, err := hex.DecodeString(hx) 490 if err != nil { 491 return "", errors.New("Could not parse MAC address " + mac) 492 } 493 494 hw[0] ^= 0x2 495 496 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 497 } 498 499 // Allocate a network interface 500 func Allocate(job *engine.Job) engine.Status { 501 var ( 502 ip net.IP 503 mac net.HardwareAddr 504 err error 505 id = job.Args[0] 506 requestedIP = net.ParseIP(job.Getenv("RequestedIP")) 507 requestedIPv6 = net.ParseIP(job.Getenv("RequestedIPv6")) 508 globalIPv6 net.IP 509 ) 510 511 if requestedIP != nil { 512 ip, err = ipallocator.RequestIP(bridgeIPv4Network, requestedIP) 513 } else { 514 ip, err = ipallocator.RequestIP(bridgeIPv4Network, nil) 515 } 516 if err != nil { 517 return job.Error(err) 518 } 519 520 // If no explicit mac address was given, generate a random one. 521 if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil { 522 mac = generateMacAddr(ip) 523 } 524 525 if globalIPv6Network != nil { 526 // if globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address 527 netmask_ones, _ := globalIPv6Network.Mask.Size() 528 if requestedIPv6 == nil && netmask_ones <= 80 { 529 requestedIPv6 = globalIPv6Network.IP 530 for i, h := range mac { 531 requestedIPv6[i+10] = h 532 } 533 } 534 535 globalIPv6, err = ipallocator.RequestIP(globalIPv6Network, requestedIPv6) 536 if err != nil { 537 log.Errorf("Allocator: RequestIP v6: %s", err.Error()) 538 return job.Error(err) 539 } 540 log.Infof("Allocated IPv6 %s", globalIPv6) 541 } 542 543 out := engine.Env{} 544 out.Set("IP", ip.String()) 545 out.Set("Mask", bridgeIPv4Network.Mask.String()) 546 out.Set("Gateway", bridgeIPv4Network.IP.String()) 547 out.Set("MacAddress", mac.String()) 548 out.Set("Bridge", bridgeIface) 549 550 size, _ := bridgeIPv4Network.Mask.Size() 551 out.SetInt("IPPrefixLen", size) 552 553 // if linklocal IPv6 554 localIPv6Net, err := linkLocalIPv6FromMac(mac.String()) 555 if err != nil { 556 return job.Error(err) 557 } 558 localIPv6, _, _ := net.ParseCIDR(localIPv6Net) 559 out.Set("LinkLocalIPv6", localIPv6.String()) 560 out.Set("MacAddress", mac.String()) 561 562 if globalIPv6Network != nil { 563 out.Set("GlobalIPv6", globalIPv6.String()) 564 sizev6, _ := globalIPv6Network.Mask.Size() 565 out.SetInt("GlobalIPv6PrefixLen", sizev6) 566 out.Set("IPv6Gateway", bridgeIPv6Addr.String()) 567 } 568 569 currentInterfaces.Set(id, &networkInterface{ 570 IP: ip, 571 IPv6: globalIPv6, 572 }) 573 574 out.WriteTo(job.Stdout) 575 576 return engine.StatusOK 577 } 578 579 // release an interface for a select ip 580 func Release(job *engine.Job) engine.Status { 581 var ( 582 id = job.Args[0] 583 containerInterface = currentInterfaces.Get(id) 584 ) 585 586 if containerInterface == nil { 587 return job.Errorf("No network information to release for %s", id) 588 } 589 590 for _, nat := range containerInterface.PortMappings { 591 if err := portmapper.Unmap(nat); err != nil { 592 log.Infof("Unable to unmap port %s: %s", nat, err) 593 } 594 } 595 596 if err := ipallocator.ReleaseIP(bridgeIPv4Network, containerInterface.IP); err != nil { 597 log.Infof("Unable to release IPv4 %s", err) 598 } 599 if globalIPv6Network != nil { 600 if err := ipallocator.ReleaseIP(globalIPv6Network, containerInterface.IPv6); err != nil { 601 log.Infof("Unable to release IPv6 %s", err) 602 } 603 } 604 return engine.StatusOK 605 } 606 607 // Allocate an external port and map it to the interface 608 func AllocatePort(job *engine.Job) engine.Status { 609 var ( 610 err error 611 612 ip = defaultBindingIP 613 id = job.Args[0] 614 hostIP = job.Getenv("HostIP") 615 hostPort = job.GetenvInt("HostPort") 616 containerPort = job.GetenvInt("ContainerPort") 617 proto = job.Getenv("Proto") 618 network = currentInterfaces.Get(id) 619 ) 620 621 if hostIP != "" { 622 ip = net.ParseIP(hostIP) 623 if ip == nil { 624 return job.Errorf("Bad parameter: invalid host ip %s", hostIP) 625 } 626 } 627 628 // host ip, proto, and host port 629 var container net.Addr 630 switch proto { 631 case "tcp": 632 container = &net.TCPAddr{IP: network.IP, Port: containerPort} 633 case "udp": 634 container = &net.UDPAddr{IP: network.IP, Port: containerPort} 635 default: 636 return job.Errorf("unsupported address type %s", proto) 637 } 638 639 // 640 // Try up to 10 times to get a port that's not already allocated. 641 // 642 // In the event of failure to bind, return the error that portmapper.Map 643 // yields. 644 // 645 646 var host net.Addr 647 for i := 0; i < MaxAllocatedPortAttempts; i++ { 648 if host, err = portmapper.Map(container, ip, hostPort); err == nil { 649 break 650 } 651 // There is no point in immediately retrying to map an explicitly 652 // chosen port. 653 if hostPort != 0 { 654 job.Logf("Failed to allocate and map port %d: %s", hostPort, err) 655 break 656 } 657 job.Logf("Failed to allocate and map port: %s, retry: %d", err, i+1) 658 } 659 660 if err != nil { 661 return job.Error(err) 662 } 663 664 network.PortMappings = append(network.PortMappings, host) 665 666 out := engine.Env{} 667 switch netAddr := host.(type) { 668 case *net.TCPAddr: 669 out.Set("HostIP", netAddr.IP.String()) 670 out.SetInt("HostPort", netAddr.Port) 671 case *net.UDPAddr: 672 out.Set("HostIP", netAddr.IP.String()) 673 out.SetInt("HostPort", netAddr.Port) 674 } 675 if _, err := out.WriteTo(job.Stdout); err != nil { 676 return job.Error(err) 677 } 678 679 return engine.StatusOK 680 } 681 682 func LinkContainers(job *engine.Job) engine.Status { 683 var ( 684 action = job.Args[0] 685 nfAction iptables.Action 686 childIP = job.Getenv("ChildIP") 687 parentIP = job.Getenv("ParentIP") 688 ignoreErrors = job.GetenvBool("IgnoreErrors") 689 ports = job.GetenvList("Ports") 690 ) 691 692 switch action { 693 case "-A": 694 nfAction = iptables.Append 695 case "-I": 696 nfAction = iptables.Insert 697 case "-D": 698 nfAction = iptables.Delete 699 default: 700 return job.Errorf("Invalid action '%s' specified", action) 701 } 702 703 ip1 := net.ParseIP(parentIP) 704 if ip1 == nil { 705 return job.Errorf("parent IP '%s' is invalid", parentIP) 706 } 707 ip2 := net.ParseIP(childIP) 708 if ip2 == nil { 709 return job.Errorf("child IP '%s' is invalid", childIP) 710 } 711 712 chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface} 713 for _, p := range ports { 714 port := nat.Port(p) 715 if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil { 716 return job.Error(err) 717 } 718 } 719 return engine.StatusOK 720 }