static void delete_flow__(struct rule *rule, struct ofopgroup *,
enum ofp_flow_removed_reason)
OVS_REQUIRES(ofproto_mutex);
+static bool ofproto_group_exists__(const struct ofproto *ofproto,
+ uint32_t group_id)
+ OVS_REQ_RDLOCK(ofproto->groups_rwlock);
+static bool ofproto_group_exists(const struct ofproto *ofproto,
+ uint32_t group_id)
+ OVS_EXCLUDED(ofproto->groups_rwlock);
static enum ofperr add_group(struct ofproto *, struct ofputil_group_mod *);
static bool handle_openflow(struct ofconn *, const struct ofpbuf *);
static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
ovs_rwlock_init(&ofproto->groups_rwlock);
hmap_init(&ofproto->groups);
ovs_mutex_unlock(&ofproto_mutex);
+ ofproto->ogf.actions[0] =
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) (1 << ENUM) |
+#include "ofp-util.def"
+ 0;
error = ofproto->ofproto_class->construct(ofproto);
if (error) {
return error;
}
+static void
+flow_mod_init(struct ofputil_flow_mod *fm,
+ const struct match *match, unsigned int priority,
+ const struct ofpact *ofpacts, size_t ofpacts_len,
+ enum ofp_flow_mod_command command)
+{
+ memset(fm, 0, sizeof *fm);
+ fm->match = *match;
+ fm->priority = priority;
+ fm->cookie = 0;
+ fm->new_cookie = 0;
+ fm->modify_cookie = false;
+ fm->table_id = 0;
+ fm->command = command;
+ fm->idle_timeout = 0;
+ fm->hard_timeout = 0;
+ fm->buffer_id = UINT32_MAX;
+ fm->out_port = OFPP_ANY;
+ fm->out_group = OFPG_ANY;
+ fm->flags = 0;
+ fm->ofpacts = CONST_CAST(struct ofpact *, ofpacts);
+ fm->ofpacts_len = ofpacts_len;
+}
+
static int
simple_flow_mod(struct ofproto *ofproto,
const struct match *match, unsigned int priority,
{
struct ofputil_flow_mod fm;
- memset(&fm, 0, sizeof fm);
- fm.match = *match;
- fm.priority = priority;
- fm.cookie = 0;
- fm.new_cookie = 0;
- fm.modify_cookie = false;
- fm.table_id = 0;
- fm.command = command;
- fm.idle_timeout = 0;
- fm.hard_timeout = 0;
- fm.buffer_id = UINT32_MAX;
- fm.out_port = OFPP_ANY;
- fm.out_group = OFPG_ANY;
- fm.flags = 0;
- fm.ofpacts = CONST_CAST(struct ofpact *, ofpacts);
- fm.ofpacts_len = ofpacts_len;
+ flow_mod_init(&fm, match, priority, ofpacts, ofpacts_len, command);
return handle_flow_mod__(ofproto, NULL, &fm, NULL);
}
}
}
-/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are appropriate
- * for a packet with the prerequisites satisfied by 'flow' in table 'table_id'.
- * 'flow' may be temporarily modified, but is restored at return.
- */
+/* Checks that the 'ofpacts_len' bytes of action in 'ofpacts' are appropriate
+ * for 'ofproto':
+ *
+ * - If they use a meter, then 'ofproto' has that meter configured.
+ *
+ * - If they use any groups, then 'ofproto' has that group configured.
+ *
+ * Returns 0 if successful, otherwise an OpenFlow error. */
static enum ofperr
ofproto_check_ofpacts(struct ofproto *ofproto,
- const struct ofpact ofpacts[], size_t ofpacts_len,
- struct flow *flow, uint8_t table_id,
- const struct ofp_header *oh)
+ const struct ofpact ofpacts[], size_t ofpacts_len)
{
- enum ofperr error;
+ const struct ofpact *a;
uint32_t mid;
- error = ofpacts_check(ofpacts, ofpacts_len, flow,
- u16_to_ofp(ofproto->max_ports), table_id,
- oh && oh->version > OFP10_VERSION);
- if (error) {
- return error;
- }
-
mid = ofpacts_get_meter(ofpacts, ofpacts_len);
if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
return OFPERR_OFPMMFC_INVALID_METER;
}
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ if (a->type == OFPACT_GROUP
+ && !ofproto_group_exists(ofproto, ofpact_get_GROUP(a)->group_id)) {
+ return OFPERR_OFPBAC_BAD_OUT_GROUP;
+ }
+ }
+
return 0;
}
/* 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, oh);
+ error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len);
if (!error) {
error = p->ofproto_class->packet_out(p, payload, &flow,
po.ofpacts, po.ofpacts_len);
}
}
- /* Verify actions. */
- error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len,
- &fm->match.flow, table_id, request);
- if (error) {
- cls_rule_destroy(&cr);
- return error;
- }
-
/* Serialize against pending deletion. */
if (is_flow_deletion_pending(ofproto, &cr, table_id)) {
cls_rule_destroy(&cr);
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;
ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
- &ofpacts);
+ &ofpacts,
+ u16_to_ofp(ofproto->max_ports),
+ ofproto->n_tables);
+ if (!error) {
+ error = ofproto_check_ofpacts(ofproto, fm.ofpacts, fm.ofpacts_len);
+ }
if (!error) {
error = handle_flow_mod__(ofproto, ofconn, &fm, oh);
}
}
static bool
-ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
+ofproto_group_exists__(const struct ofproto *ofproto, uint32_t group_id)
OVS_REQ_RDLOCK(ofproto->groups_rwlock)
{
struct ofgroup *grp;
return false;
}
+static bool
+ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
+ OVS_EXCLUDED(ofproto->groups_rwlock)
+{
+ bool exists;
+
+ ovs_rwlock_rdlock(&ofproto->groups_rwlock);
+ exists = ofproto_group_exists__(ofproto, group_id);
+ ovs_rwlock_unlock(&ofproto->groups_rwlock);
+
+ return exists;
+}
+
+static uint32_t
+group_get_ref_count(struct ofgroup *group)
+ OVS_EXCLUDED(ofproto_mutex)
+{
+ struct ofproto *ofproto = group->ofproto;
+ struct rule_criteria criteria;
+ struct rule_collection rules;
+ struct match match;
+ enum ofperr error;
+ uint32_t count;
+
+ match_init_catchall(&match);
+ rule_criteria_init(&criteria, 0xff, &match, 0, htonll(0), htonll(0),
+ OFPP_ANY, group->group_id);
+ ovs_mutex_lock(&ofproto_mutex);
+ error = collect_rules_loose(ofproto, &criteria, &rules);
+ ovs_mutex_unlock(&ofproto_mutex);
+ rule_criteria_destroy(&criteria);
+
+ count = !error && rules.n < UINT32_MAX ? rules.n : UINT32_MAX;
+
+ rule_collection_destroy(&rules);
+ return count;
+}
+
static void
append_group_stats(struct ofgroup *group, struct list *replies)
OVS_REQ_RDLOCK(group->rwlock)
ogs.bucket_stats = xmalloc(group->n_buckets * sizeof *ogs.bucket_stats);
+ /* Provider sets the packet and byte counts, we do the rest. */
+ ogs.ref_count = group_get_ref_count(group);
+ ogs.n_buckets = group->n_buckets;
+
error = (ofproto->ofproto_class->group_get_stats
? ofproto->ofproto_class->group_get_stats(group, &ogs)
: EOPNOTSUPP);
if (error) {
- ogs.ref_count = UINT32_MAX;
ogs.packet_count = UINT64_MAX;
ogs.byte_count = UINT64_MAX;
- ogs.n_buckets = group->n_buckets;
memset(ogs.bucket_stats, 0xff,
ogs.n_buckets * sizeof *ogs.bucket_stats);
}
goto unlock_out;
}
- if (ofproto_group_exists(ofproto, gm->group_id)) {
+ if (ofproto_group_exists__(ofproto, gm->group_id)) {
error = OFPERR_OFPGMFC_GROUP_EXISTS;
goto unlock_out;
}
delete_group__(struct ofproto *ofproto, struct ofgroup *ofgroup)
OVS_RELEASES(ofproto->groups_rwlock)
{
+ struct match match;
+ struct ofputil_flow_mod fm;
+
+ /* Delete all flow entries containing this group in a group action */
+ match_init_catchall(&match);
+ flow_mod_init(&fm, &match, 0, NULL, 0, OFPFC_DELETE);
+ fm.out_group = ofgroup->group_id;
+ handle_flow_mod__(ofproto, NULL, &fm, NULL);
+
/* Must wait until existing readers are done,
* while holding the container's write lock at the same time. */
ovs_rwlock_wrlock(&ofgroup->rwlock);
ofproto->vlans_changed = false;
OFPROTO_FOR_EACH_TABLE (oftable, ofproto) {
- const struct cls_table *table;
+ const struct cls_subtable *table;
ovs_rwlock_rdlock(&oftable->cls.rwlock);
- HMAP_FOR_EACH (table, hmap_node, &oftable->cls.tables) {
+ HMAP_FOR_EACH (table, hmap_node, &oftable->cls.subtables) {
if (minimask_get_vid_mask(&table->mask) == VLAN_VID_MASK) {
const struct cls_rule *rule;