Browse code

Merge pull request #8390 from MalteJ/set-macaddress

Adding docker-cli run param to set MAC address

Alexandr Morozov authored on 2014/11/05 00:54:59
Showing 11 changed files
... ...
@@ -457,6 +457,7 @@ func (container *Container) AllocateNetwork() error {
457 457
 	)
458 458
 
459 459
 	job := eng.Job("allocate_interface", container.ID)
460
+	job.Setenv("RequestedMac", container.Config.MacAddress)
460 461
 	if env, err = job.Stdout.AddEnv(); err != nil {
461 462
 		return err
462 463
 	}
... ...
@@ -29,6 +29,7 @@ docker-run - Run a command in a new container
29 29
 [**-m**|**--memory**[=*MEMORY*]]
30 30
 [**--name**[=*NAME*]]
31 31
 [**--net**[=*"bridge"*]]
32
+[**--mac-address**[=*MACADDRESS*]]
32 33
 [**-P**|**--publish-all**[=*false*]]
33 34
 [**-p**|**--publish**[=*[]*]]
34 35
 [**--privileged**[=*false*]]
... ...
@@ -187,6 +188,14 @@ and foreground Docker containers.
187 187
                                'container:<name|id>': reuses another container network stack
188 188
                                '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.
189 189
 
190
+**--mac-address**=*macaddress*
191
+   Set the MAC address for the container's Ethernet device:
192
+    --mac-address=12:34:56:78:9a:bc
193
+
194
+Remember that the MAC address in an Ethernet network must be unique.
195
+The IPv6 link-local address will be based on the device's MAC address
196
+according to RFC4862.
197
+
190 198
 **-P**, **--publish-all**=*true*|*false*
191 199
    When set to true publish all exposed ports to the host interfaces. The
192 200
 default is false. If the operator uses -P (or -p) then Docker will make the
... ...
@@ -104,6 +104,9 @@ Finally, several networking options can only be provided when calling
104 104
  *  `--net=bridge|none|container:NAME_or_ID|host` — see
