Skip to content

Commit 7b64b1c

Browse files
committed
Add internal network support for bridge networks
Signed-off-by: Chun Chen <[email protected]>
1 parent 88040e2 commit 7b64b1c

4 files changed

Lines changed: 89 additions & 40 deletions

File tree

libnetwork/drivers/bridge/bridge.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ type networkConfiguration struct {
6868
DefaultGatewayIPv6 net.IP
6969
dbIndex uint64
7070
dbExists bool
71+
Internal bool
7172
}
7273

7374
// endpointConfiguration represents the user specified configuration for the sandbox endpoint
@@ -280,16 +281,25 @@ func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) {
280281
// from each of the other networks
281282
func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) error {
282283
n.Lock()
283-
thisIface := n.config.BridgeName
284+
thisConfig := n.config
284285
n.Unlock()
285286

287+
if thisConfig.Internal {
288+
return nil
289+
}
290+
286291
// Install the rules to isolate this networks against each of the other networks
287292
for _, o := range others {
288293
o.Lock()
289-
otherIface := o.config.BridgeName
294+
otherConfig := o.config
290295
o.Unlock()
291-
if thisIface != otherIface {
292-
if err := setINC(thisIface, otherIface, enable); err != nil {
296+
297+
if otherConfig.Internal {
298+
continue
299+
}
300+
301+
if thisConfig.BridgeName != otherConfig.BridgeName {
302+
if err := setINC(thisConfig.BridgeName, otherConfig.BridgeName, enable); err != nil {
293303
return err
294304
}
295305
}
@@ -483,7 +493,7 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati
483493

484494
if val, ok := option[netlabel.Internal]; ok {
485495
if internal, ok := val.(bool); ok && internal {
486-
return nil, &driverapi.ErrNotImplemented{}
496+
config.Internal = true
487497
}
488498
}
489499

libnetwork/drivers/bridge/setup_ip_tables.go

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -82,38 +82,46 @@ func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInt
8282
IP: ipnet.IP.Mask(ipnet.Mask),
8383
Mask: ipnet.Mask,
8484
}
85-
if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
86-
return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
87-
}
88-
n.registerIptCleanFunc(func() error {
89-
return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
90-
})
85+
if config.Internal {
86+
if err = setupInternalNetworkRules(config.BridgeName, maskedAddrv4, true); err != nil {
87+
return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
88+
}
89+
n.registerIptCleanFunc(func() error {
90+
return setupInternalNetworkRules(config.BridgeName, maskedAddrv4, false)
91+
})
92+
} else {
93+
if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
94+
return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
95+
}
96+
n.registerIptCleanFunc(func() error {
97+
return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false)
98+
})
99+
natChain, filterChain, _, err := n.getDriverChains()
100+
if err != nil {
101+
return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
102+
}
91103

92-
natChain, filterChain, _, err := n.getDriverChains()
93-
if err != nil {
94-
return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
95-
}
104+
err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
105+
if err != nil {
106+
return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
107+
}
96108

97-
err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true)
98-
if err != nil {
99-
return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
100-
}
109+
err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
110+
if err != nil {
111+
return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
112+
}
101113

102-
err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true)
103-
if err != nil {
104-
return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
114+
n.registerIptCleanFunc(func() error {
115+
return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
116+
})
117+
118+
n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName())
105119
}
106120

107121
if err := ensureJumpRule("FORWARD", IsolationChain); err != nil {
108122
return err
109123
}
110124

111-
n.registerIptCleanFunc(func() error {
112-
return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
113-
})
114-
115-
n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName())
116-
117125
return nil
118126
}
119127

@@ -312,12 +320,26 @@ func ensureJumpRule(fromChain, toChain string) error {
312320

313321
func removeIPChains() {
314322
for _, chainInfo := range []iptables.ChainInfo{
315-
iptables.ChainInfo{Name: DockerChain, Table: iptables.Nat},
316-
iptables.ChainInfo{Name: DockerChain, Table: iptables.Filter},
317-
iptables.ChainInfo{Name: IsolationChain, Table: iptables.Filter},
323+
{Name: DockerChain, Table: iptables.Nat},
324+
{Name: DockerChain, Table: iptables.Filter},
325+
{Name: IsolationChain, Table: iptables.Filter},
318326
} {
319327
if err := chainInfo.Remove(); err != nil {
320328
logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err)
321329
}
322330
}
323331
}
332+
333+
func setupInternalNetworkRules(bridgeIface string, addr net.Addr, insert bool) error {
334+
var (
335+
inDropRule = iptRule{table: iptables.Filter, chain: IsolationChain, args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"}}
336+
outDropRule = iptRule{table: iptables.Filter, chain: IsolationChain, args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"}}
337+
)
338+
if err := programChainRule(inDropRule, "DROP INCOMING", insert); err != nil {
339+
return err
340+
}
341+
if err := programChainRule(outDropRule, "DROP OUTGOING", insert); err != nil {
342+
return err
343+
}
344+
return nil
345+
}

libnetwork/libnetwork_test.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,10 +2352,3 @@ func TestParallel2(t *testing.T) {
23522352
func TestParallel3(t *testing.T) {
23532353
runParallelTests(t, 3)
23542354
}
2355-
2356-
func TestNetworkInternal(t *testing.T) {
2357-
_, err := controller.NewNetwork(bridgeNetType, "testnetworkinternal", libnetwork.NetworkOptionInternalNetwork())
2358-
if err == nil || err.Error() != (&driverapi.ErrNotImplemented{}).Error() {
2359-
t.Fatal("bridge network can't be internal")
2360-
}
2361-
}

libnetwork/test/integration/dnet/bridge.bats

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ function test_single_network_connectivity() {
2020
# Now test connectivity between all the containers using service names
2121
for i in `seq ${start} ${end}`;
2222
do
23-
runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) \
24-
"ping -c 1 www.google.com"
23+
if [ "${nw_name}" != "internal" ]; then
24+
runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) \
25+
"ping -c 1 www.google.com"
26+
fi
2527
for j in `seq ${start} ${end}`;
2628
do
2729
if [ "$i" -eq "$j" ]; then
@@ -250,6 +252,7 @@ function test_single_network_connectivity() {
250252
dnet_cmd $(inst_id2port 1) network rm br1
251253
}
252254

255+
253256
@test "Test bridge network global alias support" {
254257
skip_for_circleci
255258
dnet_cmd $(inst_id2port 1) network create -d bridge br1
@@ -279,3 +282,24 @@ function test_single_network_connectivity() {
279282

280283
dnet_cmd $(inst_id2port 1) network rm br1
281284
}
285+
286+
@test "Test bridge network internal network" {
287+
skip_for_circleci
288+
289+
echo $(docker ps)
290+
dnet_cmd $(inst_id2port 1) network create -d bridge --internal internal
291+
dnet_cmd $(inst_id2port 1) container create container_1
292+
# connects to internal network, confirm it can't conmunicate with outside world
293+
net_connect 1 container_1 internal
294+
run runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 www.google.com"
295+
[[ "$output" == *"1 packets transmitted, 0 packets received, 100% packet loss"* ]]
296+
net_disconnect 1 container_1 internal
297+
# connects to bridge network, confirm it can conmunicate with outside world
298+
net_connect 1 container_1 bridge
299+
runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 www.google.com"
300+
net_disconnect 1 container_1 bridge
301+
dnet_cmd $(inst_id2port 1) container rm container_1
302+
# test conmunications within internal network
303+
test_single_network_connectivity internal 3
304+
dnet_cmd $(inst_id2port 1) network rm internal
305+
}

0 commit comments

Comments
 (0)