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