Skip to content

Commit dc6cbb5

Browse files
authored
Merge pull request moby#2572 from bboehmke/ipv6_nat
Enable IPv6 NAT (rebase of moby#2023)
2 parents 20c88eb + 34f4706 commit dc6cbb5

18 files changed

+576
-294
lines changed

libnetwork/drivers/bridge/bridge.go

Lines changed: 87 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type iptablesCleanFuncs []iptableCleanFunc
5757
type configuration struct {
5858
EnableIPForwarding bool
5959
EnableIPTables bool
60+
EnableIP6Tables bool
6061
EnableUserlandProxy bool
6162
UserlandProxyPath string
6263
}
@@ -133,22 +134,27 @@ type bridgeNetwork struct {
133134
config *networkConfiguration
134135
endpoints map[string]*bridgeEndpoint // key: endpoint id
135136
portMapper *portmapper.PortMapper
137+
portMapperV6 *portmapper.PortMapper
136138
driver *driver // The network's driver
137139
iptCleanFuncs iptablesCleanFuncs
138140
sync.Mutex
139141
}
140142

141143
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
144+
config *configuration
145+
network *bridgeNetwork
146+
natChain *iptables.ChainInfo
147+
filterChain *iptables.ChainInfo
148+
isolationChain1 *iptables.ChainInfo
149+
isolationChain2 *iptables.ChainInfo
150+
natChainV6 *iptables.ChainInfo
151+
filterChainV6 *iptables.ChainInfo
152+
isolationChain1V6 *iptables.ChainInfo
153+
isolationChain2V6 *iptables.ChainInfo
154+
networks map[string]*bridgeNetwork
155+
store datastore.DataStore
156+
nlh *netlink.Handle
157+
configNetwork sync.Mutex
152158
sync.Mutex
153159
}
154160

@@ -277,14 +283,18 @@ func (n *bridgeNetwork) registerIptCleanFunc(clean iptableCleanFunc) {
277283
n.iptCleanFuncs = append(n.iptCleanFuncs, clean)
278284
}
279285

280-
func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
286+
func (n *bridgeNetwork) getDriverChains(version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
281287
n.Lock()
282288
defer n.Unlock()
283289

284290
if n.driver == nil {
285291
return nil, nil, nil, nil, types.BadRequestErrorf("no driver found")
286292
}
287293

294+
if version == iptables.IPv6 {
295+
return n.driver.natChainV6, n.driver.filterChainV6, n.driver.isolationChain1V6, n.driver.isolationChain2V6, nil
296+
}
297+
288298
return n.driver.natChain, n.driver.filterChain, n.driver.isolationChain1, n.driver.isolationChain2, nil
289299
}
290300

@@ -323,17 +333,31 @@ func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) err
323333
}
324334

325335
// Install the rules to isolate this network against each of the other networks
326-
return setINC(thisConfig.BridgeName, enable)
336+
if n.driver.config.EnableIP6Tables {
337+
err := setINC(iptables.IPv6, thisConfig.BridgeName, enable)
338+
if err != nil {
339+
return err
340+
}
341+
}
342+
343+
if n.driver.config.EnableIPTables {
344+
return setINC(iptables.IPv4, thisConfig.BridgeName, enable)
345+
}
346+
return nil
327347
}
328348

