#include "packets.h"
#include "poll-loop.h"
#include "rconn.h"
+#include "stp.h"
#include "vconn.h"
#include "table.h"
#include "timeval.h"
#define THIS_MODULE VLM_datapath
#include "vlog.h"
-#define BRIDGE_PORT_NO_FLOOD 0x00000001
+extern char mfr_desc;
+extern char hw_desc;
+extern char sw_desc;
+extern char serial_num;
/* Capabilities supported by this implementation. */
#define OFP_SUPPORTED_CAPABILITIES ( OFPC_FLOW_STATS \
| (1 << OFPAT_SET_TP_SRC) \
| (1 << OFPAT_SET_TP_DST) )
+#define PORT_STATUS_BITS (OFPPFL_PORT_DOWN | OFPPFL_LINK_DOWN)
+#define PORT_FLAG_BITS (~PORT_STATUS_BITS)
+
struct sw_port {
- uint32_t flags;
+ uint32_t flags; /* Some subset of PORT_FLAG_BITS. */
+ uint32_t status; /* Some subset of PORT_STATUS_BITS. */
struct datapath *dp;
struct netdev *netdev;
struct list node; /* Element in datapath.ports. */
- unsigned long long int rx_count, tx_count, drop_count;
+ unsigned long long int rx_packets, tx_packets;
+ unsigned long long int rx_bytes, tx_bytes;
+ unsigned long long int tx_dropped;
};
/* The origin of a received OpenFlow message, to enable sending a reply. */
/* Remote connections. */
struct remote *controller; /* Connection to controller. */
struct list remotes; /* All connections (including controller). */
- struct vconn *listen_vconn;
+ struct pvconn *listen_pvconn;
time_t last_timeout;
struct list port_list; /* List of ports, for flooding. */
};
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60);
+
static struct remote *remote_create(struct datapath *, struct rconn *);
static void remote_run(struct datapath *, struct remote *);
static void remote_wait(struct remote *);
static void remote_destroy(struct remote *);
void dp_output_port(struct datapath *, struct buffer *,
- int in_port, int out_port);
-void dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
+ int in_port, int out_port, bool ignore_no_fwd);
+void dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm);
void dp_output_control(struct datapath *, struct buffer *, int in_port,
size_t max_len, int reason);
static void send_flow_expired(struct datapath *, struct sw_flow *,
enum ofp_flow_expired_reason);
+static int update_port_status(struct sw_port *p);
static void send_port_status(struct sw_port *p, uint8_t status);
static void del_switch_port(struct sw_port *p);
static void execute_actions(struct datapath *, struct buffer *,
int in_port, const struct sw_flow_key *,
- const struct ofp_action *, int n_actions);
+ const struct ofp_action *, int n_actions,
+ bool ignore_no_fwd);
static void modify_vlan(struct buffer *buffer, const struct sw_flow_key *key,
const struct ofp_action *a);
static void modify_nh(struct buffer *buffer, uint16_t eth_proto,
#define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS)
-int run_flow_through_tables(struct datapath *, struct buffer *, int in_port);
-void fwd_port_input(struct datapath *, struct buffer *, int in_port);
+int run_flow_through_tables(struct datapath *, struct buffer *,
+ struct sw_port *);
+void fwd_port_input(struct datapath *, struct buffer *, struct sw_port *);
int fwd_control_input(struct datapath *, const struct sender *,
const void *, size_t);
dp->last_timeout = time_now();
list_init(&dp->remotes);
dp->controller = remote_create(dp, rconn);
- dp->listen_vconn = NULL;
+ dp->listen_pvconn = NULL;
dp->id = dpid <= UINT64_C(0xffffffffffff) ? dpid : gen_datapath_id();
dp->chain = chain_create();
if (!dp->chain) {
}
error = netdev_set_flags(netdev, NETDEV_UP | NETDEV_PROMISC, false);
if (error) {
- VLOG_ERR("Couldn't set promiscuous mode on %s device", name);
+ VLOG_ERR("couldn't set promiscuous mode on %s device", name);
netdev_close(netdev);
return error;
}
}
}
+ memset(p, '\0', sizeof *p);
+
p->dp = dp;
p->netdev = netdev;
- p->tx_count = 0;
- p->rx_count = 0;
- p->drop_count = 0;
list_push_back(&dp->port_list, &p->node);
/* Notify the ctlpath that this port has been added */
}
void
-dp_add_listen_vconn(struct datapath *dp, struct vconn *listen_vconn)
+dp_add_listen_pvconn(struct datapath *dp, struct pvconn *listen_pvconn)
{
- assert(!dp->listen_vconn);
- dp->listen_vconn = listen_vconn;
+ assert(!dp->listen_pvconn);
+ dp->listen_pvconn = listen_pvconn;
}
void
struct list deleted = LIST_INITIALIZER(&deleted);
struct sw_flow *f, *n;
+ LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
+ if (update_port_status(p)) {
+ send_port_status(p, OFPPR_MOD);
+ }
+ }
+
chain_timeout(dp->chain, &deleted);
LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) {
send_flow_expired(dp, f, f->reason);
}
error = netdev_recv(p->netdev, buffer);
if (!error) {
- p->rx_count++;
- fwd_port_input(dp, buffer, port_no(dp, p));
+ p->rx_packets++;
+ p->rx_bytes += buffer->size;
+ fwd_port_input(dp, buffer, p);
buffer = NULL;
} else if (error != EAGAIN) {
- VLOG_ERR("Error receiving data from %s: %s",
- netdev_get_name(p->netdev), strerror(error));
- del_switch_port(p);
+ VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
+ netdev_get_name(p->netdev), strerror(error));
}
}
buffer_delete(buffer);
LIST_FOR_EACH_SAFE (r, rn, struct remote, node, &dp->remotes) {
remote_run(dp, r);
}
- if (dp->listen_vconn) {
+ if (dp->listen_pvconn) {
for (;;) {
struct vconn *new_vconn;
int retval;
- retval = vconn_accept(dp->listen_vconn, &new_vconn);
+ retval = pvconn_accept(dp->listen_pvconn, OFP_VERSION, &new_vconn);
if (retval) {
if (retval != EAGAIN) {
- VLOG_WARN("accept failed (%s)", strerror(retval));
+ VLOG_WARN_RL(&rl, "accept failed (%s)", strerror(retval));
}
break;
}
sender.xid = oh->xid;
fwd_control_input(dp, &sender, buffer->data, buffer->size);
} else {
- VLOG_WARN("received too-short OpenFlow message");
+ VLOG_WARN_RL(&rl, "received too-short OpenFlow message");
}
buffer_delete(buffer);
} else {
int error = r->cb_dump(dp, r->cb_aux);
if (error <= 0) {
if (error) {
- VLOG_WARN("dump callback error: %s", strerror(-error));
+ VLOG_WARN_RL(&rl, "dump callback error: %s",
+ strerror(-error));
}
r->cb_done(r->cb_aux);
r->cb_dump = NULL;
list_push_back(&dp->remotes, &remote->node);
remote->rconn = rconn;
remote->cb_dump = NULL;
+ remote->n_txq = 0;
return remote;
}
LIST_FOR_EACH (r, struct remote, node, &dp->remotes) {
remote_wait(r);
}
- if (dp->listen_vconn) {
- vconn_accept_wait(dp->listen_vconn);
+ if (dp->listen_pvconn) {
+ pvconn_wait(dp->listen_pvconn);
}
}
if (port_no(dp, p) == in_port) {
continue;
}
- if (flood && p->flags & BRIDGE_PORT_NO_FLOOD) {
+ if (flood && p->flags & OFPPFL_NO_FLOOD) {
continue;
}
if (prev_port != -1) {
- dp_output_port(dp, buffer_clone(buffer), in_port, prev_port);
+ dp_output_port(dp, buffer_clone(buffer), in_port, prev_port,
+ false);
}
prev_port = port_no(dp, p);
}
if (prev_port != -1)
- dp_output_port(dp, buffer, in_port, prev_port);
+ dp_output_port(dp, buffer, in_port, prev_port, false);
else
buffer_delete(buffer);
{
if (out_port >= 0 && out_port < OFPP_MAX) {
struct sw_port *p = &dp->ports[out_port];
- if (p->netdev != NULL) {
+ if (p->netdev != NULL && !(p->status & OFPPFL_PORT_DOWN)) {
if (!netdev_send(p->netdev, buffer)) {
- p->tx_count++;
+ p->tx_packets++;
+ p->tx_bytes += buffer->size;
} else {
- p->drop_count++;
+ p->tx_dropped++;
}
return;
}
}
buffer_delete(buffer);
- /* FIXME: ratelimit */
- VLOG_DBG("can't forward to bad port %d\n", out_port);
+ VLOG_DBG_RL(&rl, "can't forward to bad port %d\n", out_port);
}
/* Takes ownership of 'buffer' and transmits it to 'out_port' on 'dp'.
*/
void
dp_output_port(struct datapath *dp, struct buffer *buffer,
- int in_port, int out_port)
+ int in_port, int out_port, bool ignore_no_fwd)
{
assert(buffer);
output_all(dp, buffer, in_port, 0);
} else if (out_port == OFPP_CONTROLLER) {
dp_output_control(dp, buffer, in_port, 0, OFPR_ACTION);
+ } else if (out_port == OFPP_IN_PORT) {
+ output_packet(dp, buffer, in_port);
} else if (out_port == OFPP_TABLE) {
- if (run_flow_through_tables(dp, buffer, in_port)) {
+ struct sw_port *p = in_port < OFPP_MAX ? &dp->ports[in_port] : 0;
+ if (run_flow_through_tables(dp, buffer, p)) {
buffer_delete(buffer);
}
} else {
+ if (in_port == out_port) {
+ VLOG_DBG_RL(&rl, "can't directly forward to input port");
+ return;
+ }
output_packet(dp, buffer, out_port);
}
}
int retval;
update_openflow_length(buffer);
- retval = (remote->n_txq < TXQ_LIMIT
- ? rconn_send(rconn, buffer, &remote->n_txq)
- : EAGAIN);
+ retval = rconn_send_with_limit(rconn, buffer, &remote->n_txq, TXQ_LIMIT);
if (retval) {
- VLOG_WARN("send to %s failed: %s",
- rconn_get_name(rconn), strerror(retval));
- buffer_delete(buffer);
+ VLOG_WARN_RL(&rl, "send to %s failed: %s",
+ rconn_get_name(rconn), strerror(retval));
}
return retval;
}
sizeof desc->name);
desc->name[sizeof desc->name - 1] = '\0';
memcpy(desc->hw_addr, netdev_get_etheraddr(p->netdev), ETH_ADDR_LEN);
- desc->flags = htonl(p->flags);
+ desc->flags = 0;
desc->features = htonl(netdev_get_features(p->netdev));
desc->speed = htonl(netdev_get_speed(p->netdev));
+ desc->flags = htonl(p->flags | p->status);
}
static void
ofr = make_openflow_reply(sizeof *ofr, OFPT_FEATURES_REPLY,
sender, &buffer);
- ofr->datapath_id = htonll(dp->id);
- ofr->n_exact = htonl(2 * TABLE_HASH_MAX_FLOWS);
- ofr->n_compression = 0; /* Not supported */
- ofr->n_general = htonl(TABLE_LINEAR_MAX_FLOWS);
- ofr->buffer_mb = htonl(UINT32_MAX);
- ofr->n_buffers = htonl(N_PKT_BUFFERS);
- ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES);
- ofr->actions = htonl(OFP_SUPPORTED_ACTIONS);
+ ofr->datapath_id = htonll(dp->id);
+ ofr->n_tables = dp->chain->n_tables;
+ ofr->n_buffers = htonl(N_PKT_BUFFERS);
+ ofr->capabilities = htonl(OFP_SUPPORTED_CAPABILITIES);
+ ofr->actions = htonl(OFP_SUPPORTED_ACTIONS);
LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
struct ofp_phy_port *opp = buffer_put_uninit(buffer, sizeof *opp);
memset(opp, 0, sizeof *opp);
}
void
-dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp)
+dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm)
{
+ const struct ofp_phy_port *opp = &opm->desc;
int port_no = ntohs(opp->port_no);
if (port_no < OFPP_MAX) {
struct sw_port *p = &dp->ports[port_no];
+ uint32_t flag_mask;
/* Make sure the port id hasn't changed since this was sent */
if (!p || memcmp(opp->hw_addr, netdev_get_etheraddr(p->netdev),
ETH_ADDR_LEN) != 0) {
return;
}
- p->flags = htonl(opp->flags);
+
+
+ flag_mask = ntohl(opm->mask) & PORT_FLAG_BITS;
+ if (flag_mask) {
+ p->flags &= ~flag_mask;
+ p->flags |= ntohl(opp->flags) & flag_mask;
+ }
+
+ if (opm->mask & htonl(OFPPFL_PORT_DOWN)) {
+ if ((opp->flags & htonl(OFPPFL_PORT_DOWN))
+ && (p->status & OFPPFL_PORT_DOWN) == 0) {
+ p->status |= OFPPFL_PORT_DOWN;
+ netdev_turn_flags_off(p->netdev, NETDEV_UP, true);
+ } else if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) == 0
+ && (p->status & OFPPFL_PORT_DOWN)) {
+ p->status &= ~OFPPFL_PORT_DOWN;
+ netdev_turn_flags_on(p->netdev, NETDEV_UP, true);
+ }
+ }
+ }
+}
+
+/* Update the port status field of the bridge port. A non-zero return
+ * value indicates some field has changed.
+ *
+ * NB: Callers of this function may hold the RCU read lock, so any
+ * additional checks must not sleep.
+ */
+static int
+update_port_status(struct sw_port *p)
+{
+ int retval;
+ enum netdev_flags flags;
+ uint32_t orig_status = p->status;
+
+ if (netdev_get_flags(p->netdev, &flags) < 0) {
+ VLOG_WARN_RL(&rl, "could not get netdev flags for %s",
+ netdev_get_name(p->netdev));
+ return 0;
+ } else {
+ if (flags & NETDEV_UP) {
+ p->status &= ~OFPPFL_PORT_DOWN;
+ } else {
+ p->status |= OFPPFL_PORT_DOWN;
+ }
}
+
+ /* Not all cards support this getting link status, so don't warn on
+ * error. */
+ retval = netdev_get_link_status(p->netdev);
+ if (retval == 1) {
+ p->status &= ~OFPPFL_LINK_DOWN;
+ } else if (retval == 0) {
+ p->status |= OFPPFL_LINK_DOWN;
+ }
+
+ return (orig_status != p->status);
}
static void
void
dp_send_error_msg(struct datapath *dp, const struct sender *sender,
- uint16_t type, uint16_t code, const uint8_t *data, size_t len)
+ uint16_t type, uint16_t code, const void *data, size_t len)
{
struct buffer *buffer;
struct ofp_error_msg *oem;
- oem = make_openflow_reply(sizeof(*oem)+len, OFPT_ERROR_MSG,
- sender, &buffer);
+ oem = make_openflow_reply(sizeof(*oem)+len, OFPT_ERROR, sender, &buffer);
oem->type = htons(type);
oem->code = htons(code);
memcpy(oem->data, data, len);
ofs->length = htons(length);
ofs->table_id = table_idx;
ofs->pad = 0;
- ofs->match.wildcards = htons(flow->key.wildcards);
+ ofs->match.wildcards = htonl(flow->key.wildcards);
ofs->match.in_port = flow->key.flow.in_port;
memcpy(ofs->match.dl_src, flow->key.flow.dl_src, ETH_ADDR_LEN);
memcpy(ofs->match.dl_dst, flow->key.flow.dl_dst, ETH_ADDR_LEN);
ofs->match.nw_src = flow->key.flow.nw_src;
ofs->match.nw_dst = flow->key.flow.nw_dst;
ofs->match.nw_proto = flow->key.flow.nw_proto;
- memset(ofs->match.pad, 0, sizeof ofs->match.pad);
+ ofs->match.pad = 0;
ofs->match.tp_src = flow->key.flow.tp_src;
ofs->match.tp_dst = flow->key.flow.tp_dst;
ofs->duration = htonl(now - flow->created);
}
\f
-/* 'buffer' was received on 'in_port', a physical switch port between 0 and
- * OFPP_MAX. Process it according to 'dp''s flow table. Returns 0 if
+/* 'buffer' was received on 'p', which may be a a physical switch port or a
+ * null pointer. Process it according to 'dp''s flow table. Returns 0 if
* successful, in which case 'buffer' is destroyed, or -ESRCH if there is no
* matching flow, in which case 'buffer' still belongs to the caller. */
int run_flow_through_tables(struct datapath *dp, struct buffer *buffer,
- int in_port)
+ struct sw_port *p)
{
struct sw_flow_key key;
struct sw_flow *flow;
key.wildcards = 0;
- if (flow_extract(buffer, in_port, &key.flow)
+ if (flow_extract(buffer, p ? port_no(dp, p) : OFPP_NONE, &key.flow)
&& (dp->flags & OFPC_FRAG_MASK) == OFPC_FRAG_DROP) {
/* Drop fragment. */
buffer_delete(buffer);
return 0;
}
+ if (p && p->flags & (OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP)
+ && p->flags & (!eth_addr_equals(key.flow.dl_dst, stp_eth_addr)
+ ? OFPPFL_NO_RECV : OFPPFL_NO_RECV_STP)) {
+ buffer_delete(buffer);
+ return 0;
+ }
flow = chain_lookup(dp->chain, &key);
if (flow != NULL) {
flow_used(flow, buffer);
- execute_actions(dp, buffer, in_port, &key,
- flow->actions, flow->n_actions);
+ execute_actions(dp, buffer, port_no(dp, p),
+ &key, flow->actions, flow->n_actions, false);
return 0;
} else {
return -ESRCH;
}
}
-/* 'buffer' was received on 'in_port', a physical switch port between 0 and
- * OFPP_MAX. Process it according to 'dp''s flow table, sending it up to the
- * controller if no flow matches. Takes ownership of 'buffer'. */
-void fwd_port_input(struct datapath *dp, struct buffer *buffer, int in_port)
+/* 'buffer' was received on 'p', which may be a a physical switch port or a
+ * null pointer. Process it according to 'dp''s flow table, sending it up to
+ * the controller if no flow matches. Takes ownership of 'buffer'. */
+void fwd_port_input(struct datapath *dp, struct buffer *buffer,
+ struct sw_port *p)
{
- if (run_flow_through_tables(dp, buffer, in_port)) {
- dp_output_control(dp, buffer, in_port, dp->miss_send_len,
- OFPR_NO_MATCH);
+ if (run_flow_through_tables(dp, buffer, p)) {
+ dp_output_control(dp, buffer, port_no(dp, p),
+ dp->miss_send_len, OFPR_NO_MATCH);
}
}
static void
do_output(struct datapath *dp, struct buffer *buffer, int in_port,
- size_t max_len, int out_port)
+ size_t max_len, int out_port, bool ignore_no_fwd)
{
if (out_port != OFPP_CONTROLLER) {
- dp_output_port(dp, buffer, in_port, out_port);
+ dp_output_port(dp, buffer, in_port, out_port, ignore_no_fwd);
} else {
dp_output_control(dp, buffer, in_port, max_len, OFPR_ACTION);
}
static void
execute_actions(struct datapath *dp, struct buffer *buffer,
int in_port, const struct sw_flow_key *key,
- const struct ofp_action *actions, int n_actions)
+ const struct ofp_action *actions, int n_actions,
+ bool ignore_no_fwd)
{
/* Every output action needs a separate clone of 'buffer', but the common
* case is just a single output action, so that doing a clone and then
struct eth_header *eh = buffer->l2;
if (prev_port != -1) {
- do_output(dp, buffer_clone(buffer), in_port, max_len, prev_port);
+ do_output(dp, buffer_clone(buffer), in_port, max_len, prev_port,
+ ignore_no_fwd);
prev_port = -1;
}
}
}
if (prev_port != -1)
- do_output(dp, buffer, in_port, max_len, prev_port);
+ do_output(dp, buffer, in_port, max_len, prev_port, ignore_no_fwd);
else
buffer_delete(buffer);
}
const void *msg)
{
const struct ofp_packet_out *opo = msg;
+ struct sw_flow_key key;
+ struct buffer *buffer;
+ int n_actions = ntohs(opo->n_actions);
+ int act_len = n_actions * sizeof opo->actions[0];
+
+ if (act_len > (ntohs(opo->header.length) - sizeof *opo)) {
+ VLOG_DBG_RL(&rl, "message too short for number of actions");
+ return -EINVAL;
+ }
if (ntohl(opo->buffer_id) == (uint32_t) -1) {
/* FIXME: can we avoid copying data here? */
- int data_len = ntohs(opo->header.length) - sizeof *opo;
- struct buffer *buffer = buffer_new(data_len);
- buffer_put(buffer, opo->u.data, data_len);
- dp_output_port(dp, buffer,
- ntohs(opo->in_port), ntohs(opo->out_port));
+ int data_len = ntohs(opo->header.length) - sizeof *opo - act_len;
+ buffer = buffer_new(data_len);
+ buffer_put(buffer, &opo->actions[n_actions], data_len);
} else {
- struct sw_flow_key key;
- struct buffer *buffer;
- int n_acts;
-
buffer = retrieve_buffer(ntohl(opo->buffer_id));
if (!buffer) {
return -ESRCH;
}
-
- n_acts = (ntohs(opo->header.length) - sizeof *opo)
- / sizeof *opo->u.actions;
- flow_extract(buffer, ntohs(opo->in_port), &key.flow);
- execute_actions(dp, buffer, ntohs(opo->in_port),
- &key, opo->u.actions, n_acts);
}
- return 0;
+
+ flow_extract(buffer, ntohs(opo->in_port), &key.flow);
+ execute_actions(dp, buffer, ntohs(opo->in_port),
+ &key, opo->actions, n_actions, true);
+
+ return 0;
}
static int
{
const struct ofp_port_mod *opm = msg;
- dp_update_port_flags(dp, &opm->desc);
+ dp_update_port_flags(dp, opm);
return 0;
}
add_flow(struct datapath *dp, const struct ofp_flow_mod *ofm)
{
int error = -ENOMEM;
- int n_acts;
+ int n_actions;
int i;
struct sw_flow *flow;
/* To prevent loops, make sure there's no action to send to the
* OFP_TABLE virtual port.
*/
- n_acts = (ntohs(ofm->header.length) - sizeof *ofm) / sizeof *ofm->actions;
- for (i=0; i<n_acts; i++) {
+ n_actions = (ntohs(ofm->header.length) - sizeof *ofm)
+ / sizeof *ofm->actions;
+ for (i=0; i<n_actions; i++) {
const struct ofp_action *a = &ofm->actions[i];
if (a->type == htons(OFPAT_OUTPUT)
&& (a->arg.output.port == htons(OFPP_TABLE)
- || a->arg.output.port == htons(OFPP_NONE))) {
+ || a->arg.output.port == htons(OFPP_NONE)
+ || a->arg.output.port == ofm->match.in_port)) {
/* xxx Send fancy new error message? */
goto error;
}
}
/* Allocate memory. */
- flow = flow_alloc(n_acts);
+ flow = flow_alloc(n_actions);
if (flow == NULL)
goto error;
flow->idle_timeout = ntohs(ofm->idle_timeout);
flow->hard_timeout = ntohs(ofm->hard_timeout);
flow->used = flow->created = time_now();
- flow->n_actions = n_acts;
+ flow->n_actions = n_actions;
flow->byte_count = 0;
flow->packet_count = 0;
- memcpy(flow->actions, ofm->actions, n_acts * sizeof *flow->actions);
+ memcpy(flow->actions, ofm->actions, n_actions * sizeof *flow->actions);
/* Act. */
error = chain_insert(dp->chain, flow);
uint16_t in_port = ntohs(ofm->match.in_port);
flow_used(flow, buffer);
flow_extract(buffer, in_port, &key.flow);
- execute_actions(dp, buffer, in_port, &key, ofm->actions, n_acts);
+ execute_actions(dp, buffer, in_port, &key,
+ ofm->actions, n_actions, false);
} else {
error = -ESRCH;
}
}
}
+static int desc_stats_dump(struct datapath *dp, void *state,
+ struct buffer *buffer)
+{
+ struct ofp_desc_stats *ods = buffer_put_uninit(buffer, sizeof *ods);
+
+ strncpy(ods->mfr_desc, &mfr_desc, sizeof ods->mfr_desc);
+ strncpy(ods->hw_desc, &hw_desc, sizeof ods->hw_desc);
+ strncpy(ods->sw_desc, &sw_desc, sizeof ods->sw_desc);
+ strncpy(ods->serial_num, &serial_num, sizeof ods->serial_num);
+
+ return 0;
+}
+
struct flow_stats_state {
int table_idx;
struct sw_table_position position;
dp->chain->tables[i]->stats(dp->chain->tables[i], &stats);
strncpy(ots->name, stats.name, sizeof ots->name);
ots->table_id = i;
+ ots->wildcards = htonl(stats.wildcards);
memset(ots->pad, 0, sizeof ots->pad);
ots->max_entries = htonl(stats.max_flows);
ots->active_count = htonl(stats.n_flows);
ops = buffer_put_uninit(buffer, sizeof *ops);
ops->port_no = htons(port_no(dp, p));
memset(ops->pad, 0, sizeof ops->pad);
- ops->rx_count = htonll(p->rx_count);
- ops->tx_count = htonll(p->tx_count);
- ops->drop_count = htonll(p->drop_count);
+ ops->rx_packets = htonll(p->rx_packets);
+ ops->tx_packets = htonll(p->tx_packets);
+ ops->rx_bytes = htonll(p->rx_bytes);
+ ops->tx_bytes = htonll(p->tx_bytes);
+ ops->rx_dropped = htonll(-1);
+ ops->tx_dropped = htonll(p->tx_dropped);
+ ops->rx_errors = htonll(-1);
+ ops->tx_errors = htonll(-1);
+ ops->rx_frame_err = htonll(-1);
+ ops->rx_over_err = htonll(-1);
+ ops->rx_crc_err = htonll(-1);
+ ops->collisions = htonll(-1);
ops++;
}
s->port = i;
};
static const struct stats_type stats[] = {
+ [OFPST_DESC] = {
+ 0,
+ 0,
+ NULL,
+ desc_stats_dump,
+ NULL
+ },
[OFPST_FLOW] = {
sizeof(struct ofp_flow_stats_request),
sizeof(struct ofp_flow_stats_request),
type = ntohs(rq->type);
if (type >= ARRAY_SIZE(stats) || !stats[type].dump) {
- VLOG_WARN("received stats request of unknown type %d", type);
+ dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_STAT,
+ rq, rq_len);
+ VLOG_WARN_RL(&rl, "received stats request of unknown type %d", type);
return -EINVAL;
}
body_len = rq_len - offsetof(struct ofp_stats_request, body);
if (body_len < cb->s->min_body || body_len > cb->s->max_body) {
- VLOG_WARN("stats request type %d with bad body length %d",
- type, body_len);
+ VLOG_WARN_RL(&rl, "stats request type %d with bad body length %d",
+ type, body_len);
err = -EINVAL;
goto error;
}
if (cb->s->init) {
err = cb->s->init(dp, rq->body, body_len, &cb->state);
if (err) {
- VLOG_WARN("failed initialization of stats request type %d: %s",
- type, strerror(-err));
+ VLOG_WARN_RL(&rl,
+ "failed initialization of stats request type %d: %s",
+ type, strerror(-err));
goto error;
}
}
},
};
- const struct openflow_packet *pkt;
struct ofp_header *oh;
oh = (struct ofp_header *) msg;
- assert(oh->version == OFP_VERSION);
- if (oh->type >= ARRAY_SIZE(packets) || ntohs(oh->length) > length)
+ if (ntohs(oh->length) > length)
return -EINVAL;
- pkt = &packets[oh->type];
- if (!pkt->handler)
- return -ENOSYS;
- if (length < pkt->min_size)
- return -EFAULT;
-
- return pkt->handler(dp, sender, msg);
+ if (oh->type < ARRAY_SIZE(packets)) {
+ const struct openflow_packet *pkt = &packets[oh->type];
+ if (pkt->handler) {
+ if (length < pkt->min_size)
+ return -EFAULT;
+ return pkt->handler(dp, sender, msg);
+ }
+ }
+ dp_send_error_msg(dp, sender, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE,
+ msg, length);
+ return -EINVAL;
}
\f
/* Packet buffering. */