mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 06:15:47 +00:00
ofp-util: Implement OFPMP_TABLE_FEATURES decoding and printing.
Signed-off-by: Alexander Wu <alexander.wu@huawei.com> Co-authored-by: Ben Pfaff <blp@nicira.com> Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
359
lib/ofp-util.c
359
lib/ofp-util.c
@@ -42,6 +42,7 @@
|
||||
#include "unaligned.h"
|
||||
#include "type-props.h"
|
||||
#include "vlog.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
VLOG_DEFINE_THIS_MODULE(ofp_util);
|
||||
|
||||
@@ -4205,6 +4206,349 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
|
||||
return b;
|
||||
}
|
||||
|
||||
struct ofp_prop_header {
|
||||
ovs_be16 type;
|
||||
ovs_be16 len;
|
||||
};
|
||||
|
||||
static enum ofperr
|
||||
ofputil_pull_property(struct ofpbuf *msg, struct ofpbuf *payload,
|
||||
uint16_t *typep)
|
||||
{
|
||||
struct ofp_prop_header *oph;
|
||||
unsigned int len;
|
||||
|
||||
if (msg->size < sizeof *oph) {
|
||||
return OFPERR_OFPTFFC_BAD_LEN;
|
||||
}
|
||||
|
||||
oph = msg->data;
|
||||
len = ntohs(oph->len);
|
||||
if (len < sizeof *oph || ROUND_UP(len, 8) > msg->size) {
|
||||
return OFPERR_OFPTFFC_BAD_LEN;
|
||||
}
|
||||
|
||||
*typep = ntohs(oph->type);
|
||||
if (payload) {
|
||||
ofpbuf_use_const(payload, msg->data, len);
|
||||
ofpbuf_pull(payload, sizeof *oph);
|
||||
}
|
||||
ofpbuf_pull(msg, ROUND_UP(len, 8));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PRINTF_FORMAT(2, 3)
|
||||
log_property(bool loose, const char *message, ...)
|
||||
{
|
||||
enum vlog_level level = loose ? VLL_DBG : VLL_WARN;
|
||||
if (!vlog_should_drop(THIS_MODULE, level, &bad_ofmsg_rl)) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, message);
|
||||
vlog_valist(THIS_MODULE, level, message, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
parse_table_ids(struct ofpbuf *payload, uint32_t *ids)
|
||||
{
|
||||
uint16_t type;
|
||||
|
||||
*ids = 0;
|
||||
while (payload->size > 0) {
|
||||
enum ofperr error = ofputil_pull_property(payload, NULL, &type);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if (type < CHAR_BIT * sizeof *ids) {
|
||||
*ids |= 1u << type;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
parse_instruction_ids(struct ofpbuf *payload, bool loose, uint32_t *insts)
|
||||
{
|
||||
*insts = 0;
|
||||
while (payload->size > 0) {
|
||||
enum ovs_instruction_type inst;
|
||||
enum ofperr error;
|
||||
uint16_t ofpit;
|
||||
|
||||
error = ofputil_pull_property(payload, NULL, &ofpit);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
error = ovs_instruction_type_from_inst_type(&inst, ofpit);
|
||||
if (!error) {
|
||||
*insts |= 1u << inst;
|
||||
} else if (!loose) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
parse_table_features_next_table(struct ofpbuf *payload,
|
||||
unsigned long int *next_tables)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
memset(next_tables, 0, bitmap_n_bytes(255));
|
||||
for (i = 0; i < payload->size; i++) {
|
||||
uint8_t id = ((const uint8_t *) payload->data)[i];
|
||||
if (id >= 255) {
|
||||
return OFPERR_OFPTFFC_BAD_ARGUMENT;
|
||||
}
|
||||
bitmap_set1(next_tables, id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
parse_oxm(struct ofpbuf *b, bool loose,
|
||||
const struct mf_field **fieldp, bool *hasmask)
|
||||
{
|
||||
ovs_be32 *oxmp;
|
||||
uint32_t oxm;
|
||||
|
||||
oxmp = ofpbuf_try_pull(b, sizeof *oxmp);
|
||||
if (!oxmp) {
|
||||
return OFPERR_OFPTFFC_BAD_LEN;
|
||||
}
|
||||
oxm = ntohl(*oxmp);
|
||||
|
||||
/* Determine '*hasmask'. If 'oxm' is masked, convert it to the equivalent
|
||||
* unmasked version, because the table of OXM fields we support only has
|
||||
* masked versions of fields that we support with masks, but we should be
|
||||
* able to parse the masked versions of those here. */
|
||||
*hasmask = NXM_HASMASK(oxm);
|
||||
if (*hasmask) {
|
||||
if (NXM_LENGTH(oxm) & 1) {
|
||||
return OFPERR_OFPTFFC_BAD_ARGUMENT;
|
||||
}
|
||||
oxm = NXM_HEADER(NXM_VENDOR(oxm), NXM_FIELD(oxm), NXM_LENGTH(oxm) / 2);
|
||||
}
|
||||
|
||||
*fieldp = mf_from_nxm_header(oxm);
|
||||
if (!*fieldp) {
|
||||
log_property(loose, "unknown OXM field %#"PRIx32, ntohl(*oxmp));
|
||||
}
|
||||
return *fieldp ? 0 : OFPERR_OFPBMC_BAD_FIELD;
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
parse_oxms(struct ofpbuf *payload, bool loose,
|
||||
uint64_t *exactp, uint64_t *maskedp)
|
||||
{
|
||||
uint64_t exact, masked;
|
||||
|
||||
exact = masked = 0;
|
||||
while (payload->size > 0) {
|
||||
const struct mf_field *field;
|
||||
enum ofperr error;
|
||||
bool hasmask;
|
||||
|
||||
error = parse_oxm(payload, loose, &field, &hasmask);
|
||||
if (!error) {
|
||||
if (hasmask) {
|
||||
masked |= UINT64_C(1) << field->id;
|
||||
} else {
|
||||
exact |= UINT64_C(1) << field->id;
|
||||
}
|
||||
} else if (error != OFPERR_OFPBMC_BAD_FIELD || !loose) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
if (exactp) {
|
||||
*exactp = exact;
|
||||
} else if (exact) {
|
||||
return OFPERR_OFPBMC_BAD_MASK;
|
||||
}
|
||||
if (maskedp) {
|
||||
*maskedp = masked;
|
||||
} else if (masked) {
|
||||
return OFPERR_OFPBMC_BAD_MASK;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Converts an OFPMP_TABLE_FEATURES request or reply in 'msg' into an abstract
|
||||
* ofputil_table_features in 'tf'.
|
||||
*
|
||||
* If 'loose' is true, this function ignores properties and values that it does
|
||||
* not understand, as a controller would want to do when interpreting
|
||||
* capabilities provided by a switch. If 'loose' is false, this function
|
||||
* treats unknown properties and values as an error, as a switch would want to
|
||||
* do when interpreting a configuration request made by a controller.
|
||||
*
|
||||
* A single OpenFlow message can specify features for multiple tables. Calling
|
||||
* this function multiple times for a single 'msg' iterates through the tables
|
||||
* in the message. The caller must initially leave 'msg''s layer pointers null
|
||||
* and not modify them between calls.
|
||||
*
|
||||
* Returns 0 if successful, EOF if no tables were left in this 'msg', otherwise
|
||||
* a positive "enum ofperr" value. */
|
||||
int
|
||||
ofputil_decode_table_features(struct ofpbuf *msg,
|
||||
struct ofputil_table_features *tf, bool loose)
|
||||
{
|
||||
struct ofp13_table_features *otf;
|
||||
unsigned int len;
|
||||
|
||||
if (!msg->l2) {
|
||||
msg->l2 = msg->data;
|
||||
ofpraw_pull_assert(msg);
|
||||
}
|
||||
|
||||
if (!msg->size) {
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (msg->size < sizeof *otf) {
|
||||
return OFPERR_OFPTFFC_BAD_LEN;
|
||||
}
|
||||
|
||||
otf = msg->data;
|
||||
len = ntohs(otf->length);
|
||||
if (len < sizeof *otf || len % 8 || len > msg->size) {
|
||||
return OFPERR_OFPTFFC_BAD_LEN;
|
||||
}
|
||||
ofpbuf_pull(msg, sizeof *otf);
|
||||
|
||||
tf->table_id = otf->table_id;
|
||||
if (tf->table_id == OFPTT_ALL) {
|
||||
return OFPERR_OFPTFFC_BAD_TABLE;
|
||||
}
|
||||
|
||||
ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
|
||||
tf->metadata_match = otf->metadata_match;
|
||||
tf->metadata_write = otf->metadata_write;
|
||||
tf->config = ntohl(otf->config);
|
||||
tf->max_entries = ntohl(otf->max_entries);
|
||||
|
||||
while (msg->size > 0) {
|
||||
struct ofpbuf payload;
|
||||
enum ofperr error;
|
||||
uint16_t type;
|
||||
|
||||
error = ofputil_pull_property(msg, &payload, &type);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
switch ((enum ofp13_table_feature_prop_type) type) {
|
||||
case OFPTFPT13_INSTRUCTIONS:
|
||||
error = parse_instruction_ids(&payload, loose,
|
||||
&tf->nonmiss.instructions);
|
||||
break;
|
||||
case OFPTFPT13_INSTRUCTIONS_MISS:
|
||||
error = parse_instruction_ids(&payload, loose,
|
||||
&tf->miss.instructions);
|
||||
break;
|
||||
|
||||
case OFPTFPT13_NEXT_TABLES:
|
||||
error = parse_table_features_next_table(&payload,
|
||||
tf->nonmiss.next);
|
||||
break;
|
||||
case OFPTFPT13_NEXT_TABLES_MISS:
|
||||
error = parse_table_features_next_table(&payload, tf->miss.next);
|
||||
break;
|
||||
|
||||
case OFPTFPT13_WRITE_ACTIONS:
|
||||
error = parse_table_ids(&payload, &tf->nonmiss.write.actions);
|
||||
break;
|
||||
case OFPTFPT13_WRITE_ACTIONS_MISS:
|
||||
error = parse_table_ids(&payload, &tf->miss.write.actions);
|
||||
break;
|
||||
|
||||
case OFPTFPT13_APPLY_ACTIONS:
|
||||
error = parse_table_ids(&payload, &tf->nonmiss.apply.actions);
|
||||
break;
|
||||
case OFPTFPT13_APPLY_ACTIONS_MISS:
|
||||
error = parse_table_ids(&payload, &tf->miss.apply.actions);
|
||||
break;
|
||||
|
||||
case OFPTFPT13_MATCH:
|
||||
error = parse_oxms(&payload, loose, &tf->match, &tf->mask);
|
||||
break;
|
||||
case OFPTFPT13_WILDCARDS:
|
||||
error = parse_oxms(&payload, loose, &tf->wildcard, NULL);
|
||||
break;
|
||||
|
||||
case OFPTFPT13_WRITE_SETFIELD:
|
||||
error = parse_oxms(&payload, loose,
|
||||
&tf->nonmiss.write.set_fields, NULL);
|
||||
break;
|
||||
case OFPTFPT13_WRITE_SETFIELD_MISS:
|
||||
error = parse_oxms(&payload, loose,
|
||||
&tf->miss.write.set_fields, NULL);
|
||||
break;
|
||||
case OFPTFPT13_APPLY_SETFIELD:
|
||||
error = parse_oxms(&payload, loose,
|
||||
&tf->nonmiss.apply.set_fields, NULL);
|
||||
break;
|
||||
case OFPTFPT13_APPLY_SETFIELD_MISS:
|
||||
error = parse_oxms(&payload, loose,
|
||||
&tf->miss.apply.set_fields, NULL);
|
||||
break;
|
||||
|
||||
case OFPTFPT13_EXPERIMENTER:
|
||||
case OFPTFPT13_EXPERIMENTER_MISS:
|
||||
log_property(loose,
|
||||
"unknown table features experimenter property");
|
||||
error = loose ? 0 : OFPERR_OFPTFFC_BAD_TYPE;
|
||||
break;
|
||||
}
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix inconsistencies:
|
||||
*
|
||||
* - Turn off 'mask' and 'wildcard' bits that are not in 'match',
|
||||
* because a field must be matchable to be masked or wildcarded.
|
||||
*
|
||||
* - Turn on 'wildcard' bits that are set in 'mask', because a field
|
||||
* that is arbitrarily maskable can be wildcarded entirely. */
|
||||
tf->mask &= tf->match;
|
||||
tf->wildcard &= tf->match;
|
||||
|
||||
tf->wildcard |= tf->mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Encodes and returns a request to obtain the table features of a switch.
|
||||
* The message is encoded for OpenFlow version 'ofp_version'. */
|
||||
struct ofpbuf *
|
||||
ofputil_encode_table_features_request(enum ofp_version ofp_version)
|
||||
{
|
||||
struct ofpbuf *request = NULL;
|
||||
|
||||
switch (ofp_version) {
|
||||
case OFP10_VERSION:
|
||||
case OFP11_VERSION:
|
||||
case OFP12_VERSION:
|
||||
ovs_fatal(0, "dump-table-features needs OpenFlow 1.3 or later "
|
||||
"(\'-O OpenFlow13\')");
|
||||
case OFP13_VERSION:
|
||||
case OFP14_VERSION:
|
||||
request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
|
||||
ofp_version, 0);
|
||||
break;
|
||||
default:
|
||||
OVS_NOT_REACHED();
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/* ofputil_table_mod */
|
||||
|
||||
/* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in
|
||||
@@ -5266,6 +5610,21 @@ ofputil_action_name_from_code(enum ofputil_action_code code)
|
||||
: "Unknown action";
|
||||
}
|
||||
|
||||
enum ofputil_action_code
|
||||
ofputil_action_code_from_ofp13_action(enum ofp13_action_type type)
|
||||
{
|
||||
switch (type) {
|
||||
|
||||
#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
|
||||
case ENUM: \
|
||||
return OFPUTIL_##ENUM;
|
||||
#include "ofp-util.def"
|
||||
|
||||
default:
|
||||
return OFPUTIL_ACTION_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/* Appends an action of the type specified by 'code' to 'buf' and returns the
|
||||
* action. Initializes the parts of 'action' that identify it as having type
|
||||
* <ENUM> and length 'sizeof *action' and zeros the rest. For actions that
|
||||
|
Reference in New Issue
Block a user