ofproto: Implement "hidden" and "readonly" OpenFlow tables.
[sliver-openvswitch.git] / ofproto / ofproto.c
index 0504026..822f1fb 100644 (file)
@@ -124,19 +124,29 @@ static void ofoperation_create(struct ofopgroup *, struct rule *,
                                enum ofoperation_type);
 static void ofoperation_destroy(struct ofoperation *);
 
-static void ofport_destroy__(struct ofport *);
-static void ofport_destroy(struct ofport *);
-
-static uint64_t pick_datapath_id(const struct ofproto *);
-static uint64_t pick_fallback_dpid(void);
+/* oftable. */
+static void oftable_init(struct oftable *);
+static void oftable_destroy(struct oftable *);
 
-static void ofproto_destroy__(struct ofproto *);
+static void oftable_remove_rule(struct rule *);
+static struct rule *oftable_replace_rule(struct rule *);
+static void oftable_substitute_rule(struct rule *old, struct rule *new);
 
+/* rule. */
 static void ofproto_rule_destroy__(struct rule *);
 static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
+static bool rule_is_modifiable(const struct rule *);
+static bool rule_is_hidden(const struct rule *);
 
-static void ofopgroup_destroy(struct ofopgroup *);
+/* ofport. */
+static void ofport_destroy__(struct ofport *);
+static void ofport_destroy(struct ofport *);
+
+static void update_port(struct ofproto *, const char *devname);
+static int init_ports(struct ofproto *);
+static void reinit_ports(struct ofproto *);
 
+/* OpenFlow. */
 static enum ofperr add_flow(struct ofproto *, struct ofconn *,
                             const struct ofputil_flow_mod *,
                             const struct ofp_header *);
@@ -144,13 +154,15 @@ static enum ofperr add_flow(struct ofproto *, struct ofconn *,
 static bool handle_openflow(struct ofconn *, struct ofpbuf *);
 static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
                                      const struct ofputil_flow_mod *,
-                             const struct ofp_header *);
+                                     const struct ofp_header *);
 
-static void update_port(struct ofproto *, const char *devname);
-static int init_ports(struct ofproto *);
-static void reinit_ports(struct ofproto *);
+/* ofproto. */
+static uint64_t pick_datapath_id(const struct ofproto *);
+static uint64_t pick_fallback_dpid(void);
+static void ofproto_destroy__(struct ofproto *);
 static void set_internal_devs_mtu(struct ofproto *);
 
+/* unixctl. */
 static void ofproto_unixctl_init(void);
 
 /* All registered ofproto classes, in probe order. */
@@ -287,9 +299,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
                struct ofproto **ofprotop)
 {
     const struct ofproto_class *class;
-    struct classifier *table;
     struct ofproto *ofproto;
-    int n_tables;
     int error;
 
     *ofprotop = NULL;
@@ -342,7 +352,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->vlan_bitmap = NULL;
     ofproto->vlans_changed = false;
 
-    error = ofproto->ofproto_class->construct(ofproto, &n_tables);
+    error = ofproto->ofproto_class->construct(ofproto);
     if (error) {
         VLOG_ERR("failed to open datapath %s: %s",
                  datapath_name, strerror(error));
@@ -350,12 +360,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
         return error;
     }
 
-    assert(n_tables >= 1 && n_tables <= 255);
-    ofproto->n_tables = n_tables;
-    ofproto->tables = xmalloc(n_tables * sizeof *ofproto->tables);
-    OFPROTO_FOR_EACH_TABLE (table, ofproto) {
-        classifier_init(table);
-    }
+    assert(ofproto->n_tables);
 
     ofproto->datapath_id = pick_datapath_id(ofproto);
     VLOG_INFO("using datapath ID %016"PRIx64, ofproto->datapath_id);
@@ -365,6 +370,21 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     return 0;
 }
 
+void
+ofproto_init_tables(struct ofproto *ofproto, int n_tables)
+{
+    struct oftable *table;
+
+    assert(!ofproto->n_tables);
+    assert(n_tables >= 1 && n_tables <= 255);
+
+    ofproto->n_tables = n_tables;
+    ofproto->tables = xmalloc(n_tables * sizeof *ofproto->tables);
+    OFPROTO_FOR_EACH_TABLE (table, ofproto) {
+        oftable_init(table);
+    }
+}
+
 void
 ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
 {
@@ -799,8 +819,8 @@ ofproto_get_snoops(const struct ofproto *ofproto, struct sset *snoops)
 static void
 ofproto_flush__(struct ofproto *ofproto)
 {
-    struct classifier *table;
     struct ofopgroup *group;
+    struct oftable *table;
 
     if (ofproto->ofproto_class->flush) {
         ofproto->ofproto_class->flush(ofproto);
@@ -811,11 +831,15 @@ ofproto_flush__(struct ofproto *ofproto)
         struct rule *rule, *next_rule;
         struct cls_cursor cursor;
 
-        cls_cursor_init(&cursor, table, NULL);
+        if (table->flags & OFTABLE_HIDDEN) {
+            continue;
+        }
+
+        cls_cursor_init(&cursor, &table->cls, NULL);
         CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
             if (!rule->pending) {
                 ofoperation_create(group, rule, OFOPERATION_DELETE);
-                classifier_remove(table, &rule->cr);
+                oftable_remove_rule(rule);
                 ofproto->ofproto_class->rule_destruct(rule);
             }
         }
@@ -826,7 +850,7 @@ ofproto_flush__(struct ofproto *ofproto)
 static void
 ofproto_destroy__(struct ofproto *ofproto)
 {
-    struct classifier *table;
+    struct oftable *table;
 
     assert(list_is_empty(&ofproto->pending));
     assert(!ofproto->n_pending);
@@ -845,8 +869,7 @@ ofproto_destroy__(struct ofproto *ofproto)
     shash_destroy(&ofproto->port_by_name);
 
     OFPROTO_FOR_EACH_TABLE (table, ofproto) {
-        assert(classifier_is_empty(table));
-        classifier_destroy(table);
+        oftable_destroy(table);
     }
     free(ofproto->tables);
 
@@ -1177,7 +1200,7 @@ ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule,
     const struct rule *rule;
 
     rule = rule_from_cls_rule(classifier_find_rule_exactly(
-                                    &ofproto->tables[0], cls_rule));
+                                    &ofproto->tables[0].cls, cls_rule));
     if (!rule || !ofputil_actions_equal(rule->actions, rule->n_actions,
                                         actions, n_actions)) {
         struct ofputil_flow_mod fm;
@@ -1212,7 +1235,7 @@ ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target)
     struct rule *rule;
 
     rule = rule_from_cls_rule(classifier_find_rule_exactly(
-                                  &ofproto->tables[0], target));
+                                  &ofproto->tables[0].cls, target));
     if (!rule) {
         /* No such rule -> success. */
         return true;
@@ -1224,7 +1247,7 @@ ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target)
         /* Initiate deletion -> success. */
         struct ofopgroup *group = ofopgroup_create_unattached(ofproto);
         ofoperation_create(group, rule, OFOPERATION_DELETE);
-        classifier_remove(&ofproto->tables[rule->table_id], &rule->cr);
+        oftable_remove_rule(rule);
         rule->ofproto->ofproto_class->rule_destruct(rule);
         ofopgroup_submit(group);
         return true;
@@ -1648,7 +1671,7 @@ void
 ofproto_rule_destroy(struct rule *rule)
 {
     assert(!rule->pending);
-    classifier_remove(&rule->ofproto->tables[rule->table_id], &rule->cr);
+    oftable_remove_rule(rule);
     ofproto_rule_destroy__(rule);
 }
 
@@ -1701,6 +1724,18 @@ rule_is_hidden(const struct rule *rule)
 {
     return rule->cr.priority > UINT16_MAX;
 }
+
+static enum oftable_flags
+rule_get_flags(const struct rule *rule)
+{
+    return rule->ofproto->tables[rule->table_id].flags;
+}
+
+static bool
+rule_is_modifiable(const struct rule *rule)
+{
+    return !(rule_get_flags(rule) & OFTABLE_READONLY);
+}
 \f
 static enum ofperr
 handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
@@ -1956,7 +1991,7 @@ handle_table_stats_request(struct ofconn *ofconn,
         sprintf(ots[i].name, "table%zu", i);
         ots[i].wildcards = htonl(OFPFW_ALL);
         ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
-        ots[i].active_count = htonl(classifier_count(&p->tables[i]));
+        ots[i].active_count = htonl(classifier_count(&p->tables[i].cls));
     }
 
     p->ofproto_class->get_tables(p, ots);
