Signed-off-by: Ben Pfaff <b...@nicira.com> --- lib/netdev-dummy.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 170 insertions(+), 1 deletions(-)
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index 4790492..e9d9a9d 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -35,12 +35,20 @@ #include "poll-loop.h" #include "shash.h" #include "sset.h" +#include "stream.h" #include "timeval.h" +#include "unaligned.h" #include "unixctl.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(netdev_dummy); +struct dummy_stream { + struct stream *stream; + struct ofpbuf rxbuf; + struct list txq; +}; + struct netdev_dev_dummy { struct netdev_dev netdev_dev; uint8_t hwaddr[ETH_ADDR_LEN]; @@ -53,6 +61,10 @@ struct netdev_dev_dummy { struct pktgen **pktgens; size_t n_pktgens; + struct pstream *pstream; + struct dummy_stream *streams; + size_t n_streams; + FILE *tx_pcap, *rx_pcap; struct list devs; /* List of child "netdev_dummy"s. */ @@ -93,6 +105,8 @@ static int netdev_dev_dummy_update_flags(struct netdev_dev_dummy *, static void netdev_dummy_queue_packet(struct netdev_dev_dummy *, struct ofpbuf *); +static void dummy_stream_close(struct dummy_stream *); + static bool is_dummy_class(const struct netdev_class *class) { @@ -139,10 +153,109 @@ netdev_dummy_run(void) dev->pktgens[i--] = dev->pktgens[--dev->n_pktgens]; } } + + if (dev->pstream) { + struct stream *new_stream; + int error; + + error = pstream_accept(dev->pstream, &new_stream); + if (!error) { + struct dummy_stream *s; + + dev->streams = xrealloc(dev->streams, + ((dev->n_streams + 1) + * sizeof *dev->streams)); + s = &dev->streams[dev->n_streams++]; + s->stream = new_stream; + ofpbuf_init(&s->rxbuf, 2048); + list_init(&s->txq); + } else if (error != EAGAIN) { + VLOG_WARN("%s: accept failed (%s)", + pstream_get_name(dev->pstream), strerror(error)); + pstream_close(dev->pstream); + dev->pstream = NULL; + } + } + + for (i = 0; i < dev->n_streams; i++) { + struct dummy_stream *s = &dev->streams[i]; + int error = 0; + size_t n; + + stream_run(s->stream); + + if (!list_is_empty(&s->txq)) { + struct ofpbuf *txbuf; + int retval; + + txbuf = ofpbuf_from_list(list_front(&s->txq)); + retval = stream_send(s->stream, txbuf->data, txbuf->size); + if (retval > 0) { + ofpbuf_pull(txbuf, retval); + if (!txbuf->size) { + list_remove(&txbuf->list_node); + ofpbuf_delete(txbuf); + } + } else if (retval != -EAGAIN) { + error = -retval; + } + } + + if (!error) { + if (s->rxbuf.size < 2) { + n = 2 - s->rxbuf.size; + } else { + uint16_t frame_len; + + frame_len = ntohs(get_unaligned_be16(s->rxbuf.data)); + if (frame_len < ETH_HEADER_LEN) { + error = EPROTO; + n = 0; + } else { + n = (2 + frame_len) - s->rxbuf.size; + } + } + } + if (!error) { + int retval; + + ofpbuf_prealloc_tailroom(&s->rxbuf, n); + retval = stream_recv(s->stream, ofpbuf_tail(&s->rxbuf), n); + if (retval > 0) { + s->rxbuf.size += retval; + if (retval == n && s->rxbuf.size > 2) { + ofpbuf_pull(&s->rxbuf, 2); + netdev_dummy_queue_packet(dev, + ofpbuf_clone(&s->rxbuf)); + ofpbuf_clear(&s->rxbuf); + } + } else if (retval != -EAGAIN) { + error = (retval < 0 ? -retval + : s->rxbuf.size ? EPROTO + : EOF); + } + } + + if (error) { + VLOG_DBG("%s: closing connection (%s)", + stream_get_name(s->stream), + ovs_retval_to_string(error)); + dummy_stream_close(&dev->streams[i]); + dev->streams[i] = dev->streams[--dev->n_streams]; + } + } } } static void +dummy_stream_close(struct dummy_stream *s) +{ + stream_close(s->stream); + ofpbuf_uninit(&s->rxbuf); + ofpbuf_list_delete(&s->txq); +} + +static void netdev_dummy_wait(void) { struct shash_node *node; @@ -154,6 +267,18 @@ netdev_dummy_wait(void) for (i = 0; i < dev->n_pktgens; i++) { pktgen_wait(dev->pktgens[i]); } + if (dev->pstream) { + pstream_wait(dev->pstream); + } + for (i = 0; i < dev->n_streams; i++) { + struct dummy_stream *s = &dev->streams[i]; + + stream_run_wait(s->stream); + if (!list_is_empty(&s->txq)) { + stream_send_wait(s->stream); + } + stream_recv_wait(s->stream); + } } } @@ -177,6 +302,10 @@ netdev_dummy_create(const struct netdev_class *class, const char *name, netdev_dev->change_seq = 1; list_init(&netdev_dev->devs); + netdev_dev->pstream = NULL; + netdev_dev->streams = NULL; + netdev_dev->n_streams = 0; + shash_add(&dummy_netdev_devs, name, netdev_dev); n++; @@ -199,6 +328,11 @@ netdev_dummy_destroy(struct netdev_dev *netdev_dev_) } free(netdev_dev->pktgens); free(netdev_dev->peer); + pstream_close(netdev_dev->pstream); + for (i = 0; i < netdev_dev->n_streams; i++) { + dummy_stream_close(&netdev_dev->streams[i]); + } + free(netdev_dev->streams); free(netdev_dev); } @@ -209,6 +343,7 @@ netdev_dummy_set_config(struct netdev_dev *netdev_dev_, struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_); const char *name = netdev_dev_get_name(netdev_dev_); const struct smap_node *node; + const char *pstream; const char *peer; const char *pcap; @@ -216,7 +351,8 @@ netdev_dummy_set_config(struct netdev_dev *netdev_dev_, if (strcmp(node->key, "peer") && strcmp(node->key, "pcap") && strcmp(node->key, "rx_pcap") - && strcmp(node->key, "tx_pcap")) { + && strcmp(node->key, "tx_pcap") + && strcmp(node->key, "pstream")) { VLOG_WARN("%s: unknown option '%s'", name, node->key); return EINVAL; } @@ -248,6 +384,23 @@ netdev_dummy_set_config(struct netdev_dev *netdev_dev_, } } + pstream = smap_get(args, "pstream"); + if (!pstream + || !netdev_dev->pstream + || strcmp(pstream_get_name(netdev_dev->pstream), pstream)) { + pstream_close(netdev_dev->pstream); + netdev_dev->pstream = NULL; + + if (pstream) { + int error; + + error = pstream_open(pstream, &netdev_dev->pstream, DSCP_DEFAULT); + if (error) { + VLOG_WARN("%s: open failed (%s)", pstream, strerror(error)); + } + } + } + return 0; } @@ -259,6 +412,9 @@ netdev_dummy_get_config(struct netdev_dev *netdev_dev_, struct smap *args) if (netdev_dev->peer) { smap_add(args, "peer", netdev_dev->peer); } + if (netdev_dev->pstream) { + smap_add(args, "pstream", pstream_get_name(netdev_dev->pstream)); + } return 0; } @@ -343,6 +499,7 @@ netdev_dummy_send(struct netdev *netdev, const void *buffer, size_t size) { struct netdev_dev_dummy *dev = netdev_dev_dummy_cast(netdev_get_dev(netdev)); + size_t i; if (size < ETH_HEADER_LEN) { return EMSGSIZE; @@ -383,6 +540,18 @@ netdev_dummy_send(struct netdev *netdev, const void *buffer, size_t size) } } + for (i = 0; i < dev->n_streams; i++) { + struct dummy_stream *s = &dev->streams[i]; + + if (list_size(&s->txq) < NETDEV_DUMMY_MAX_QUEUE) { + struct ofpbuf *b; + + b = ofpbuf_clone_data_with_headroom(buffer, size, 2); + put_unaligned_be16(ofpbuf_push_uninit(b, 2), htons(size)); + list_push_back(&s->txq, &b->list_node); + } + } + return 0; } -- 1.7.2.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev