+ * For "loose" matching, the 'priority' parameter is unimportant and may be
+ * supplied as 0. */
+static void
+rule_criteria_init(struct rule_criteria *criteria, uint8_t table_id,
+ const struct match *match, unsigned int priority,
+ ovs_be64 cookie, ovs_be64 cookie_mask,
+ ofp_port_t out_port, uint32_t out_group)
+{
+ criteria->table_id = table_id;
+ cls_rule_init(&criteria->cr, match, priority);
+ criteria->cookie = cookie;
+ criteria->cookie_mask = cookie_mask;
+ criteria->out_port = out_port;
+ criteria->out_group = out_group;
+}
+
+static void
+rule_criteria_destroy(struct rule_criteria *criteria)
+{
+ cls_rule_destroy(&criteria->cr);
+}
+
+void
+rule_collection_init(struct rule_collection *rules)
+{
+ rules->rules = rules->stub;
+ rules->n = 0;
+ rules->capacity = ARRAY_SIZE(rules->stub);
+}
+
+void
+rule_collection_add(struct rule_collection *rules, struct rule *rule)
+{
+ if (rules->n >= rules->capacity) {
+ size_t old_size, new_size;
+
+ old_size = rules->capacity * sizeof *rules->rules;
+ rules->capacity *= 2;
+ new_size = rules->capacity * sizeof *rules->rules;
+
+ if (rules->rules == rules->stub) {
+ rules->rules = xmalloc(new_size);
+ memcpy(rules->rules, rules->stub, old_size);
+ } else {
+ rules->rules = xrealloc(rules->rules, new_size);
+ }
+ }
+
+ rules->rules[rules->n++] = rule;
+}
+
+void
+rule_collection_ref(struct rule_collection *rules)
+ OVS_REQUIRES(ofproto_mutex)
+{
+ size_t i;
+
+ for (i = 0; i < rules->n; i++) {
+ ofproto_rule_ref(rules->rules[i]);
+ }
+}
+
+void
+rule_collection_unref(struct rule_collection *rules)
+{
+ size_t i;
+
+ for (i = 0; i < rules->n; i++) {
+ ofproto_rule_unref(rules->rules[i]);
+ }
+}
+
+void
+rule_collection_destroy(struct rule_collection *rules)
+{
+ if (rules->rules != rules->stub) {
+ free(rules->rules);
+ }
+}
+
+static enum ofperr
+collect_rule(struct rule *rule, const struct rule_criteria *c,
+ struct rule_collection *rules)
+ OVS_REQUIRES(ofproto_mutex)
+{
+ /* We ordinarily want to skip hidden rules, but there has to be a way for
+ * code internal to OVS to modify and delete them, so if the criteria
+ * specify a priority that can only be for a hidden flow, then allow hidden
+ * rules to be selected. (This doesn't allow OpenFlow clients to meddle
+ * with hidden flows because OpenFlow uses only a 16-bit field to specify
+ * priority.) */
+ if (ofproto_rule_is_hidden(rule) && c->cr.priority <= UINT16_MAX) {
+ return 0;
+ } else if (rule->pending) {
+ return OFPROTO_POSTPONE;
+ } else {
+ if ((c->table_id == rule->table_id || c->table_id == 0xff)
+ && ofproto_rule_has_out_port(rule, c->out_port)
+ && ofproto_rule_has_out_group(rule, c->out_group)
+ && !((rule->flow_cookie ^ c->cookie) & c->cookie_mask)) {
+ rule_collection_add(rules, rule);
+ }
+ return 0;
+ }
+}
+
+/* Searches 'ofproto' for rules that match the criteria in 'criteria'. Matches
+ * on classifiers rules are done in the "loose" way required for OpenFlow
+ * OFPFC_MODIFY and OFPFC_DELETE requests. Puts the selected rules on list
+ * 'rules'.