Before this commit, setting the `com.docker.network.host_ipv4` bridge
option when `enable_ipv6` is true and the experimental `ip6tables`
option is enabled would cause Docker to fail to create the network:
> failed to create network `test-network`: Error response from daemon:
> Failed to Setup IP tables: Unable to enable NAT rule: (iptables
> failed: `ip6tables --wait -t nat -I POSTROUTING -s fd01::/64 ! -o
> br-test -j SNAT --to-source 192.168.0.2`: ip6tables
> v1.8.7 (nf_tables): Bad IP address "192.168.0.2"
>
> Try `ip6tables -h` or `ip6tables --help` for more information.
> (exit status 2))
Fix this error by passing nil -- not the `host_ipv4` address -- when
creating the IPv6 rules.
Signed-off-by: Richard Hansen <rhansen@rhansen.org>
| ... | ... |
@@ -157,11 +157,15 @@ func (n *bridgeNetwork) setupIPTables(ipVersion iptables.IPVersion, maskedAddr * |
| 157 | 157 |
return setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, false) |
| 158 | 158 |
}) |
| 159 | 159 |
} else {
|
| 160 |
- if err = setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
|
|
| 160 |
+ hostIP := config.HostIP |
|
| 161 |
+ if ipVersion != iptables.IPv4 {
|
|
| 162 |
+ hostIP = nil |
|
| 163 |
+ } |
|
| 164 |
+ if err = setupIPTablesInternal(hostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
|
|
| 161 | 165 |
return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
|
| 162 | 166 |
} |
| 163 | 167 |
n.registerIptCleanFunc(func() error {
|
| 164 |
- return setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false) |
|
| 168 |
+ return setupIPTablesInternal(hostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false) |
|
| 165 | 169 |
}) |
| 166 | 170 |
natChain, filterChain, _, _, err := n.getDriverChains(ipVersion) |
| 167 | 171 |
if err != nil {
|
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
|
| 7 | 7 |
"github.com/docker/docker/internal/testutils/netnsutils" |
| 8 | 8 |
"github.com/docker/docker/libnetwork/iptables" |
| 9 |
+ "github.com/docker/docker/libnetwork/netlabel" |
|
| 9 | 10 |
"github.com/docker/docker/libnetwork/portmapper" |
| 10 | 11 |
"github.com/vishvananda/netlink" |
| 11 | 12 |
) |
| ... | ... |
@@ -92,6 +93,11 @@ func createTestBridge(config *networkConfiguration, br *bridgeInterface, t *test |
| 92 | 92 |
if err := setupBridgeIPv4(config, br); err != nil {
|
| 93 | 93 |
t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
|
| 94 | 94 |
} |
| 95 |
+ if config.EnableIPv6 {
|
|
| 96 |
+ if err := setupBridgeIPv6(config, br); err != nil {
|
|
| 97 |
+ t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
|
|
| 98 |
+ } |
|
| 99 |
+ } |
|
| 95 | 100 |
} |
| 96 | 101 |
|
| 97 | 102 |
// Assert base function which pushes iptables chain rules on insertion and removal. |
| ... | ... |
@@ -123,13 +129,20 @@ func assertChainConfig(d *driver, t *testing.T) {
|
| 123 | 123 |
if err != nil {
|
| 124 | 124 |
t.Fatal(err) |
| 125 | 125 |
} |
| 126 |
+ if d.config.EnableIP6Tables {
|
|
| 127 |
+ d.natChainV6, d.filterChainV6, d.isolationChain1V6, d.isolationChain2V6, err = setupIPChains(d.config, iptables.IPv6) |
|
| 128 |
+ if err != nil {
|
|
| 129 |
+ t.Fatal(err) |
|
| 130 |
+ } |
|
| 131 |
+ } |
|
| 126 | 132 |
} |
| 127 | 133 |
|
| 128 | 134 |
// Assert function which pushes chains based on bridge config parameters. |
| 129 | 135 |
func assertBridgeConfig(config *networkConfiguration, br *bridgeInterface, d *driver, t *testing.T) {
|
| 130 | 136 |
nw := bridgeNetwork{
|
| 131 |
- portMapper: portmapper.New(""),
|
|
| 132 |
- config: config, |
|
| 137 |
+ portMapper: portmapper.New(""),
|
|
| 138 |
+ portMapperV6: portmapper.New(""),
|
|
| 139 |
+ config: config, |
|
| 133 | 140 |
} |
| 134 | 141 |
nw.driver = d |
| 135 | 142 |
|
| ... | ... |
@@ -138,4 +151,37 @@ func assertBridgeConfig(config *networkConfiguration, br *bridgeInterface, d *dr |
| 138 | 138 |
if err != nil {
|
| 139 | 139 |
t.Fatalf("%v", err)
|
| 140 | 140 |
} |
| 141 |
+ if d.config.EnableIP6Tables {
|
|
| 142 |
+ if err := nw.setupIP6Tables(config, br); err != nil {
|
|
| 143 |
+ t.Fatalf("%v", err)
|
|
| 144 |
+ } |
|
| 145 |
+ } |
|
| 146 |
+} |
|
| 147 |
+ |
|
| 148 |
+// Regression test for https://github.com/moby/moby/issues/46445 |
|
| 149 |
+func TestSetupIP6TablesWithHostIP(t *testing.T) {
|
|
| 150 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 151 |
+ d := newDriver() |
|
| 152 |
+ dc := &configuration{
|
|
| 153 |
+ EnableIPTables: true, |
|
| 154 |
+ EnableIP6Tables: true, |
|
| 155 |
+ } |
|
| 156 |
+ if err := d.configure(map[string]interface{}{netlabel.GenericData: dc}); err != nil {
|
|
| 157 |
+ t.Fatal(err) |
|
| 158 |
+ } |
|
| 159 |
+ nc := &networkConfiguration{
|
|
| 160 |
+ BridgeName: DefaultBridgeName, |
|
| 161 |
+ AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)},
|
|
| 162 |
+ EnableIPMasquerade: true, |
|
| 163 |
+ EnableIPv6: true, |
|
| 164 |
+ AddressIPv6: &net.IPNet{IP: net.ParseIP("2001:db8::1"), Mask: net.CIDRMask(64, 128)},
|
|
| 165 |
+ HostIP: net.ParseIP("192.0.2.2"),
|
|
| 166 |
+ } |
|
| 167 |
+ nh, err := netlink.NewHandle() |
|
| 168 |
+ if err != nil {
|
|
| 169 |
+ t.Fatal(err) |
|
| 170 |
+ } |
|
| 171 |
+ br := &bridgeInterface{nlh: nh}
|
|
| 172 |
+ createTestBridge(nc, br, t) |
|
| 173 |
+ assertBridgeConfig(nc, br, d, t) |
|
| 141 | 174 |
} |