From 1c4efb6aa05026efce99a7a5bb7e710c0f1b3002 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Thu, 9 Jun 2016 15:10:59 -0700 Subject: [PATCH] Allow user to specify container's link-local addresses Signed-off-by: Alessandro Boch --- api/client/network/connect.go | 19 ++++---- container/container.go | 10 +++- docs/reference/api/docker_remote_api_v1.24.md | 3 +- docs/reference/commandline/create.md | 1 + docs/reference/commandline/network_connect.md | 1 + docs/reference/commandline/run.md | 1 + docs/reference/run.md | 25 +++++----- .../docker_cli_network_unix_test.go | 47 +++++++++++++++++++ man/docker-create.1.md | 4 ++ man/docker-run.1.md | 4 ++ runconfig/opts/parse.go | 22 ++++++--- 11 files changed, 107 insertions(+), 30 deletions(-) diff --git a/api/client/network/connect.go b/api/client/network/connect.go index 3184fd6bf6..1242582c3b 100644 --- a/api/client/network/connect.go +++ b/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, diff --git a/container/container.go b/container/container.go index 1300d96d9c..7f4b0c0238 100644 --- a/container/container.go +++ b/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 { diff --git a/docs/reference/api/docker_remote_api_v1.24.md b/docs/reference/api/docker_remote_api_v1.24.md index f5b3ae208c..d333e78a13 100644 --- a/docs/reference/api/docker_remote_api_v1.24.md +++ b/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"] diff --git a/docs/reference/commandline/create.md b/docs/reference/commandline/create.md index 03d3779237..ebf78b66f6 100644 --- a/docs/reference/commandline/create.md +++ b/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 diff --git a/docs/reference/commandline/network_connect.md b/docs/reference/commandline/network_connect.md index a815ca38ec..ae29c44bb3 100644 --- a/docs/reference/commandline/network_connect.md +++ b/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 diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index 6b66c394a8..75bd4a682c 100644 --- a/docs/reference/commandline/run.md +++ b/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 diff --git a/docs/reference/run.md b/docs/reference/run.md index f76336e67d..db0e86b6e9 100644 --- a/docs/reference/run.md +++ b/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:': reuse another container's network stack - 'host': use the Docker host network stack - '|': 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:': reuse another container's network stack + 'host': use the Docker host network stack + '|': 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 diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go index 041c01ced5..4537529a06 100644 --- a/integration-cli/docker_cli_network_unix_test.go +++ b/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") diff --git a/man/docker-create.1.md b/man/docker-create.1.md index e630d5fdcd..4dc90d89d8 100644 --- a/man/docker-create.1.md +++ b/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 :alias or just 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 diff --git a/man/docker-run.1.md b/man/docker-run.1.md index 2d02b7cbfb..055c8e6d67 100644 --- a/man/docker-run.1.md +++ b/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 diff --git a/runconfig/opts/parse.go b/runconfig/opts/parse.go index 77254e05f4..e201abb734 100644 --- a/runconfig/opts/parse.go +++ b/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()) } }