Browse Source

Adding docker-cli run param to set MAC address

Signed-off-by: Malte Janduda <mail@janduda.net>
Malte Janduda 10 years ago
parent
commit
f2df38050e

+ 1 - 0
daemon/container.go

@@ -457,6 +457,7 @@ func (container *Container) AllocateNetwork() error {
 	)
 	)
 
 
 	job := eng.Job("allocate_interface", container.ID)
 	job := eng.Job("allocate_interface", container.ID)
+	job.Setenv("RequestedMac", container.Config.MacAddress)
 	if env, err = job.Stdout.AddEnv(); err != nil {
 	if env, err = job.Stdout.AddEnv(); err != nil {
 		return err
 		return err
 	}
 	}

+ 9 - 0
docs/man/docker-run.1.md

@@ -29,6 +29,7 @@ docker-run - Run a command in a new container
 [**-m**|**--memory**[=*MEMORY*]]
 [**-m**|**--memory**[=*MEMORY*]]
 [**--name**[=*NAME*]]
 [**--name**[=*NAME*]]
 [**--net**[=*"bridge"*]]
 [**--net**[=*"bridge"*]]
+[**--mac-address**[=*MACADDRESS*]]
 [**-P**|**--publish-all**[=*false*]]
 [**-P**|**--publish-all**[=*false*]]
 [**-p**|**--publish**[=*[]*]]
 [**-p**|**--publish**[=*[]*]]
 [**--privileged**[=*false*]]
 [**--privileged**[=*false*]]
@@ -187,6 +188,14 @@ and foreground Docker containers.
                                'container:<name|id>': reuses another container network stack
                                'container:<name|id>': reuses another container network stack
                                'host': use the host network stack inside the container.  Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
                                'host': use the host network stack inside the container.  Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
 
 
+**--mac-address**=*macaddress*
+   Set the MAC address for the container's ethernet device:
+    --mac-address=12:34:56:78:9a:bc
+
+Remember that the MAC address in an ethernet network must be unique.
+The IPv6 link-local address will be based on the device's MAC address
+according to RFC4862.
+
 **-P**, **--publish-all**=*true*|*false*
 **-P**, **--publish-all**=*true*|*false*
    When set to true publish all exposed ports to the host interfaces. The
    When set to true publish all exposed ports to the host interfaces. The
 default is false. If the operator uses -P (or -p) then Docker will make the
 default is false. If the operator uses -P (or -p) then Docker will make the

+ 12 - 2
docs/sources/articles/networking.md