105 105
     [How Docker networks a container](#container-networking)
106 106
 
107
+ *  `--mac-address=MACADDRESS...` — see
108
+    [How Docker networks a container](#container-networking)
109
+
107 110
  *  `-p SPEC` or `--publish=SPEC` — see
108 111
     [Binding container ports](#binding-ports)
109 112
 
... ...
@@ -537,9 +540,15 @@ The steps with which Docker configures a container are:
537 537
     separate and unique network interface namespace, there are no
538 538
     physical interfaces with which this name could collide.
539 539
 
540
-4.  Give the container's `eth0` a new IP address from within the
540
+4.  Set the interface's MAC address according to the `--mac-address`
541
+    parameter or generate a random one.
542
+
543
+5.  Give the container's `eth0` a new IP address from within the
541 544
     bridge's range of network addresses, and set its default route to
542
-    the IP address that the Docker host owns on the bridge.
545
+    the IP address that the Docker host owns on the bridge. If available
546
+    the IP address is generated from the MAC address. This prevents ARP
547
+    cache invalidation problems, when a new container comes up with an
548
+    IP used in the past by another container with another MAC.
543 549
 
544 550
 With these steps complete, the container now possesses an `eth0`
545 551
 (virtual) network card and will find itself able to communicate with
... ...
@@ -621,6 +630,7 @@ Docker do all of the configuration:
621 621
 
622 622
     $ sudo ip link set B netns $pid
623 623
     $ sudo ip netns exec $pid ip link set dev B name eth0
624
+    $ sudo ip netns exec $pid ip link set eth0 address 12:34:56:78:9a:bc
624 625
     $ sudo ip netns exec $pid ip link set eth0 up
625 626
     $ sudo ip netns exec $pid ip addr add 172.17.42.99/16 dev eth0
626 627
     $ sudo ip netns exec $pid ip route add default via 172.17.42.1
... ...
@@ -52,6 +52,10 @@ You can still call an old version of the API using
52 52
 `info` now returns the number of CPUs available on the machine (`NCPU`) and
53 53
 total memory available (`MemTotal`).
54 54
 
55
+`POST /containers/create`
56
+**New!**
57
+You can set the new container's MAC address explicitly.
58
+
55 59
 ## v1.15
56 60
 
57 61
 ### Full Documentation
... ...
@@ -131,6 +131,7 @@ Create a container
131 131
              },
132 132
              "WorkingDir":"",
133 133
              "NetworkDisabled": false,
134
+             "MacAddress":"12:34:56:78:9a:bc",
134 135
              "ExposedPorts":{
135 136
                      "22/tcp": {}
136 137
              },
... ...
@@ -131,6 +131,7 @@ Create a container
131 131
              },
132 132
              "WorkingDir":"",
133 133
              "NetworkDisabled": false,
134
+             "MacAddress":"12:34:56:78:9a:bc",
134 135
              "ExposedPorts":{
135 136
                      "22/tcp": {}
136 137
              },
... ...
@@ -516,6 +516,7 @@ Creates a new container.
516 516
       --lxc-conf=[]              (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
517 517
       -m, --memory=""            Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
518 518
       --name=""                  Assign a name to the container
519
+      --mac-address=""           Set the container's MAC address
519 520
       --net="bridge"             Set the Network mode for the container
520 521
                                    'bridge': creates a new network stack for the container on the docker bridge
521 522
                                    'none': no networking for this container
... ...
@@ -867,6 +868,13 @@ straightforward manner.
867 867
 
868 868
     $ sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' $INSTANCE_ID
869 869
 
870
+**Get an instance's MAC Address:**
871
+
872
+For the most part, you can pick out any field from the JSON in a fairly
873
+straightforward manner.
874
+
875
+    $ sudo docker inspect --format='{{.NetworkSettings.MacAddress}}' $INSTANCE_ID
876
+
870 877
 **List All Port Bindings:**
871 878
 
872 879
 One can loop over arrays and maps in the results to produce simple text
... ...
@@ -133,13 +133,14 @@ example, `docker run ubuntu:14.04`.
133 133
 
134 134
 ## Network settings
135 135
 
136
-    --dns=[]        : Set custom dns servers for the container
137
-    --net="bridge"  : Set the Network mode for the container
138
-                                 'bridge': creates a new network stack for the container on the docker bridge
139
-                                 'none': no networking for this container
140
-                                 'container:<name|id>': reuses another container network stack
141
-                                 'host': use the host network stack inside the container
142
-    --add-host=""   : Add a line to /etc/hosts (host:IP)
136
+    --dns=[]         : Set custom dns servers for the container
137
+    --net="bridge"   : Set the Network mode for the container
138
+                                  'bridge': creates a new network stack for the container on the docker bridge
139
+                                  'none': no networking for this container
140
+                                  'container:<name|id>': reuses another container network stack
141
+                                  'host': use the host network stack inside the container
142
+    --add-host=""    : Add a line to /etc/hosts (host:IP)
143
+    --mac-address="" : Sets the container's Ethernet device's MAC address
143 144
 
144 145
 By default, all containers have networking enabled and they can make any
145 146
 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
150 150
 Your container will use the same DNS servers as the host by default, but
151 151
 you can override this with `--dns`.
152 152
 
153
+By default a random MAC is generated. You can set the container's MAC address
154
+explicitly by providing a MAC via the `--mac-address` parameter (format:
155
+`12:34:56:78:9a:bc`).
156
+
153 157
 Supported networking modes are:
154 158
 
155 159
 * none - no networking in the container
... ...
@@ -2018,6 +2018,41 @@ func TestRunNetworkNotInitializedNoneMode(t *testing.T) {
2018 2018
 	logDone("run - network must not be initialized in 'none' mode")
2019 2019
 }
2020 2020
 
2021
+func TestRunSetMacAddress(t *testing.T) {
2022
+	mac := "12:34:56:78:9a:bc"
2023
+	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 }'"`)
2024
+	out, _, err := runCommandWithOutput(cmd)
2025
+	if err != nil {
2026
+		t.Fatal(err)
2027
+	}
2028
+	actualMac := strings.TrimSpace(out)
2029
+	if actualMac != mac {
2030
+		t.Fatalf("Set MAC address with --mac-address failed. The container has an incorrect MAC address: %q, expected: %q", actualMac, mac)
2031
+	}
2032
+
2033
+	deleteAllContainers()
2034
+	logDone("run - setting MAC address with --mac-address")
2035
+}
2036
+
2037
+func TestRunInspectMacAddress(t *testing.T) {
2038
+	mac := "12:34:56:78:9a:bc"
2039
+	cmd := exec.Command(dockerBinary, "run", "-d", "--mac-address="+mac, "busybox", "top")
2040
+	out, _, err := runCommandWithOutput(cmd)
2041
+	if err != nil {
2042
+		t.Fatal(err)
2043
+	}
2044
+	id := strings.TrimSpace(out)
2045
+	inspectedMac, err := inspectField(id, "NetworkSettings.MacAddress")
2046
+	if err != nil {
2047
+		t.Fatal(err)
2048
+	}
2049
+	if inspectedMac != mac {
2050
+		t.Fatalf("docker inspect outputs wrong MAC address: %q, should be: %q", inspectedMac, mac)
2051
+	}
2052
+	deleteAllContainers()
2053
+	logDone("run - inspecting MAC address")
2054
+}
2055
+
2021 2056
 func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) {
2022 2057
 	cmd := exec.Command(dockerBinary, "run", "-d", "-p", "23:23", "busybox", "top")
2023 2058
 	out, _, err := runCommandWithOutput(cmd)
... ...
@@ -31,6 +31,7 @@ type Config struct {
31 31
 	WorkingDir      string
32 32
 	Entrypoint      []string
33 33
 	NetworkDisabled bool
34
+	MacAddress      string
34 35
 	OnBuild         []string
35 36
 	SecurityOpt     []string
36 37
 }
... ...
@@ -53,6 +54,7 @@ func ContainerConfigFromJob(job *engine.Job) *Config {
53 53
 		Image:           job.Getenv("Image"),
54 54
 		WorkingDir:      job.Getenv("WorkingDir"),
55 55
 		NetworkDisabled: job.GetenvBool("NetworkDisabled"),
56
+		MacAddress:      job.Getenv("MacAddress"),
56 57
 	}
57 58
 	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
58 59
 	job.GetenvJson("Volumes", &config.Volumes)
... ...
@@ -59,6 +59,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
59 59
 		flCpuShares       = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
60 60
 		flCpuset          = cmd.String([]string{"-cpuset"}, "", "CPUs in which to allow execution (0-3, 0,1)")
61 61
 		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.")
62
+		flMacAddress      = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
62 63
 		flRestartPolicy   = cmd.String([]string{"-restart"}, "", "Restart policy to apply when a container exits (no, on-failure[:max-retry], always)")
63 64
 	)
64 65
 
... ...
@@ -269,6 +270,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
269 269
 		Cmd:             runCmd,
270 270
 		Image:           image,
271 271
 		Volumes:         flVolumes.GetMap(),
272
+		MacAddress:      *flMacAddress,
272 273
 		Entrypoint:      entrypoint,
273 274
 		WorkingDir:      *flWorkingDir,
274 275
 		SecurityOpt:     flSecurityOpt.GetAll(),