github.com/jjyr/docker@v1.5.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 log.Infof("enableIPv6 = %t", enableIPv6) 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{"POSTROUTING", "-t", "nat", "-s", addr.String(), "!", "-o", bridgeIface, "-j", "MASQUERADE"} 289 290 if !iptables.Exists(natArgs...) { 291 if output, err := iptables.Raw(append([]string{"-I"}, 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{"FORWARD", "-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"}, acceptArgs...)...) 307 308 if !iptables.Exists(dropArgs...) { 309 log.Debugf("Disable inter-container communication") 310 if output, err := iptables.Raw(append([]string{"-I"}, 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"}, dropArgs...)...) 318 319 if !iptables.Exists(acceptArgs...) { 320 log.Debugf("Enable inter-container communication") 321 if output, err := iptables.Raw(append([]string{"-I"}, 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{"FORWARD", "-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"} 331 if !iptables.Exists(outgoingArgs...) { 332 if output, err := iptables.Raw(append([]string{"-I"}, 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{"FORWARD", "-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"} 341 342 if !iptables.Exists(existingArgs...) { 343 if output, err := iptables.Raw(append([]string{"-I"}, 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 if requestedIP != nil { 513 ip, err = ipallocator.RequestIP(bridgeIPv4Network, requestedIP) 514 } else { 515 ip, err = ipallocator.RequestIP(bridgeIPv4Network, nil) 516 } 517 if err != nil { 518 return job.Error(err) 519 } 520 521 // If no explicit mac address was given, generate a random one. 522 if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil { 523 mac = generateMacAddr(ip) 524 } 525 526 if globalIPv6Network != nil { 527 // if globalIPv6Network Size is at least a /80 subnet generate IPv6 address from MAC address 528 netmask_ones, _ := globalIPv6Network.Mask.Size() 529 if requestedIPv6 == nil && netmask_ones <= 80 { 530 requestedIPv6 = globalIPv6Network.IP 531 for i, h := range mac { 532 requestedIPv6[i+10] = h 533 } 534 } 535 536 globalIPv6, err = ipallocator.RequestIP(globalIPv6Network, requestedIPv6) 537 if err != nil { 538 log.Errorf("Allocator: RequestIP v6: %s", err.Error()) 539 return job.Error(err) 540 } 541 log.Infof("Allocated IPv6 %s", globalIPv6) 542 } 543 544 out := engine.Env{} 545 out.Set("IP", ip.String()) 546 out.Set("Mask", bridgeIPv4Network.Mask.String()) 547 out.Set("Gateway", bridgeIPv4Network.IP.String()) 548 out.Set("MacAddress", mac.String()) 549 out.Set("Bridge", bridgeIface) 550 551 size, _ := bridgeIPv4Network.Mask.Size() 552 out.SetInt("IPPrefixLen", size) 553 554 // if linklocal IPv6 555 localIPv6Net, err := linkLocalIPv6FromMac(mac.String()) 556 if err != nil { 557 return job.Error(err) 558 } 559 localIPv6, _, _ := net.ParseCIDR(localIPv6Net) 560 out.Set("LinkLocalIPv6", localIPv6.String()) 561 out.Set("MacAddress", mac.String()) 562 563 if globalIPv6Network != nil { 564 out.Set("GlobalIPv6", globalIPv6.String()) 565 sizev6, _ := globalIPv6Network.Mask.Size() 566 out.SetInt("GlobalIPv6PrefixLen", sizev6) 567 out.Set("IPv6Gateway", bridgeIPv6Addr.String()) 568 } 569 570 currentInterfaces.Set(id, &networkInterface{ 571 IP: ip, 572 IPv6: globalIPv6, 573 }) 574 575 out.WriteTo(job.Stdout) 576 577 return engine.StatusOK 578 } 579 580 // release an interface for a select ip 581 func Release(job *engine.Job) engine.Status { 582 var ( 583 id = job.Args[0] 584 containerInterface = currentInterfaces.Get(id) 585 ) 586 587 if containerInterface == nil { 588 return job.Errorf("No network information to release for %s", id) 589 } 590 591 for _, nat := range containerInterface.PortMappings { 592 if err := portmapper.Unmap(nat); err != nil { 593 log.Infof("Unable to unmap port %s: %s", nat, err) 594 } 595 } 596 597 if err := ipallocator.ReleaseIP(bridgeIPv4Network, containerInterface.IP); err != nil { 598 log.Infof("Unable to release IPv4 %s", err) 599 } 600 if globalIPv6Network != nil { 601 if err := ipallocator.ReleaseIP(globalIPv6Network, containerInterface.IPv6); err != nil { 602 log.Infof("Unable to release IPv6 %s", err) 603 } 604 } 605 return engine.StatusOK 606 } 607 608 // Allocate an external port and map it to the interface 609 func AllocatePort(job *engine.Job) engine.Status { 610 var ( 611 err error 612 613 ip = defaultBindingIP 614 id = job.Args[0] 615 hostIP = job.Getenv("HostIP") 616 hostPort = job.GetenvInt("HostPort") 617 containerPort = job.GetenvInt("ContainerPort") 618 proto = job.Getenv("Proto") 619 network = currentInterfaces.Get(id) 620 ) 621 622 if hostIP != "" { 623 ip = net.ParseIP(hostIP) 624 if ip == nil { 625 return job.Errorf("Bad parameter: invalid host ip %s", hostIP) 626 } 627 } 628 629 // host ip, proto, and host port 630 var container net.Addr 631 switch proto { 632 case "tcp": 633 container = &net.TCPAddr{IP: network.IP, Port: containerPort} 634 case "udp": 635 container = &net.UDPAddr{IP: network.IP, Port: containerPort} 636 default: 637 return job.Errorf("unsupported address type %s", proto) 638 } 639 640 // 641 // Try up to 10 times to get a port that's not already allocated. 642 // 643 // In the event of failure to bind, return the error that portmapper.Map 644 // yields. 645 // 646 647 var host net.Addr 648 for i := 0; i < MaxAllocatedPortAttempts; i++ { 649 if host, err = portmapper.Map(container, ip, hostPort); err == nil { 650 break 651 } 652 // There is no point in immediately retrying to map an explicitly 653 // chosen port. 654 if hostPort != 0 { 655 job.Logf("Failed to allocate and map port %d: %s", hostPort, err) 656 break 657 } 658 job.Logf("Failed to allocate and map port: %s, retry: %d", err, i+1) 659 } 660 661 if err != nil { 662 return job.Error(err) 663 } 664 665 network.PortMappings = append(network.PortMappings, host) 666 667 out := engine.Env{} 668 switch netAddr := host.(type) { 669 case *net.TCPAddr: 670 out.Set("HostIP", netAddr.IP.String()) 671 out.SetInt("HostPort", netAddr.Port) 672 case *net.UDPAddr: 673 out.Set("HostIP", netAddr.IP.String()) 674 out.SetInt("HostPort", netAddr.Port) 675 } 676 if _, err := out.WriteTo(job.Stdout); err != nil { 677 return job.Error(err) 678 } 679 680 return engine.StatusOK 681 } 682 683 func LinkContainers(job *engine.Job) engine.Status { 684 var ( 685 action = job.Args[0] 686 nfAction iptables.Action 687 childIP = job.Getenv("ChildIP") 688 parentIP = job.Getenv("ParentIP") 689 ignoreErrors = job.GetenvBool("IgnoreErrors") 690 ports = job.GetenvList("Ports") 691 ) 692 693 switch action { 694 case "-A": 695 nfAction = iptables.Append 696 case "-I": 697 nfAction = iptables.Insert 698 case "-D": 699 nfAction = iptables.Delete 700 default: 701 return job.Errorf("Invalid action '%s' specified", action) 702 } 703 704 ip1 := net.ParseIP(parentIP) 705 if ip1 == nil { 706 return job.Errorf("parent IP '%s' is invalid", parentIP) 707 } 708 ip2 := net.ParseIP(childIP) 709 if ip2 == nil { 710 return job.Errorf("child IP '%s' is invalid", childIP) 711 } 712 713 chain := iptables.Chain{Name: "DOCKER", Bridge: bridgeIface} 714 for _, p := range ports { 715 port := nat.Port(p) 716 if err := chain.Link(nfAction, ip1, ip2, port.Int(), port.Proto()); !ignoreErrors && err != nil { 717 return job.Error(err) 718 } 719 } 720 return engine.StatusOK 721 }