[ovs-dev] [RFC PATCH 4/8] ofproto: Implement OpenFlow 1.3 meter table.

Jarno Rajahalme jarno.rajahalme at nsn.com
Thu Jun 20 14:26:19 UTC 2013


Signed-off-by: Jarno Rajahalme <jarno.rajahalme at nsn.com>
---
 lib/ofp-actions.c          |   35 +++-
 lib/ofp-actions.h          |   14 ++
 lib/rconn.c                |   14 +-
 ofproto/ofproto-dpif.c     |   33 ++++
 ofproto/ofproto-provider.h |   24 +++
 ofproto/ofproto.c          |  467 +++++++++++++++++++++++++++++++++++++++++---
 ofproto/ofproto.h          |    4 +
 7 files changed, 555 insertions(+), 36 deletions(-)

diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index b487f5e..056ecd6 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -1237,10 +1237,16 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, int max_ports)
 
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_WRITE_METADATA:
-    case OFPACT_METER:
     case OFPACT_GOTO_TABLE:
         return 0;
 
+    case OFPACT_METER: {
+        uint32_t mid = ofpact_get_METER(a)->meter_id;
+        if (mid == 0 || mid > OFPM13_MAX) {
+            return OFPERR_OFPBIC_EPERM; /* XXX: NEED OFPERR_OFPBIC_BAD_METER */
+        }
+        return 0;
+    }
     default:
         NOT_REACHED();
     }
@@ -1268,6 +1274,33 @@ out:
     return error;
 }
 
+/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are
+ * appropriate for a packet with the prerequisites satisfied by 'flow' using
+ * the context 'ctx'. */
+enum ofperr
+ofpacts_check_ctx(struct ofpacts_check_ctx *ctx,
+                  const struct ofpact ofpacts[], size_t ofpacts_len,
+                  struct flow *flow)
+{
+    const struct ofpact *a;
+    ovs_be16 dl_type = flow->dl_type;
+    enum ofperr error = 0;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        if (ctx->funcs[a->type]) {
+            error = ctx->funcs[a->type](ctx, a, flow);
+        } else {
+            error = ofpact_check__(a, flow, ctx->max_ports);
+        }
+        if (error) {
+            goto out;
+        }
+    }
+out:
+    flow->dl_type = dl_type; /* Restore. */
+    return error;
+}
+
 /* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are
  * in the appropriate order as defined by the OpenFlow spec. */
 enum ofperr
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index d445cf0..f039f88 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -504,6 +504,20 @@ enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
 /* May temporarily modify flow */
 enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len,
                           struct flow *, int max_ports);
+
+struct ofpacts_check_ctx;
+typedef enum ofperr (*ofpacts_checker_fp)(struct ofpacts_check_ctx *,
+                                          const struct ofpact *,
+                                          const struct flow *);
+struct ofpacts_check_ctx {
+    void *owner;
+    ofpacts_checker_fp const *funcs;
+    int max_ports;
+};
+enum ofperr ofpacts_check_ctx(struct ofpacts_check_ctx *,
+                              const struct ofpact[], size_t ofpacts_len,
+                              struct flow *);
+
 enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len);
 
 /* Converting ofpacts to OpenFlow. */
diff --git a/lib/rconn.c b/lib/rconn.c
index 4922a5c..ffd2738 100644
--- a/lib/rconn.c
+++ b/lib/rconn.c
@@ -1137,19 +1137,12 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_GET_ASYNC_REQUEST:
     case OFPTYPE_GET_ASYNC_REPLY:
-    case OFPTYPE_METER_MOD:
     case OFPTYPE_GROUP_REQUEST:
     case OFPTYPE_GROUP_REPLY:
     case OFPTYPE_GROUP_DESC_REQUEST:
     case OFPTYPE_GROUP_DESC_REPLY:
     case OFPTYPE_GROUP_FEATURES_REQUEST:
     case OFPTYPE_GROUP_FEATURES_REPLY:
-    case OFPTYPE_METER_REQUEST:
-    case OFPTYPE_METER_REPLY:
-    case OFPTYPE_METER_CONFIG_REQUEST:
-    case OFPTYPE_METER_CONFIG_REPLY:
-    case OFPTYPE_METER_FEATURES_REQUEST:
-    case OFPTYPE_METER_FEATURES_REPLY:
     case OFPTYPE_TABLE_FEATURES_REQUEST:
     case OFPTYPE_TABLE_FEATURES_REPLY:
         return false;
@@ -1160,6 +1153,7 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_PACKET_OUT:
     case OFPTYPE_FLOW_MOD:
     case OFPTYPE_PORT_MOD:
+    case OFPTYPE_METER_MOD:
     case OFPTYPE_BARRIER_REQUEST:
     case OFPTYPE_BARRIER_REPLY:
     case OFPTYPE_DESC_STATS_REQUEST:
@@ -1176,6 +1170,12 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_QUEUE_STATS_REPLY:
     case OFPTYPE_PORT_DESC_STATS_REQUEST:
     case OFPTYPE_PORT_DESC_STATS_REPLY:
+    case OFPTYPE_METER_REQUEST:
+    case OFPTYPE_METER_REPLY:
+    case OFPTYPE_METER_CONFIG_REQUEST:
+    case OFPTYPE_METER_CONFIG_REPLY:
+    case OFPTYPE_METER_FEATURES_REQUEST:
+    case OFPTYPE_METER_FEATURES_REPLY:
     case OFPTYPE_ROLE_REQUEST:
     case OFPTYPE_ROLE_REPLY:
     case OFPTYPE_SET_FLOW_FORMAT:
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 6f7fd0d..7afce20 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -6605,6 +6605,35 @@ vsp_add(struct ofport_dpif *port, uint16_t realdev_ofp_port, int vid)
     }
 }
 
