Browse code

Vendor in Libnetwork changes

Bring in changes from https://github.com/moby/libnetwork/pull/2572 to moby

Signed-off-by: Arko Dasgupta <arko.dasgupta@docker.com>

Arko Dasgupta authored on 2020/10/30 06:19:11
Showing 14 changed files
... ...
@@ -3,7 +3,7 @@
3 3
 # LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
4 4
 # updating the binary version, consider updating github.com/docker/libnetwork
5 5
 # in vendor.conf accordingly
6
-: "${LIBNETWORK_COMMIT:=d0951081b35fa4216fc4f0064bf065beeb55a74b}"
6
+: "${LIBNETWORK_COMMIT:=d511c60c5c23e6753631244f271a1ec6097254a5}"
7 7
 
8 8
 install_proxy() {
9 9
 	case "$1" in
... ...
@@ -40,7 +40,7 @@ github.com/grpc-ecosystem/go-grpc-middleware        3c51f7f332123e8be5a157c0802a
40 40
 # libnetwork
41 41
 
42 42
 # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy.installer accordingly
43
-github.com/docker/libnetwork                        d0951081b35fa4216fc4f0064bf065beeb55a74b
43
+github.com/docker/libnetwork                        d511c60c5c23e6753631244f271a1ec6097254a5 
44 44
 github.com/docker/go-events                         e31b211e4f1cd09aa76fe4ac244571fab96ae47f
45 45
 github.com/armon/go-radix                           e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
46 46
 github.com/armon/go-metrics                         eb0af217e5e9747e41dd5303755356b62d28e3ec
... ...
@@ -57,6 +57,7 @@ type iptablesCleanFuncs []iptableCleanFunc
57 57
 type configuration struct {
58 58
 	EnableIPForwarding  bool
59 59
 	EnableIPTables      bool
60
+	EnableIP6Tables     bool
60 61
 	EnableUserlandProxy bool
61 62
 	UserlandProxyPath   string
62 63
 }
... ...
@@ -133,22 +134,27 @@ type bridgeNetwork struct {
133 133
 	config        *networkConfiguration
134 134
 	endpoints     map[string]*bridgeEndpoint // key: endpoint id
135 135
 	portMapper    *portmapper.PortMapper
136
+	portMapperV6  *portmapper.PortMapper
136 137
 	driver        *driver // The network's driver
137 138
 	iptCleanFuncs iptablesCleanFuncs
138 139
 	sync.Mutex
139 140
 }
140 141
 
141 142
 type driver struct {
142
-	config          *configuration
143
-	network         *bridgeNetwork
144
-	natChain        *iptables.ChainInfo
145
-	filterChain     *iptables.ChainInfo
146
-	isolationChain1 *iptables.ChainInfo
147
-	isolationChain2 *iptables.ChainInfo
148
-	networks        map[string]*bridgeNetwork
149
-	store           datastore.DataStore
150
-	nlh             *netlink.Handle
151
-	configNetwork   sync.Mutex
143
+	config            *configuration
144
+	network           *bridgeNetwork
145
+	natChain          *iptables.ChainInfo
146
+	filterChain       *iptables.ChainInfo
147
+	isolationChain1   *iptables.ChainInfo
148
+	isolationChain2   *iptables.ChainInfo
149
+	natChainV6        *iptables.ChainInfo
150
+	filterChainV6     *iptables.ChainInfo
151
+	isolationChain1V6 *iptables.ChainInfo
152
+	isolationChain2V6 *iptables.ChainInfo
153
+	networks          map[string]*bridgeNetwork
154
+	store             datastore.DataStore
155
+	nlh               *netlink.Handle
156
+	configNetwork     sync.Mutex
152 157
 	sync.Mutex
153 158
 }
154 159
 
... ...
@@ -277,7 +283,7 @@ func (n *bridgeNetwork) registerIptCleanFunc(clean iptableCleanFunc) {
277 277
 	n.iptCleanFuncs = append(n.iptCleanFuncs, clean)
278 278
 }
279 279
 
280
-func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
280
+func (n *bridgeNetwork) getDriverChains(version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
281 281
 	n.Lock()
282 282
 	defer n.Unlock()
283 283
 
... ...
@@ -285,6 +291,10 @@ func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainI
285 285
 		return nil, nil, nil, nil, types.BadRequestErrorf("no driver found")
286 286
 	}
287 287
 
288
+	if version == iptables.IPv6 {
289
+		return n.driver.natChainV6, n.driver.filterChainV6, n.driver.isolationChain1V6, n.driver.isolationChain2V6, nil
290
+	}
291
+
288 292
 	return n.driver.natChain, n.driver.filterChain, n.driver.isolationChain1, n.driver.isolationChain2, nil
289 293
 }
290 294
 
... ...
@@ -323,17 +333,31 @@ func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) err
323 323
 	}
324 324
 
325 325
 	// Install the rules to isolate this network against each of the other networks
326
-	return setINC(thisConfig.BridgeName, enable)
326
+	if n.driver.config.EnableIP6Tables {
327
+		err := setINC(iptables.IPv6, thisConfig.BridgeName, enable)
328
+		if err != nil {
329
+			return err
330
+		}
331
+	}
332
+
333
+	if n.driver.config.EnableIPTables {
334
+		return setINC(iptables.IPv4, thisConfig.BridgeName, enable)
335
+	}
336
+	return nil
327 337
 }
328 338
 
329 339
 func (d *driver) configure(option map[string]interface{}) error {
330 340
 	var (
331
-		config          *configuration
332
-		err             error
333
-		natChain        *iptables.ChainInfo
334
-		filterChain     *iptables.ChainInfo
335
-		isolationChain1 *iptables.ChainInfo
336
-		isolationChain2 *iptables.ChainInfo
341
+		config            *configuration
342
+		err               error
343
+		natChain          *iptables.ChainInfo
344
+		filterChain       *iptables.ChainInfo
345
+		isolationChain1   *iptables.ChainInfo
346
+		isolationChain2   *iptables.ChainInfo
347
+		natChainV6        *iptables.ChainInfo
348
+		filterChainV6     *iptables.ChainInfo
349
+		isolationChain1V6 *iptables.ChainInfo
350
+		isolationChain2V6 *iptables.ChainInfo
337 351
 	)
338 352
 
339 353
 	genericData, ok := option[netlabel.GenericData]
... ...
@@ -354,23 +378,46 @@ func (d *driver) configure(option map[string]interface{}) error {
354 354
 		return &ErrInvalidDriverConfig{}
355 355
 	}
356 356
 
357
-	if config.EnableIPTables {
357
+	if config.EnableIPTables || config.EnableIP6Tables {
358 358
 		if _, err := os.Stat("/proc/sys/net/bridge"); err != nil {
359 359
 			if out, err := exec.Command("modprobe", "-va", "bridge", "br_netfilter").CombinedOutput(); err != nil {
360 360
 				logrus.Warnf("Running modprobe bridge br_netfilter failed with message: %s, error: %v", out, err)
361 361
 			}
362 362
 		}
363
-		removeIPChains()
364
-		natChain, filterChain, isolationChain1, isolationChain2, err = setupIPChains(config)
363
+	}
364
+
365
+	if config.EnableIPTables {
366
+		removeIPChains(iptables.IPv4)
367
+
368
+		natChain, filterChain, isolationChain1, isolationChain2, err = setupIPChains(config, iptables.IPv4)
369
+		if err != nil {
370
+			return err
371
+		}
372
+
373
+		// Make sure on firewall reload, first thing being re-played is chains creation
374
+		iptables.OnReloaded(func() {
375
+			logrus.Debugf("Recreating iptables chains on firewall reload")
376
+			setupIPChains(config, iptables.IPv4)
377
+		})
378
+	}
379
+
380
+	if config.EnableIP6Tables {
381
+		removeIPChains(iptables.IPv6)
382
+
383
+		natChainV6, filterChainV6, isolationChain1V6, isolationChain2V6, err = setupIPChains(config, iptables.IPv6)
365 384
 		if err != nil {
366 385
 			return err
367 386
 		}
387
+
368 388
 		// Make sure on firewall reload, first thing being re-played is chains creation
369
-		iptables.OnReloaded(func() { logrus.Debugf("Recreating iptables chains on firewall reload"); setupIPChains(config) })
389
+		iptables.OnReloaded(func() {
390
+			logrus.Debugf("Recreating ip6tables chains on firewall reload")
391
+			setupIPChains(config, iptables.IPv6)
392
+		})
370 393
 	}
371 394
 
372 395
 	if config.EnableIPForwarding {
373
-		err = setupIPForwarding(config.EnableIPTables)
396
+		err = setupIPForwarding(config.EnableIPTables, config.EnableIP6Tables)
374 397
 		if err != nil {
375 398
 			logrus.Warn(err)
376 399
 			return err
... ...
@@ -382,6 +429,10 @@ func (d *driver) configure(option map[string]interface{}) error {
382 382
 	d.filterChain = filterChain
383 383
 	d.isolationChain1 = isolationChain1
384 384
 	d.isolationChain2 = isolationChain2
385
+	d.natChainV6 = natChainV6
386
+	d.filterChainV6 = filterChainV6
387
+	d.isolationChain1V6 = isolationChain1V6
388
+	d.isolationChain2V6 = isolationChain2V6
385 389
 	d.config = config
386 390
 	d.Unlock()
387 391
 
... ...
@@ -644,12 +695,13 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) {
644 644
 
645 645
 	// Create and set network handler in driver
646 646
 	network := &bridgeNetwork{
647
-		id:         config.ID,
648
-		endpoints:  make(map[string]*bridgeEndpoint),
649
-		config:     config,
650
-		portMapper: portmapper.New(d.config.UserlandProxyPath),
651
-		bridge:     bridgeIface,
652
-		driver:     d,
647
+		id:           config.ID,
648
+		endpoints:    make(map[string]*bridgeEndpoint),
649
+		config:       config,
650
+		portMapper:   portmapper.New(d.config.UserlandProxyPath),
651
+		portMapperV6: portmapper.New(d.config.UserlandProxyPath),
652
+		bridge:       bridgeIface,
653
+		driver:       d,
653 654
 	}
654 655
 
655 656
 	d.Lock()
... ...
@@ -724,11 +776,16 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) {
724 724
 		{!d.config.EnableUserlandProxy, setupLoopbackAddressesRouting},
725 725
 
726 726
 		// Setup IPTables.
727
-		{d.config.EnableIPTables, network.setupIPTables},
727
+		{d.config.EnableIPTables, network.setupIP4Tables},
728
+
729
+		// Setup IP6Tables.
730
+		{d.config.EnableIP6Tables, network.setupIP6Tables},
728 731
 
729 732
 		//We want to track firewalld configuration so that
730 733
 		//if it is started/reloaded, the rules can be applied correctly
731 734
 		{d.config.EnableIPTables, network.setupFirewalld},
735
+		// same for IPv6
736
+		{d.config.EnableIP6Tables, network.setupFirewalld6},
732 737
 
733 738
 		// Setup DefaultGatewayIPv4
734 739
 		{config.DefaultGatewayIPv4 != nil, setupGatewayIPv4},
... ...
@@ -12,7 +12,8 @@ import (
12 12
 )
13 13
 
14 14
 var (
15
-	defaultBindingIP = net.IPv4(0, 0, 0, 0)
15
+	defaultBindingIP   = net.IPv4(0, 0, 0, 0)
16
+	defaultBindingIPV6 = net.ParseIP("::")
16 17
 )
17 18
 
18 19
 func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
... ...
@@ -25,7 +26,25 @@ func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, u
25 25
 		defHostIP = reqDefBindIP
26 26
 	}
27 27
 
28
-	return n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled)
28
+	// IPv4 port binding including user land proxy
29
+	pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled)
30
+	if err != nil {
31
+		return nil, err
32
+	}
33
+
34
+	// IPv6 port binding excluding user land proxy
35
+	if n.driver.config.EnableIP6Tables && ep.addrv6 != nil {
36
+		// TODO IPv6 custom default binding IP
37
+		pbv6, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addrv6.IP, defaultBindingIPV6, false)
38
+		if err != nil {
39
+			// ensure we clear the previous allocated IPv4 ports
40
+			n.releasePortsInternal(pb)
41
+			return nil, err
42
+		}
43
+
44
+		pb = append(pb, pbv6...)
45
+	}
46
+	return pb, nil
29 47
 }
