Signed-off-by: Jarno Rajahalme <jarno.rajaha...@nsn.com> --- v2: Addresses comments by Ben. --- lib/ofp-actions.c | 20 +- lib/ofp-actions.h | 2 + lib/rconn.c | 14 +- ofproto/ofproto-dpif.c | 4 + ofproto/ofproto-provider.h | 49 ++++- ofproto/ofproto.c | 490 +++++++++++++++++++++++++++++++++++++++++--- ofproto/ofproto.h | 4 + 7 files changed, 539 insertions(+), 44 deletions(-)
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 34a700b..af7143a 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -1150,9 +1150,9 @@ exit: return error; } -/* May modify flow->dl_type, caller must restore it. */ -static enum ofperr -ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports) +enum ofperr +ofpacts_check_action(const struct ofpact *a, const struct flow *flow, + ovs_be16 *new_dl_type, ofp_port_t max_ports) { const struct ofpact_enqueue *enqueue; @@ -1225,11 +1225,11 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports) return 0; case OFPACT_PUSH_MPLS: - flow->dl_type = ofpact_get_PUSH_MPLS(a)->ethertype; + *new_dl_type = ofpact_get_PUSH_MPLS(a)->ethertype; return 0; case OFPACT_POP_MPLS: - flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype; + *new_dl_type = ofpact_get_POP_MPLS(a)->ethertype; return 0; case OFPACT_SAMPLE: @@ -1237,10 +1237,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_OFPMMFC_INVALID_METER; + } + return 0; + } default: NOT_REACHED(); } @@ -1260,7 +1266,7 @@ ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len, enum ofperr error = 0; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { - error = ofpact_check__(a, flow, max_ports); + error = ofpacts_check_action(a, flow, &flow->dl_type, max_ports); if (error) { break; } diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index 799f64c..ca80cdf 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -501,6 +501,8 @@ enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow, unsigned int instructions_len, uint8_t table_id, struct ofpbuf *ofpacts); +enum ofperr ofpacts_check_action(const struct ofpact *, const struct flow *, + ovs_be16 *new_dl_type, ofp_port_t max_ports); enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len, struct flow *, ofp_port_t max_ports); enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len); 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 c6a7abc..68f7fad 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -6810,4 +6810,8 @@ const struct ofproto_class ofproto_dpif_class = { forward_bpdu_changed, set_mac_table_config, set_realdev, + NULL, /* meter_get_features */ + NULL, /* meter_set */ + NULL, /* meter_get */ + NULL, /* meter_del */ }; diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 565cb01..96471cd 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 or 0. */ + /* 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, or zero. */ + 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. */ @@ -1317,10 +1329,43 @@ struct ofproto_class { * If 'realdev_ofp_port' is zero, then this function deconfigures 'ofport' * as a VLAN splinter port. * - * This function should be NULL if a an implementation does not support - * it. */ + * This function should be NULL if an implementation does not support it. + */ int (*set_realdev)(struct ofport *ofport, ofp_port_t realdev_ofp_port, int vid); + +/* ## ------------------------ ## */ +/* ## OpenFlow Meter Functions ## */ +/* ## ------------------------ ## */ + + /* These functions should be NULL if an implementation does not support + * them. If meter_get_features is non-NULL, then the rest must also + * be non-NULL. */ + void (*meter_get_features)(const struct ofproto *, + struct ofputil_meter_features *); + /* Modifies an existing meter, if the 'ofproto_meter_id *' points to + * a value other than UINT32_MAX, otherwise adds a new meter. On success + * a provider meter id is stored at 'ofproto_meter_id *'. All further + * references to the meter will be made with the returned provider meter id + * rather than the OpenFlow meter id. The caller does not try to interpret + * the provider meter id (other than UINT32_MAX signifying an invalid + * provider meter id), giving the implementation the freedom to either use + * the OpenFlow meter_id value provided in the meter configuration, or any + * other value suitable for the implementation. + * If a meter modification fails, the existing meter configuration is left + * intact. */ + enum ofperr (*meter_set)(struct ofproto *, ofproto_meter_id *, + const struct ofputil_meter_config *); + /* Gets the meter and meter band packet and byte counts for maximum of + * 'stats->n_bands' bands. The caller fills in the other stats values. + * The band stats are copied to memory at 'stats->bands' provided by the + * caller. The number of returned band stats is returned in + * 'stats->n_bands'. */ + enum ofperr (*meter_get)(const struct ofproto *, ofproto_meter_id, + struct ofputil_meter_stats *stats); + /* Deletes a meter, making the 'ofproto_meter_id' invalid for any + * further calls. */ + 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 66aa3c9..b6f1944 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. */ @@ -197,13 +198,15 @@ static bool rule_is_modifiable(const struct rule *); /* OpenFlow. */ static enum ofperr add_flow(struct ofproto *, struct ofconn *, const struct ofputil_flow_mod *, - const struct ofp_header *); + const struct ofp_header *, + const uint32_t *meter_id); static void delete_flow__(struct rule *, struct ofopgroup *, enum ofp_flow_removed_reason); 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 *, + const uint32_t *meter_id); static void calc_duration(long long int start, long long int now, uint32_t *sec, uint32_t *nsec); @@ -465,6 +468,17 @@ ofproto_create(const char *datapath_name, const char *datapath_type, ofproto->datapath_id = pick_datapath_id(ofproto); init_ports(ofproto); + /* Initialize meters table. */ + if (ofproto->ofproto_class->meter_get_features) { + ofproto->ofproto_class->meter_get_features(ofproto, + &ofproto->meter_features); + } else { + memset(&ofproto->meter_features, 0, sizeof 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; } @@ -1571,7 +1585,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 its 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 @@ -1592,6 +1606,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; + uint32_t meter_id = 0; memset(&fm, 0, sizeof fm); fm.match = *match; @@ -1599,7 +1614,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, &meter_id); free(fm.ofpacts); } } @@ -1608,11 +1623,14 @@ ofproto_add_flow(struct ofproto *ofproto, const struct match *match, * OFPERR_* OpenFlow error code on failure, or OFPROTO_POSTPONE if the * operation cannot be initiated now but may be retried later. * + * 'fm->ofpacts' may not contain a meter action. + * * This is a helper function for in-band control and fail-open. */ int ofproto_flow_mod(struct ofproto *ofproto, const struct ofputil_flow_mod *fm) { - return handle_flow_mod__(ofproto, NULL, fm, NULL); + uint32_t meter_id = 0; + return handle_flow_mod__(ofproto, NULL, fm, NULL, &meter_id); } /* Searches for a rule with matching criteria exactly equal to 'target' in @@ -2361,6 +2379,46 @@ reject_slave_controller(struct ofconn *ofconn) } } +/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are + * appropriate for a packet with the prerequisites satisfied by 'flow'. + * 'flow' may be temporarily modified, but is restored at return. + * If the actions include a meter action, and a non-NULL 'meter_id' is + * provided, the meter_id is stored at '*meter_id', if valid. + */ +static enum ofperr +ofproto_check_ofpacts(struct ofproto *ofproto, + const struct ofpact ofpacts[], size_t ofpacts_len, + struct flow *flow, uint32_t *meter_id) +{ + const struct ofpact *a; + ovs_be16 dl_type = flow->dl_type; + enum ofperr error = 0; + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (a->type == OFPACT_METER) { + uint32_t mid = 0; + + mid = ofpact_get_METER(a)->meter_id; + + if (ofproto_get_provider_meter_id(ofproto, mid) != UINT32_MAX) { + if (meter_id) { + *meter_id = mid; + } + } else { + error = OFPERR_OFPMMFC_INVALID_METER; + } + } else { + error = ofpacts_check_action(a, flow, &flow->dl_type, + ofproto->max_ports); + } + if (error) { + break; + } + } + flow->dl_type = dl_type; /* Restore. */ + return error; +} + static enum ofperr handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh) { @@ -2407,7 +2465,7 @@ 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); + error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len, &flow, NULL); if (!error) { error = p->ofproto_class->packet_out(p, payload, &flow, po.ofpacts, po.ofpacts_len); @@ -3217,7 +3275,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, + const uint32_t *meter_id) { struct oftable *table; struct ofopgroup *group; @@ -3294,6 +3353,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 = *meter_id; + list_init(&rule->meter_list_node); rule->evictable = true; rule->eviction_group = NULL; list_init(&rule->expirable); @@ -3365,7 +3426,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, + const uint32_t *meter_id, struct list *rules) { struct ofopgroup *group; struct rule *rule; @@ -3397,8 +3459,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 = *meter_id; rule->ofproto->ofproto_class->rule_modify_actions(rule); } else { ofoperation_complete(op, 0); @@ -3412,12 +3476,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, + const uint32_t *meter_id) { 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, meter_id); } /* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code on @@ -3428,7 +3493,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, + const uint32_t *meter_id) { struct list rules; int error; @@ -3439,9 +3505,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, meter_id); } else { - return modify_flows__(ofproto, ofconn, fm, request, &rules); + return modify_flows__(ofproto, ofconn, fm, request, meter_id, &rules); } } @@ -3453,7 +3519,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, + const uint32_t *meter_id) { struct list rules; int error; @@ -3465,10 +3532,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, meter_id); } else { return list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn, - fm, request, &rules) + fm, request, + meter_id, &rules) : 0; } } @@ -3618,6 +3686,7 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) struct ofpbuf ofpacts; enum ofperr error; long long int now; + uint32_t meter_id = 0; error = reject_slave_controller(ofconn); if (error) { @@ -3628,11 +3697,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 = ofproto_check_ofpacts(ofproto, fm.ofpacts, fm.ofpacts_len, + &fm.match.flow, &meter_id); } + if (!error) { - error = handle_flow_mod__(ofproto, ofconn, &fm, oh); + error = handle_flow_mod__(ofproto, ofconn, &fm, oh, &meter_id); } if (error) { goto exit_free_ofpacts; @@ -3673,7 +3743,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, + const uint32_t *meter_id) { if (ofproto->n_pending >= 50) { ovs_assert(!list_is_empty(&ofproto->pending)); @@ -3682,13 +3753,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, meter_id); case OFPFC_MODIFY: - return modify_flows_loose(ofproto, ofconn, fm, oh); + return modify_flows_loose(ofproto, ofconn, fm, oh, meter_id); case OFPFC_MODIFY_STRICT: - return modify_flow_strict(ofproto, ofconn, fm, oh); + return modify_flow_strict(ofproto, ofconn, fm, oh, meter_id); case OFPFC_DELETE: return delete_flows_loose(ofproto, ofconn, fm, oh); @@ -4109,6 +4180,351 @@ 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); + 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. */ + 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; + + error = reject_slave_controller(ofconn); + if (error) { + goto exit; + } + + ofpbuf_use_stub(&bands, bands_stub, sizeof bands_stub); + + error = ofputil_decode_meter_mod(oh, &mm, &bands); + if (error) { + goto exit_free_bands; + } + + meter_id = mm.meter.meter_id; + + if (mm.command != OFPMC13_DELETE) { + /* Fails also when meters are not implemented by the provider. */ + if (!meter_id || meter_id > ofproto->meter_features.max_meters) { + error = OFPERR_OFPMMFC_INVALID_METER; + goto exit_free_bands; + } + if (mm.meter.n_bands > ofproto->meter_features.max_bands) { + error = OFPERR_OFPMMFC_OUT_OF_BANDS; + goto exit_free_bands; + } + } + + 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' is 0 when meters are not implemented by the provider. */ + last = ofproto->last_meter; + } else { + /* True also when meters are not implemented by the provider. */ + if (!meter_id || 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 collected rules. */ + if (!list_is_empty(&rules)) { + delete_flows__(ofproto, ofconn, oh, &rules, OFPRR_METER_DELETE); + } + + /* 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; + } + +exit_free_bands: + ofpbuf_uninit(&bands); +exit: + 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; + + if (ofproto->ofproto_class->meter_get_features) { + ofproto->ofproto_class->meter_get_features(ofproto, &features); + } else { + memset(&features, 0, sizeof 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 = 1; + /* 'last' is 0 when meters are not implemented by the provider. */ + last = ofproto->last_meter; + } else { + /* True also when meters are not implemented by the provider. */ + if (!meter_id || 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) { @@ -4144,6 +4560,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); @@ -4202,16 +4621,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; @@ -4964,11 +5386,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 @@ -4986,9 +5414,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..b3ce87e 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.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev