Parcourir la source

Brought in iptables package into libnetwork.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
Jana Radhakrishnan il y a 10 ans
Parent
commit
9714bcac87

+ 1 - 1
libnetwork/drivers/bridge/bridge_test.go

@@ -7,8 +7,8 @@ import (
 	"regexp"
 	"testing"
 
-	"github.com/docker/docker/pkg/iptables"
 	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/pkg/iptables"
 	"github.com/docker/libnetwork/pkg/netlabel"
 	"github.com/vishvananda/netlink"
 )

+ 1 - 1
libnetwork/drivers/bridge/link.go

@@ -5,8 +5,8 @@ import (
 	"net"
 
 	log "github.com/Sirupsen/logrus"
-	"github.com/docker/docker/pkg/iptables"
 	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/pkg/iptables"
 )
 
 type link struct {

+ 1 - 1
libnetwork/drivers/bridge/setup_ip_tables.go

@@ -4,8 +4,8 @@ import (
 	"fmt"
 	"net"
 
-	"github.com/docker/docker/pkg/iptables"
 	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/pkg/iptables"
 )
 
 // DockerChain: DOCKER iptable chain name

+ 1 - 1
libnetwork/drivers/bridge/setup_ip_tables_test.go

@@ -4,8 +4,8 @@ import (
 	"net"
 	"testing"
 
-	"github.com/docker/docker/pkg/iptables"
 	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/pkg/iptables"
 )
 
 const (

+ 169 - 0
libnetwork/pkg/iptables/firewalld.go

@@ -0,0 +1,169 @@
+package iptables
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/godbus/dbus"
+)
+
+// IPV defines the table string
+type IPV string
+
+const (
+	// Iptables point ipv4 table
+	Iptables IPV = "ipv4"
+	// IP6tables point to ipv6 table
+	IP6tables IPV = "ipv6"
+	// Ebtables point to bridge table
+	Ebtables IPV = "eb"
+)
+const (
+	dbusInterface = "org.fedoraproject.FirewallD1"
+	dbusPath      = "/org/fedoraproject/FirewallD1"
+)
+
+// Conn is a connection to firewalld dbus endpoint.
+type Conn struct {
+	sysconn *dbus.Conn
+	sysobj  *dbus.Object
+	signal  chan *dbus.Signal
+}
+
+var (
+	connection       *Conn
+	firewalldRunning bool      // is Firewalld service running
+	onReloaded       []*func() // callbacks when Firewalld has been reloaded
+)
+
+// FirewalldInit initializes firewalld management code.
+func FirewalldInit() {
+	var err error
+
+	connection, err = newConnection()
+
+	if err != nil {
+		logrus.Errorf("Failed to connect to D-Bus system bus: %s", err)
+	}
+	if connection != nil {
+		go signalHandler()
+	}
+
+	firewalldRunning = checkRunning()
+}
+
+// New() establishes a connection to the system bus.
+func newConnection() (*Conn, error) {
+	c := new(Conn)
+	if err := c.initConnection(); err != nil {
+		return nil, err
+	}
+
+	return c, nil
+}
+
+// Innitialize D-Bus connection.
+func (c *Conn) initConnection() error {
+	var err error
+
+	c.sysconn, err = dbus.SystemBus()
+	if err != nil {
+		return err
+	}
+
+	// This never fails, even if the service is not running atm.
+	c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))
+
+	rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'",
+		dbusPath, dbusInterface, dbusInterface)
+	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
+
+	rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'",
+		dbusInterface)
+	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
+
+	c.signal = make(chan *dbus.Signal, 10)
+	c.sysconn.Signal(c.signal)
+
+	return nil
+}
+
+func signalHandler() {
+	for signal := range connection.signal {
+		if strings.Contains(signal.Name, "NameOwnerChanged") {
+			firewalldRunning = checkRunning()
+			dbusConnectionChanged(signal.Body)
+		} else if strings.Contains(signal.Name, "Reloaded") {
+			reloaded()
+		}
+	}
+}
+
+func dbusConnectionChanged(args []interface{}) {
+	name := args[0].(string)
+	oldOwner := args[1].(string)
+	newOwner := args[2].(string)
+
+	if name != dbusInterface {
+		return
+	}
+
+	if len(newOwner) > 0 {
+		connectionEstablished()
+	} else if len(oldOwner) > 0 {
+		connectionLost()
+	}
+}
+
+func connectionEstablished() {
+	reloaded()
+}
+
+func connectionLost() {
+	// Doesn't do anything for now. Libvirt also doesn't react to this.
+}
+
+// call all callbacks
+func reloaded() {
+	for _, pf := range onReloaded {
+		(*pf)()
+	}
+}
+
+// OnReloaded add callback
+func OnReloaded(callback func()) {
+	for _, pf := range onReloaded {
+		if pf == &callback {
+			return
+		}
+	}
+	onReloaded = append(onReloaded, &callback)
+}
+
+// Call some remote method to see whether the service is actually running.
+func checkRunning() bool {
+	var zone string
+	var err error
+
+	if connection != nil {
+		err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
+		logrus.Infof("Firewalld running: %t", err == nil)
+		return err == nil
+	}
+	logrus.Info("Firewalld not running")
+	return false
+}
+
+// Passthrough method simply passes args through to iptables/ip6tables
+func Passthrough(ipv IPV, args ...string) ([]byte, error) {
+	var output string
+
+	logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args)
+	err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output)
+	if output != "" {
+		logrus.Debugf("passthrough output: %s", output)
+	}
+
+	return []byte(output), err
+}

