2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-02 23:35:27 +00:00

ofproto: Centralize action checking, doing it at decode time.

Jarno pointed out that modify_flows__() didn't really need to check every
instance of the flow separately.  After some further investigation I
decided that this was even more of an improvement.

CC: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
This commit is contained in:
Ben Pfaff
2013-11-01 21:45:28 -07:00
parent 0e19706066
commit 7e9f8266a4
8 changed files with 76 additions and 75 deletions

View File

@@ -1823,8 +1823,9 @@ ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports)
* Modifies some actions, filling in fields that could not be properly set * Modifies some actions, filling in fields that could not be properly set
* without context. */ * without context. */
static enum ofperr static enum ofperr
ofpact_check__(struct ofpact *a, struct flow *flow, ofp_port_t max_ports, ofpact_check__(struct ofpact *a, struct flow *flow,
uint8_t table_id, bool enforce_consistency) bool enforce_consistency, ofp_port_t max_ports,
uint8_t table_id, uint8_t n_tables)
{ {
const struct ofpact_enqueue *enqueue; const struct ofpact_enqueue *enqueue;
const struct mf_field *mf; const struct mf_field *mf;
@@ -2020,7 +2021,7 @@ ofpact_check__(struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
case OFPACT_WRITE_ACTIONS: { case OFPACT_WRITE_ACTIONS: {
struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a); struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
return ofpacts_check(on->actions, ofpact_nest_get_action_len(on), return ofpacts_check(on->actions, ofpact_nest_get_action_len(on),
flow, max_ports, table_id, false); flow, false, max_ports, table_id, n_tables);
} }
case OFPACT_WRITE_METADATA: case OFPACT_WRITE_METADATA:
@@ -2034,11 +2035,14 @@ ofpact_check__(struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
return 0; return 0;
} }
case OFPACT_GOTO_TABLE: case OFPACT_GOTO_TABLE: {
if (ofpact_get_GOTO_TABLE(a)->table_id <= table_id) { uint8_t goto_table = ofpact_get_GOTO_TABLE(a)->table_id;
if ((table_id != 255 && goto_table <= table_id)
|| (n_tables != 255 && goto_table >= n_tables)) {
return OFPERR_OFPBRC_BAD_TABLE_ID; return OFPERR_OFPBRC_BAD_TABLE_ID;
} }
return 0; return 0;
}
case OFPACT_GROUP: case OFPACT_GROUP:
return 0; return 0;
@@ -2063,8 +2067,9 @@ ofpact_check__(struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
* May temporarily modify 'flow', but restores the changes before returning. */ * May temporarily modify 'flow', but restores the changes before returning. */
enum ofperr enum ofperr
ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len, ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
struct flow *flow, ofp_port_t max_ports, uint8_t table_id, struct flow *flow, bool enforce_consistency,
bool enforce_consistency) ofp_port_t max_ports,
uint8_t table_id, uint8_t n_tables)
{ {
struct ofpact *a; struct ofpact *a;
ovs_be16 dl_type = flow->dl_type; ovs_be16 dl_type = flow->dl_type;
@@ -2072,8 +2077,8 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
enum ofperr error = 0; enum ofperr error = 0;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
error = ofpact_check__(a, flow, max_ports, table_id, error = ofpact_check__(a, flow, enforce_consistency,
enforce_consistency); max_ports, table_id, n_tables);
if (error) { if (error) {
break; break;
} }

View File

@@ -587,8 +587,9 @@ enum ofperr ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
enum ofp_version version, enum ofp_version version,
struct ofpbuf *ofpacts); struct ofpbuf *ofpacts);
enum ofperr ofpacts_check(struct ofpact[], size_t ofpacts_len, enum ofperr ofpacts_check(struct ofpact[], size_t ofpacts_len,
struct flow *, ofp_port_t max_ports, struct flow *, bool enforce_consistency,
uint8_t table_id, bool enforce_consistency); ofp_port_t max_ports,
uint8_t table_id, uint8_t n_tables);
enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len); enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len);
enum ofperr ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports); enum ofperr ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports);

View File