@@ -2036,11 +2071,27 @@ check_table_id(const struct ofproto *ofproto, uint8_t table_id)
 
 }
 
-static struct classifier *
+static struct oftable *
+next_visible_table(struct ofproto *ofproto, uint8_t table_id)
+{
+    struct oftable *table;
+
+    for (table = &ofproto->tables[table_id];
+         table < &ofproto->tables[ofproto->n_tables];
+         table++) {
+        if (!(table->flags & OFTABLE_HIDDEN)) {
+            return table;
+        }
+    }
+
+    return NULL;
+}
+
+static struct oftable *
 first_matching_table(struct ofproto *ofproto, uint8_t table_id)
 {
     if (table_id == 0xff) {
-        return &ofproto->tables[0];
+        return next_visible_table(ofproto, 0);
     } else if (table_id < ofproto->n_tables) {
         return &ofproto->tables[table_id];
     } else {
@@ -2048,23 +2099,23 @@ first_matching_table(struct ofproto *ofproto, uint8_t table_id)
     }
 }
 
-static struct classifier *
+static struct oftable *
 next_matching_table(struct ofproto *ofproto,
-                    struct classifier *cls, uint8_t table_id)
+                    struct oftable *table, uint8_t table_id)
 {
-    return (table_id == 0xff && cls != &ofproto->tables[ofproto->n_tables - 1]
-            ? cls + 1
+    return (table_id == 0xff
+            ? next_visible_table(ofproto, (table - ofproto->tables) + 1)
             : NULL);
 }
 
-/* Assigns CLS to each classifier table, in turn, that matches TABLE_ID in
- * OFPROTO:
+/* Assigns TABLE to each oftable, in turn, that matches TABLE_ID in OFPROTO:
  *
  *   - If TABLE_ID is 0xff, this iterates over every classifier table in
- *     OFPROTO.
+ *     OFPROTO, skipping tables marked OFTABLE_HIDDEN.
  *
  *   - If TABLE_ID is the number of a table in OFPROTO, then the loop iterates
- *     only once, for that table.
+ *     only once, for that table.  (This can be used to access tables marked
+ *     OFTABLE_HIDDEN.)
  *
  *   - Otherwise, TABLE_ID isn't valid for OFPROTO, so the loop won't be
  *     entered at all.  (Perhaps you should have validated TABLE_ID with
@@ -2072,10 +2123,10 @@ next_matching_table(struct ofproto *ofproto,
  *
  * All parameters are evaluated multiple times.
  */
-#define FOR_EACH_MATCHING_TABLE(CLS, TABLE_ID, OFPROTO)         \
-    for ((CLS) = first_matching_table(OFPROTO, TABLE_ID);       \
-         (CLS) != NULL;                                         \
-         (CLS) = next_matching_table(OFPROTO, CLS, TABLE_ID))
+#define FOR_EACH_MATCHING_TABLE(TABLE, TABLE_ID, OFPROTO)         \
+    for ((TABLE) = first_matching_table(OFPROTO, TABLE_ID);       \
+         (TABLE) != NULL;                                         \
+         (TABLE) = next_matching_table(OFPROTO, TABLE, TABLE_ID))
 
 /* Searches 'ofproto' for rules in table 'table_id' (or in all tables, if
  * 'table_id' is 0xff) that match 'match' in the "loose" way required for
@@ -2094,7 +2145,7 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
                     ovs_be64 cookie, ovs_be64 cookie_mask,
                     uint16_t out_port, struct list *rules)
 {
-    struct classifier *cls;
+    struct oftable *table;
     enum ofperr error;
 
     error = check_table_id(ofproto, table_id);
@@ -2103,11 +2154,11 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
     }
 
     list_init(rules);
-    FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) {
+    FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
         struct cls_cursor cursor;
         struct rule *rule;
 
-        cls_cursor_init(&cursor, cls, match);
+        cls_cursor_init(&cursor, &table->cls, match);
         CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
             if (rule->pending) {
                 return OFPROTO_POSTPONE;
@@ -2138,7 +2189,7 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
                      ovs_be64 cookie, ovs_be64 cookie_mask,
                      uint16_t out_port, struct list *rules)
 {
-    struct classifier *cls;
+    struct oftable *table;
     int error;
 
     error = check_table_id(ofproto, table_id);
@@ -2147,10 +2198,11 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
     }
 
     list_init(rules);
-    FOR_EACH_MATCHING_TABLE (cls, table_id, ofproto) {
+    FOR_EACH_MATCHING_TABLE (table, table_id, ofproto) {
         struct rule *rule;
 
-        rule = rule_from_cls_rule(classifier_find_rule_exactly(cls, match));
+        rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls,
+                                                               match));
         if (rule) {
             if (rule->pending) {
                 return OFPROTO_POSTPONE;
@@ -2240,13 +2292,13 @@ flow_stats_ds(struct rule *rule, struct ds *results)
 void
 ofproto_get_all_flows(struct ofproto *p, struct ds *results)
 {
-    struct classifier *cls;
+    struct oftable *table;
 
-    OFPROTO_FOR_EACH_TABLE (cls, p) {
+    OFPROTO_FOR_EACH_TABLE (table, p) {
         struct cls_cursor cursor;
         struct rule *rule;
 
-        cls_cursor_init(&cursor, cls, NULL);
+        cls_cursor_init(&cursor, &table->cls, NULL);
         CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
             flow_stats_ds(rule, results);
         }
@@ -2469,7 +2521,7 @@ static enum ofperr
 add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
          const struct ofputil_flow_mod *fm, const struct ofp_header *request)
 {
-    struct classifier *table;
+    struct oftable *table;
     struct ofopgroup *group;
     struct rule *victim;
     struct rule *rule;
@@ -2500,9 +2552,13 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
         return OFPERR_NXFMFC_BAD_TABLE_ID;
     }
 
+    if (table->flags & OFTABLE_READONLY) {
+        return OFPERR_OFPBRC_EPERM;
+    }
+
     /* Check for overlap, if requested. */
     if (fm->flags & OFPFF_CHECK_OVERLAP
-        && classifier_rule_overlaps(table, &fm->cr)) {
+        && classifier_rule_overlaps(&table->cls, &fm->cr)) {
         return OFPERR_OFPFMFC_OVERLAP;
     }
 
@@ -2531,8 +2587,10 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     rule->n_actions = fm->n_actions;
 
     /* Insert new rule. */
-    victim = rule_from_cls_rule(classifier_replace(table, &rule->cr));
-    if (victim && victim->pending) {
+    victim = oftable_replace_rule(rule);
+    if (victim && !rule_is_modifiable(victim)) {
+        error = OFPERR_OFPBRC_EPERM;
+    } else if (victim && victim->pending) {
         error = OFPROTO_POSTPONE;
     } else {
         group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
@@ -2548,11 +2606,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
 
     /* Back out if an error occurred. */
     if (error) {
-        if (victim) {
-            classifier_replace(table, &victim->cr);
-        } else {
-            classifier_remove(table, &rule->cr);
-        }
+        oftable_substitute_rule(rule, victim);
         ofproto_rule_destroy__(rule);
     }
     return error;
@@ -2574,9 +2628,18 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
 {
     struct ofopgroup *group;
     struct rule *rule;
+    enum ofperr error;
 
     group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
+    error = OFPERR_OFPBRC_EPERM;
     LIST_FOR_EACH (rule, ofproto_node, rules) {
+        if (rule_is_modifiable(rule)) {
+            /* At least one rule is modifiable, don't report EPERM error. */
+            error = 0;
+        } else {
+            continue;
+        }
+
         if (!ofputil_actions_equal(fm->actions, fm->n_actions,
                                    rule->actions, rule->n_actions)) {
             ofoperation_create(group, rule, OFOPERATION_MODIFY);
@@ -2592,7 +2655,7 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
     }
     ofopgroup_submit(group);
 
-    return 0;
+    return error;
 }
 
 /* Implements OFPFC_MODIFY.  Returns 0 on success or an OpenFlow error code on
@@ -2656,7 +2719,7 @@ delete_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
         ofproto_rule_send_removed(rule, OFPRR_DELETE);
 
         ofoperation_create(group, rule, OFOPERATION_DELETE);
-        classifier_remove(&ofproto->tables[rule->table_id], &rule->cr);
+        oftable_remove_rule(rule);
         rule->ofproto->ofproto_class->rule_destruct(rule);
     }
     ofopgroup_submit(group);
@@ -2738,7 +2801,7 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason)
 
     group = ofopgroup_create_unattached(ofproto);
     ofoperation_create(group, rule, OFOPERATION_DELETE);
-    classifier_remove(&ofproto->tables[rule->table_id], &rule->cr);
+    oftable_remove_rule(rule);
     rule->ofproto->ofproto_class->rule_destruct(rule);
     ofopgroup_submit(group);
 }
@@ -2880,7 +2943,7 @@ handle_nxt_set_packet_in_format(struct ofconn *ofconn,
 
     msg = (const struct nx_set_packet_in_format *) oh;
     format = ntohl(msg->format);
-    if (format != NXFF_OPENFLOW10 && format != NXPIF_NXM) {
+    if (format != NXPIF_OPENFLOW10 && format != NXPIF_NXM) {
         return OFPERR_OFPBRC_EPERM;
     }
 
@@ -3194,7 +3257,6 @@ ofoperation_complete(struct ofoperation *op, enum ofperr error)
     struct ofopgroup *group = op->group;
     struct rule *rule = op->rule;
     struct ofproto *ofproto = rule->ofproto;
-    struct classifier *table = &ofproto->tables[rule->table_id];
 
     assert(rule->pending == op);
     assert(op->status < 0);
@@ -3239,12 +3301,7 @@ ofoperation_complete(struct ofoperation *op, enum ofperr error)
                 }
             }
         } else {
-            if (op->victim) {
-                classifier_replace(table, &op->victim->cr);
-                op->victim = NULL;
-            } else {
-                classifier_remove(table, &rule->cr);
-            }
+            oftable_substitute_rule(rule, op->victim);
             ofproto_rule_destroy__(rule);
         }
         op->victim = NULL;
@@ -3308,6 +3365,59 @@ pick_fallback_dpid(void)
     return eth_addr_to_uint64(ea);
 }
 \f
+/* oftables. */
+
+/* Initializes 'table'. */
+static void
+oftable_init(struct oftable *table)
+{
+    memset(table, 0, sizeof *table);
+    classifier_init(&table->cls);
+}
+
+/* Destroys 'table'.
+ *
+ * The caller is responsible for freeing 'table' itself. */
+static void
+oftable_destroy(struct oftable *table)
+{
+    assert(classifier_is_empty(&table->cls));
+    classifier_destroy(&table->cls);
+}
+
+/* Removes 'rule' from the oftable that contains it. */
+static void
+oftable_remove_rule(struct rule *rule)
+{
+    struct ofproto *ofproto = rule->ofproto;
+    struct oftable *table = &ofproto->tables[rule->table_id];
+
+    classifier_remove(&table->cls, &rule->cr);
+}
+
+/* Inserts 'rule' into its oftable.  Removes any existing rule from 'rule''s
+ * oftable that has an identical cls_rule.  Returns the rule that was removed,
+ * if any, and otherwise NULL. */
+static struct rule *
+oftable_replace_rule(struct rule *rule)
+{
+    struct ofproto *ofproto = rule->ofproto;
+    struct oftable *table = &ofproto->tables[rule->table_id];
+
+    return rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
+}
+
+/* Removes 'old' from its oftable then, if 'new' is nonnull, inserts 'new'. */
+static void
+oftable_substitute_rule(struct rule *old, struct rule *new)
+{
+    if (new) {
+        oftable_replace_rule(new);
+    } else {
+        oftable_remove_rule(old);
+    }
+}
+\f
 /* unixctl commands. */
 
 struct ofproto *
@@ -3364,16 +3474,16 @@ ofproto_unixctl_init(void)
 void
 ofproto_get_vlan_usage(struct ofproto *ofproto, unsigned long int *vlan_bitmap)
 {
-    const struct classifier *cls;
+    const struct oftable *oftable;
 
     free(ofproto->vlan_bitmap);
     ofproto->vlan_bitmap = bitmap_allocate(4096);
     ofproto->vlans_changed = false;
 
-    OFPROTO_FOR_EACH_TABLE (cls, ofproto) {
+    OFPROTO_FOR_EACH_TABLE (oftable, ofproto) {
         const struct cls_table *table;
 
-        HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
+        HMAP_FOR_EACH (table, hmap_node, &oftable->cls.tables) {
             if ((table->wc.vlan_tci_mask & htons(VLAN_VID_MASK))
                 == htons(VLAN_VID_MASK)) {
                 const struct cls_rule *rule;