diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index e0467b6bd7..880eaefef0 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -6,6 +6,7 @@ import ( "net" "os" "strconv" + "strings" "sync" log "github.com/Sirupsen/logrus" @@ -501,35 +502,48 @@ func AllocatePort(job *engine.Job) engine.Status { func LinkContainers(job *engine.Job) engine.Status { var ( action = job.Args[0] + nfAction iptables.Action childIP = job.Getenv("ChildIP") parentIP = job.Getenv("ParentIP") ignoreErrors = job.GetenvBool("IgnoreErrors") ports = job.GetenvList("Ports") + chain = iptables.Chain{} ) - for _, value := range ports { - port := nat.Port(value) - if output, err := iptables.Raw(action, "FORWARD", - "-i", bridgeIface, "-o", bridgeIface, - "-p", port.Proto(), - "-s", parentIP, - "--dport", strconv.Itoa(port.Int()), - "-d", childIP, - "-j", "ACCEPT"); !ignoreErrors && err != nil { - return job.Error(err) - } else if len(output) != 0 { - return job.Errorf("Error toggle iptables forward: %s", output) - } + split := func(p string) (string, string) { + parts := strings.Split(p, "/") + return parts[0], parts[1] + } - if output, err := iptables.Raw(action, "FORWARD", - "-i", bridgeIface, "-o", bridgeIface, - "-p", port.Proto(), - "-s", childIP, - "--sport", strconv.Itoa(port.Int()), - "-d", parentIP, - "-j", "ACCEPT"); !ignoreErrors && err != nil { + switch action { + case "-A": + nfAction = iptables.Append + case "-I": + nfAction = iptables.Insert + case "-D": + nfAction = iptables.Delete + default: + return job.Errorf("Invalid action '%s' specified", action) + } + + ip1 := net.ParseIP(parentIP) + if ip1 == nil { + return job.Errorf("parent IP '%s' is invalid", parentIP) + } + ip2 := net.ParseIP(childIP) + if ip2 == nil { + return job.Errorf("child IP '%s' is invalid", childIP) + } + + chain.Name = "DOCKER" + chain.Bridge = bridgeIface + for _, p := range ports { + portStr, proto := split(p) + port, err := strconv.Atoi(portStr) + if !ignoreErrors && err != nil { + return job.Errorf("port '%s' is invalid", portStr) + } + if err := chain.Link(nfAction, ip1, ip2, port, proto); !ignoreErrors && err != nil { return job.Error(err) - } else if len(output) != 0 { - return job.Errorf("Error toggle iptables forward: %s", output) } } return engine.StatusOK diff --git a/daemon/networkdriver/portmapper/mapper.go b/daemon/networkdriver/portmapper/mapper.go index 4bf8cd142c..9f2ca5a754 100644 --- a/daemon/networkdriver/portmapper/mapper.go +++ b/daemon/networkdriver/portmapper/mapper.go @@ -93,7 +93,7 @@ func Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err er } containerIP, containerPort := getIPAndPort(m.container) - if err := forward(iptables.Add, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { + if err := forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { return nil, err } diff --git a/links/links.go b/links/links.go index fc4d95ab08..ab03eadf5b 100644 --- a/links/links.go +++ b/links/links.go @@ -138,7 +138,8 @@ func (l *Link) getDefaultPort() *nat.Port { } func (l *Link) Enable() error { - if err := l.toggle("-I", false); err != nil { + // -A == iptables append flag + if err := l.toggle("-A", false); err != nil { return err } l.IsEnabled = true @@ -148,6 +149,7 @@ func (l *Link) Enable() error { func (l *Link) Disable() { // We do not care about errors here because the link may not // exist in iptables + // -D == iptables delete flag l.toggle("-D", true) l.IsEnabled = false diff --git a/pkg/iptables/iptables.go b/pkg/iptables/iptables.go index b783347fa3..a7d216a973 100644 --- a/pkg/iptables/iptables.go +++ b/pkg/iptables/iptables.go @@ -15,8 +15,9 @@ import ( type Action string const ( - Add Action = "-A" + Append Action = "-A" Delete Action = "-D" + Insert Action = "-I" ) var ( @@ -54,10 +55,10 @@ func NewChain(name, bridge string) (*Chain, error) { Bridge: bridge, } - if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil { + if err := chain.Prerouting(Append, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil { return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err) } - if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil { + if err := chain.Output(Append, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil { return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err) } return chain, nil @@ -78,7 +79,7 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str // value" by both iptables and ip6tables. daddr = "0/0" } - if output, err := Raw("-t", "nat", fmt.Sprint(action), c.Name, + if output, err := Raw("-t", "nat", string(action), c.Name, "-p", proto, "-d", daddr, "--dport", strconv.Itoa(port), @@ -90,11 +91,13 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str return &ChainError{Chain: "FORWARD", Output: output} } - fAction := action - if fAction == Add { - fAction = "-I" + if action != Delete { + if err := c.createForwardChain(); err != nil { + return err + } } - if output, err := Raw(string(fAction), "FORWARD", + + if output, err := Raw(string(action), c.Name, "!", "-i", c.Bridge, "-o", c.Bridge, "-p", proto, @@ -109,6 +112,39 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str return nil } +func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error { + if action != Delete { + if err := c.createForwardChain(); err != nil { + return err + } + } + if output, err := Raw(string(action), c.Name, + "-i", c.Bridge, "-o", c.Bridge, + "-p", proto, + "-s", ip1.String(), + "--dport", strconv.Itoa(port), + "-d", ip2.String(), + "-j", "ACCEPT"); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Error toggle iptables forward: %s", output) + } + + if output, err := Raw(string(action), c.Name, + "-i", c.Bridge, "-o", c.Bridge, + "-p", proto, + "-s", ip2.String(), + "--dport", strconv.Itoa(port), + "-d", ip1.String(), + "-j", "ACCEPT"); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Error toggle iptables forward: %s", output) + } + + return nil +} + func (c *Chain) Prerouting(action Action, args ...string) error { a := append(nat, fmt.Sprint(action), "PREROUTING") if len(args) > 0 { @@ -199,3 +235,28 @@ func Raw(args ...string) ([]byte, error) { return output, err } + +func (c *Chain) createForwardChain() error { + // Add chain if doesn't exist + if _, err := Raw("-n", "-L", c.Name); err != nil { + output, err := Raw("-N", c.Name) + if err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Error iptables forward: %s", output) + } + } + // Add linking rule if it doesn't exist + if !Exists("FORWARD", + "-o", c.Bridge, + "-j", c.Name) { + if output2, err := Raw(string(Insert), "FORWARD", + "-o", c.Bridge, + "-j", c.Name); err != nil { + return err + } else if len(output2) != 0 { + return fmt.Errorf("Error iptables forward: %s", output2) + } + } + return nil +}