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