329349
func (d *driver) configure(option map[string]interface{}) error {
330350
var (
331-
config *configuration
332-
err error
333-
natChain *iptables.ChainInfo
334-
filterChain *iptables.ChainInfo
335-
isolationChain1 *iptables.ChainInfo
336-
isolationChain2 *iptables.ChainInfo
351+
config *configuration
352+
err error
353+
natChain *iptables.ChainInfo
354+
filterChain *iptables.ChainInfo
355+
isolationChain1 *iptables.ChainInfo
356+
isolationChain2 *iptables.ChainInfo
357+
natChainV6 *iptables.ChainInfo
358+
filterChainV6 *iptables.ChainInfo
359+
isolationChain1V6 *iptables.ChainInfo
360+
isolationChain2V6 *iptables.ChainInfo
337361
)
338362

339363
genericData, ok := option[netlabel.GenericData]
@@ -354,23 +378,46 @@ func (d *driver) configure(option map[string]interface{}) error {
354378
return &ErrInvalidDriverConfig{}
355379
}
356380

357-
if config.EnableIPTables {
381+
if config.EnableIPTables || config.EnableIP6Tables {
358382
if _, err := os.Stat("/proc/sys/net/bridge"); err != nil {
359383
if out, err := exec.Command("modprobe", "-va", "bridge", "br_netfilter").CombinedOutput(); err != nil {
360384
logrus.Warnf("Running modprobe bridge br_netfilter failed with message: %s, error: %v", out, err)
361385
}
362386
}
363-
removeIPChains()
364-
natChain, filterChain, isolationChain1, isolationChain2, err = setupIPChains(config)
387+
}
388+
389+
if config.EnableIPTables {
390+
removeIPChains(iptables.IPv4)
391+
392+
natChain, filterChain, isolationChain1, isolationChain2, err = setupIPChains(config, iptables.IPv4)
393+
if err != nil {
394+
return err
395+
}
396+
397+
// Make sure on firewall reload, first thing being re-played is chains creation
398+
iptables.OnReloaded(func() {
399+
logrus.Debugf("Recreating iptables chains on firewall reload")
400+
setupIPChains(config, iptables.IPv4)
401+
})
402+
}
403+
404+
if config.EnableIP6Tables {
405+
removeIPChains(iptables.IPv6)
406+
407+
natChainV6, filterChainV6, isolationChain1V6, isolationChain2V6, err = setupIPChains(config, iptables.IPv6)
365408
if err != nil {
366409
return err
367410
}
411+
368412
// 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) })
413+
iptables.OnReloaded(func() {
414+
logrus.Debugf("Recreating ip6tables chains on firewall reload")
415+
setupIPChains(config, iptables.IPv6)
416+
})
370417
}
371418

372419
if config.EnableIPForwarding {
373-
err = setupIPForwarding(config.EnableIPTables)
420+
err = setupIPForwarding(config.EnableIPTables, config.EnableIP6Tables)
374421
if err != nil {
375422
logrus.Warn(err)
376423
return err
@@ -382,6 +429,10 @@ func (d *driver) configure(option map[string]interface{}) error {
382429
d.filterChain = filterChain
383430
d.isolationChain1 = isolationChain1
384431
d.isolationChain2 = isolationChain2
432+
d.natChainV6 = natChainV6
433+
d.filterChainV6 = filterChainV6
434+
d.isolationChain1V6 = isolationChain1V6
435+
d.isolationChain2V6 = isolationChain2V6
385436
d.config = config
386437
d.Unlock()
387438

@@ -644,12 +695,13 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) {
644695

645696
// Create and set network handler in driver
646697
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,
698+
id: config.ID,
699+
endpoints: make(map[string]*bridgeEndpoint),
700+
config: config,
701+
portMapper: portmapper.New(d.config.UserlandProxyPath),
702+
portMapperV6: portmapper.New(d.config.UserlandProxyPath),
703+
bridge: bridgeIface,
704+
driver: d,
653705
}
654706

655707
d.Lock()
@@ -724,11 +776,16 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) {
724776
{!d.config.EnableUserlandProxy, setupLoopbackAddressesRouting},
725777

726778
// Setup IPTables.
727-
{d.config.EnableIPTables, network.setupIPTables},
779+
{d.config.EnableIPTables, network.setupIP4Tables},
780+
781+
// Setup IP6Tables.
782+
{d.config.EnableIP6Tables, network.setupIP6Tables},
728783

729784
//We want to track firewalld configuration so that
730785
//if it is started/reloaded, the rules can be applied correctly
731786
{d.config.EnableIPTables, network.setupFirewalld},
787+
// same for IPv6
788+
{d.config.EnableIP6Tables, network.setupFirewalld6},
732789

733790
// Setup DefaultGatewayIPv4
734791
{config.DefaultGatewayIPv4 != nil, setupGatewayIPv4},

libnetwork/drivers/bridge/bridge_test.go

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -468,11 +468,12 @@ func TestCreateMultipleNetworks(t *testing.T) {
468468

469469
// Verify the network isolation rules are installed for each network
470470
func verifyV4INCEntries(networks map[string]*bridgeNetwork, t *testing.T) {
471-
out1, err := iptables.Raw("-S", IsolationChain1)
471+
iptable := iptables.GetIptable(iptables.IPv4)
472+
out1, err := iptable.Raw("-S", IsolationChain1)
472473
if err != nil {
473474
t.Fatal(err)
474475
}
475-
out2, err := iptables.Raw("-S", IsolationChain2)
476+
out2, err := iptable.Raw("-S", IsolationChain2)
476477
if err != nil {
477478
t.Fatal(err)
478479
}
@@ -715,6 +716,7 @@ func TestLinkContainers(t *testing.T) {
715716
}
716717

717718
d := newDriver()
719+
iptable := iptables.GetIptable(iptables.IPv4)
718720

719721
config := &configuration{
720722
EnableIPTables: true,
@@ -790,7 +792,7 @@ func TestLinkContainers(t *testing.T) {
790792
t.Fatalf("Failed to program external connectivity: %v", err)
791793
}
792794

793-
out, err := iptables.Raw("-L", DockerChain)
795+
out, err := iptable.Raw("-L", DockerChain)
794796
for _, pm := range exposedPorts {
795797
regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
796798
re := regexp.MustCompile(regex)
@@ -816,7 +818,7 @@ func TestLinkContainers(t *testing.T) {
816818
t.Fatal("Failed to unlink ep1 and ep2")
817819
}
818820

819-
out, err = iptables.Raw("-L", DockerChain)
821+
out, err = iptable.Raw("-L", DockerChain)
820822
for _, pm := range exposedPorts {
821823
regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
822824
re := regexp.MustCompile(regex)
@@ -844,7 +846,7 @@ func TestLinkContainers(t *testing.T) {
844846
}
845847
err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions)
846848
if err != nil {
847-
out, err = iptables.Raw("-L", DockerChain)
849+
out, err = iptable.Raw("-L", DockerChain)
848850
for _, pm := range exposedPorts {
849851
regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
850852
re := regexp.MustCompile(regex)
@@ -998,18 +1000,25 @@ func TestCleanupIptableRules(t *testing.T) {
9981000
{Name: DockerChain, Table: iptables.Filter},
9991001
{Name: IsolationChain1, Table: iptables.Filter},
10001002
}
1001-
if _, _, _, _, err := setupIPChains(&configuration{EnableIPTables: true}); err != nil {
1002-
t.Fatalf("Error setting up ip chains: %v", err)
1003-
}
1004-
for _, chainInfo := range bridgeChain {
1005-
if !iptables.ExistChain(chainInfo.Name, chainInfo.Table) {
1006-
t.Fatalf("iptables chain %s of %s table should have been created", chainInfo.Name, chainInfo.Table)
1003+
1004+
ipVersions := []iptables.IPVersion{iptables.IPv4, iptables.IPv6}
1005+
1006+
for _, version := range ipVersions {
1007+
if _, _, _, _, err := setupIPChains(&configuration{EnableIPTables: true}, version); err != nil {
1008+
t.Fatalf("Error setting up ip chains for %s: %v", version, err)
10071009
}
1008-
}
1009-
removeIPChains()
1010-
for _, chainInfo := range bridgeChain {
1011-
if iptables.ExistChain(chainInfo.Name, chainInfo.Table) {
1012-
t.Fatalf("iptables chain %s of %s table should have been deleted", chainInfo.Name, chainInfo.Table)
1010+
1011+
iptable := iptables.GetIptable(version)
1012+
for _, chainInfo := range bridgeChain {
1013+
if !iptable.ExistChain(chainInfo.Name, chainInfo.Table) {
1014+
t.Fatalf("iptables version %s chain %s of %s table should have been created", version, chainInfo.Name, chainInfo.Table)
1015+
}
1016+
}
1017+
removeIPChains(version)
1018+
for _, chainInfo := range bridgeChain {
1019+
if iptable.ExistChain(chainInfo.Name, chainInfo.Table) {
1020+
t.Fatalf("iptables version %s chain %s of %s table should have been deleted", version, chainInfo.Name, chainInfo.Table)
1021+
}
10131022
}
10141023
}
10151024
}

libnetwork/drivers/bridge/port_mapping.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212
)
1313

1414
var (
15-
defaultBindingIP = net.IPv4(0, 0, 0, 0)
15+
defaultBindingIP = net.IPv4(0, 0, 0, 0)
16+
defaultBindingIPV6 = net.ParseIP("::")
1617
)
1718

1819
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
2526
defHostIP = reqDefBindIP
2627
}
2728

28-
return n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled)
29+
// IPv4 port binding including user land proxy
30+
pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
// IPv6 port binding excluding user land proxy
36+
if n.driver.config.EnableIP6Tables && ep.addrv6 != nil {
37+
// TODO IPv6 custom default binding IP
38+
pbv6, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addrv6.IP, defaultBindingIPV6, false)
39+
if err != nil {
40+
// ensure we clear the previous allocated IPv4 ports
41+
n.releasePortsInternal(pb)
42+
return nil, err
43+
}
44+
45+
pb = append(pb, pbv6...)
46+
}
47+
return pb, nil
2948
}
3049

3150
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
6988
return err
7089
}
7190

91+
portmapper := n.portMapper
92+
93+
if containerIP.To4() == nil {
94+
portmapper = n.portMapperV6
95+
}
96+
7297
// Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
7398
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 {
99+
if host, err = portmapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil {
75100
break
76101
}
77102
// 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 {
128153
if err != nil {
129154
return err
130155
}
131-
return n.portMapper.Unmap(host)
156+
157+
portmapper := n.portMapper
158+
159+
if bnd.HostIP.To4() == nil {
160+
portmapper = n.portMapperV6
161+
}
162+
163+
return portmapper.Unmap(host)
132164
}

libnetwork/drivers/bridge/setup_firewalld.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,23 @@ func (n *bridgeNetwork) setupFirewalld(config *networkConfiguration, i *bridgeIn
1313
return IPTableCfgError(config.BridgeName)
1414
}
1515

16-
iptables.OnReloaded(func() { n.setupIPTables(config, i) })
16+
iptables.OnReloaded(func() { n.setupIP4Tables(config, i) })
1717
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+
}
1831

32+
iptables.OnReloaded(func() { n.setupIP6Tables(config, i) })
33+
iptables.OnReloaded(n.portMapperV6.ReMapAll)
1934
return nil
2035
}

0 commit comments

Comments
 (0)