@@ -3,6 +3,7 @@ package networking
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "net"
6
7
"os/exec"
7
8
"regexp"
8
9
"strings"
@@ -14,9 +15,12 @@ import (
14
15
"github.com/docker/docker/client"
15
16
"github.com/docker/docker/integration/internal/container"
16
17
"github.com/docker/docker/integration/internal/network"
18
+ "github.com/docker/docker/internal/testutils/networking"
19
+ "github.com/docker/docker/libnetwork/drivers/bridge"
17
20
"github.com/docker/docker/libnetwork/netlabel"
18
21
"github.com/docker/docker/testutil"
19
22
"github.com/docker/docker/testutil/daemon"
23
+ "github.com/docker/go-connections/nat"
20
24
"github.com/google/go-cmp/cmp/cmpopts"
21
25
"gotest.tools/v3/assert"
22
26
is "gotest.tools/v3/assert/cmp"
@@ -335,6 +339,153 @@ func TestBridgeINC(t *testing.T) {
335
339
}
336
340
}
337
341
342
+ // TestBridgeINCRouted makes sure a container on a gateway-mode=nat network can establish
343
+ // a connection to a container on a gateway-mode=routed network, but not vice-versa.
344
+ func TestBridgeINCRouted (t * testing.T ) {
345
+ ctx := setupTest (t )
346
+
347
+ d := daemon .New (t )
348
+ d .StartWithBusybox (ctx , t )
349
+ t .Cleanup (func () { d .Stop (t ) })
350
+
351
+ c := d .NewClientT (t )
352
+ t .Cleanup (func () { c .Close () })
353
+
354
+ type ctrDesc struct {
355
+ id string
356
+ ipv4 string
357
+ ipv6 string
358
+ }
359
+
360
+ // Create a network and run a container on it.
361
+ // Run http servers on ports 80 and 81, but only map/open port 80.
362
+ createNet := func (gwMode string ) ctrDesc {
363
+ netName := "test-" + gwMode
364
+ network .CreateNoError (ctx , t , c , netName ,
365
+ network .WithDriver ("bridge" ),
366
+ network .WithIPv6 (),
367
+ network .WithOption (bridge .BridgeName , "br-" + gwMode ),
368
+ network .WithOption (bridge .IPv4GatewayMode , gwMode ),
369
+ network .WithOption (bridge .IPv6GatewayMode , gwMode ),
370
+ )
371
+ t .Cleanup (func () {
372
+ network .RemoveNoError (ctx , t , c , netName )
373
+ })
374
+
375
+ ctrId := container .Run (ctx , t , c ,
376
+ container .WithNetworkMode (netName ),
377
+ container .WithName ("ctr-" + gwMode ),
378
+ container .WithExposedPorts ("80/tcp" ),
379
+ container .WithPortMap (nat.PortMap {"80/tcp" : {}}),
380
+ )
381
+ t .Cleanup (func () {
382
+ c .ContainerRemove (ctx , ctrId , containertypes.RemoveOptions {Force : true })
383
+ })
384
+
385
+ container .ExecT (ctx , t , c , ctrId , []string {"httpd" , "-p" , "80" })
386
+ container .ExecT (ctx , t , c , ctrId , []string {"httpd" , "-p" , "81" })
387
+
388
+ insp := container .Inspect (ctx , t , c , ctrId )
389
+ return ctrDesc {
390
+ id : ctrId ,
391
+ ipv4 : insp .NetworkSettings .Networks [netName ].IPAddress ,
392
+ ipv6 : insp .NetworkSettings .Networks [netName ].GlobalIPv6Address ,
393
+ }
394
+ }
395
+
396
+ natDesc := createNet ("nat" )
397
+ routedDesc := createNet ("routed" )
398
+
399
+ const (
400
+ httpSuccess = "404 Not Found"
401
+ httpFail = "download timed out"
402
+ pingSuccess = 0
403
+ pingFail = 1
404
+ )
405
+
406
+ testcases := []struct {
407
+ name string
408
+ from ctrDesc
409
+ to ctrDesc
410
+ port string
411
+ expPingExit int
412
+ expHttpStderr string
413
+ }{
414
+ {
415
+ name : "nat to routed open port" ,
416
+ from : natDesc ,
417
+ to : routedDesc ,
418
+ port : "80" ,
419
+ expPingExit : pingSuccess ,
420
+ expHttpStderr : httpSuccess ,
421
+ },
422
+ {
423
+ name : "nat to routed closed port" ,
424
+ from : natDesc ,
425
+ to : routedDesc ,
426
+ port : "81" ,
427
+ expPingExit : pingSuccess ,
428
+ expHttpStderr : httpFail ,
429
+ },
430
+ {
431
+ name : "routed to nat open port" ,
432
+ from : routedDesc ,
433
+ to : natDesc ,
434
+ port : "80" ,
435
+ expPingExit : pingFail ,
436
+ expHttpStderr : httpFail ,
437
+ },
438
+ {
439
+ name : "routed to nat closed port" ,
440
+ from : routedDesc ,
441
+ to : natDesc ,
442
+ port : "81" ,
443
+ expPingExit : pingFail ,
444
+ expHttpStderr : httpFail ,
445
+ },
446
+ }
447
+
448
+ for _ , fwdPolicy := range []string {"ACCEPT" , "DROP" } {
449
+ networking .SetFilterForwardPolicies (t , fwdPolicy )
450
+ t .Run (fwdPolicy , func (t * testing.T ) {
451
+ for _ , tc := range testcases {
452
+ t .Run (tc .name + "/v4/ping" , func (t * testing.T ) {
453
+ t .Parallel ()
454
+ ctx := testutil .StartSpan (ctx , t )
455
+ pingRes4 := container .ExecT (ctx , t , c , tc .from .id , []string {
456
+ "ping" , "-4" , "-c1" , "-W3" , tc .to .ipv4 ,
457
+ })
458
+ assert .Check (t , is .Equal (pingRes4 .ExitCode , tc .expPingExit ))
459
+ })
460
+ t .Run (tc .name + "/v6/ping" , func (t * testing.T ) {
461
+ t .Parallel ()
462
+ ctx := testutil .StartSpan (ctx , t )
463
+ pingRes6 := container .ExecT (ctx , t , c , tc .from .id , []string {
464
+ "ping" , "-6" , "-c1" , "-W3" , tc .to .ipv6 ,
465
+ })
466
+ assert .Check (t , is .Equal (pingRes6 .ExitCode , tc .expPingExit ))
467
+ })
468
+ t .Run (tc .name + "/v4/http" , func (t * testing.T ) {
469
+ t .Parallel ()
470
+ ctx := testutil .StartSpan (ctx , t )
471
+ httpRes4 := container .ExecT (ctx , t , c , tc .from .id , []string {
472
+ "wget" , "-T3" , "http://" + net .JoinHostPort (tc .to .ipv4 , tc .port ),
473
+ })
474
+ assert .Check (t , is .Contains (httpRes4 .Stderr (), tc .expHttpStderr ))
475
+ })
476
+ t .Run (tc .name + "/v6/http" , func (t * testing.T ) {
477
+ t .Parallel ()
478
+ ctx := testutil .StartSpan (ctx , t )
479
+ httpRes6 := container .ExecT (ctx , t , c , tc .from .id , []string {
480
+ "wget" , "-T3" , "http://" + net .JoinHostPort (tc .to .ipv6 , tc .port ),
481
+ })
482
+ assert .Check (t , is .Contains (httpRes6 .Stderr (), tc .expHttpStderr ))
483
+ })
484
+ }
485
+ })
486
+ }
487
+ }
488
+
338
489
func TestDefaultBridgeIPv6 (t * testing.T ) {
339
490
ctx := setupTest (t )
340
491
0 commit comments