]>
Commit | Line | Data |
---|---|---|
614c4892 | 1 | /* |
275707c3 | 2 | * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc. |
614c4892 BP |
3 | * |
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: | |
7 | * | |
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. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | ||
19 | #include "dummy.h" | |
20 | ||
21 | #include <errno.h> | |
22 | ||
fbac791a | 23 | #include "flow.h" |
614c4892 BP |
24 | #include "list.h" |
25 | #include "netdev-provider.h" | |
c060c4cf | 26 | #include "netdev-vport.h" |
fbac791a BP |
27 | #include "odp-util.h" |
28 | #include "ofp-print.h" | |
29 | #include "ofpbuf.h" | |
614c4892 | 30 | #include "packets.h" |
fbac791a | 31 | #include "poll-loop.h" |
614c4892 | 32 | #include "shash.h" |
0cbfe35d | 33 | #include "sset.h" |
eab5611a BP |
34 | #include "stream.h" |
35 | #include "unaligned.h" | |
fbac791a | 36 | #include "unixctl.h" |
614c4892 BP |
37 | #include "vlog.h" |
38 | ||
39 | VLOG_DEFINE_THIS_MODULE(netdev_dummy); | |
40 | ||
eab5611a BP |
41 | struct dummy_stream { |
42 | struct stream *stream; | |
43 | struct ofpbuf rxbuf; | |
44 | struct list txq; | |
45 | }; | |
46 | ||
b5d57fc8 BP |
47 | struct netdev_dummy { |
48 | struct netdev up; | |
614c4892 BP |
49 | uint8_t hwaddr[ETH_ADDR_LEN]; |
50 | int mtu; | |
51 | struct netdev_stats stats; | |
52 | enum netdev_flags flags; | |
ac4d3bcb | 53 | unsigned int change_seq; |
8073dd31 | 54 | int ifindex; |
796223f5 | 55 | |
eab5611a BP |
56 | struct pstream *pstream; |
57 | struct dummy_stream *streams; | |
58 | size_t n_streams; | |
59 | ||
796223f5 | 60 | struct list rxes; /* List of child "netdev_rx_dummy"s. */ |
614c4892 BP |
61 | }; |
62 | ||
86f1d032 BP |
63 | static const struct netdev_class dummy_class; |
64 | ||
e34cfdd9 BP |
65 | /* Max 'recv_queue_len' in struct netdev_dummy. */ |
66 | #define NETDEV_DUMMY_MAX_QUEUE 100 | |
67 | ||
796223f5 BP |
68 | struct netdev_rx_dummy { |
69 | struct netdev_rx up; | |
b5d57fc8 | 70 | struct list node; /* In netdev_dummy's "rxes" list. */ |
fbac791a | 71 | struct list recv_queue; |
e34cfdd9 BP |
72 | int recv_queue_len; /* list_size(&recv_queue). */ |
73 | bool listening; | |
614c4892 BP |
74 | }; |
75 | ||
796223f5 BP |
76 | static const struct netdev_rx_class netdev_rx_dummy_class; |
77 | ||
95d4ec33 | 78 | static unixctl_cb_func netdev_dummy_set_admin_state; |
614c4892 | 79 | static int netdev_dummy_create(const struct netdev_class *, const char *, |
b5d57fc8 BP |
80 | struct netdev **); |
81 | static void netdev_dummy_poll_notify(struct netdev_dummy *); | |
eab5611a BP |
82 | static void netdev_dummy_queue_packet(struct netdev_dummy *, struct ofpbuf *); |
83 | ||
84 | static void dummy_stream_close(struct dummy_stream *); | |
614c4892 BP |
85 | |
86 | static bool | |
87 | is_dummy_class(const struct netdev_class *class) | |
88 | { | |
89 | return class->create == netdev_dummy_create; | |
90 | } | |
91 | ||
614c4892 BP |
92 | static struct netdev_dummy * |
93 | netdev_dummy_cast(const struct netdev *netdev) | |
94 | { | |
b5d57fc8 | 95 | ovs_assert(is_dummy_class(netdev_get_class(netdev))); |
180c6d0b | 96 | return CONTAINER_OF(netdev, struct netdev_dummy, up); |
614c4892 BP |
97 | } |
98 | ||
796223f5 BP |
99 | static struct netdev_rx_dummy * |
100 | netdev_rx_dummy_cast(const struct netdev_rx *rx) | |
101 | { | |
102 | netdev_rx_assert_class(rx, &netdev_rx_dummy_class); | |
103 | return CONTAINER_OF(rx, struct netdev_rx_dummy, up); | |
104 | } | |
105 | ||
eab5611a BP |
106 | static void |
107 | netdev_dummy_run(void) | |
108 | { | |
86f1d032 | 109 | struct shash dummy_netdevs; |
eab5611a BP |
110 | struct shash_node *node; |
111 | ||
86f1d032 BP |
112 | shash_init(&dummy_netdevs); |
113 | netdev_get_devices(&dummy_class, &dummy_netdevs); | |
eab5611a BP |
114 | SHASH_FOR_EACH (node, &dummy_netdevs) { |
115 | struct netdev_dummy *dev = node->data; | |
116 | size_t i; | |
117 | ||
118 | if (dev->pstream) { | |
119 | struct stream *new_stream; | |
120 | int error; | |
121 | ||
122 | error = pstream_accept(dev->pstream, &new_stream); | |
123 | if (!error) { | |
124 | struct dummy_stream *s; | |
125 | ||
126 | dev->streams = xrealloc(dev->streams, | |
127 | ((dev->n_streams + 1) | |
128 | * sizeof *dev->streams)); | |
129 | s = &dev->streams[dev->n_streams++]; | |
130 | s->stream = new_stream; | |
131 | ofpbuf_init(&s->rxbuf, 2048); | |
132 | list_init(&s->txq); | |
133 | } else if (error != EAGAIN) { | |
134 | VLOG_WARN("%s: accept failed (%s)", | |
10a89ef0 | 135 | pstream_get_name(dev->pstream), ovs_strerror(error)); |
eab5611a BP |
136 | pstream_close(dev->pstream); |
137 | dev->pstream = NULL; | |
138 | } | |
139 | } | |
140 | ||
141 | for (i = 0; i < dev->n_streams; i++) { | |
142 | struct dummy_stream *s = &dev->streams[i]; | |
143 | int error = 0; | |
144 | size_t n; | |
145 | ||
146 | stream_run(s->stream); | |
147 | ||
148 | if (!list_is_empty(&s->txq)) { | |
149 | struct ofpbuf *txbuf; | |
150 | int retval; | |
151 | ||
152 | txbuf = ofpbuf_from_list(list_front(&s->txq)); | |
153 | retval = stream_send(s->stream, txbuf->data, txbuf->size); | |
154 | if (retval > 0) { | |
155 | ofpbuf_pull(txbuf, retval); | |
156 | if (!txbuf->size) { | |
157 | list_remove(&txbuf->list_node); | |
158 | ofpbuf_delete(txbuf); | |
159 | } | |
160 | } else if (retval != -EAGAIN) { | |
161 | error = -retval; | |
162 | } | |
163 | } | |
164 | ||
165 | if (!error) { | |
166 | if (s->rxbuf.size < 2) { | |
167 | n = 2 - s->rxbuf.size; | |
168 | } else { | |
169 | uint16_t frame_len; | |
170 | ||
171 | frame_len = ntohs(get_unaligned_be16(s->rxbuf.data)); | |
172 | if (frame_len < ETH_HEADER_LEN) { | |
173 | error = EPROTO; | |
174 | n = 0; | |
175 | } else { | |
176 | n = (2 + frame_len) - s->rxbuf.size; | |
177 | } | |
178 | } | |
179 | } | |
180 | if (!error) { | |
181 | int retval; | |
182 | ||
183 | ofpbuf_prealloc_tailroom(&s->rxbuf, n); | |
184 | retval = stream_recv(s->stream, ofpbuf_tail(&s->rxbuf), n); | |
185 | if (retval > 0) { | |
186 | s->rxbuf.size += retval; | |
187 | if (retval == n && s->rxbuf.size > 2) { | |
188 | ofpbuf_pull(&s->rxbuf, 2); | |
189 | netdev_dummy_queue_packet(dev, | |
190 | ofpbuf_clone(&s->rxbuf)); | |
191 | ofpbuf_clear(&s->rxbuf); | |
192 | } | |
193 | } else if (retval != -EAGAIN) { | |
194 | error = (retval < 0 ? -retval | |
195 | : s->rxbuf.size ? EPROTO | |
196 | : EOF); | |
197 | } | |
198 | } | |
199 | ||
200 | if (error) { | |
201 | VLOG_DBG("%s: closing connection (%s)", | |
202 | stream_get_name(s->stream), | |
203 | ovs_retval_to_string(error)); | |
204 | dummy_stream_close(&dev->streams[i]); | |
205 | dev->streams[i] = dev->streams[--dev->n_streams]; | |
206 | } | |
207 | } | |
86f1d032 BP |
208 | |
209 | netdev_close(&dev->up); | |
eab5611a | 210 | } |
86f1d032 | 211 | shash_destroy(&dummy_netdevs); |
eab5611a BP |
212 | } |
213 | ||
214 | static void | |
215 | dummy_stream_close(struct dummy_stream *s) | |
216 | { | |
217 | stream_close(s->stream); | |
218 | ofpbuf_uninit(&s->rxbuf); | |
219 | ofpbuf_list_delete(&s->txq); | |
220 | } | |
221 | ||
222 | static void | |
223 | netdev_dummy_wait(void) | |
224 | { | |
86f1d032 | 225 | struct shash dummy_netdevs; |
eab5611a BP |
226 | struct shash_node *node; |
227 | ||
86f1d032 BP |
228 | shash_init(&dummy_netdevs); |
229 | netdev_get_devices(&dummy_class, &dummy_netdevs); | |
eab5611a BP |
230 | SHASH_FOR_EACH (node, &dummy_netdevs) { |
231 | struct netdev_dummy *dev = node->data; | |
232 | size_t i; | |
233 | ||
234 | if (dev->pstream) { | |
235 | pstream_wait(dev->pstream); | |
236 | } | |
237 | for (i = 0; i < dev->n_streams; i++) { | |
238 | struct dummy_stream *s = &dev->streams[i]; | |
239 | ||
240 | stream_run_wait(s->stream); | |
241 | if (!list_is_empty(&s->txq)) { | |
242 | stream_send_wait(s->stream); | |
243 | } | |
244 | stream_recv_wait(s->stream); | |
245 | } | |
86f1d032 | 246 | netdev_close(&dev->up); |
eab5611a | 247 | } |
86f1d032 | 248 | shash_destroy(&dummy_netdevs); |
eab5611a BP |
249 | } |
250 | ||
614c4892 BP |
251 | static int |
252 | netdev_dummy_create(const struct netdev_class *class, const char *name, | |
b5d57fc8 | 253 | struct netdev **netdevp) |
614c4892 | 254 | { |
97be1538 | 255 | static atomic_uint next_n = ATOMIC_VAR_INIT(0xaa550000); |
b5d57fc8 | 256 | struct netdev_dummy *netdev; |
01cdb3a1 BP |
257 | unsigned int n; |
258 | ||
97be1538 | 259 | atomic_add(&next_n, 1, &n); |
b5d57fc8 BP |
260 | netdev = xzalloc(sizeof *netdev); |
261 | netdev_init(&netdev->up, name, class); | |
262 | netdev->hwaddr[0] = 0xaa; | |
263 | netdev->hwaddr[1] = 0x55; | |
264 | netdev->hwaddr[2] = n >> 24; | |
265 | netdev->hwaddr[3] = n >> 16; | |
266 | netdev->hwaddr[4] = n >> 8; | |
267 | netdev->hwaddr[5] = n; | |
268 | netdev->mtu = 1500; | |
269 | netdev->flags = 0; | |
270 | netdev->change_seq = 1; | |
271 | netdev->ifindex = -EOPNOTSUPP; | |
eab5611a BP |
272 | |
273 | netdev->pstream = NULL; | |
274 | netdev->streams = NULL; | |
275 | netdev->n_streams = 0; | |
276 | ||
b5d57fc8 BP |
277 | list_init(&netdev->rxes); |
278 | ||
b5d57fc8 | 279 | *netdevp = &netdev->up; |
614c4892 BP |
280 | |
281 | return 0; | |
282 | } | |
283 | ||
284 | static void | |
b5d57fc8 | 285 | netdev_dummy_destroy(struct netdev *netdev_) |
614c4892 | 286 | { |
b5d57fc8 | 287 | struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); |
eab5611a | 288 | size_t i; |
614c4892 | 289 | |
eab5611a BP |
290 | pstream_close(netdev->pstream); |
291 | for (i = 0; i < netdev->n_streams; i++) { | |
292 | dummy_stream_close(&netdev->streams[i]); | |
293 | } | |
294 | free(netdev->streams); | |
b5d57fc8 | 295 | free(netdev); |
614c4892 BP |
296 | } |
297 | ||
8073dd31 | 298 | static int |
b5d57fc8 | 299 | netdev_dummy_get_config(const struct netdev *netdev_, struct smap *args) |
8073dd31 | 300 | { |
b5d57fc8 | 301 | struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); |
8073dd31 | 302 | |
b5d57fc8 BP |
303 | if (netdev->ifindex >= 0) { |
304 | smap_add_format(args, "ifindex", "%d", netdev->ifindex); | |
8073dd31 | 305 | } |
eab5611a BP |
306 | if (netdev->pstream) { |
307 | smap_add(args, "pstream", pstream_get_name(netdev->pstream)); | |
308 | } | |
8073dd31 NM |
309 | return 0; |
310 | } | |
311 | ||
312 | static int | |
b5d57fc8 | 313 | netdev_dummy_set_config(struct netdev *netdev_, const struct smap *args) |
614c4892 | 314 | { |
b5d57fc8 | 315 | struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); |
eab5611a | 316 | const char *pstream; |
614c4892 | 317 | |
b5d57fc8 | 318 | netdev->ifindex = smap_get_int(args, "ifindex", -EOPNOTSUPP); |
eab5611a BP |
319 | |
320 | pstream = smap_get(args, "pstream"); | |
321 | if (!pstream | |
322 | || !netdev->pstream | |
323 | || strcmp(pstream_get_name(netdev->pstream), pstream)) { | |
324 | pstream_close(netdev->pstream); | |
325 | netdev->pstream = NULL; | |
326 | ||
327 | if (pstream) { | |
328 | int error; | |
329 | ||
330 | error = pstream_open(pstream, &netdev->pstream, DSCP_DEFAULT); | |
331 | if (error) { | |
10a89ef0 BP |
332 | VLOG_WARN("%s: open failed (%s)", |
333 | pstream, ovs_strerror(error)); | |
eab5611a BP |
334 | } |
335 | } | |
336 | } | |
614c4892 BP |
337 | return 0; |
338 | } | |
339 | ||
7b6b0ef4 | 340 | static int |
796223f5 | 341 | netdev_dummy_rx_open(struct netdev *netdev_, struct netdev_rx **rxp) |
7b6b0ef4 | 342 | { |
b5d57fc8 | 343 | struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); |
796223f5 BP |
344 | struct netdev_rx_dummy *rx; |
345 | ||
346 | rx = xmalloc(sizeof *rx); | |
b5d57fc8 BP |
347 | netdev_rx_init(&rx->up, &netdev->up, &netdev_rx_dummy_class); |
348 | list_push_back(&netdev->rxes, &rx->node); | |
796223f5 | 349 | list_init(&rx->recv_queue); |
e34cfdd9 | 350 | rx->recv_queue_len = 0; |
796223f5 BP |
351 | |
352 | *rxp = &rx->up; | |
7b6b0ef4 BP |
353 | return 0; |
354 | } | |
355 | ||
356 | static int | |
796223f5 | 357 | netdev_rx_dummy_recv(struct netdev_rx *rx_, void *buffer, size_t size) |
fbac791a | 358 | { |
796223f5 | 359 | struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_); |
fbac791a | 360 | struct ofpbuf *packet; |
7d6ce2d1 | 361 | int retval; |
fbac791a | 362 | |
796223f5 | 363 | if (list_is_empty(&rx->recv_queue)) { |
fbac791a BP |
364 | return -EAGAIN; |
365 | } | |
366 | ||
796223f5 | 367 | packet = ofpbuf_from_list(list_pop_front(&rx->recv_queue)); |
e34cfdd9 | 368 | rx->recv_queue_len--; |
7d6ce2d1 BP |
369 | if (packet->size <= size) { |
370 | memcpy(buffer, packet->data, packet->size); | |
371 | retval = packet->size; | |
372 | } else { | |
373 | retval = -EMSGSIZE; | |
fbac791a | 374 | } |
fbac791a BP |
375 | ofpbuf_delete(packet); |
376 | ||
7d6ce2d1 | 377 | return retval; |
fbac791a BP |
378 | } |
379 | ||
380 | static void | |
796223f5 | 381 | netdev_rx_dummy_destroy(struct netdev_rx *rx_) |
7b6b0ef4 | 382 | { |
796223f5 BP |
383 | struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_); |
384 | list_remove(&rx->node); | |
385 | ofpbuf_list_delete(&rx->recv_queue); | |
386 | free(rx); | |
387 | } | |
388 | ||
389 | static void | |
390 | netdev_rx_dummy_wait(struct netdev_rx *rx_) | |
391 | { | |
392 | struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_); | |
393 | if (!list_is_empty(&rx->recv_queue)) { | |
fbac791a BP |
394 | poll_immediate_wake(); |
395 | } | |
396 | } | |
397 | ||
398 | static int | |
796223f5 | 399 | netdev_rx_dummy_drain(struct netdev_rx *rx_) |
fbac791a | 400 | { |
796223f5 BP |
401 | struct netdev_rx_dummy *rx = netdev_rx_dummy_cast(rx_); |
402 | ofpbuf_list_delete(&rx->recv_queue); | |
e34cfdd9 | 403 | rx->recv_queue_len = 0; |
fbac791a | 404 | return 0; |
7b6b0ef4 BP |
405 | } |
406 | ||
02d5bfe3 | 407 | static int |
eab5611a | 408 | netdev_dummy_send(struct netdev *netdev, const void *buffer, size_t size) |
02d5bfe3 | 409 | { |
b5d57fc8 | 410 | struct netdev_dummy *dev = netdev_dummy_cast(netdev); |
eab5611a BP |
411 | size_t i; |
412 | ||
413 | if (size < ETH_HEADER_LEN) { | |
414 | return EMSGSIZE; | |
415 | } else { | |
416 | const struct eth_header *eth = buffer; | |
417 | int max_size; | |
418 | ||
419 | max_size = dev->mtu + ETH_HEADER_LEN; | |
420 | if (eth->eth_type == htons(ETH_TYPE_VLAN)) { | |
421 | max_size += VLAN_HEADER_LEN; | |
422 | } | |
423 | if (size > max_size) { | |
424 | return EMSGSIZE; | |
425 | } | |
426 | } | |
02d5bfe3 BP |
427 | |
428 | dev->stats.tx_packets++; | |
429 | dev->stats.tx_bytes += size; | |
430 | ||
eab5611a BP |
431 | for (i = 0; i < dev->n_streams; i++) { |
432 | struct dummy_stream *s = &dev->streams[i]; | |
433 | ||
434 | if (list_size(&s->txq) < NETDEV_DUMMY_MAX_QUEUE) { | |
435 | struct ofpbuf *b; | |
436 | ||
437 | b = ofpbuf_clone_data_with_headroom(buffer, size, 2); | |
438 | put_unaligned_be16(ofpbuf_push_uninit(b, 2), htons(size)); | |
439 | list_push_back(&s->txq, &b->list_node); | |
440 | } | |
441 | } | |
442 | ||
02d5bfe3 BP |
443 | return 0; |
444 | } | |
445 | ||
614c4892 BP |
446 | static int |
447 | netdev_dummy_set_etheraddr(struct netdev *netdev, | |
448 | const uint8_t mac[ETH_ADDR_LEN]) | |
449 | { | |
b5d57fc8 | 450 | struct netdev_dummy *dev = netdev_dummy_cast(netdev); |
614c4892 BP |
451 | |
452 | if (!eth_addr_equals(dev->hwaddr, mac)) { | |
453 | memcpy(dev->hwaddr, mac, ETH_ADDR_LEN); | |
b5d57fc8 | 454 | netdev_dummy_poll_notify(dev); |
614c4892 BP |
455 | } |
456 | ||
457 | return 0; | |
458 | } | |
459 | ||
460 | static int | |
461 | netdev_dummy_get_etheraddr(const struct netdev *netdev, | |
462 | uint8_t mac[ETH_ADDR_LEN]) | |
463 | { | |
b5d57fc8 | 464 | const struct netdev_dummy *dev = netdev_dummy_cast(netdev); |
614c4892 BP |
465 | |
466 | memcpy(mac, dev->hwaddr, ETH_ADDR_LEN); | |
467 | return 0; | |
468 | } | |
469 | ||
470 | static int | |
471 | netdev_dummy_get_mtu(const struct netdev *netdev, int *mtup) | |
472 | { | |
b5d57fc8 | 473 | const struct netdev_dummy *dev = netdev_dummy_cast(netdev); |
614c4892 BP |
474 | |
475 | *mtup = dev->mtu; | |
476 | return 0; | |
477 | } | |
478 | ||
9b020780 PS |
479 | static int |
480 | netdev_dummy_set_mtu(const struct netdev *netdev, int mtu) | |
481 | { | |
b5d57fc8 | 482 | struct netdev_dummy *dev = netdev_dummy_cast(netdev); |
9b020780 PS |
483 | |
484 | dev->mtu = mtu; | |
485 | return 0; | |
486 | } | |
487 | ||
614c4892 BP |
488 | static int |
489 | netdev_dummy_get_stats(const struct netdev *netdev, struct netdev_stats *stats) | |
490 | { | |
b5d57fc8 | 491 | const struct netdev_dummy *dev = netdev_dummy_cast(netdev); |
614c4892 BP |
492 | |
493 | *stats = dev->stats; | |
494 | return 0; | |
495 | } | |
496 | ||
497 | static int | |
498 | netdev_dummy_set_stats(struct netdev *netdev, const struct netdev_stats *stats) | |
499 | { | |
b5d57fc8 | 500 | struct netdev_dummy *dev = netdev_dummy_cast(netdev); |
614c4892 BP |
501 | |
502 | dev->stats = *stats; | |
503 | return 0; | |
504 | } | |
505 | ||
8073dd31 NM |
506 | static int |
507 | netdev_dummy_get_ifindex(const struct netdev *netdev) | |
508 | { | |
b5d57fc8 | 509 | struct netdev_dummy *dev = netdev_dummy_cast(netdev); |
8073dd31 NM |
510 | |
511 | return dev->ifindex; | |
512 | } | |
513 | ||
614c4892 | 514 | static int |
b5d57fc8 | 515 | netdev_dummy_update_flags(struct netdev *netdev_, |
614c4892 BP |
516 | enum netdev_flags off, enum netdev_flags on, |
517 | enum netdev_flags *old_flagsp) | |
518 | { | |
b5d57fc8 | 519 | struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); |
95d4ec33 | 520 | |
614c4892 BP |
521 | if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) { |
522 | return EINVAL; | |
523 | } | |
524 | ||
b5d57fc8 BP |
525 | *old_flagsp = netdev->flags; |
526 | netdev->flags |= on; | |
527 | netdev->flags &= ~off; | |
528 | if (*old_flagsp != netdev->flags) { | |
529 | netdev_dummy_poll_notify(netdev); | |
614c4892 BP |
530 | } |
531 | return 0; | |
532 | } | |
533 | ||
ac4d3bcb EJ |
534 | static unsigned int |
535 | netdev_dummy_change_seq(const struct netdev *netdev) | |
536 | { | |
b5d57fc8 | 537 | return netdev_dummy_cast(netdev)->change_seq; |
ac4d3bcb | 538 | } |
614c4892 BP |
539 | \f |
540 | /* Helper functions. */ | |
541 | ||
542 | static void | |
b5d57fc8 | 543 | netdev_dummy_poll_notify(struct netdev_dummy *dev) |
614c4892 | 544 | { |
ac4d3bcb EJ |
545 | dev->change_seq++; |
546 | if (!dev->change_seq) { | |
547 | dev->change_seq++; | |
548 | } | |
614c4892 BP |
549 | } |
550 | ||
551 | static const struct netdev_class dummy_class = { | |
552 | "dummy", | |
553 | NULL, /* init */ | |
eab5611a BP |
554 | netdev_dummy_run, |
555 | netdev_dummy_wait, | |
614c4892 BP |
556 | |
557 | netdev_dummy_create, | |
558 | netdev_dummy_destroy, | |
8073dd31 NM |
559 | netdev_dummy_get_config, |
560 | netdev_dummy_set_config, | |
f431bf7d | 561 | NULL, /* get_tunnel_config */ |
614c4892 | 562 | |
796223f5 | 563 | netdev_dummy_rx_open, |
614c4892 | 564 | |
02d5bfe3 | 565 | netdev_dummy_send, /* send */ |
614c4892 BP |
566 | NULL, /* send_wait */ |
567 | ||
568 | netdev_dummy_set_etheraddr, | |
569 | netdev_dummy_get_etheraddr, | |
570 | netdev_dummy_get_mtu, | |
9b020780 | 571 | netdev_dummy_set_mtu, |
8073dd31 | 572 | netdev_dummy_get_ifindex, |
614c4892 | 573 | NULL, /* get_carrier */ |
65c3058c | 574 | NULL, /* get_carrier_resets */ |
63331829 | 575 | NULL, /* get_miimon */ |
614c4892 BP |
576 | netdev_dummy_get_stats, |
577 | netdev_dummy_set_stats, | |
578 | ||
579 | NULL, /* get_features */ | |
580 | NULL, /* set_advertisements */ | |
614c4892 BP |
581 | |
582 | NULL, /* set_policing */ | |
583 | NULL, /* get_qos_types */ | |
584 | NULL, /* get_qos_capabilities */ | |
585 | NULL, /* get_qos */ | |
586 | NULL, /* set_qos */ | |
587 | NULL, /* get_queue */ | |
588 | NULL, /* set_queue */ | |
589 | NULL, /* delete_queue */ | |
590 | NULL, /* get_queue_stats */ | |
591 | NULL, /* dump_queues */ | |
592 | NULL, /* dump_queue_stats */ | |
593 | ||
594 | NULL, /* get_in4 */ | |
595 | NULL, /* set_in4 */ | |
596 | NULL, /* get_in6 */ | |
597 | NULL, /* add_router */ | |
598 | NULL, /* get_next_hop */ | |
275707c3 | 599 | NULL, /* get_status */ |
614c4892 BP |
600 | NULL, /* arp_lookup */ |
601 | ||
602 | netdev_dummy_update_flags, | |
603 | ||
ac4d3bcb | 604 | netdev_dummy_change_seq |
614c4892 BP |
605 | }; |
606 | ||
796223f5 BP |
607 | static const struct netdev_rx_class netdev_rx_dummy_class = { |
608 | netdev_rx_dummy_destroy, | |
609 | netdev_rx_dummy_recv, | |
610 | netdev_rx_dummy_wait, | |
611 | netdev_rx_dummy_drain, | |
612 | }; | |
613 | ||
fbac791a BP |
614 | static struct ofpbuf * |
615 | eth_from_packet_or_flow(const char *s) | |
616 | { | |
617 | enum odp_key_fitness fitness; | |
618 | struct ofpbuf *packet; | |
619 | struct ofpbuf odp_key; | |
620 | struct flow flow; | |
621 | int error; | |
622 | ||
623 | if (!eth_from_hex(s, &packet)) { | |
624 | return packet; | |
625 | } | |
626 | ||
627 | /* Convert string to datapath key. | |
628 | * | |
629 | * It would actually be nicer to parse an OpenFlow-like flow key here, but | |
630 | * the code for that currently calls exit() on parse error. We have to | |
631 | * settle for parsing a datapath key for now. | |
632 | */ | |
633 | ofpbuf_init(&odp_key, 0); | |
e6cc0bab | 634 | error = odp_flow_from_string(s, NULL, &odp_key, NULL); |
fbac791a BP |
635 | if (error) { |
636 | ofpbuf_uninit(&odp_key); | |
637 | return NULL; | |
638 | } | |
639 | ||
640 | /* Convert odp_key to flow. */ | |
641 | fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); | |
642 | if (fitness == ODP_FIT_ERROR) { | |
643 | ofpbuf_uninit(&odp_key); | |
644 | return NULL; | |
645 | } | |
646 | ||
647 | packet = ofpbuf_new(0); | |
648 | flow_compose(packet, &flow); | |
649 | ||
650 | ofpbuf_uninit(&odp_key); | |
651 | return packet; | |
652 | } | |
653 | ||
323cc924 | 654 | static void |
eab5611a | 655 | netdev_dummy_queue_packet__(struct netdev_rx_dummy *rx, struct ofpbuf *packet) |
2273ea5a | 656 | { |
eab5611a BP |
657 | list_push_back(&rx->recv_queue, &packet->list_node); |
658 | rx->recv_queue_len++; | |
659 | } | |
2273ea5a | 660 | |
eab5611a BP |
661 | static void |
662 | netdev_dummy_queue_packet(struct netdev_dummy *dummy, struct ofpbuf *packet) | |
663 | { | |
664 | struct netdev_rx_dummy *rx, *prev; | |
665 | ||
666 | prev = NULL; | |
2273ea5a BP |
667 | LIST_FOR_EACH (rx, node, &dummy->rxes) { |
668 | if (rx->recv_queue_len < NETDEV_DUMMY_MAX_QUEUE) { | |
eab5611a BP |
669 | if (prev) { |
670 | netdev_dummy_queue_packet__(prev, ofpbuf_clone(packet)); | |
671 | } | |
672 | prev = rx; | |
2273ea5a BP |
673 | } |
674 | } | |
eab5611a BP |
675 | if (prev) { |
676 | netdev_dummy_queue_packet__(prev, packet); | |
677 | } else { | |
678 | ofpbuf_delete(packet); | |
679 | } | |
2273ea5a BP |
680 | } |
681 | ||
fbac791a BP |
682 | static void |
683 | netdev_dummy_receive(struct unixctl_conn *conn, | |
684 | int argc, const char *argv[], void *aux OVS_UNUSED) | |
685 | { | |
b5d57fc8 | 686 | struct netdev_dummy *dummy_dev; |
86f1d032 | 687 | struct netdev *netdev; |
fbac791a BP |
688 | int i; |
689 | ||
86f1d032 BP |
690 | netdev = netdev_from_name(argv[1]); |
691 | if (!netdev || !is_dummy_class(netdev->netdev_class)) { | |
bde9f75d | 692 | unixctl_command_reply_error(conn, "no such dummy netdev"); |
86f1d032 | 693 | goto exit; |
fbac791a | 694 | } |
86f1d032 | 695 | dummy_dev = netdev_dummy_cast(netdev); |
fbac791a | 696 | |
fbac791a | 697 | for (i = 2; i < argc; i++) { |
fbac791a BP |
698 | struct ofpbuf *packet; |
699 | ||
700 | packet = eth_from_packet_or_flow(argv[i]); | |
701 | if (!packet) { | |
bde9f75d | 702 | unixctl_command_reply_error(conn, "bad packet syntax"); |
86f1d032 | 703 | goto exit; |
fbac791a BP |
704 | } |
705 | ||
02d5bfe3 BP |
706 | dummy_dev->stats.rx_packets++; |
707 | dummy_dev->stats.rx_bytes += packet->size; | |
708 | ||
eab5611a | 709 | netdev_dummy_queue_packet(dummy_dev, packet); |
fbac791a BP |
710 | } |
711 | ||
323cc924 | 712 | unixctl_command_reply(conn, NULL); |
86f1d032 BP |
713 | |
714 | exit: | |
715 | netdev_close(netdev); | |
fbac791a BP |
716 | } |
717 | ||
95d4ec33 | 718 | static void |
b5d57fc8 | 719 | netdev_dummy_set_admin_state__(struct netdev_dummy *dev, bool admin_state) |
95d4ec33 EJ |
720 | { |
721 | enum netdev_flags old_flags; | |
722 | ||
723 | if (admin_state) { | |
b5d57fc8 | 724 | netdev_dummy_update_flags(&dev->up, 0, NETDEV_UP, &old_flags); |
95d4ec33 | 725 | } else { |
b5d57fc8 | 726 | netdev_dummy_update_flags(&dev->up, NETDEV_UP, 0, &old_flags); |
95d4ec33 EJ |
727 | } |
728 | } | |
729 | ||
730 | static void | |
731 | netdev_dummy_set_admin_state(struct unixctl_conn *conn, int argc, | |
732 | const char *argv[], void *aux OVS_UNUSED) | |
733 | { | |
734 | bool up; | |
735 | ||
736 | if (!strcasecmp(argv[argc - 1], "up")) { | |
737 | up = true; | |
738 | } else if ( !strcasecmp(argv[argc - 1], "down")) { | |
739 | up = false; | |
740 | } else { | |
741 | unixctl_command_reply_error(conn, "Invalid Admin State"); | |
742 | return; | |
743 | } | |
744 | ||
745 | if (argc > 2) { | |
86f1d032 BP |
746 | struct netdev *netdev = netdev_from_name(argv[1]); |
747 | if (netdev && is_dummy_class(netdev->netdev_class)) { | |
748 | struct netdev_dummy *dummy_dev = netdev_dummy_cast(netdev); | |
95d4ec33 | 749 | |
b5d57fc8 | 750 | netdev_dummy_set_admin_state__(dummy_dev, up); |
86f1d032 | 751 | netdev_close(netdev); |
95d4ec33 EJ |
752 | } else { |
753 | unixctl_command_reply_error(conn, "Unknown Dummy Interface"); | |
86f1d032 | 754 | netdev_close(netdev); |
95d4ec33 EJ |
755 | return; |
756 | } | |
757 | } else { | |
86f1d032 | 758 | struct shash dummy_netdevs; |
95d4ec33 EJ |
759 | struct shash_node *node; |
760 | ||
86f1d032 BP |
761 | shash_init(&dummy_netdevs); |
762 | netdev_get_devices(&dummy_class, &dummy_netdevs); | |
b5d57fc8 | 763 | SHASH_FOR_EACH (node, &dummy_netdevs) { |
86f1d032 BP |
764 | struct netdev *netdev = node->data; |
765 | netdev_dummy_set_admin_state__(netdev_dummy_cast(netdev), up); | |
766 | netdev_close(netdev); | |
95d4ec33 | 767 | } |
86f1d032 | 768 | shash_destroy(&dummy_netdevs); |
95d4ec33 EJ |
769 | } |
770 | unixctl_command_reply(conn, "OK"); | |
771 | } | |
772 | ||
614c4892 | 773 | void |
0cbfe35d | 774 | netdev_dummy_register(bool override) |
614c4892 | 775 | { |
fbac791a BP |
776 | unixctl_command_register("netdev-dummy/receive", "NAME PACKET|FLOW...", |
777 | 2, INT_MAX, netdev_dummy_receive, NULL); | |
95d4ec33 EJ |
778 | unixctl_command_register("netdev-dummy/set-admin-state", |
779 | "[netdev] up|down", 1, 2, | |
780 | netdev_dummy_set_admin_state, NULL); | |
0cbfe35d BP |
781 | |
782 | if (override) { | |
783 | struct sset types; | |
784 | const char *type; | |
785 | ||
786 | sset_init(&types); | |
787 | netdev_enumerate_types(&types); | |
788 | SSET_FOR_EACH (type, &types) { | |
789 | if (!netdev_unregister_provider(type)) { | |
790 | struct netdev_class *class; | |
791 | ||
792 | class = xmalloc(sizeof *class); | |
793 | *class = dummy_class; | |
794 | class->type = xstrdup(type); | |
795 | netdev_register_provider(class); | |
796 | } | |
797 | } | |
798 | sset_destroy(&types); | |
799 | } | |
800 | netdev_register_provider(&dummy_class); | |
c060c4cf | 801 | |
7c54c27f | 802 | netdev_vport_tunnel_register(); |
614c4892 | 803 | } |