]>
Commit | Line | Data |
---|---|---|
064af421 BP |
1 | /* |
2 | * Copyright (c) 2008, 2009 Nicira Networks. | |
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" | |
31 | #include "list.h" | |
8b61709d | 32 | #include "netdev-provider.h" |
064af421 | 33 | #include "ofpbuf.h" |
064af421 BP |
34 | #include "packets.h" |
35 | #include "poll-loop.h" | |
e9e28be3 | 36 | #include "shash.h" |
064af421 BP |
37 | #include "svec.h" |
38 | ||
064af421 BP |
39 | #define THIS_MODULE VLM_netdev |
40 | #include "vlog.h" | |
41 | ||
8b61709d BP |
42 | static const struct netdev_class *netdev_classes[] = { |
43 | &netdev_linux_class, | |
44 | &netdev_tap_class, | |
064af421 | 45 | }; |
559843ed | 46 | static int n_netdev_classes = ARRAY_SIZE(netdev_classes); |
064af421 BP |
47 | |
48 | /* All open network devices. */ | |
49 | static struct list netdev_list = LIST_INITIALIZER(&netdev_list); | |
50 | ||
064af421 BP |
51 | /* This is set pretty low because we probably won't learn anything from the |
52 | * additional log messages. */ | |
53 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); | |
54 | ||
8b61709d | 55 | static void restore_all_flags(void *aux); |
064af421 | 56 | static int restore_flags(struct netdev *netdev); |
8b61709d BP |
57 | |
58 | /* Attempts to initialize the netdev module. Returns 0 if successful, | |
59 | * otherwise a positive errno value. | |
60 | * | |
61 | * Calling this function is optional. If not called explicitly, it will | |
62 | * automatically be called upon the first attempt to open a network device. */ | |
63 | int | |
64 | netdev_initialize(void) | |
064af421 | 65 | { |
8b61709d BP |
66 | static int status = -1; |
67 | if (status < 0) { | |
559843ed | 68 | int i, j; |
8b61709d BP |
69 | |
70 | fatal_signal_add_hook(restore_all_flags, NULL, true); | |
064af421 | 71 | |
8b61709d | 72 | status = 0; |
559843ed | 73 | for (i = j = 0; i < n_netdev_classes; i++) { |
8b61709d BP |
74 | const struct netdev_class *class = netdev_classes[i]; |
75 | if (class->init) { | |
76 | int retval = class->init(); | |
559843ed BP |
77 | if (!retval) { |
78 | netdev_classes[j++] = class; | |
79 | } else { | |
8b61709d BP |
80 | VLOG_ERR("failed to initialize %s network device " |
81 | "class: %s", class->name, strerror(retval)); | |
82 | if (!status) { | |
83 | status = retval; | |
84 | } | |
85 | } | |
0a6d2aa6 JG |
86 | } else { |
87 | netdev_classes[j++] = class; | |
8b61709d | 88 | } |
064af421 | 89 | } |
559843ed | 90 | n_netdev_classes = j; |
064af421 | 91 | } |
8b61709d | 92 | return status; |
064af421 BP |
93 | } |
94 | ||
8b61709d BP |
95 | /* Performs periodic work needed by all the various kinds of netdevs. |
96 | * | |
97 | * If your program opens any netdevs, it must call this function within its | |
98 | * main poll loop. */ | |
99 | void | |
100 | netdev_run(void) | |
064af421 | 101 | { |
8b61709d | 102 | int i; |
559843ed | 103 | for (i = 0; i < n_netdev_classes; i++) { |
8b61709d BP |
104 | const struct netdev_class *class = netdev_classes[i]; |
105 | if (class->run) { | |
106 | class->run(); | |
064af421 | 107 | } |
064af421 BP |
108 | } |
109 | } | |
110 | ||
8b61709d BP |
111 | /* Arranges for poll_block() to wake up when netdev_run() needs to be called. |
112 | * | |
113 | * If your program opens any netdevs, it must call this function within its | |
114 | * main poll loop. */ | |
115 | void | |
116 | netdev_wait(void) | |
064af421 | 117 | { |
8b61709d | 118 | int i; |
559843ed | 119 | for (i = 0; i < n_netdev_classes; i++) { |
8b61709d BP |
120 | const struct netdev_class *class = netdev_classes[i]; |
121 | if (class->wait) { | |
122 | class->wait(); | |
123 | } | |
064af421 | 124 | } |
064af421 BP |
125 | } |
126 | ||
127 | /* Opens the network device named 'name' (e.g. "eth0") and returns zero if | |
128 | * successful, otherwise a positive errno value. On success, sets '*netdevp' | |
129 | * to the new network device, otherwise to null. | |
130 | * | |
131 | * 'ethertype' may be a 16-bit Ethernet protocol value in host byte order to | |
132 | * capture frames of that type received on the device. It may also be one of | |
133 | * the 'enum netdev_pseudo_ethertype' values to receive frames in one of those | |
134 | * categories. */ | |
135 | int | |
8b61709d | 136 | netdev_open(const char *name_, int ethertype, struct netdev **netdevp) |
064af421 | 137 | { |
8b61709d BP |
138 | char *name = xstrdup(name_); |
139 | char *prefix, *suffix, *colon; | |
140 | struct netdev *netdev = NULL; | |
064af421 | 141 | int error; |
8b61709d | 142 | int i; |
064af421 | 143 | |
559843ed | 144 | netdev_initialize(); |
8b61709d BP |
145 | colon = strchr(name, ':'); |
146 | if (colon) { | |
147 | *colon = '\0'; | |
148 | prefix = name; | |
149 | suffix = colon + 1; | |
150 | } else { | |
151 | prefix = ""; | |
152 | suffix = name; | |
064af421 | 153 | } |
064af421 | 154 | |
559843ed | 155 | for (i = 0; i < n_netdev_classes; i++) { |
8b61709d BP |
156 | const struct netdev_class *class = netdev_classes[i]; |
157 | if (!strcmp(prefix, class->prefix)) { | |
158 | error = class->open(name_, suffix, ethertype, &netdev); | |
159 | goto exit; | |
160 | } | |
064af421 | 161 | } |
8b61709d | 162 | error = EAFNOSUPPORT; |
064af421 | 163 | |
8b61709d BP |
164 | exit: |
165 | *netdevp = error ? NULL : netdev; | |
effb3acf | 166 | free(name); |
064af421 BP |
167 | return error; |
168 | } | |
169 | ||
170 | /* Closes and destroys 'netdev'. */ | |
171 | void | |
172 | netdev_close(struct netdev *netdev) | |
173 | { | |
174 | if (netdev) { | |
8b61709d | 175 | char *name; |
064af421 | 176 | int error; |
8b61709d BP |
177 | |
178 | /* Restore flags that we changed, if any. */ | |
064af421 BP |
179 | fatal_signal_block(); |
180 | error = restore_flags(netdev); | |
181 | list_remove(&netdev->node); | |
182 | fatal_signal_unblock(); | |
183 | if (error) { | |
184 | VLOG_WARN("failed to restore network device flags on %s: %s", | |
185 | netdev->name, strerror(error)); | |
186 | } | |
187 | ||
188 | /* Free. */ | |
8b61709d BP |
189 | name = netdev->name; |
190 | netdev->class->close(netdev); | |
191 | free(name); | |
064af421 BP |
192 | } |
193 | } | |
194 | ||
8b61709d BP |
195 | /* Returns true if a network device named 'name' exists and may be opened, |
196 | * otherwise false. */ | |
5bfc0cd3 BP |
197 | bool |
198 | netdev_exists(const char *name) | |
199 | { | |
8b61709d | 200 | struct netdev *netdev; |
5bfc0cd3 BP |
201 | int error; |
202 | ||
8b61709d BP |
203 | error = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev); |
204 | if (!error) { | |
205 | netdev_close(netdev); | |
206 | return true; | |
207 | } else { | |
208 | if (error != ENODEV) { | |
209 | VLOG_WARN("failed to open network device %s: %s", | |
210 | name, strerror(error)); | |
211 | } | |
212 | return false; | |
213 | } | |
5bfc0cd3 BP |
214 | } |
215 | ||
8b61709d BP |
216 | /* Initializes 'svec' with a list of the names of all known network devices. */ |
217 | int | |
218 | netdev_enumerate(struct svec *svec) | |
064af421 | 219 | { |
8b61709d BP |
220 | int error; |
221 | int i; | |
222 | ||
223 | svec_init(svec); | |
224 | ||
559843ed | 225 | netdev_initialize(); |
8b61709d BP |
226 | |
227 | error = 0; | |
559843ed | 228 | for (i = 0; i < n_netdev_classes; i++) { |
8b61709d BP |
229 | const struct netdev_class *class = netdev_classes[i]; |
230 | if (class->enumerate) { | |
231 | int retval = class->enumerate(svec); | |
232 | if (retval) { | |
233 | VLOG_WARN("failed to enumerate %s network devices: %s", | |
234 | class->name, strerror(retval)); | |
235 | if (!error) { | |
236 | error = retval; | |
237 | } | |
238 | } | |
239 | } | |
240 | } | |
241 | return error; | |
064af421 BP |
242 | } |
243 | ||
244 | /* Attempts to receive a packet from 'netdev' into 'buffer', which the caller | |
245 | * must have initialized with sufficient room for the packet. The space | |
246 | * required to receive any packet is ETH_HEADER_LEN bytes, plus VLAN_HEADER_LEN | |
247 | * bytes, plus the device's MTU (which may be retrieved via netdev_get_mtu()). | |
248 | * (Some devices do not allow for a VLAN header, in which case VLAN_HEADER_LEN | |
249 | * need not be included.) | |
250 | * | |
251 | * If a packet is successfully retrieved, returns 0. In this case 'buffer' is | |
252 | * guaranteed to contain at least ETH_TOTAL_MIN bytes. Otherwise, returns a | |
253 | * positive errno value. Returns EAGAIN immediately if no packet is ready to | |
254 | * be returned. | |
255 | */ | |
256 | int | |
257 | netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) | |
258 | { | |
8b61709d | 259 | int retval; |
064af421 BP |
260 | |
261 | assert(buffer->size == 0); | |
262 | assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN); | |
8b61709d BP |
263 | |
264 | retval = netdev->class->recv(netdev, | |
265 | buffer->data, ofpbuf_tailroom(buffer)); | |
266 | if (retval >= 0) { | |
064af421 | 267 | COVERAGE_INC(netdev_received); |
8b61709d BP |
268 | buffer->size += retval; |
269 | if (buffer->size < ETH_TOTAL_MIN) { | |
270 | ofpbuf_put_zeros(buffer, ETH_TOTAL_MIN - buffer->size); | |
271 | } | |
064af421 | 272 | return 0; |
8b61709d BP |
273 | } else { |
274 | return -retval; | |
064af421 BP |
275 | } |
276 | } | |
277 | ||
278 | /* Registers with the poll loop to wake up from the next call to poll_block() | |
279 | * when a packet is ready to be received with netdev_recv() on 'netdev'. */ | |
280 | void | |
281 | netdev_recv_wait(struct netdev *netdev) | |
282 | { | |
8b61709d | 283 | netdev->class->recv_wait(netdev); |
064af421 BP |
284 | } |
285 | ||
286 | /* Discards all packets waiting to be received from 'netdev'. */ | |
287 | int | |
288 | netdev_drain(struct netdev *netdev) | |
289 | { | |
8b61709d | 290 | return netdev->class->drain(netdev); |
064af421 BP |
291 | } |
292 | ||
293 | /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive | |
294 | * errno value. Returns EAGAIN without blocking if the packet cannot be queued | |
295 | * immediately. Returns EMSGSIZE if a partial packet was transmitted or if | |
296 | * the packet is too big or too small to transmit on the device. | |
297 | * | |
298 | * The caller retains ownership of 'buffer' in all cases. | |
299 | * | |
300 | * The kernel maintains a packet transmission queue, so the caller is not | |
301 | * expected to do additional queuing of packets. */ | |
302 | int | |
303 | netdev_send(struct netdev *netdev, const struct ofpbuf *buffer) | |
304 | { | |
8b61709d BP |
305 | int error = netdev->class->send(netdev, buffer->data, buffer->size); |
306 | if (!error) { | |
064af421 | 307 | COVERAGE_INC(netdev_sent); |
064af421 | 308 | } |
8b61709d | 309 | return error; |
064af421 BP |
310 | } |
311 | ||
312 | /* Registers with the poll loop to wake up from the next call to poll_block() | |
313 | * when the packet transmission queue has sufficient room to transmit a packet | |
314 | * with netdev_send(). | |
315 | * | |
316 | * The kernel maintains a packet transmission queue, so the client is not | |
317 | * expected to do additional queuing of packets. Thus, this function is | |
318 | * unlikely to ever be used. It is included for completeness. */ | |
319 | void | |
320 | netdev_send_wait(struct netdev *netdev) | |
321 | { | |
8b61709d | 322 | return netdev->class->send_wait(netdev); |
064af421 BP |
323 | } |
324 | ||
325 | /* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, | |
326 | * otherwise a positive errno value. */ | |
327 | int | |
328 | netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN]) | |
329 | { | |
8b61709d | 330 | return netdev->class->set_etheraddr(netdev, mac); |
064af421 BP |
331 | } |
332 | ||
80992a35 BP |
333 | /* Retrieves 'netdev''s MAC address. If successful, returns 0 and copies the |
334 | * the MAC address into 'mac'. On failure, returns a positive errno value and | |
335 | * clears 'mac' to all-zeros. */ | |
336 | int | |
337 | netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN]) | |
064af421 | 338 | { |
8b61709d | 339 | return netdev->class->get_etheraddr(netdev, mac); |
064af421 BP |
340 | } |
341 | ||
342 | /* Returns the name of the network device that 'netdev' represents, | |
343 | * e.g. "eth0". The caller must not modify or free the returned string. */ | |
344 | const char * | |
345 | netdev_get_name(const struct netdev *netdev) | |
346 | { | |
347 | return netdev->name; | |
348 | } | |
349 | ||
3d222126 BP |
350 | /* Retrieves the MTU of 'netdev'. The MTU is the maximum size of transmitted |
351 | * (and received) packets, in bytes, not including the hardware header; thus, | |
352 | * this is typically 1500 bytes for Ethernet devices. | |
353 | * | |
354 | * If successful, returns 0 and stores the MTU size in '*mtup'. On failure, | |
355 | * returns a positive errno value and stores ETH_PAYLOAD_MAX (1500) in | |
356 | * '*mtup'. */ | |
064af421 | 357 | int |
3d222126 | 358 | netdev_get_mtu(const struct netdev *netdev, int *mtup) |
064af421 | 359 | { |
8b61709d BP |
360 | int error = netdev->class->get_mtu(netdev, mtup); |
361 | if (error) { | |
362 | VLOG_WARN_RL(&rl, "failed to retrieve MTU for network device %s: %s", | |
363 | netdev_get_name(netdev), strerror(error)); | |
364 | *mtup = ETH_PAYLOAD_MAX; | |
365 | } | |
366 | return error; | |
064af421 BP |
367 | } |
368 | ||
9ab3d9a3 BP |
369 | /* Returns the ifindex of 'netdev', if successful, as a positive number. On |
370 | * failure, returns a negative errno value. | |
371 | * | |
372 | * The desired semantics of the ifindex value are a combination of those | |
373 | * specified by POSIX for if_nametoindex() and by SNMP for ifIndex. An ifindex | |
374 | * value should be unique within a host and remain stable at least until | |
375 | * reboot. SNMP says an ifindex "ranges between 1 and the value of ifNumber" | |
376 | * but many systems do not follow this rule anyhow. | |
377 | */ | |
378 | int | |
379 | netdev_get_ifindex(const struct netdev *netdev) | |
380 | { | |
381 | return netdev->class->get_ifindex(netdev); | |
382 | } | |
383 | ||
064af421 BP |
384 | /* Stores the features supported by 'netdev' into each of '*current', |
385 | * '*advertised', '*supported', and '*peer' that are non-null. Each value is a | |
386 | * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if | |
387 | * successful, otherwise a positive errno value. On failure, all of the | |
388 | * passed-in values are set to 0. */ | |
389 | int | |
390 | netdev_get_features(struct netdev *netdev, | |
391 | uint32_t *current, uint32_t *advertised, | |
392 | uint32_t *supported, uint32_t *peer) | |
393 | { | |
394 | uint32_t dummy[4]; | |
7671589a BP |
395 | int error; |
396 | ||
397 | if (!current) { | |
398 | current = &dummy[0]; | |
399 | } | |
400 | if (!advertised) { | |
401 | advertised = &dummy[1]; | |
402 | } | |
403 | if (!supported) { | |
404 | supported = &dummy[2]; | |
405 | } | |
406 | if (!peer) { | |
407 | peer = &dummy[3]; | |
408 | } | |
409 | ||
410 | error = netdev->class->get_features(netdev, current, advertised, supported, | |
411 | peer); | |
412 | if (error) { | |
413 | *current = *advertised = *supported = *peer = 0; | |
414 | } | |
415 | return error; | |
064af421 BP |
416 | } |
417 | ||
8b61709d BP |
418 | /* Set the features advertised by 'netdev' to 'advertise'. Returns 0 if |
419 | * successful, otherwise a positive errno value. */ | |
064af421 BP |
420 | int |
421 | netdev_set_advertisements(struct netdev *netdev, uint32_t advertise) | |
422 | { | |
8b61709d BP |
423 | return (netdev->class->set_advertisements |
424 | ? netdev->class->set_advertisements(netdev, advertise) | |
425 | : EOPNOTSUPP); | |
064af421 BP |
426 | } |
427 | ||
f1acd62b BP |
428 | /* If 'netdev' has an assigned IPv4 address, sets '*address' to that address |
429 | * and '*netmask' to its netmask and returns 0. Otherwise, returns a positive | |
430 | * errno value and sets '*address' to 0 (INADDR_ANY). | |
8b61709d BP |
431 | * |
432 | * The following error values have well-defined meanings: | |
433 | * | |
434 | * - EADDRNOTAVAIL: 'netdev' has no assigned IPv4 address. | |
435 | * | |
436 | * - EOPNOTSUPP: No IPv4 network stack attached to 'netdev'. | |
437 | * | |
f1acd62b BP |
438 | * 'address' or 'netmask' or both may be null, in which case the address or netmask |
439 | * is not reported. */ | |
6b9bd979 | 440 | int |
f1acd62b BP |
441 | netdev_get_in4(const struct netdev *netdev, |
442 | struct in_addr *address_, struct in_addr *netmask_) | |
064af421 | 443 | { |
f1acd62b BP |
444 | struct in_addr address; |
445 | struct in_addr netmask; | |
064af421 BP |
446 | int error; |
447 | ||
8b61709d | 448 | error = (netdev->class->get_in4 |
f1acd62b | 449 | ? netdev->class->get_in4(netdev, &address, &netmask) |
8b61709d | 450 | : EOPNOTSUPP); |
f1acd62b BP |
451 | if (address_) { |
452 | address_->s_addr = error ? 0 : address.s_addr; | |
453 | } | |
454 | if (netmask_) { | |
455 | netmask_->s_addr = error ? 0 : netmask.s_addr; | |
064af421 BP |
456 | } |
457 | return error; | |
458 | } | |
459 | ||
460 | /* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If | |
461 | * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a | |
462 | * positive errno value. */ | |
463 | int | |
464 | netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask) | |
465 | { | |
8b61709d BP |
466 | return (netdev->class->set_in4 |
467 | ? netdev->class->set_in4(netdev, addr, mask) | |
468 | : EOPNOTSUPP); | |
064af421 BP |
469 | } |
470 | ||
0efaf4b5 BP |
471 | /* Adds 'router' as a default IP gateway for the TCP/IP stack that corresponds |
472 | * to 'netdev'. */ | |
064af421 | 473 | int |
8b61709d | 474 | netdev_add_router(struct netdev *netdev, struct in_addr router) |
064af421 | 475 | { |
064af421 | 476 | COVERAGE_INC(netdev_add_router); |
8b61709d BP |
477 | return (netdev->class->add_router |
478 | ? netdev->class->add_router(netdev, router) | |
479 | : EOPNOTSUPP); | |
064af421 BP |
480 | } |
481 | ||
f1acd62b BP |
482 | /* Looks up the next hop for 'host' for the TCP/IP stack that corresponds to |
483 | * 'netdev'. If a route cannot not be determined, sets '*next_hop' to 0, | |
484 | * '*netdev_name' to null, and returns a positive errno value. Otherwise, if a | |
485 | * next hop is found, stores the next hop gateway's address (0 if 'host' is on | |
486 | * a directly connected network) in '*next_hop' and a copy of the name of the | |
487 | * device to reach 'host' in '*netdev_name', and returns 0. The caller is | |
488 | * responsible for freeing '*netdev_name' (by calling free()). */ | |
489 | int | |
490 | netdev_get_next_hop(const struct netdev *netdev, | |
491 | const struct in_addr *host, struct in_addr *next_hop, | |
492 | char **netdev_name) | |
493 | { | |
494 | int error = (netdev->class->get_next_hop | |
495 | ? netdev->class->get_next_hop(host, next_hop, netdev_name) | |
496 | : EOPNOTSUPP); | |
064af421 | 497 | if (error) { |
f1acd62b BP |
498 | next_hop->s_addr = 0; |
499 | *netdev_name = NULL; | |
064af421 BP |
500 | } |
501 | return error; | |
502 | } | |
503 | ||
8b61709d BP |
504 | /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address and |
505 | * returns 0. Otherwise, returns a positive errno value and sets '*in6' to | |
506 | * all-zero-bits (in6addr_any). | |
507 | * | |
508 | * The following error values have well-defined meanings: | |
509 | * | |
510 | * - EADDRNOTAVAIL: 'netdev' has no assigned IPv6 address. | |
511 | * | |
512 | * - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'. | |
513 | * | |
514 | * 'in6' may be null, in which case the address itself is not reported. */ | |
064af421 | 515 | int |
8b61709d | 516 | netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6) |
064af421 | 517 | { |
8b61709d BP |
518 | struct in6_addr dummy; |
519 | int error; | |
b1bf7d43 | 520 | |
8b61709d BP |
521 | error = (netdev->class->get_in6 |
522 | ? netdev->class->get_in6(netdev, in6 ? in6 : &dummy) | |
523 | : EOPNOTSUPP); | |
524 | if (error && in6) { | |
525 | memset(in6, 0, sizeof *in6); | |
b1bf7d43 | 526 | } |
8b61709d | 527 | return error; |
064af421 BP |
528 | } |
529 | ||
530 | /* On 'netdev', turns off the flags in 'off' and then turns on the flags in | |
531 | * 'on'. If 'permanent' is true, the changes will persist; otherwise, they | |
532 | * will be reverted when 'netdev' is closed or the program exits. Returns 0 if | |
533 | * successful, otherwise a positive errno value. */ | |
534 | static int | |
535 | do_update_flags(struct netdev *netdev, enum netdev_flags off, | |
8b61709d BP |
536 | enum netdev_flags on, enum netdev_flags *old_flagsp, |
537 | bool permanent) | |
064af421 | 538 | { |
8b61709d | 539 | enum netdev_flags old_flags; |
064af421 BP |
540 | int error; |
541 | ||
8b61709d | 542 | error = netdev->class->update_flags(netdev, off & ~on, on, &old_flags); |
064af421 | 543 | if (error) { |
8b61709d BP |
544 | VLOG_WARN_RL(&rl, "failed to %s flags for network device %s: %s", |
545 | off || on ? "set" : "get", netdev_get_name(netdev), | |
546 | strerror(error)); | |
547 | old_flags = 0; | |
548 | } else if ((off || on) && !permanent) { | |
549 | enum netdev_flags new_flags = (old_flags & ~off) | on; | |
550 | enum netdev_flags changed_flags = old_flags ^ new_flags; | |
551 | if (changed_flags) { | |
552 | if (!netdev->changed_flags) { | |
553 | netdev->save_flags = old_flags; | |
554 | } | |
555 | netdev->changed_flags |= changed_flags; | |
556 | } | |
064af421 | 557 | } |
8b61709d BP |
558 | if (old_flagsp) { |
559 | *old_flagsp = old_flags; | |
064af421 BP |
560 | } |
561 | return error; | |
562 | } | |
563 | ||
8b61709d BP |
564 | /* Obtains the current flags for 'netdev' and stores them into '*flagsp'. |
565 | * Returns 0 if successful, otherwise a positive errno value. On failure, | |
566 | * stores 0 into '*flagsp'. */ | |
567 | int | |
568 | netdev_get_flags(const struct netdev *netdev_, enum netdev_flags *flagsp) | |
569 | { | |
570 | struct netdev *netdev = (struct netdev *) netdev_; | |
571 | return do_update_flags(netdev, 0, 0, flagsp, false); | |
572 | } | |
573 | ||
064af421 BP |
574 | /* Sets the flags for 'netdev' to 'flags'. |
575 | * If 'permanent' is true, the changes will persist; otherwise, they | |
576 | * will be reverted when 'netdev' is closed or the program exits. | |
577 | * Returns 0 if successful, otherwise a positive errno value. */ | |
578 | int | |
579 | netdev_set_flags(struct netdev *netdev, enum netdev_flags flags, | |
580 | bool permanent) | |
581 | { | |
8b61709d | 582 | return do_update_flags(netdev, -1, flags, NULL, permanent); |
064af421 BP |
583 | } |
584 | ||
585 | /* Turns on the specified 'flags' on 'netdev'. | |
586 | * If 'permanent' is true, the changes will persist; otherwise, they | |
587 | * will be reverted when 'netdev' is closed or the program exits. | |
588 | * Returns 0 if successful, otherwise a positive errno value. */ | |
589 | int | |
590 | netdev_turn_flags_on(struct netdev *netdev, enum netdev_flags flags, | |
591 | bool permanent) | |
592 | { | |
8b61709d | 593 | return do_update_flags(netdev, 0, flags, NULL, permanent); |
064af421 BP |
594 | } |
595 | ||
596 | /* Turns off the specified 'flags' on 'netdev'. | |
597 | * If 'permanent' is true, the changes will persist; otherwise, they | |
598 | * will be reverted when 'netdev' is closed or the program exits. | |
599 | * Returns 0 if successful, otherwise a positive errno value. */ | |
600 | int | |
601 | netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags, | |
602 | bool permanent) | |
603 | { | |
8b61709d | 604 | return do_update_flags(netdev, flags, 0, NULL, permanent); |
064af421 BP |
605 | } |
606 | ||
607 | /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be | |
608 | * successfully retrieved, it stores the corresponding MAC address in 'mac' and | |
609 | * returns 0. Otherwise, it returns a positive errno value; in particular, | |
8b61709d | 610 | * ENXIO indicates that there is no ARP table entry for 'ip' on 'netdev'. */ |
064af421 | 611 | int |
8b61709d BP |
612 | netdev_arp_lookup(const struct netdev *netdev, |
613 | uint32_t ip, uint8_t mac[ETH_ADDR_LEN]) | |
064af421 | 614 | { |
8b61709d BP |
615 | int error = (netdev->class->arp_lookup |
616 | ? netdev->class->arp_lookup(netdev, ip, mac) | |
617 | : EOPNOTSUPP); | |
064af421 | 618 | if (error) { |
8b61709d | 619 | memset(mac, 0, ETH_ADDR_LEN); |
064af421 | 620 | } |
8b61709d | 621 | return error; |
064af421 BP |
622 | } |
623 | ||
8b61709d | 624 | /* Sets 'carrier' to true if carrier is active (link light is on) on |
887fd0ba | 625 | * 'netdev'. */ |
064af421 BP |
626 | int |
627 | netdev_get_carrier(const struct netdev *netdev, bool *carrier) | |
628 | { | |
8b61709d BP |
629 | int error = (netdev->class->get_carrier |
630 | ? netdev->class->get_carrier(netdev, carrier) | |
631 | : EOPNOTSUPP); | |
632 | if (error) { | |
633 | *carrier = false; | |
064af421 | 634 | } |
064af421 BP |
635 | return error; |
636 | } | |
637 | ||
887fd0ba | 638 | /* Retrieves current device stats for 'netdev'. */ |
064af421 BP |
639 | int |
640 | netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats) | |
641 | { | |
642 | int error; | |
643 | ||
644 | COVERAGE_INC(netdev_get_stats); | |
8b61709d BP |
645 | error = (netdev->class->get_stats |
646 | ? netdev->class->get_stats(netdev, stats) | |
647 | : EOPNOTSUPP); | |
064af421 BP |
648 | if (error) { |
649 | memset(stats, 0xff, sizeof *stats); | |
650 | } | |
651 | return error; | |
652 | } | |
653 | ||
8b61709d BP |
654 | /* Attempts to set input rate limiting (policing) policy, such that up to |
655 | * 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative burst | |
656 | * size of 'kbits' kb. */ | |
064af421 | 657 | int |
b1bf7d43 BP |
658 | netdev_set_policing(struct netdev *netdev, uint32_t kbits_rate, |
659 | uint32_t kbits_burst) | |
064af421 | 660 | { |
8b61709d BP |
661 | return (netdev->class->set_policing |
662 | ? netdev->class->set_policing(netdev, kbits_rate, kbits_burst) | |
663 | : EOPNOTSUPP); | |
064af421 BP |
664 | } |
665 | ||
8b61709d BP |
666 | /* If 'netdev' is a VLAN network device (e.g. one created with vconfig(8)), |
667 | * sets '*vlan_vid' to the VLAN VID associated with that device and returns 0. | |
668 | * Otherwise returns a errno value (specifically ENOENT if 'netdev_name' is the | |
669 | * name of a network device that is not a VLAN device) and sets '*vlan_vid' to | |
670 | * -1. */ | |
671 | int | |
672 | netdev_get_vlan_vid(const struct netdev *netdev, int *vlan_vid) | |
064af421 | 673 | { |
8b61709d BP |
674 | int error = (netdev->class->get_vlan_vid |
675 | ? netdev->class->get_vlan_vid(netdev, vlan_vid) | |
676 | : ENOENT); | |
677 | if (error) { | |
678 | *vlan_vid = 0; | |
064af421 | 679 | } |
8b61709d | 680 | return error; |
064af421 BP |
681 | } |
682 | ||
c752217a BP |
683 | /* Returns a network device that has 'in4' as its IP address, if one exists, |
684 | * otherwise a null pointer. */ | |
685 | struct netdev * | |
686 | netdev_find_dev_by_in4(const struct in_addr *in4) | |
79c720a8 | 687 | { |
c752217a | 688 | struct netdev *netdev; |
79c720a8 | 689 | struct svec dev_list; |
c752217a | 690 | size_t i; |
79c720a8 | 691 | |
79c720a8 | 692 | netdev_enumerate(&dev_list); |
c752217a BP |
693 | for (i = 0; i < dev_list.n; i++) { |
694 | const char *name = dev_list.names[i]; | |
695 | struct in_addr dev_in4; | |
696 | ||
697 | if (!netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev) | |
f1acd62b | 698 | && !netdev_get_in4(netdev, &dev_in4, NULL) |
c752217a BP |
699 | && dev_in4.s_addr == in4->s_addr) { |
700 | goto exit; | |
79c720a8 | 701 | } |
c752217a | 702 | netdev_close(netdev); |
79c720a8 | 703 | } |
c752217a | 704 | netdev = NULL; |
79c720a8 | 705 | |
c752217a | 706 | exit: |
79c720a8 | 707 | svec_destroy(&dev_list); |
c752217a | 708 | return netdev; |
79c720a8 | 709 | } |
8b61709d BP |
710 | \f |
711 | /* Initializes 'netdev' as a netdev named 'name' of the specified 'class'. | |
712 | * | |
713 | * This function adds 'netdev' to a netdev-owned linked list, so it is very | |
714 | * important that 'netdev' only be freed after calling netdev_close(). */ | |
715 | void | |
716 | netdev_init(struct netdev *netdev, const char *name, | |
717 | const struct netdev_class *class) | |
064af421 | 718 | { |
8b61709d BP |
719 | netdev->class = class; |
720 | netdev->name = xstrdup(name); | |
721 | netdev->save_flags = 0; | |
722 | netdev->changed_flags = 0; | |
723 | list_push_back(&netdev_list, &netdev->node); | |
724 | } | |
064af421 | 725 | |
8b61709d BP |
726 | /* Initializes 'notifier' as a netdev notifier for 'netdev', for which |
727 | * notification will consist of calling 'cb', with auxiliary data 'aux'. */ | |
728 | void | |
729 | netdev_notifier_init(struct netdev_notifier *notifier, struct netdev *netdev, | |
730 | void (*cb)(struct netdev_notifier *), void *aux) | |
731 | { | |
732 | notifier->netdev = netdev; | |
733 | notifier->cb = cb; | |
734 | notifier->aux = aux; | |
064af421 BP |
735 | } |
736 | \f | |
8b61709d | 737 | /* Tracks changes in the status of a set of network devices. */ |
e9e28be3 | 738 | struct netdev_monitor { |
e9e28be3 BP |
739 | struct shash polled_netdevs; |
740 | struct shash changed_netdevs; | |
741 | }; | |
742 | ||
8b61709d BP |
743 | /* Creates and returns a new structure for monitor changes in the status of |
744 | * network devices. */ | |
745 | struct netdev_monitor * | |
746 | netdev_monitor_create(void) | |
e9e28be3 | 747 | { |
8b61709d | 748 | struct netdev_monitor *monitor = xmalloc(sizeof *monitor); |
e9e28be3 BP |
749 | shash_init(&monitor->polled_netdevs); |
750 | shash_init(&monitor->changed_netdevs); | |
8b61709d | 751 | return monitor; |
e9e28be3 BP |
752 | } |
753 | ||
8b61709d | 754 | /* Destroys 'monitor'. */ |
e9e28be3 BP |
755 | void |
756 | netdev_monitor_destroy(struct netdev_monitor *monitor) | |
757 | { | |
758 | if (monitor) { | |
8b61709d BP |
759 | struct shash_node *node; |
760 | ||
761 | SHASH_FOR_EACH (node, &monitor->polled_netdevs) { | |
762 | struct netdev_notifier *notifier = node->data; | |
763 | notifier->netdev->class->poll_remove(notifier); | |
764 | } | |
765 | ||
e9e28be3 | 766 | shash_destroy(&monitor->polled_netdevs); |
8b61709d | 767 | shash_destroy(&monitor->changed_netdevs); |
e9e28be3 BP |
768 | free(monitor); |
769 | } | |
770 | } | |
771 | ||
8b61709d BP |
772 | static void |
773 | netdev_monitor_cb(struct netdev_notifier *notifier) | |
774 | { | |
775 | struct netdev_monitor *monitor = notifier->aux; | |
776 | const char *name = netdev_get_name(notifier->netdev); | |
777 | if (!shash_find(&monitor->changed_netdevs, name)) { | |
778 | shash_add(&monitor->changed_netdevs, name, NULL); | |
779 | } | |
780 | } | |
781 | ||
782 | /* Attempts to add 'netdev' as a netdev monitored by 'monitor'. Returns 0 if | |
783 | * successful, otherwise a positive errno value. | |
784 | * | |
785 | * Adding a given 'netdev' to a monitor multiple times is equivalent to adding | |
786 | * it once. */ | |
787 | int | |
e9e28be3 BP |
788 | netdev_monitor_add(struct netdev_monitor *monitor, struct netdev *netdev) |
789 | { | |
8b61709d BP |
790 | const char *netdev_name = netdev_get_name(netdev); |
791 | int error = 0; | |
792 | if (!shash_find(&monitor->polled_netdevs, netdev_name) | |
793 | && netdev->class->poll_add) | |
794 | { | |
795 | struct netdev_notifier *notifier; | |
796 | error = netdev->class->poll_add(netdev, netdev_monitor_cb, monitor, | |
797 | ¬ifier); | |
798 | if (!error) { | |
799 | assert(notifier->netdev == netdev); | |
800 | shash_add(&monitor->polled_netdevs, netdev_name, notifier); | |
801 | } | |
e9e28be3 | 802 | } |
8b61709d | 803 | return error; |
e9e28be3 BP |
804 | } |
805 | ||
8b61709d BP |
806 | /* Removes 'netdev' from the set of netdevs monitored by 'monitor'. (This has |
807 | * no effect if 'netdev' is not in the set of devices monitored by | |
808 | * 'monitor'.) */ | |
e9e28be3 BP |
809 | void |
810 | netdev_monitor_remove(struct netdev_monitor *monitor, struct netdev *netdev) | |
811 | { | |
8b61709d | 812 | const char *netdev_name = netdev_get_name(netdev); |
e9e28be3 BP |
813 | struct shash_node *node; |
814 | ||
8b61709d | 815 | node = shash_find(&monitor->polled_netdevs, netdev_name); |
e9e28be3 | 816 | if (node) { |
8b61709d BP |
817 | /* Cancel future notifications. */ |
818 | struct netdev_notifier *notifier = node->data; | |
819 | netdev->class->poll_remove(notifier); | |
e9e28be3 | 820 | shash_delete(&monitor->polled_netdevs, node); |
8b61709d BP |
821 | |
822 | /* Drop any pending notification. */ | |
823 | node = shash_find(&monitor->changed_netdevs, netdev_name); | |
e9e28be3 BP |
824 | if (node) { |
825 | shash_delete(&monitor->changed_netdevs, node); | |
826 | } | |
827 | } | |
828 | } | |
829 | ||
8b61709d BP |
830 | /* Checks for changes to netdevs in the set monitored by 'monitor'. If any of |
831 | * the attributes (Ethernet address, carrier status, speed or peer-advertised | |
832 | * speed, flags, etc.) of a network device monitored by 'monitor' has changed, | |
833 | * sets '*devnamep' to the name of a device that has changed and returns 0. | |
834 | * The caller is responsible for freeing '*devnamep' (with free()). | |
835 | * | |
836 | * If no devices have changed, sets '*devnamep' to NULL and returns EAGAIN. | |
837 | */ | |
e9e28be3 BP |
838 | int |
839 | netdev_monitor_poll(struct netdev_monitor *monitor, char **devnamep) | |
840 | { | |
8b61709d BP |
841 | struct shash_node *node = shash_first(&monitor->changed_netdevs); |
842 | if (!node) { | |
843 | *devnamep = NULL; | |
844 | return EAGAIN; | |
845 | } else { | |
e9e28be3 BP |
846 | *devnamep = xstrdup(node->name); |
847 | shash_delete(&monitor->changed_netdevs, node); | |
8b61709d | 848 | return 0; |
e9e28be3 | 849 | } |
e9e28be3 BP |
850 | } |
851 | ||
8b61709d BP |
852 | /* Registers with the poll loop to wake up from the next call to poll_block() |
853 | * when netdev_monitor_poll(monitor) would indicate that a device has | |
854 | * changed. */ | |
e9e28be3 BP |
855 | void |
856 | netdev_monitor_poll_wait(const struct netdev_monitor *monitor) | |
857 | { | |
8b61709d | 858 | if (!shash_is_empty(&monitor->changed_netdevs)) { |
e9e28be3 BP |
859 | poll_immediate_wake(); |
860 | } else { | |
8b61709d BP |
861 | /* XXX Nothing needed here for netdev_linux, but maybe other netdev |
862 | * classes need help. */ | |
e9e28be3 BP |
863 | } |
864 | } | |
865 | \f | |
064af421 BP |
866 | /* Restore the network device flags on 'netdev' to those that were active |
867 | * before we changed them. Returns 0 if successful, otherwise a positive | |
868 | * errno value. | |
869 | * | |
870 | * To avoid reentry, the caller must ensure that fatal signals are blocked. */ | |
871 | static int | |
872 | restore_flags(struct netdev *netdev) | |
873 | { | |
8b61709d BP |
874 | if (netdev->changed_flags) { |
875 | enum netdev_flags restore = netdev->save_flags & netdev->changed_flags; | |
876 | enum netdev_flags old_flags; | |
877 | return netdev->class->update_flags(netdev, | |
878 | netdev->changed_flags & ~restore, | |
879 | restore, &old_flags); | |
064af421 | 880 | } |
064af421 BP |
881 | return 0; |
882 | } | |
883 | ||
884 | /* Retores all the flags on all network devices that we modified. Called from | |
885 | * a signal handler, so it does not attempt to report error conditions. */ | |
886 | static void | |
887 | restore_all_flags(void *aux UNUSED) | |
888 | { | |
889 | struct netdev *netdev; | |
890 | LIST_FOR_EACH (netdev, struct netdev, node, &netdev_list) { | |
891 | restore_flags(netdev); | |
892 | } | |
893 | } |