autoconf: Tolerate missing file when grepping.
[sliver-openvswitch.git] / lib / classifier.c
index f4280ef..ae2019f 100644 (file)
 #include <assert.h>
 #include <errno.h>
 #include <netinet/in.h>
+#include "dynamic-string.h"
 #include "flow.h"
 #include "hash.h"
 
 const struct cls_field cls_fields[CLS_N_FIELDS + 1] = {
 #define CLS_FIELD(WILDCARDS, MEMBER, NAME)      \
-    { offsetof(flow_t, MEMBER),                 \
-      sizeof ((flow_t *)0)->MEMBER,             \
+    { offsetof(struct flow, MEMBER),            \
+      sizeof ((struct flow *)0)->MEMBER,        \
       WILDCARDS,                                \
       #NAME },
     CLS_FIELDS
 #undef CLS_FIELD
-    { sizeof(flow_t), 0, 0, "exact" },
+    { sizeof(struct flow), 0, 0, "exact" },
 };
 
-static uint32_t hash_fields(const flow_t *, int table_idx);
-static bool equal_fields(const flow_t *, const flow_t *, int table_idx);
+static uint32_t hash_fields(const struct flow *, int table_idx);
+static bool equal_fields(const struct flow *, const struct flow *,
+                         int table_idx);
 
 static int table_idx_from_wildcards(uint32_t wildcards);
 static struct cls_rule *table_insert(struct hmap *, struct cls_rule *);
@@ -45,17 +47,18 @@ static struct cls_bucket *find_bucket(struct hmap *, size_t hash,
 static struct cls_rule *search_table(const struct hmap *table, int field_idx,
                                      const struct cls_rule *);
 static struct cls_rule *search_exact_table(const struct classifier *,
-                                           size_t hash, const flow_t *);
+                                           size_t hash, const struct flow *);
 static bool rules_match_1wild(const struct cls_rule *fixed,
                               const struct cls_rule *wild, int field_idx);
+static bool rules_match_2wild(const struct cls_rule *wild1,
+                              const struct cls_rule *wild2, int field_idx);
 
 /* Converts the flow in 'flow' into a cls_rule in 'rule', with the given
  * 'wildcards' and 'priority'.*/
 void
-cls_rule_from_flow(struct cls_rule *rule, const flow_t *flow,
-                   uint32_t wildcards, unsigned int priority)
+cls_rule_from_flow(const struct flow *flow, uint32_t wildcards,
+                   unsigned int priority, struct cls_rule *rule)
 {
-    assert(flow->reserved == 0);
     rule->flow = *flow;
     flow_wildcards_init(&rule->wc, wildcards);
     rule->priority = priority;
@@ -63,18 +66,32 @@ cls_rule_from_flow(struct cls_rule *rule, const flow_t *flow,
 }
 
 /* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
- * 'priority'. */
+ * 'priority'.  If 'tun_id_from_cookie' is set then the upper 32 bits of
+ * 'cookie' are stored in the rule as the tunnel ID. */
 void
-cls_rule_from_match(struct cls_rule *rule, const struct ofp_match *match,
-                    unsigned int priority)
+cls_rule_from_match(const struct ofp_match *match, unsigned int priority,
+                    bool tun_id_from_cookie, uint64_t cookie,
+                    struct cls_rule *rule)
 {
     uint32_t wildcards;
-    flow_from_match(&rule->flow, &wildcards, match);
+    flow_from_match(match, tun_id_from_cookie, cookie, &rule->flow, &wildcards);
     flow_wildcards_init(&rule->wc, wildcards);
     rule->priority = rule->wc.wildcards ? priority : UINT16_MAX;
     rule->table_idx = table_idx_from_wildcards(rule->wc.wildcards);
 }
 
+/* Converts 'rule' to a string and returns the string.  The caller must free
+ * the string (with free()). */
+char *
+cls_rule_to_string(const struct cls_rule *rule)
+{
+    struct ds s = DS_EMPTY_INITIALIZER;
+    ds_put_format(&s, "wildcards=%x priority=%u ",
+                  rule->wc.wildcards, rule->priority);
+    flow_format(&s, &rule->flow);
+    return ds_cstr(&s);
+}
+
 /* Prints cls_rule 'rule', for debugging.
  *
  * (The output could be improved and expanded, but this was good enough to
@@ -86,55 +103,6 @@ cls_rule_print(const struct cls_rule *rule)
     flow_print(stdout, &rule->flow);
     putc('\n', stdout);
 }
-
-/* Adjusts pointers around 'old', which must be in classifier 'cls', to
- * compensate for it having been moved in memory to 'new' (e.g. due to
- * realloc()).
- *
- * This function cannot be realized in all possible flow classifier
- * implementations, so we will probably have to change the interface if we
- * change the implementation.  Shouldn't be a big deal though. */
-void
-cls_rule_moved(struct classifier *cls, struct cls_rule *old,
-               struct cls_rule *new)
-{
-    if (old != new) {
-        if (new->wc.wildcards) {
-            list_moved(&new->node.list);
-        } else {
-            hmap_node_moved(&cls->exact_table,
-                            &old->node.hmap, &new->node.hmap);
-        }
-    }
-}
-
-/* Replaces 'old', which must be in classifier 'cls', by 'new' (e.g. due to
- * realloc()); that is, after calling this function 'new' will be in 'cls' in
- * place of 'old'.
- *
- * 'new' and 'old' must be exactly the same: wildcard the same fields, have the
- * same fixed values for non-wildcarded fields, and have the same priority.
- *
- * The caller takes ownership of 'old' and is thus responsible for freeing it,
- * etc., as necessary.
- *
- * This function cannot be realized in all possible flow classifier
- * implementations, so we will probably have to change the interface if we
- * change the implementation.  Shouldn't be a big deal though. */
-void
-cls_rule_replace(struct classifier *cls, const struct cls_rule *old,
-                 struct cls_rule *new)
-{
-    assert(old != new);
-    assert(old->wc.wildcards == new->wc.wildcards);
-    assert(old->priority == new->priority);
-
-    if (new->wc.wildcards) {
-        list_replace(&new->node.list, &old->node.list);
-    } else {
-        hmap_replace(&cls->exact_table, &old->node.hmap, &new->node.hmap);
-    }
-}
 \f
 /* Initializes 'cls' as a classifier that initially contains no classification
  * rules. */
@@ -160,8 +128,7 @@ classifier_destroy(struct classifier *cls)
         struct hmap *tbl;
 
         for (tbl = &cls->tables[0]; tbl < &cls->tables[CLS_N_FIELDS]; tbl++) {
-            HMAP_FOR_EACH_SAFE (bucket, next_bucket,
-                                struct cls_bucket, hmap_node, tbl) {
+            HMAP_FOR_EACH_SAFE (bucket, next_bucket, hmap_node, tbl) {
                 free(bucket);
             }
             hmap_destroy(tbl);
@@ -217,18 +184,6 @@ classifier_insert(struct classifier *cls, struct cls_rule *rule)
     return old;
 }
 
-/* Inserts 'rule' into 'cls'.  Transfers ownership of 'rule' to 'cls'.
- *
- * 'rule' must be an exact-match rule (rule->wc.wildcards must be 0) and 'cls'
- * must not contain any rule with an identical key. */
-void
-classifier_insert_exact(struct classifier *cls, struct cls_rule *rule)
-{
-    hmap_insert(&cls->exact_table, &rule->node.hmap,
-                flow_hash(&rule->flow, 0));
-    cls->n_rules++;
-}
-
 /* Removes 'rule' from 'cls'.  It is caller's responsibility to free 'rule', if
  * this is desirable. */
 void
@@ -257,40 +212,23 @@ classifier_remove(struct classifier *cls, struct cls_rule *rule)
     cls->n_rules--;
 }
 
-/* Finds and returns the highest-priority rule in 'cls' that matches 'flow'.
- * Returns a null pointer if no rules in 'cls' match 'flow'.  If multiple rules
- * of equal priority match 'flow', returns one arbitrarily.
- *
- * (When multiple rules of equal priority happen to fall into the same bucket,
- * rules added more recently take priority over rules added less recently, but
- * this is subject to change and should not be depended upon.) */
-struct cls_rule *
-classifier_lookup(const struct classifier *cls, const flow_t *flow)
-{
-    struct cls_rule *rule = classifier_lookup_exact(cls, flow);
-    if (!rule) {
-        rule = classifier_lookup_wild(cls, flow);
-    }
-    return rule;
-}
-
-struct cls_rule *
-classifier_lookup_exact(const struct classifier *cls, const flow_t *flow)
+static struct cls_rule *
+classifier_lookup_exact(const struct classifier *cls, const struct flow *flow)
 {
     return (!hmap_is_empty(&cls->exact_table)
             ? search_exact_table(cls, flow_hash(flow, 0), flow)
             : NULL);
 }
 
-struct cls_rule *
-classifier_lookup_wild(const struct classifier *cls, const flow_t *flow)
+static struct cls_rule *
+classifier_lookup_wild(const struct classifier *cls, const struct flow *flow)
 {
     struct cls_rule *best = NULL;
     if (cls->n_rules > hmap_count(&cls->exact_table)) {
         struct cls_rule target;
         int i;
 
-        cls_rule_from_flow(&target, flow, 0, 0);
+        cls_rule_from_flow(flow, 0, 0, &target);
         for (i = 0; i < CLS_N_FIELDS; i++) {
             struct cls_rule *rule = search_table(&cls->tables[i], i, &target);
             if (rule && (!best || rule->priority > best->priority)) {
@@ -301,33 +239,58 @@ classifier_lookup_wild(const struct classifier *cls, const flow_t *flow)
     return best;
 }
 
+/* Finds and returns the highest-priority rule in 'cls' that matches 'flow'.
+ * Returns a null pointer if no rules in 'cls' match 'flow'.  If multiple rules
+ * of equal priority match 'flow', returns one arbitrarily.
+ *
+ * (When multiple rules of equal priority happen to fall into the same bucket,
+ * rules added more recently take priority over rules added less recently, but
+ * this is subject to change and should not be depended upon.) */
+struct cls_rule *
+classifier_lookup(const struct classifier *cls, const struct flow *flow,
+                  int include)
+{
+    if (include & CLS_INC_EXACT) {
+        struct cls_rule *rule = classifier_lookup_exact(cls, flow);
+        if (rule) {
+            return rule;
+        }
+    }
+
+    if (include & CLS_INC_WILD) {
+        return classifier_lookup_wild(cls, flow);
+    }
+
+    return NULL;
+}
+
 struct cls_rule *
 classifier_find_rule_exactly(const struct classifier *cls,
-                             const flow_t *target, uint32_t wildcards,
-                             unsigned int priority)
+                             const struct cls_rule *target)
 {
     struct cls_bucket *bucket;
     int table_idx;
     uint32_t hash;
 
-    if (!wildcards) {
-        /* Ignores 'priority'. */
-        return search_exact_table(cls, flow_hash(target, 0), target);
+    if (!target->wc.wildcards) {
+        /* Ignores 'target->priority'. */
+        return search_exact_table(cls, flow_hash(&target->flow, 0),
+                                  &target->flow);
     }
 
-    assert(wildcards == (wildcards & OFPFW_ALL));
-    table_idx = table_idx_from_wildcards(wildcards);
-    hash = hash_fields(target, table_idx);
-    HMAP_FOR_EACH_WITH_HASH (bucket, struct cls_bucket, hmap_node, hash,
+    assert(target->wc.wildcards == (target->wc.wildcards & OVSFW_ALL));
+    table_idx = table_idx_from_wildcards(target->wc.wildcards);
+    hash = hash_fields(&target->flow, table_idx);
+    HMAP_FOR_EACH_WITH_HASH (bucket, hmap_node, hash,
                              &cls->tables[table_idx]) {
-        if (equal_fields(&bucket->fixed, target, table_idx)) {
+        if (equal_fields(&bucket->fixed, &target->flow, table_idx)) {
             struct cls_rule *pos;
-            LIST_FOR_EACH (pos, struct cls_rule, node.list, &bucket->rules) {
-                if (pos->priority < priority) {
+            LIST_FOR_EACH (pos, node.list, &bucket->rules) {
+                if (pos->priority < target->priority) {
                     return NULL;
-                } else if (pos->priority == priority &&
-                           pos->wc.wildcards == wildcards &&
-                           flow_equal(target, &pos->flow)) {
+                } else if (pos->priority == target->priority &&
+                           pos->wc.wildcards == target->wc.wildcards &&
+                           flow_equal(&target->flow, &pos->flow)) {
                     return pos;
                 }
             }
@@ -336,6 +299,38 @@ classifier_find_rule_exactly(const struct classifier *cls,
     return NULL;
 }
 
+/* Checks if 'target' would overlap any other rule in 'cls'.  Two rules are
+ * considered to overlap if both rules have the same priority and a packet
+ * could match both. */
+bool
+classifier_rule_overlaps(const struct classifier *cls,
+                         const struct cls_rule *target)
+{
+    const struct hmap *tbl;
+
+    if (!target->wc.wildcards) {
+        return (search_exact_table(cls, flow_hash(&target->flow, 0),
+                                   &target->flow) != NULL);
+    }
+
+    for (tbl = &cls->tables[0]; tbl < &cls->tables[CLS_N_FIELDS]; tbl++) {
+        struct cls_bucket *bucket;
+
+        HMAP_FOR_EACH (bucket, hmap_node, tbl) {
+            struct cls_rule *rule;
+
+            LIST_FOR_EACH (rule, node.list, &bucket->rules) {
+                if (rule->priority == target->priority
+                    && rules_match_2wild(rule, target, 0)) {
+                    return true;
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
 /* Ignores target->priority.
  *
  * 'callback' is allowed to delete the rule that is passed as its argument, but
@@ -355,8 +350,7 @@ classifier_for_each_match(const struct classifier *cls,
              table++) {
             struct cls_bucket *bucket, *next_bucket;
 
-            HMAP_FOR_EACH_SAFE (bucket, next_bucket,
-                                struct cls_bucket, hmap_node, table) {
+            HMAP_FOR_EACH_SAFE (bucket, next_bucket, hmap_node, table) {
                 /* XXX there is a bit of room for optimization here based on
                  * rejecting entire buckets on their fixed fields, but it will
                  * only be worthwhile for big buckets (which we hope we won't
@@ -368,8 +362,7 @@ classifier_for_each_match(const struct classifier *cls,
                  * bucket itself will be destroyed.  The bucket contains the
                  * list head so that's a use-after-free error. */
                 prev_rule = NULL;
-                LIST_FOR_EACH (rule, struct cls_rule, node.list,
-                               &bucket->rules) {
+                LIST_FOR_EACH (rule, node.list, &bucket->rules) {
                     if (rules_match_1wild(rule, target, 0)) {
                         if (prev_rule) {
                             callback(prev_rule, aux);
@@ -388,7 +381,7 @@ classifier_for_each_match(const struct classifier *cls,
         if (target->wc.wildcards) {
             struct cls_rule *rule, *next_rule;
 
-            HMAP_FOR_EACH_SAFE (rule, next_rule, struct cls_rule, node.hmap,
+            HMAP_FOR_EACH_SAFE (rule, next_rule, node.hmap,
                                 &cls->exact_table) {
                 if (rules_match_1wild(rule, target, 0)) {
                     callback(rule, aux);
@@ -411,7 +404,10 @@ classifier_for_each_match(const struct classifier *cls,
  * it must not delete (or move) any other rules in 'cls' that are in the same
  * table as the argument rule.  Two rules are in the same table if their
  * cls_rule structs have the same table_idx; as a special case, a rule with
- * wildcards and an exact-match rule will never be in the same table. */
+ * wildcards and an exact-match rule will never be in the same table.
+ *
+ * If 'include' is CLS_INC_EXACT then CLASSIFIER_FOR_EACH_EXACT_RULE(_SAFE) is
+ * probably easier to use. */
 void
 classifier_for_each(const struct classifier *cls, int include,
                     void (*callback)(struct cls_rule *, void *aux),
@@ -423,8 +419,7 @@ classifier_for_each(const struct classifier *cls, int include,
         for (tbl = &cls->tables[0]; tbl < &cls->tables[CLS_N_FIELDS]; tbl++) {
             struct cls_bucket *bucket, *next_bucket;
 
-            HMAP_FOR_EACH_SAFE (bucket, next_bucket,
-                                struct cls_bucket, hmap_node, tbl) {
+            HMAP_FOR_EACH_SAFE (bucket, next_bucket, hmap_node, tbl) {
                 struct cls_rule *prev_rule, *rule;
 
                 /* We can't just use LIST_FOR_EACH_SAFE here because, if the
@@ -432,8 +427,7 @@ classifier_for_each(const struct classifier *cls, int include,
                  * bucket itself will be destroyed.  The bucket contains the
                  * list head so that's a use-after-free error. */
                 prev_rule = NULL;
-                LIST_FOR_EACH (rule, struct cls_rule, node.list,
-                               &bucket->rules) {
+                LIST_FOR_EACH (rule, node.list, &bucket->rules) {
                     if (prev_rule) {
                         callback(prev_rule, aux);
                     }
@@ -449,15 +443,14 @@ classifier_for_each(const struct classifier *cls, int include,
     if (include & CLS_INC_EXACT) {
         struct cls_rule *rule, *next_rule;
 
-        HMAP_FOR_EACH_SAFE (rule, next_rule,
-                            struct cls_rule, node.hmap, &cls->exact_table) {
+        HMAP_FOR_EACH_SAFE (rule, next_rule, node.hmap, &cls->exact_table) {
             callback(rule, aux);
         }
     }
 }
 \f
 static struct cls_bucket *create_bucket(struct hmap *, size_t hash,
-                                        const flow_t *fixed);
+                                        const struct flow *fixed);
 static struct cls_rule *bucket_insert(struct cls_bucket *, struct cls_rule *);
 
 static inline bool equal_bytes(const void *, const void *, size_t n);
@@ -466,7 +459,7 @@ static inline bool equal_bytes(const void *, const void *, size_t n);
  * (CLS_F_IDX_*) are less than 'table_idx'.  (If 'table_idx' is
  * CLS_F_IDX_EXACT, hashes all the fields in 'flow'). */
 static uint32_t
-hash_fields(const flow_t *flow, int table_idx)
+hash_fields(const struct flow *flow, int table_idx)
 {
     /* I just know I'm going to hell for writing code this way.
      *
@@ -532,7 +525,7 @@ finish:
  *
  * Returns true if all the compared fields are equal, false otherwise. */
 static bool
-equal_fields(const flow_t *a, const flow_t *b, int table_idx)
+equal_fields(const struct flow *a, const struct flow *b, int table_idx)
 {
     /* XXX The generated code could be better here. */
 #define CLS_FIELD(WILDCARDS, MEMBER, NAME)                              \
@@ -587,15 +580,15 @@ static struct cls_rule *
 bucket_insert(struct cls_bucket *bucket, struct cls_rule *rule)
 {
     struct cls_rule *pos;
-    LIST_FOR_EACH (pos, struct cls_rule, node.list, &bucket->rules) {
-        if (pos->priority <= rule->priority) {
-            if (pos->priority == rule->priority
-                && pos->wc.wildcards == rule->wc.wildcards
+    LIST_FOR_EACH (pos, node.list, &bucket->rules) {
+        if (pos->priority == rule->priority) {
+            if (pos->wc.wildcards == rule->wc.wildcards
                 && rules_match_1wild(pos, rule, rule->table_idx))
             {
                 list_replace(&rule->node.list, &pos->node.list);
                 return pos;
             }
+        } else if (pos->priority < rule->priority) {
             break;
         }
     }
@@ -625,8 +618,7 @@ static struct cls_bucket *
 find_bucket(struct hmap *table, size_t hash, const struct cls_rule *rule)
 {
     struct cls_bucket *bucket;
-    HMAP_FOR_EACH_WITH_HASH (bucket, struct cls_bucket, hmap_node, hash,
-                             table) {
+    HMAP_FOR_EACH_WITH_HASH (bucket, hmap_node, hash, table) {
         if (equal_fields(&bucket->fixed, &rule->flow, rule->table_idx)) {
             return bucket;
         }
@@ -637,7 +629,7 @@ find_bucket(struct hmap *table, size_t hash, const struct cls_rule *rule)
 /* Creates a bucket and inserts it in 'table' with the given 'hash' and 'fixed'
  * values.  Returns the new bucket. */
 static struct cls_bucket *
-create_bucket(struct hmap *table, size_t hash, const flow_t *fixed)
+create_bucket(struct hmap *table, size_t hash, const struct flow *fixed)
 {
     struct cls_bucket *bucket = xmalloc(sizeof *bucket);
     list_init(&bucket->rules);
@@ -698,7 +690,7 @@ read_uint32(const void *p)
  * The compared field is the one with wildcard bit or bits 'field_wc', offset
  * 'rule_ofs' within cls_rule's "fields" member, and length 'len', in bytes. */
 static inline bool ALWAYS_INLINE
-field_matches(const flow_t *a_, const flow_t *b_,
+field_matches(const struct flow *a_, const struct flow *b_,
               uint32_t wildcards, uint32_t nw_src_mask, uint32_t nw_dst_mask,
               uint32_t field_wc, int ofs, int len)
 {
@@ -739,7 +731,7 @@ rules_match(const struct cls_rule *a, const struct cls_rule *b,
         case CLS_F_IDX_##NAME:                                      \
             if (!field_matches(&a->flow, &b->flow,                  \
                                wildcards, nw_src_mask, nw_dst_mask, \
-                               WILDCARDS, offsetof(flow_t, MEMBER), \
+                               WILDCARDS, offsetof(struct flow, MEMBER), \
                                sizeof a->flow.MEMBER)) {            \
                 return false;                                       \
             }                                                       \
@@ -764,6 +756,23 @@ rules_match_1wild(const struct cls_rule *fixed, const struct cls_rule *wild,
                        wild->wc.nw_dst_mask, field_idx);
 }
 
+/* Returns true if 'wild1' and 'wild2' match, that is, if their fields
+ * are equal modulo wildcards in 'wild1' or 'wild2'.
+ *
+ * 'field_idx' is the index of the first field to be compared; fields before
+ * 'field_idx' are assumed to match.  Always returns true if 'field_idx' is
+ * CLS_N_FIELDS. */
+static bool
+rules_match_2wild(const struct cls_rule *wild1, const struct cls_rule *wild2,
+                  int field_idx)
+{
+    return rules_match(wild1, wild2,
+                       wild1->wc.wildcards | wild2->wc.wildcards,
+                       wild1->wc.nw_src_mask & wild2->wc.nw_src_mask,
+                       wild1->wc.nw_dst_mask & wild2->wc.nw_dst_mask,
+                       field_idx);
+}
+
 /* Searches 'bucket' for a rule that matches 'target'.  Returns the
  * highest-priority match, if one is found, or a null pointer if there is no
  * match.
@@ -779,7 +788,7 @@ search_bucket(struct cls_bucket *bucket, int field_idx,
         return NULL;
     }
 
-    LIST_FOR_EACH (pos, struct cls_rule, node.list, &bucket->rules) {
+    LIST_FOR_EACH (pos, node.list, &bucket->rules) {
         if (rules_match_1wild(target, pos, field_idx)) {
             return pos;
         }
@@ -807,7 +816,7 @@ search_table(const struct hmap *table, int field_idx,
         return search_bucket(bucket, field_idx, target);
     }
 
-    HMAP_FOR_EACH_WITH_HASH (bucket, struct cls_bucket, hmap_node,
+    HMAP_FOR_EACH_WITH_HASH (bucket, hmap_node,
                              hash_fields(&target->flow, field_idx), table) {
         struct cls_rule *rule = search_bucket(bucket, field_idx, target);
         if (rule) {
@@ -819,12 +828,11 @@ search_table(const struct hmap *table, int field_idx,
 
 static struct cls_rule *
 search_exact_table(const struct classifier *cls, size_t hash,
-                   const flow_t *target)
+                   const struct flow *target)
 {
     struct cls_rule *rule;
 
-    HMAP_FOR_EACH_WITH_HASH (rule, struct cls_rule, node.hmap,
-                             hash, &cls->exact_table) {
+    HMAP_FOR_EACH_WITH_HASH (rule, node.hmap, hash, &cls->exact_table) {
         if (flow_equal(&rule->flow, target)) {
             return rule;
         }