Browse code

Internal macvlan networks don't need a gateway address.

Signed-off-by: Rob Murray <rob.murray@docker.com>

Rob Murray authored on 2025/01/04 03:24:27
Showing 2 changed files
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"context"
7 7
 	"strings"
8 8
 	"testing"
9
+	"time"
9 10
 
10 11
 	containertypes "github.com/docker/docker/api/types/container"
11 12
 	"github.com/docker/docker/api/types/network"
... ...
@@ -627,3 +628,35 @@ func TestMACVlanDNS(t *testing.T) {
627 627
 		})
628 628
 	}
629 629
 }
630
+
631
+// TestPointToPoint checks that no gateway is reserved for a macvlan network
632
+// with no parent interface (an "internal" network).
633
+func TestPointToPoint(t *testing.T) {
634
+	ctx := testutil.StartSpan(baseContext, t)
635
+	apiClient := testEnv.APIClient()
636
+
637
+	const netName = "p2pmacvlan"
638
+	net.CreateNoError(ctx, t, apiClient, netName,
639
+		net.WithMacvlan(""),
640
+		net.WithIPAM("192.168.135.0/31", ""),
641
+	)
642
+	defer net.RemoveNoError(ctx, t, apiClient, netName)
643
+
644
+	const ctrName = "ctr1"
645
+	id := container.Run(ctx, t, apiClient,
646
+		container.WithNetworkMode(netName),
647
+		container.WithName(ctrName),
648
+	)
649
+	defer apiClient.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true})
650
+
651
+	attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
652
+	defer cancel()
653
+	res := container.RunAttach(attachCtx, t, apiClient,
654
+		container.WithCmd([]string{"ping", "-c1", "-W3", ctrName}...),
655
+		container.WithNetworkMode(netName),
656
+	)
657
+	defer apiClient.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
658
+	assert.Check(t, is.Equal(res.ExitCode, 0))
659
+	assert.Check(t, is.Equal(res.Stderr.Len(), 0))
660
+	assert.Check(t, is.Contains(res.Stdout.String(), "1 packets transmitted, 1 packets received"))
661
+}
... ...
@@ -40,7 +40,6 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
40 40
 	// if parent interface not specified, create a dummy type link to use named dummy+net_id
41 41
 	if config.Parent == "" {
42 42
 		config.Parent = getDummyName(config.ID)
43
-		config.Internal = true
44 43
 	}
45 44
 	foundExisting, err := d.createNetwork(config)
46 45
 	if err != nil {
... ...
@@ -62,6 +61,15 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
62 62
 	return nil
63 63
 }
64 64
 
65
+func (d *driver) GetSkipGwAlloc(opts options.Generic) (ipv4, ipv6 bool, _ error) {
66
+	cfg, err := parseNetworkOptions("dummy", opts)
67
+	if err != nil {
68
+		return false, false, err
69
+	}
70
+	// "--internal" networks don't need a gateway address.
71
+	return cfg.Internal, cfg.Internal, nil
72
+}
73
+
65 74
 // createNetwork is used by new network callbacks and persistent network cache
66 75
 func (d *driver) createNetwork(config *configuration) (bool, error) {
67 76
 	foundExisting := false
... ...
@@ -225,6 +233,11 @@ func parseNetworkOptions(id string, option options.Generic) (*configuration, err
225 225
 		return nil, fmt.Errorf("loopback interface is not a valid macvlan parent link")
226 226
 	}
227 227
 
228
+	// With no parent interface, the network is "internal".
229
+	if config.Parent == "" {
230
+		config.Internal = true
231
+	}
232
+
228 233
 	config.ID = id
229 234
 	return config, nil
230 235
 }