#define THIS_MODULE VLM_datapath
#include "vlog.h"
-#define BRIDGE_PORT_NO_FLOOD 0x00000001
+enum br_port_flags {
+ BRPF_NO_FLOOD = 1 << 0,
+};
+
+enum br_port_status {
+ BRPS_PORT_DOWN = 1 << 0,
+ BRPS_LINK_DOWN = 1 << 1,
+};
+
+extern char mfr_desc;
+extern char hw_desc;
+extern char sw_desc;
/* Capabilities supported by this implementation. */
#define OFP_SUPPORTED_CAPABILITIES ( OFPC_FLOW_STATS \
| (1 << OFPAT_SET_TP_DST) )
struct sw_port {
- uint32_t flags;
+ uint32_t flags; /* BRPF_* flags */
+ uint32_t status; /* BRPS_* flags */
struct datapath *dp;
struct netdev *netdev;
struct list node; /* Element in datapath.ports. */
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 *);
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);
+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 *,
}
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;
}
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);
fwd_port_input(dp, buffer, port_no(dp, 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);
retval = vconn_accept(dp->listen_vconn, &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;
if (port_no(dp, p) == in_port) {
continue;
}
- if (flood && p->flags & BRIDGE_PORT_NO_FLOOD) {
+ if (flood && p->flags & BRPF_NO_FLOOD) {
continue;
}
if (prev_port != -1) {
{
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 & BRPS_PORT_DOWN)) {
if (!netdev_send(p->netdev, buffer)) {
p->tx_packets++;
p->tx_bytes += buffer->size;
}
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'.
}
} else {
if (in_port == out_port) {
- /* FIXME: ratelimit */
- VLOG_DBG("can't directly forward to input port");
+ VLOG_DBG_RL(&rl, "can't directly forward to input port");
return;
}
output_packet(dp, buffer, out_port);
? rconn_send(rconn, buffer, &remote->n_txq)
: EAGAIN);
if (retval) {
- VLOG_WARN("send to %s failed: %s",
- rconn_get_name(rconn), strerror(retval));
+ VLOG_WARN_RL(&rl, "send to %s failed: %s",
+ rconn_get_name(rconn), strerror(retval));
buffer_delete(buffer);
}
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));
+
+ if (p->flags & BRPF_NO_FLOOD) {
+ desc->flags |= htonl(OFPPFL_NO_FLOOD);
+ } else if (p->status & BRPS_PORT_DOWN) {
+ desc->flags |= htonl(OFPPFL_PORT_DOWN);
+ } else if (p->status & BRPS_LINK_DOWN) {
+ desc->flags |= htonl(OFPPFL_LINK_DOWN);
+ }
}
static void
}
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];
ETH_ADDR_LEN) != 0) {
return;
}
- p->flags = htonl(opp->flags);
+
+
+ if (opm->mask & htonl(OFPPFL_NO_FLOOD)) {
+ if (opp->flags & htonl(OFPPFL_NO_FLOOD))
+ p->flags |= BRPF_NO_FLOOD;
+ else
+ p->flags &= ~BRPF_NO_FLOOD;
+ }
+
+ if (opm->mask & htonl(OFPPFL_PORT_DOWN)) {
+ if ((opp->flags & htonl(OFPPFL_PORT_DOWN))
+ && (p->status & BRPS_PORT_DOWN) == 0) {
+ p->status |= BRPS_PORT_DOWN;
+ netdev_turn_flags_off(p->netdev, NETDEV_UP, true);
+ } else if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) == 0
+ && (p->status & BRPS_PORT_DOWN)) {
+ p->status &= ~BRPS_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 &= ~BRPS_PORT_DOWN;
+ } else {
+ p->status |= BRPS_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 &= ~BRPS_LINK_DOWN;
+ } else if (retval == 0) {
+ p->status |= BRPS_LINK_DOWN;
+ }
+
+ return (orig_status != p->status);
+}
+
static void
send_port_status(struct sw_port *p, uint8_t status)
{
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);
int act_len = n_actions * sizeof opo->actions[0];
if (act_len > (ntohs(opo->header.length) - sizeof *opo)) {
- VLOG_DBG("message too short for number of actions");
+ VLOG_DBG_RL(&rl, "message too short for number of actions");
return -EINVAL;
}
{
const struct ofp_port_mod *opm = msg;
- dp_update_port_flags(dp, &opm->desc);
+ dp_update_port_flags(dp, opm);
return 0;
}
}
}
+static int version_stats_dump(struct datapath *dp, void *state,
+ struct buffer *buffer)
+{
+ struct ofp_version_stats *ovs = buffer_put_uninit(buffer, sizeof *ovs);
+
+ strncpy(ovs->mfr_desc, &mfr_desc, sizeof ovs->mfr_desc);
+ strncpy(ovs->hw_desc, &hw_desc, sizeof ovs->hw_desc);
+ strncpy(ovs->sw_desc, &sw_desc, sizeof ovs->sw_desc);
+
+ return 0;
+}
+
struct flow_stats_state {
int table_idx;
struct sw_table_position position;
};
static const struct stats_type stats[] = {
+ [OFPST_VERSION] = {
+ 0,
+ 0,
+ NULL,
+ version_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);
+ 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;
}
}