+static void
+meter_get_features(const struct ofproto *ofproto_ OVS_UNUSED,
+                   struct ofputil_meter_features *features)
+{
+    memset(features, 0, sizeof *features);
+}
+
+static enum ofperr
+meter_set(struct ofproto *ofproto_ OVS_UNUSED,
+          ofproto_meter_id *meter_id OVS_UNUSED,
+          const struct ofputil_meter_config *config OVS_UNUSED)
+{
+    return OFPERR_OFPMMFC_OUT_OF_METERS;
+}
+
+static enum ofperr
+meter_get(const struct ofproto *ofproto_ OVS_UNUSED,
+          ofproto_meter_id meter_id OVS_UNUSED,
+          struct ofputil_meter_stats *stats OVS_UNUSED)
+{
+    return OFPERR_OFPMMFC_UNKNOWN_METER;
+}
+
+static void
+meter_del(struct ofproto *ofproto_ OVS_UNUSED,
+          ofproto_meter_id meter_id OVS_UNUSED)
+{
+}
+
 uint32_t
 ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, uint16_t ofp_port)
 {
@@ -6754,4 +6783,8 @@ const struct ofproto_class ofproto_dpif_class = {
     forward_bpdu_changed,
     set_mac_table_config,
     set_realdev,
+    meter_get_features,
+    meter_set,
+    meter_get,
+    meter_del,
 };
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 41b589f..217c6df 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -35,6 +35,7 @@ struct match;
 struct ofpact;
 struct ofputil_flow_mod;
 struct bfd_cfg;
+struct meter;
 
 /* An OpenFlow switch.
  *
@@ -76,6 +77,14 @@ struct ofproto {
      * These flows should all be present in tables. */
     struct list expirable;      /* Expirable 'struct rule"s in all tables. */
 
+    /* Meter table.
+     * OpenFlow meters start at 1.  To avoid confusion we leave the first
+     * pointer in the array un-used, and index directly with the OpenFlow
+     * meter_id. */
+    struct ofputil_meter_features meter_features;
+    struct meter **meters; /* meter_features.max_meter + 1 pointers */
+    uint32_t last_meter;   /* Last meter in use */
+
     /* OpenFlow connections. */
     struct connmgr *connmgr;
 
@@ -224,6 +233,9 @@ struct rule {
     struct ofpact *ofpacts;      /* Sequence of "struct ofpacts". */
     unsigned int ofpacts_len;    /* Size of 'ofpacts', in bytes. */
 
+    uint32_t meter_id;           /* Non-zero OF meter_id, if any */
+    struct list meter_list_node; /* In owning meter's 'rules' list. */
+
     /* Flow monitors. */
     enum nx_flow_monitor_flags monitor_flags;
     uint64_t add_seqno;         /* Sequence number when added. */
@@ -1321,6 +1333,18 @@ struct ofproto_class {
      * it. */
     int (*set_realdev)(struct ofport *ofport,
                        uint16_t realdev_ofp_port, int vid);
+
+/* ## ------------------------ ## */
+/* ## OpenFlow Meter Functions ## */
+/* ## ------------------------ ## */
+
+    void (*meter_get_features)(const struct ofproto *,
+                               struct ofputil_meter_features *);
+    enum ofperr (*meter_set)(struct ofproto *, ofproto_meter_id *,
+                             const struct ofputil_meter_config *);
+    enum ofperr (*meter_get)(const struct ofproto *, ofproto_meter_id,
+                             struct ofputil_meter_stats *);
+    void (*meter_del)(struct ofproto *, ofproto_meter_id);
 };
 
 extern const struct ofproto_class ofproto_dpif_class;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index eabe850..4b920ef 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -126,6 +126,7 @@ struct ofoperation {
     /* OFOPERATION_MODIFY: The old actions, if the actions are changing. */
     struct ofpact *ofpacts;
     size_t ofpacts_len;
+    uint32_t meter_id;
 
     /* OFOPERATION_DELETE. */
     enum ofp_flow_removed_reason reason; /* Reason flow was removed. */
@@ -195,14 +196,18 @@ static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
 static bool rule_is_modifiable(const struct rule *);
 
 /* OpenFlow. */
+struct ofproto_check_ctx;
+
 static enum ofperr add_flow(struct ofproto *, struct ofconn *,
                             const struct ofputil_flow_mod *,
-                            const struct ofp_header *);
+                            const struct ofp_header *,
+                            struct ofproto_check_ctx *);
 static void delete_flow__(struct rule *, struct ofopgroup *);
 static bool handle_openflow(struct ofconn *, const 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 *,
+                                     struct ofproto_check_ctx *);
 static void calc_duration(long long int start, long long int now,
                           uint32_t *sec, uint32_t *nsec);
 
@@ -233,6 +238,35 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 /* The default value of true waits for flow restore. */
 static bool flow_restore_wait = true;
 
+struct ofproto_check_ctx {
+    struct ofpacts_check_ctx up;
+    uint32_t meter_id;
+};
+
+#define CHECK_CTX_INITIALIZER(ofproto) \
+    { { ofproto, ofproto_checkers, ofproto->max_ports }, 0 }
+
+static enum ofperr
+ofproto_check_meter(struct ofproto_check_ctx *ctx,
+                    const struct ofpact *act,
+                    const struct flow *flow OVS_UNUSED)
+{
+    struct ofproto * ofproto = (struct ofproto *)ctx->up.owner;
+    uint32_t mid = ofpact_get_METER(act)->meter_id;
+
+    if (mid == 0 || mid > OFPM13_MAX ||
+        ofproto_get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
+        return OFPERR_OFPBIC_EPERM; /* XXX: NEED OFPERR_OFPBIC_BAD_METER */
+    }
+    /* Store the meter id for later use. */
+    ctx->meter_id = mid;
+    return 0;
+}
+
+static ofpacts_checker_fp const ofproto_checkers[N_OFPACTS] = {
+    [OFPACT_METER] = (ofpacts_checker_fp)ofproto_check_meter,
+};
+
 /* Must be called to initialize the ofproto library.
  *
  * The caller may pass in 'iface_hints', which contains an shash of
@@ -464,6 +498,13 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->datapath_id = pick_datapath_id(ofproto);
     init_ports(ofproto);
 
+    /* Initialize meters table */
+    ofproto->ofproto_class->meter_get_features(ofproto,
+                                               &ofproto->meter_features);
+    ofproto->meters = xzalloc((ofproto->meter_features.max_meters + 1)
+                              * sizeof(struct meter *));
+    ofproto->last_meter = 0; /* Last meter in use or zero */
+
     *ofprotop = ofproto;
     return 0;
 }
@@ -1569,7 +1610,7 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
 
 /* Adds a flow to OpenFlow flow table 0 in 'p' that matches 'cls_rule' and
  * performs the 'n_actions' actions in 'actions'.  The new flow will not
- * timeout.
+ * timeout and the actions may not include a meter action.
  *
  * If cls_rule->priority is in the range of priorities supported by OpenFlow
  * (0...65535, inclusive) then the flow will be visible to OpenFlow
@@ -1590,6 +1631,7 @@ ofproto_add_flow(struct ofproto *ofproto, const struct match *match,
     if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
                                 ofpacts, ofpacts_len)) {
         struct ofputil_flow_mod fm;
+        struct ofproto_check_ctx ctx = CHECK_CTX_INITIALIZER(ofproto);
 
         memset(&fm, 0, sizeof fm);
         fm.match = *match;
@@ -1597,7 +1639,7 @@ ofproto_add_flow(struct ofproto *ofproto, const struct match *match,
         fm.buffer_id = UINT32_MAX;
         fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
         fm.ofpacts_len = ofpacts_len;
-        add_flow(ofproto, NULL, &fm, NULL);
+        add_flow(ofproto, NULL, &fm, NULL, &ctx);
         free(fm.ofpacts);
     }
 }
@@ -1610,7 +1652,8 @@ ofproto_add_flow(struct ofproto *ofproto, const struct match *match,
 int
 ofproto_flow_mod(struct ofproto *ofproto, const struct ofputil_flow_mod *fm)
 {
-    return handle_flow_mod__(ofproto, NULL, fm, NULL);
+    struct ofproto_check_ctx ctx = CHECK_CTX_INITIALIZER(ofproto);
+    return handle_flow_mod__(ofproto, NULL, fm, NULL, &ctx);
 }
 
 /* Searches for a rule with matching criteria exactly equal to 'target' in
@@ -2392,7 +2435,10 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
 
     /* Verify actions against packet, then send packet if successful. */
     flow_extract(payload, 0, 0, NULL, po.in_port, &flow);
-    error = ofpacts_check(po.ofpacts, po.ofpacts_len, &flow, p->max_ports);
+    {
+        struct ofproto_check_ctx ctx = CHECK_CTX_INITIALIZER(p);
+        error = ofpacts_check_ctx(&ctx.up, po.ofpacts, po.ofpacts_len, &flow);
+    }
     if (!error) {
         error = p->ofproto_class->packet_out(p, payload, &flow,
                                              po.ofpacts, po.ofpacts_len);
@@ -3202,7 +3248,8 @@ is_flow_deletion_pending(const struct ofproto *ofproto,
  * if any. */
 static enum ofperr
 add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
-         const struct ofputil_flow_mod *fm, const struct ofp_header *request)
+         const struct ofputil_flow_mod *fm, const struct ofp_header *request,
+         struct ofproto_check_ctx *ctx)
 {
     struct oftable *table;
     struct ofopgroup *group;
@@ -3279,6 +3326,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
        and OFPFF13_NO_BYT_COUNTS */
     rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
     rule->ofpacts_len = fm->ofpacts_len;
+    rule->meter_id = ctx->meter_id;
+    list_init(&rule->meter_list_node);
     rule->evictable = true;
     rule->eviction_group = NULL;
     list_init(&rule->expirable);
@@ -3350,7 +3399,8 @@ exit:
 static enum ofperr
 modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
                const struct ofputil_flow_mod *fm,
-               const struct ofp_header *request, struct list *rules)
+               const struct ofp_header *request,
+               struct ofproto_check_ctx *ctx, struct list *rules)
 {
     struct ofopgroup *group;
     struct rule *rule;
@@ -3382,8 +3432,10 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
         if (actions_changed) {
             op->ofpacts = rule->ofpacts;
             op->ofpacts_len = rule->ofpacts_len;
+            op->meter_id = rule->meter_id;
             rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
             rule->ofpacts_len = fm->ofpacts_len;
+            rule->meter_id = ctx->meter_id;
             rule->ofproto->ofproto_class->rule_modify_actions(rule);
         } else {
             ofoperation_complete(op, 0);
@@ -3397,12 +3449,13 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
 static enum ofperr
 modify_flows_add(struct ofproto *ofproto, struct ofconn *ofconn,
                  const struct ofputil_flow_mod *fm,
-                 const struct ofp_header *request)
+                 const struct ofp_header *request,
+                 struct ofproto_check_ctx *ctx)
 {
     if (fm->cookie_mask != htonll(0) || fm->new_cookie == htonll(UINT64_MAX)) {
         return 0;
     }
-    return add_flow(ofproto, ofconn, fm, request);
+    return add_flow(ofproto, ofconn, fm, request, ctx);
 }
 
 /* Implements OFPFC_MODIFY.  Returns 0 on success or an OpenFlow error code on
@@ -3413,7 +3466,8 @@ modify_flows_add(struct ofproto *ofproto, struct ofconn *ofconn,
 static enum ofperr
 modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofputil_flow_mod *fm,
-                   const struct ofp_header *request)
+                   const struct ofp_header *request,
+                   struct ofproto_check_ctx *ctx)
 {
     struct list rules;
     int error;
@@ -3424,9 +3478,9 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
     if (error) {
         return error;
     } else if (list_is_empty(&rules)) {
-        return modify_flows_add(ofproto, ofconn, fm, request);
+        return modify_flows_add(ofproto, ofconn, fm, request, ctx);
     } else {
-        return modify_flows__(ofproto, ofconn, fm, request, &rules);
+        return modify_flows__(ofproto, ofconn, fm, request, ctx, &rules);
     }
 }
 
@@ -3438,7 +3492,8 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
 static enum ofperr
 modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofputil_flow_mod *fm,
-                   const struct ofp_header *request)
+                   const struct ofp_header *request,
+                   struct ofproto_check_ctx *ctx)
 {
     struct list rules;
     int error;
@@ -3450,10 +3505,11 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
     if (error) {
         return error;
     } else if (list_is_empty(&rules)) {
-        return modify_flows_add(ofproto, ofconn, fm, request);
+        return modify_flows_add(ofproto, ofconn, fm, request, ctx);
     } else {
         return list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
-                                                          fm, request, &rules)
+                                                          fm, request, ctx,
+                                                          &rules)
                                          : 0;
     }
 }
@@ -3600,6 +3656,7 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     struct ofpbuf ofpacts;
     enum ofperr error;
     long long int now;
+    struct ofproto_check_ctx ctx = CHECK_CTX_INITIALIZER(ofproto);
 
     error = reject_slave_controller(ofconn);
     if (error) {
@@ -3610,11 +3667,12 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
     error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
                                     &ofpacts);
     if (!error) {
-        error = ofpacts_check(fm.ofpacts, fm.ofpacts_len,
-                              &fm.match.flow, ofproto->max_ports);
+        error = ofpacts_check_ctx(&ctx.up, fm.ofpacts, fm.ofpacts_len,
+                                  &fm.match.flow);
     }
+
     if (!error) {
-        error = handle_flow_mod__(ofproto, ofconn, &fm, oh);
+        error = handle_flow_mod__(ofproto, ofconn, &fm, oh, &ctx);
     }
     if (error) {
         goto exit_free_ofpacts;
@@ -3655,7 +3713,8 @@ exit:
 static enum ofperr
 handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
                   const struct ofputil_flow_mod *fm,
-                  const struct ofp_header *oh)
+                  const struct ofp_header *oh,
+                  struct ofproto_check_ctx *ctx)
 {
     if (ofproto->n_pending >= 50) {
         ovs_assert(!list_is_empty(&ofproto->pending));
@@ -3664,13 +3723,13 @@ handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
 
     switch (fm->command) {
     case OFPFC_ADD:
-        return add_flow(ofproto, ofconn, fm, oh);
+        return add_flow(ofproto, ofconn, fm, oh, ctx);
 
     case OFPFC_MODIFY:
-        return modify_flows_loose(ofproto, ofconn, fm, oh);
+        return modify_flows_loose(ofproto, ofconn, fm, oh, ctx);
 
     case OFPFC_MODIFY_STRICT:
-        return modify_flow_strict(ofproto, ofconn, fm, oh);
+        return modify_flow_strict(ofproto, ofconn, fm, oh, ctx);
 
     case OFPFC_DELETE:
         return delete_flows_loose(ofproto, ofconn, fm, oh);
@@ -4091,6 +4150,340 @@ handle_flow_monitor_cancel(struct ofconn *ofconn, const struct ofp_header *oh)
     return 0;
 }
 
+/* Meters implementation */
+
+/* Meter table entry, indexed by the OpenFlow meter_id.
+ * These are always dynamically allocated to allocate enough space for
+ * the bands.
+ * 'created' is used to compute the duration for meter stats.
+ * 'list rules' is needed so that we can delete the dependent rules when the
+ * meter table entry is deleted.
+ * 'provider_meter_id' is for the provider's private use.
+ */
+struct meter {
+    long long int created;      /* Time created. */
+    struct list rules;          /* List of "struct rule_dpif"s. */
+    ofproto_meter_id provider_meter_id;
+    uint16_t flags;             /* Meter flags */
+    uint16_t n_bands;           /* Number of meter bands */
+    struct ofputil_meter_band bands[0];
+};
+
+/*
+ * This is used in instruction validation at flow set-up time,
+ * as flows may not use non-existing meters.
+ * This is also used by ofproto-providers to translate OpenFlow meter_ids
+ * in METER instructions to the corresponding provider meter IDs.
+ * Return value of UINT32_MAX signifies an invalid meter.
+ */
+uint32_t
+ofproto_get_provider_meter_id(const struct ofproto * ofproto,
+                              uint32_t of_meter_id)
+{
+    if (of_meter_id && of_meter_id <= ofproto->last_meter) {
+        const struct meter *meter = ofproto->meters[of_meter_id];
+        if (meter) {
+            return meter->provider_meter_id.uint32;
+        }
+    }
+    return UINT32_MAX;
+}
+
+static struct meter *
+meter_create(const struct ofputil_meter_config *config)
+{
+    struct meter *meter
+        = xmalloc(sizeof *meter + config->n_bands * sizeof *meter->bands);
+    int i;
+
+    if (meter) {
+        meter->provider_meter_id.uint32 = UINT32_MAX;
+        meter->flags = config->flags;
+        meter->n_bands = config->n_bands;
+        meter->created = time_msec();
+        list_init(&meter->rules); /* Must not have rules with invalid meters */
+        for (i = 0; i < config->n_bands; ++i) {
+            meter->bands[i] = config->bands[i];
+        }
+    }
+    return meter;
+}
+
+static void
+meter_update(struct ofproto *ofproto, struct meter *meter,
+             struct meter *new_meter,
+             const struct ofputil_meter_config *config)
+{
+    if (new_meter) {
+        new_meter->provider_meter_id = meter->provider_meter_id;
+        /* move rules list over */
+        if (list_is_empty(&meter->rules)) {
+            list_init(&new_meter->rules);
+        } else {
+            new_meter->rules = meter->rules;
+            list_moved(&new_meter->rules);
+        }
+        ofproto->meters[config->meter_id] = new_meter;
+        free(meter);
+    } else {
+        int i;
+        /* Update existing meter.
+         * FIXME: It seems from the spec that old field need to be reset. */
+        meter->flags = config->flags;
+        meter->n_bands = config->n_bands;
+        for (i = 0; i < config->n_bands; ++i) {
+            meter->bands[i] = config->bands[i];
+        }
+    }
+}
+
+static enum ofperr
+handle_meter_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofputil_meter_mod mm;
+    uint64_t bands_stub[256 / 8];
+    struct ofpbuf bands;
+    uint32_t meter_id;
+    enum ofperr error;
+
+    ofpbuf_use_stub(&bands, bands_stub, sizeof bands_stub);
+
+    error = ofputil_decode_meter_mod(oh, &mm, &bands);
+    if (error) {
+        goto out_free;
+    }
+
+    meter_id = mm.meter.meter_id;
+
+    if (mm.command != OFPMC13_DELETE) {
+        if (!meter_id || meter_id > ofproto->meter_features.max_meters) {
+            error = OFPERR_OFPMMFC_INVALID_METER;
+            goto out_free;
+        }
+        if (mm.meter.n_bands > ofproto->meter_features.max_bands) {
+            error = OFPERR_OFPMMFC_OUT_OF_BANDS;
+            goto out_free;
+        }
+    }
+
+    switch (mm.command) {
+    case OFPMC13_ADD: {
+        struct meter *meter;
+
+        if (ofproto->meters[meter_id]) {
+            error = OFPERR_OFPMMFC_METER_EXISTS;
+            break;
+        }
+
+        meter = meter_create(&mm.meter);
+        if (!meter) {
+            error = OFPERR_OFPMMFC_OUT_OF_METERS;
+            break;
+        }
+
+        error = ofproto->ofproto_class->meter_set(ofproto,
+                                                  &meter->provider_meter_id,
+                                                  &mm.meter);
+        if (!error) {
+            ofproto->meters[meter_id] = meter;
+            if (ofproto->last_meter < meter_id) {
+                ofproto->last_meter = meter_id;
+            }
+        } else {
+            free(meter);
+        }
+        break;
+    }
+    case OFPMC13_MODIFY: {
+        struct meter *meter = ofproto->meters[meter_id];
+        struct meter *new_meter = NULL;
+
+        if (!meter) {
+            error = OFPERR_OFPMMFC_UNKNOWN_METER;
+            break;
+        }
+        /* realloc meter if number of bands increases */
+        if (mm.meter.n_bands > meter->n_bands) {
+            new_meter = meter_create(&mm.meter);
+            if (!new_meter) {
+                error = OFPERR_OFPMMFC_OUT_OF_METERS;
+                break;
+            }
+        }
+
+        error = ofproto->ofproto_class->meter_set(ofproto,
+                                                  &meter->provider_meter_id,
+                                                  &mm.meter);
+        if (!error) {
+            meter_update(ofproto, meter, new_meter, &mm.meter);
+        } else if (new_meter) {
+            free(new_meter);
+        }
+        break;
+    }
+    case OFPMC13_DELETE: {
+        struct list rules;
+        uint32_t first, last;
+
+        if (meter_id == OFPM13_ALL) {
+            first = 1;
+            last = ofproto->last_meter;
+        } else {
+            if (meter_id > ofproto->last_meter) {
+                return 0;
+            }
+            first = last = meter_id;
+        }
+
+        /* First collect flows to be deleted, possibly postponing
+         * the whole operation */
+        list_init(&rules);
+        for (meter_id = first; meter_id <= last; ++meter_id) {
+            struct meter *meter = ofproto->meters[meter_id];
+            if (!meter) {
+                continue; /* Skip non-existing meters */
+            }
+
+            if (!list_is_empty(&meter->rules)) {
+                struct rule *rule, *next_rule;
+                /* Move rules depending on this meter to be deleted later */
+                LIST_FOR_EACH_SAFE (rule, next_rule, meter_list_node,
+                                    &meter->rules) {
+                    if (rule->pending) {
+                        return OFPROTO_POSTPONE;
+                    }
+                    list_push_back(&rules, &rule->ofproto_node);
+                }
+            }
+        }
+
+        /* Delete the rules */
+        if (!list_is_empty(&rules)) {
+            /* FIXME: Should have a different reason code for this case. */
+            delete_flows__(ofproto, ofconn, oh, &rules);
+        }
+
+        /* Delete the meters */
+        for (meter_id = first; meter_id <= last; ++meter_id) {
+            struct meter *meter = ofproto->meters[meter_id];
+            if (!meter) {
+                continue; /* Skip non-existing meters */
+            }
+
+            ofproto->meters[meter_id] = NULL;
+            ofproto->ofproto_class->meter_del(ofproto,
+                                              meter->provider_meter_id);
+            free(meter);
+        }
+
+        if (first == last) {
+            /* Deleted only one meter. */
+            if (ofproto->last_meter == last) {
+                while (--last && !ofproto->meters[last]) {}
+                ofproto->last_meter = last; /* Last meter or 0 */
+            }
+        } else {
+            /* Deleted all meters */
+            ofproto->last_meter = 0;
+        }
+
+        /* Delete does not parse bands, no need to free. */
+        return 0;
+    }
+    default:
+        error = OFPERR_OFPMMFC_BAD_COMMAND;
+    }
+
+out_free:
+    ofpbuf_uninit(&bands);
+    return error;
+}
+
+static enum ofperr
+handle_meter_features_request(struct ofconn *ofconn,
+                              const struct ofp_header *request)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofputil_meter_features features;
+    struct ofpbuf *b;
+
+    ofproto->ofproto_class->meter_get_features(ofproto, &features);
+
+    b = ofputil_encode_meter_features_reply(&features, request);
+
+    ofconn_send_reply(ofconn, b);
+    return 0;
+}
+
+static enum ofperr
+handle_meter_request(struct ofconn *ofconn, const struct ofp_header *request,
+                     enum ofptype type)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct list replies;
+    uint64_t bands_stub[256 / 8];
+    struct ofpbuf bands;
+    uint32_t meter_id, first, last;
+
+    ofputil_decode_meter_request(request, &meter_id);
+
+    if (meter_id == OFPM13_ALL) {
+        first = 0;
+        last = ofproto->last_meter;
+    } else {
+        if (meter_id > ofproto->meter_features.max_meters ||
+            !ofproto->meters[meter_id]) {
+            return OFPERR_OFPMMFC_UNKNOWN_METER;
+        }
+        first = last = meter_id;
+    }
+
+    ofpbuf_use_stub(&bands, bands_stub, sizeof bands_stub);
+    ofpmp_init(&replies, request);
+
+    for (meter_id = first; meter_id <= last; ++meter_id) {
+        struct meter *meter = ofproto->meters[meter_id];
+        if (!meter) {
+            continue; /* Skip non-existing meters */
+        }
+        if (type == OFPTYPE_METER_REQUEST) {
+            struct ofputil_meter_stats stats;
+
+            stats.meter_id = meter_id;
+
+            /* provider sets the packet and byte counts, we do the rest */
+            stats.flow_count = list_size(&meter->rules);
+            calc_duration(meter->created, time_msec(),
+                          &stats.duration_sec, &stats.duration_nsec);
+            stats.n_bands = meter->n_bands;
+            ofpbuf_clear(&bands);
+            stats.bands
+                = ofpbuf_put_uninit(&bands,
+                                    meter->n_bands * sizeof *stats.bands);
+
+            if (!ofproto->ofproto_class->meter_get(ofproto,
+                                                   meter->provider_meter_id,
+                                                   &stats)) {
+                ofputil_append_meter_stats(&replies, &stats);
+            }
+        } else { /* type == OFPTYPE_METER_CONFIG_REQUEST */
+            struct ofputil_meter_config config;
+
+            config.meter_id = meter_id;
+            config.flags = meter->flags;
+            config.n_bands = meter->n_bands;
+            config.bands = meter->bands;
+            ofputil_append_meter_config(&replies, &config);
+        }
+    }
+
+    ofconn_send_replies(ofconn, &replies);
+    ofpbuf_uninit(&bands);
+    return 0;
+}
+
+
 static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 {
@@ -4126,6 +4519,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MOD:
         return handle_flow_mod(ofconn, oh);
 
+    case OFPTYPE_METER_MOD:
+        return handle_meter_mod(ofconn, oh);
+
     case OFPTYPE_BARRIER_REQUEST:
         return handle_barrier_request(ofconn, oh);
 
@@ -4184,16 +4580,19 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
         return handle_flow_monitor_request(ofconn, oh);
 
+    case OFPTYPE_METER_REQUEST:
+    case OFPTYPE_METER_CONFIG_REQUEST:
+        return handle_meter_request(ofconn, oh, type);
+
+    case OFPTYPE_METER_FEATURES_REQUEST:
+        return handle_meter_features_request(ofconn, oh);
+
         /* FIXME: Change the following once they are implemented: */
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
     case OFPTYPE_GET_ASYNC_REQUEST:
-    case OFPTYPE_METER_MOD:
     case OFPTYPE_GROUP_REQUEST:
     case OFPTYPE_GROUP_DESC_REQUEST:
     case OFPTYPE_GROUP_FEATURES_REQUEST:
-    case OFPTYPE_METER_REQUEST:
-    case OFPTYPE_METER_CONFIG_REQUEST:
-    case OFPTYPE_METER_FEATURES_REQUEST:
     case OFPTYPE_TABLE_FEATURES_REQUEST:
         return OFPERR_OFPBRC_BAD_TYPE;
 
@@ -4946,11 +5345,17 @@ oftable_remove_rule(struct rule *rule)
     struct oftable *table = &ofproto->tables[rule->table_id];
 
     classifier_remove(&table->cls, &rule->cr);
+    if (rule->meter_id) {
+        list_remove(&rule->meter_list_node);
+    }
     cookies_remove(ofproto, rule);
     eviction_group_remove_rule(rule);
     if (!list_is_empty(&rule->expirable)) {
         list_remove(&rule->expirable);
     }
+    if (!list_is_empty(&rule->meter_list_node)) {
+        list_remove(&rule->meter_list_node);
+    }
 }
 
 /* Inserts 'rule' into its oftable.  Removes any existing rule from 'rule''s
@@ -4968,9 +5373,15 @@ oftable_replace_rule(struct rule *rule)
         list_insert(&ofproto->expirable, &rule->expirable);
     }
     cookies_insert(ofproto, rule);
-
+    if (rule->meter_id) {
+        struct meter *meter = ofproto->meters[rule->meter_id];
+        list_insert(&meter->rules, &rule->meter_list_node);
+    }
     victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
     if (victim) {
+        if (victim->meter_id) {
+            list_remove(&victim->meter_list_node);
+        }
         cookies_remove(ofproto, victim);
 
         if (!list_is_empty(&victim->expirable)) {
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 5f8244c..4ab9acf 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -421,6 +421,10 @@ bool ofproto_has_vlan_usage_changed(const struct ofproto *);
 int ofproto_port_set_realdev(struct ofproto *, uint16_t vlandev_ofp_port,
                              uint16_t realdev_ofp_port, int vid);
 
+/* Returns UINT32_MAX if the given of_meter_id is invalid */
+uint32_t ofproto_get_provider_meter_id(const struct ofproto *,
+                                       uint32_t of_meter_id);
+
 #ifdef  __cplusplus
 }
 #endif
-- 
1.7.10.4




More information about the dev mailing list