Browse code

Allow user to specify container's link-local addresses

Signed-off-by: Alessandro Boch <aboch@docker.com>

Alessandro Boch authored on 2016/06/10 07:10:59
Showing 11 changed files
... ...
@@ -12,12 +12,13 @@ import (
12 12
 )
13 13
 
14 14
 type connectOptions struct {
15
-	network     string
16
-	container   string
17
-	ipaddress   string
18
-	ipv6address string
19
-	links       opts.ListOpts
20
-	aliases     []string
15
+	network      string
16
+	container    string
17
+	ipaddress    string
18
+	ipv6address  string
19
+	links        opts.ListOpts
20
+	aliases      []string
21
+	linklocalips []string
21 22
 }
22 23
 
23 24
 func newConnectCommand(dockerCli *client.DockerCli) *cobra.Command {
... ...
@@ -41,6 +42,7 @@ func newConnectCommand(dockerCli *client.DockerCli) *cobra.Command {
41 41
 	flags.StringVar(&opts.ipv6address, "ip6", "", "IPv6 Address")
42 42
 	flags.Var(&opts.links, "link", "Add link to another container")
43 43
 	flags.StringSliceVar(&opts.aliases, "alias", []string{}, "Add network-scoped alias for the container")
44
+	flags.StringSliceVar(&opts.linklocalips, "link-local-ip", []string{}, "Add a link-local address for the container")
44 45
 
45 46
 	return cmd
46 47
 }
... ...
@@ -50,8 +52,9 @@ func runConnect(dockerCli *client.DockerCli, opts connectOptions) error {
50 50
 
51 51
 	epConfig := &network.EndpointSettings{
52 52
 		IPAMConfig: &network.EndpointIPAMConfig{
53
-			IPv4Address: opts.ipaddress,
54
-			IPv6Address: opts.ipv6address,
53
+			IPv4Address:  opts.ipaddress,
54
+			IPv6Address:  opts.ipv6address,
55
+			LinkLocalIPs: opts.linklocalips,
55 56
 		},
56 57
 		Links:   opts.links.GetAll(),
57 58
 		Aliases: opts.aliases,
... ...
@@ -789,9 +789,15 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epC
789 789
 
790 790
 	if epConfig != nil {
791 791
 		ipam := epConfig.IPAMConfig
792
-		if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "") {
792
+		if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "" || len(ipam.LinkLocalIPs) > 0) {
793
+			var ipList []net.IP
794
+			for _, ips := range ipam.LinkLocalIPs {
795
+				if ip := net.ParseIP(ips); ip != nil {
796
+					ipList = append(ipList, ip)
797
+				}
798
+			}
793 799
 			createOptions = append(createOptions,
794
-				libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil, nil))
800
+				libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), ipList, nil))
795 801
 		}
796 802
 
