ofp-actions: Set-Field OF 1.0/1.1 compatibility.
[sliver-openvswitch.git] / ofproto / ofproto.c
index bf145e0..052c8cd 100644 (file)
@@ -271,6 +271,9 @@ static enum ofperr modify_flows__(struct ofproto *, struct ofconn *,
 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 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 *,
@@ -2832,11 +2835,12 @@ reject_slave_controller(struct ofconn *ofconn)
  */
 static enum ofperr
 ofproto_check_ofpacts(struct ofproto *ofproto,
-                      const struct ofpact ofpacts[], size_t ofpacts_len,
+                      struct ofpact ofpacts[], size_t ofpacts_len,
                       struct flow *flow, uint8_t table_id,
                       const struct ofp_header *oh)
 {
     enum ofperr error;
+    const struct ofpact *a;
     uint32_t mid;
 
     error = ofpacts_check(ofpacts, ofpacts_len, flow,
@@ -2846,6 +2850,21 @@ ofproto_check_ofpacts(struct ofproto *ofproto,
         return error;
     }
 
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        if (a->type == OFPACT_GROUP) {
+            bool exists;
+
+            ovs_rwlock_rdlock(&ofproto->groups_rwlock);
+            exists = ofproto_group_exists(ofproto,
+                                          ofpact_get_GROUP(a)->group_id);
+            ovs_rwlock_unlock(&ofproto->groups_rwlock);
+
+            if (!exists) {
+                return OFPERR_OFPBAC_BAD_OUT_GROUP;
+            }
+        }
+    }
+
     mid = ofpacts_get_meter(ofpacts, ofpacts_len);
     if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
         return OFPERR_OFPMMFC_INVALID_METER;
@@ -5316,6 +5335,31 @@ ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
     return false;
 }
 
+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)
@@ -5327,14 +5371,16 @@ append_group_stats(struct ofgroup *group, struct list *replies)
 
     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);
     }