]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
e0edde6f | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. |
064af421 | 3 | * |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "netdev.h" | |
19 | ||
20 | #include <assert.h> | |
21 | #include <errno.h> | |
064af421 | 22 | #include <inttypes.h> |
064af421 BP |
23 | #include <netinet/in.h> |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | #include <unistd.h> | |
27 | ||
28 | #include "coverage.h" | |
29 | #include "dynamic-string.h" | |
30 | #include "fatal-signal.h" | |
149f577a | 31 | #include "hash.h" |
064af421 | 32 | #include "list.h" |
8b61709d | 33 | #include "netdev-provider.h" |
2b9d6589 | 34 | #include "netdev-vport.h" |
064af421 | 35 | #include "ofpbuf.h" |
622ee2cf | 36 | #include "openflow/openflow.h" |
064af421 BP |
37 | #include "packets.h" |
38 | #include "poll-loop.h" | |
e9e28be3 | 39 | #include "shash.h" |
79f1cbe9 | 40 | #include "smap.h" |
b3c01ed3 | 41 | #include "sset.h" |
064af421 | 42 | #include "svec.h" |
064af421 BP |
43 | #include "vlog.h" |
44 | ||
d98e6007 | 45 | VLOG_DEFINE_THIS_MODULE(netdev); |
5136ce49 | 46 | |
d76f09ea BP |
47 | COVERAGE_DEFINE(netdev_received); |
48 | COVERAGE_DEFINE(netdev_sent); | |
49 | COVERAGE_DEFINE(netdev_add_router); | |
50 | COVERAGE_DEFINE(netdev_get_stats); | |
51 | ||
77909859 | 52 | static struct shash netdev_classes = SHASH_INITIALIZER(&netdev_classes); |
064af421 | 53 | |
6c88d577 | 54 | /* All created network devices. */ |
149f577a | 55 | static struct shash netdev_dev_shash = SHASH_INITIALIZER(&netdev_dev_shash); |
6c88d577 | 56 | |
064af421 BP |
57 | /* All open network devices. */ |
58 | static struct list netdev_list = LIST_INITIALIZER(&netdev_list); | |
59 | ||
064af421 BP |
60 | /* This is set pretty low because we probably won't learn anything from the |
61 | * additional log messages. */ | |
62 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); | |
63 | ||
c69ee87c | 64 | static void close_all_netdevs(void *aux OVS_UNUSED); |
064af421 | 65 | static int restore_flags(struct netdev *netdev); |
0f4f4a61 | 66 | void update_device_args(struct netdev_dev *, const struct shash *args); |
8b61709d | 67 | |
77909859 | 68 | static void |
8b61709d | 69 | netdev_initialize(void) |
064af421 | 70 | { |
2b9d6589 | 71 | static bool inited; |
e3830e90 | 72 | |
2b9d6589 BP |
73 | if (!inited) { |
74 | inited = true; | |
8b61709d | 75 | |
e3830e90 | 76 | fatal_signal_add_hook(close_all_netdevs, NULL, NULL, true); |
064af421 | 77 | |
2b9d6589 BP |
78 | #ifdef HAVE_NETLINK |
79 | netdev_register_provider(&netdev_linux_class); | |
c3827f61 | 80 | netdev_register_provider(&netdev_internal_class); |
2b9d6589 BP |
81 | netdev_register_provider(&netdev_tap_class); |
82 | netdev_vport_register(); | |
f6eb6b20 GL |
83 | #endif |
84 | #ifdef __FreeBSD__ | |
85 | netdev_register_provider(&netdev_tap_class); | |
86 | netdev_register_provider(&netdev_bsd_class); | |
2b9d6589 | 87 | #endif |
064af421 | 88 | } |
064af421 BP |
89 | } |
90 | ||
8b61709d BP |
91 | /* Performs periodic work needed by all the various kinds of netdevs. |
92 | * | |
93 | * If your program opens any netdevs, it must call this function within its | |
94 | * main poll loop. */ | |
95 | void | |
96 | netdev_run(void) | |
064af421 | 97 | { |
77909859 JG |
98 | struct shash_node *node; |
99 | SHASH_FOR_EACH(node, &netdev_classes) { | |
7dab847a JG |
100 | const struct netdev_class *netdev_class = node->data; |
101 | if (netdev_class->run) { | |
102 | netdev_class->run(); | |
064af421 | 103 | } |
064af421 BP |
104 | } |
105 | } | |
106 | ||
8b61709d BP |
107 | /* Arranges for poll_block() to wake up when netdev_run() needs to be called. |
108 | * | |
109 | * If your program opens any netdevs, it must call this function within its | |
110 | * main poll loop. */ | |
111 | void | |
112 | netdev_wait(void) | |
064af421 | 113 | { |
77909859 JG |
114 | struct shash_node *node; |
115 | SHASH_FOR_EACH(node, &netdev_classes) { | |
7dab847a JG |
116 | const struct netdev_class *netdev_class = node->data; |
117 | if (netdev_class->wait) { | |
118 | netdev_class->wait(); | |
8b61709d | 119 | } |
064af421 | 120 | } |
064af421 BP |
121 | } |
122 | ||
77909859 JG |
123 | /* Initializes and registers a new netdev provider. After successful |
124 | * registration, new netdevs of that type can be opened using netdev_open(). */ | |
125 | int | |
126 | netdev_register_provider(const struct netdev_class *new_class) | |
127 | { | |
77909859 JG |
128 | if (shash_find(&netdev_classes, new_class->type)) { |
129 | VLOG_WARN("attempted to register duplicate netdev provider: %s", | |
130 | new_class->type); | |
131 | return EEXIST; | |
132 | } | |
133 | ||
134 | if (new_class->init) { | |
135 | int error = new_class->init(); | |
136 | if (error) { | |
137 | VLOG_ERR("failed to initialize %s network device class: %s", | |
138 | new_class->type, strerror(error)); | |
139 | return error; | |
140 | } | |
141 | } | |
142 | ||
2b9d6589 | 143 | shash_add(&netdev_classes, new_class->type, new_class); |
77909859 JG |
144 | |
145 | return 0; | |
146 | } | |
147 | ||
148 | /* Unregisters a netdev provider. 'type' must have been previously | |
149 | * registered and not currently be in use by any netdevs. After unregistration | |
150 | * new netdevs of that type cannot be opened using netdev_open(). */ | |
151 | int | |
152 | netdev_unregister_provider(const char *type) | |
153 | { | |
154 | struct shash_node *del_node, *netdev_dev_node; | |
155 | ||
156 | del_node = shash_find(&netdev_classes, type); | |
157 | if (!del_node) { | |
158 | VLOG_WARN("attempted to unregister a netdev provider that is not " | |
159 | "registered: %s", type); | |
160 | return EAFNOSUPPORT; | |
161 | } | |
162 | ||
163 | SHASH_FOR_EACH(netdev_dev_node, &netdev_dev_shash) { | |
164 | struct netdev_dev *netdev_dev = netdev_dev_node->data; | |
a4af0040 | 165 | if (!strcmp(netdev_dev->netdev_class->type, type)) { |
77909859 JG |
166 | VLOG_WARN("attempted to unregister in use netdev provider: %s", |
167 | type); | |
168 | return EBUSY; | |
169 | } | |
170 | } | |
171 | ||
172 | shash_delete(&netdev_classes, del_node); | |
77909859 JG |
173 | |
174 | return 0; | |
175 | } | |
176 | ||
c3827f61 BP |
177 | const struct netdev_class * |
178 | netdev_lookup_provider(const char *type) | |
179 | { | |
180 | netdev_initialize(); | |
181 | return shash_find_data(&netdev_classes, type && type[0] ? type : "system"); | |
182 | } | |
183 | ||
77909859 | 184 | /* Clears 'types' and enumerates the types of all currently registered netdev |
19993ef3 | 185 | * providers into it. The caller must first initialize the sset. */ |
77909859 | 186 | void |
19993ef3 | 187 | netdev_enumerate_types(struct sset *types) |
77909859 JG |
188 | { |
189 | struct shash_node *node; | |
190 | ||
191 | netdev_initialize(); | |
19993ef3 | 192 | sset_clear(types); |
77909859 JG |
193 | |
194 | SHASH_FOR_EACH(node, &netdev_classes) { | |
195 | const struct netdev_class *netdev_class = node->data; | |
19993ef3 | 196 | sset_add(types, netdev_class->type); |
77909859 JG |
197 | } |
198 | } | |
199 | ||
18812dff BP |
200 | /* Opens the network device named 'name' (e.g. "eth0") of the specified 'type' |
201 | * (e.g. "system") and returns zero if successful, otherwise a positive errno | |
202 | * value. On success, sets '*netdevp' to the new network device, otherwise to | |
203 | * null. | |
064af421 | 204 | * |
de5cdb90 BP |
205 | * Some network devices may need to be configured (with netdev_set_config()) |
206 | * before they can be used. */ | |
064af421 | 207 | int |
18812dff | 208 | netdev_open(const char *name, const char *type, struct netdev **netdevp) |
064af421 | 209 | { |
149f577a | 210 | struct netdev_dev *netdev_dev; |
064af421 | 211 | int error; |
064af421 | 212 | |
149f577a | 213 | *netdevp = NULL; |
559843ed | 214 | netdev_initialize(); |
6c88d577 | 215 | |
18812dff | 216 | netdev_dev = shash_find_data(&netdev_dev_shash, name); |
149f577a JG |
217 | |
218 | if (!netdev_dev) { | |
c3827f61 BP |
219 | const struct netdev_class *class; |
220 | ||
18812dff | 221 | class = netdev_lookup_provider(type); |
c3827f61 BP |
222 | if (!class) { |
223 | VLOG_WARN("could not create netdev %s of unknown type %s", | |
18812dff | 224 | name, type); |
c3827f61 BP |
225 | return EAFNOSUPPORT; |
226 | } | |
18812dff | 227 | error = class->create(class, name, &netdev_dev); |
149f577a JG |
228 | if (error) { |
229 | return error; | |
230 | } | |
c3827f61 | 231 | assert(netdev_dev->netdev_class == class); |
149f577a | 232 | |
064af421 | 233 | } |
149f577a | 234 | |
7b6b0ef4 | 235 | error = netdev_dev->netdev_class->open(netdev_dev, netdevp); |
149f577a | 236 | |
6c88d577 | 237 | if (!error) { |
149f577a JG |
238 | netdev_dev->ref_cnt++; |
239 | } else { | |
240 | if (!netdev_dev->ref_cnt) { | |
241 | netdev_dev_uninit(netdev_dev, true); | |
242 | } | |
6c88d577 | 243 | } |
064af421 | 244 | |
064af421 BP |
245 | return error; |
246 | } | |
247 | ||
149f577a JG |
248 | /* Reconfigures the device 'netdev' with 'args'. 'args' may be empty |
249 | * or NULL if none are needed. */ | |
250 | int | |
79f1cbe9 | 251 | netdev_set_config(struct netdev *netdev, const struct smap *args) |
149f577a | 252 | { |
149f577a JG |
253 | struct netdev_dev *netdev_dev = netdev_get_dev(netdev); |
254 | ||
6d9e6eb4 | 255 | if (netdev_dev->netdev_class->set_config) { |
79f1cbe9 | 256 | struct smap no_args = SMAP_INITIALIZER(&no_args); |
de5cdb90 BP |
257 | return netdev_dev->netdev_class->set_config(netdev_dev, |
258 | args ? args : &no_args); | |
79f1cbe9 | 259 | } else if (args && !smap_is_empty(args)) { |
de5cdb90 BP |
260 | VLOG_WARN("%s: arguments provided to device that is not configurable", |
261 | netdev_get_name(netdev)); | |
149f577a JG |
262 | } |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
de5cdb90 | 267 | /* Returns the current configuration for 'netdev' in 'args'. The caller must |
79f1cbe9 | 268 | * have already initialized 'args' with smap_init(). Returns 0 on success, in |
de5cdb90 BP |
269 | * which case 'args' will be filled with 'netdev''s configuration. On failure |
270 | * returns a positive errno value, in which case 'args' will be empty. | |
6d9e6eb4 | 271 | * |
de5cdb90 | 272 | * The caller owns 'args' and its contents and must eventually free them with |
79f1cbe9 | 273 | * smap_destroy(). */ |
de5cdb90 | 274 | int |
79f1cbe9 | 275 | netdev_get_config(const struct netdev *netdev, struct smap *args) |
6d9e6eb4 | 276 | { |
de5cdb90 BP |
277 | struct netdev_dev *netdev_dev = netdev_get_dev(netdev); |
278 | int error; | |
279 | ||
79f1cbe9 | 280 | smap_clear(args); |
de5cdb90 BP |
281 | if (netdev_dev->netdev_class->get_config) { |
282 | error = netdev_dev->netdev_class->get_config(netdev_dev, args); | |
283 | if (error) { | |
79f1cbe9 | 284 | smap_clear(args); |
de5cdb90 BP |
285 | } |
286 | } else { | |
287 | error = 0; | |
288 | } | |
289 | ||
290 | return error; | |
6d9e6eb4 BP |
291 | } |
292 | ||
064af421 BP |
293 | /* Closes and destroys 'netdev'. */ |
294 | void | |
295 | netdev_close(struct netdev *netdev) | |
296 | { | |
297 | if (netdev) { | |
149f577a | 298 | struct netdev_dev *netdev_dev = netdev_get_dev(netdev); |
6c88d577 | 299 | |
149f577a JG |
300 | assert(netdev_dev->ref_cnt); |
301 | netdev_dev->ref_cnt--; | |
302 | netdev_uninit(netdev, true); | |
6c88d577 | 303 | |
149f577a JG |
304 | /* If the reference count for the netdev device is zero, destroy it. */ |
305 | if (!netdev_dev->ref_cnt) { | |
306 | netdev_dev_uninit(netdev_dev, true); | |
064af421 | 307 | } |
064af421 BP |
308 | } |
309 | } | |
310 | ||
8b61709d BP |
311 | /* Returns true if a network device named 'name' exists and may be opened, |
312 | * otherwise false. */ | |
5bfc0cd3 BP |
313 | bool |
314 | netdev_exists(const char *name) | |
315 | { | |
8b61709d | 316 | struct netdev *netdev; |
5bfc0cd3 BP |
317 | int error; |
318 | ||
18812dff | 319 | error = netdev_open(name, "system", &netdev); |
8b61709d BP |
320 | if (!error) { |
321 | netdev_close(netdev); | |
322 | return true; | |
323 | } else { | |
324 | if (error != ENODEV) { | |
325 | VLOG_WARN("failed to open network device %s: %s", | |
326 | name, strerror(error)); | |
327 | } | |
328 | return false; | |
329 | } | |
5bfc0cd3 BP |
330 | } |
331 | ||
fdd82248 JG |
332 | /* Returns true if a network device named 'name' is currently opened, |
333 | * otherwise false. */ | |
334 | bool | |
335 | netdev_is_open(const char *name) | |
336 | { | |
337 | return !!shash_find_data(&netdev_dev_shash, name); | |
338 | } | |
339 | ||
a75531e5 BP |
340 | /* Parses 'netdev_name_', which is of the form [type@]name into its component |
341 | * pieces. 'name' and 'type' must be freed by the caller. */ | |
342 | void | |
343 | netdev_parse_name(const char *netdev_name_, char **name, char **type) | |
344 | { | |
345 | char *netdev_name = xstrdup(netdev_name_); | |
346 | char *separator; | |
347 | ||
348 | separator = strchr(netdev_name, '@'); | |
349 | if (separator) { | |
350 | *separator = '\0'; | |
351 | *type = netdev_name; | |
352 | *name = xstrdup(separator + 1); | |
353 | } else { | |
354 | *name = netdev_name; | |
355 | *type = xstrdup("system"); | |
356 | } | |
357 | } | |
358 | ||
7b6b0ef4 BP |
359 | /* Attempts to set up 'netdev' for receiving packets with netdev_recv(). |
360 | * Returns 0 if successful, otherwise a positive errno value. EOPNOTSUPP | |
361 | * indicates that the network device does not implement packet reception | |
362 | * through this interface. */ | |
363 | int | |
364 | netdev_listen(struct netdev *netdev) | |
365 | { | |
366 | int (*listen)(struct netdev *); | |
367 | ||
368 | listen = netdev_get_dev(netdev)->netdev_class->listen; | |
369 | return listen ? (listen)(netdev) : EOPNOTSUPP; | |
370 | } | |
371 | ||
064af421 BP |
372 | /* Attempts to receive a packet from 'netdev' into 'buffer', which the caller |
373 | * must have initialized with sufficient room for the packet. The space | |
374 | * required to receive any packet is ETH_HEADER_LEN bytes, plus VLAN_HEADER_LEN | |
375 | * bytes, plus the device's MTU (which may be retrieved via netdev_get_mtu()). | |
376 | * (Some devices do not allow for a VLAN header, in which case VLAN_HEADER_LEN | |
377 | * need not be included.) | |
378 | * | |
7b6b0ef4 BP |
379 | * This function can only be expected to return a packet if ->listen() has |
380 | * been called successfully. | |
381 | * | |
064af421 BP |
382 | * If a packet is successfully retrieved, returns 0. In this case 'buffer' is |
383 | * guaranteed to contain at least ETH_TOTAL_MIN bytes. Otherwise, returns a | |
384 | * positive errno value. Returns EAGAIN immediately if no packet is ready to | |
385 | * be returned. | |
1ac98180 BP |
386 | * |
387 | * Some network devices may not implement support for this function. In such | |
c1fdab01 | 388 | * cases this function will always return EOPNOTSUPP. */ |
064af421 BP |
389 | int |
390 | netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) | |
391 | { | |
1ac98180 | 392 | int (*recv)(struct netdev *, void *, size_t); |
8b61709d | 393 | int retval; |
064af421 BP |
394 | |
395 | assert(buffer->size == 0); | |
396 | assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN); | |
8b61709d | 397 | |
1ac98180 BP |
398 | recv = netdev_get_dev(netdev)->netdev_class->recv; |
399 | retval = (recv | |
400 | ? (recv)(netdev, buffer->data, ofpbuf_tailroom(buffer)) | |
401 | : -EOPNOTSUPP); | |
8b61709d | 402 | if (retval >= 0) { |
064af421 | 403 | COVERAGE_INC(netdev_received); |
8b61709d BP |
404 | buffer->size += retval; |
405 | if (buffer->size < ETH_TOTAL_MIN) { | |
406 | ofpbuf_put_zeros(buffer, ETH_TOTAL_MIN - buffer->size); | |
407 | } | |
064af421 | 408 | return 0; |
8b61709d BP |
409 | } else { |
410 | return -retval; | |
064af421 BP |
411 | } |
412 | } | |
413 | ||
414 | /* Registers with the poll loop to wake up from the next call to poll_block() | |
415 | * when a packet is ready to be received with netdev_recv() on 'netdev'. */ | |
416 | void | |
417 | netdev_recv_wait(struct netdev *netdev) | |
418 | { | |
1ac98180 BP |
419 | void (*recv_wait)(struct netdev *); |
420 | ||
421 | recv_wait = netdev_get_dev(netdev)->netdev_class->recv_wait; | |
422 | if (recv_wait) { | |
423 | recv_wait(netdev); | |
424 | } | |
064af421 BP |
425 | } |
426 | ||
427 | /* Discards all packets waiting to be received from 'netdev'. */ | |
428 | int | |
429 | netdev_drain(struct netdev *netdev) | |
430 | { | |
1ac98180 BP |
431 | int (*drain)(struct netdev *); |
432 | ||
433 | drain = netdev_get_dev(netdev)->netdev_class->drain; | |
434 | return drain ? drain(netdev) : 0; | |
064af421 BP |
435 | } |
436 | ||
437 | /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive | |
438 | * errno value. Returns EAGAIN without blocking if the packet cannot be queued | |
439 | * immediately. Returns EMSGSIZE if a partial packet was transmitted or if | |
440 | * the packet is too big or too small to transmit on the device. | |
441 | * | |
442 | * The caller retains ownership of 'buffer' in all cases. | |
443 | * | |
444 | * The kernel maintains a packet transmission queue, so the caller is not | |
1ac98180 BP |
445 | * expected to do additional queuing of packets. |
446 | * | |
447 | * Some network devices may not implement support for this function. In such | |
448 | * cases this function will always return EOPNOTSUPP. */ | |
064af421 BP |
449 | int |
450 | netdev_send(struct netdev *netdev, const struct ofpbuf *buffer) | |
451 | { | |
1ac98180 BP |
452 | int (*send)(struct netdev *, const void *, size_t); |
453 | int error; | |
454 | ||
455 | send = netdev_get_dev(netdev)->netdev_class->send; | |
456 | error = send ? (send)(netdev, buffer->data, buffer->size) : EOPNOTSUPP; | |
8b61709d | 457 | if (!error) { |
064af421 | 458 | COVERAGE_INC(netdev_sent); |
064af421 | 459 | } |
8b61709d | 460 | return error; |
064af421 BP |
461 | } |
462 | ||
463 | /* Registers with the poll loop to wake up from the next call to poll_block() | |
464 | * when the packet transmission queue has sufficient room to transmit a packet | |
465 | * with netdev_send(). | |
466 | * | |
467 | * The kernel maintains a packet transmission queue, so the client is not | |
468 | * expected to do additional queuing of packets. Thus, this function is | |
469 | * unlikely to ever be used. It is included for completeness. */ | |
470 | void | |
471 | netdev_send_wait(struct netdev *netdev) | |
472 | { | |
1ac98180 BP |
473 | void (*send_wait)(struct netdev *); |
474 | ||
475 | send_wait = netdev_get_dev(netdev)->netdev_class->send_wait; | |
476 | if (send_wait) { | |
477 | send_wait(netdev); | |
478 | } | |
064af421 BP |
479 | } |
480 | ||
481 | /* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, | |
482 | * otherwise a positive errno value. */ | |
483 | int | |
484 | netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN]) | |
485 | { | |
a4af0040 | 486 | return netdev_get_dev(netdev)->netdev_class->set_etheraddr(netdev, mac); |
064af421 BP |
487 | } |
488 | ||
80992a35 BP |
489 | /* Retrieves 'netdev''s MAC address. If successful, returns 0 and copies the |
490 | * the MAC address into 'mac'. On failure, returns a positive errno value and | |
491 | * clears 'mac' to all-zeros. */ | |
492 | int | |
493 | netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN]) | |
064af421 | 494 | { |
a4af0040 | 495 | return netdev_get_dev(netdev)->netdev_class->get_etheraddr(netdev, mac); |
064af421 BP |
496 | } |
497 | ||
498 | /* Returns the name of the network device that 'netdev' represents, | |
499 | * e.g. "eth0". The caller must not modify or free the returned string. */ | |
500 | const char * | |
501 | netdev_get_name(const struct netdev *netdev) | |
502 | { | |
149f577a | 503 | return netdev_get_dev(netdev)->name; |
064af421 BP |
504 | } |
505 | ||
3d222126 BP |
506 | /* Retrieves the MTU of 'netdev'. The MTU is the maximum size of transmitted |
507 | * (and received) packets, in bytes, not including the hardware header; thus, | |
508 | * this is typically 1500 bytes for Ethernet devices. | |
509 | * | |
9b020780 PS |
510 | * If successful, returns 0 and stores the MTU size in '*mtup'. Returns |
511 | * EOPNOTSUPP if 'netdev' does not have an MTU (as e.g. some tunnels do not). | |
14622f22 BP |
512 | * On other failure, returns a positive errno value. On failure, sets '*mtup' |
513 | * to 0. */ | |
064af421 | 514 | int |
3d222126 | 515 | netdev_get_mtu(const struct netdev *netdev, int *mtup) |
064af421 | 516 | { |
14622f22 BP |
517 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; |
518 | int error; | |
519 | ||
520 | error = class->get_mtu ? class->get_mtu(netdev, mtup) : EOPNOTSUPP; | |
521 | if (error) { | |
522 | *mtup = 0; | |
523 | if (error != EOPNOTSUPP) { | |
90a6637d | 524 | VLOG_DBG_RL(&rl, "failed to retrieve MTU for network device %s: " |
14622f22 BP |
525 | "%s", netdev_get_name(netdev), strerror(error)); |
526 | } | |
8b61709d BP |
527 | } |
528 | return error; | |
064af421 BP |
529 | } |
530 | ||
9b020780 PS |
531 | /* Sets the MTU of 'netdev'. The MTU is the maximum size of transmitted |
532 | * (and received) packets, in bytes. | |
533 | * | |
534 | * If successful, returns 0. Returns EOPNOTSUPP if 'netdev' does not have an | |
535 | * MTU (as e.g. some tunnels do not). On other failure, returns a positive | |
536 | * errno value. */ | |
537 | int | |
538 | netdev_set_mtu(const struct netdev *netdev, int mtu) | |
539 | { | |
14622f22 BP |
540 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; |
541 | int error; | |
9b020780 | 542 | |
14622f22 | 543 | error = class->set_mtu ? class->set_mtu(netdev, mtu) : EOPNOTSUPP; |
9b020780 | 544 | if (error && error != EOPNOTSUPP) { |
90a6637d | 545 | VLOG_DBG_RL(&rl, "failed to set MTU for network device %s: %s", |
9b020780 PS |
546 | netdev_get_name(netdev), strerror(error)); |
547 | } | |
548 | ||
549 | return error; | |
550 | } | |
551 | ||
9ab3d9a3 BP |
552 | /* Returns the ifindex of 'netdev', if successful, as a positive number. On |
553 | * failure, returns a negative errno value. | |
554 | * | |
555 | * The desired semantics of the ifindex value are a combination of those | |
556 | * specified by POSIX for if_nametoindex() and by SNMP for ifIndex. An ifindex | |
557 | * value should be unique within a host and remain stable at least until | |
558 | * reboot. SNMP says an ifindex "ranges between 1 and the value of ifNumber" | |
559 | * but many systems do not follow this rule anyhow. | |
4c0f1780 JG |
560 | * |
561 | * Some network devices may not implement support for this function. In such | |
562 | * cases this function will always return -EOPNOTSUPP. | |
9ab3d9a3 BP |
563 | */ |
564 | int | |
565 | netdev_get_ifindex(const struct netdev *netdev) | |
566 | { | |
4c0f1780 JG |
567 | int (*get_ifindex)(const struct netdev *); |
568 | ||
569 | get_ifindex = netdev_get_dev(netdev)->netdev_class->get_ifindex; | |
570 | ||
571 | return get_ifindex ? get_ifindex(netdev) : -EOPNOTSUPP; | |
9ab3d9a3 BP |
572 | } |
573 | ||
064af421 BP |
574 | /* Stores the features supported by 'netdev' into each of '*current', |
575 | * '*advertised', '*supported', and '*peer' that are non-null. Each value is a | |
576 | * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if | |
577 | * successful, otherwise a positive errno value. On failure, all of the | |
4c0f1780 JG |
578 | * passed-in values are set to 0. |
579 | * | |
580 | * Some network devices may not implement support for this function. In such | |
c1fdab01 | 581 | * cases this function will always return EOPNOTSUPP. */ |
064af421 | 582 | int |
6f2f5cce | 583 | netdev_get_features(const struct netdev *netdev, |
6c038611 BP |
584 | enum netdev_features *current, |
585 | enum netdev_features *advertised, | |
586 | enum netdev_features *supported, | |
587 | enum netdev_features *peer) | |
064af421 | 588 | { |
6f2f5cce | 589 | int (*get_features)(const struct netdev *netdev, |
6c038611 BP |
590 | enum netdev_features *current, |
591 | enum netdev_features *advertised, | |
592 | enum netdev_features *supported, | |
593 | enum netdev_features *peer); | |
594 | enum netdev_features dummy[4]; | |
7671589a BP |
595 | int error; |
596 | ||
597 | if (!current) { | |
598 | current = &dummy[0]; | |
599 | } | |
600 | if (!advertised) { | |
601 | advertised = &dummy[1]; | |
602 | } | |
603 | if (!supported) { | |
604 | supported = &dummy[2]; | |
605 | } | |
606 | if (!peer) { | |
607 | peer = &dummy[3]; | |
608 | } | |
609 | ||
4c0f1780 JG |
610 | get_features = netdev_get_dev(netdev)->netdev_class->get_features; |
611 | error = get_features | |
a00ca915 EJ |
612 | ? get_features(netdev, current, advertised, supported, |
613 | peer) | |
4c0f1780 | 614 | : EOPNOTSUPP; |
7671589a BP |
615 | if (error) { |
616 | *current = *advertised = *supported = *peer = 0; | |
617 | } | |
618 | return error; | |
064af421 BP |
619 | } |
620 | ||
6c038611 BP |
621 | /* Returns the maximum speed of a network connection that has the NETDEV_F_* |
622 | * bits in 'features', in bits per second. If no bits that indicate a speed | |
623 | * are set in 'features', assumes 100Mbps. */ | |
622ee2cf | 624 | uint64_t |
6c038611 | 625 | netdev_features_to_bps(enum netdev_features features) |
622ee2cf BP |
626 | { |
627 | enum { | |
6c038611 BP |
628 | F_1000000MB = NETDEV_F_1TB_FD, |
629 | F_100000MB = NETDEV_F_100GB_FD, | |
630 | F_40000MB = NETDEV_F_40GB_FD, | |
631 | F_10000MB = NETDEV_F_10GB_FD, | |
632 | F_1000MB = NETDEV_F_1GB_HD | NETDEV_F_1GB_FD, | |
633 | F_100MB = NETDEV_F_100MB_HD | NETDEV_F_100MB_FD, | |
634 | F_10MB = NETDEV_F_10MB_HD | NETDEV_F_10MB_FD | |
622ee2cf BP |
635 | }; |
636 | ||
6c038611 BP |
637 | return ( features & F_1000000MB ? UINT64_C(1000000000000) |
638 | : features & F_100000MB ? UINT64_C(100000000000) | |
639 | : features & F_40000MB ? UINT64_C(40000000000) | |
640 | : features & F_10000MB ? UINT64_C(10000000000) | |
641 | : features & F_1000MB ? UINT64_C(1000000000) | |
642 | : features & F_100MB ? UINT64_C(100000000) | |
643 | : features & F_10MB ? UINT64_C(10000000) | |
644 | : UINT64_C(100000000)); | |
622ee2cf BP |
645 | } |
646 | ||
6c038611 BP |
647 | /* Returns true if any of the NETDEV_F_* bits that indicate a full-duplex link |
648 | * are set in 'features', otherwise false. */ | |
622ee2cf | 649 | bool |
6c038611 | 650 | netdev_features_is_full_duplex(enum netdev_features features) |
622ee2cf | 651 | { |
6c038611 BP |
652 | return (features & (NETDEV_F_10MB_FD | NETDEV_F_100MB_FD | NETDEV_F_1GB_FD |
653 | | NETDEV_F_10GB_FD | NETDEV_F_40GB_FD | |
654 | | NETDEV_F_100GB_FD | NETDEV_F_1TB_FD)) != 0; | |
622ee2cf BP |
655 | } |
656 | ||
8b61709d BP |
657 | /* Set the features advertised by 'netdev' to 'advertise'. Returns 0 if |
658 | * successful, otherwise a positive errno value. */ | |
064af421 | 659 | int |
6c038611 BP |
660 | netdev_set_advertisements(struct netdev *netdev, |
661 | enum netdev_features advertise) | |
064af421 | 662 | { |
a4af0040 JP |
663 | return (netdev_get_dev(netdev)->netdev_class->set_advertisements |
664 | ? netdev_get_dev(netdev)->netdev_class->set_advertisements( | |
665 | netdev, advertise) | |
8b61709d | 666 | : EOPNOTSUPP); |
064af421 BP |
667 | } |
668 | ||
f1acd62b BP |
669 | /* If 'netdev' has an assigned IPv4 address, sets '*address' to that address |
670 | * and '*netmask' to its netmask and returns 0. Otherwise, returns a positive | |
671 | * errno value and sets '*address' to 0 (INADDR_ANY). | |
8b61709d BP |
672 | * |
673 | * The following error values have well-defined meanings: | |
674 | * | |
675 | * - EADDRNOTAVAIL: 'netdev' has no assigned IPv4 address. | |
676 | * | |
677 | * - EOPNOTSUPP: No IPv4 network stack attached to 'netdev'. | |
678 | * | |
b53055f4 | 679 | * 'address' or 'netmask' or both may be null, in which case the address or |
c1fdab01 | 680 | * netmask is not reported. */ |
6b9bd979 | 681 | int |
f1acd62b BP |
682 | netdev_get_in4(const struct netdev *netdev, |
683 | struct in_addr *address_, struct in_addr *netmask_) | |
064af421 | 684 | { |
f1acd62b BP |
685 | struct in_addr address; |
686 | struct in_addr netmask; | |
064af421 BP |
687 | int error; |
688 | ||
a4af0040 | 689 | error = (netdev_get_dev(netdev)->netdev_class->get_in4 |
d295e8e9 | 690 | ? netdev_get_dev(netdev)->netdev_class->get_in4(netdev, |
a4af0040 | 691 | &address, &netmask) |
8b61709d | 692 | : EOPNOTSUPP); |
f1acd62b BP |
693 | if (address_) { |
694 | address_->s_addr = error ? 0 : address.s_addr; | |
695 | } | |
696 | if (netmask_) { | |
697 | netmask_->s_addr = error ? 0 : netmask.s_addr; | |
064af421 BP |
698 | } |
699 | return error; | |
700 | } | |
701 | ||
702 | /* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If | |
703 | * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a | |
704 | * positive errno value. */ | |
705 | int | |
706 | netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask) | |
707 | { | |
a4af0040 JP |
708 | return (netdev_get_dev(netdev)->netdev_class->set_in4 |
709 | ? netdev_get_dev(netdev)->netdev_class->set_in4(netdev, addr, mask) | |
8b61709d | 710 | : EOPNOTSUPP); |
064af421 BP |
711 | } |
712 | ||
733adf2a LG |
713 | /* Obtains ad IPv4 address from device name and save the address in |
714 | * in4. Returns 0 if successful, otherwise a positive errno value. | |
715 | */ | |
716 | int | |
717 | netdev_get_in4_by_name(const char *device_name, struct in_addr *in4) | |
718 | { | |
719 | struct netdev *netdev; | |
720 | int error; | |
721 | ||
722 | error = netdev_open(device_name, "system", &netdev); | |
723 | if (error) { | |
724 | in4->s_addr = htonl(0); | |
725 | return error; | |
726 | } | |
727 | ||
728 | error = netdev_get_in4(netdev, in4, NULL); | |
729 | netdev_close(netdev); | |
730 | return error; | |
731 | } | |
732 | ||
0efaf4b5 BP |
733 | /* Adds 'router' as a default IP gateway for the TCP/IP stack that corresponds |
734 | * to 'netdev'. */ | |
064af421 | 735 | int |
8b61709d | 736 | netdev_add_router(struct netdev *netdev, struct in_addr router) |
064af421 | 737 | { |
064af421 | 738 | COVERAGE_INC(netdev_add_router); |
a4af0040 JP |
739 | return (netdev_get_dev(netdev)->netdev_class->add_router |
740 | ? netdev_get_dev(netdev)->netdev_class->add_router(netdev, router) | |
8b61709d | 741 | : EOPNOTSUPP); |
064af421 BP |
742 | } |
743 | ||
f1acd62b BP |
744 | /* Looks up the next hop for 'host' for the TCP/IP stack that corresponds to |
745 | * 'netdev'. If a route cannot not be determined, sets '*next_hop' to 0, | |
746 | * '*netdev_name' to null, and returns a positive errno value. Otherwise, if a | |
747 | * next hop is found, stores the next hop gateway's address (0 if 'host' is on | |
748 | * a directly connected network) in '*next_hop' and a copy of the name of the | |
749 | * device to reach 'host' in '*netdev_name', and returns 0. The caller is | |
750 | * responsible for freeing '*netdev_name' (by calling free()). */ | |
751 | int | |
752 | netdev_get_next_hop(const struct netdev *netdev, | |
753 | const struct in_addr *host, struct in_addr *next_hop, | |
754 | char **netdev_name) | |
755 | { | |
a4af0040 JP |
756 | int error = (netdev_get_dev(netdev)->netdev_class->get_next_hop |
757 | ? netdev_get_dev(netdev)->netdev_class->get_next_hop( | |
758 | host, next_hop, netdev_name) | |
f1acd62b | 759 | : EOPNOTSUPP); |
064af421 | 760 | if (error) { |
f1acd62b BP |
761 | next_hop->s_addr = 0; |
762 | *netdev_name = NULL; | |
064af421 BP |
763 | } |
764 | return error; | |
765 | } | |
766 | ||
79f1cbe9 | 767 | /* Populates 'smap' with status information. |
ea763e0e | 768 | * |
79f1cbe9 EJ |
769 | * Populates 'smap' with 'netdev' specific status information. This |
770 | * information may be used to populate the status column of the Interface table | |
771 | * as defined in ovs-vswitchd.conf.db(5). */ | |
ea763e0e | 772 | int |
79f1cbe9 | 773 | netdev_get_drv_info(const struct netdev *netdev, struct smap *smap) |
ea83a2fc EJ |
774 | { |
775 | struct netdev_dev *dev = netdev_get_dev(netdev); | |
776 | ||
2c2ea5a8 | 777 | return (dev->netdev_class->get_drv_info |
79f1cbe9 | 778 | ? dev->netdev_class->get_drv_info(netdev, smap) |
ea763e0e | 779 | : EOPNOTSUPP); |
ea83a2fc EJ |
780 | } |
781 | ||
8b61709d BP |
782 | /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address and |
783 | * returns 0. Otherwise, returns a positive errno value and sets '*in6' to | |
784 | * all-zero-bits (in6addr_any). | |
785 | * | |
786 | * The following error values have well-defined meanings: | |
787 | * | |
788 | * - EADDRNOTAVAIL: 'netdev' has no assigned IPv6 address. | |
789 | * | |
790 | * - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'. | |
791 | * | |
792 | * 'in6' may be null, in which case the address itself is not reported. */ | |
064af421 | 793 | int |
8b61709d | 794 | netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6) |
064af421 | 795 | { |
8b61709d BP |
796 | struct in6_addr dummy; |
797 | int error; | |
b1bf7d43 | 798 | |
a4af0040 | 799 | error = (netdev_get_dev(netdev)->netdev_class->get_in6 |
d295e8e9 | 800 | ? netdev_get_dev(netdev)->netdev_class->get_in6(netdev, |
a4af0040 | 801 | in6 ? in6 : &dummy) |
8b61709d BP |
802 | : EOPNOTSUPP); |
803 | if (error && in6) { | |
804 | memset(in6, 0, sizeof *in6); | |
b1bf7d43 | 805 | } |
8b61709d | 806 | return error; |
064af421 BP |
807 | } |
808 | ||
809 | /* On 'netdev', turns off the flags in 'off' and then turns on the flags in | |
810 | * 'on'. If 'permanent' is true, the changes will persist; otherwise, they | |
811 | * will be reverted when 'netdev' is closed or the program exits. Returns 0 if | |
812 | * successful, otherwise a positive errno value. */ | |
813 | static int | |
814 | do_update_flags(struct netdev *netdev, enum netdev_flags off, | |
8b61709d BP |
815 | enum netdev_flags on, enum netdev_flags *old_flagsp, |
816 | bool permanent) | |
064af421 | 817 | { |
8b61709d | 818 | enum netdev_flags old_flags; |
064af421 BP |
819 | int error; |
820 | ||
d295e8e9 | 821 | error = netdev_get_dev(netdev)->netdev_class->update_flags(netdev, |
a4af0040 | 822 | off & ~on, on, &old_flags); |
064af421 | 823 | if (error) { |
8b61709d BP |
824 | VLOG_WARN_RL(&rl, "failed to %s flags for network device %s: %s", |
825 | off || on ? "set" : "get", netdev_get_name(netdev), | |
826 | strerror(error)); | |
827 | old_flags = 0; | |
828 | } else if ((off || on) && !permanent) { | |
829 | enum netdev_flags new_flags = (old_flags & ~off) | on; | |
830 | enum netdev_flags changed_flags = old_flags ^ new_flags; | |
831 | if (changed_flags) { | |
832 | if (!netdev->changed_flags) { | |
833 | netdev->save_flags = old_flags; | |
834 | } | |
835 | netdev->changed_flags |= changed_flags; | |
836 | } | |
064af421 | 837 | } |
8b61709d BP |
838 | if (old_flagsp) { |
839 | *old_flagsp = old_flags; | |
064af421 BP |
840 | } |
841 | return error; | |
842 | } | |
843 | ||
8b61709d BP |
844 | /* Obtains the current flags for 'netdev' and stores them into '*flagsp'. |
845 | * Returns 0 if successful, otherwise a positive errno value. On failure, | |
846 | * stores 0 into '*flagsp'. */ | |
847 | int | |
848 | netdev_get_flags(const struct netdev *netdev_, enum netdev_flags *flagsp) | |
849 | { | |
850 | struct netdev *netdev = (struct netdev *) netdev_; | |
851 | return do_update_flags(netdev, 0, 0, flagsp, false); | |
852 | } | |
853 | ||
064af421 BP |
854 | /* Sets the flags for 'netdev' to 'flags'. |
855 | * If 'permanent' is true, the changes will persist; otherwise, they | |
856 | * will be reverted when 'netdev' is closed or the program exits. | |
857 | * Returns 0 if successful, otherwise a positive errno value. */ | |
858 | int | |
859 | netdev_set_flags(struct netdev *netdev, enum netdev_flags flags, | |
860 | bool permanent) | |
861 | { | |
8b61709d | 862 | return do_update_flags(netdev, -1, flags, NULL, permanent); |
064af421 BP |
863 | } |
864 | ||
865 | /* Turns on the specified 'flags' on 'netdev'. | |
866 | * If 'permanent' is true, the changes will persist; otherwise, they | |
867 | * will be reverted when 'netdev' is closed or the program exits. | |
868 | * Returns 0 if successful, otherwise a positive errno value. */ | |
869 | int | |
870 | netdev_turn_flags_on(struct netdev *netdev, enum netdev_flags flags, | |
871 | bool permanent) | |
872 | { | |
8b61709d | 873 | return do_update_flags(netdev, 0, flags, NULL, permanent); |
064af421 BP |
874 | } |
875 | ||
876 | /* Turns off the specified 'flags' on 'netdev'. | |
877 | * If 'permanent' is true, the changes will persist; otherwise, they | |
878 | * will be reverted when 'netdev' is closed or the program exits. | |
879 | * Returns 0 if successful, otherwise a positive errno value. */ | |
880 | int | |
881 | netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags, | |
882 | bool permanent) | |
883 | { | |
8b61709d | 884 | return do_update_flags(netdev, flags, 0, NULL, permanent); |
064af421 BP |
885 | } |
886 | ||
887 | /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be | |
888 | * successfully retrieved, it stores the corresponding MAC address in 'mac' and | |
889 | * returns 0. Otherwise, it returns a positive errno value; in particular, | |
8b61709d | 890 | * ENXIO indicates that there is no ARP table entry for 'ip' on 'netdev'. */ |
064af421 | 891 | int |
8b61709d | 892 | netdev_arp_lookup(const struct netdev *netdev, |
dbba996b | 893 | ovs_be32 ip, uint8_t mac[ETH_ADDR_LEN]) |
064af421 | 894 | { |
a4af0040 | 895 | int error = (netdev_get_dev(netdev)->netdev_class->arp_lookup |
d295e8e9 | 896 | ? netdev_get_dev(netdev)->netdev_class->arp_lookup(netdev, |
a4af0040 | 897 | ip, mac) |
8b61709d | 898 | : EOPNOTSUPP); |
064af421 | 899 | if (error) { |
8b61709d | 900 | memset(mac, 0, ETH_ADDR_LEN); |
064af421 | 901 | } |
8b61709d | 902 | return error; |
064af421 BP |
903 | } |
904 | ||
85da620e JG |
905 | /* Returns true if carrier is active (link light is on) on 'netdev'. */ |
906 | bool | |
907 | netdev_get_carrier(const struct netdev *netdev) | |
064af421 | 908 | { |
85da620e JG |
909 | int error; |
910 | enum netdev_flags flags; | |
911 | bool carrier; | |
912 | ||
913 | netdev_get_flags(netdev, &flags); | |
914 | if (!(flags & NETDEV_UP)) { | |
915 | return false; | |
916 | } | |
917 | ||
918 | if (!netdev_get_dev(netdev)->netdev_class->get_carrier) { | |
919 | return true; | |
920 | } | |
921 | ||
922 | error = netdev_get_dev(netdev)->netdev_class->get_carrier(netdev, | |
923 | &carrier); | |
8b61709d | 924 | if (error) { |
85da620e JG |
925 | VLOG_DBG("%s: failed to get network device carrier status, assuming " |
926 | "down: %s", netdev_get_name(netdev), strerror(error)); | |
927 | carrier = false; | |
064af421 | 928 | } |
85da620e JG |
929 | |
930 | return carrier; | |
064af421 BP |
931 | } |
932 | ||
65c3058c EJ |
933 | /* Returns the number of times 'netdev''s carrier has changed. */ |
934 | long long int | |
935 | netdev_get_carrier_resets(const struct netdev *netdev) | |
936 | { | |
937 | return (netdev_get_dev(netdev)->netdev_class->get_carrier_resets | |
938 | ? netdev_get_dev(netdev)->netdev_class->get_carrier_resets(netdev) | |
939 | : 0); | |
940 | } | |
941 | ||
1670c579 EJ |
942 | /* Attempts to force netdev_get_carrier() to poll 'netdev''s MII registers for |
943 | * link status instead of checking 'netdev''s carrier. 'netdev''s MII | |
944 | * registers will be polled once ever 'interval' milliseconds. If 'netdev' | |
945 | * does not support MII, another method may be used as a fallback. If | |
946 | * 'interval' is less than or equal to zero, reverts netdev_get_carrier() to | |
947 | * its normal behavior. | |
948 | * | |
949 | * Returns 0 if successful, otherwise a positive errno value. */ | |
950 | int | |
951 | netdev_set_miimon_interval(struct netdev *netdev, long long int interval) | |
63331829 | 952 | { |
1670c579 EJ |
953 | struct netdev_dev *netdev_dev = netdev_get_dev(netdev); |
954 | return (netdev_dev->netdev_class->set_miimon_interval | |
955 | ? netdev_dev->netdev_class->set_miimon_interval(netdev, interval) | |
956 | : EOPNOTSUPP); | |
63331829 EJ |
957 | } |
958 | ||
887fd0ba | 959 | /* Retrieves current device stats for 'netdev'. */ |
064af421 BP |
960 | int |
961 | netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats) | |
962 | { | |
963 | int error; | |
964 | ||
965 | COVERAGE_INC(netdev_get_stats); | |
a4af0040 JP |
966 | error = (netdev_get_dev(netdev)->netdev_class->get_stats |
967 | ? netdev_get_dev(netdev)->netdev_class->get_stats(netdev, stats) | |
8b61709d | 968 | : EOPNOTSUPP); |
064af421 BP |
969 | if (error) { |
970 | memset(stats, 0xff, sizeof *stats); | |
971 | } | |
972 | return error; | |
973 | } | |
974 | ||
8722022c BP |
975 | /* Attempts to change the stats for 'netdev' to those provided in 'stats'. |
976 | * Returns 0 if successful, otherwise a positive errno value. | |
977 | * | |
978 | * This will probably fail for most network devices. Some devices might only | |
979 | * allow setting their stats to 0. */ | |
980 | int | |
981 | netdev_set_stats(struct netdev *netdev, const struct netdev_stats *stats) | |
982 | { | |
983 | return (netdev_get_dev(netdev)->netdev_class->set_stats | |
984 | ? netdev_get_dev(netdev)->netdev_class->set_stats(netdev, stats) | |
985 | : EOPNOTSUPP); | |
986 | } | |
987 | ||
8b61709d BP |
988 | /* Attempts to set input rate limiting (policing) policy, such that up to |
989 | * 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative burst | |
990 | * size of 'kbits' kb. */ | |
064af421 | 991 | int |
b1bf7d43 BP |
992 | netdev_set_policing(struct netdev *netdev, uint32_t kbits_rate, |
993 | uint32_t kbits_burst) | |
064af421 | 994 | { |
a4af0040 | 995 | return (netdev_get_dev(netdev)->netdev_class->set_policing |
d295e8e9 | 996 | ? netdev_get_dev(netdev)->netdev_class->set_policing(netdev, |
a4af0040 | 997 | kbits_rate, kbits_burst) |
8b61709d | 998 | : EOPNOTSUPP); |
064af421 BP |
999 | } |
1000 | ||
c1c9c9c4 BP |
1001 | /* Adds to 'types' all of the forms of QoS supported by 'netdev', or leaves it |
1002 | * empty if 'netdev' does not support QoS. Any names added to 'types' should | |
1003 | * be documented as valid for the "type" column in the "QoS" table in | |
1004 | * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). | |
1005 | * | |
1006 | * Every network device supports disabling QoS with a type of "", but this type | |
1007 | * will not be added to 'types'. | |
1008 | * | |
19993ef3 | 1009 | * The caller must initialize 'types' (e.g. with sset_init()) before calling |
c1c9c9c4 | 1010 | * this function. The caller is responsible for destroying 'types' (e.g. with |
19993ef3 | 1011 | * sset_destroy()) when it is no longer needed. |
c1c9c9c4 BP |
1012 | * |
1013 | * Returns 0 if successful, otherwise a positive errno value. */ | |
1014 | int | |
19993ef3 | 1015 | netdev_get_qos_types(const struct netdev *netdev, struct sset *types) |
c1c9c9c4 BP |
1016 | { |
1017 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1018 | return (class->get_qos_types | |
1019 | ? class->get_qos_types(netdev, types) | |
1020 | : 0); | |
1021 | } | |
1022 | ||
1023 | /* Queries 'netdev' for its capabilities regarding the specified 'type' of QoS, | |
1024 | * which should be "" or one of the types returned by netdev_get_qos_types() | |
1025 | * for 'netdev'. Returns 0 if successful, otherwise a positive errno value. | |
1026 | * On success, initializes 'caps' with the QoS capabilities; on failure, clears | |
1027 | * 'caps' to all zeros. */ | |
1028 | int | |
1029 | netdev_get_qos_capabilities(const struct netdev *netdev, const char *type, | |
1030 | struct netdev_qos_capabilities *caps) | |
1031 | { | |
1032 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1033 | ||
1034 | if (*type) { | |
1035 | int retval = (class->get_qos_capabilities | |
1036 | ? class->get_qos_capabilities(netdev, type, caps) | |
1037 | : EOPNOTSUPP); | |
1038 | if (retval) { | |
1039 | memset(caps, 0, sizeof *caps); | |
1040 | } | |
1041 | return retval; | |
1042 | } else { | |
1043 | /* Every netdev supports turning off QoS. */ | |
1044 | memset(caps, 0, sizeof *caps); | |
1045 | return 0; | |
1046 | } | |
1047 | } | |
1048 | ||
1049 | /* Obtains the number of queues supported by 'netdev' for the specified 'type' | |
1050 | * of QoS. Returns 0 if successful, otherwise a positive errno value. Stores | |
1051 | * the number of queues (zero on failure) in '*n_queuesp'. | |
1052 | * | |
1053 | * This is just a simple wrapper around netdev_get_qos_capabilities(). */ | |
1054 | int | |
1055 | netdev_get_n_queues(const struct netdev *netdev, | |
1056 | const char *type, unsigned int *n_queuesp) | |
1057 | { | |
1058 | struct netdev_qos_capabilities caps; | |
1059 | int retval; | |
1060 | ||
1061 | retval = netdev_get_qos_capabilities(netdev, type, &caps); | |
1062 | *n_queuesp = caps.n_queues; | |
1063 | return retval; | |
1064 | } | |
1065 | ||
1066 | /* Queries 'netdev' about its currently configured form of QoS. If successful, | |
1067 | * stores the name of the current form of QoS into '*typep', stores any details | |
1068 | * of configuration as string key-value pairs in 'details', and returns 0. On | |
1069 | * failure, sets '*typep' to NULL and returns a positive errno value. | |
1070 | * | |
1071 | * A '*typep' of "" indicates that QoS is currently disabled on 'netdev'. | |
1072 | * | |
79f1cbe9 EJ |
1073 | * The caller must initialize 'details' as an empty smap (e.g. with |
1074 | * smap_init()) before calling this function. The caller must free 'details' | |
1075 | * when it is no longer needed (e.g. with smap_destroy()). | |
c1c9c9c4 BP |
1076 | * |
1077 | * The caller must not modify or free '*typep'. | |
1078 | * | |
1079 | * '*typep' will be one of the types returned by netdev_get_qos_types() for | |
1080 | * 'netdev'. The contents of 'details' should be documented as valid for | |
1081 | * '*typep' in the "other_config" column in the "QoS" table in | |
1082 | * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). */ | |
1083 | int | |
1084 | netdev_get_qos(const struct netdev *netdev, | |
79f1cbe9 | 1085 | const char **typep, struct smap *details) |
c1c9c9c4 BP |
1086 | { |
1087 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1088 | int retval; | |
1089 | ||
1090 | if (class->get_qos) { | |
1091 | retval = class->get_qos(netdev, typep, details); | |
1092 | if (retval) { | |
1093 | *typep = NULL; | |
79f1cbe9 | 1094 | smap_clear(details); |
c1c9c9c4 BP |
1095 | } |
1096 | return retval; | |
1097 | } else { | |
1098 | /* 'netdev' doesn't support QoS, so report that QoS is disabled. */ | |
1099 | *typep = ""; | |
1100 | return 0; | |
1101 | } | |
1102 | } | |
1103 | ||
1104 | /* Attempts to reconfigure QoS on 'netdev', changing the form of QoS to 'type' | |
1105 | * with details of configuration from 'details'. Returns 0 if successful, | |
1106 | * otherwise a positive errno value. On error, the previous QoS configuration | |
1107 | * is retained. | |
1108 | * | |
1109 | * When this function changes the type of QoS (not just 'details'), this also | |
1110 | * resets all queue configuration for 'netdev' to their defaults (which depend | |
1111 | * on the specific type of QoS). Otherwise, the queue configuration for | |
1112 | * 'netdev' is unchanged. | |
1113 | * | |
1114 | * 'type' should be "" (to disable QoS) or one of the types returned by | |
1115 | * netdev_get_qos_types() for 'netdev'. The contents of 'details' should be | |
1116 | * documented as valid for the given 'type' in the "other_config" column in the | |
1117 | * "QoS" table in vswitchd/vswitch.xml (which is built as | |
1118 | * ovs-vswitchd.conf.db(8)). | |
1119 | * | |
1120 | * NULL may be specified for 'details' if there are no configuration | |
1121 | * details. */ | |
1122 | int | |
1123 | netdev_set_qos(struct netdev *netdev, | |
79f1cbe9 | 1124 | const char *type, const struct smap *details) |
c1c9c9c4 BP |
1125 | { |
1126 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1127 | ||
1128 | if (!type) { | |
1129 | type = ""; | |
1130 | } | |
1131 | ||
1132 | if (class->set_qos) { | |
1133 | if (!details) { | |
79f1cbe9 | 1134 | static struct smap empty = SMAP_INITIALIZER(&empty); |
c1c9c9c4 BP |
1135 | details = ∅ |
1136 | } | |
1137 | return class->set_qos(netdev, type, details); | |
1138 | } else { | |
1139 | return *type ? EOPNOTSUPP : 0; | |
1140 | } | |
1141 | } | |
1142 | ||
1143 | /* Queries 'netdev' for information about the queue numbered 'queue_id'. If | |
1144 | * successful, adds that information as string key-value pairs to 'details'. | |
1145 | * Returns 0 if successful, otherwise a positive errno value. | |
1146 | * | |
1147 | * 'queue_id' must be less than the number of queues supported by 'netdev' for | |
1148 | * the current form of QoS (e.g. as returned by netdev_get_n_queues(netdev)). | |
1149 | * | |
1150 | * The returned contents of 'details' should be documented as valid for the | |
1151 | * given 'type' in the "other_config" column in the "Queue" table in | |
1152 | * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). | |
1153 | * | |
79f1cbe9 EJ |
1154 | * The caller must initialize 'details' (e.g. with smap_init()) before calling |
1155 | * this function. The caller must free 'details' when it is no longer needed | |
1156 | * (e.g. with smap_destroy()). */ | |
c1c9c9c4 BP |
1157 | int |
1158 | netdev_get_queue(const struct netdev *netdev, | |
79f1cbe9 | 1159 | unsigned int queue_id, struct smap *details) |
c1c9c9c4 BP |
1160 | { |
1161 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1162 | int retval; | |
1163 | ||
1164 | retval = (class->get_queue | |
1165 | ? class->get_queue(netdev, queue_id, details) | |
1166 | : EOPNOTSUPP); | |
1167 | if (retval) { | |
79f1cbe9 | 1168 | smap_clear(details); |
c1c9c9c4 BP |
1169 | } |
1170 | return retval; | |
1171 | } | |
1172 | ||
1173 | /* Configures the queue numbered 'queue_id' on 'netdev' with the key-value | |
1174 | * string pairs in 'details'. The contents of 'details' should be documented | |
1175 | * as valid for the given 'type' in the "other_config" column in the "Queue" | |
1176 | * table in vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). | |
1177 | * Returns 0 if successful, otherwise a positive errno value. On failure, the | |
1178 | * given queue's configuration should be unmodified. | |
1179 | * | |
1180 | * 'queue_id' must be less than the number of queues supported by 'netdev' for | |
1181 | * the current form of QoS (e.g. as returned by netdev_get_n_queues(netdev)). | |
1182 | * | |
1183 | * This function does not modify 'details', and the caller retains ownership of | |
c1fdab01 | 1184 | * it. */ |
c1c9c9c4 BP |
1185 | int |
1186 | netdev_set_queue(struct netdev *netdev, | |
79f1cbe9 | 1187 | unsigned int queue_id, const struct smap *details) |
c1c9c9c4 BP |
1188 | { |
1189 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1190 | return (class->set_queue | |
1191 | ? class->set_queue(netdev, queue_id, details) | |
1192 | : EOPNOTSUPP); | |
1193 | } | |
1194 | ||
1195 | /* Attempts to delete the queue numbered 'queue_id' from 'netdev'. Some kinds | |
1196 | * of QoS may have a fixed set of queues, in which case attempts to delete them | |
1197 | * will fail with EOPNOTSUPP. | |
1198 | * | |
1199 | * Returns 0 if successful, otherwise a positive errno value. On failure, the | |
1200 | * given queue will be unmodified. | |
1201 | * | |
1202 | * 'queue_id' must be less than the number of queues supported by 'netdev' for | |
1203 | * the current form of QoS (e.g. as returned by | |
1204 | * netdev_get_n_queues(netdev)). */ | |
1205 | int | |
1206 | netdev_delete_queue(struct netdev *netdev, unsigned int queue_id) | |
1207 | { | |
1208 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1209 | return (class->delete_queue | |
1210 | ? class->delete_queue(netdev, queue_id) | |
1211 | : EOPNOTSUPP); | |
1212 | } | |
1213 | ||
1214 | /* Obtains statistics about 'queue_id' on 'netdev'. On success, returns 0 and | |
1215 | * fills 'stats' with the queue's statistics; individual members of 'stats' may | |
1216 | * be set to all-1-bits if the statistic is unavailable. On failure, returns a | |
1217 | * positive errno value and fills 'stats' with all-1-bits. */ | |
1218 | int | |
1219 | netdev_get_queue_stats(const struct netdev *netdev, unsigned int queue_id, | |
1220 | struct netdev_queue_stats *stats) | |
1221 | { | |
1222 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1223 | int retval; | |
1224 | ||
1225 | retval = (class->get_queue_stats | |
1226 | ? class->get_queue_stats(netdev, queue_id, stats) | |
1227 | : EOPNOTSUPP); | |
1228 | if (retval) { | |
1229 | memset(stats, 0xff, sizeof *stats); | |
1230 | } | |
1231 | return retval; | |
1232 | } | |
1233 | ||
1234 | /* Iterates over all of 'netdev''s queues, calling 'cb' with the queue's ID, | |
1235 | * its configuration, and the 'aux' specified by the caller. The order of | |
1236 | * iteration is unspecified, but (when successful) each queue is visited | |
1237 | * exactly once. | |
1238 | * | |
1239 | * Calling this function may be more efficient than calling netdev_get_queue() | |
1240 | * for every queue. | |
1241 | * | |
f486e840 BP |
1242 | * 'cb' must not modify or free the 'details' argument passed in. It may |
1243 | * delete or modify the queue passed in as its 'queue_id' argument. It may | |
1244 | * modify but must not delete any other queue within 'netdev'. 'cb' should not | |
1245 | * add new queues because this may cause some queues to be visited twice or not | |
1246 | * at all. | |
c1c9c9c4 BP |
1247 | * |
1248 | * Returns 0 if successful, otherwise a positive errno value. On error, some | |
1249 | * configured queues may not have been included in the iteration. */ | |
1250 | int | |
1251 | netdev_dump_queues(const struct netdev *netdev, | |
1252 | netdev_dump_queues_cb *cb, void *aux) | |
1253 | { | |
1254 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1255 | return (class->dump_queues | |
1256 | ? class->dump_queues(netdev, cb, aux) | |
1257 | : EOPNOTSUPP); | |
1258 | } | |
1259 | ||
1260 | /* Iterates over all of 'netdev''s queues, calling 'cb' with the queue's ID, | |
1261 | * its statistics, and the 'aux' specified by the caller. The order of | |
1262 | * iteration is unspecified, but (when successful) each queue is visited | |
1263 | * exactly once. | |
1264 | * | |
1265 | * Calling this function may be more efficient than calling | |
1266 | * netdev_get_queue_stats() for every queue. | |
1267 | * | |
1268 | * 'cb' must not modify or free the statistics passed in. | |
1269 | * | |
1270 | * Returns 0 if successful, otherwise a positive errno value. On error, some | |
1271 | * configured queues may not have been included in the iteration. */ | |
1272 | int | |
1273 | netdev_dump_queue_stats(const struct netdev *netdev, | |
1274 | netdev_dump_queue_stats_cb *cb, void *aux) | |
1275 | { | |
1276 | const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class; | |
1277 | return (class->dump_queue_stats | |
1278 | ? class->dump_queue_stats(netdev, cb, aux) | |
1279 | : EOPNOTSUPP); | |
1280 | } | |
1281 | ||
ac4d3bcb EJ |
1282 | /* Returns a sequence number which indicates changes in one of 'netdev''s |
1283 | * properties. The returned sequence will be nonzero so that callers have a | |
1284 | * value which they may use as a reset when tracking 'netdev'. | |
1285 | * | |
1286 | * The returned sequence number will change whenever 'netdev''s flags, | |
1287 | * features, ethernet address, or carrier changes. It may change for other | |
1288 | * reasons as well, or no reason at all. */ | |
1289 | unsigned int | |
1290 | netdev_change_seq(const struct netdev *netdev) | |
1291 | { | |
1292 | return netdev_get_dev(netdev)->netdev_class->change_seq(netdev); | |
1293 | } | |
8b61709d | 1294 | \f |
6d9e6eb4 BP |
1295 | /* Initializes 'netdev_dev' as a netdev device named 'name' of the specified |
1296 | * 'netdev_class'. This function is ordinarily called from a netdev provider's | |
1297 | * 'create' function. | |
1298 | * | |
149f577a JG |
1299 | * This function adds 'netdev_dev' to a netdev-owned shash, so it is |
1300 | * very important that 'netdev_dev' only be freed after calling | |
1301 | * the refcount drops to zero. */ | |
1302 | void | |
1303 | netdev_dev_init(struct netdev_dev *netdev_dev, const char *name, | |
7dab847a | 1304 | const struct netdev_class *netdev_class) |
149f577a JG |
1305 | { |
1306 | assert(!shash_find(&netdev_dev_shash, name)); | |
1307 | ||
0f4f4a61 | 1308 | memset(netdev_dev, 0, sizeof *netdev_dev); |
7dab847a | 1309 | netdev_dev->netdev_class = netdev_class; |
149f577a JG |
1310 | netdev_dev->name = xstrdup(name); |
1311 | netdev_dev->node = shash_add(&netdev_dev_shash, name, netdev_dev); | |
1312 | } | |
1313 | ||
1314 | /* Undoes the results of initialization. | |
1315 | * | |
1316 | * Normally this function does not need to be called as netdev_close has | |
1317 | * the same effect when the refcount drops to zero. | |
1318 | * However, it may be called by providers due to an error on creation | |
1319 | * that occurs after initialization. It this case netdev_close() would | |
1320 | * never be called. */ | |
6c88d577 | 1321 | void |
149f577a | 1322 | netdev_dev_uninit(struct netdev_dev *netdev_dev, bool destroy) |
6c88d577 | 1323 | { |
149f577a JG |
1324 | char *name = netdev_dev->name; |
1325 | ||
1326 | assert(!netdev_dev->ref_cnt); | |
1327 | ||
1328 | shash_delete(&netdev_dev_shash, netdev_dev->node); | |
6c88d577 | 1329 | |
149f577a | 1330 | if (destroy) { |
a4af0040 | 1331 | netdev_dev->netdev_class->destroy(netdev_dev); |
149f577a JG |
1332 | } |
1333 | free(name); | |
6c88d577 JP |
1334 | } |
1335 | ||
149f577a | 1336 | /* Returns the class type of 'netdev_dev'. |
a740f0de JG |
1337 | * |
1338 | * The caller must not free the returned value. */ | |
149f577a JG |
1339 | const char * |
1340 | netdev_dev_get_type(const struct netdev_dev *netdev_dev) | |
a740f0de | 1341 | { |
a4af0040 | 1342 | return netdev_dev->netdev_class->type; |
a740f0de JG |
1343 | } |
1344 | ||
15b3596a JG |
1345 | /* Returns the class associated with 'netdev_dev'. */ |
1346 | const struct netdev_class * | |
1347 | netdev_dev_get_class(const struct netdev_dev *netdev_dev) | |
1348 | { | |
1349 | return netdev_dev->netdev_class; | |
1350 | } | |
1351 | ||
149f577a | 1352 | /* Returns the name of 'netdev_dev'. |
a740f0de JG |
1353 | * |
1354 | * The caller must not free the returned value. */ | |
149f577a JG |
1355 | const char * |
1356 | netdev_dev_get_name(const struct netdev_dev *netdev_dev) | |
a740f0de | 1357 | { |
149f577a | 1358 | return netdev_dev->name; |
a740f0de JG |
1359 | } |
1360 | ||
46415c90 JG |
1361 | /* Returns the netdev_dev with 'name' or NULL if there is none. |
1362 | * | |
1363 | * The caller must not free the returned value. */ | |
1364 | struct netdev_dev * | |
1365 | netdev_dev_from_name(const char *name) | |
1366 | { | |
1367 | return shash_find_data(&netdev_dev_shash, name); | |
1368 | } | |
1369 | ||
7dab847a | 1370 | /* Fills 'device_list' with devices that match 'netdev_class'. |
46415c90 JG |
1371 | * |
1372 | * The caller is responsible for initializing and destroying 'device_list' | |
1373 | * but the contained netdev_devs must not be freed. */ | |
1374 | void | |
7dab847a | 1375 | netdev_dev_get_devices(const struct netdev_class *netdev_class, |
46415c90 JG |
1376 | struct shash *device_list) |
1377 | { | |
1378 | struct shash_node *node; | |
1379 | SHASH_FOR_EACH (node, &netdev_dev_shash) { | |
1380 | struct netdev_dev *dev = node->data; | |
1381 | ||
7dab847a | 1382 | if (dev->netdev_class == netdev_class) { |
46415c90 JG |
1383 | shash_add(device_list, node->name, node->data); |
1384 | } | |
1385 | } | |
1386 | } | |
1387 | ||
149f577a | 1388 | /* Initializes 'netdev' as a instance of the netdev_dev. |
8b61709d BP |
1389 | * |
1390 | * This function adds 'netdev' to a netdev-owned linked list, so it is very | |
1391 | * important that 'netdev' only be freed after calling netdev_close(). */ | |
1392 | void | |
149f577a | 1393 | netdev_init(struct netdev *netdev, struct netdev_dev *netdev_dev) |
064af421 | 1394 | { |
0f4f4a61 | 1395 | memset(netdev, 0, sizeof *netdev); |
149f577a | 1396 | netdev->netdev_dev = netdev_dev; |
8b61709d BP |
1397 | list_push_back(&netdev_list, &netdev->node); |
1398 | } | |
064af421 | 1399 | |
149f577a JG |
1400 | /* Undoes the results of initialization. |
1401 | * | |
1402 | * Normally this function only needs to be called from netdev_close(). | |
1403 | * However, it may be called by providers due to an error on opening | |
1404 | * that occurs after initialization. It this case netdev_close() would | |
1405 | * never be called. */ | |
1406 | void | |
1407 | netdev_uninit(struct netdev *netdev, bool close) | |
1408 | { | |
1409 | /* Restore flags that we changed, if any. */ | |
1410 | int error = restore_flags(netdev); | |
1411 | list_remove(&netdev->node); | |
1412 | if (error) { | |
1413 | VLOG_WARN("failed to restore network device flags on %s: %s", | |
1414 | netdev_get_name(netdev), strerror(error)); | |
1415 | } | |
1416 | ||
1417 | if (close) { | |
a4af0040 | 1418 | netdev_get_dev(netdev)->netdev_class->close(netdev); |
149f577a JG |
1419 | } |
1420 | } | |
1421 | ||
1422 | ||
d295e8e9 | 1423 | /* Returns the class type of 'netdev'. |
6c88d577 JP |
1424 | * |
1425 | * The caller must not free the returned value. */ | |
149f577a JG |
1426 | const char * |
1427 | netdev_get_type(const struct netdev *netdev) | |
1428 | { | |
a4af0040 | 1429 | return netdev_get_dev(netdev)->netdev_class->type; |
149f577a JG |
1430 | } |
1431 | ||
1432 | struct netdev_dev * | |
1433 | netdev_get_dev(const struct netdev *netdev) | |
6c88d577 | 1434 | { |
149f577a | 1435 | return netdev->netdev_dev; |
6c88d577 | 1436 | } |
e9e28be3 | 1437 | \f |
064af421 BP |
1438 | /* Restore the network device flags on 'netdev' to those that were active |
1439 | * before we changed them. Returns 0 if successful, otherwise a positive | |
1440 | * errno value. | |
1441 | * | |
1442 | * To avoid reentry, the caller must ensure that fatal signals are blocked. */ | |
1443 | static int | |
1444 | restore_flags(struct netdev *netdev) | |
1445 | { | |
8b61709d BP |
1446 | if (netdev->changed_flags) { |
1447 | enum netdev_flags restore = netdev->save_flags & netdev->changed_flags; | |
1448 | enum netdev_flags old_flags; | |
a4af0040 | 1449 | return netdev_get_dev(netdev)->netdev_class->update_flags(netdev, |
8b61709d BP |
1450 | netdev->changed_flags & ~restore, |
1451 | restore, &old_flags); | |
064af421 | 1452 | } |
064af421 BP |
1453 | return 0; |
1454 | } | |
1455 | ||
0b0544d7 JG |
1456 | /* Close all netdevs on shutdown so they can do any needed cleanup such as |
1457 | * destroying devices, restoring flags, etc. */ | |
064af421 | 1458 | static void |
c69ee87c | 1459 | close_all_netdevs(void *aux OVS_UNUSED) |
064af421 | 1460 | { |
0b0544d7 | 1461 | struct netdev *netdev, *next; |
4e8e4213 | 1462 | LIST_FOR_EACH_SAFE(netdev, next, node, &netdev_list) { |
0b0544d7 | 1463 | netdev_close(netdev); |
064af421 BP |
1464 | } |
1465 | } |