@@ -104,6 +104,9 @@ Finally, several networking options can only be provided when calling
  *  `--net=bridge|none|container:NAME_or_ID|host` — see
  *  `--net=bridge|none|container:NAME_or_ID|host` — see
     [How Docker networks a container](#container-networking)
     [How Docker networks a container](#container-networking)
 
 
+ *  `--mac-address=MACADDRESS...` — see
+    [How docker networks a container](#container-networking)
+
  *  `-p SPEC` or `--publish=SPEC` — see
  *  `-p SPEC` or `--publish=SPEC` — see
     [Binding container ports](#binding-ports)
     [Binding container ports](#binding-ports)
 
 
@@ -537,9 +540,15 @@ The steps with which Docker configures a container are:
     separate and unique network interface namespace, there are no
     separate and unique network interface namespace, there are no
     physical interfaces with which this name could collide.
     physical interfaces with which this name could collide.
 
 
-4.  Give the container's `eth0` a new IP address from within the
+4.  Set the interface's mac address according to the `--mac-address`
+    parameter or generate a random one.
+
+5.  Give the container's `eth0` a new IP address from within the
     bridge's range of network addresses, and set its default route to
     bridge's range of network addresses, and set its default route to
-    the IP address that the Docker host owns on the bridge.
+    the IP address that the Docker host owns on the bridge. If available
+    the IP address is generated from the MAC address. This prevents arp
+    cache invalidation problems, when a new container comes up with an
+    IP used in the past by another container with another MAC.
 
 
 With these steps complete, the container now possesses an `eth0`
 With these steps complete, the container now possesses an `eth0`
 (virtual) network card and will find itself able to communicate with
 (virtual) network card and will find itself able to communicate with
@@ -621,6 +630,7 @@ Docker do all of the configuration:
 
 
     $ sudo ip link set B netns $pid
     $ sudo ip link set B netns $pid
     $ sudo ip netns exec $pid ip link set dev B name eth0
     $ sudo ip netns exec $pid ip link set dev B name eth0
+    $ sudo ip netns exec $pid ip link set eth0 address 12:34:56:78:9a:bc
     $ sudo ip netns exec $pid ip link set eth0 up
     $ sudo ip netns exec $pid ip link set eth0 up
     $ sudo ip netns exec $pid ip addr add 172.17.42.99/16 dev eth0
     $ sudo ip netns exec $pid ip addr add 172.17.42.99/16 dev eth0
     $ sudo ip netns exec $pid ip route add default via 172.17.42.1
     $ sudo ip netns exec $pid ip route add default via 172.17.42.1

+ 4 - 0
docs/sources/reference/api/docker_remote_api.md

@@ -52,6 +52,10 @@ You can still call an old version of the API using
 `info` now returns the number of CPUs available on the machine (`NCPU`) and
 `info` now returns the number of CPUs available on the machine (`NCPU`) and
 total memory available (`MemTotal`).
 total memory available (`MemTotal`).
 
 
+`POST /containers/create`
+**New!**
+You can define the container's MAC address by providing a MacAddress key-value pair.
+
 ## v1.15
 ## v1.15
 
 
 ### Full Documentation
 ### Full Documentation

+ 1 - 0
docs/sources/reference/api/docker_remote_api_v1.15.md

@@ -131,6 +131,7 @@ Create a container
              },
              },
              "WorkingDir":"",
              "WorkingDir":"",
              "NetworkDisabled": false,
              "NetworkDisabled": false,
+             "MacAddress":"12:34:56:78:9a:bc",
              "ExposedPorts":{
              "ExposedPorts":{
                      "22/tcp": {}
                      "22/tcp": {}
              },
              },

+ 1 - 0
docs/sources/reference/api/docker_remote_api_v1.16.md

@@ -131,6 +131,7 @@ Create a container
              },
              },
              "WorkingDir":"",
              "WorkingDir":"",
              "NetworkDisabled": false,
              "NetworkDisabled": false,
+             "MacAddress":"12:34:56:78:9a:bc",
              "ExposedPorts":{
              "ExposedPorts":{
                      "22/tcp": {}
                      "22/tcp": {}
              },
              },

+ 8 - 0
docs/sources/reference/commandline/cli.md

@@ -516,6 +516,7 @@ Creates a new container.
       --lxc-conf=[]              (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
       --lxc-conf=[]              (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
       -m, --memory=""            Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
       -m, --memory=""            Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
       --name=""                  Assign a name to the container
       --name=""                  Assign a name to the container
+      --mac-address=""           Set the container's MAC address
       --net="bridge"             Set the Network mode for the container
       --net="bridge"             Set the Network mode for the container
                                    'bridge': creates a new network stack for the container on the docker bridge
                                    'bridge': creates a new network stack for the container on the docker bridge
                                    'none': no networking for this container
                                    'none': no networking for this container
@@ -867,6 +868,13 @@ straightforward manner.
 
 
     $ sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' $INSTANCE_ID
     $ sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' $INSTANCE_ID
 
 
+**Get an instance's MAC Address:**
+
+For the most part, you can pick out any field from the JSON in a fairly
+straightforward manner.
+
+    $ sudo docker inspect --format='{{.NetworkSettings.MacAddress}}' $INSTANCE_ID
+
 **List All Port Bindings:**
 **List All Port Bindings:**
 
 
 One can loop over arrays and maps in the results to produce simple text
 One can loop over arrays and maps in the results to produce simple text

+ 12 - 7
docs/sources/reference/run.md

@@ -133,13 +133,14 @@ example, `docker run ubuntu:14.04`.
 
 
 ## Network settings
 ## Network settings
 
 
-    --dns=[]        : Set custom dns servers for the container
-    --net="bridge"  : Set the Network mode for the container
-                                 'bridge': creates a new network stack for the container on the docker bridge
-                                 'none': no networking for this container
-                                 'container:<name|id>': reuses another container network stack
-                                 'host': use the host network stack inside the container
-    --add-host=""   : Add a line to /etc/hosts (host:IP)
+    --dns=[]         : Set custom dns servers for the container
+    --net="bridge"   : Set the Network mode for the container
+                                  'bridge': creates a new network stack for the container on the docker bridge
+                                  'none': no networking for this container
+                                  'container:<name|id>': reuses another container network stack
+                                  'host': use the host network stack inside the container
+    --add-host=""    : Add a line to /etc/hosts (host:IP)
+    --mac-address="" : Sets the container's ethernet device's mac address
 
 
 By default, all containers have networking enabled and they can make any
 By default, all containers have networking enabled and they can make any
 outgoing connections. The operator can completely disable networking
 outgoing connections. The operator can completely disable networking
@@ -150,6 +151,10 @@ networking. In cases like this, you would perform I/O through files or
 Your container will use the same DNS servers as the host by default, but
 Your container will use the same DNS servers as the host by default, but
 you can override this with `--dns`.
 you can override this with `--dns`.
 
 
+By default a random mac is generated. You can set the container's mac address
+explicitly by providing a mac via the `--mac-address` parameter (format:
+12:34:56:78:9a:bc).
+
 Supported networking modes are:
 Supported networking modes are:
 
 
 * none - no networking in the container
 * none - no networking in the container

+ 35 - 0
integration-cli/docker_cli_run_test.go

@@ -2018,6 +2018,41 @@ func TestRunNetworkNotInitializedNoneMode(t *testing.T) {
 	logDone("run - network must not be initialized in 'none' mode")
 	logDone("run - network must not be initialized in 'none' mode")
 }
 }
 
 
+func TestRunSetMacAddress(t *testing.T) {
+	mac := "12:34:56:78:9a:bc"
+	cmd := exec.Command("/bin/bash", "-c", dockerBinary+` run -i --rm --mac-address=`+mac+` busybox /bin/sh -c "ip link show eth0 | tail -1 | awk '{ print \$2 }'"`)
+	out, _, err := runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err)
+	}
+	actualMac := strings.TrimSpace(out)
+	if actualMac != mac {
+		t.Fatalf("Set Mac Address with --mac-address failed. The container has an incorrect MAC address: %q, expected: %q", actualMac, mac)
+	}
+
+	deleteAllContainers()
+	logDone("run - setting Mac Address with --mac-address")
+}
+
+func TestRunInspectMacAddress(t *testing.T) {
+	mac := "12:34:56:78:9a:bc"
+	cmd := exec.Command(dockerBinary, "run", "-d", "--mac-address="+mac, "busybox", "top")
+	out, _, err := runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err)
+	}
+	id := strings.TrimSpace(out)
+	inspectedMac, err := inspectField(id, "NetworkSettings.MacAddress")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if inspectedMac != mac {
+		t.Fatalf("Inspecting Mac Address with failed. docker inspect shows incorrect MacAddress: %q, actual Mac: %q", inspectedMac, mac)
+	}
+	deleteAllContainers()
+	logDone("run - inspecting Mac Address")
+}
+
 func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) {
 func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) {
 	cmd := exec.Command(dockerBinary, "run", "-d", "-p", "23:23", "busybox", "top")
 	cmd := exec.Command(dockerBinary, "run", "-d", "-p", "23:23", "busybox", "top")
 	out, _, err := runCommandWithOutput(cmd)
 	out, _, err := runCommandWithOutput(cmd)

+ 2 - 0
runconfig/config.go

@@ -31,6 +31,7 @@ type Config struct {
 	WorkingDir      string
 	WorkingDir      string
 	Entrypoint      []string
 	Entrypoint      []string
 	NetworkDisabled bool
 	NetworkDisabled bool
+	MacAddress      string
 	OnBuild         []string
 	OnBuild         []string
 	SecurityOpt     []string
 	SecurityOpt     []string
 }
 }
