2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

ofp-actions: Support OF1.5 meter action.

OpenFlow 1.5 changed "meter" from an instruction to an action.  This commit
supports it properly.

Acked-by: Numan Siddique <nusiddiq@redhat.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Ben Pfaff 2019-04-30 09:19:27 -07:00
parent 3925b3e9af
commit 4332b67199
6 changed files with 101 additions and 43 deletions

View File

@ -242,12 +242,6 @@ features are listed in the previous section.
(optional for OF1.5+) (optional for OF1.5+)
* Meter action
(EXT-379)
(required for OF1.5+ if metering is supported)
* Port properties for pipeline fields * Port properties for pipeline fields
Prototype for OVS was done during specification. Prototype for OVS was done during specification.

1
NEWS
View File

@ -8,6 +8,7 @@ Post-v2.11.0
- OpenFlow: - OpenFlow:
* Removed support for OpenFlow 1.6 (draft), which ONF abandoned. * Removed support for OpenFlow 1.6 (draft), which ONF abandoned.
* New action "check_pkt_larger". * New action "check_pkt_larger".
* Support for OpenFlow 1.5 "meter" action.
- Userspace datapath: - Userspace datapath:
* ICMPv6 ND enhancements: support for match and set ND options type * ICMPv6 ND enhancements: support for match and set ND options type
and reserved fields. and reserved fields.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc. * Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017, 2019 Nicira, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -133,7 +133,7 @@ struct vl_mff_map;
OFPACT(DEBUG_RECIRC, ofpact_null, ofpact, "debug_recirc") \ OFPACT(DEBUG_RECIRC, ofpact_null, ofpact, "debug_recirc") \
OFPACT(DEBUG_SLOW, ofpact_null, ofpact, "debug_slow") \ OFPACT(DEBUG_SLOW, ofpact_null, ofpact, "debug_slow") \
\ \
/* Instructions. */ \ /* Instructions ("meter" is an action in OF1.5+). */ \
OFPACT(METER, ofpact_meter, ofpact, "meter") \ OFPACT(METER, ofpact_meter, ofpact, "meter") \
OFPACT(CLEAR_ACTIONS, ofpact_null, ofpact, "clear_actions") \ OFPACT(CLEAR_ACTIONS, ofpact_null, ofpact, "clear_actions") \
OFPACT(WRITE_ACTIONS, ofpact_nest, actions, "write_actions") \ OFPACT(WRITE_ACTIONS, ofpact_nest, actions, "write_actions") \
@ -1360,7 +1360,7 @@ enum {
const char *ovs_instruction_name_from_type(enum ovs_instruction_type type); const char *ovs_instruction_name_from_type(enum ovs_instruction_type type);
int ovs_instruction_type_from_name(const char *name); int ovs_instruction_type_from_name(const char *name);
enum ovs_instruction_type ovs_instruction_type_from_ofpact_type( enum ovs_instruction_type ovs_instruction_type_from_ofpact_type(
enum ofpact_type); enum ofpact_type, enum ofp_version);
enum ofperr ovs_instruction_type_from_inst_type( enum ofperr ovs_instruction_type_from_inst_type(
enum ovs_instruction_type *instruction_type, const uint16_t inst_type); enum ovs_instruction_type *instruction_type, const uint16_t inst_type);

View File

@ -259,6 +259,9 @@ enum ofp_raw_action_type {
/* NX1.0-1.4(6): struct nx_action_reg_move, ... VLMFF */ /* NX1.0-1.4(6): struct nx_action_reg_move, ... VLMFF */
NXAST_RAW_REG_MOVE, NXAST_RAW_REG_MOVE,
/* OF1.5+(29): uint32_t. */
OFPAT_RAW15_METER,
/* ## ------------------------- ## */ /* ## ------------------------- ## */
/* ## Nicira extension actions. ## */ /* ## Nicira extension actions. ## */
/* ## ------------------------- ## */ /* ## ------------------------- ## */
@ -407,7 +410,7 @@ static void ofpacts_update_instruction_actions(struct ofpbuf *openflow,
static void pad_ofpat(struct ofpbuf *openflow, size_t start_ofs); static void pad_ofpat(struct ofpbuf *openflow, size_t start_ofs);
static enum ofperr ofpacts_verify(const struct ofpact[], size_t ofpacts_len, static enum ofperr ofpacts_verify(const struct ofpact[], size_t ofpacts_len,
uint32_t allowed_ovsinsts, enum ofp_version, uint32_t allowed_ovsinsts,
enum ofpact_type outer_action, enum ofpact_type outer_action,
char **errorp); char **errorp);
@ -5957,7 +5960,7 @@ parse_CLONE(char *arg, const struct ofpact_parse_params *pp)
char *error; char *error;
ofpbuf_pull(pp->ofpacts, sizeof *clone); ofpbuf_pull(pp->ofpacts, sizeof *clone);
error = ofpacts_parse_copy(arg, pp, false, 0); error = ofpacts_parse_copy(arg, pp, false, OFPACT_CLONE);
/* header points to the action list */ /* header points to the action list */
pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, sizeof *clone); pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, sizeof *clone);
clone = pp->ofpacts->header; clone = pp->ofpacts->header;
@ -7216,14 +7219,32 @@ check_OUTPUT_TRUNC(const struct ofpact_output_trunc *a,
return ofpact_check_output_port(a->port, cp->max_ports); return ofpact_check_output_port(a->port, cp->max_ports);
} }
/* Meter instruction. */ /* Meter.
*
* In OpenFlow 1.3 and 1.4, "meter" is an instruction.
* In OpenFlow 1.5 and later, "meter" is an action.
*
* OpenFlow 1.5 */
static enum ofperr
decode_OFPAT_RAW15_METER(uint32_t meter_id,
enum ofp_version ofp_version OVS_UNUSED,
struct ofpbuf *out)
{
struct ofpact_meter *om = ofpact_put_METER(out);
om->meter_id = meter_id;
om->provider_meter_id = UINT32_MAX; /* No provider meter ID. */
return 0;
}
static void static void
encode_METER(const struct ofpact_meter *meter, encode_METER(const struct ofpact_meter *meter,
enum ofp_version ofp_version, struct ofpbuf *out) enum ofp_version ofp_version, struct ofpbuf *out)
{ {
if (ofp_version >= OFP13_VERSION) { if (ofp_version == OFP13_VERSION || ofp_version == OFP14_VERSION) {
instruction_put_OFPIT13_METER(out)->meter_id = htonl(meter->meter_id); instruction_put_OFPIT13_METER(out)->meter_id = htonl(meter->meter_id);
} else if (ofp_version >= OFP15_VERSION) {
put_OFPAT15_METER(out, meter->meter_id);
} }
} }
@ -7683,8 +7704,8 @@ ofpacts_pull_openflow_actions__(struct ofpbuf *openflow,
error = ofpacts_decode(actions, actions_len, version, vl_mff_map, error = ofpacts_decode(actions, actions_len, version, vl_mff_map,
ofpacts_tlv_bitmap, ofpacts); ofpacts_tlv_bitmap, ofpacts);
if (!error) { if (!error) {
error = ofpacts_verify(ofpacts->data, ofpacts->size, allowed_ovsinsts, error = ofpacts_verify(ofpacts->data, ofpacts->size, version,
outer_action, NULL); allowed_ovsinsts, outer_action, NULL);
} }
if (error) { if (error) {
ofpbuf_clear(ofpacts); ofpbuf_clear(ofpacts);
@ -7724,10 +7745,10 @@ ofpacts_pull_openflow_actions(struct ofpbuf *openflow,
uint64_t *ofpacts_tlv_bitmap, uint64_t *ofpacts_tlv_bitmap,
struct ofpbuf *ofpacts) struct ofpbuf *ofpacts)
{ {
return ofpacts_pull_openflow_actions__(openflow, actions_len, version, return ofpacts_pull_openflow_actions__(
1u << OVSINST_OFPIT11_APPLY_ACTIONS, openflow, actions_len, version,
ofpacts, 0, vl_mff_map, (1u << OVSINST_OFPIT11_APPLY_ACTIONS) | (1u << OVSINST_OFPIT13_METER),
ofpacts_tlv_bitmap); ofpacts, 0, vl_mff_map, ofpacts_tlv_bitmap);
} }
/* OpenFlow 1.1 action sets. */ /* OpenFlow 1.1 action sets. */
@ -7981,11 +8002,14 @@ ovs_instruction_type_from_name(const char *name)
} }
enum ovs_instruction_type enum ovs_instruction_type
ovs_instruction_type_from_ofpact_type(enum ofpact_type type) ovs_instruction_type_from_ofpact_type(enum ofpact_type type,
enum ofp_version version)
{ {
switch (type) { switch (type) {
case OFPACT_METER: case OFPACT_METER:
return OVSINST_OFPIT13_METER; return (version >= OFP15_VERSION
? OVSINST_OFPIT11_APPLY_ACTIONS
: OVSINST_OFPIT13_METER);
case OFPACT_CLEAR_ACTIONS: case OFPACT_CLEAR_ACTIONS:
return OVSINST_OFPIT11_CLEAR_ACTIONS; return OVSINST_OFPIT11_CLEAR_ACTIONS;
case OFPACT_WRITE_ACTIONS: case OFPACT_WRITE_ACTIONS:
@ -8080,7 +8104,7 @@ struct ovsinst_map {
static const struct ovsinst_map * static const struct ovsinst_map *
get_ovsinst_map(enum ofp_version version) get_ovsinst_map(enum ofp_version version)
{ {
/* OpenFlow 1.1 and 1.2 instructions. */ /* OpenFlow 1.1, 1.2, and 1.5 instructions. */
static const struct ovsinst_map of11[] = { static const struct ovsinst_map of11[] = {
{ OVSINST_OFPIT11_GOTO_TABLE, 1 }, { OVSINST_OFPIT11_GOTO_TABLE, 1 },
{ OVSINST_OFPIT11_WRITE_METADATA, 2 }, { OVSINST_OFPIT11_WRITE_METADATA, 2 },
@ -8090,7 +8114,7 @@ get_ovsinst_map(enum ofp_version version)
{ 0, -1 }, { 0, -1 },
}; };
/* OpenFlow 1.3+ instructions. */ /* OpenFlow 1.3 and 1.4 instructions. */
static const struct ovsinst_map of13[] = { static const struct ovsinst_map of13[] = {
{ OVSINST_OFPIT11_GOTO_TABLE, 1 }, { OVSINST_OFPIT11_GOTO_TABLE, 1 },
{ OVSINST_OFPIT11_WRITE_METADATA, 2 }, { OVSINST_OFPIT11_WRITE_METADATA, 2 },
@ -8101,7 +8125,7 @@ get_ovsinst_map(enum ofp_version version)
{ 0, -1 }, { 0, -1 },
}; };
return version < OFP13_VERSION ? of11 : of13; return version == OFP13_VERSION || version == OFP14_VERSION ? of13 : of11;
} }
/* Converts 'ovsinst_bitmap', a bitmap whose bits correspond to OVSINST_* /* Converts 'ovsinst_bitmap', a bitmap whose bits correspond to OVSINST_*
@ -8193,7 +8217,7 @@ OVS_INSTRUCTIONS
static enum ofperr static enum ofperr
decode_openflow11_instructions(const struct ofp11_instruction insts[], decode_openflow11_instructions(const struct ofp11_instruction insts[],
size_t n_insts, size_t n_insts, enum ofp_version version,
const struct ofp11_instruction *out[]) const struct ofp11_instruction *out[])
{ {
const struct ofp11_instruction *inst; const struct ofp11_instruction *inst;
@ -8209,6 +8233,11 @@ decode_openflow11_instructions(const struct ofp11_instruction insts[],
return error; return error;
} }
if (type == OVSINST_OFPIT13_METER && version >= OFP15_VERSION) {
/* "meter" is an action, not an instruction, in OpenFlow 1.5. */
return OFPERR_OFPBIC_UNKNOWN_INST;
}
if (out[type]) { if (out[type]) {
return OFPERR_OFPBIC_DUP_INST; return OFPERR_OFPBIC_DUP_INST;
} }
@ -8271,7 +8300,7 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
} }
error = decode_openflow11_instructions( error = decode_openflow11_instructions(
instructions, instructions_len / OFP11_INSTRUCTION_ALIGN, instructions, instructions_len / OFP11_INSTRUCTION_ALIGN, version,
insts); insts);
if (error) { if (error) {
goto exit; goto exit;
@ -8344,7 +8373,7 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
ogt->table_id = oigt->table_id; ogt->table_id = oigt->table_id;
} }
error = ofpacts_verify(ofpacts->data, ofpacts->size, error = ofpacts_verify(ofpacts->data, ofpacts->size, version,
(1u << N_OVS_INSTRUCTIONS) - 1, 0, NULL); (1u << N_OVS_INSTRUCTIONS) - 1, 0, NULL);
exit: exit:
if (error) { if (error) {
@ -8559,7 +8588,8 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action,
if (outer_action) { if (outer_action) {
ovs_assert(outer_action == OFPACT_WRITE_ACTIONS ovs_assert(outer_action == OFPACT_WRITE_ACTIONS
|| outer_action == OFPACT_CT); || outer_action == OFPACT_CT
|| outer_action == OFPACT_CLONE);
if (outer_action == OFPACT_CT) { if (outer_action == OFPACT_CT) {
if (!field) { if (!field) {
@ -8571,6 +8601,10 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action,
return OFPERR_OFPBAC_BAD_ARGUMENT; return OFPERR_OFPBAC_BAD_ARGUMENT;
} }
} }
if (a->type == OFPACT_METER) {
return unsupported_nesting(a->type, outer_action, errorp);
}
} }
return 0; return 0;
@ -8580,6 +8614,10 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action,
* appropriate order as defined by the OpenFlow spec and as required by Open * appropriate order as defined by the OpenFlow spec and as required by Open
* vSwitch. * vSwitch.
* *
* The 'version' is relevant only for error reporting: Open vSwitch enforces
* the same rules for every version of OpenFlow, but different versions require
* different error codes.
*
* 'allowed_ovsinsts' is a bitmap of OVSINST_* values, in which 1-bits indicate * 'allowed_ovsinsts' is a bitmap of OVSINST_* values, in which 1-bits indicate
* instructions that are allowed within 'ofpacts[]'. * instructions that are allowed within 'ofpacts[]'.
* *
@ -8587,8 +8625,8 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action,
* within another action of type 'outer_action'. */ * within another action of type 'outer_action'. */
static enum ofperr static enum ofperr
ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len, ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len,
uint32_t allowed_ovsinsts, enum ofpact_type outer_action, enum ofp_version version, uint32_t allowed_ovsinsts,
char **errorp) enum ofpact_type outer_action, char **errorp)
{ {
const struct ofpact *a; const struct ofpact *a;
enum ovs_instruction_type inst; enum ovs_instruction_type inst;
@ -8616,7 +8654,7 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len,
return error; return error;
} }
next = ovs_instruction_type_from_ofpact_type(a->type); next = ovs_instruction_type_from_ofpact_type(a->type, version);
if (a > ofpacts if (a > ofpacts
&& (inst == OVSINST_OFPIT11_APPLY_ACTIONS && (inst == OVSINST_OFPIT11_APPLY_ACTIONS
? next < inst ? next < inst
@ -8638,8 +8676,13 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len,
if (!((1u << next) & allowed_ovsinsts)) { if (!((1u << next) & allowed_ovsinsts)) {
const char *name = ovs_instruction_name_from_type(next); const char *name = ovs_instruction_name_from_type(next);
verify_error(errorp, "%s instruction not allowed here", name); if (next == OVSINST_OFPIT13_METER && version >= OFP15_VERSION) {
return OFPERR_OFPBIC_UNSUP_INST; verify_error(errorp, "%s action not allowed here", name);
return OFPERR_OFPBAC_BAD_TYPE;
} else {
verify_error(errorp, "%s instruction not allowed here", name);
return OFPERR_OFPBIC_UNSUP_INST;
}
} }
inst = next; inst = next;
@ -8684,9 +8727,9 @@ ofpacts_put_openflow_actions(const struct ofpact ofpacts[], size_t ofpacts_len,
} }
static enum ovs_instruction_type static enum ovs_instruction_type
ofpact_is_apply_actions(const struct ofpact *a) ofpact_is_apply_actions(const struct ofpact *a, enum ofp_version version)
{ {
return (ovs_instruction_type_from_ofpact_type(a->type) return (ovs_instruction_type_from_ofpact_type(a->type, version)
== OVSINST_OFPIT11_APPLY_ACTIONS); == OVSINST_OFPIT11_APPLY_ACTIONS);
} }
@ -8707,14 +8750,14 @@ ofpacts_put_openflow_instructions(const struct ofpact ofpacts[],
a = ofpacts; a = ofpacts;
while (a < end) { while (a < end) {
if (ofpact_is_apply_actions(a)) { if (ofpact_is_apply_actions(a, ofp_version)) {
size_t ofs = openflow->size; size_t ofs = openflow->size;
instruction_put_OFPIT11_APPLY_ACTIONS(openflow); instruction_put_OFPIT11_APPLY_ACTIONS(openflow);
do { do {
encode_ofpact(a, ofp_version, openflow); encode_ofpact(a, ofp_version, openflow);
a = ofpact_next(a); a = ofpact_next(a);
} while (a < end && ofpact_is_apply_actions(a)); } while (a < end && ofpact_is_apply_actions(a, ofp_version));
ofpacts_update_instruction_actions(openflow, ofs); ofpacts_update_instruction_actions(openflow, ofs);
} else { } else {
encode_ofpact(a, ofp_version, openflow); encode_ofpact(a, ofp_version, openflow);
@ -9023,12 +9066,13 @@ ofpacts_get_meter(const struct ofpact ofpacts[], size_t ofpacts_len)
const struct ofpact *a; const struct ofpact *a;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
enum ovs_instruction_type inst;
inst = ovs_instruction_type_from_ofpact_type(a->type);
if (a->type == OFPACT_METER) { if (a->type == OFPACT_METER) {
return ofpact_get_METER(a)->meter_id; return ofpact_get_METER(a)->meter_id;
} else if (inst > OVSINST_OFPIT13_METER) { }
enum ovs_instruction_type inst
= ovs_instruction_type_from_ofpact_type(a->type, 0);
if (inst > OVSINST_OFPIT13_METER) {
break; break;
} }
} }
@ -9174,6 +9218,13 @@ ofpacts_parse__(char *str, const struct ofpact_parse_params *pp,
ofp_port_t port; ofp_port_t port;
if (ofpact_type_from_name(key, &type)) { if (ofpact_type_from_name(key, &type)) {
error = ofpact_parse(type, value, pp); error = ofpact_parse(type, value, pp);
if (type == OFPACT_METER && !allow_instructions) {
/* Meter is an action in OF1.5 and it's being used in a
* context where instructions aren't allowed. Therefore,
* this must be OF1.5+. */
*pp->usable_protocols &= OFPUTIL_P_OF15_UP;
}
} else if (!strcasecmp(key, "mod_vlan_vid")) { } else if (!strcasecmp(key, "mod_vlan_vid")) {
error = parse_set_vlan_vid(value, true, pp); error = parse_set_vlan_vid(value, true, pp);
} else if (!strcasecmp(key, "mod_vlan_pcp")) { } else if (!strcasecmp(key, "mod_vlan_pcp")) {
@ -9208,7 +9259,7 @@ ofpacts_parse__(char *str, const struct ofpact_parse_params *pp,
} }
char *error = NULL; char *error = NULL;
ofpacts_verify(pp->ofpacts->data, pp->ofpacts->size, ofpacts_verify(pp->ofpacts->data, pp->ofpacts->size, OFP11_VERSION,
(allow_instructions (allow_instructions
? (1u << N_OVS_INSTRUCTIONS) - 1 ? (1u << N_OVS_INSTRUCTIONS) - 1
: ((1u << OVSINST_OFPIT11_APPLY_ACTIONS) : ((1u << OVSINST_OFPIT11_APPLY_ACTIONS)

View File

@ -2832,11 +2832,20 @@ while <var>link</var> &gt; <var>max_link</var>
1.5 changes <code>meter</code> from an instruction to an action. 1.5 changes <code>meter</code> from an instruction to an action.
</p> </p>
<p>
OpenFlow 1.5 allows implementations to restrict <code>meter</code> to
be the first action in an action list and to exclude
<code>meter</code> from action sets, for better compatibility with
OpenFlow 1.3 and 1.4. Open vSwitch restricts the <code>meter</code>
action both ways.
</p>
<p> <p>
Open vSwitch 2.0 introduced OpenFlow protocol support for meters, but Open vSwitch 2.0 introduced OpenFlow protocol support for meters, but
it did not include a datapath implementation. Open vSwitch 2.7 added it did not include a datapath implementation. Open vSwitch 2.7 added
meter support to the userspace datapath. Open vSwitch 2.10 added meter support to the userspace datapath. Open vSwitch 2.10 added
meter support to the kernel datapath. meter support to the kernel datapath. Open vSwitch 2.12 added
support for meter as an action in OpenFlow 1.5.
</p> </p>
</conformance> </conformance>
</action> </action>

View File

@ -792,6 +792,9 @@ AT_DATA([test-data], [dnl
# actions=set_field:00:00:00:00:12:34/00:00:00:00:ff:ff->eth_src # actions=set_field:00:00:00:00:12:34/00:00:00:00:ff:ff->eth_src
0019 0018 8000090c 000000001234 00000000ffff 00000000 0019 0018 8000090c 000000001234 00000000ffff 00000000
# actions=meter:5
001d 0008 00000005
]) ])
sed '/^[[#&]]/d' < test-data > input.txt sed '/^[[#&]]/d' < test-data > input.txt
sed -n 's/^# //p; /^$/p' < test-data > expout sed -n 's/^# //p; /^$/p' < test-data > expout