30 48
 
31 49
 func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
... ...
@@ -69,9 +88,15 @@ func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHos
69 69
 		return err
70 70
 	}
71 71
 
72
+	portmapper := n.portMapper
73
+
74
+	if containerIP.To4() == nil {
75
+		portmapper = n.portMapperV6
76
+	}
77
+
72 78
 	// Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
73 79
 	for i := 0; i < maxAllocatePortAttempts; i++ {
74
-		if host, err = n.portMapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil {
80
+		if host, err = portmapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil {
75 81
 			break
76 82
 		}
77 83
 		// There is no point in immediately retrying to map an explicitly chosen port.
... ...
@@ -128,5 +153,12 @@ func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error {
128 128
 	if err != nil {
129 129
 		return err
130 130
 	}
131
-	return n.portMapper.Unmap(host)
131
+
132
+	portmapper := n.portMapper
133
+
134
+	if bnd.HostIP.To4() == nil {
135
+		portmapper = n.portMapperV6
136
+	}
137
+
138
+	return portmapper.Unmap(host)
132 139
 }
... ...
@@ -13,8 +13,23 @@ func (n *bridgeNetwork) setupFirewalld(config *networkConfiguration, i *bridgeIn
13 13
 		return IPTableCfgError(config.BridgeName)
14 14
 	}
15 15
 
16
-	iptables.OnReloaded(func() { n.setupIPTables(config, i) })
16
+	iptables.OnReloaded(func() { n.setupIP4Tables(config, i) })
17 17
 	iptables.OnReloaded(n.portMapper.ReMapAll)
18
+	return nil
19
+}
20
+
21
+func (n *bridgeNetwork) setupFirewalld6(config *networkConfiguration, i *bridgeInterface) error {
22
+	d := n.driver
23
+	d.Lock()
24
+	driverConfig := d.config
25
+	d.Unlock()
26
+
27
+	// Sanity check.
28
+	if !driverConfig.EnableIP6Tables {
29
+		return IPTableCfgError(config.BridgeName)
30
+	}
18 31
 
32
+	iptables.OnReloaded(func() { n.setupIP6Tables(config, i) })
33
+	iptables.OnReloaded(n.portMapperV6.ReMapAll)
19 34
 	return nil
20 35
 }
... ...
@@ -21,7 +21,7 @@ func configureIPForwarding(enable bool) error {
21 21
 	return ioutil.WriteFile(ipv4ForwardConf, []byte{val, '\n'}, ipv4ForwardConfPerm)
22 22
 }
23 23
 
24
-func setupIPForwarding(enableIPTables bool) error {
24
+func setupIPForwarding(enableIPTables bool, enableIP6Tables bool) error {
25 25
 	// Get current IPv4 forward setup
26 26
 	ipv4ForwardData, err := ioutil.ReadFile(ipv4ForwardConf)
27 27
 	if err != nil {
... ...
@@ -36,21 +36,36 @@ func setupIPForwarding(enableIPTables bool) error {
36 36
 		}
37 37
 		// When enabling ip_forward set the default policy on forward chain to
38 38
 		// drop only if the daemon option iptables is not set to false.
39
-		if !enableIPTables {
40
-			return nil
41
-		}
42
-		if err := iptables.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil {
43
-			if err := configureIPForwarding(false); err != nil {
44
-				logrus.Errorf("Disabling IP forwarding failed, %v", err)
39
+		if enableIPTables {
40
+			iptable := iptables.GetIptable(iptables.IPv4)
41
+			if err := iptable.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil {
42
+				if err := configureIPForwarding(false); err != nil {
43
+					logrus.Errorf("Disabling IP forwarding failed, %v", err)
44
+				}
45
+				return err
45 46
 			}
46
-			return err
47
+			iptables.OnReloaded(func() {
48
+				logrus.Debug("Setting the default DROP policy on firewall reload")
49
+				if err := iptable.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil {
50
+					logrus.Warnf("Setting the default DROP policy on firewall reload failed, %v", err)
51
+				}
52
+			})
53
+		}
54
+	}
55
+
56
+	// add only iptables rules - forwarding is handled by setupIPv6Forwarding in setup_ipv6
57
+	if enableIP6Tables {
58
+		iptable := iptables.GetIptable(iptables.IPv6)
59
+		if err := iptable.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil {
60
+			logrus.Warnf("Setting the default DROP policy on firewall reload failed, %v", err)
47 61
 		}
48 62
 		iptables.OnReloaded(func() {
49 63
 			logrus.Debug("Setting the default DROP policy on firewall reload")
50
-			if err := iptables.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil {
64
+			if err := iptable.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil {
51 65
 				logrus.Warnf("Setting the default DROP policy on firewall reload failed, %v", err)
52 66
 			}
53 67
 		})
54 68
 	}
69
+
55 70
 	return nil
56 71
 }
... ...
@@ -26,7 +26,7 @@ const (
26 26
 	IsolationChain2 = "DOCKER-ISOLATION-STAGE-2"
27 27
 )
28 28
 
29
-func setupIPChains(config *configuration) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
29
+func setupIPChains(config *configuration, version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
30 30
 	// Sanity check.
31 31
 	if config.EnableIPTables == false {
32 32
 		return nil, nil, nil, nil, errors.New("cannot create new chains, EnableIPTable is disabled")
... ...
@@ -34,129 +34,157 @@ func setupIPChains(config *configuration) (*iptables.ChainInfo, *iptables.ChainI
34 34
 
35 35
 	hairpinMode := !config.EnableUserlandProxy
36 36
 
37
-	natChain, err := iptables.NewChain(DockerChain, iptables.Nat, hairpinMode)
37
+	iptable := iptables.GetIptable(version)
38
+
39
+	natChain, err := iptable.NewChain(DockerChain, iptables.Nat, hairpinMode)
38 40
 	if err != nil {
39 41
 		return nil, nil, nil, nil, fmt.Errorf("failed to create NAT chain %s: %v", DockerChain, err)
40 42
 	}
41 43
 	defer func() {
42 44
 		if err != nil {
43
-			if err := iptables.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
45
+			if err := iptable.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
44 46
 				logrus.Warnf("failed on removing iptables NAT chain %s on cleanup: %v", DockerChain, err)
45 47
 			}
46 48
 		}
47 49
 	}()
48 50
 
49
-	filterChain, err := iptables.NewChain(DockerChain, iptables.Filter, false)
51
+	filterChain, err := iptable.NewChain(DockerChain, iptables.Filter, false)
50 52
 	if err != nil {
51 53
 		return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER chain %s: %v", DockerChain, err)
52 54
 	}
53 55
 	defer func() {
54 56
 		if err != nil {
55
-			if err := iptables.RemoveExistingChain(DockerChain, iptables.Filter); err != nil {
57
+			if err := iptable.RemoveExistingChain(DockerChain, iptables.Filter); err != nil {
56 58
 				logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", DockerChain, err)
57 59
 			}
58 60
 		}
59 61
 	}()
60 62
 
61
-	isolationChain1, err := iptables.NewChain(IsolationChain1, iptables.Filter, false)
63
+	isolationChain1, err := iptable.NewChain(IsolationChain1, iptables.Filter, false)
62 64
 	if err != nil {
63 65
 		return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
64 66
 	}
65 67
 	defer func() {
66 68
 		if err != nil {
67
-			if err := iptables.RemoveExistingChain(IsolationChain1, iptables.Filter); err != nil {
69
+			if err := iptable.RemoveExistingChain(IsolationChain1, iptables.Filter); err != nil {
68 70
 				logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain1, err)
69 71
 			}
70 72
 		}
71 73
 	}()
72 74
 
73
-	isolationChain2, err := iptables.NewChain(IsolationChain2, iptables.Filter, false)
75
+	isolationChain2, err := iptable.NewChain(IsolationChain2, iptables.Filter, false)
74 76
 	if err != nil {
75 77
 		return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
76 78
 	}
77 79
 	defer func() {
78 80
 		if err != nil {
79
-			if err := iptables.RemoveExistingChain(IsolationChain2, iptables.Filter); err != nil {
81
+			if err := iptable.RemoveExistingChain(IsolationChain2, iptables.Filter); err != nil {
80 82
 				logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain2, err)
81 83
 			}
82 84
 		}
83 85
 	}()
84 86
 
85
-	if err := iptables.AddReturnRule(IsolationChain1); err != nil {
87
+	if err := iptable.AddReturnRule(IsolationChain1); err != nil {
86 88
 		return nil, nil, nil, nil, err
87 89
 	}
88 90
 
89
-	if err := iptables.AddReturnRule(IsolationChain2); err != nil {
91
+	if err := iptable.AddReturnRule(IsolationChain2); err != nil {
90 92
 		return nil, nil, nil, nil, err
91 93
 	}
92 94
 
93 95
 	return natChain, filterChain, isolationChain1, isolationChain2, nil
94 96
 }
95 97
 
96
-func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInterface) error {
97
-	var err error
98
-
98
+func (n *bridgeNetwork) setupIP4Tables(config *networkConfiguration, i *bridgeInterface) error {
99 99
 	d := n.driver
100 100
 	d.Lock()
101 101
 	driverConfig := d.config
102 102
 	d.Unlock()
103 103
 
104 104
 	// Sanity check.
105
-	if driverConfig.EnableIPTables == false {
105
+	if !driverConfig.EnableIPTables {
106 106
 		return errors.New("Cannot program chains, EnableIPTable is disabled")
107 107
 	}
108 108
 
109
-	// Pickup this configuration option from driver
110
-	hairpinMode := !driverConfig.EnableUserlandProxy
111
-
112 109
 	maskedAddrv4 := &net.IPNet{
113 110
 		IP:   i.bridgeIPv4.IP.Mask(i.bridgeIPv4.Mask),
114 111
 		Mask: i.bridgeIPv4.Mask,
115 112
 	}
113
+	return n.setupIPTables(iptables.IPv4, maskedAddrv4, config, i)
114
+}
115
+
116
+func (n *bridgeNetwork) setupIP6Tables(config *networkConfiguration, i *bridgeInterface) error {
117
+	d := n.driver
118
+	d.Lock()
119
+	driverConfig := d.config
120
+	d.Unlock()
121
+
122
+	// Sanity check.
123
+	if !driverConfig.EnableIP6Tables {
124
+		return errors.New("Cannot program chains, EnableIP6Tables is disabled")
125
+	}
126
+
127
+	maskedAddrv6 := &net.IPNet{
128
+		IP:   i.bridgeIPv6.IP.Mask(i.bridgeIPv6.Mask),
129
+		Mask: i.bridgeIPv6.Mask,
130
+	}
131
+
132
+	return n.setupIPTables(iptables.IPv6, maskedAddrv6, config, i)
133
+}
134
+
135
+func (n *bridgeNetwork) setupIPTables(ipVersion iptables.IPVersion, maskedAddr *net.IPNet, config *networkConfiguration, i *bridgeInterface) error {
136
+	var err error
137
+
138
+	d := n.driver
139
+	d.Lock()
140
+	driverConfig := d.config
141
+	d.Unlock()
142
+
143
+	// Pickup this configuration option from driver
144
+	hairpinMode := !driverConfig.EnableUserlandProxy
145
+
146
+	iptable := iptables.GetIptable(ipVersion)
147
+
116 148
 	if config.Internal {
117
-		if err = setupInternalNetworkRules(config.BridgeName, maskedAddrv4, config.EnableICC, true); err != nil {
149
+		if err = setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, true); err != nil {
118 150
 			return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
119 151
 		}
120 152
 		n.registerIptCleanFunc(func() error {
121
-			return setupInternalNetworkRules(config.BridgeName, maskedAddrv4, config.EnableICC, false)
153
+			return setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, false)
122 154
 		})
123 155
 	} else {
124
-		if err = setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
156
+		if err = setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
125 157
 			return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
126 158
 		}
127 159
 		n.registerIptCleanFunc(func() error {
128
-			return setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
160
+			return setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
129 161
 		})
130
-		natChain, filterChain, _, _, err := n.getDriverChains()
162
+		natChain, filterChain, _, _, err := n.getDriverChains(ipVersion)
131 163
 		if err != nil {
132 164
 			return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
133 165
 		}
134 166
 
135
-		err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
167
+		err = iptable.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
136 168
 		if err != nil {
137 169
 			return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
138 170
 		}
139 171
 
140
-		err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
172
+		err = iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
141 173
 		if err != nil {
142 174
 			return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
143 175
 		}
144 176
 
145 177
 		n.registerIptCleanFunc(func() error {
146
-			return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
178
+			return iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
147 179
 		})
148 180
 
149 181
 		n.portMapper.SetIptablesChain(natChain, n.getNetworkBridgeName())
150 182
 	}
151 183
 
152 184
 	d.Lock()
153
-	err = iptables.EnsureJumpRule("FORWARD", IsolationChain1)
185
+	err = iptable.EnsureJumpRule("FORWARD", IsolationChain1)
154 186
 	d.Unlock()
155
-	if err != nil {
156
-		return err
157
-	}
158
-
159
-	return nil
187
+	return err
160 188
 }
161 189
 
162 190
 type iptRule struct {
... ...
@@ -166,7 +194,7 @@ type iptRule struct {
166 166
 	args    []string
167 167
 }
168 168
 
169
-func setupIPTablesInternal(hostIP net.IP, bridgeIface string, addr net.Addr, icc, ipmasq, hairpin, enable bool) error {
169
+func setupIPTablesInternal(hostIP net.IP, bridgeIface string, addr *net.IPNet, icc, ipmasq, hairpin, enable bool) error {
170 170
 
171 171
 	var (
172 172
 		address   = addr.String()
... ...
@@ -189,41 +217,50 @@ func setupIPTablesInternal(hostIP net.IP, bridgeIface string, addr net.Addr, icc
189 189
 	natRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: natArgs}
190 190
 	hpNatRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: hpNatArgs}
191 191
 
192
+	ipVersion := iptables.IPv4
193
+
194
+	if addr.IP.To4() == nil {
195
+		ipVersion = iptables.IPv6
196
+	}
197
+
192 198
 	// Set NAT.
193 199
 	if ipmasq {
194
-		if err := programChainRule(natRule, "NAT", enable); err != nil {
200
+		if err := programChainRule(ipVersion, natRule, "NAT", enable); err != nil {
195 201
 			return err
196 202
 		}
197 203
 	}
198 204
 
199 205
 	if ipmasq && !hairpin {
200
-		if err := programChainRule(skipDNAT, "SKIP DNAT", enable); err != nil {
206
+		if err := programChainRule(ipVersion, skipDNAT, "SKIP DNAT", enable); err != nil {
201 207
 			return err
202 208
 		}
203 209
 	}
204 210
 
205 211
 	// In hairpin mode, masquerade traffic from localhost
206 212
 	if hairpin {
207
-		if err := programChainRule(hpNatRule, "MASQ LOCAL HOST", enable); err != nil {
213
+		if err := programChainRule(ipVersion, hpNatRule, "MASQ LOCAL HOST", enable); err != nil {
208 214
 			return err
209 215
 		}
210 216
 	}
211 217
 
212 218
 	// Set Inter Container Communication.
213
-	if err := setIcc(bridgeIface, icc, enable); err != nil {
219
+	if err := setIcc(ipVersion, bridgeIface, icc, enable); err != nil {
214 220
 		return err
215 221
 	}
216 222
 
217 223
 	// Set Accept on all non-intercontainer outgoing packets.
218
-	return programChainRule(outRule, "ACCEPT NON_ICC OUTGOING", enable)
224
+	return programChainRule(ipVersion, outRule, "ACCEPT NON_ICC OUTGOING", enable)
219 225
 }
220 226
 
221
-func programChainRule(rule iptRule, ruleDescr string, insert bool) error {
227
+func programChainRule(version iptables.IPVersion, rule iptRule, ruleDescr string, insert bool) error {
228
+
229
+	iptable := iptables.GetIptable(version)
230
+
222 231
 	var (
223 232
 		prefix    []string
224 233
 		operation string
225 234
 		condition bool
226
-		doesExist = iptables.Exists(rule.table, rule.chain, rule.args...)
235
+		doesExist = iptable.Exists(rule.table, rule.chain, rule.args...)
227 236
 	)
228 237
 
229 238
 	if insert {
... ...
@@ -240,7 +277,7 @@ func programChainRule(rule iptRule, ruleDescr string, insert bool) error {
240 240
 	}
241 241
 
242 242
 	if condition {
243
-		if err := iptables.RawCombinedOutput(append(prefix, rule.args...)...); err != nil {
243
+		if err := iptable.RawCombinedOutput(append(prefix, rule.args...)...); err != nil {
244 244
 			return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
245 245
 		}
246 246
 	}
... ...
@@ -248,7 +285,8 @@ func programChainRule(rule iptRule, ruleDescr string, insert bool) error {
248 248
 	return nil
249 249
 }
250 250
 
251
-func setIcc(bridgeIface string, iccEnable, insert bool) error {
251
+func setIcc(version iptables.IPVersion, bridgeIface string, iccEnable, insert bool) error {
252
+	iptable := iptables.GetIptable(version)
252 253
 	var (
253 254
 		table      = iptables.Filter
254 255
 		chain      = "FORWARD"
... ...
@@ -259,18 +297,18 @@ func setIcc(bridgeIface string, iccEnable, insert bool) error {
259 259
 
260 260
 	if insert {
261 261
 		if !iccEnable {
262
-			iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...)
262
+			iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...)
263 263
 
264
-			if !iptables.Exists(table, chain, dropArgs...) {
265
-				if err := iptables.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil {
264
+			if !iptable.Exists(table, chain, dropArgs...) {
265
+				if err := iptable.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil {
266 266
 					return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
267 267
 				}
268 268
 			}
269 269
 		} else {
270
-			iptables.Raw(append([]string{"-D", chain}, dropArgs...)...)
270
+			iptable.Raw(append([]string{"-D", chain}, dropArgs...)...)
271 271
 
272
-			if !iptables.Exists(table, chain, acceptArgs...) {
273
-				if err := iptables.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil {
272
+			if !iptable.Exists(table, chain, acceptArgs...) {
273
+				if err := iptable.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil {
274 274
 					return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
275 275
 				}
276 276
 			}
... ...
@@ -278,12 +316,12 @@ func setIcc(bridgeIface string, iccEnable, insert bool) error {
278 278
 	} else {
279 279
 		// Remove any ICC rule.
280 280
 		if !iccEnable {
281
-			if iptables.Exists(table, chain, dropArgs...) {
282
-				iptables.Raw(append([]string{"-D", chain}, dropArgs...)...)
281
+			if iptable.Exists(table, chain, dropArgs...) {
282
+				iptable.Raw(append([]string{"-D", chain}, dropArgs...)...)
283 283
 			}
284 284
 		} else {
285
-			if iptables.Exists(table, chain, acceptArgs...) {
286
-				iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...)
285
+			if iptable.Exists(table, chain, acceptArgs...) {
286
+				iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...)
287 287
 			}
288 288
 		}
289 289
 	}
... ...
@@ -292,7 +330,8 @@ func setIcc(bridgeIface string, iccEnable, insert bool) error {
292 292
 }
293 293
 
294 294
 // Control Inter Network Communication. Install[Remove] only if it is [not] present.
295
-func setINC(iface string, enable bool) error {
295
+func setINC(version iptables.IPVersion, iface string, enable bool) error {
296
+	iptable := iptables.GetIptable(version)
296 297
 	var (
297 298
 		action    = iptables.Insert
298 299
 		actionMsg = "add"
... ...
@@ -309,12 +348,12 @@ func setINC(iface string, enable bool) error {
309 309
 	}
310 310
 
311 311
 	for i, chain := range chains {
312
-		if err := iptables.ProgramRule(iptables.Filter, chain, action, rules[i]); err != nil {
312
+		if err := iptable.ProgramRule(iptables.Filter, chain, action, rules[i]); err != nil {
313 313
 			msg := fmt.Sprintf("unable to %s inter-network communication rule: %v", actionMsg, err)
314 314
 			if enable {
315 315
 				if i == 1 {
316 316
 					// Rollback the rule installed on first chain
317
-					if err2 := iptables.ProgramRule(iptables.Filter, chains[0], iptables.Delete, rules[0]); err2 != nil {
317
+					if err2 := iptable.ProgramRule(iptables.Filter, chains[0], iptables.Delete, rules[0]); err2 != nil {
318 318
 						logrus.Warnf("Failed to rollback iptables rule after failure (%v): %v", err, err2)
319 319
 					}
320 320
 				}
... ...
@@ -330,37 +369,47 @@ func setINC(iface string, enable bool) error {
330 330
 // Obsolete chain from previous docker versions
331 331
 const oldIsolationChain = "DOCKER-ISOLATION"
332 332
 
333
-func removeIPChains() {
333
+func removeIPChains(version iptables.IPVersion) {
334
+	ipt := iptables.IPTable{Version: version}
335
+
334 336
 	// Remove obsolete rules from default chains
335
-	iptables.ProgramRule(iptables.Filter, "FORWARD", iptables.Delete, []string{"-j", oldIsolationChain})
337
+	ipt.ProgramRule(iptables.Filter, "FORWARD", iptables.Delete, []string{"-j", oldIsolationChain})
336 338
 
337 339
 	// Remove chains
338 340
 	for _, chainInfo := range []iptables.ChainInfo{
339
-		{Name: DockerChain, Table: iptables.Nat},
340
-		{Name: DockerChain, Table: iptables.Filter},
341
-		{Name: IsolationChain1, Table: iptables.Filter},
342
-		{Name: IsolationChain2, Table: iptables.Filter},
343
-		{Name: oldIsolationChain, Table: iptables.Filter},
341
+		{Name: DockerChain, Table: iptables.Nat, IPTable: ipt},
342
+		{Name: DockerChain, Table: iptables.Filter, IPTable: ipt},
343
+		{Name: IsolationChain1, Table: iptables.Filter, IPTable: ipt},
344
+		{Name: IsolationChain2, Table: iptables.Filter, IPTable: ipt},
345
+		{Name: oldIsolationChain, Table: iptables.Filter, IPTable: ipt},
344 346
 	} {
347
+
345 348
 		if err := chainInfo.Remove(); err != nil {
346 349
 			logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err)
347 350
 		}
348 351
 	}
349 352
 }
350 353
 
351
-func setupInternalNetworkRules(bridgeIface string, addr net.Addr, icc, insert bool) error {
354
+func setupInternalNetworkRules(bridgeIface string, addr *net.IPNet, icc, insert bool) error {
352 355
 	var (
353 356
 		inDropRule  = iptRule{table: iptables.Filter, chain: IsolationChain1, args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"}}
354 357
 		outDropRule = iptRule{table: iptables.Filter, chain: IsolationChain1, args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"}}
355 358
 	)
356
-	if err := programChainRule(inDropRule, "DROP INCOMING", insert); err != nil {
359
+
360
+	version := iptables.IPv4
361
+
362
+	if addr.IP.To4() == nil {
363
+		version = iptables.IPv6
364
+	}
365
+
366
+	if err := programChainRule(version, inDropRule, "DROP INCOMING", insert); err != nil {
357 367
 		return err
358 368
 	}
359
-	if err := programChainRule(outDropRule, "DROP OUTGOING", insert); err != nil {
369
+	if err := programChainRule(version, outDropRule, "DROP OUTGOING", insert); err != nil {
360 370
 		return err
361 371
 	}
362 372
 	// Set Inter Container Communication.
363
-	return setIcc(bridgeIface, icc, insert)
373
+	return setIcc(version, bridgeIface, icc, insert)
364 374
 }
365 375
 
366 376
 func clearEndpointConnections(nlh *netlink.Handle, ep *bridgeEndpoint) {
... ...
@@ -210,7 +210,10 @@ func programMangle(vni uint32, add bool) (err error) {
210 210
 		action = "install"
211 211
 	)
212 212
 
213
-	if add == iptables.Exists(iptables.Mangle, chain, rule...) {
213
+	// TODO IPv6 support
214
+	iptable := iptables.GetIptable(iptables.IPv4)
215
+
216
+	if add == iptable.Exists(iptables.Mangle, chain, rule...) {
214 217
 		return
215 218
 	}
216 219
 
... ...
@@ -219,7 +222,7 @@ func programMangle(vni uint32, add bool) (err error) {
219 219
 		action = "remove"
220 220
 	}
221 221
 
222
-	if err = iptables.RawCombinedOutput(append([]string{"-t", string(iptables.Mangle), a, chain}, rule...)...); err != nil {
222
+	if err = iptable.RawCombinedOutput(append([]string{"-t", string(iptables.Mangle), a, chain}, rule...)...); err != nil {
223 223
 		logrus.Warnf("could not %s mangle rule: %v", action, err)
224 224
 	}
225 225
 
... ...
@@ -239,16 +242,19 @@ func programInput(vni uint32, add bool) (err error) {
239 239
 		msg        = "add"
240 240
 	)
241 241
 
242
+	// TODO IPv6 support
243
+	iptable := iptables.GetIptable(iptables.IPv4)
244
+
242 245
 	if !add {
243 246
 		action = iptables.Delete
244 247
 		msg = "remove"
245 248
 	}
246 249
 
247
-	if err := iptables.ProgramRule(iptables.Filter, chain, action, accept); err != nil {
250
+	if err := iptable.ProgramRule(iptables.Filter, chain, action, accept); err != nil {
248 251
 		logrus.Errorf("could not %s input rule: %v. Please do it manually.", msg, err)
249 252
 	}
250 253
 
251
-	if err := iptables.ProgramRule(iptables.Filter, chain, action, block); err != nil {
254
+	if err := iptable.ProgramRule(iptables.Filter, chain, action, block); err != nil {
252 255
 		logrus.Errorf("could not %s input rule: %v. Please do it manually.", msg, err)
253 256
 	}
254 257
 
... ...
@@ -20,7 +20,9 @@ func filterWait() func() {
20 20
 }
21 21
 
22 22
 func chainExists(cname string) bool {
23
-	if _, err := iptables.Raw("-L", cname); err != nil {
23
+	// TODO IPv6 support
24
+	iptable := iptables.GetIptable(iptables.IPv4)
25
+	if _, err := iptable.Raw("-L", cname); err != nil {
24 26
 		return false
25 27
 	}
26 28
 
... ...
@@ -28,22 +30,26 @@ func chainExists(cname string) bool {
28 28
 }
29 29
 
30 30
 func setupGlobalChain() {
31
+	// TODO IPv6 support
32
+	iptable := iptables.GetIptable(iptables.IPv4)
31 33
 	// Because of an ungraceful shutdown, chain could already be present
32 34
 	if !chainExists(globalChain) {
33
-		if err := iptables.RawCombinedOutput("-N", globalChain); err != nil {
35
+		if err := iptable.RawCombinedOutput("-N", globalChain); err != nil {
34 36
 			logrus.Errorf("could not create global overlay chain: %v", err)
35 37
 			return
36 38
 		}
37 39
 	}
38 40
 
39
-	if !iptables.Exists(iptables.Filter, globalChain, "-j", "RETURN") {
40
-		if err := iptables.RawCombinedOutput("-A", globalChain, "-j", "RETURN"); err != nil {
41
+	if !iptable.Exists(iptables.Filter, globalChain, "-j", "RETURN") {
42
+		if err := iptable.RawCombinedOutput("-A", globalChain, "-j", "RETURN"); err != nil {
41 43
 			logrus.Errorf("could not install default return chain in the overlay global chain: %v", err)
42 44
 		}
43 45
 	}
44 46
 }
45 47
 
46 48
 func setNetworkChain(cname string, remove bool) error {
49
+	// TODO IPv6 support
50
+	iptable := iptables.GetIptable(iptables.IPv4)
47 51
 	// Initialize the onetime global overlay chain
48 52
 	filterOnce.Do(setupGlobalChain)
49 53
 
... ...
@@ -52,21 +58,21 @@ func setNetworkChain(cname string, remove bool) error {
52 52
 	opt := "-N"
53 53
 	// In case of remove, make sure to flush the rules in the chain
54 54
 	if remove && exists {
55
-		if err := iptables.RawCombinedOutput("-F", cname); err != nil {
55
+		if err := iptable.RawCombinedOutput("-F", cname); err != nil {
56 56
 			return fmt.Errorf("failed to flush overlay network chain %s rules: %v", cname, err)
57 57
 		}
58 58
 		opt = "-X"
59 59
 	}
60 60
 
61 61
 	if (!remove && !exists) || (remove && exists) {
62
-		if err := iptables.RawCombinedOutput(opt, cname); err != nil {
62
+		if err := iptable.RawCombinedOutput(opt, cname); err != nil {
63 63
 			return fmt.Errorf("failed network chain operation %q for chain %s: %v", opt, cname, err)
64 64
 		}
65 65
 	}
66 66
 
67 67
 	if !remove {
68
-		if !iptables.Exists(iptables.Filter, cname, "-j", "DROP") {
69
-			if err := iptables.RawCombinedOutput("-A", cname, "-j", "DROP"); err != nil {
68
+		if !iptable.Exists(iptables.Filter, cname, "-j", "DROP") {
69
+			if err := iptable.RawCombinedOutput("-A", cname, "-j", "DROP"); err != nil {
70 70
 				return fmt.Errorf("failed adding default drop rule to overlay network chain %s: %v", cname, err)
71 71
 			}
72 72
 		}
... ...
@@ -92,37 +98,39 @@ func setFilters(cname, brName string, remove bool) error {
92 92
 	if remove {
93 93
 		opt = "-D"
94 94
 	}
95
+	// TODO IPv6 support
96
+	iptable := iptables.GetIptable(iptables.IPv4)
95 97
 
96 98
 	// Every time we set filters for a new subnet make sure to move the global overlay hook to the top of the both the OUTPUT and forward chains
97 99
 	if !remove {
98 100
 		for _, chain := range []string{"OUTPUT", "FORWARD"} {
99
-			exists := iptables.Exists(iptables.Filter, chain, "-j", globalChain)
101
+			exists := iptable.Exists(iptables.Filter, chain, "-j", globalChain)
100 102
 			if exists {
101
-				if err := iptables.RawCombinedOutput("-D", chain, "-j", globalChain); err != nil {
103
+				if err := iptable.RawCombinedOutput("-D", chain, "-j", globalChain); err != nil {
102 104
 					return fmt.Errorf("failed to delete overlay hook in chain %s while moving the hook: %v", chain, err)
103 105
 				}
104 106
 			}
105 107
 
106
-			if err := iptables.RawCombinedOutput("-I", chain, "-j", globalChain); err != nil {
108
+			if err := iptable.RawCombinedOutput("-I", chain, "-j", globalChain); err != nil {
107 109
 				return fmt.Errorf("failed to insert overlay hook in chain %s: %v", chain, err)
108 110
 			}
109 111
 		}
110 112
 	}
111 113
 
112 114
 	// Insert/Delete the rule to jump to per-bridge chain
113
-	exists := iptables.Exists(iptables.Filter, globalChain, "-o", brName, "-j", cname)
115
+	exists := iptable.Exists(iptables.Filter, globalChain, "-o", brName, "-j", cname)
114 116
 	if (!remove && !exists) || (remove && exists) {
115
-		if err := iptables.RawCombinedOutput(opt, globalChain, "-o", brName, "-j", cname); err != nil {
117
+		if err := iptable.RawCombinedOutput(opt, globalChain, "-o", brName, "-j", cname); err != nil {
116 118
 			return fmt.Errorf("failed to add per-bridge filter rule for bridge %s, network chain %s: %v", brName, cname, err)
117 119
 		}
118 120
 	}
119 121
 
120
-	exists = iptables.Exists(iptables.Filter, cname, "-i", brName, "-j", "ACCEPT")
122
+	exists = iptable.Exists(iptables.Filter, cname, "-i", brName, "-j", "ACCEPT")
121 123
 	if (!remove && exists) || (remove && !exists) {
122 124
 		return nil
123 125
 	}
124 126
 
125
-	if err := iptables.RawCombinedOutput(opt, cname, "-i", brName, "-j", "ACCEPT"); err != nil {
127
+	if err := iptable.RawCombinedOutput(opt, cname, "-i", brName, "-j", "ACCEPT"); err != nil {
126 128
 		return fmt.Errorf("failed to add overlay filter rile for network chain %s, bridge %s: %v", cname, brName, err)
127 129
 	}
128 130
 
... ...
@@ -26,18 +26,20 @@ func arrangeUserFilterRule() {
26 26
 	if ctrl == nil || !ctrl.iptablesEnabled() {
27 27
 		return
28 28
 	}
29
-	_, err := iptables.NewChain(userChain, iptables.Filter, false)
29
+	// TODO IPv6 support
30
+	iptable := iptables.GetIptable(iptables.IPv4)
31
+	_, err := iptable.NewChain(userChain, iptables.Filter, false)
30 32
 	if err != nil {
31 33
 		logrus.Warnf("Failed to create %s chain: %v", userChain, err)
32 34
 		return
33 35
 	}
34 36
 
35
-	if err = iptables.AddReturnRule(userChain); err != nil {
37
+	if err = iptable.AddReturnRule(userChain); err != nil {
36 38
 		logrus.Warnf("Failed to add the RETURN rule for %s: %v", userChain, err)
37 39
 		return
38 40
 	}
39 41
 
40
-	err = iptables.EnsureJumpRule("FORWARD", userChain)
42
+	err = iptable.EnsureJumpRule("FORWARD", userChain)
41 43
 	if err != nil {
42 44
 		logrus.Warnf("Failed to ensure the jump rule for %s: %v", userChain, err)
43 45
 	}
... ...
@@ -23,6 +23,9 @@ type Policy string
23 23
 // Table refers to Nat, Filter or Mangle.
24 24
 type Table string
25 25
 
26
+// IPVersion refers to IP version, v4 or v6
27
+type IPVersion string
28
+
26 29
 const (
27 30
 	// Append appends the rule at the end of the chain.
28 31
 	Append Action = "-A"
... ...
@@ -40,10 +43,15 @@ const (
40 40
 	Drop Policy = "DROP"
41 41
 	// Accept is the default iptables ACCEPT policy
42 42
 	Accept Policy = "ACCEPT"
43
+	// IPv4 is version 4
44
+	IPv4 IPVersion = "IPV4"
45
+	// IPv6 is version 6
46
+	IPv6 IPVersion = "IPV6"
43 47
 )
44 48
 
45 49
 var (
46 50
 	iptablesPath  string
51
+	ip6tablesPath string
47 52
 	supportsXlock = false
48 53
 	supportsCOpt  = false
49 54
 	xLockWaitMsg  = "Another app is currently holding the xtables lock"
... ...
@@ -54,11 +62,17 @@ var (
54 54
 	initOnce            sync.Once
55 55
 )
56 56
 
57
+// IPTable defines struct with IPVersion
58
+type IPTable struct {
59
+	Version IPVersion
60
+}
61
+
57 62
 // ChainInfo defines the iptables chain.
58 63
 type ChainInfo struct {
59 64
 	Name        string
60 65
 	Table       Table
61 66
 	HairpinMode bool
67
+	IPTable     IPTable
62 68
 }
63 69
 
64 70
 // ChainError is returned to represent errors during ip table operation.
... ...
@@ -80,6 +94,11 @@ func probe() {
80 80
 	if out, err := exec.Command(path, "--wait", "-t", "nat", "-L", "-n").CombinedOutput(); err != nil {
81 81
 		logrus.Warnf("Running iptables --wait -t nat -L -n failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
82 82
 	}
83
+	_, err = exec.LookPath("ip6tables")
84
+	if err != nil {
85
+		logrus.Warnf("Failed to find ip6tables: %v", err)
86
+		return
87
+	}
83 88
 }
84 89
 
85 90
 func initFirewalld() {
... ...
@@ -94,6 +113,11 @@ func detectIptables() {
94 94
 		return
95 95
 	}
96 96
 	iptablesPath = path
97
+	path, err = exec.LookPath("ip6tables")
98
+	if err != nil {
99
+		return
100
+	}
101
+	ip6tablesPath = path
97 102
 	supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil
98 103
 	mj, mn, mc, err := GetVersion()
99 104
 	if err != nil {
... ...
@@ -118,20 +142,26 @@ func initCheck() error {
118 118
 	return nil
119 119
 }
120 120
 
121
+// GetIptable returns an instance of IPTable with specified version
122
+func GetIptable(version IPVersion) *IPTable {
123
+	return &IPTable{Version: version}
124
+}
125
+
121 126
 // NewChain adds a new chain to ip table.
122
-func NewChain(name string, table Table, hairpinMode bool) (*ChainInfo, error) {
127
+func (iptable IPTable) NewChain(name string, table Table, hairpinMode bool) (*ChainInfo, error) {
123 128
 	c := &ChainInfo{
124 129
 		Name:        name,
125 130
 		Table:       table,
126 131
 		HairpinMode: hairpinMode,
132
+		IPTable:     iptable,
127 133
 	}
128 134
 	if string(c.Table) == "" {
129 135
 		c.Table = Filter
130 136
 	}
131 137
 
132 138
 	// Add chain if it doesn't exist
133
-	if _, err := Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil {
134
-		if output, err := Raw("-t", string(c.Table), "-N", c.Name); err != nil {
139
+	if _, err := iptable.Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil {
140
+		if output, err := iptable.Raw("-t", string(c.Table), "-N", c.Name); err != nil {
135 141
 			return nil, err
136 142
 		} else if len(output) != 0 {
137 143
 			return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output)
... ...
@@ -140,8 +170,16 @@ func NewChain(name string, table Table, hairpinMode bool) (*ChainInfo, error) {
140 140
 	return c, nil
141 141
 }
142 142
 
143
+// LoopbackByVersion returns loopback address by version
144
+func (iptable IPTable) LoopbackByVersion() string {
145
+	if iptable.Version == IPv6 {
146
+		return "::1/128"
147
+	}
148
+	return "127.0.0.0/8"
149
+}
150
+
143 151
 // ProgramChain is used to add rules to a chain
144
-func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) error {
152
+func (iptable IPTable) ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) error {
145 153
 	if c.Name == "" {
146 154
 		return errors.New("Could not program chain, missing chain name")
147 155
 	}
... ...
@@ -165,11 +203,11 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) err
165 165
 			"-m", "addrtype",
166 166
 			"--dst-type", "LOCAL",
167 167
 			"-j", c.Name}
168
-		if !Exists(Nat, "PREROUTING", preroute...) && enable {
168
+		if !iptable.Exists(Nat, "PREROUTING", preroute...) && enable {
169 169
 			if err := c.Prerouting(Append, preroute...); err != nil {
170 170
 				return fmt.Errorf("Failed to inject %s in PREROUTING chain: %s", c.Name, err)
171 171
 			}
172
-		} else if Exists(Nat, "PREROUTING", preroute...) && !enable {
172
+		} else if iptable.Exists(Nat, "PREROUTING", preroute...) && !enable {
173 173
 			if err := c.Prerouting(Delete, preroute...); err != nil {
174 174
 				return fmt.Errorf("Failed to remove %s in PREROUTING chain: %s", c.Name, err)
175 175
 			}
... ...
@@ -179,13 +217,13 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) err
179 179
 			"--dst-type", "LOCAL",
180 180
 			"-j", c.Name}
181 181
 		if !hairpinMode {
182
-			output = append(output, "!", "--dst", "127.0.0.0/8")
182
+			output = append(output, "!", "--dst", iptable.LoopbackByVersion())
183 183
 		}
184
-		if !Exists(Nat, "OUTPUT", output...) && enable {
184
+		if !iptable.Exists(Nat, "OUTPUT", output...) && enable {
185 185
 			if err := c.Output(Append, output...); err != nil {
186 186
 				return fmt.Errorf("Failed to inject %s in OUTPUT chain: %s", c.Name, err)
187 187
 			}
188
-		} else if Exists(Nat, "OUTPUT", output...) && !enable {
188
+		} else if iptable.Exists(Nat, "OUTPUT", output...) && !enable {
189 189
 			if err := c.Output(Delete, output...); err != nil {
190 190
 				return fmt.Errorf("Failed to inject %s in OUTPUT chain: %s", c.Name, err)
191 191
 			}
... ...
@@ -198,16 +236,16 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) err
198 198
 		link := []string{
199 199
 			"-o", bridgeName,
200 200
 			"-j", c.Name}
201
-		if !Exists(Filter, "FORWARD", link...) && enable {
201
+		if !iptable.Exists(Filter, "FORWARD", link...) && enable {
202 202
 			insert := append([]string{string(Insert), "FORWARD"}, link...)
203
-			if output, err := Raw(insert...); err != nil {
203
+			if output, err := iptable.Raw(insert...); err != nil {
204 204
 				return err
205 205
 			} else if len(output) != 0 {
206 206
 				return fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output)
207 207
 			}
208
-		} else if Exists(Filter, "FORWARD", link...) && !enable {
208
+		} else if iptable.Exists(Filter, "FORWARD", link...) && !enable {
209 209
 			del := append([]string{string(Delete), "FORWARD"}, link...)
210
-			if output, err := Raw(del...); err != nil {
210
+			if output, err := iptable.Raw(del...); err != nil {
211 211
 				return err
212 212
 			} else if len(output) != 0 {
213 213
 				return fmt.Errorf("Could not delete linking rule from %s/%s: %s", c.Table, c.Name, output)
... ...
@@ -219,16 +257,16 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) err
219 219
 			"-m", "conntrack",
220 220
 			"--ctstate", "RELATED,ESTABLISHED",
221 221
 			"-j", "ACCEPT"}
222
-		if !Exists(Filter, "FORWARD", establish...) && enable {
222
+		if !iptable.Exists(Filter, "FORWARD", establish...) && enable {
223 223
 			insert := append([]string{string(Insert), "FORWARD"}, establish...)
224
-			if output, err := Raw(insert...); err != nil {
224
+			if output, err := iptable.Raw(insert...); err != nil {
225 225
 				return err
226 226
 			} else if len(output) != 0 {
227 227
 				return fmt.Errorf("Could not create establish rule to %s: %s", c.Table, output)
228 228
 			}
229
-		} else if Exists(Filter, "FORWARD", establish...) && !enable {
229
+		} else if iptable.Exists(Filter, "FORWARD", establish...) && !enable {
230 230
 			del := append([]string{string(Delete), "FORWARD"}, establish...)
231
-			if output, err := Raw(del...); err != nil {
231
+			if output, err := iptable.Raw(del...); err != nil {
232 232
 				return err
233 233
 			} else if len(output) != 0 {
234 234
 				return fmt.Errorf("Could not delete establish rule from %s: %s", c.Table, output)
... ...
@@ -239,10 +277,11 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) err
239 239
 }
240 240
 
241 241
 // RemoveExistingChain removes existing chain from the table.
242
-func RemoveExistingChain(name string, table Table) error {
242
+func (iptable IPTable) RemoveExistingChain(name string, table Table) error {
243 243
 	c := &ChainInfo{
244
-		Name:  name,
245
-		Table: table,
244
+		Name:    name,
245
+		Table:   table,
246
+		IPTable: iptable,
246 247
 	}
247 248
 	if string(c.Table) == "" {
248 249
 		c.Table = Filter
... ...
@@ -252,6 +291,8 @@ func RemoveExistingChain(name string, table Table) error {
252 252
 
253 253
 // Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table.
254 254
 func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int, bridgeName string) error {
255
+
256
+	iptable := GetIptable(c.IPTable.Version)
255 257
 	daddr := ip.String()
256 258
 	if ip.IsUnspecified() {
257 259
 		// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
... ...
@@ -266,10 +307,11 @@ func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr
266 266
 		"--dport", strconv.Itoa(port),
267 267
 		"-j", "DNAT",
268 268
 		"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))}
269
+
269 270
 	if !c.HairpinMode {
270 271
 		args = append(args, "!", "-i", bridgeName)
271 272
 	}
272
-	if err := ProgramRule(Nat, c.Name, action, args); err != nil {
273
+	if err := iptable.ProgramRule(Nat, c.Name, action, args); err != nil {
273 274
 		return err
274 275
 	}
275 276
 
... ...
@@ -281,7 +323,7 @@ func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr
281 281
 		"--dport", strconv.Itoa(destPort),
282 282
 		"-j", "ACCEPT",
283 283
 	}
284
-	if err := ProgramRule(Filter, c.Name, action, args); err != nil {
284
+	if err := iptable.ProgramRule(Filter, c.Name, action, args); err != nil {
285 285
 		return err
286 286
 	}
287 287
 
... ...
@@ -293,7 +335,7 @@ func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr
293 293
 		"-j", "MASQUERADE",
294 294
 	}
295 295
 
296
-	if err := ProgramRule(Nat, "POSTROUTING", action, args); err != nil {
296
+	if err := iptable.ProgramRule(Nat, "POSTROUTING", action, args); err != nil {
297 297
 		return err
298 298
 	}
299 299
 
... ...
@@ -311,7 +353,7 @@ func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr
311 311
 			"-j", "CHECKSUM",
312 312
 			"--checksum-fill",
313 313
 		}
314
-		if err := ProgramRule(Mangle, "POSTROUTING", action, args); err != nil {
314
+		if err := iptable.ProgramRule(Mangle, "POSTROUTING", action, args); err != nil {
315 315
 			return err
316 316
 		}
317 317
 	}
... ...
@@ -322,6 +364,7 @@ func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr
322 322
 // Link adds reciprocal ACCEPT rule for two supplied IP addresses.
323 323
 // Traffic is allowed from ip1 to ip2 and vice-versa
324 324
 func (c *ChainInfo) Link(action Action, ip1, ip2 net.IP, port int, proto string, bridgeName string) error {
325
+	iptable := GetIptable(c.IPTable.Version)
325 326
 	// forward
326 327
 	args := []string{
327 328
 		"-i", bridgeName, "-o", bridgeName,
... ...
@@ -331,32 +374,34 @@ func (c *ChainInfo) Link(action Action, ip1, ip2 net.IP, port int, proto string,
331 331
 		"--dport", strconv.Itoa(port),
332 332
 		"-j", "ACCEPT",
333 333
 	}
334
-	if err := ProgramRule(Filter, c.Name, action, args); err != nil {
334
+
335
+	if err := iptable.ProgramRule(Filter, c.Name, action, args); err != nil {
335 336
 		return err
336 337
 	}
337 338
 	// reverse
338 339
 	args[7], args[9] = args[9], args[7]
339 340
 	args[10] = "--sport"
340
-	return ProgramRule(Filter, c.Name, action, args)
341
+	return iptable.ProgramRule(Filter, c.Name, action, args)
341 342
 }
342 343
 
343 344
 // ProgramRule adds the rule specified by args only if the
344 345
 // rule is not already present in the chain. Reciprocally,
345 346
 // it removes the rule only if present.
346
-func ProgramRule(table Table, chain string, action Action, args []string) error {
347
-	if Exists(table, chain, args...) != (action == Delete) {
347
+func (iptable IPTable) ProgramRule(table Table, chain string, action Action, args []string) error {
348
+	if iptable.Exists(table, chain, args...) != (action == Delete) {
348 349
 		return nil
349 350
 	}
350
-	return RawCombinedOutput(append([]string{"-t", string(table), string(action), chain}, args...)...)
351
+	return iptable.RawCombinedOutput(append([]string{"-t", string(table), string(action), chain}, args...)...)
351 352
 }
352 353
 
353 354
 // Prerouting adds linking rule to nat/PREROUTING chain.
354 355
 func (c *ChainInfo) Prerouting(action Action, args ...string) error {
356
+	iptable := GetIptable(c.IPTable.Version)
355 357
 	a := []string{"-t", string(Nat), string(action), "PREROUTING"}
356 358
 	if len(args) > 0 {
357 359
 		a = append(a, args...)
358 360
 	}
359
-	if output, err := Raw(a...); err != nil {
361
+	if output, err := iptable.Raw(a...); err != nil {
360 362
 		return err
361 363
 	} else if len(output) != 0 {
362 364
 		return ChainError{Chain: "PREROUTING", Output: output}
... ...
@@ -366,11 +411,12 @@ func (c *ChainInfo) Prerouting(action Action, args ...string) error {
366 366
 
367 367
 // Output adds linking rule to an OUTPUT chain.
368 368
 func (c *ChainInfo) Output(action Action, args ...string) error {
369
+	iptable := GetIptable(c.IPTable.Version)
369 370
 	a := []string{"-t", string(c.Table), string(action), "OUTPUT"}
370 371
 	if len(args) > 0 {
371 372
 		a = append(a, args...)
372 373
 	}
373
-	if output, err := Raw(a...); err != nil {
374
+	if output, err := iptable.Raw(a...); err != nil {
374 375
 		return err
375 376
 	} else if len(output) != 0 {
376 377
 		return ChainError{Chain: "OUTPUT", Output: output}
... ...
@@ -380,35 +426,36 @@ func (c *ChainInfo) Output(action Action, args ...string) error {
380 380
 
381 381
 // Remove removes the chain.
382 382
 func (c *ChainInfo) Remove() error {
383
+	iptable := GetIptable(c.IPTable.Version)
383 384
 	// Ignore errors - This could mean the chains were never set up
384 385
 	if c.Table == Nat {
385 386
 		c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name)
386
-		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", c.Name)
387
+		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", iptable.LoopbackByVersion(), "-j", c.Name)
387 388
 		c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name) // Created in versions <= 0.1.6
388 389
 
389 390
 		c.Prerouting(Delete)
390 391
 		c.Output(Delete)
391 392
 	}
392
-	Raw("-t", string(c.Table), "-F", c.Name)
393
-	Raw("-t", string(c.Table), "-X", c.Name)
393
+	iptable.Raw("-t", string(c.Table), "-F", c.Name)
394
+	iptable.Raw("-t", string(c.Table), "-X", c.Name)
394 395
 	return nil
395 396
 }
396 397
 
397 398
 // Exists checks if a rule exists
398
-func Exists(table Table, chain string, rule ...string) bool {
399
-	return exists(false, table, chain, rule...)
399
+func (iptable IPTable) Exists(table Table, chain string, rule ...string) bool {
400
+	return iptable.exists(false, table, chain, rule...)
400 401
 }
401 402
 
402 403
 // ExistsNative behaves as Exists with the difference it
403 404
 // will always invoke `iptables` binary.
404
-func ExistsNative(table Table, chain string, rule ...string) bool {
405
-	return exists(true, table, chain, rule...)
405
+func (iptable IPTable) ExistsNative(table Table, chain string, rule ...string) bool {
406
+	return iptable.exists(true, table, chain, rule...)
406 407
 }
407 408
 
408
-func exists(native bool, table Table, chain string, rule ...string) bool {
409
-	f := Raw
409
+func (iptable IPTable) exists(native bool, table Table, chain string, rule ...string) bool {
410
+	f := iptable.Raw
410 411
 	if native {
411
-		f = raw
412
+		f = iptable.raw
412 413
 	}
413 414
 
414 415
 	if string(table) == "" {
... ...
@@ -429,12 +476,16 @@ func exists(native bool, table Table, chain string, rule ...string) bool {
429 429
 
430 430
 	// parse "iptables -S" for the rule (it checks rules in a specific chain
431 431
 	// in a specific table and it is very unreliable)
432
-	return existsRaw(table, chain, rule...)
432
+	return iptable.existsRaw(table, chain, rule...)
433 433
 }
434 434
 
435
-func existsRaw(table Table, chain string, rule ...string) bool {
435
+func (iptable IPTable) existsRaw(table Table, chain string, rule ...string) bool {
436
+	path := iptablesPath
437
+	if iptable.Version == IPv6 {
438
+		path = ip6tablesPath
439
+	}
436 440
 	ruleString := fmt.Sprintf("%s %s\n", chain, strings.Join(rule, " "))
437
-	existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output()
441
+	existingRules, _ := exec.Command(path, "-t", string(table), "-S", chain).Output()
438 442
 
439 443
 	return strings.Contains(string(existingRules), ruleString)
440 444
 }
... ...
@@ -459,7 +510,7 @@ func filterOutput(start time.Time, output []byte, args ...string) []byte {
459 459
 }
460 460
 
461 461
 // Raw calls 'iptables' system command, passing supplied arguments.
462
-func Raw(args ...string) ([]byte, error) {
462
+func (iptable IPTable) Raw(args ...string) ([]byte, error) {
463 463
 	if firewalldRunning {
464 464
 		startTime := time.Now()
465 465
 		output, err := Passthrough(Iptables, args...)
... ...
@@ -467,10 +518,10 @@ func Raw(args ...string) ([]byte, error) {
467 467
 			return filterOutput(startTime, output, args...), err
468 468
 		}
469 469
 	}
470
-	return raw(args...)
470
+	return iptable.raw(args...)
471 471
 }
472 472
 
473
-func raw(args ...string) ([]byte, error) {
473
+func (iptable IPTable) raw(args ...string) ([]byte, error) {
474 474
 	if err := initCheck(); err != nil {
475 475
 		return nil, err
476 476
 	}
... ...
@@ -481,10 +532,15 @@ func raw(args ...string) ([]byte, error) {
481 481
 		defer bestEffortLock.Unlock()
482 482
 	}
483 483
 
484
-	logrus.Debugf("%s, %v", iptablesPath, args)
484
+	path := iptablesPath
485
+	if iptable.Version == IPv6 {
486
+		path = ip6tablesPath
487
+	}
488
+
489
+	logrus.Debugf("%s, %v", path, args)
485 490
 
486 491
 	startTime := time.Now()
487
-	output, err := exec.Command(iptablesPath, args...).CombinedOutput()
492
+	output, err := exec.Command(path, args...).CombinedOutput()
488 493
 	if err != nil {
489 494
 		return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
490 495
 	}
... ...
@@ -494,8 +550,8 @@ func raw(args ...string) ([]byte, error) {
494 494
 
495 495
 // RawCombinedOutput internally calls the Raw function and returns a non nil
496 496
 // error if Raw returned a non nil error or a non empty output
497
-func RawCombinedOutput(args ...string) error {
498
-	if output, err := Raw(args...); err != nil || len(output) != 0 {
497
+func (iptable IPTable) RawCombinedOutput(args ...string) error {
498
+	if output, err := iptable.Raw(args...); err != nil || len(output) != 0 {
499 499
 		return fmt.Errorf("%s (%v)", string(output), err)
500 500
 	}
501 501
 	return nil
... ...
@@ -503,16 +559,16 @@ func RawCombinedOutput(args ...string) error {
503 503
 
504 504
 // RawCombinedOutputNative behave as RawCombinedOutput with the difference it
505 505
 // will always invoke `iptables` binary
506
-func RawCombinedOutputNative(args ...string) error {
507
-	if output, err := raw(args...); err != nil || len(output) != 0 {
506
+func (iptable IPTable) RawCombinedOutputNative(args ...string) error {
507
+	if output, err := iptable.raw(args...); err != nil || len(output) != 0 {
508 508
 		return fmt.Errorf("%s (%v)", string(output), err)
509 509
 	}
510 510
 	return nil
511 511
 }
512 512
 
513 513
 // ExistChain checks if a chain exists
514
-func ExistChain(chain string, table Table) bool {
515
-	if _, err := Raw("-t", string(table), "-nL", chain); err == nil {
514
+func (iptable IPTable) ExistChain(chain string, table Table) bool {
515
+	if _, err := iptable.Raw("-t", string(table), "-nL", chain); err == nil {
516 516
 		return true
517 517
 	}
518 518
 	return false
... ...
@@ -528,8 +584,8 @@ func GetVersion() (major, minor, micro int, err error) {
528 528
 }
529 529
 
530 530
 // SetDefaultPolicy sets the passed default policy for the table/chain
531
-func SetDefaultPolicy(table Table, chain string, policy Policy) error {
532
-	if err := RawCombinedOutput("-t", string(table), "-P", chain, string(policy)); err != nil {
531
+func (iptable IPTable) SetDefaultPolicy(table Table, chain string, policy Policy) error {
532
+	if err := iptable.RawCombinedOutput("-t", string(table), "-P", chain, string(policy)); err != nil {
533 533
 		return fmt.Errorf("setting default policy to %v in %v chain failed: %v", policy, chain, err)
534 534
 	}
535 535
 	return nil
... ...
@@ -549,17 +605,17 @@ func supportsCOption(mj, mn, mc int) bool {
549 549
 }
550 550
 
551 551
 // AddReturnRule adds a return rule for the chain in the filter table
552
-func AddReturnRule(chain string) error {
552
+func (iptable IPTable) AddReturnRule(chain string) error {
553 553
 	var (
554 554
 		table = Filter
555 555
 		args  = []string{"-j", "RETURN"}
556 556
 	)
557 557
 
558
-	if Exists(table, chain, args...) {
558
+	if iptable.Exists(table, chain, args...) {
559 559
 		return nil
560 560
 	}
561 561
 
562
-	err := RawCombinedOutput(append([]string{"-A", chain}, args...)...)
562
+	err := iptable.RawCombinedOutput(append([]string{"-A", chain}, args...)...)
563 563
 	if err != nil {
564 564
 		return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error())
565 565
 	}
... ...
@@ -568,20 +624,20 @@ func AddReturnRule(chain string) error {
568 568
 }
569 569
 
570 570
 // EnsureJumpRule ensures the jump rule is on top
571
-func EnsureJumpRule(fromChain, toChain string) error {
571
+func (iptable IPTable) EnsureJumpRule(fromChain, toChain string) error {
572 572
 	var (
573 573
 		table = Filter
574 574
 		args  = []string{"-j", toChain}
575 575
 	)
576 576
 
577
-	if Exists(table, fromChain, args...) {
578
-		err := RawCombinedOutput(append([]string{"-D", fromChain}, args...)...)
577
+	if iptable.Exists(table, fromChain, args...) {
578
+		err := iptable.RawCombinedOutput(append([]string{"-D", fromChain}, args...)...)
579 579
 		if err != nil {
580 580
 			return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
581 581
 		}
582 582
 	}
583 583
 
584
-	err := RawCombinedOutput(append([]string{"-I", fromChain}, args...)...)
584
+	err := iptable.RawCombinedOutput(append([]string{"-I", fromChain}, args...)...)
585 585
 	if err != nil {
586 586
 		return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
587 587
 	}
... ...
@@ -151,7 +151,7 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart,
151 151
 	}
152 152
 
153 153
 	containerIP, containerPort := getIPAndPort(m.container)
154
-	if hostIP.To4() != nil {
154
+	if hostIP.To4() != nil || hostIP.To16() != nil {
155 155
 		if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
156 156
 			return nil, err
157 157
 		}
... ...
@@ -160,7 +160,7 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart,
160 160
 	cleanup := func() error {
161 161
 		// need to undo the iptables rules before we return
162 162
 		m.userlandProxy.Stop()
163
-		if hostIP.To4() != nil {
163
+		if hostIP.To4() != nil || hostIP.To16() != nil {
164 164
 			pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
165 165
 			if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
166 166
 				return err
... ...
@@ -57,25 +57,28 @@ func reexecSetupResolver() {
57 57
 		os.Exit(3)
58 58
 	}
59 59
 
60
+	// TODO IPv6 support
61
+	iptable := iptables.GetIptable(iptables.IPv4)
62
+
60 63
 	// insert outputChain and postroutingchain
61
-	err = iptables.RawCombinedOutputNative("-t", "nat", "-C", "OUTPUT", "-d", resolverIP, "-j", outputChain)
64
+	err = iptable.RawCombinedOutputNative("-t", "nat", "-C", "OUTPUT", "-d", resolverIP, "-j", outputChain)
62 65
 	if err == nil {
63
-		iptables.RawCombinedOutputNative("-t", "nat", "-F", outputChain)
66
+		iptable.RawCombinedOutputNative("-t", "nat", "-F", outputChain)
64 67
 	} else {
65
-		iptables.RawCombinedOutputNative("-t", "nat", "-N", outputChain)
66
-		iptables.RawCombinedOutputNative("-t", "nat", "-I", "OUTPUT", "-d", resolverIP, "-j", outputChain)
68
+		iptable.RawCombinedOutputNative("-t", "nat", "-N", outputChain)
69
+		iptable.RawCombinedOutputNative("-t", "nat", "-I", "OUTPUT", "-d", resolverIP, "-j", outputChain)
67 70
 	}
68 71
 
69
-	err = iptables.RawCombinedOutputNative("-t", "nat", "-C", "POSTROUTING", "-d", resolverIP, "-j", postroutingchain)
72
+	err = iptable.RawCombinedOutputNative("-t", "nat", "-C", "POSTROUTING", "-d", resolverIP, "-j", postroutingchain)
70 73
 	if err == nil {
71
-		iptables.RawCombinedOutputNative("-t", "nat", "-F", postroutingchain)
74
+		iptable.RawCombinedOutputNative("-t", "nat", "-F", postroutingchain)
72 75
 	} else {
73
-		iptables.RawCombinedOutputNative("-t", "nat", "-N", postroutingchain)
74
-		iptables.RawCombinedOutputNative("-t", "nat", "-I", "POSTROUTING", "-d", resolverIP, "-j", postroutingchain)
76
+		iptable.RawCombinedOutputNative("-t", "nat", "-N", postroutingchain)
77
+		iptable.RawCombinedOutputNative("-t", "nat", "-I", "POSTROUTING", "-d", resolverIP, "-j", postroutingchain)
75 78
 	}
76 79
 
77 80
 	for _, rule := range rules {
78
-		if iptables.RawCombinedOutputNative(rule...) != nil {
81
+		if iptable.RawCombinedOutputNative(rule...) != nil {
79 82
 			logrus.Errorf("set up rule failed, %v", rule)
80 83
 		}
81 84
 	}
... ...
@@ -302,6 +302,9 @@ func filterPortConfigs(ingressPorts []*PortConfig, isDelete bool) []*PortConfig
302 302
 }
303 303
 
304 304
 func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) error {
305
+	// TODO IPv6 support
306
+	iptable := iptables.GetIptable(iptables.IPv4)
307
+
305 308
 	addDelOpt := "-I"
306 309
 	rollbackAddDelOpt := "-D"
307 310
 	if isDelete {
... ...
@@ -312,19 +315,19 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
312 312
 	ingressMu.Lock()
313 313
 	defer ingressMu.Unlock()
314 314
 
315
-	chainExists := iptables.ExistChain(ingressChain, iptables.Nat)
316
-	filterChainExists := iptables.ExistChain(ingressChain, iptables.Filter)
315
+	chainExists := iptable.ExistChain(ingressChain, iptables.Nat)
316
+	filterChainExists := iptable.ExistChain(ingressChain, iptables.Filter)
317 317
 
318 318
 	ingressOnce.Do(func() {
319 319
 		// Flush nat table and filter table ingress chain rules during init if it
320 320
 		// exists. It might contain stale rules from previous life.
321 321
 		if chainExists {
322
-			if err := iptables.RawCombinedOutput("-t", "nat", "-F", ingressChain); err != nil {
322
+			if err := iptable.RawCombinedOutput("-t", "nat", "-F", ingressChain); err != nil {
323 323
 				logrus.Errorf("Could not flush nat table ingress chain rules during init: %v", err)
324 324
 			}
325 325
 		}
326 326
 		if filterChainExists {
327
-			if err := iptables.RawCombinedOutput("-F", ingressChain); err != nil {
327
+			if err := iptable.RawCombinedOutput("-F", ingressChain); err != nil {
328 328
 				logrus.Errorf("Could not flush filter table ingress chain rules during init: %v", err)
329 329
 			}
330 330
 		}
... ...
@@ -332,38 +335,38 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
332 332
 
333 333
 	if !isDelete {
334 334
 		if !chainExists {
335
-			if err := iptables.RawCombinedOutput("-t", "nat", "-N", ingressChain); err != nil {
335
+			if err := iptable.RawCombinedOutput("-t", "nat", "-N", ingressChain); err != nil {
336 336
 				return fmt.Errorf("failed to create ingress chain: %v", err)
337 337
 			}
338 338
 		}
339 339
 		if !filterChainExists {
340
-			if err := iptables.RawCombinedOutput("-N", ingressChain); err != nil {
340
+			if err := iptable.RawCombinedOutput("-N", ingressChain); err != nil {
341 341
 				return fmt.Errorf("failed to create filter table ingress chain: %v", err)
342 342
 			}
343 343
 		}
344 344
 
345
-		if !iptables.Exists(iptables.Nat, ingressChain, "-j", "RETURN") {
346
-			if err := iptables.RawCombinedOutput("-t", "nat", "-A", ingressChain, "-j", "RETURN"); err != nil {
345
+		if !iptable.Exists(iptables.Nat, ingressChain, "-j", "RETURN") {
346
+			if err := iptable.RawCombinedOutput("-t", "nat", "-A", ingressChain, "-j", "RETURN"); err != nil {
347 347
 				return fmt.Errorf("failed to add return rule in nat table ingress chain: %v", err)
348 348
 			}
349 349
 		}
350 350
 
351
-		if !iptables.Exists(iptables.Filter, ingressChain, "-j", "RETURN") {
352
-			if err := iptables.RawCombinedOutput("-A", ingressChain, "-j", "RETURN"); err != nil {
351
+		if !iptable.Exists(iptables.Filter, ingressChain, "-j", "RETURN") {
352
+			if err := iptable.RawCombinedOutput("-A", ingressChain, "-j", "RETURN"); err != nil {
353 353
 				return fmt.Errorf("failed to add return rule to filter table ingress chain: %v", err)
354 354
 			}
355 355
 		}
356 356
 
357 357
 		for _, chain := range []string{"OUTPUT", "PREROUTING"} {
358
-			if !iptables.Exists(iptables.Nat, chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain) {
359
-				if err := iptables.RawCombinedOutput("-t", "nat", "-I", chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain); err != nil {
358
+			if !iptable.Exists(iptables.Nat, chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain) {
359
+				if err := iptable.RawCombinedOutput("-t", "nat", "-I", chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain); err != nil {
360 360
 					return fmt.Errorf("failed to add jump rule in %s to ingress chain: %v", chain, err)
361 361
 				}
362 362
 			}
363 363
 		}
364 364
 
365
-		if !iptables.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
366
-			if err := iptables.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil {
365
+		if !iptable.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
366
+			if err := iptable.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil {
367 367
 				return fmt.Errorf("failed to add jump rule to %s in filter table forward chain: %v", ingressChain, err)
368 368
 			}
369 369
 			arrangeUserFilterRule()
... ...
@@ -380,8 +383,8 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
380 380
 		}
381 381
 
382 382
 		ruleArgs := strings.Fields(fmt.Sprintf("-m addrtype --src-type LOCAL -o %s -j MASQUERADE", oifName))
383
-		if !iptables.Exists(iptables.Nat, "POSTROUTING", ruleArgs...) {
384
-			if err := iptables.RawCombinedOutput(append([]string{"-t", "nat", "-I", "POSTROUTING"}, ruleArgs...)...); err != nil {
383
+		if !iptable.Exists(iptables.Nat, "POSTROUTING", ruleArgs...) {
384
+			if err := iptable.RawCombinedOutput(append([]string{"-t", "nat", "-I", "POSTROUTING"}, ruleArgs...)...); err != nil {
385 385
 				return fmt.Errorf("failed to add ingress localhost POSTROUTING rule for %s: %v", oifName, err)
386 386
 			}
387 387
 		}
... ...
@@ -395,7 +398,7 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
395 395
 		if portErr != nil && !isDelete {
396 396
 			filterPortConfigs(filteredPorts, !isDelete)
397 397
 			for _, rule := range rollbackRules {
398
-				if err := iptables.RawCombinedOutput(rule...); err != nil {
398
+				if err := iptable.RawCombinedOutput(rule...); err != nil {
399 399
 					logrus.Warnf("roll back rule failed, %v: %v", rule, err)
400 400
 				}
401 401
 			}
... ...
@@ -403,10 +406,10 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
403 403
 	}()
404 404
 
405 405
 	for _, iPort := range filteredPorts {
406
-		if iptables.ExistChain(ingressChain, iptables.Nat) {
406
+		if iptable.ExistChain(ingressChain, iptables.Nat) {
407 407
 			rule := strings.Fields(fmt.Sprintf("-t nat %s %s -p %s --dport %d -j DNAT --to-destination %s:%d",
408 408
 				addDelOpt, ingressChain, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort, gwIP, iPort.PublishedPort))
409
-			if portErr = iptables.RawCombinedOutput(rule...); portErr != nil {
409
+			if portErr = iptable.RawCombinedOutput(rule...); portErr != nil {
410 410
 				errStr := fmt.Sprintf("set up rule failed, %v: %v", rule, portErr)
411 411
 				if !isDelete {
412 412
 					return fmt.Errorf("%s", errStr)
... ...
@@ -423,7 +426,7 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
423 423
 		// 2) unmanaged containers on bridge networks
424 424
 		rule := strings.Fields(fmt.Sprintf("%s %s -m state -p %s --sport %d --state ESTABLISHED,RELATED -j ACCEPT",
425 425
 			addDelOpt, ingressChain, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort))
426
-		if portErr = iptables.RawCombinedOutput(rule...); portErr != nil {
426
+		if portErr = iptable.RawCombinedOutput(rule...); portErr != nil {
427 427
 			errStr := fmt.Sprintf("set up rule failed, %v: %v", rule, portErr)
428 428
 			if !isDelete {
429 429
 				return fmt.Errorf("%s", errStr)
... ...
@@ -436,7 +439,7 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
436 436
 
437 437
 		rule = strings.Fields(fmt.Sprintf("%s %s -p %s --dport %d -j ACCEPT",
438 438
 			addDelOpt, ingressChain, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort))
439
-		if portErr = iptables.RawCombinedOutput(rule...); portErr != nil {
439
+		if portErr = iptable.RawCombinedOutput(rule...); portErr != nil {
440 440
 			errStr := fmt.Sprintf("set up rule failed, %v: %v", rule, portErr)
441 441
 			if !isDelete {
442 442
 				return fmt.Errorf("%s", errStr)
... ...
@@ -461,13 +464,15 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
461 461
 // This chain has the rules to allow access to the published ports for swarm tasks
462 462
 // from local bridge networks and docker_gwbridge (ie:taks on other swarm networks)
463 463
 func arrangeIngressFilterRule() {
464
-	if iptables.ExistChain(ingressChain, iptables.Filter) {
465
-		if iptables.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
466
-			if err := iptables.RawCombinedOutput("-D", "FORWARD", "-j", ingressChain); err != nil {
464
+	// TODO IPv6 support
465
+	iptable := iptables.GetIptable(iptables.IPv4)
466
+	if iptable.ExistChain(ingressChain, iptables.Filter) {
467
+		if iptable.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
468
+			if err := iptable.RawCombinedOutput("-D", "FORWARD", "-j", ingressChain); err != nil {
467 469
 				logrus.Warnf("failed to delete jump rule to ingressChain in filter table: %v", err)
468 470
 			}
469 471
 		}
470
-		if err := iptables.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil {
472
+		if err := iptable.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil {
471 473
 			logrus.Warnf("failed to add jump rule to ingressChain in filter table: %v", err)
472 474
 		}
473 475
 	}
... ...
@@ -606,6 +611,8 @@ func invokeFWMarker(path string, vip net.IP, fwMark uint32, ingressPorts []*Port
606 606
 
607 607
 // Firewall marker reexec function.
608 608
 func fwMarker() {
609
+	// TODO IPv6 support
610
+	iptable := iptables.GetIptable(iptables.IPv4)
609 611
 	runtime.LockOSThread()
610 612
 	defer runtime.UnlockOSThread()
611 613
 
... ...
@@ -660,7 +667,7 @@ func fwMarker() {
660 660
 		}
661 661
 
662 662
 		ruleParams := strings.Fields(fmt.Sprintf("-m ipvs --ipvs -d %s -j SNAT --to-source %s", subnet, eIP))
663
-		if !iptables.Exists("nat", "POSTROUTING", ruleParams...) {
663
+		if !iptable.Exists("nat", "POSTROUTING", ruleParams...) {
664 664
 			rule := append(strings.Fields("-t nat -A POSTROUTING"), ruleParams...)
665 665
 			rules = append(rules, rule)
666 666
 
... ...
@@ -676,7 +683,7 @@ func fwMarker() {
676 676
 	rules = append(rules, rule)
677 677
 
678 678
 	for _, rule := range rules {
679
-		if err := iptables.RawCombinedOutputNative(rule...); err != nil {
679
+		if err := iptable.RawCombinedOutputNative(rule...); err != nil {
680 680
 			logrus.Errorf("set up rule failed, %v: %v", rule, err)
681 681
 			os.Exit(8)
682 682
 		}
... ...
@@ -711,6 +718,8 @@ func addRedirectRules(path string, eIP *net.IPNet, ingressPorts []*PortConfig) e
711 711
 
712 712
 // Redirector reexec function.
713 713
 func redirector() {
714
+	// TODO IPv6 support
715
+	iptable := iptables.GetIptable(iptables.IPv4)
714 716
 	runtime.LockOSThread()
715 717
 	defer runtime.UnlockOSThread()
716 718
 
... ...
@@ -763,7 +772,7 @@ func redirector() {
763 763
 	}
764 764
 
765 765
 	for _, rule := range rules {
766
-		if err := iptables.RawCombinedOutputNative(rule...); err != nil {
766
+		if err := iptable.RawCombinedOutputNative(rule...); err != nil {
767 767
 			logrus.Errorf("set up rule failed, %v: %v", rule, err)
768 768
 			os.Exit(6)
769 769
 		}
... ...
@@ -779,15 +788,15 @@ func redirector() {
779 779
 		{"-d", eIP.String(), "-p", "udp", "-j", "DROP"},
780 780
 		{"-d", eIP.String(), "-p", "tcp", "-j", "DROP"},
781 781
 	} {
782
-		if !iptables.ExistsNative(iptables.Filter, "INPUT", rule...) {
783
-			if err := iptables.RawCombinedOutputNative(append([]string{"-A", "INPUT"}, rule...)...); err != nil {
782
+		if !iptable.ExistsNative(iptables.Filter, "INPUT", rule...) {
783
+			if err := iptable.RawCombinedOutputNative(append([]string{"-A", "INPUT"}, rule...)...); err != nil {
784 784
 				logrus.Errorf("set up rule failed, %v: %v", rule, err)
785 785
 				os.Exit(7)
786 786
 			}
787 787
 		}
788 788
 		rule[0] = "-s"
789
-		if !iptables.ExistsNative(iptables.Filter, "OUTPUT", rule...) {
790
-			if err := iptables.RawCombinedOutputNative(append([]string{"-A", "OUTPUT"}, rule...)...); err != nil {
789
+		if !iptable.ExistsNative(iptables.Filter, "OUTPUT", rule...) {
790
+			if err := iptable.RawCombinedOutputNative(append([]string{"-A", "OUTPUT"}, rule...)...); err != nil {
791 791
 				logrus.Errorf("set up rule failed, %v: %v", rule, err)
792 792
 				os.Exit(8)
793 793
 			}