797 803
 		for _, alias := range epConfig.Aliases {
... ...
@@ -339,7 +339,8 @@ Create a container
339 339
               "isolated_nw" : {
340 340
                   "IPAMConfig": {
341 341
                       "IPv4Address":"172.20.30.33",
342
-                      "IPv6Address":"2001:db8:abcd::3033"
342
+                      "IPv6Address":"2001:db8:abcd::3033",
343
+                      "LinkLocalIPs:["169.254.34.68", "fe80::3468"]
343 344
                   },
344 345
                   "Links":["container_1", "container_2"],
345 346
                   "Aliases":["server_x", "server_y"]
... ...
@@ -54,6 +54,7 @@ Creates a new container.
54 54
       -l, --label=[]                Set metadata on the container (e.g., --label=com.example.key=value)
55 55
       --label-file=[]               Read in a line delimited file of labels
56 56
       --link=[]                     Add link to another container
57
+      --link-local-ip=[]            Container IPv4/IPv6 link-local addresses (e.g. 169.254.0.77, fe80::77)
57 58
       --log-driver=""               Logging driver for container
58 59
       --log-opt=[]                  Log driver specific options
59 60
       -m, --memory=""               Memory limit
... ...
@@ -19,6 +19,7 @@ parent = "smn_cli"
19 19
       --ip               IPv4 Address
20 20
       --ip6              IPv6 Address
21 21
       --link=[]          Add a link to another container
22
+      --link-local-ip=[] IPv4/IPv6 link-local addresses
22 23
 
23 24
 Connects a container to a network. You can connect a container by name
24 25
 or by ID. Once connected, the container can communicate with other containers in
... ...
@@ -55,6 +55,7 @@ parent = "smn_cli"
55 55
       -l, --label=[]                Set metadata on the container (e.g., --label=com.example.key=value)
56 56
       --label-file=[]               Read in a file of labels (EOL delimited)
57 57
       --link=[]                     Add link to another container
58
+      --link-local-ip=[]            Container IPv4/IPv6 link-local addresses (e.g. 169.254.0.77, fe80::77)
58 59
       --log-driver=""               Logging driver for container
59 60
       --log-opt=[]                  Log driver specific options
60 61
       -m, --memory=""               Memory limit
... ...
@@ -288,18 +288,19 @@ of the containers.
288 288
 
289 289
 ## Network settings
290 290
 
291
-    --dns=[]         : Set custom dns servers for the container
292
-    --net="bridge"   : Connect a container to a network
293
-                        'bridge': create a network stack on the default Docker bridge
294
-                        'none': no networking
295
-                        'container:<name|id>': reuse another container's network stack
296
-                        'host': use the Docker host network stack
297
-                        '<network-name>|<network-id>': connect to a user-defined network
298
-    --net-alias=[]   : Add network-scoped alias for the container
299
-    --add-host=""    : Add a line to /etc/hosts (host:IP)
300
-    --mac-address="" : Sets the container's Ethernet device's MAC address
301
-    --ip=""          : Sets the container's Ethernet device's IPv4 address
302
-    --ip6=""         : Sets the container's Ethernet device's IPv6 address
291
+    --dns=[]           : Set custom dns servers for the container
292
+    --net="bridge"     : Connect a container to a network
293
+                          'bridge': create a network stack on the default Docker bridge
294
+                          'none': no networking
295
+                          'container:<name|id>': reuse another container's network stack
296
+                          'host': use the Docker host network stack
297
+                          '<network-name>|<network-id>': connect to a user-defined network
298
+    --net-alias=[]     : Add network-scoped alias for the container
299
+    --add-host=""      : Add a line to /etc/hosts (host:IP)
300
+    --mac-address=""   : Sets the container's Ethernet device's MAC address
301
+    --ip=""            : Sets the container's Ethernet device's IPv4 address
302
+    --ip6=""           : Sets the container's Ethernet device's IPv6 address
303
+    --link-local-ip=[] : Sets one or more container's Ethernet device's link local IPv4/IPv6 addresses
303 304
 
304 305
 By default, all containers have networking enabled and they can make any
305 306
 outgoing connections. The operator can completely disable networking
... ...
@@ -1336,6 +1336,53 @@ func verifyIPAddresses(c *check.C, cName, nwname, ipv4, ipv6 string) {
1336 1336
 	c.Assert(strings.TrimSpace(out), check.Equals, ipv6)
1337 1337
 }
1338 1338
 
1339
+func (s *DockerNetworkSuite) TestDockerNetworkConnectLinkLocalIP(c *check.C) {
1340
+	// create one test network
1341
+	dockerCmd(c, "network", "create", "n0")
1342
+	assertNwIsAvailable(c, "n0")
1343
+
1344
+	// run a container with incorrect link-local address
1345
+	_, _, err := dockerCmdWithError("run", "--link-local-ip", "169.253.5.5", "busybox", "top")
1346
+	c.Assert(err, check.NotNil)
1347
+	_, _, err = dockerCmdWithError("run", "--link-local-ip", "2001:db8::89", "busybox", "top")
1348
+	c.Assert(err, check.NotNil)
1349
+
1350
+	// run two containers with link-local ip on the test network
1351
+	dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--link-local-ip", "169.254.7.7", "--link-local-ip", "fe80::254:77", "busybox", "top")
1352
+	c.Assert(waitRun("c0"), check.IsNil)
1353
+	dockerCmd(c, "run", "-d", "--name", "c1", "--net=n0", "--link-local-ip", "169.254.8.8", "--link-local-ip", "fe80::254:88", "busybox", "top")
1354
+	c.Assert(waitRun("c1"), check.IsNil)
1355
+
1356
+	// run a container on the default network and connect it to the test network specifying a link-local address
1357
+	dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top")
1358
+	c.Assert(waitRun("c2"), check.IsNil)
1359
+	dockerCmd(c, "network", "connect", "--link-local-ip", "169.254.9.9", "n0", "c2")
1360
+
1361
+	// verify the three containers can ping each other via the link-local addresses
1362
+	_, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8")
1363
+	c.Assert(err, check.IsNil)
1364
+	_, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9")
1365
+	c.Assert(err, check.IsNil)
1366
+	_, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7")
1367
+	c.Assert(err, check.IsNil)
1368
+
1369
+	// Stop and restart the three containers
1370
+	dockerCmd(c, "stop", "c0")
1371
+	dockerCmd(c, "stop", "c1")
1372
+	dockerCmd(c, "stop", "c2")
1373
+	dockerCmd(c, "start", "c0")
1374
+	dockerCmd(c, "start", "c1")
1375
+	dockerCmd(c, "start", "c2")
1376
+
1377
+	// verify the ping again
1378
+	_, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8")
1379
+	c.Assert(err, check.IsNil)
1380
+	_, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9")
1381
+	c.Assert(err, check.IsNil)
1382
+	_, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7")
1383
+	c.Assert(err, check.IsNil)
1384
+}
1385
+
1339 1386
 func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *check.C) {
1340 1387
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
1341 1388
 	dockerCmd(c, "network", "create", "-d", "bridge", "foo1")
... ...
@@ -43,6 +43,7 @@ docker-create - Create a new container
43 43
 [**-l**|**--label**[=*[]*]]
44 44
 [**--label-file**[=*[]*]]
45 45
 [**--link**[=*[]*]]
46
+[**--link-local-ip**[=*[]*]]
46 47
 [**--log-driver**[=*[]*]]
47 48
 [**--log-opt**[=*[]*]]
48 49
 [**-m**|**--memory**[=*MEMORY*]]
... ...
@@ -220,6 +221,9 @@ millions of trillions.
220 220
    Add link to another container in the form of <name or id>:alias or just
221 221
    <name or id> in which case the alias will match the name.
222 222
 
223
+**--link-local-ip**=[]
224
+   Add one or more link-local IPv4/IPv6 addresses to the container's interface
225
+
223 226
 **--log-driver**="*json-file*|*syslog*|*journald*|*gelf*|*fluentd*|*awslogs*|*splunk*|*etwlogs*|*gcplogs*|*none*"
224 227
   Logging driver for container. Default is defined by daemon `--log-driver` flag.
225 228
   **Warning**: the `docker logs` command works only for the `json-file` and
... ...
@@ -45,6 +45,7 @@ docker-run - Run a command in a new container
45 45
 [**-l**|**--label**[=*[]*]]
46 46
 [**--label-file**[=*[]*]]
47 47
 [**--link**[=*[]*]]
48
+[**--link-local-ip**[=*[]*]]
48 49
 [**--log-driver**[=*[]*]]
49 50
 [**--log-opt**[=*[]*]]
50 51
 [**-m**|**--memory**[=*MEMORY*]]
... ...
@@ -326,6 +327,9 @@ container can access the exposed port via a private networking interface. Docker
326 326
 will set some environment variables in the client container to help indicate
327 327
 which interface and port to use.
328 328
 
329
+**--link-local-ip**=[]
330
+   Add one or more link-local IPv4/IPv6 addresses to the container's interface
331
+
329 332
 **--log-driver**="*json-file*|*syslog*|*journald*|*gelf*|*fluentd*|*awslogs*|*splunk*|*etwlogs*|*gcplogs*|*none*"
330 333
   Logging driver for container. Default is defined by daemon `--log-driver` flag.
331 334
   **Warning**: the `docker logs` command works only for the `json-file` and
... ...
@@ -32,6 +32,7 @@ type ContainerOptions struct {
32 32
 	flDeviceWriteBps    ThrottledeviceOpt
33 33
 	flLinks             opts.ListOpts
34 34
 	flAliases           opts.ListOpts
35
+	flLinkLocalIPs      opts.ListOpts
35 36
 	flDeviceReadIOps    ThrottledeviceOpt
36 37
 	flDeviceWriteIOps   ThrottledeviceOpt
37 38
 	flEnv               opts.ListOpts
... ...
@@ -117,6 +118,7 @@ func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
117 117
 		flDeviceWriteBps:    NewThrottledeviceOpt(ValidateThrottleBpsDevice),
118 118
 		flLinks:             opts.NewListOpts(ValidateLink),
119 119
 		flAliases:           opts.NewListOpts(nil),
120
+		flLinkLocalIPs:      opts.NewListOpts(nil),
120 121
 		flDeviceReadIOps:    NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
121 122
 		flDeviceWriteIOps:   NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
122 123
 		flEnv:               opts.NewListOpts(ValidateEnv),
... ...
@@ -201,6 +203,7 @@ func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
201 201
 	flags.Var(&copts.flTmpfs, "tmpfs", "Mount a tmpfs directory")
202 202
 	flags.Var(&copts.flLinks, "link", "Add link to another container")
203 203
 	flags.Var(&copts.flAliases, "net-alias", "Add network-scoped alias for the container")
204
+	flags.Var(&copts.flLinkLocalIPs, "link-local-ip", "Container IPv4/IPv6 link-local addresses")
204 205
 	flags.Var(&copts.flDevices, "device", "Add a host device to the container")
205 206
 	flags.VarP(&copts.flLabels, "label", "l", "Set meta data on a container")
206 207
 	flags.Var(&copts.flLabelsFile, "label-file", "Read in a line delimited file of labels")
... ...
@@ -229,7 +232,6 @@ func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
229 229
 // a HostConfig and returns them with the specified command.
230 230
 // If the specified args are not valid, it will return an error.
231 231
 func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
232
-
233 232
 	var (
234 233
 		attachStdin  = copts.flAttach.Get("stdin")
235 234
 		attachStdout = copts.flAttach.Get("stdout")
... ...
@@ -575,12 +577,18 @@ func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *c
575 575
 		EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
576 576
 	}
577 577
 
578
-	if *copts.flIPv4Address != "" || *copts.flIPv6Address != "" {
579
-		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
580
-			IPAMConfig: &networktypes.EndpointIPAMConfig{
581
-				IPv4Address: *copts.flIPv4Address,
582
-				IPv6Address: *copts.flIPv6Address,
583
-			},
578
+	if *copts.flIPv4Address != "" || *copts.flIPv6Address != "" || copts.flLinkLocalIPs.Len() > 0 {
579
+		epConfig := &networktypes.EndpointSettings{}
580
+		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
581
+
582
+		epConfig.IPAMConfig = &networktypes.EndpointIPAMConfig{
583
+			IPv4Address: *copts.flIPv4Address,
584
+			IPv6Address: *copts.flIPv6Address,
585
+		}
586
+
587
+		if copts.flLinkLocalIPs.Len() > 0 {
588
+			epConfig.IPAMConfig.LinkLocalIPs = make([]string, copts.flLinkLocalIPs.Len())
589
+			copy(epConfig.IPAMConfig.LinkLocalIPs, copts.flLinkLocalIPs.GetAll())
584 590
 		}
585 591
 	}
586 592