VLOG_DEFINE_THIS_MODULE(ofproto);
-COVERAGE_DEFINE(ofproto_error);
COVERAGE_DEFINE(ofproto_flush);
-COVERAGE_DEFINE(ofproto_no_packet_in);
COVERAGE_DEFINE(ofproto_packet_out);
COVERAGE_DEFINE(ofproto_queue_req);
COVERAGE_DEFINE(ofproto_recv_openflow);
COVERAGE_DEFINE(ofproto_reinit_ports);
-COVERAGE_DEFINE(ofproto_uninstallable);
COVERAGE_DEFINE(ofproto_update_port);
enum ofproto_state {
static int init_ports(struct ofproto *);
static void reinit_ports(struct ofproto *);
+static long long int ofport_get_usage(const struct ofproto *,
+ ofp_port_t ofp_port);
+static void ofport_set_usage(struct ofproto *, ofp_port_t ofp_port,
+ long long int last_used);
+static void ofport_remove_usage(struct ofproto *, ofp_port_t ofp_port);
+
+/* Ofport usage.
+ *
+ * Keeps track of the currently used and recently used ofport values and is
+ * used to prevent immediate recycling of ofport values. */
+struct ofport_usage {
+ struct hmap_node hmap_node; /* In struct ofproto's "ofport_usage" hmap. */
+ ofp_port_t ofp_port; /* OpenFlow port number. */
+ long long int last_used; /* Last time the 'ofp_port' was used. LLONG_MAX
+ represents in-use ofports. */
+};
+
/* rule. */
static void ofproto_rule_destroy__(struct rule *);
static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
{
const struct ofproto_class *class = ofproto_class_find__(type);
return class ? class->enumerate_names(type, names) : EAFNOSUPPORT;
- }
+}
int
ofproto_create(const char *datapath_name, const char *datapath_type,
ofproto->dp_desc = NULL;
ofproto->frag_handling = OFPC_FRAG_NORMAL;
hmap_init(&ofproto->ports);
+ hmap_init(&ofproto->ofport_usage);
shash_init(&ofproto->port_by_name);
simap_init(&ofproto->ofp_requests);
ofproto->max_ports = ofp_to_u16(OFPP_MAX);
return error;
}
- /* The "max_ports" member should have been set by ->construct(ofproto).
- * Port 0 is not a valid OpenFlow port, so mark that as unavailable. */
- ofproto->ofp_port_ids = bitmap_allocate(ofproto->max_ports);
- bitmap_set1(ofproto->ofp_port_ids, 0);
-
/* Check that hidden tables, if any, are at the end. */
ovs_assert(ofproto->n_tables);
for (i = 0; i + 1 < ofproto->n_tables; i++) {
}
static void
-ofproto_rule_delete__(struct ofproto *ofproto, struct rule *rule)
+ofproto_rule_delete__(struct ofproto *ofproto, struct rule *rule,
+ uint8_t reason)
OVS_REQUIRES(ofproto_mutex)
{
struct ofopgroup *group;
ovs_assert(!rule->pending);
group = ofopgroup_create_unattached(ofproto);
- delete_flow__(rule, group, OFPRR_DELETE);
+ delete_flow__(rule, group, reason);
ofopgroup_submit(group);
}
ovs_rwlock_unlock(&table->cls.rwlock);
CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
if (!rule->pending) {
- ofproto_rule_delete__(ofproto, rule);
+ ofproto_rule_delete__(ofproto, rule, OFPRR_DELETE);
}
}
}
free(ofproto->serial_desc);
free(ofproto->dp_desc);
hmap_destroy(&ofproto->ports);
+ hmap_destroy(&ofproto->ofport_usage);
shash_destroy(&ofproto->port_by_name);
- bitmap_free(ofproto->ofp_port_ids);
simap_destroy(&ofproto->ofp_requests);
OFPROTO_FOR_EACH_TABLE (table, ofproto) {
OVS_EXCLUDED(ofproto_mutex)
{
struct ofport *ofport, *next_ofport;
+ struct ofport_usage *usage, *next_usage;
if (!p) {
return;
ofport_destroy(ofport);
}
+ HMAP_FOR_EACH_SAFE (usage, next_usage, hmap_node, &p->ofport_usage) {
+ hmap_remove(&p->ofport_usage, &usage->hmap_node);
+ free(usage);
+ }
+
p->ofproto_class->destruct(p);
ofproto_destroy__(p);
}
port_idx = port_idx ? port_idx : UINT16_MAX;
if (port_idx >= ofproto->max_ports
- || bitmap_is_set(ofproto->ofp_port_ids, port_idx)) {
- uint16_t end_port_no = ofproto->alloc_port_no;
+ || ofport_get_usage(ofproto, u16_to_ofp(port_idx)) == LLONG_MAX) {
+ uint16_t lru_ofport = 0, end_port_no = ofproto->alloc_port_no;
+ long long int last_used_at, lru = LLONG_MAX;
/* Search for a free OpenFlow port number. We try not to
* immediately reuse them to prevent problems due to old
* flows. */
for (;;) {
if (++ofproto->alloc_port_no >= ofproto->max_ports) {
- ofproto->alloc_port_no = 0;
+ ofproto->alloc_port_no = 1;
}
- if (!bitmap_is_set(ofproto->ofp_port_ids,
- ofproto->alloc_port_no)) {
+ last_used_at = ofport_get_usage(ofproto,
+ u16_to_ofp(ofproto->alloc_port_no));
+ if (!last_used_at) {
+ port_idx = ofproto->alloc_port_no;
+ break;
+ } else if ( last_used_at < time_msec() - 60*60*1000) {
+ /* If the port with ofport 'ofproto->alloc_port_no' was deleted
+ * more than an hour ago, consider it usable. */
+ ofport_remove_usage(ofproto,
+ u16_to_ofp(ofproto->alloc_port_no));
port_idx = ofproto->alloc_port_no;
break;
+ } else if (last_used_at < lru) {
+ lru = last_used_at;
+ lru_ofport = ofproto->alloc_port_no;
}
+
if (ofproto->alloc_port_no == end_port_no) {
+ if (lru_ofport) {
+ port_idx = lru_ofport;
+ break;
+ }
return OFPP_NONE;
}
}
}
- bitmap_set1(ofproto->ofp_port_ids, port_idx);
+ ofport_set_usage(ofproto, u16_to_ofp(port_idx), LLONG_MAX);
return u16_to_ofp(port_idx);
}
static void
-dealloc_ofp_port(const struct ofproto *ofproto, ofp_port_t ofp_port)
+dealloc_ofp_port(struct ofproto *ofproto, ofp_port_t ofp_port)
{
if (ofp_to_u16(ofp_port) < ofproto->max_ports) {
- bitmap_set0(ofproto->ofp_port_ids, ofp_to_u16(ofp_port));
+ ofport_set_usage(ofproto, ofp_port, time_msec());
}
}
return NULL;
}
+static long long int
+ofport_get_usage(const struct ofproto *ofproto, ofp_port_t ofp_port)
+{
+ struct ofport_usage *usage;
+
+ HMAP_FOR_EACH_IN_BUCKET (usage, hmap_node, hash_ofp_port(ofp_port),
+ &ofproto->ofport_usage) {
+ if (usage->ofp_port == ofp_port) {
+ return usage->last_used;
+ }
+ }
+ return 0;
+}
+
+static void
+ofport_set_usage(struct ofproto *ofproto, ofp_port_t ofp_port,
+ long long int last_used)
+{
+ struct ofport_usage *usage;
+ HMAP_FOR_EACH_IN_BUCKET (usage, hmap_node, hash_ofp_port(ofp_port),
+ &ofproto->ofport_usage) {
+ if (usage->ofp_port == ofp_port) {
+ usage->last_used = last_used;
+ return;
+ }
+ }
+ ovs_assert(last_used == LLONG_MAX);
+
+ usage = xmalloc(sizeof *usage);
+ usage->ofp_port = ofp_port;
+ usage->last_used = last_used;
+ hmap_insert(&ofproto->ofport_usage, &usage->hmap_node,
+ hash_ofp_port(ofp_port));
+}
+
+static void
+ofport_remove_usage(struct ofproto *ofproto, ofp_port_t ofp_port)
+{
+ struct ofport_usage *usage;
+ HMAP_FOR_EACH_IN_BUCKET (usage, hmap_node, hash_ofp_port(ofp_port),
+ &ofproto->ofport_usage) {
+ if (usage->ofp_port == ofp_port) {
+ hmap_remove(&ofproto->ofport_usage, &usage->hmap_node);
+ free(usage);
+ break;
+ }
+ }
+}
+
int
ofproto_port_get_stats(const struct ofport *port, struct netdev_stats *stats)
{
static enum ofperr
ofproto_check_ofpacts(struct ofproto *ofproto,
const struct ofpact ofpacts[], size_t ofpacts_len,
- struct flow *flow, uint8_t table_id)
+ struct flow *flow, uint8_t table_id,
+ const struct ofp_header *oh)
{
enum ofperr error;
uint32_t mid;
error = ofpacts_check(ofpacts, ofpacts_len, flow,
- u16_to_ofp(ofproto->max_ports), table_id);
+ u16_to_ofp(ofproto->max_ports), table_id,
+ oh && oh->version > OFP10_VERSION);
if (error) {
return error;
}
/* Verify actions against packet, then send packet if successful. */
in_port_.ofp_port = po.in_port;
flow_extract(payload, 0, 0, NULL, &in_port_, &flow);
- error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len, &flow, 0);
+ error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len, &flow, 0, oh);
if (!error) {
error = p->ofproto_class->packet_out(p, payload, &flow,
po.ofpacts, po.ofpacts_len);
cls_rule_format(&rule->cr, results);
ds_put_char(results, ',');
+ ds_put_cstr(results, "actions=");
ofpacts_format(actions->ofpacts, actions->ofpacts_len, results);
ds_put_cstr(results, "\n");
/* Verify actions. */
error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len,
- &fm->match.flow, table_id);
+ &fm->match.flow, table_id, request);
if (error) {
cls_rule_destroy(&cr);
return error;
enum ofperr error;
size_t i;
+ /* Verify actions before we start to modify any rules, to avoid partial
+ * flow table modifications. */
+ for (i = 0; i < rules->n; i++) {
+ struct rule *rule = rules->rules[i];
+
+ error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len,
+ &fm->match.flow, rule->table_id,
+ request);
+ if (error) {
+ return error;
+ }
+ }
+
type = fm->command == OFPFC_ADD ? OFOPERATION_REPLACE : OFOPERATION_MODIFY;
group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
error = OFPERR_OFPBRC_EPERM;
continue;
}
- /* Verify actions. */
- error = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
- u16_to_ofp(ofproto->max_ports), rule->table_id);
- if (error) {
- return error;
- }
-
actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
rule->actions->ofpacts,
rule->actions->ofpacts_len);
ovs_assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT
|| reason == OFPRR_DELETE || reason == OFPRR_GROUP_DELETE);
- ofproto_rule_send_removed(rule, reason);
- ofproto_rule_delete__(ofproto, rule);
+ ofproto_rule_delete__(ofproto, rule, reason);
}
/* Reduces '*timeout' to no more than 'max'. A value of zero in either case
return 0;
error:
- ovs_mutex_unlock(&ofproto_mutex);
-
for (i = 0; i < n_monitors; i++) {
ofmonitor_destroy(monitors[i]);
}
free(monitors);
+ ovs_mutex_unlock(&ofproto_mutex);
+
return error;
}
if (error) {
return error;
}
+ if (oh->version >= OFP13_VERSION && ofpmsg_is_stat_request(oh)
+ && ofpmp_more(oh)) {
+ /* We have no buffer implementation for multipart requests.
+ * Report overflow for requests which consists of multiple
+ * messages. */
+ return OFPERR_OFPBRC_MULTIPART_BUFFER_OVERFLOW;
+ }
switch (type) {
/* OpenFlow requests. */
/* FIXME: Change the following once they are implemented: */
case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
- return OFPERR_OFPBRC_BAD_TYPE;
+ /* fallthrough */
case OFPTYPE_HELLO:
case OFPTYPE_ERROR:
case OFPTYPE_METER_FEATURES_STATS_REPLY:
case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
default:
- return OFPERR_OFPBRC_BAD_TYPE;
+ if (ofpmsg_is_stat_request(oh)) {
+ return OFPERR_OFPBRC_BAD_STAT;
+ } else {
+ return OFPERR_OFPBRC_BAD_TYPE;
+ }
}
}