X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=34b06425b70bcea0fe33c0d1ea1bb3238e77d934;hb=fe7e574970ae0ba1b8515a329c9ce88f22365b95;hp=76baaa4d90d8ef59ee217e571415e406de305eeb;hpb=0386824614e9739d4b20b8355cfe973d0a3655c7;p=sliver-openvswitch.git diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 76baaa4d9..34b06425b 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -271,6 +271,12 @@ static enum ofperr modify_flows__(struct ofproto *, struct ofconn *, static void delete_flow__(struct rule *rule, struct ofopgroup *, enum ofp_flow_removed_reason) OVS_REQUIRES(ofproto_mutex); +static bool ofproto_group_exists__(const struct ofproto *ofproto, + uint32_t group_id) + OVS_REQ_RDLOCK(ofproto->groups_rwlock); +static bool ofproto_group_exists(const struct ofproto *ofproto, + uint32_t group_id) + OVS_EXCLUDED(ofproto->groups_rwlock); static enum ofperr add_group(struct ofproto *, struct ofputil_group_mod *); static bool handle_openflow(struct ofconn *, const struct ofpbuf *); static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *, @@ -525,6 +531,15 @@ ofproto_create(const char *datapath_name, const char *datapath_type, ovs_rwlock_init(&ofproto->groups_rwlock); hmap_init(&ofproto->groups); ovs_mutex_unlock(&ofproto_mutex); + ofproto->ogf.capabilities = OFPGFC_CHAINING | OFPGFC_SELECT_LIVENESS; + ofproto->ogf.max_groups[OFPGT11_ALL] = OFPG_MAX; + ofproto->ogf.max_groups[OFPGT11_SELECT] = OFPG_MAX; + ofproto->ogf.max_groups[OFPGT11_INDIRECT] = OFPG_MAX; + ofproto->ogf.max_groups[OFPGT11_FF] = OFPG_MAX; + ofproto->ogf.actions[0] = +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) (1 << ENUM) | +#include "ofp-util.def" + 0; error = ofproto->ofproto_class->construct(ofproto); if (error) { @@ -1799,6 +1814,30 @@ ofproto_port_del(struct ofproto *ofproto, ofp_port_t ofp_port) return error; } +static void +flow_mod_init(struct ofputil_flow_mod *fm, + const struct match *match, unsigned int priority, + const struct ofpact *ofpacts, size_t ofpacts_len, + enum ofp_flow_mod_command command) +{ + memset(fm, 0, sizeof *fm); + fm->match = *match; + fm->priority = priority; + fm->cookie = 0; + fm->new_cookie = 0; + fm->modify_cookie = false; + fm->table_id = 0; + fm->command = command; + fm->idle_timeout = 0; + fm->hard_timeout = 0; + fm->buffer_id = UINT32_MAX; + fm->out_port = OFPP_ANY; + fm->out_group = OFPG_ANY; + fm->flags = 0; + fm->ofpacts = CONST_CAST(struct ofpact *, ofpacts); + fm->ofpacts_len = ofpacts_len; +} + static int simple_flow_mod(struct ofproto *ofproto, const struct match *match, unsigned int priority, @@ -1807,22 +1846,7 @@ simple_flow_mod(struct ofproto *ofproto, { struct ofputil_flow_mod fm; - memset(&fm, 0, sizeof fm); - fm.match = *match; - fm.priority = priority; - fm.cookie = 0; - fm.new_cookie = 0; - fm.modify_cookie = false; - fm.table_id = 0; - fm.command = command; - fm.idle_timeout = 0; - fm.hard_timeout = 0; - fm.buffer_id = UINT32_MAX; - fm.out_port = OFPP_ANY; - fm.out_group = OFPG_ANY; - fm.flags = 0; - fm.ofpacts = CONST_CAST(struct ofpact *, ofpacts); - fm.ofpacts_len = ofpacts_len; + flow_mod_init(&fm, match, priority, ofpacts, ofpacts_len, command); return handle_flow_mod__(ofproto, NULL, &fm, NULL); } @@ -2817,30 +2841,33 @@ 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' in table 'table_id'. - * 'flow' may be temporarily modified, but is restored at return. - */ +/* Checks that the 'ofpacts_len' bytes of action in 'ofpacts' are appropriate + * for 'ofproto': + * + * - If they use a meter, then 'ofproto' has that meter configured. + * + * - If they use any groups, then 'ofproto' has that group configured. + * + * Returns 0 if successful, otherwise an OpenFlow error. */ static enum ofperr ofproto_check_ofpacts(struct ofproto *ofproto, - const struct ofpact ofpacts[], size_t ofpacts_len, - struct flow *flow, uint8_t table_id, - const struct ofp_header *oh) + const struct ofpact ofpacts[], size_t ofpacts_len) { - enum ofperr error; + const struct ofpact *a; uint32_t mid; - error = ofpacts_check(ofpacts, ofpacts_len, flow, - u16_to_ofp(ofproto->max_ports), table_id, - oh && oh->version > OFP10_VERSION); - if (error) { - return error; - } - mid = ofpacts_get_meter(ofpacts, ofpacts_len); if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) { return OFPERR_OFPMMFC_INVALID_METER; } + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (a->type == OFPACT_GROUP + && !ofproto_group_exists(ofproto, ofpact_get_GROUP(a)->group_id)) { + return OFPERR_OFPBAC_BAD_OUT_GROUP; + } + } + return 0; } @@ -2875,7 +2902,6 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh) goto exit_free_ofpacts; } - /* Get payload. */ if (po.buffer_id != UINT32_MAX) { error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL); @@ -2890,7 +2916,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 = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len, &flow, 0, oh); + error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len); if (!error) { error = p->ofproto_class->packet_out(p, payload, &flow, po.ofpacts, po.ofpacts_len); @@ -3938,14 +3964,6 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, } } - /* Verify actions. */ - error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len, - &fm->match.flow, table_id, request); - if (error) { - cls_rule_destroy(&cr); - return error; - } - /* Serialize against pending deletion. */ if (is_flow_deletion_pending(ofproto, &cr, table_id)) { cls_rule_destroy(&cr); @@ -4044,19 +4062,6 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, enum ofperr error; size_t i; - /* Verify actions before we start to modify any rules, to avoid partial - * flow table modifications. */ - for (i = 0; i < rules->n; i++) { - struct rule *rule = rules->rules[i]; - - error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len, - &fm->match.flow, rule->table_id, - request); - if (error) { - return error; - } - } - type = fm->command == OFPFC_ADD ? OFOPERATION_REPLACE : OFOPERATION_MODIFY; group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id); error = OFPERR_OFPBRC_EPERM; @@ -4389,7 +4394,12 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn), - &ofpacts); + &ofpacts, + u16_to_ofp(ofproto->max_ports), + ofproto->n_tables); + if (!error) { + error = ofproto_check_ofpacts(ofproto, fm.ofpacts, fm.ofpacts_len); + } if (!error) { error = handle_flow_mod__(ofproto, ofconn, &fm, oh); } @@ -5293,7 +5303,7 @@ ofproto_group_write_lookup(const struct ofproto *ofproto, uint32_t group_id, } static bool -ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id) +ofproto_group_exists__(const struct ofproto *ofproto, uint32_t group_id) OVS_REQ_RDLOCK(ofproto->groups_rwlock) { struct ofgroup *grp; @@ -5307,6 +5317,44 @@ ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id) return false; } +static bool +ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id) + OVS_EXCLUDED(ofproto->groups_rwlock) +{ + bool exists; + + ovs_rwlock_rdlock(&ofproto->groups_rwlock); + exists = ofproto_group_exists__(ofproto, group_id); + ovs_rwlock_unlock(&ofproto->groups_rwlock); + + return exists; +} + +static uint32_t +group_get_ref_count(struct ofgroup *group) + OVS_EXCLUDED(ofproto_mutex) +{ + struct ofproto *ofproto = group->ofproto; + struct rule_criteria criteria; + struct rule_collection rules; + struct match match; + enum ofperr error; + uint32_t count; + + match_init_catchall(&match); + rule_criteria_init(&criteria, 0xff, &match, 0, htonll(0), htonll(0), + OFPP_ANY, group->group_id); + ovs_mutex_lock(&ofproto_mutex); + error = collect_rules_loose(ofproto, &criteria, &rules); + ovs_mutex_unlock(&ofproto_mutex); + rule_criteria_destroy(&criteria); + + count = !error && rules.n < UINT32_MAX ? rules.n : UINT32_MAX; + + rule_collection_destroy(&rules); + return count; +} + static void append_group_stats(struct ofgroup *group, struct list *replies) OVS_REQ_RDLOCK(group->rwlock) @@ -5318,14 +5366,16 @@ append_group_stats(struct ofgroup *group, struct list *replies) ogs.bucket_stats = xmalloc(group->n_buckets * sizeof *ogs.bucket_stats); + /* Provider sets the packet and byte counts, we do the rest. */ + ogs.ref_count = group_get_ref_count(group); + ogs.n_buckets = group->n_buckets; + error = (ofproto->ofproto_class->group_get_stats ? ofproto->ofproto_class->group_get_stats(group, &ogs) : EOPNOTSUPP); if (error) { - ogs.ref_count = UINT32_MAX; ogs.packet_count = UINT64_MAX; ogs.byte_count = UINT64_MAX; - ogs.n_buckets = group->n_buckets; memset(ogs.bucket_stats, 0xff, ogs.n_buckets * sizeof *ogs.bucket_stats); } @@ -5514,7 +5564,7 @@ add_group(struct ofproto *ofproto, struct ofputil_group_mod *gm) goto unlock_out; } - if (ofproto_group_exists(ofproto, gm->group_id)) { + if (ofproto_group_exists__(ofproto, gm->group_id)) { error = OFPERR_OFPGMFC_GROUP_EXISTS; goto unlock_out; } @@ -5609,6 +5659,15 @@ static void delete_group__(struct ofproto *ofproto, struct ofgroup *ofgroup) OVS_RELEASES(ofproto->groups_rwlock) { + struct match match; + struct ofputil_flow_mod fm; + + /* Delete all flow entries containing this group in a group action */ + match_init_catchall(&match); + flow_mod_init(&fm, &match, 0, NULL, 0, OFPFC_DELETE); + fm.out_group = ofgroup->group_id; + handle_flow_mod__(ofproto, NULL, &fm, NULL); + /* Must wait until existing readers are done, * while holding the container's write lock at the same time. */ ovs_rwlock_wrlock(&ofgroup->rwlock);