Signed-off-by: Jarno Rajahalme <jarno.rajaha...@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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev