Browse code

Add internal network support for bridge networks

Signed-off-by: Chun Chen <ramichen@tencent.com>

Chun Chen authored on 2016/01/06 22:06:23
Showing 4 changed files
... ...
@@ -68,6 +68,7 @@ type networkConfiguration struct {
68 68
 	DefaultGatewayIPv6 net.IP
69 69
 	dbIndex            uint64
70 70
 	dbExists           bool
71
+	Internal           bool
71 72
 }
72 73
 
73 74
 // endpointConfiguration represents the user specified configuration for the sandbox endpoint
... ...
@@ -280,16 +281,25 @@ func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) {
280 280
 // from each of the other networks
281 281
 func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) error {
282 282
 	n.Lock()
283
-	thisIface := n.config.BridgeName
283
+	thisConfig := n.config
284 284
 	n.Unlock()
285 285
 
286
+	if thisConfig.Internal {
287
+		return nil
288
+	}
289
+
286 290
 	// Install the rules to isolate this networks against each of the other networks
287 291
 	for _, o := range others {
288 292
 		o.Lock()
289
-		otherIface := o.config.BridgeName
293
+		otherConfig := o.config
290 294
 		o.Unlock()
291
-		if thisIface != otherIface {
292
-			if err := setINC(thisIface, otherIface, enable); err != nil {
295
+
296
+		if otherConfig.Internal {
297
+			continue
298
+		}
299
+
300
+		if thisConfig.BridgeName != otherConfig.BridgeName {
301
+			if err := setINC(thisConfig.BridgeName, otherConfig.BridgeName, enable); err != nil {
293 302
 				return err
294 303
 			}
295 304
 		}
... ...
@@ -483,7 +493,7 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati
483 483
 
484 484
 	if val, ok := option[netlabel.Internal]; ok {
485 485
 		if internal, ok := val.(bool); ok && internal {
486
-			return nil, &driverapi.ErrNotImplemented{}
486
+			config.Internal = true
487 487
 		}
488 488
 	}
489 489
 
... ...
@@ -82,38 +82,46 @@ func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInt
82 82
 		IP:   ipnet.IP.Mask(ipnet.Mask),
83 83
 		Mask: ipnet.Mask,
84 84
 	}
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
+		}
91 103
 
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
+		}
96 108
 
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
+		}
101 113
 
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())
105 119
 	}
106 120
 
107 121
 	if err := ensureJumpRule("FORWARD", IsolationChain); err != nil {
108 122
 		return err
109 123
 	}
110 124
 
111
-	n.registerIptCleanFunc(func() error {
112
-		return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false)
113
-	})
114
-
115
-	n.portMapper.SetIptablesChain(filterChain, n.getNetworkBridgeName())
116
-
117 125
 	return nil
118 126
 }
119 127
 
... ...
@@ -312,12 +320,26 @@ func ensureJumpRule(fromChain, toChain string) error {
312 312
 
313 313
 func removeIPChains() {
314 314
 	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},
315
+		{Name: DockerChain, Table: iptables.Nat},
316
+		{Name: DockerChain, Table: iptables.Filter},
317
+		{Name: IsolationChain, Table: iptables.Filter},
318 318
 	} {
319 319
 		if err := chainInfo.Remove(); err != nil {
320 320
 			logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err)
321 321
 		}
322 322
 	}
323 323
 }
324
+
325
+func setupInternalNetworkRules(bridgeIface string, addr net.Addr, insert bool) error {
326
+	var (
327
+		inDropRule  = iptRule{table: iptables.Filter, chain: IsolationChain, args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"}}
328
+		outDropRule = iptRule{table: iptables.Filter, chain: IsolationChain, args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"}}
329
+	)
330
+	if err := programChainRule(inDropRule, "DROP INCOMING", insert); err != nil {
331
+		return err
332
+	}
333
+	if err := programChainRule(outDropRule, "DROP OUTGOING", insert); err != nil {
334
+		return err
335
+	}
336
+	return nil
337
+}
... ...
@@ -2352,10 +2352,3 @@ func TestParallel2(t *testing.T) {
2352 2352
 func TestParallel3(t *testing.T) {
2353 2353
 	runParallelTests(t, 3)
2354 2354
 }
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
-}
... ...
@@ -20,8 +20,10 @@ function test_single_network_connectivity() {
20 20
     # Now test connectivity between all the containers using service names
21 21
     for i in `seq ${start} ${end}`;
22 22
     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
25 27
 	for j in `seq ${start} ${end}`;
26 28
 	do
27 29
 	    if [ "$i" -eq "$j" ]; then
... ...
@@ -250,6 +252,7 @@ function test_single_network_connectivity() {
250 250
     dnet_cmd $(inst_id2port 1) network rm br1
251 251
 }
252 252
 
253
+
253 254
 @test "Test bridge network global alias support" {
254 255
     skip_for_circleci
255 256
     dnet_cmd $(inst_id2port 1) network create -d bridge br1
... ...
@@ -279,3 +282,24 @@ function test_single_network_connectivity() {
279 279
 
280 280
     dnet_cmd $(inst_id2port 1) network rm br1
281 281
 }
282
+
283
+@test "Test bridge network internal network" {
284
+    skip_for_circleci
285
+
286
+    echo $(docker ps)
287
+    dnet_cmd $(inst_id2port 1) network create -d bridge --internal internal
288
+    dnet_cmd $(inst_id2port 1) container create container_1
289
+    # connects to internal network, confirm it can't conmunicate with outside world
290
+    net_connect 1 container_1 internal
291
+    run runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 www.google.com"
292
+    [[ "$output" == *"1 packets transmitted, 0 packets received, 100% packet loss"* ]]
293
+    net_disconnect 1 container_1 internal
294
+    # connects to bridge network, confirm it can conmunicate with outside world
295
+    net_connect 1 container_1 bridge
296
+    runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 www.google.com"
297
+    net_disconnect 1 container_1 bridge
298
+    dnet_cmd $(inst_id2port 1) container rm container_1
299
+    # test conmunications within internal network
300
+    test_single_network_connectivity internal 3
301
+    dnet_cmd $(inst_id2port 1) network rm internal
302
+}