Skip to content

Commit

Permalink
Allow user to specify container's link-local addresses
Browse files Browse the repository at this point in the history
Signed-off-by: Alessandro Boch <[email protected]>
  • Loading branch information
aboch committed Jun 14, 2016
1 parent c913dd5 commit 1c4efb6
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 30 deletions.
19 changes: 11 additions & 8 deletions api/client/network/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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,
Expand Down
10 changes: 8 additions & 2 deletions container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion docs/reference/api/docker_remote_api_v1.24.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
1 change: 1 addition & 0 deletions docs/reference/commandline/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/reference/commandline/network_connect.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/reference/commandline/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 13 additions & 12 deletions docs/reference/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 47 additions & 0 deletions integration-cli/docker_cli_network_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
4 changes: 4 additions & 0 deletions man/docker-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*]]
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions man/docker-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*]]
Expand Down Expand Up @@ -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
Expand Down
22 changes: 15 additions & 7 deletions runconfig/opts/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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())
}
}

Expand Down

0 comments on commit 1c4efb6

Please sign in to comment.