Переглянути джерело

Allow user to specify container's link-local addresses

Signed-off-by: Alessandro Boch <aboch@docker.com>
Alessandro Boch 9 роки тому
батько
коміт
1c4efb6aa0

+ 11 - 8
api/client/network/connect.go

@@ -12,12 +12,13 @@ import (
 )
 
 type connectOptions struct {
-	network     string
-	container   string
-	ipaddress   string
-	ipv6address string
-	links       opts.ListOpts
-	aliases     []string
+	network      string
+	container    string
+	ipaddress    string
+	ipv6address  string
+	links        opts.ListOpts
+	aliases      []string
+	linklocalips []string
 }
 
 func newConnectCommand(dockerCli *client.DockerCli) *cobra.Command {
@@ -41,6 +42,7 @@ func newConnectCommand(dockerCli *client.DockerCli) *cobra.Command {
 	flags.StringVar(&opts.ipv6address, "ip6", "", "IPv6 Address")
 	flags.Var(&opts.links, "link", "Add link to another container")
 	flags.StringSliceVar(&opts.aliases, "alias", []string{}, "Add network-scoped alias for the container")
+	flags.StringSliceVar(&opts.linklocalips, "link-local-ip", []string{}, "Add a link-local address for the container")
 
 	return cmd
 }
@@ -50,8 +52,9 @@ func runConnect(dockerCli *client.DockerCli, opts connectOptions) error {
 
 	epConfig := &network.EndpointSettings{
 		IPAMConfig: &network.EndpointIPAMConfig{
-			IPv4Address: opts.ipaddress,
-			IPv6Address: opts.ipv6address,
+			IPv4Address:  opts.ipaddress,
+			IPv6Address:  opts.ipv6address,
+			LinkLocalIPs: opts.linklocalips,
 		},
 		Links:   opts.links.GetAll(),
 		Aliases: opts.aliases,

+ 8 - 2
container/container.go

@@ -789,9 +789,15 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epC
 
 	if epConfig != nil {
 		ipam := epConfig.IPAMConfig
-		if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "") {
+		if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "" || len(ipam.LinkLocalIPs) > 0) {
+			var ipList []net.IP
+			for _, ips := range ipam.LinkLocalIPs {
+				if ip := net.ParseIP(ips); ip != nil {
+					ipList = append(ipList, ip)
+				}
+			}
 			createOptions = append(createOptions,
-				libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil, nil))
+				libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), ipList, nil))
 		}
 
 		for _, alias := range epConfig.Aliases {

+ 2 - 1
docs/reference/api/docker_remote_api_v1.24.md

@@ -339,7 +339,8 @@ Create a container
               "isolated_nw" : {
                   "IPAMConfig": {
                       "IPv4Address":"172.20.30.33",
-                      "IPv6Address":"2001:db8:abcd::3033"
+                      "IPv6Address":"2001:db8:abcd::3033",
+                      "LinkLocalIPs:["169.254.34.68", "fe80::3468"]
                   },
                   "Links":["container_1", "container_2"],
                   "Aliases":["server_x", "server_y"]

+ 1 - 0
docs/reference/commandline/create.md

@@ -54,6 +54,7 @@ Creates a new container.
       -l, --label=[]                Set metadata on the container (e.g., --label=com.example.key=value)
       --label-file=[]               Read in a line delimited file of labels
       --link=[]                     Add link to another container
+      --link-local-ip=[]            Container IPv4/IPv6 link-local addresses (e.g. 169.254.0.77, fe80::77)
       --log-driver=""               Logging driver for container
       --log-opt=[]                  Log driver specific options
       -m, --memory=""               Memory limit

+ 1 - 0
docs/reference/commandline/network_connect.md

@@ -19,6 +19,7 @@ parent = "smn_cli"
       --ip               IPv4 Address
       --ip6              IPv6 Address
       --link=[]          Add a link to another container
+      --link-local-ip=[] IPv4/IPv6 link-local addresses
 
 Connects a container to a network. You can connect a container by name
 or by ID. Once connected, the container can communicate with other containers in

+ 1 - 0
docs/reference/commandline/run.md

@@ -55,6 +55,7 @@ parent = "smn_cli"
       -l, --label=[]                Set metadata on the container (e.g., --label=com.example.key=value)
       --label-file=[]               Read in a file of labels (EOL delimited)
       --link=[]                     Add link to another container
+      --link-local-ip=[]            Container IPv4/IPv6 link-local addresses (e.g. 169.254.0.77, fe80::77)
       --log-driver=""               Logging driver for container
       --log-opt=[]                  Log driver specific options
       -m, --memory=""               Memory limit

+ 13 - 12
docs/reference/run.md

@@ -288,18 +288,19 @@ of the containers.
 
 ## Network settings
 
-    --dns=[]         : Set custom dns servers for the container
-    --net="bridge"   : Connect a container to a network
-                        'bridge': create a network stack on the default Docker bridge
-                        'none': no networking
-                        'container:<name|id>': reuse another container's network stack
-                        'host': use the Docker host network stack
-                        '<network-name>|<network-id>': connect to a user-defined network
-    --net-alias=[]   : Add network-scoped alias for the container
-    --add-host=""    : Add a line to /etc/hosts (host:IP)
-    --mac-address="" : Sets the container's Ethernet device's MAC address
-    --ip=""          : Sets the container's Ethernet device's IPv4 address
-    --ip6=""         : Sets the container's Ethernet device's IPv6 address
+    --dns=[]           : Set custom dns servers for the container
+    --net="bridge"     : Connect a container to a network
+                          'bridge': create a network stack on the default Docker bridge
+                          'none': no networking
+                          'container:<name|id>': reuse another container's network stack
+                          'host': use the Docker host network stack
+                          '<network-name>|<network-id>': connect to a user-defined network
+    --net-alias=[]     : Add network-scoped alias for the container
+    --add-host=""      : Add a line to /etc/hosts (host:IP)
+    --mac-address=""   : Sets the container's Ethernet device's MAC address
+    --ip=""            : Sets the container's Ethernet device's IPv4 address
+    --ip6=""           : Sets the container's Ethernet device's IPv6 address
+    --link-local-ip=[] : Sets one or more container's Ethernet device's link local IPv4/IPv6 addresses
 
 By default, all containers have networking enabled and they can make any
 outgoing connections. The operator can completely disable networking

+ 47 - 0
integration-cli/docker_cli_network_unix_test.go

@@ -1336,6 +1336,53 @@ func verifyIPAddresses(c *check.C, cName, nwname, ipv4, ipv6 string) {
 	c.Assert(strings.TrimSpace(out), check.Equals, ipv6)
 }
 
+func (s *DockerNetworkSuite) TestDockerNetworkConnectLinkLocalIP(c *check.C) {
+	// create one test network
+	dockerCmd(c, "network", "create", "n0")
+	assertNwIsAvailable(c, "n0")
+
+	// run a container with incorrect link-local address
+	_, _, err := dockerCmdWithError("run", "--link-local-ip", "169.253.5.5", "busybox", "top")
+	c.Assert(err, check.NotNil)
+	_, _, err = dockerCmdWithError("run", "--link-local-ip", "2001:db8::89", "busybox", "top")
+	c.Assert(err, check.NotNil)
+
+	// run two containers with link-local ip on the test network
+	dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--link-local-ip", "169.254.7.7", "--link-local-ip", "fe80::254:77", "busybox", "top")
+	c.Assert(waitRun("c0"), check.IsNil)
+	dockerCmd(c, "run", "-d", "--name", "c1", "--net=n0", "--link-local-ip", "169.254.8.8", "--link-local-ip", "fe80::254:88", "busybox", "top")
+	c.Assert(waitRun("c1"), check.IsNil)
+
+	// run a container on the default network and connect it to the test network specifying a link-local address
+	dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top")
+	c.Assert(waitRun("c2"), check.IsNil)
+	dockerCmd(c, "network", "connect", "--link-local-ip", "169.254.9.9", "n0", "c2")
+
+	// verify the three containers can ping each other via the link-local addresses
+	_, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8")
+	c.Assert(err, check.IsNil)
+	_, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9")
+	c.Assert(err, check.IsNil)
+	_, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7")
+	c.Assert(err, check.IsNil)
+
+	// Stop and restart the three containers
+	dockerCmd(c, "stop", "c0")
+	dockerCmd(c, "stop", "c1")
+	dockerCmd(c, "stop", "c2")
+	dockerCmd(c, "start", "c0")
+	dockerCmd(c, "start", "c1")
+	dockerCmd(c, "start", "c2")
+
+	// verify the ping again
+	_, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8")
+	c.Assert(err, check.IsNil)
+	_, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9")
+	c.Assert(err, check.IsNil)
+	_, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7")
+	c.Assert(err, check.IsNil)
+}
+
 func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *check.C) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	dockerCmd(c, "network", "create", "-d", "bridge", "foo1")

+ 4 - 0
man/docker-create.1.md

@@ -43,6 +43,7 @@ docker-create - Create a new container
 [**-l**|**--label**[=*[]*]]
 [**--label-file**[=*[]*]]
 [**--link**[=*[]*]]
+[**--link-local-ip**[=*[]*]]
 [**--log-driver**[=*[]*]]
 [**--log-opt**[=*[]*]]
 [**-m**|**--memory**[=*MEMORY*]]
@@ -220,6 +221,9 @@ millions of trillions.
    Add link to another container in the form of <name or id>:alias or just
    <name or id> in which case the alias will match the name.
 
+**--link-local-ip**=[]
+   Add one or more link-local IPv4/IPv6 addresses to the container's interface
+
 **--log-driver**="*json-file*|*syslog*|*journald*|*gelf*|*fluentd*|*awslogs*|*splunk*|*etwlogs*|*gcplogs*|*none*"
   Logging driver for container. Default is defined by daemon `--log-driver` flag.
   **Warning**: the `docker logs` command works only for the `json-file` and

+ 4 - 0
man/docker-run.1.md

@@ -45,6 +45,7 @@ docker-run - Run a command in a new container
 [**-l**|**--label**[=*[]*]]
 [**--label-file**[=*[]*]]
 [**--link**[=*[]*]]
+[**--link-local-ip**[=*[]*]]
 [**--log-driver**[=*[]*]]
 [**--log-opt**[=*[]*]]
 [**-m**|**--memory**[=*MEMORY*]]
@@ -326,6 +327,9 @@ container can access the exposed port via a private networking interface. Docker
 will set some environment variables in the client container to help indicate
 which interface and port to use.
 
+**--link-local-ip**=[]
+   Add one or more link-local IPv4/IPv6 addresses to the container's interface
+
 **--log-driver**="*json-file*|*syslog*|*journald*|*gelf*|*fluentd*|*awslogs*|*splunk*|*etwlogs*|*gcplogs*|*none*"
   Logging driver for container. Default is defined by daemon `--log-driver` flag.
   **Warning**: the `docker logs` command works only for the `json-file` and

+ 15 - 7
runconfig/opts/parse.go

@@ -32,6 +32,7 @@ type ContainerOptions struct {
 	flDeviceWriteBps    ThrottledeviceOpt
 	flLinks             opts.ListOpts
 	flAliases           opts.ListOpts
+	flLinkLocalIPs      opts.ListOpts
 	flDeviceReadIOps    ThrottledeviceOpt
 	flDeviceWriteIOps   ThrottledeviceOpt
 	flEnv               opts.ListOpts
@@ -117,6 +118,7 @@ func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
 		flDeviceWriteBps:    NewThrottledeviceOpt(ValidateThrottleBpsDevice),
 		flLinks:             opts.NewListOpts(ValidateLink),
 		flAliases:           opts.NewListOpts(nil),
+		flLinkLocalIPs:      opts.NewListOpts(nil),
 		flDeviceReadIOps:    NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
 		flDeviceWriteIOps:   NewThrottledeviceOpt(ValidateThrottleIOpsDevice),
 		flEnv:               opts.NewListOpts(ValidateEnv),
@@ -201,6 +203,7 @@ func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
 	flags.Var(&copts.flTmpfs, "tmpfs", "Mount a tmpfs directory")
 	flags.Var(&copts.flLinks, "link", "Add link to another container")
 	flags.Var(&copts.flAliases, "net-alias", "Add network-scoped alias for the container")
+	flags.Var(&copts.flLinkLocalIPs, "link-local-ip", "Container IPv4/IPv6 link-local addresses")
 	flags.Var(&copts.flDevices, "device", "Add a host device to the container")
 	flags.VarP(&copts.flLabels, "label", "l", "Set meta data on a container")
 	flags.Var(&copts.flLabelsFile, "label-file", "Read in a line delimited file of labels")
@@ -229,7 +232,6 @@ func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
 // a HostConfig and returns them with the specified command.
 // If the specified args are not valid, it will return an error.
 func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
-
 	var (
 		attachStdin  = copts.flAttach.Get("stdin")
 		attachStdout = copts.flAttach.Get("stdout")
@@ -575,12 +577,18 @@ func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *c
 		EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
 	}
 
-	if *copts.flIPv4Address != "" || *copts.flIPv6Address != "" {
-		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
-			IPAMConfig: &networktypes.EndpointIPAMConfig{
-				IPv4Address: *copts.flIPv4Address,
-				IPv6Address: *copts.flIPv6Address,
-			},
+	if *copts.flIPv4Address != "" || *copts.flIPv6Address != "" || copts.flLinkLocalIPs.Len() > 0 {
+		epConfig := &networktypes.EndpointSettings{}
+		networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = epConfig
+
+		epConfig.IPAMConfig = &networktypes.EndpointIPAMConfig{
+			IPv4Address: *copts.flIPv4Address,
+			IPv6Address: *copts.flIPv6Address,
+		}
+
+		if copts.flLinkLocalIPs.Len() > 0 {
+			epConfig.IPAMConfig.LinkLocalIPs = make([]string, copts.flLinkLocalIPs.Len())
+			copy(epConfig.IPAMConfig.LinkLocalIPs, copts.flLinkLocalIPs.GetAll())
 		}
 	}