@@ -1368,7 +1368,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
enum ofperr err; enum ofperr err;
err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow, err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow,
OFPP_MAX, 0, true); true, OFPP_MAX, fm->table_id, 255);
if (err) { if (err) {
if (!enforce_consistency && if (!enforce_consistency &&
err == OFPERR_OFPBAC_MATCH_INCONSISTENT) { err == OFPERR_OFPBAC_MATCH_INCONSISTENT) {
@@ -1377,7 +1377,8 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
/* Try again, allowing for inconsistency. /* Try again, allowing for inconsistency.
* XXX: As a side effect, logging may be duplicated. */ * XXX: As a side effect, logging may be duplicated. */
err = ofpacts_check(ofpacts.data, ofpacts.size, err = ofpacts_check(ofpacts.data, ofpacts.size,
&fm->match.flow, OFPP_MAX, 0, false); &fm->match.flow, false,
OFPP_MAX, fm->table_id, 255);
} }
if (err) { if (err) {
error = xasprintf("actions are invalid with specified match " error = xasprintf("actions are invalid with specified match "

View File

@@ -757,7 +757,8 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
protocol = ofputil_protocol_set_tid(protocol, true); protocol = ofputil_protocol_set_tid(protocol, true);
ofpbuf_init(&ofpacts, 64); ofpbuf_init(&ofpacts, 64);
error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts); error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts,
OFPP_MAX, 255);
if (error) { if (error) {
ofpbuf_uninit(&ofpacts); ofpbuf_uninit(&ofpacts);
ofp_print_error(s, error); ofp_print_error(s, error);

View File

@@ -1484,7 +1484,8 @@ enum ofperr
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
const struct ofp_header *oh, const struct ofp_header *oh,
enum ofputil_protocol protocol, enum ofputil_protocol protocol,
struct ofpbuf *ofpacts) struct ofpbuf *ofpacts,
ofp_port_t max_port, uint8_t max_table)
{ {
ovs_be16 raw_flags; ovs_be16 raw_flags;
enum ofperr error; enum ofperr error;
@@ -1663,7 +1664,9 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
: OFPERR_OFPFMFC_TABLE_FULL); : OFPERR_OFPFMFC_TABLE_FULL);
} }
return 0; return ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
oh->version > OFP10_VERSION, max_port,
fm->table_id, max_table);
} }
static enum ofperr static enum ofperr

View File

@@ -294,7 +294,9 @@ struct ofputil_flow_mod {
enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *, enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
const struct ofp_header *, const struct ofp_header *,
enum ofputil_protocol, enum ofputil_protocol,
struct ofpbuf *ofpacts); struct ofpbuf *ofpacts,
ofp_port_t max_port,
uint8_t max_table);
struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *, struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
enum ofputil_protocol); enum ofputil_protocol);

View File

