/*
- * Copyright (c) 2010, 2011, 2012, 2013, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 2012, 2013, 2015, 2016, 2017 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "ovs-atomic.h"
#include "packets.h"
#include "pcap-file.h"
-#include "poll-loop.h"
-#include "shash.h"
+#include "openvswitch/poll-loop.h"
+#include "openvswitch/shash.h"
#include "sset.h"
#include "stream.h"
#include "unaligned.h"
struct dummy_packet_stream {
struct stream *stream;
- struct dp_packet rxbuf;
struct ovs_list txq;
+ struct dp_packet rxbuf;
};
enum dummy_packet_conn_type {
}
static void
-netdev_dummy_run(void)
+netdev_dummy_run(const struct netdev_class *netdev_class)
{
struct netdev_dummy *dev;
ovs_mutex_lock(&dummy_list_mutex);
LIST_FOR_EACH (dev, list_node, &dummy_list) {
+ if (netdev_get_class(&dev->up) != netdev_class) {
+ continue;
+ }
ovs_mutex_lock(&dev->mutex);
dummy_packet_conn_run(dev);
ovs_mutex_unlock(&dev->mutex);
}
static void
-netdev_dummy_wait(void)
+netdev_dummy_wait(const struct netdev_class *netdev_class)
{
struct netdev_dummy *dev;
ovs_mutex_lock(&dummy_list_mutex);
LIST_FOR_EACH (dev, list_node, &dummy_list) {
+ if (netdev_get_class(&dev->up) != netdev_class) {
+ continue;
+ }
ovs_mutex_lock(&dev->mutex);
dummy_packet_conn_wait(&dev->conn);
ovs_mutex_unlock(&dev->mutex);
ovs_mutex_unlock(&dummy_list_mutex);
ovs_mutex_lock(&netdev->mutex);
+ if (netdev->rxq_pcap) {
+ fclose(netdev->rxq_pcap);
+ }
+ if (netdev->tx_pcap && netdev->tx_pcap != netdev->rxq_pcap) {
+ fclose(netdev->tx_pcap);
+ }
dummy_packet_conn_close(&netdev->conn);
netdev->conn.type = NONE;
return 0;
}
+#define DUMMY_MAX_QUEUES_PER_PORT 1024
+
static int
-netdev_dummy_set_config(struct netdev *netdev_, const struct smap *args)
+netdev_dummy_set_config(struct netdev *netdev_, const struct smap *args,
+ char **errp OVS_UNUSED)
{
struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
const char *pcap;
goto exit;
}
- new_n_rxq = MAX(smap_get_int(args, "n_rxq", netdev->requested_n_rxq), 1);
- new_n_txq = MAX(smap_get_int(args, "n_txq", netdev->requested_n_txq), 1);
+ new_n_rxq = MAX(smap_get_int(args, "n_rxq", NR_QUEUE), 1);
+ new_n_txq = MAX(smap_get_int(args, "n_txq", NR_QUEUE), 1);
+
+ if (new_n_rxq > DUMMY_MAX_QUEUES_PER_PORT ||
+ new_n_txq > DUMMY_MAX_QUEUES_PER_PORT) {
+ VLOG_WARN("The one or both of interface %s queues"
+ "(rxq: %d, txq: %d) exceed %d. Sets it %d.\n",
+ netdev_get_name(netdev_),
+ new_n_rxq,
+ new_n_txq,
+ DUMMY_MAX_QUEUES_PER_PORT,
+ DUMMY_MAX_QUEUES_PER_PORT);
+
+ new_n_rxq = MIN(DUMMY_MAX_QUEUES_PER_PORT, new_n_rxq);
+ new_n_txq = MIN(DUMMY_MAX_QUEUES_PER_PORT, new_n_txq);
+ }
+
new_numa_id = smap_get_int(args, "numa_id", 0);
if (new_n_rxq != netdev->requested_n_rxq
|| new_n_txq != netdev->requested_n_txq
netdev->stats.rx_bytes += dp_packet_size(packet);
ovs_mutex_unlock(&netdev->mutex);
- dp_packet_pad(packet);
-
batch->packets[0] = packet;
batch->count = 1;
return 0;
static int
netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
- struct dp_packet_batch *batch, bool may_steal)
+ struct dp_packet_batch *batch,
+ bool concurrent_txq OVS_UNUSED)
{
struct netdev_dummy *dev = netdev_dummy_cast(netdev);
int error = 0;
- int i;
- for (i = 0; i < batch->count; i++) {
- const void *buffer = dp_packet_data(batch->packets[i]);
- size_t size = dp_packet_size(batch->packets[i]);
+ struct dp_packet *packet;
+ DP_PACKET_BATCH_FOR_EACH(packet, batch) {
+ const void *buffer = dp_packet_data(packet);
+ size_t size = dp_packet_size(packet);
- size -= dp_packet_get_cutlen(batch->packets[i]);
+ if (batch->packets[i]->packet_type != htonl(PT_ETH)) {
+ error = EPFNOSUPPORT;
+ break;
+ }
if (size < ETH_HEADER_LEN) {
error = EMSGSIZE;
/* Reply to ARP requests for 'dev''s assigned IP address. */
if (dev->address.s_addr) {
- struct dp_packet packet;
+ struct dp_packet dp;
struct flow flow;
- dp_packet_use_const(&packet, buffer, size);
- flow_extract(&packet, &flow);
+ dp_packet_use_const(&dp, buffer, size);
+ flow_extract(&dp, &flow);
if (flow.dl_type == htons(ETH_TYPE_ARP)
&& flow.nw_proto == ARP_OP_REQUEST
&& flow.nw_dst == dev->address.s_addr) {
}
if (dev->tx_pcap) {
- struct dp_packet packet;
+ struct dp_packet dp;
- dp_packet_use_const(&packet, buffer, size);
- ovs_pcap_write(dev->tx_pcap, &packet);
+ dp_packet_use_const(&dp, buffer, size);
+ ovs_pcap_write(dev->tx_pcap, &dp);
fflush(dev->tx_pcap);
}
ovs_mutex_unlock(&dev->mutex);
}
- dp_packet_delete_batch(batch, may_steal);
+ dp_packet_delete_batch(batch, true);
return error;
}
return 0;
}
+#define DUMMY_MIN_MTU 68
+#define DUMMY_MAX_MTU 65535
+
static int
-netdev_dummy_set_mtu(const struct netdev *netdev, int mtu)
+netdev_dummy_set_mtu(struct netdev *netdev, int mtu)
{
+ if (mtu < DUMMY_MIN_MTU || mtu > DUMMY_MAX_MTU) {
+ return EINVAL;
+ }
+
struct netdev_dummy *dev = netdev_dummy_cast(netdev);
ovs_mutex_lock(&dev->mutex);
- dev->mtu = mtu;
+ if (dev->mtu != mtu) {
+ dev->mtu = mtu;
+ netdev_change_seq_changed(netdev);
+ }
ovs_mutex_unlock(&dev->mutex);
return 0;
\
NULL, /* get_features */ \
NULL, /* set_advertisements */ \
+ NULL, /* get_pt_mode */ \
\
NULL, /* set_policing */ \
NULL, /* get_qos_types */ \
netdev_dummy_rxq_recv, \
netdev_dummy_rxq_wait, \
netdev_dummy_rxq_drain, \
+ \
+ NO_OFFLOAD_API \
}
static const struct netdev_class dummy_class =
NETDEV_DUMMY_CLASS("dummy", false, NULL);
+static const struct netdev_class dummy_internal_class =
+ NETDEV_DUMMY_CLASS("dummy-internal", false, NULL);
+
static const struct netdev_class dummy_pmd_class =
NETDEV_DUMMY_CLASS("dummy-pmd", true,
netdev_dummy_reconfigure);
}
static struct dp_packet *
-eth_from_packet_or_flow(const char *s)
+eth_from_packet(const char *s)
+{
+ struct dp_packet *packet;
+ eth_from_hex(s, &packet);
+ return packet;
+}
+
+static struct dp_packet *
+eth_from_flow(const char *s, size_t packet_size)
{
enum odp_key_fitness fitness;
struct dp_packet *packet;
struct flow flow;
int error;
- if (!eth_from_hex(s, &packet)) {
- return packet;
- }
-
/* Convert string to datapath key.
*
* It would actually be nicer to parse an OpenFlow-like flow key here, but
}
packet = dp_packet_new(0);
- flow_compose(packet, &flow);
+ if (!flow_compose(packet, &flow, packet_size)) {
+ dp_packet_delete(packet);
+ packet = NULL;
+ };
ofpbuf_uninit(&odp_key);
return packet;
for (i = k; i < argc; i++) {
struct dp_packet *packet;
- packet = eth_from_packet_or_flow(argv[i]);
+ /* Try to parse 'argv[i]' as packet in hex. */
+ packet = eth_from_packet(argv[i]);
+
if (!packet) {
- unixctl_command_reply_error(conn, "bad packet syntax");
- goto exit;
+ int packet_size = 0;
+ const char *flow_str = argv[i];
+
+ /* Parse optional --len argument immediately follows a 'flow'. */
+ if (argc >= i + 2 && !strcmp(argv[i + 1], "--len")) {
+ packet_size = strtol(argv[i + 2], NULL, 10);
+
+ if (packet_size < ETH_TOTAL_MIN) {
+ unixctl_command_reply_error(conn, "too small packet len");
+ goto exit;
+ }
+ i += 2;
+ }
+ /* Try parse 'argv[i]' as odp flow. */
+ packet = eth_from_flow(flow_str, packet_size);
+
+ if (!packet) {
+ unixctl_command_reply_error(conn, "bad packet or flow syntax");
+ goto exit;
+ }
}
netdev_dummy_queue_packet(dummy_dev, packet, rx_qid);
unixctl_command_reply_error(conn, error);
free(error);
}
- netdev_close(netdev);
} else {
unixctl_command_reply_error(conn, "Unknown Dummy Interface");
}
netdev_dummy_register(enum dummy_level level)
{
unixctl_command_register("netdev-dummy/receive",
- "name [--qid queue_id] packet|flow...",
+ "name [--qid queue_id] packet|flow [--len packet_len]",
2, INT_MAX, netdev_dummy_receive, NULL);
unixctl_command_register("netdev-dummy/set-admin-state",
"[netdev] up|down", 1, 2,
netdev_dummy_override("system");
}
netdev_register_provider(&dummy_class);
+ netdev_register_provider(&dummy_internal_class);
netdev_register_provider(&dummy_pmd_class);
netdev_vport_tunnel_register();