+ 78 - 0
libnetwork/pkg/iptables/firewalld_test.go

@@ -0,0 +1,78 @@
+package iptables
+
+import (
+	"net"
+	"strconv"
+	"testing"
+)
+
+func TestFirewalldInit(t *testing.T) {
+	FirewalldInit()
+}
+
+func TestReloaded(t *testing.T) {
+	var err error
+	var fwdChain *Chain
+
+	fwdChain, err = NewChain("FWD", "lo", Filter)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer fwdChain.Remove()
+
+	// copy-pasted from iptables_test:TestLink
+	ip1 := net.ParseIP("192.168.1.1")
+	ip2 := net.ParseIP("192.168.1.2")
+	port := 1234
+	proto := "tcp"
+
+	err = fwdChain.Link(Append, ip1, ip2, port, proto)
+	if err != nil {
+		t.Fatal(err)
+	} else {
+		// to be re-called again later
+		OnReloaded(func() { fwdChain.Link(Append, ip1, ip2, port, proto) })
+	}
+
+	rule1 := []string{
+		"-i", fwdChain.Bridge,
+		"-o", fwdChain.Bridge,
+		"-p", proto,
+		"-s", ip1.String(),
+		"-d", ip2.String(),
+		"--dport", strconv.Itoa(port),
+		"-j", "ACCEPT"}
+
+	if !Exists(fwdChain.Table, fwdChain.Name, rule1...) {
+		t.Fatalf("rule1 does not exist")
+	}
+
+	// flush all rules
+	fwdChain.Remove()
+
+	reloaded()
+
+	// make sure the rules have been recreated
+	if !Exists(fwdChain.Table, fwdChain.Name, rule1...) {
+		t.Fatalf("rule1 hasn't been recreated")
+	}
+}
+
+func TestPassthrough(t *testing.T) {
+	rule1 := []string{
+		"-i", "lo",
+		"-p", "udp",
+		"--dport", "123",
+		"-j", "ACCEPT"}
+
+	if firewalldRunning {
+		_, err := Passthrough(Iptables, append([]string{"-A"}, rule1...)...)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !Exists(Filter, "INPUT", rule1...) {
+			t.Fatalf("rule1 does not exist")
+		}
+	}
+
+}

+ 321 - 0
libnetwork/pkg/iptables/iptables.go

@@ -0,0 +1,321 @@
+package iptables
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"os/exec"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"github.com/Sirupsen/logrus"
+)
+
+//Action signifies the iptable action.
+type Action string
+
+//Table refers to Nat, Filter or Mangle.
+type Table string
+
+const (
+	//Append appends the rule at the end of the chain.
+	Append Action = "-A"
+	//Delete deletes the rule from the chain.
+	Delete Action = "-D"
+	//Insert inserts the rule at the top of the chain.
+	Insert Action = "-I"
+	//Nat table is used for nat translation rules.
+	Nat Table = "nat"
+	//Filter table is used for filter rules.
+	Filter Table = "filter"
+	//Mangle table is used for mangling the packet.
+	Mangle Table = "mangle"
+)
+
+var (
+	iptablesPath  string
+	supportsXlock = false
+	//ErrIptablesNotFound is returned when the rule is not found.
+	ErrIptablesNotFound = errors.New("Iptables not found")
+)
+
+//Chain defines the iptables chain.
+type Chain struct {
+	Name   string
+	Bridge string
+	Table  Table
+}
+
+//ChainError is returned to represent errors during ip table operation.
+type ChainError struct {
+	Chain  string
+	Output []byte
+}
+
+func (e ChainError) Error() string {
+	return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output))
+}
+
+func initCheck() error {
+
+	if iptablesPath == "" {
+		path, err := exec.LookPath("iptables")
+		if err != nil {
+			return ErrIptablesNotFound
+		}
+		iptablesPath = path
+		supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil
+	}
+	return nil
+}
+
+//NewChain adds a new chain to ip table.
+func NewChain(name, bridge string, table Table) (*Chain, error) {
+	c := &Chain{
+		Name:   name,
+		Bridge: bridge,
+		Table:  table,
+	}
+
+	if string(c.Table) == "" {
+		c.Table = Filter
+	}
+
+	// Add chain if it doesn't exist
+	if _, err := Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil {
+		if output, err := Raw("-t", string(c.Table), "-N", c.Name); err != nil {
+			return nil, err
+		} else if len(output) != 0 {
+			return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output)
+		}
+	}
+
+	switch table {
+	case Nat:
+		preroute := []string{
+			"-m", "addrtype",
+			"--dst-type", "LOCAL"}
+		if !Exists(Nat, "PREROUTING", preroute...) {
+			if err := c.Prerouting(Append, preroute...); err != nil {
+				return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
+			}
+		}
+		output := []string{
+			"-m", "addrtype",
+			"--dst-type", "LOCAL",
+			"!", "--dst", "127.0.0.0/8"}
+		if !Exists(Nat, "OUTPUT", output...) {
+			if err := c.Output(Append, output...); err != nil {
+				return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
+			}
+		}
+	case Filter:
+		link := []string{
+			"-o", c.Bridge,
+			"-j", c.Name}
+		if !Exists(Filter, "FORWARD", link...) {
+			insert := append([]string{string(Insert), "FORWARD"}, link...)
+			if output, err := Raw(insert...); err != nil {
+				return nil, err
+			} else if len(output) != 0 {
+				return nil, fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output)
+			}
+		}
+	}
+	return c, nil
+}
+
+//RemoveExistingChain removes existing chain from the table.
+func RemoveExistingChain(name string, table Table) error {
+	c := &Chain{
+		Name:  name,
+		Table: table,
+	}
+	if string(c.Table) == "" {
+		c.Table = Filter
+	}
+	return c.Remove()
+}
+
+//Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table
+func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error {
+	daddr := ip.String()
+	if ip.IsUnspecified() {
+		// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
+		// want "0.0.0.0/0". "0/0" is correctly interpreted as "any
+		// value" by both iptables and ip6tables.
+		daddr = "0/0"
+	}
+	if output, err := Raw("-t", string(Nat), string(action), c.Name,
+		"-p", proto,
+		"-d", daddr,
+		"--dport", strconv.Itoa(port),
+		"!", "-i", c.Bridge,
+		"-j", "DNAT",
+		"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
+		return err
+	} else if len(output) != 0 {
+		return ChainError{Chain: "FORWARD", Output: output}
+	}
+
+	if output, err := Raw("-t", string(Filter), string(action), c.Name,
+		"!", "-i", c.Bridge,
+		"-o", c.Bridge,
+		"-p", proto,
+		"-d", destAddr,
+		"--dport", strconv.Itoa(destPort),
+		"-j", "ACCEPT"); err != nil {
+		return err
+	} else if len(output) != 0 {
+		return ChainError{Chain: "FORWARD", Output: output}
+	}
+
+	if output, err := Raw("-t", string(Nat), string(action), "POSTROUTING",
+		"-p", proto,
+		"-s", destAddr,
+		"-d", destAddr,
+		"--dport", strconv.Itoa(destPort),
+		"-j", "MASQUERADE"); err != nil {
+		return err
+	} else if len(output) != 0 {
+		return ChainError{Chain: "FORWARD", Output: output}
+	}
+
+	return nil
+}
+
+//Link adds reciprocal ACCEPT rule for two supplied IP addresses.
+// Traffic is allowed from ip1 to ip2 and vice-versa
+func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error {
+	if output, err := Raw("-t", string(Filter), string(action), c.Name,
+		"-i", c.Bridge, "-o", c.Bridge,
+		"-p", proto,
+		"-s", ip1.String(),
+		"-d", ip2.String(),
+		"--dport", strconv.Itoa(port),
+		"-j", "ACCEPT"); err != nil {
+		return err
+	} else if len(output) != 0 {
+		return fmt.Errorf("Error iptables forward: %s", output)
+	}
+	if output, err := Raw("-t", string(Filter), string(action), c.Name,
+		"-i", c.Bridge, "-o", c.Bridge,
+		"-p", proto,
+		"-s", ip2.String(),
+		"-d", ip1.String(),
+		"--sport", strconv.Itoa(port),
+		"-j", "ACCEPT"); err != nil {
+		return err
+	} else if len(output) != 0 {
+		return fmt.Errorf("Error iptables forward: %s", output)
+	}
+	return nil
+}
+
+//Prerouting adds linking rule to nat/PREROUTING chain.
+func (c *Chain) Prerouting(action Action, args ...string) error {
+	a := []string{"-t", string(Nat), string(action), "PREROUTING"}
+	if len(args) > 0 {
+		a = append(a, args...)
+	}
+	if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
+		return err
+	} else if len(output) != 0 {
+		return ChainError{Chain: "PREROUTING", Output: output}
+	}
+	return nil
+}
+
+//Output adds linking rule to an OUTPUT chain
+func (c *Chain) Output(action Action, args ...string) error {
+	a := []string{"-t", string(c.Table), string(action), "OUTPUT"}
+	if len(args) > 0 {
+		a = append(a, args...)
+	}
+	if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
+		return err
+	} else if len(output) != 0 {
+		return ChainError{Chain: "OUTPUT", Output: output}
+	}
+	return nil
+}
+
+// Remove removes the chain
+func (c *Chain) Remove() error {
+	// Ignore errors - This could mean the chains were never set up
+	if c.Table == Nat {
+		c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
+		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
+		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
+
+		c.Prerouting(Delete)
+		c.Output(Delete)
+	}
+	Raw("-t", string(c.Table), "-F", c.Name)
+	Raw("-t", string(c.Table), "-X", c.Name)
+	return nil
+}
+
+//Exists checks if a rule exists
+func Exists(table Table, chain string, rule ...string) bool {
+	if string(table) == "" {
+		table = Filter
+	}
+
+	// iptables -C, --check option was added in v.1.4.11
+	// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt
+
+	// try -C
+	// if exit status is 0 then return true, the rule exists
+	if _, err := Raw(append([]string{
+		"-t", string(table), "-C", chain}, rule...)...); err == nil {
+		return true
+	}
+
+	// parse "iptables -S" for the rule (this checks rules in a specific chain
+	// in a specific table)
+	ruleString := strings.Join(rule, " ")
+	existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output()
+
+	// regex to replace ips in rule
+	// because MASQUERADE rule will not be exactly what was passed
+	re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}`)
+
+	return strings.Contains(
+		re.ReplaceAllString(string(existingRules), "?"),
+		re.ReplaceAllString(ruleString, "?"),
+	)
+}
+
+//Raw calls 'iptables' system command, passing supplied arguments
+func Raw(args ...string) ([]byte, error) {
+	if firewalldRunning {
+		output, err := Passthrough(Iptables, args...)
+		if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") {
+			return output, err
+		}
+
+	}
+
+	if err := initCheck(); err != nil {
+		return nil, err
+	}
+	if supportsXlock {
+		args = append([]string{"--wait"}, args...)
+	}
+
+	logrus.Debugf("%s, %v", iptablesPath, args)
+
+	output, err := exec.Command(iptablesPath, args...).CombinedOutput()
+	if err != nil {
+		return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
+	}
+
+	// ignore iptables' message about xtables lock
+	if strings.Contains(string(output), "waiting for it to exit") {
+		output = []byte("")
+	}
+
+	return output, err
+}

+ 198 - 0
libnetwork/pkg/iptables/iptables_test.go

@@ -0,0 +1,198 @@
+package iptables
+
+import (
+	"net"
+	"os/exec"
+	"strconv"
+	"strings"
+	"testing"
+)
+
+const chainName = "DOCKERTEST"
+
+var natChain *Chain
+var filterChain *Chain
+
+func TestNewChain(t *testing.T) {
+	var err error
+
+	natChain, err = NewChain(chainName, "lo", Nat)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	filterChain, err = NewChain(chainName, "lo", Filter)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestForward(t *testing.T) {
+	ip := net.ParseIP("192.168.1.1")
+	port := 1234
+	dstAddr := "172.17.0.1"
+	dstPort := 4321
+	proto := "tcp"
+
+	err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	dnatRule := []string{
+		"!", "-i", filterChain.Bridge,
+		"-d", ip.String(),
+		"-p", proto,
+		"--dport", strconv.Itoa(port),
+		"-j", "DNAT",
+		"--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
+	}
+
+	if !Exists(natChain.Table, natChain.Name, dnatRule...) {
+		t.Fatalf("DNAT rule does not exist")
+	}
+
+	filterRule := []string{
+		"!", "-i", filterChain.Bridge,
+		"-o", filterChain.Bridge,
+		"-d", dstAddr,
+		"-p", proto,
+		"--dport", strconv.Itoa(dstPort),
+		"-j", "ACCEPT",
+	}
+
+	if !Exists(filterChain.Table, filterChain.Name, filterRule...) {
+		t.Fatalf("filter rule does not exist")
+	}
+
+	masqRule := []string{
+		"-d", dstAddr,
+		"-s", dstAddr,
+		"-p", proto,
+		"--dport", strconv.Itoa(dstPort),
+		"-j", "MASQUERADE",
+	}
+
+	if !Exists(natChain.Table, "POSTROUTING", masqRule...) {
+		t.Fatalf("MASQUERADE rule does not exist")
+	}
+}
+
+func TestLink(t *testing.T) {
+	var err error
+
+	ip1 := net.ParseIP("192.168.1.1")
+	ip2 := net.ParseIP("192.168.1.2")
+	port := 1234
+	proto := "tcp"
+
+	err = filterChain.Link(Append, ip1, ip2, port, proto)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rule1 := []string{
+		"-i", filterChain.Bridge,
+		"-o", filterChain.Bridge,
+		"-p", proto,
+		"-s", ip1.String(),
+		"-d", ip2.String(),
+		"--dport", strconv.Itoa(port),
+		"-j", "ACCEPT"}
+
+	if !Exists(filterChain.Table, filterChain.Name, rule1...) {
+		t.Fatalf("rule1 does not exist")
+	}
+
+	rule2 := []string{
+		"-i", filterChain.Bridge,
+		"-o", filterChain.Bridge,
+		"-p", proto,
+		"-s", ip2.String(),
+		"-d", ip1.String(),
+		"--sport", strconv.Itoa(port),
+		"-j", "ACCEPT"}
+
+	if !Exists(filterChain.Table, filterChain.Name, rule2...) {
+		t.Fatalf("rule2 does not exist")
+	}
+}
+
+func TestPrerouting(t *testing.T) {
+	args := []string{
+		"-i", "lo",
+		"-d", "192.168.1.1"}
+
+	err := natChain.Prerouting(Insert, args...)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rule := []string{
+		"-j", natChain.Name}
+
+	rule = append(rule, args...)
+
+	if !Exists(natChain.Table, "PREROUTING", rule...) {
+		t.Fatalf("rule does not exist")
+	}
+
+	delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...)
+	if _, err = Raw(delRule...); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestOutput(t *testing.T) {
+	args := []string{
+		"-o", "lo",
+		"-d", "192.168.1.1"}
+
+	err := natChain.Output(Insert, args...)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rule := []string{
+		"-j", natChain.Name}
+
+	rule = append(rule, args...)
+
+	if !Exists(natChain.Table, "OUTPUT", rule...) {
+		t.Fatalf("rule does not exist")
+	}
+
+	delRule := append([]string{"-D", "OUTPUT", "-t",
+		string(natChain.Table)}, rule...)
+	if _, err = Raw(delRule...); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestCleanup(t *testing.T) {
+	var err error
+	var rules []byte
+
+	// Cleanup filter/FORWARD first otherwise output of iptables-save is dirty
+	link := []string{"-t", string(filterChain.Table),
+		string(Delete), "FORWARD",
+		"-o", filterChain.Bridge,
+		"-j", filterChain.Name}
+	if _, err = Raw(link...); err != nil {
+		t.Fatal(err)
+	}
+	filterChain.Remove()
+
+	err = RemoveExistingChain(chainName, Nat)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rules, err = exec.Command("iptables-save").Output()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if strings.Contains(string(rules), chainName) {
+		t.Fatalf("Removing chain failed. %s found in iptables-save", chainName)
+	}
+}

+ 1 - 1
libnetwork/portmapper/mapper.go

@@ -7,7 +7,7 @@ import (
 	"sync"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/pkg/iptables"
+	"github.com/docker/libnetwork/pkg/iptables"
 	"github.com/docker/libnetwork/pkg/portallocator"
 )
 

+ 1 - 1
libnetwork/portmapper/mapper_test.go

@@ -4,7 +4,7 @@ import (
 	"net"
 	"testing"
 
-	"github.com/docker/docker/pkg/iptables"
+	"github.com/docker/libnetwork/pkg/iptables"
 )
 
 func init() {