@@ -53,6 +54,7 @@ func ContainerConfigFromJob(job *engine.Job) *Config {
 		Image:           job.Getenv("Image"),
 		Image:           job.Getenv("Image"),
 		WorkingDir:      job.Getenv("WorkingDir"),
 		WorkingDir:      job.Getenv("WorkingDir"),
 		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
 		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
+		MacAddress:      job.Getenv("MacAddress"),
 	}
 	}
 	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
 	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
 	job.GetenvJson("Volumes", &config.Volumes)
 	job.GetenvJson("Volumes", &config.Volumes)

+ 2 - 0
runconfig/parse.go

@@ -59,6 +59,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
 		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
 		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
 		flCpuset          = cmd.String([]string{"-cpuset"}, "", "CPUs in which to allow execution (0-3, 0,1)")
 		flCpuset          = cmd.String([]string{"-cpuset"}, "", "CPUs in which to allow execution (0-3, 0,1)")
 		flNetMode         = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container\n'bridge': creates a new network stack for the container on the docker bridge\n'none': no networking for this container\n'container:<name|id>': reuses another container network stack\n'host': use the host network stack inside the container.  Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.")
 		flNetMode         = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container\n'bridge': creates a new network stack for the container on the docker bridge\n'none': no networking for this container\n'container:<name|id>': reuses another container network stack\n'host': use the host network stack inside the container.  Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.")
+		flMacAddress      = cmd.String([]string{"-mac-address"}, "", "Container MAC address (ex: 92:d0:c6:0a:29:33)")
 		flRestartPolicy   = cmd.String([]string{"-restart"}, "", "Restart policy to apply when a container exits (no, on-failure[:max-retry], always)")
 		flRestartPolicy   = cmd.String([]string{"-restart"}, "", "Restart policy to apply when a container exits (no, on-failure[:max-retry], always)")
 	)
 	)
 
 
@@ -269,6 +270,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
 		Cmd:             runCmd,
 		Cmd:             runCmd,
 		Image:           image,
 		Image:           image,
 		Volumes:         flVolumes.GetMap(),
 		Volumes:         flVolumes.GetMap(),
+		MacAddress:      *flMacAddress,
 		Entrypoint:      entrypoint,
 		Entrypoint:      entrypoint,
 		WorkingDir:      *flWorkingDir,
 		WorkingDir:      *flWorkingDir,
 		SecurityOpt:     flSecurityOpt.GetAll(),
 		SecurityOpt:     flSecurityOpt.GetAll(),