[ovs-dev] [RFC PATCH 4/8] ofproto: Implement OpenFlow 1.3 meter table.
Ben Pfaff
blp at nicira.com
Tue Jun 25 19:38:32 UTC 2013
On Thu, Jun 20, 2013 at 05:26:19PM +0300, Jarno Rajahalme wrote:
>
> Signed-off-by: Jarno Rajahalme <jarno.rajahalme at nsn.com>
I rebased this patch against master and resolved a bunch of conflicts
to come up with the appended patch.
I have some comments on this patch.
We can add a "bad meter" extension error very easily by editing
ofp-errors.h. Feel free to use the Nicira extension namespace for
this, or you could easily add a Nokia-Siemens extension namespace by
adding a *_VENDOR_ID to openflow-common.h.
I'm not fond of the structure of the new ofpacts_check_ctx(). At a
minimum, I think that ofpacts_check() should be a trivial wrapper
around it (just change ofpacts_check_ctx() to ignore 'ctx' if it's
NULL). But the inversion of control from a callback function is
somewhat awkward to begin with, and it's worse with a whole array of
them. Could we instead make the caller do the iteration? It could
handle the special cases as it likes and call the ofpact-by-ofpact
verification function for the other cases.
The new ofproto-provider functions lack comments. I think it might be
nice if they could just be NULL for providers that don't support them.
I otherwise skimmed the code and it looked OK.
Thanks,
Ben.
--8<--------------------------cut here-------------------------->8--
From: Jarno Rajahalme <jarno.rajahalme at nsn.com>
Date: Thu, 20 Jun 2013 17:26:19 +0300
Subject: [PATCH] ofproto: Implement OpenFlow 1.3 meter table.
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 99494a2..b6baa64 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -1239,10 +1239,16 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t 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();
}
@@ -1271,6 +1277,33 @@ ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len,
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 799f64c..7c538e5 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -503,6 +503,20 @@ enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
struct ofpbuf *ofpacts);
enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len,
struct flow *, ofp_port_t 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;
+ const ofpacts_checker_fp *funcs;
+ ofp_port_t 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 2fff66e..6880f95 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -6661,6 +6661,35 @@ vsp_add(struct ofport_dpif *port, ofp_port_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)
+{
+}
+
odp_port_t
ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
{
@@ -6809,4 +6838,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 565cb01..3fe7600 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,
ofp_port_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 5e6a252..2604e9e 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;
}
@@ -1570,7 +1611,7 @@ ofproto_port_del(struct ofproto *ofproto, ofp_port_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
@@ -1591,6 +1632,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;
@@ -1598,7 +1640,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);
}
}
@@ -1611,7 +1653,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
@@ -2406,7 +2449,10 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
/* Verify actions against packet, then send packet if successful. */
in_port_.ofp_port = po.in_port;
flow_extract(payload, 0, 0, NULL, &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);
@@ -3216,7 +3262,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;
@@ -3293,6 +3340,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);
@@ -3364,7 +3413,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;
@@ -3396,8 +3446,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);
@@ -3411,12 +3463,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
@@ -3427,7 +3480,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;
@@ -3438,9 +3492,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);
}
}
@@ -3452,7 +3506,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;
@@ -3464,10 +3519,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;
}
}
@@ -3614,6 +3670,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) {
@@ -3624,11 +3681,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;
@@ -3669,7 +3727,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));
@@ -3678,13 +3737,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);
@@ -4105,6 +4164,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)
{
@@ -4140,6 +4533,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);
@@ -4198,16 +4594,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;
@@ -4960,11 +5359,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
@@ -4982,9 +5387,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 2609c94..08abd23 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -422,6 +422,10 @@ bool ofproto_has_vlan_usage_changed(const struct ofproto *);
int ofproto_port_set_realdev(struct ofproto *, ofp_port_t vlandev_ofp_port,
ofp_port_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.2.5
More information about the dev
mailing list