@@ -271,9 +271,12 @@ static enum ofperr modify_flows__(struct ofproto *, struct ofconn *,
static void delete_flow__(struct rule *rule, struct ofopgroup *, static void delete_flow__(struct rule *rule, struct ofopgroup *,
enum ofp_flow_removed_reason) enum ofp_flow_removed_reason)
OVS_REQUIRES(ofproto_mutex); 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, static bool ofproto_group_exists(const struct ofproto *ofproto,
uint32_t group_id) uint32_t group_id)
OVS_REQ_RDLOCK(ofproto->groups_rwlock); OVS_EXCLUDED(ofproto->groups_rwlock);
static enum ofperr add_group(struct ofproto *, struct ofputil_group_mod *); static enum ofperr add_group(struct ofproto *, struct ofputil_group_mod *);
static bool handle_openflow(struct ofconn *, const struct ofpbuf *); static bool handle_openflow(struct ofconn *, const struct ofpbuf *);
static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *, static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
@@ -2829,46 +2832,33 @@ reject_slave_controller(struct ofconn *ofconn)
} }
} }
/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are appropriate /* Checks that the 'ofpacts_len' bytes of action in 'ofpacts' are appropriate
* for a packet with the prerequisites satisfied by 'flow' in table 'table_id'. * for 'ofproto':
* 'flow' may be temporarily modified, but is restored at return. *
*/ * - 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 static enum ofperr
ofproto_check_ofpacts(struct ofproto *ofproto, ofproto_check_ofpacts(struct ofproto *ofproto,
struct ofpact ofpacts[], size_t ofpacts_len, const struct ofpact ofpacts[], size_t ofpacts_len)
struct flow *flow, uint8_t table_id,
const struct ofp_header *oh)
{ {
enum ofperr error;
const struct ofpact *a; const struct ofpact *a;
uint32_t mid; 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;
}
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
if (a->type == OFPACT_GROUP) {
bool exists;
ovs_rwlock_rdlock(&ofproto->groups_rwlock);
exists = ofproto_group_exists(ofproto,
ofpact_get_GROUP(a)->group_id);
ovs_rwlock_unlock(&ofproto->groups_rwlock);
if (!exists) {
return OFPERR_OFPBAC_BAD_OUT_GROUP;
}
}
}
mid = ofpacts_get_meter(ofpacts, ofpacts_len); mid = ofpacts_get_meter(ofpacts, ofpacts_len);
if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) { if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
return OFPERR_OFPMMFC_INVALID_METER; 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; return 0;
} }
@@ -2918,7 +2908,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
/* Verify actions against packet, then send packet if successful. */ /* Verify actions against packet, then send packet if successful. */
in_port_.ofp_port = po.in_port; in_port_.ofp_port = po.in_port;
flow_extract(payload, 0, 0, NULL, &in_port_, &flow); 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) { if (!error) {
error = p->ofproto_class->packet_out(p, payload, &flow, error = p->ofproto_class->packet_out(p, payload, &flow,
po.ofpacts, po.ofpacts_len); po.ofpacts, po.ofpacts_len);
@@ -3966,14 +3956,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. */ /* Serialize against pending deletion. */
if (is_flow_deletion_pending(ofproto, &cr, table_id)) { if (is_flow_deletion_pending(ofproto, &cr, table_id)) {
cls_rule_destroy(&cr); cls_rule_destroy(&cr);
@@ -4072,19 +4054,6 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
enum ofperr error; enum ofperr error;
size_t i; 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; type = fm->command == OFPFC_ADD ? OFOPERATION_REPLACE : OFOPERATION_MODIFY;
group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id); group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
error = OFPERR_OFPBRC_EPERM; error = OFPERR_OFPBRC_EPERM;
@@ -4417,7 +4386,12 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn), 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) { if (!error) {
error = handle_flow_mod__(ofproto, ofconn, &fm, oh); error = handle_flow_mod__(ofproto, ofconn, &fm, oh);
} }
@@ -5321,7 +5295,7 @@ ofproto_group_write_lookup(const struct ofproto *ofproto, uint32_t group_id,
} }
static bool 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) OVS_REQ_RDLOCK(ofproto->groups_rwlock)
{ {
struct ofgroup *grp; struct ofgroup *grp;
@@ -5335,6 +5309,19 @@ ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
return false; 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 static uint32_t
group_get_ref_count(struct ofgroup *group) group_get_ref_count(struct ofgroup *group)
OVS_EXCLUDED(ofproto_mutex) OVS_EXCLUDED(ofproto_mutex)
@@ -5569,7 +5556,7 @@ add_group(struct ofproto *ofproto, struct ofputil_group_mod *gm)
goto unlock_out; goto unlock_out;
} }
if (ofproto_group_exists(ofproto, gm->group_id)) { if (ofproto_group_exists__(ofproto, gm->group_id)) {
error = OFPERR_OFPGMFC_GROUP_EXISTS; error = OFPERR_OFPGMFC_GROUP_EXISTS;
goto unlock_out; goto unlock_out;
} }

View File

@@ -3077,8 +3077,9 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
/* Verify actions, enforce consistency. */ /* Verify actions, enforce consistency. */
struct flow flow; struct flow flow;
memset(&flow, 0, sizeof flow); memset(&flow, 0, sizeof flow);
error = ofpacts_check(ofpacts.data, ofpacts.size, &flow, OFPP_MAX, error = ofpacts_check(ofpacts.data, ofpacts.size, &flow,
table_id ? atoi(table_id) : 0, true); true, OFPP_MAX,
table_id ? atoi(table_id) : 0, 255);
} }
if (error) { if (error) {
printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error)); printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));