@@ -21,6 +21,7 @@ import (
21
21
"errors"
22
22
"fmt"
23
23
"io/fs"
24
+ "net"
24
25
"path/filepath"
25
26
"runtime"
26
27
"strings"
@@ -110,75 +111,88 @@ func withCustomHosts(src string) func(context.Context, oci.Client, *containers.C
110
111
}
111
112
}
112
113
113
- func generateNetOpts (cmd * cobra.Command , dataStore , stateDir , ns , id string ) ([]oci.SpecOpts , []string , string , []gocni.PortMapping , error ) {
114
+ func generateNetOpts (cmd * cobra.Command , dataStore , stateDir , ns , id string ) ([]oci.SpecOpts , []string , string , []gocni.PortMapping , string , error ) {
114
115
opts := []oci.SpecOpts {}
115
116
portSlice , err := cmd .Flags ().GetStringSlice ("publish" )
116
117
if err != nil {
117
- return nil , nil , "" , nil , err
118
+ return nil , nil , "" , nil , "" , err
118
119
}
119
120
ipAddress , err := cmd .Flags ().GetString ("ip" )
120
121
if err != nil {
121
- return nil , nil , "" , nil , err
122
+ return nil , nil , "" , nil , "" , err
122
123
}
123
124
netSlice , err := getNetworkSlice (cmd )
124
125
if err != nil {
125
- return nil , nil , "" , nil , err
126
+ return nil , nil , "" , nil , "" , err
126
127
}
127
128
128
129
if (len (netSlice ) == 0 ) && (ipAddress != "" ) {
129
130
logrus .Warnf ("You have assign an IP address %s but no network, So we will use the default network" , ipAddress )
130
131
}
131
132
133
+ macAddress , err := getMACAddress (cmd , netSlice )
134
+ if err != nil {
135
+ return nil , nil , "" , nil , "" , err
136
+ }
137
+
132
138
ports := make ([]gocni.PortMapping , 0 )
133
139
netType , err := nettype .Detect (netSlice )
134
140
if err != nil {
135
- return nil , nil , "" , nil , err
141
+ return nil , nil , "" , nil , "" , err
136
142
}
137
143
138
144
switch netType {
139
145
case nettype .None :
140
146
// NOP
147
+ // Docker compatible: if macAddress is specified, set MAC address shall
148
+ // not work but run command will success
141
149
case nettype .Host :
150
+ if macAddress != "" {
151
+ return nil , nil , "" , nil , "" , errors .New ("conflicting options: mac-address and the network mode" )
152
+ }
142
153
opts = append (opts , oci .WithHostNamespace (specs .NetworkNamespace ), oci .WithHostHostsFile , oci .WithHostResolvconf )
143
154
case nettype .CNI :
144
155
// We only verify flags and generate resolv.conf here.
145
156
// The actual network is configured in the oci hook.
146
- if err := verifyCNINetwork (cmd , netSlice ); err != nil {
147
- return nil , nil , "" , nil , err
157
+ if err := verifyCNINetwork (cmd , netSlice , macAddress ); err != nil {
158
+ return nil , nil , "" , nil , "" , err
148
159
}
149
160
150
161
if runtime .GOOS == "linux" {
151
162
resolvConfPath := filepath .Join (stateDir , "resolv.conf" )
152
163
if err := buildResolvConf (cmd , resolvConfPath ); err != nil {
153
- return nil , nil , "" , nil , err
164
+ return nil , nil , "" , nil , "" , err
154
165
}
155
166
156
167
// the content of /etc/hosts is created in OCI Hook
157
168
etcHostsPath , err := hostsstore .AllocHostsFile (dataStore , ns , id )
158
169
if err != nil {
159
- return nil , nil , "" , nil , err
170
+ return nil , nil , "" , nil , "" , err
160
171
}
161
172
opts = append (opts , withCustomResolvConf (resolvConfPath ), withCustomHosts (etcHostsPath ))
162
173
for _ , p := range portSlice {
163
174
pm , err := portutil .ParseFlagP (p )
164
175
if err != nil {
165
- return nil , nil , "" , pm , err
176
+ return nil , nil , "" , pm , "" , err
166
177
}
167
178
ports = append (ports , pm ... )
168
179
}
169
180
}
170
181
case nettype .Container :
182
+ if macAddress != "" {
183
+ return nil , nil , "" , nil , "" , errors .New ("conflicting options: mac-address and the network mode" )
184
+ }
171
185
if err := verifyContainerNetwork (cmd , netSlice ); err != nil {
172
- return nil , nil , "" , nil , err
186
+ return nil , nil , "" , nil , "" , err
173
187
}
174
188
network := strings .Split (netSlice [0 ], ":" )
175
189
if len (network ) != 2 {
176
- return nil , nil , "" , nil , fmt .Errorf ("invalid network: %s, should be \" container:<id|name>\" " , netSlice [0 ])
190
+ return nil , nil , "" , nil , "" , fmt .Errorf ("invalid network: %s, should be \" container:<id|name>\" " , netSlice [0 ])
177
191
}
178
192
containerName := network [1 ]
179
193
client , ctx , cancel , err := newClient (cmd )
180
194
if err != nil {
181
- return nil , nil , "" , nil , err
195
+ return nil , nil , "" , nil , "" , err
182
196
}
183
197
defer cancel ()
184
198
@@ -224,15 +238,15 @@ func generateNetOpts(cmd *cobra.Command, dataStore, stateDir, ns, id string) ([]
224
238
}
225
239
n , err := walker .Walk (ctx , containerName )
226
240
if err != nil {
227
- return nil , nil , "" , nil , err
241
+ return nil , nil , "" , nil , "" , err
228
242
}
229
243
if n == 0 {
230
- return nil , nil , "" , nil , fmt .Errorf ("no such container: %s" , containerName )
244
+ return nil , nil , "" , nil , "" , fmt .Errorf ("no such container: %s" , containerName )
231
245
}
232
246
default :
233
- return nil , nil , "" , nil , fmt .Errorf ("unexpected network type %v" , netType )
247
+ return nil , nil , "" , nil , "" , fmt .Errorf ("unexpected network type %v" , netType )
234
248
}
235
- return opts , netSlice , ipAddress , ports , nil
249
+ return opts , netSlice , ipAddress , ports , macAddress , nil
236
250
}
237
251
238
252
func getContainerNetNSPath (ctx context.Context , c containerd.Container ) (string , error ) {
@@ -250,7 +264,7 @@ func getContainerNetNSPath(ctx context.Context, c containerd.Container) (string,
250
264
return fmt .Sprintf ("/proc/%d/ns/net" , task .Pid ()), nil
251
265
}
252
266
253
- func verifyCNINetwork (cmd * cobra.Command , netSlice []string ) error {
267
+ func verifyCNINetwork (cmd * cobra.Command , netSlice []string , macAddress string ) error {
254
268
cniPath , err := cmd .Flags ().GetString ("cni-path" )
255
269
if err != nil {
256
270
return err
@@ -263,12 +277,19 @@ func verifyCNINetwork(cmd *cobra.Command, netSlice []string) error {
263
277
if err != nil {
264
278
return err
265
279
}
280
+ macValidNetworks := []string {"bridge" , "macvlan" }
266
281
netMap := e .NetworkMap ()
267
282
for _ , netstr := range netSlice {
268
- _ , ok := netMap [netstr ]
283
+ netConfig , ok := netMap [netstr ]
269
284
if ! ok {
270
285
return fmt .Errorf ("network %s not found" , netstr )
271
286
}
287
+ // if MAC address is specified, the type of the network
288
+ // must be one of macValidNetworks
289
+ netType := netConfig .Plugins [0 ].Network .Type
290
+ if macAddress != "" && ! strutil .InStringSlice (macValidNetworks , netType ) {
291
+ return fmt .Errorf ("%s interfaces on network %s do not support --mac-address" , netType , netstr )
292
+ }
272
293
}
273
294
return nil
274
295
}
@@ -360,3 +381,17 @@ func buildResolvConf(cmd *cobra.Command, resolvConfPath string) error {
360
381
}
361
382
return nil
362
383
}
384
+
385
+ func getMACAddress (cmd * cobra.Command , netSlice []string ) (string , error ) {
386
+ macAddress , err := cmd .Flags ().GetString ("mac-address" )
387
+ if err != nil {
388
+ return "" , err
389
+ }
390
+ if macAddress == "" {
391
+ return "" , nil
392
+ }
393
+ if _ , err := net .ParseMAC (macAddress ); err != nil {
394
+ return "" , err
395
+ }
396
+ return macAddress , nil
397
+ }
0 commit comments