* Modifies some actions, filling in fields that could not be properly set
* without context. */
static enum ofperr
-ofpact_check__(struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
- uint8_t table_id, bool enforce_consistency)
+ofpact_check__(struct ofpact *a, struct flow *flow,
+ bool enforce_consistency, ofp_port_t max_ports,
+ uint8_t table_id, uint8_t n_tables)
{
const struct ofpact_enqueue *enqueue;
const struct mf_field *mf;
case OFPACT_WRITE_ACTIONS: {
struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
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:
return 0;
}
- case OFPACT_GOTO_TABLE:
- if (ofpact_get_GOTO_TABLE(a)->table_id <= table_id) {
+ case OFPACT_GOTO_TABLE: {
+ 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 0;
+ }
case OFPACT_GROUP:
return 0;
* May temporarily modify 'flow', but restores the changes before returning. */
enum ofperr
ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
- struct flow *flow, ofp_port_t max_ports, uint8_t table_id,
- bool enforce_consistency)
+ struct flow *flow, bool enforce_consistency,
+ ofp_port_t max_ports,
+ uint8_t table_id, uint8_t n_tables)
{
struct ofpact *a;
ovs_be16 dl_type = flow->dl_type;
enum ofperr error = 0;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
- error = ofpact_check__(a, flow, max_ports, table_id,
- enforce_consistency);
+ error = ofpact_check__(a, flow, enforce_consistency,
+ max_ports, table_id, n_tables);
if (error) {
break;
}
enum ofp_version version,
struct ofpbuf *ofpacts);
enum ofperr ofpacts_check(struct ofpact[], size_t ofpacts_len,
- struct flow *, ofp_port_t max_ports,
- uint8_t table_id, bool enforce_consistency);
+ struct flow *, 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 ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports);
enum ofperr err;
err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow,
- OFPP_MAX, 0, true);
+ true, OFPP_MAX, fm->table_id, 255);
if (err) {
if (!enforce_consistency &&
err == OFPERR_OFPBAC_MATCH_INCONSISTENT) {
/* Try again, allowing for inconsistency.
* XXX: As a side effect, logging may be duplicated. */
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) {
error = xasprintf("actions are invalid with specified match "
protocol = ofputil_protocol_set_tid(protocol, true);
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) {
ofpbuf_uninit(&ofpacts);
ofp_print_error(s, error);
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
const struct ofp_header *oh,
enum ofputil_protocol protocol,
- struct ofpbuf *ofpacts)
+ struct ofpbuf *ofpacts,
+ ofp_port_t max_port, uint8_t max_table)
{
ovs_be16 raw_flags;
enum ofperr error;
: 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
enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
const struct ofp_header *,
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 *,
enum ofputil_protocol);
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_REQ_RDLOCK(ofproto->groups_rwlock);
+ 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 *,
}
}
-/* 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,
- 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) {
- 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;
- }
+ if (a->type == OFPACT_GROUP
+ && !ofproto_group_exists(ofproto, ofpact_get_GROUP(a)->group_id)) {
+ return OFPERR_OFPBAC_BAD_OUT_GROUP;
}
}
- mid = ofpacts_get_meter(ofpacts, ofpacts_len);
- if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
- return OFPERR_OFPMMFC_INVALID_METER;
- }
return 0;
}
/* 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);
}
}
- /* 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);
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;
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);
}
}
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;
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)
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;
}
/* Verify actions, enforce consistency. */
struct flow flow;
memset(&flow, 0, sizeof flow);
- error = ofpacts_check(ofpacts.data, ofpacts.size, &flow, OFPP_MAX,
- table_id ? atoi(table_id) : 0, true);
+ error = ofpacts_check(ofpacts.data, ofpacts.size, &flow,
+ true, OFPP_MAX,
+ table_id ? atoi(table_id) : 0, 255);
}
if (error) {
printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));