static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-static void
-ofproto_initialize(void)
+/* Must be called to initialize the ofproto library.
+ *
+ * The caller may pass in 'iface_hints', which contains an shash of
+ * "iface_hint" elements indexed by the interface's name. The provider
+ * may use these hints to describe the startup configuration in order to
+ * reinitialize its state. The caller owns the provided data, so a
+ * provider will make copies of anything required. An ofproto provider
+ * will remove any existing state that is not described by the hint, and
+ * may choose to remove it all. */
+void
+ofproto_init(const struct shash *iface_hints)
{
- static bool inited;
+ size_t i;
- if (!inited) {
- inited = true;
- ofproto_class_register(&ofproto_dpif_class);
+ ofproto_class_register(&ofproto_dpif_class);
+
+ for (i = 0; i < n_ofproto_classes; i++) {
+ ofproto_classes[i]->init(iface_hints);
}
}
{
size_t i;
- ofproto_initialize();
for (i = 0; i < n_ofproto_classes; i++) {
const struct ofproto_class *class = ofproto_classes[i];
struct sset types;
{
size_t i;
- ofproto_initialize();
for (i = 0; i < n_ofproto_classes; i++) {
ofproto_classes[i]->enumerate_types(types);
}
*ofprotop = NULL;
- ofproto_initialize();
ofproto_unixctl_init();
datapath_type = ofproto_normalize_type(datapath_type);
hash_string(ofproto->name, 0));
ofproto->datapath_id = 0;
ofproto_set_flow_eviction_threshold(ofproto,
- OFPROTO_FLOW_EVICTON_THRESHOLD_DEFAULT);
+ OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT);
ofproto->forward_bpdu = false;
ofproto->fallback_dpid = pick_fallback_dpid();
ofproto->mfr_desc = xstrdup(DEFAULT_MFR_DESC);
return dump->error == EOF ? 0 : dump->error;
}
-/* Attempts to add 'netdev' as a port on 'ofproto'. If successful, returns 0
- * and sets '*ofp_portp' to the new port's OpenFlow port number (if 'ofp_portp'
- * is non-null). On failure, returns a positive errno value and sets
- * '*ofp_portp' to OFPP_NONE (if 'ofp_portp' is non-null). */
+/* Attempts to add 'netdev' as a port on 'ofproto'. If 'ofp_portp' is
+ * non-null and '*ofp_portp' is not OFPP_NONE, attempts to use that as
+ * the port's OpenFlow port number.
+ *
+ * If successful, returns 0 and sets '*ofp_portp' to the new port's
+ * OpenFlow port number (if 'ofp_portp' is non-null). On failure,
+ * returns a positive errno value and sets '*ofp_portp' to OFPP_NONE (if
+ * 'ofp_portp' is non-null). */
int
ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
uint16_t *ofp_portp)
{
- uint16_t ofp_port;
+ uint16_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE;
int error;
error = ofproto->ofproto_class->port_add(ofproto, netdev, &ofp_port);
assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in));
- flow_extract(packet, 0, 0, in_port, &flow);
+ flow_extract(packet, 0, NULL, in_port, &flow);
return rule->ofproto->ofproto_class->rule_execute(rule, &flow, packet);
}
goto exit_free_ofpacts;
}
if (po.in_port >= p->max_ports && po.in_port < OFPP_MAX) {
- error = OFPERR_NXBRC_BAD_IN_PORT;
+ error = OFPERR_OFPBRC_BAD_PORT;
goto exit_free_ofpacts;
}
+
/* Get payload. */
if (po.buffer_id != UINT32_MAX) {
error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL);
}
/* Verify actions against packet, then send packet if successful. */
- flow_extract(payload, 0, 0, po.in_port, &flow);
+ flow_extract(payload, 0, NULL, po.in_port, &flow);
error = ofpacts_check(po.ofpacts, po.ofpacts_len, &flow, p->max_ports);
if (!error) {
error = p->ofproto_class->packet_out(p, payload, &flow,
const struct ofp_header *request)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
- struct ofp10_table_stats *ots;
+ struct ofp12_table_stats *ots;
struct ofpbuf *msg;
size_t i;
- msg = ofpraw_alloc_stats_reply(request, sizeof *ots * p->n_tables);
- ots = ofpbuf_put_zeros(msg, sizeof *ots * p->n_tables);
+ /* Set up default values.
+ *
+ * ofp12_table_stats is used as a generic structure as
+ * it is able to hold all the fields for ofp10_table_stats
+ * and ofp11_table_stats (and of course itself).
+ */
+ ots = xcalloc(p->n_tables, sizeof *ots);
for (i = 0; i < p->n_tables; i++) {
ots[i].table_id = i;
sprintf(ots[i].name, "table%zu", i);
- ots[i].wildcards = htonl(OFPFW10_ALL);
+ ots[i].match = htonll(OFPXMT12_MASK);
+ ots[i].wildcards = htonll(OFPXMT12_MASK);
+ ots[i].write_actions = htonl(OFPAT11_OUTPUT);
+ ots[i].apply_actions = htonl(OFPAT11_OUTPUT);
+ ots[i].write_setfields = htonll(OFPXMT12_MASK);
+ ots[i].apply_setfields = htonll(OFPXMT12_MASK);
+ ots[i].metadata_match = htonll(UINT64_MAX);
+ ots[i].metadata_write = htonll(UINT64_MAX);
+ ots[i].instructions = htonl(OFPIT11_ALL);
+ ots[i].config = htonl(OFPTC11_TABLE_MISS_MASK);
ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
ots[i].active_count = htonl(classifier_count(&p->tables[i].cls));
}
}
}
+ msg = ofputil_encode_table_stats_reply(ots, p->n_tables, request);
ofconn_send_reply(ofconn, msg);
+
+ free(ots);
+
return 0;
}
static void
append_port_stat(struct ofport *port, struct list *replies)
{
- struct netdev_stats stats;
- struct ofp10_port_stats *ops;
+ struct ofputil_port_stats ops = { .port_no = port->pp.port_no };
/* Intentionally ignore return value, since errors will set
* 'stats' to all-1s, which is correct for OpenFlow, and
* netdev_get_stats() will log errors. */
- ofproto_port_get_stats(port, &stats);
-
- ops = ofpmp_append(replies, sizeof *ops);
- ops->port_no = htons(port->pp.port_no);
- memset(ops->pad, 0, sizeof ops->pad);
- put_32aligned_be64(&ops->rx_packets, htonll(stats.rx_packets));
- put_32aligned_be64(&ops->tx_packets, htonll(stats.tx_packets));
- put_32aligned_be64(&ops->rx_bytes, htonll(stats.rx_bytes));
- put_32aligned_be64(&ops->tx_bytes, htonll(stats.tx_bytes));
- put_32aligned_be64(&ops->rx_dropped, htonll(stats.rx_dropped));
- put_32aligned_be64(&ops->tx_dropped, htonll(stats.tx_dropped));
- put_32aligned_be64(&ops->rx_errors, htonll(stats.rx_errors));
- put_32aligned_be64(&ops->tx_errors, htonll(stats.tx_errors));
- put_32aligned_be64(&ops->rx_frame_err, htonll(stats.rx_frame_errors));
- put_32aligned_be64(&ops->rx_over_err, htonll(stats.rx_over_errors));
- put_32aligned_be64(&ops->rx_crc_err, htonll(stats.rx_crc_errors));
- put_32aligned_be64(&ops->collisions, htonll(stats.collisions));
+ ofproto_port_get_stats(port, &ops.stats);
+
+ ofputil_append_port_stat(replies, &ops);
}
static enum ofperr
const struct ofp_header *request)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
- const struct ofp10_port_stats_request *psr = ofpmsg_body(request);
struct ofport *port;
struct list replies;
+ uint16_t port_no;
+ enum ofperr error;
+
+ error = ofputil_decode_port_stats_request(request, &port_no);
+ if (error) {
+ return error;
+ }
ofpmp_init(&replies, request);
- if (psr->port_no != htons(OFPP_NONE)) {
- port = ofproto_get_port(p, ntohs(psr->port_no));
+ if (port_no != OFPP_NONE) {
+ port = ofproto_get_port(p, port_no);
if (port) {
append_port_stat(port, &replies);
}
{
return (table_id == 0xff || table_id < ofproto->n_tables
? 0
- : OFPERR_NXBRC_BAD_TABLE_ID);
+ : OFPERR_OFPBRC_BAD_TABLE_ID);
}
long long int now = time_msec();
struct ofputil_flow_stats fs;
- fs.match = rule->cr.match;
+ minimatch_expand(&rule->cr.match, &fs.match);
fs.priority = rule->cr.priority;
fs.cookie = rule->flow_cookie;
fs.table_id = rule->table_id;
put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id,
const struct netdev_queue_stats *stats)
{
- struct ofp10_queue_stats *reply;
- reply = ofpmp_append(&cbdata->replies, sizeof *reply);
- reply->port_no = htons(cbdata->ofport->pp.port_no);
- memset(reply->pad, 0, sizeof reply->pad);
- reply->queue_id = htonl(queue_id);
- put_32aligned_be64(&reply->tx_bytes, htonll(stats->tx_bytes));
- put_32aligned_be64(&reply->tx_packets, htonll(stats->tx_packets));
- put_32aligned_be64(&reply->tx_errors, htonll(stats->tx_errors));
+ struct ofputil_queue_stats oqs = {
+ .port_no = cbdata->ofport->pp.port_no,
+ .queue_id = queue_id,
+ .stats = *stats,
+ };
+ ofputil_append_queue_stat(&cbdata->replies, &oqs);
}
static void
const struct ofp_header *rq)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- const struct ofp10_queue_stats_request *qsr = ofpmsg_body(rq);
struct queue_stats_cbdata cbdata;
- unsigned int port_no;
struct ofport *port;
- uint32_t queue_id;
enum ofperr error;
+ struct ofputil_queue_stats_request oqsr;
COVERAGE_INC(ofproto_queue_req);
ofpmp_init(&cbdata.replies, rq);
- port_no = ntohs(qsr->port_no);
- queue_id = ntohl(qsr->queue_id);
- if (port_no == OFPP_ALL) {
+ error = ofputil_decode_queue_stats_request(rq, &oqsr);
+ if (error) {
+ return error;
+ }
+
+ if (oqsr.port_no == OFPP_ALL) {
error = OFPERR_OFPQOFC_BAD_QUEUE;
HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
- if (!handle_queue_stats_for_port(port, queue_id, &cbdata)) {
+ if (!handle_queue_stats_for_port(port, oqsr.queue_id, &cbdata)) {
error = 0;
}
}
} else {
- port = ofproto_get_port(ofproto, port_no);
+ port = ofproto_get_port(ofproto, oqsr.port_no);
error = (port
- ? handle_queue_stats_for_port(port, queue_id, &cbdata)
+ ? handle_queue_stats_for_port(port, oqsr.queue_id, &cbdata)
: OFPERR_OFPQOFC_BAD_PORT);
}
if (!error) {
} else if (fm->table_id < ofproto->n_tables) {
table = &ofproto->tables[fm->table_id];
} else {
- return OFPERR_NXFMFC_BAD_TABLE_ID;
+ return OFPERR_OFPBRC_BAD_TABLE_ID;
}
if (table->flags & OFTABLE_READONLY) {
rule->evictable = was_evictable;
if (!evict) {
- error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ error = OFPERR_OFPFMFC_TABLE_FULL;
goto exit;
} else if (evict->pending) {
error = OFPROTO_POSTPONE;
return error;
}
+static enum ofperr
+modify_flows_add(struct ofproto *ofproto, struct ofconn *ofconn,
+ const struct ofputil_flow_mod *fm,
+ const struct ofp_header *request)
+{
+ if (fm->cookie_mask != htonll(0) || fm->new_cookie == htonll(UINT64_MAX)) {
+ return 0;
+ }
+ return add_flow(ofproto, ofconn, fm, request);
+}
+
/* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code on
* failure.
*
if (error) {
return error;
} else if (list_is_empty(&rules)) {
- return fm->cookie_mask ? 0 : add_flow(ofproto, ofconn, fm, request);
+ return modify_flows_add(ofproto, ofconn, fm, request);
} else {
return modify_flows__(ofproto, ofconn, fm, request, &rules);
}
if (error) {
return error;
} else if (list_is_empty(&rules)) {
- return fm->cookie_mask ? 0 : add_flow(ofproto, ofconn, fm, request);
+ return modify_flows_add(ofproto, ofconn, fm, request);
} else {
return list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
fm, request, &rules)
return;
}
- fr.match = rule->cr.match;
+ minimatch_expand(&rule->cr.match, &fr.match);
fr.priority = rule->cr.priority;
fr.cookie = rule->flow_cookie;
fr.reason = reason;
+ fr.table_id = rule->table_id;
calc_flow_duration__(rule->created, time_msec(),
&fr.duration_sec, &fr.duration_nsec);
fr.idle_timeout = rule->idle_timeout;
}
if (fm.flags & OFPFF10_EMERG) {
- /* We do not support the OpenFlow 1.0 emergency flow cache, which is not
- * required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. */
- /* We do not support the emergency flow cache. It will hopefully get
- * dropped from OpenFlow in the near future. There is no good error
- * code, so just state that the flow table is full. */
- error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ /* We do not support the OpenFlow 1.0 emergency flow cache, which
+ * is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1.
+ * There is no good error code, so just state that the flow table
+ * is full. */
+ error = OFPERR_OFPFMFC_TABLE_FULL;
}
if (!error) {
error = ofpacts_check(fm.ofpacts, fm.ofpacts_len,
{
struct ofoperation *op = rule->pending;
struct ofputil_flow_update fu;
+ struct match match;
if (op && op->type == OFOPERATION_ADD && !op->victim) {
/* We'll report the final flow when the operation completes. Reporting
fu.hard_timeout = rule->hard_timeout;
fu.table_id = rule->table_id;
fu.cookie = rule->flow_cookie;
- fu.match = CONST_CAST(struct match *, &rule->cr.match);
+ minimatch_expand(&rule->cr.match, &match);
+ fu.match = &match;
+ fu.priority = rule->cr.priority;
if (!(flags & NXFMF_ACTIONS)) {
fu.ofpacts = NULL;
fu.ofpacts_len = 0;
const struct oftable *table;
struct cls_rule target;
- cls_rule_init(&target, &m->match, 0);
+ cls_rule_init_from_minimatch(&target, &m->match, 0);
FOR_EACH_MATCHING_TABLE (table, m->table_id, ofproto) {
struct cls_cursor cursor;
struct rule *rule;
switch (op->type) {
case OFOPERATION_ADD:
if (!op->error) {
+ uint16_t vid_mask;
+
ofproto_rule_destroy__(op->victim);
- if ((rule->cr.match.wc.masks.vlan_tci & htons(VLAN_VID_MASK))
- == htons(VLAN_VID_MASK)) {
+ vid_mask = minimask_get_vid_mask(&rule->cr.match.mask);
+ if (vid_mask == VLAN_VID_MASK) {
if (ofproto->vlan_bitmap) {
- uint16_t vid;
-
- vid = vlan_tci_to_vid(rule->cr.match.flow.vlan_tci);
+ uint16_t vid = miniflow_get_vid(&rule->cr.match.flow);
if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
bitmap_set1(ofproto->vlan_bitmap, vid);
ofproto->vlans_changed = true;
{
struct oftable *table = &rule->ofproto->tables[rule->table_id];
const struct mf_subfield *sf;
+ struct flow flow;
uint32_t hash;
hash = table->eviction_group_id_basis;
+ miniflow_expand(&rule->cr.match.flow, &flow);
for (sf = table->eviction_fields;
sf < &table->eviction_fields[table->n_eviction_fields];
sf++)
{
- if (mf_are_prereqs_ok(sf->field, &rule->cr.match.flow)) {
+ if (mf_are_prereqs_ok(sf->field, &flow)) {
union mf_value value;
- mf_get_value(sf->field, &rule->cr.match.flow, &value);
+ mf_get_value(sf->field, &flow, &value);
if (sf->ofs) {
bitwise_zero(&value, sf->field->n_bytes, 0, sf->ofs);
}
const struct cls_table *table;
HMAP_FOR_EACH (table, hmap_node, &oftable->cls.tables) {
- if ((table->wc.masks.vlan_tci & htons(VLAN_VID_MASK))
- == htons(VLAN_VID_MASK)) {
+ if (minimask_get_vid_mask(&table->mask) == VLAN_VID_MASK) {
const struct cls_rule *rule;
HMAP_FOR_EACH (rule, hmap_node, &table->rules) {
- uint16_t vid = vlan_tci_to_vid(rule->match.flow.vlan_tci);
+ uint16_t vid = miniflow_get_vid(&rule->match.flow);
bitmap_set1(vlan_bitmap, vid);
bitmap_set1(ofproto->vlan_bitmap, vid);
}