#include "list.h"
#include "mac-learning.h"
#include "netdev.h"
+#include "odp-util.h"
#include "ofp-print.h"
#include "ofpbuf.h"
#include "poll-loop.h"
#include "rconn.h"
#include "secchan/ofproto.h"
#include "socket-util.h"
-#include "stats.h"
#include "stp.h"
#include "svec.h"
#include "timeval.h"
struct mac_learning *ml; /* MAC learning table, or null not to learn. */
int flow_idle_time; /* Idle time for flows we set up. */
bool sent_config_request; /* Successfully sent config request? */
- bool sent_features_request; /* Successfully sent features request? */
uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */
/* Support for remote controllers. */
bool flush;
/* Flow statistics gathering. */
- struct stats_mgr *stats_mgr;
- struct stats_request *flow_rq; /* Current flow statistics request. */
time_t next_stats_request;
/* Port mirroring. */
static struct iface *iface_lookup(const struct bridge *, const char *name);
static struct iface *iface_from_dp_ifidx(const struct bridge *,
uint16_t dp_ifidx);
+
+/* Hooks into ofproto processing. */
+static struct ofhooks bridge_ofhooks;
\f
/* Public functions. */
if (br->ml) {
mac_learning_wait(br->ml);
}
- stats_mgr_wait(br->stats_mgr);
flowstats_wait(br);
bond_wait(br);
brstp_wait(br);
return NULL;
}
- error = ofproto_create(name, &br->ofproto);
+ error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto);
if (error) {
VLOG_ERR("failed to create OpenFlow switch %s: %s",
name, strerror(error));
br->ml = mac_learning_create();
br->flow_idle_time = 5;
br->sent_config_request = false;
- br->sent_features_request = false;
eth_addr_random(br->default_ea);
br->rconn = rconn_create(30, 1);
tag_set_init(&br->revalidate_set);
br->flush = false;
- br->stats_mgr = stats_mgr_create(br->rconn);
-
list_push_back(&all_bridges, &br->node);
VLOG_INFO("created bridge %s on dp%u", br->name, dpif_id(&br->dpif));
rconn_packet_counter_destroy(br->txqlen);
free(br->controller);
ft_destroy(br->ft);
- stats_request_destroy(br->flow_rq);
- stats_mgr_destroy(br->stats_mgr);
mac_learning_destroy(br->ml);
port_array_destroy(&br->ifaces);
free(br->ports);
}
}
-static void
-send_features_request(struct bridge *br)
-{
- struct ofp_header *oh;
- struct ofpbuf *msg;
-
- oh = make_openflow(sizeof *oh, OFPT_FEATURES_REQUEST, &msg);
- if (!rconn_send_with_limit(br->rconn, msg, br->txqlen, INT_MAX)) {
- br->sent_features_request = true;
- }
-}
-
static int
bridge_run_one(struct bridge *br)
{
if (!br->sent_config_request) {
send_set_config_request(br);
}
- if (!br->sent_features_request) {
- send_features_request(br);
- }
}
/* Now do the things that may want to revalidate flows. */
typedef void packet_handler_func(struct bridge *, void *);
static packet_handler_func process_echo_request;
-static packet_handler_func process_packet_in;
-static packet_handler_func process_flow_expired;
-static packet_handler_func process_stats_reply;
-static packet_handler_func process_error_msg;
-static packet_handler_func process_features_reply;
-static packet_handler_func process_port_status;
static void
bridge_process_msg(struct bridge *br, struct ofpbuf *msg)
case OFPT_ECHO_REQUEST:
process_echo_request(br, msg->data);
break;
- case OFPT_PACKET_IN:
- process_packet_in(br, msg->data);
- break;
- case OFPT_FLOW_EXPIRED:
- process_flow_expired(br, msg->data);
- break;
- case OFPT_STATS_REPLY:
- process_stats_reply(br, msg->data);
- break;
- case OFPT_ERROR:
- process_error_msg(br, msg->data);
- break;
- case OFPT_FEATURES_REPLY:
- process_features_reply(br, msg->data);
- break;
- case OFPT_PORT_STATUS:
- process_port_status(br, msg->data);
- break;
default:
if (VLOG_IS_DBG_ENABLED()) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
}
}
-struct received_packet {
- struct ofpbuf *buf; /* NULL if no packet data. */
- uint32_t buffer_id; /* Host byte order; UINT32_MAX if not buffered. */
-};
-
static void
send_packets(struct bridge *br, const flow_t *flow,
- const struct received_packet *pkt, uint16_t vlan,
+ const struct ofpbuf *pkt, uint16_t vlan,
const struct port *in_port, const struct port *out_port,
tag_type tags, bool setup_flow)
{
struct ft_dst dsts[DP_MAX_PORTS * (MAX_MIRRORS + 1)];
size_t actions_len; /* Estimated length of actions, in bytes. */
size_t n_dsts;
+ flow_t odp_flow;
n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, &tags);
actions_len = (sizeof(struct ofp_action_header) + 2) * n_dsts;
+ /* XXX for now everything in bridge.c is still in terms of OFP ports. In
+ * the long term we should change that (and avoid this awkward copy). */
+ odp_flow = *flow;
+ odp_flow.in_port = ofp_port_to_odp_port(odp_flow.in_port);
+
if (setup_flow) {
+ enum { NO_OP, ADD_FLOW, SET_ACTIONS } command;
struct ft_flow *f;
- int command;
f = ft_lookup(br->ft, flow, flow_hash(flow, 0));
if (f) {
* OFPFC_ADD so that we don't reset the idle-timer
* countdown for this flow. */
ftf_set_dsts(f, dsts, n_dsts);
- command = OFPFC_MODIFY_STRICT;
+ command = SET_ACTIONS;
} else {
/* Correct flow is already in the flow table, nothing to do.
* This should only happen on revalidate, since
* no-match packet-in message. (If it could happen in other
* circumstances then we'd want to arrange to send a packet-out
* below, but there's no need.) */
- command = -1;
+ command = NO_OP;
}
f->tags = tags;
} else {
f = ftf_create(flow, dsts, n_dsts, tags);
ft_insert(br->ft, f);
- command = OFPFC_ADD;
+ command = ADD_FLOW;
}
f->need_drop = false;
- if (command >= 0) {
- struct ofp_flow_mod *ofm;
- struct ofpbuf *fbuf;
+ if (command != NO_OP) {
+ struct ofpbuf abuf;
+ size_t n_actions;
+
+ ofpbuf_init(&abuf, actions_len);
+ put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), &abuf);
+ n_actions = abuf.size / sizeof(union ofp_action);
- fbuf = make_add_flow(flow, pkt->buffer_id, bridge_idle_time(br),
- actions_len);
- ofm = fbuf->data;
- ofm->command = htons(command);
- put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), fbuf);
- queue_tx(br, fbuf);
+ if (command == ADD_FLOW) {
+ ofproto_add_flow(br->ofproto, &odp_flow, abuf.data, n_actions,
+ pkt, bridge_idle_time(br));
+ pkt = NULL; /* Already sent. */
- /* The add_flow message will reset the byte counters. */
- f->last_byte_count = f->byte_count = 0;
+ /* ofproto_add_flow() will reset the byte counters. */
+ f->last_byte_count = f->byte_count = 0;
+ } else {
+ ofproto_set_actions(br->ofproto, &odp_flow, abuf.data,
+ n_actions);
+ }
+ ofpbuf_uninit(&abuf);
}
} else {
struct ft_flow *f = ft_lookup(br->ft, flow, flow_hash(flow, 0));
}
}
- if (pkt->buffer_id == UINT32_MAX ? pkt->buf != NULL : !setup_flow) {
- size_t pkt_size = pkt->buf ? pkt->buf->size : 0;
- struct ofp_packet_out *opo;
- struct ofpbuf *pbuf;
+ /* XXX we should send the packet *before* we set up the flow, so as to not
+ * reorder packets in the flow. */
+ if (pkt) {
+ struct ofpbuf abuf;
- pbuf = ofpbuf_new(sizeof *opo + actions_len + pkt_size);
- opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, pbuf);
- opo->buffer_id = htonl(pkt->buffer_id);
- opo->in_port = flow->in_port;
- put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), pbuf);
- opo = pbuf->data;
- opo->actions_len = htons(pbuf->size - sizeof *opo);
- if (pkt->buffer_id == UINT32_MAX) {
- ofpbuf_put(pbuf, pkt->buf->data, pkt->buf->size);
- }
- queue_tx(br, pbuf);
+ ofpbuf_init(&abuf, actions_len);
+ put_actions(dsts, n_dsts, ntohs(flow->dl_vlan), &abuf);
+ ofproto_send_packet(br->ofproto, &odp_flow,
+ abuf.data, abuf.size / sizeof(union ofp_action),
+ pkt);
+ ofpbuf_uninit(&abuf);
}
}
}
static void
-process_flow(struct bridge *br, const flow_t *flow,
- struct received_packet *pkt)
+process_flow(struct bridge *br, const flow_t *flow, const struct ofpbuf *pkt)
{
struct iface *in_iface;
struct port *in_port;
if (!in_iface) {
/* No interface? Something fishy... */
struct ft_flow *f = ft_lookup(br->ft, flow, flow_hash(flow, 0));
- if (pkt->buf || (f && f->need_drop)) {
+ if (pkt || (f && f->need_drop)) {
/* Odd. A few possible reasons here:
*
* - We deleted an interface but there are still a few packets
* one of our bridge ports.
*/
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ flow_t odp_flow;
+
VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
"interface %"PRIu16, br->name, flow->in_port);
- queue_tx(br, make_add_flow(flow, pkt->buffer_id,
- bridge_idle_time(br), 0));
+ /* XXX for now everything in bridge.c is still in terms of OFP
+ * ports. In the long term we should change that (and avoid this
+ * awkward copy). */
+ odp_flow = *flow;
+ odp_flow.in_port = ofp_port_to_odp_port(odp_flow.in_port);
+ ofproto_add_flow(br->ofproto, &odp_flow, NULL, 0, NULL,
+ bridge_idle_time(br));
if (f) {
ftf_set_dsts(f, NULL, 0);
f->tags = tags;
/* We're revalidating, not receiving a fresh packet. Most likely,
* this interface existed and has now been deleted from the
* datapath. Drop the flow. */
+ flow_t odp_flow;
+
assert(f);
- queue_tx(br, make_del_flow(flow));
+
+ /* XXX for now everything in bridge.c is still in terms of OFP
+ * ports. In the long term we should change that (and avoid this
+ * awkward copy). */
+ odp_flow = *flow;
+ odp_flow.in_port = ofp_port_to_odp_port(odp_flow.in_port);
+ ofproto_delete_flow(br->ofproto, &odp_flow);
+
+ /* ofproto_delete_flow() better not call back to
+ * bridge_flow_expired_ofhook_cb(). */
ft_remove(br->ft, f);
ftf_destroy(f);
/* 'flow' pointed to &f->flow, so don't access it anymore. */
if (in_port->vlan >= 0) {
if (vlan) {
/* XXX support double tagging? */
- if (pkt->buf) {
+ if (pkt) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
"packet received on port %s configured with "
int out_port_idx;
bool may_learn;
- if (!pkt->buf) {
+ if (!pkt) {
/* Don't try to learn from revalidation. */
may_learn = false;
} else if (in_port->n_ifaces > 1) {
/* Broadcast ARP replies are an exception to this rule: the host
* has moved to another switch. */
- if (!may_learn && is_bcast_arp_reply(flow, pkt->buf)) {
+ if (!may_learn && is_bcast_arp_reply(flow, pkt)) {
may_learn = true;
}
} else {
static void
revalidate_flow(struct bridge *br, struct ft_flow *f)
{
- struct received_packet pkt;
- pkt.buf = NULL;
- pkt.buffer_id = UINT32_MAX;
- process_flow(br, &f->flow, &pkt);
-}
-
-static void
-process_packet_in(struct bridge *br, void *opi_)
-{
- struct ofp_packet_in *opi = opi_;
- struct received_packet pkt;
- struct ofpbuf buf;
- flow_t flow;
-
- if (check_ofp_message_array(&opi->header, OFPT_PACKET_IN,
- offsetof(struct ofp_packet_in, data),
- 1, &buf.size)) {
- return;
- }
- buf.data = opi->data;
- pkt.buf = &buf;
- pkt.buffer_id = ntohl(opi->buffer_id);
- flow_extract(&buf, ntohs(opi->in_port), &flow);
-
- if (opi->reason == OFPR_NO_MATCH) {
- /* Delete any existing flow from the flow table. It must not really be
- * there (otherwise we wouldn't be getting a packet-in). */
- struct ft_flow *f = ft_lookup(br->ft, &flow, flow_hash(&flow, 0));
- if (f) {
- ft_remove(br->ft, f);
- ftf_destroy(f);
- }
- }
-
- if (flow.dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
- && eth_addr_equals(flow.dl_dst, stp_eth_addr)) {
- brstp_receive(br, &flow, &buf);
- return;
- }
-
- process_flow(br, &flow, &pkt);
+ process_flow(br, &f->flow, NULL);
}
static void
queue_tx(br, make_echo_reply(rq));
}
+/* Careful: 'opp' is in host byte order and opp->port_no is an OFP port
+ * number. */
static void
-process_flow_expired(struct bridge *br, void *ofe_)
-{
- struct ofp_flow_expired *ofe = ofe_;
- struct ft_flow *f;
- flow_t flow;
-
- if (check_ofp_message(&ofe->header, OFPT_FLOW_EXPIRED, sizeof *ofe)) {
- return;
- }
-
- if (ofe->match.wildcards != htonl(0)) {
- /* We don't use flows with wildcards, so there's nothing to do. */
- return;
- }
- flow_from_match(&flow, NULL, &ofe->match);
-
- f = ft_lookup(br->ft, &flow, flow_hash(&flow, 0));
- if (f) {
- /* Update statistics. */
- f->last_byte_count = f->byte_count;
- f->byte_count = ntohll(ofe->byte_count);
- bond_account_flow(br, f);
-
- ft_remove(br->ft, f);
- ftf_destroy(f);
- } else if (VLOG_IS_DBG_ENABLED()) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_DBG_RL(&rl, "received flow expiration for flow not in table");
- }
-}
-
-static void
-process_stats_reply(struct bridge *br, void *osr)
-{
- stats_mgr_receive_stats_reply(br->stats_mgr, osr);
-}
-
-static void
-process_error_msg(struct bridge *br, void *oem)
-{
- stats_mgr_receive_error_msg(br->stats_mgr, oem);
-}
-
-static void
-phy_port_changed(struct bridge *br, enum ofp_port_reason reason,
- const struct ofp_phy_port *opp)
+bridge_port_changed_ofhook_cb(enum ofp_port_reason reason,
+ const struct ofp_phy_port *opp,
+ void *br_)
{
+ struct bridge *br = br_;
struct iface *iface;
struct port *port;
- iface = iface_from_dp_ifidx(br, ntohs(opp->port_no));
+ iface = iface_from_dp_ifidx(br, opp->port_no);
if (!iface) {
return;
}
bridge_flush(br);
} else {
if (port->n_ifaces > 1) {
- bool up = !(opp->state & htonl(OFPPS_LINK_DOWN));
+ bool up = !(opp->state & OFPPS_LINK_DOWN);
bond_link_status_update(iface, up);
}
memcpy(iface->mac, opp->hw_addr, ETH_ADDR_LEN);
}
}
-static void
-process_features_reply(struct bridge *br, void *osf_)
+static bool
+bridge_packet_in_ofhook_cb(const flow_t *flow_, const struct ofpbuf *payload,
+ void *br_)
{
- struct ofp_switch_features *osf = osf_;
- struct ofp_phy_port *pp;
- size_t n_pp;
+ struct bridge *br = br_;
+ struct ft_flow *f;
+ flow_t flow;
- if (check_ofp_message_array(&osf->header, OFPT_FEATURES_REPLY,
- sizeof *osf, sizeof *osf->ports, &n_pp)) {
- return;
+ /* XXX for now everything in bridge.c is still in terms of OFP ports. In
+ * the long term we should change that (and avoid this awkward copy). */
+ flow = *flow_;
+ flow.in_port = odp_port_to_ofp_port(flow.in_port);
+
+ /* Delete any existing flow from the flow tracker. The flow cannot really
+ * be in the flow table (otherwise we wouldn't be getting called). */
+ f = ft_lookup(br->ft, &flow, flow_hash(&flow, 0));
+ if (f) {
+ ft_remove(br->ft, f);
+ ftf_destroy(f);
}
- for (pp = &osf->ports[0]; pp < &osf->ports[n_pp]; pp++) {
- phy_port_changed(br, OFPPR_MODIFY, pp);
+
+ if (flow.dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
+ && eth_addr_equals(flow.dl_dst, stp_eth_addr)) {
+ brstp_receive(br, &flow, payload);
+ return true;
}
+
+ process_flow(br, &flow, payload);
+
+ return true;
}
static void
-process_port_status(struct bridge *br, void *ops_)
+bridge_flow_expired_ofhook_cb(const struct ofexpired *expired, void *br_)
{
- struct ofp_port_status *ops = ops_;
+ struct bridge *br = br_;
+ struct ft_flow *f;
+ flow_t flow;
- if (check_ofp_message(&ops->header, OFPT_PORT_STATUS, sizeof *ops)) {
- return;
+ /* XXX for now everything in bridge.c is still in terms of OFP ports. In
+ * the long term we should change that (and avoid this awkward copy). */
+ flow = expired->flow;
+ flow.in_port = odp_port_to_ofp_port(flow.in_port);
+
+ f = ft_lookup(br->ft, &flow, flow_hash(&flow, 0));
+ if (f) {
+ /* Update statistics. */
+ f->last_byte_count = f->byte_count;
+ f->byte_count = expired->byte_count;
+ bond_account_flow(br, f);
+
+ ft_remove(br->ft, f);
+ ftf_destroy(f);
+ } else if (VLOG_IS_DBG_ENABLED()) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_DBG_RL(&rl, "received flow expiration for flow not in table");
}
- phy_port_changed(br, ops->reason, &ops->desc);
}
+
+static struct ofhooks bridge_ofhooks = {
+ bridge_port_changed_ofhook_cb,
+ bridge_packet_in_ofhook_cb,
+ bridge_flow_expired_ofhook_cb,
+};
\f
/* Flow statistics collection. */
static void
request_flow_stats(struct bridge *br)
-{
- struct ofp_flow_stats_request *ofsr;
- struct ofp_stats_request *osr;
- struct ofpbuf *msg;
-
- osr = make_openflow(sizeof *osr + sizeof *ofsr, OFPT_STATS_REQUEST, &msg);
- osr->type = htons(OFPST_FLOW);
- osr->flags = htons(0);
- ofsr = (struct ofp_flow_stats_request *) osr->body;
- ofsr->match.wildcards = htonl(OFPFW_ALL);
- ofsr->table_id = 0xff;
- ofsr->out_port = htons(OFPP_NONE);
-
- stats_request_destroy(br->flow_rq);
- br->flow_rq = stats_request_create(br->stats_mgr, msg);
-}
-
-static void
-flowstats_process(struct bridge *br)
{
/* This ft_dst will never appear as a valid flow's action. Thus, we can
* set it as a flow's action to force revalidation to update that flow. */
static const struct ft_dst invalid_dst = {0xffff, 0xffff};
+ struct odp_flow *flows, *odp_flow;
+ size_t n_flows;
+ int error;
+
tag_type flowstats_tag;
- struct ofpbuf *msg;
struct ft *new_ft;
size_t n_tagged;
+ error = dpif_flow_list_all(&br->dpif, &flows, &n_flows);
+ if (error) {
+ return;
+ }
+
new_ft = ft_create();
flowstats_tag = tag_create_random();
n_tagged = 0;
- while (stats_request_get_reply(br->flow_rq, &msg)) {
- struct ofp_stats_reply *osr = msg->data;
- const struct ofp_flow_stats *fs;
- struct flow_stats_iterator i;
-
- for (fs = flow_stats_first(&i, osr); fs; fs = flow_stats_next(&i)) {
- size_t n_actions = ((ntohs(fs->length)
- - offsetof(struct ofp_flow_stats, actions))
- / sizeof(struct ofp_action_header));
- struct ft_flow *f;
- flow_t flow;
- size_t hash;
-
- if (fs->match.wildcards != htonl(0)) {
- /* XXX delete flow */
- continue;
- }
- flow_from_match(&flow, NULL, &fs->match);
- hash = flow_hash(&flow, 0);
+ for (odp_flow = flows; odp_flow < &flows[n_flows]; odp_flow++) {
+ const flow_t *flow = &odp_flow->key;
+ size_t hash = flow_hash(flow, 0);
+ struct ft_flow *f = ft_lookup(br->ft, flow, hash);
+ if (f) {
+ /* Move from br->ft to new_ft. */
+ ft_remove(br->ft, f);
+ hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash);
- f = ft_lookup(br->ft, &flow, hash);
- if (f) {
- /* Move from br->ft to new_ft. */
- ft_remove(br->ft, f);
- hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash);
+ /* Update statistics.
+ * XXX is this still accurate with the new datapath? */
+ f->last_byte_count = f->byte_count;
+ f->byte_count = odp_flow->stats.n_bytes;
- /* Update statistics. */
- f->last_byte_count = f->byte_count;
- f->byte_count = ntohll(fs->byte_count);
-
- /* Force the flow to be revalidated and replaced if its actions
- * aren't what we expect. */
- if (!ftd_equal_actions(f->dsts, f->n_dsts,
- fs->actions, n_actions,
- ntohs(flow.dl_vlan))) {
- ftf_set_dsts(f, &invalid_dst, 1);
- f->tags |= flowstats_tag;
- n_tagged++;
- } else {
- bond_account_flow(br, f);
+ bond_account_flow(br, f);
+
+ /* XXX Should we verify that the flow's actions are what we
+ * expect? (This used to be easy; now it's harder.) */
+ } else {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ f = ft_lookup(new_ft, flow, hash);
+ if (!f) {
+ if (!VLOG_DROP_WARN(&rl)) {
+ char *flow_string = flow_to_string(flow);
+ VLOG_WARN("unexpected flow in flow table: %s",
+ flow_string);
+ free(flow_string);
}
+ f = ftf_create(flow, &invalid_dst, 1, flowstats_tag);
+ hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash);
+ f->tags |= flowstats_tag;
+ n_tagged++;
+ f->last_byte_count = f->byte_count = odp_flow->stats.n_bytes;
} else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- f = ft_lookup(new_ft, &flow, hash);
- if (!f) {
- if (!VLOG_DROP_WARN(&rl)) {
- char *flow_string = flow_to_string(&flow);
- VLOG_WARN("unexpected flow in flow table: %s",
- flow_string);
- free(flow_string);
- }
- f = ftf_create(&flow, &invalid_dst, 1, flowstats_tag);
- hmap_insert_fast(&new_ft->flows, &f->node, f->node.hash);
- f->tags |= flowstats_tag;
- n_tagged++;
- f->last_byte_count = f->byte_count = fs->byte_count;
- } else {
- VLOG_WARN_RL(&rl, "duplicate flow in flow stats reply");
- }
+ VLOG_WARN_RL(&rl, "duplicate flow in flow stats reply");
}
}
- ofpbuf_delete(msg);
}
/* Delete all the flows that remain in br->ft, replacing them by the ones
static void
flowstats_run(struct bridge *br)
{
- stats_mgr_run(br->stats_mgr);
- if (br->flow_rq) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- switch (stats_request_get_status(br->flow_rq)) {
- case EAGAIN:
- return;
-
- case 0:
- flowstats_process(br);
- break;
-
- default:
- VLOG_WARN_RL(&rl, "flow stats request returned error: %s",
- strerror(stats_request_get_status(br->flow_rq)));
- br->next_stats_request = time_now() + 1;
- break;
- }
- stats_request_destroy(br->flow_rq);
- br->flow_rq = NULL;
- } else {
- if (rconn_is_connected(br->rconn)
- && time_now() >= br->next_stats_request) {
- request_flow_stats(br);
- br->next_stats_request = time_now() + 10;
- }
+ if (rconn_is_connected(br->rconn)
+ && time_now() >= br->next_stats_request) {
+ request_flow_stats(br);
+ br->next_stats_request = time_now() + 10;
}
}
static void
flowstats_wait(struct bridge *br)
{
- if (!br->flow_rq && rconn_is_connected(br->rconn)) {
+ if (rconn_is_connected(br->rconn)) {
poll_timer_wait((br->next_stats_request - time_now()) * 1000);
}
}
static void
iface_create(struct port *port, const char *name)
{
- struct iface *iface = xcalloc(1, sizeof *iface);
+ enum netdev_flags flags;
+ struct iface *iface;
+
+ iface = xcalloc(1, sizeof *iface);
iface->port = port;
iface->port_ifidx = port->n_ifaces;
iface->name = xstrdup(name);
iface->enabled = true;
iface->delay_expires = LLONG_MAX;
+ if (!netdev_nodev_get_flags(name, &flags)) {
+ iface->enabled = (flags & NETDEV_UP) != 0;
+ }
+
if (port->n_ifaces >= port->allocated_ifaces) {
port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces,
sizeof *port->ifaces);
VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d with unknown MAC",
br->name, port_no);
} else {
+ union ofp_action action;
struct eth_header *eth = pkt->l2;
+ flow_t flow;
+
memcpy(eth->eth_src, iface->mac, ETH_ADDR_LEN);
- queue_tx(br, make_unbuffered_packet_out(pkt, OFPP_NONE, port_no));
+
+ memset(&action, 0, sizeof action);
+ action.type = htons(OFPAT_OUTPUT);
+ action.output.len = htons(sizeof action);
+ action.output.port = htons(port_no);
+
+ flow_extract(pkt, htons(OFPP_NONE), &flow);
+ ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt);
}
ofpbuf_delete(pkt);
}
+++ /dev/null
-/* Copyright (c) 2008, 2009 Nicira Networks
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * In addition, as a special exception, Nicira Networks gives permission
- * to link the code of its release of vswitchd with the OpenSSL project's
- * "OpenSSL" library (or with modified versions of it that use the same
- * license as the "OpenSSL" library), and distribute the linked
- * executables. You must obey the GNU General Public License in all
- * respects for all of the code used other than "OpenSSL". If you modify
- * this file, you may extend this exception to your version of the file,
- * but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version.
- */
-
-#include <config.h>
-#include "stats.h"
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <arpa/inet.h>
-#include <stdlib.h>
-#include "list.h"
-#include "ofpbuf.h"
-#include "openflow/openflow.h"
-#include "poll-loop.h"
-#include "rconn.h"
-#include "timeval.h"
-#include "util.h"
-#include "vconn.h"
-
-#define THIS_MODULE VLM_stats
-#include "vlog.h"
-
-/* XXX when an ongoing stats requests is canceled, might want to wait for the
- * completed replies before sending the next request */
-
-struct stats_mgr {
- struct stats_request *rq;
- struct list requests;
- struct rconn *rconn;
- unsigned int rconn_seqno;
- struct rconn_packet_counter *txqlen;
- time_t timeout;
-};
-
-/* Number of seconds we'll wait for a stats reply before giving up. */
-#define STATS_TIMEOUT 5
-
-struct stats_request {
- struct stats_mgr *mgr;
- struct list node;
- struct ofpbuf *osr;
- uint32_t xid;
- uint16_t type;
- enum {
- SR_INIT,
- SR_PARTIAL,
- SR_DONE
- } state;
- int error;
- struct ofp_queue replies;
-};
-
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
-static void complete_request(struct stats_request *, int error);
-static void send_stats_request(struct stats_mgr *);
-static void cancel_all_requests(struct stats_mgr *, int error);
-\f
-/* Creates and returns a new stats_mgr that sends stats requests on 'rconn'. */
-struct stats_mgr *
-stats_mgr_create(struct rconn *rconn)
-{
- struct stats_mgr *mgr = xcalloc(1, sizeof *mgr);
- mgr->rq = NULL;
- list_init(&mgr->requests);
- mgr->rconn = rconn;
- mgr->rconn_seqno = rconn_get_connection_seqno(rconn);
- mgr->txqlen = rconn_packet_counter_create();
- return mgr;
-}
-
-/* Destroys 'mgr'. Careful: the rconn underlying 'mgr' must be destroyed
- * *before* calling this function (because destroying the rconn will update
- * 'mgr->txqlen').
- *
- * stats_requests on 'mgr' are canceled, but not destroyed; the client must
- * destroy them individually with stats_request_destroy(). */
-void
-stats_mgr_destroy(struct stats_mgr *mgr)
-{
- if (mgr) {
- rconn_packet_counter_destroy(mgr->txqlen);
- cancel_all_requests(mgr, ECONNABORTED);
- free(mgr);
- }
-}
-
-/* Allows 'mgr' to process 'osr' that was received on 'mgr''s rconn. */
-void
-stats_mgr_receive_stats_reply(struct stats_mgr *mgr,
- const struct ofp_stats_reply *osr)
-{
- struct stats_request *rq = mgr->rq;
-
- if (!rq || check_ofp_message_array(&osr->header, OFPT_STATS_REPLY,
- sizeof *osr, 1, NULL)) {
- return;
- }
- if (osr->header.xid != rq->xid) {
- VLOG_DBG_RL(&rl, "stats reply has xid %08"PRIx32" != "
- "expected %08"PRIx32, osr->header.xid, rq->xid);
- return;
- }
- if (osr->type != rq->type) {
- VLOG_WARN_RL(&rl, "stats reply has type %04"PRIx16" != "
- "expected %04"PRIx16, osr->type, rq->type);
- return;
- }
-
- queue_push_tail(&rq->replies,
- ofpbuf_clone_data(osr, ntohs(osr->header.length)));
- if (osr->flags & htons(OFPSF_REPLY_MORE)) {
- rq->state = SR_PARTIAL;
- mgr->timeout = time_now() + STATS_TIMEOUT;
- } else {
- complete_request(rq, 0);
- send_stats_request(mgr);
- }
-}
-
-/* Allows 'mgr' to process 'oem' that was received on 'mgr''s rconn. */
-void
-stats_mgr_receive_error_msg(struct stats_mgr *mgr,
- const struct ofp_error_msg *oem)
-{
- struct stats_request *rq = mgr->rq;
-
- if (!rq
- || check_ofp_message_array(&oem->header, OFPT_ERROR,
- sizeof *oem, 1, NULL)
- || oem->header.xid != rq->xid) {
- return;
- }
-
- VLOG_WARN_RL(&rl, "error reply to stats request (%"PRIu16", %"PRIu16")",
- ntohs(oem->type), ntohs(oem->code));
- complete_request(rq, EPROTO);
- send_stats_request(mgr);
-}
-
-/* Performs internal processing for 'mgr'. */
-void
-stats_mgr_run(struct stats_mgr *mgr)
-{
- if (mgr->rq && time_now() > mgr->timeout) {
- VLOG_WARN_RL(&rl, "stats request timed out");
- complete_request(mgr->rq, ETIMEDOUT);
- send_stats_request(mgr);
- }
-}
-
-/* Causes the next call to poll_block() to wake up when stats_mgr_run()
- * should be called on 'mgr'. */
-void
-stats_mgr_wait(struct stats_mgr *mgr)
-{
- if (mgr->rq) {
- poll_timer_wait((mgr->timeout - time_now()) * 1000);
- }
-}
-\f
-/* Creates a new stats_request, adds it to 'mgr', and returns it. 'msg' will
- * be sent as the statistics request. */
-struct stats_request *
-stats_request_create(struct stats_mgr *mgr, struct ofpbuf *msg)
-{
- struct ofp_stats_request *osr = msg->data;
- struct stats_request *rq;
-
- /* Flush out any dead requests if the rconn connected or disconnected, and
- * update mgr->rconn_seqno so that the request we are about to queue can be
- * sent. */
- send_stats_request(mgr);
-
- /* Queue the request. */
- rq = xcalloc(1, sizeof *rq);
- rq->mgr = mgr;
- list_push_back(&mgr->requests, &rq->node);
- rq->xid = osr->header.xid;
- rq->type = osr->type;
- rq->osr = msg;
- rq->state = SR_INIT;
- queue_init(&rq->replies);
-
- /* Send the request, if possible. */
- send_stats_request(mgr);
-
- return rq;
-}
-
-/* Destroys 'rq'. */
-void
-stats_request_destroy(struct stats_request *rq)
-{
- if (rq) {
- complete_request(rq, ECANCELED);
- queue_destroy(&rq->replies);
- free(rq);
- }
-}
-
-/* Reports the status of 'rq': 0 if the request completed successfully, EAGAIN
- * if the request is in progress, otherwise a positive errno value if the
- * request completed with an error. */
-int
-stats_request_get_status(const struct stats_request *rq)
-{
- return rq->state == SR_DONE ? rq->error : EAGAIN;
-}
-
-/*
- * If statistics reply messages remain in the queue in 'rq', stores the first
- * one in '*msg' and returns true. Otherwise, if no statistics reply messages
- * remain in 'rq''s queue, stores a null pointer in '*msg' and returns false.
- *
- * This function may only be called for 'rq' if stats_request_get_status(rq)
- * returns 0. */
-bool
-stats_request_get_reply(struct stats_request *rq, struct ofpbuf **msg)
-{
- assert(rq->state == SR_DONE && !rq->error);
- if (rq->replies.n) {
- *msg = queue_pop_head(&rq->replies);
- return true;
- } else {
- *msg = NULL;
- return false;
- }
-}
-\f
-static void
-complete_request(struct stats_request *rq, int error)
-{
- struct stats_mgr *mgr = rq->mgr;
- if (mgr) {
- if (mgr->rq == rq) {
- mgr->rq = NULL;
- } else {
- list_remove(&rq->node);
- }
- rq->mgr = NULL;
- }
- if (rq->osr) {
- ofpbuf_delete(rq->osr);
- rq->osr = NULL;
- }
- rq->state = SR_DONE;
- rq->error = error;
-}
-
-static void
-cancel_all_requests(struct stats_mgr *mgr, int error)
-{
- struct stats_request *rq, *next;
- LIST_FOR_EACH_SAFE (rq, next, struct stats_request, node, &mgr->requests) {
- complete_request(rq, error);
- }
-}
-
-static void
-send_stats_request(struct stats_mgr *mgr)
-{
- if (mgr->rconn_seqno != rconn_get_connection_seqno(mgr->rconn)
- || !rconn_is_connected(mgr->rconn)) {
- mgr->rconn_seqno = rconn_get_connection_seqno(mgr->rconn);
- cancel_all_requests(mgr, ENOTCONN);
- } else if (!mgr->txqlen->n && !mgr->rq && !list_is_empty(&mgr->requests)) {
- struct stats_request *rq;
- int retval;
-
- rq = mgr->rq = CONTAINER_OF(list_pop_front(&mgr->requests),
- struct stats_request, node);
- retval = rconn_send(mgr->rconn, rq->osr, mgr->txqlen);
- if (!retval) {
- /* rconn_send() consumed the message. */
- rq->osr = NULL;
- mgr->timeout = time_now() + STATS_TIMEOUT;
- } else {
- /* Sending failed, although it shouldn't have since we already
- * checked that we were connected. Odd. */
- cancel_all_requests(mgr, ENOTCONN);
- }
- }
-}