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