mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 01:51:26 +00:00
ovs-vswitchd: Implement OFPT_TABLE_FEATURES table modification request.
This allows a controller to change the name of OpenFlow flow tables in the OVS software switch. CC: Brad Cowie <brad@cowie.nz> Acked-by: Justin Pettit <jpettit@ovn.org> Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
parent
d1a227ecef
commit
4e413ac88d
4
NEWS
4
NEWS
@ -2,6 +2,10 @@ Post-v2.10.0
|
||||
---------------------
|
||||
- Linux datapath:
|
||||
* Support for the kernel versions 4.16.x and 4.17.x.
|
||||
- OpenFlow:
|
||||
* OFPMP_TABLE_FEATURES_REQUEST can now modify table features.
|
||||
- ovs-ofctl:
|
||||
* "mod-table" command can now change OpenFlow table names.
|
||||
- The environment variable OVS_SYSLOG_METHOD, if set, is now used
|
||||
as the default syslog method.
|
||||
- The environment variable OVS_CTL_TIMEOUT, if set, is now used
|
||||
|
@ -1913,6 +1913,9 @@ struct mf_bitmap {
|
||||
};
|
||||
#define MF_BITMAP_INITIALIZER { { [0] = 0 } }
|
||||
|
||||
bool mf_bitmap_is_superset(const struct mf_bitmap *super,
|
||||
const struct mf_bitmap *sub);
|
||||
|
||||
/* Use this macro as CASE_MFF_REGS: in a switch statement to choose all of the
|
||||
* MFF_REGn cases. */
|
||||
#if FLOW_N_REGS ==16
|
||||
|
@ -674,6 +674,9 @@ enum ofperr {
|
||||
/* OF1.5+(13,10). Can't handle this many flow tables. */
|
||||
OFPERR_OFPTFFC_TOO_MANY,
|
||||
|
||||
/* NX1.3+(44). Table specified multiple times. */
|
||||
OFPERR_NXTFFC_DUP_TABLE,
|
||||
|
||||
|
||||
/* ## ------------------ ## */
|
||||
/* ## OFPET_BAD_PROPERTY ## */
|
||||
|
@ -426,7 +426,7 @@ enum ofpraw {
|
||||
/* OFPST 1.3+ (11): struct ofp13_meter_features. */
|
||||
OFPRAW_OFPST13_METER_FEATURES_REPLY,
|
||||
|
||||
/* OFPST 1.3+ (12): void. */
|
||||
/* OFPST 1.3+ (12): uint8_t[8][]. */
|
||||
OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
|
||||
|
||||
/* OFPST 1.3+ (12): struct ofp13_table_features, uint8_t[8][]. */
|
||||
|
@ -155,7 +155,7 @@ struct ofpbuf *ofputil_encode_table_mod(const struct ofputil_table_mod *,
|
||||
enum ofputil_protocol);
|
||||
void ofputil_table_mod_format(struct ds *, const struct ofputil_table_mod *,
|
||||
const struct ofputil_table_map *);
|
||||
char *parse_ofp_table_mod(struct ofputil_table_mod *,
|
||||
char *parse_ofp_table_mod(struct ofputil_table_mod *, const char **namep,
|
||||
const char *table_id, const char *flow_miss_handling,
|
||||
const struct ofputil_table_map *,
|
||||
uint32_t *usable_versions)
|
||||
@ -271,15 +271,18 @@ struct ofputil_table_features {
|
||||
struct mf_bitmap wildcard; /* Subset of 'match' that may be wildcarded. */
|
||||
};
|
||||
|
||||
int ofputil_decode_table_features(struct ofpbuf *,
|
||||
struct ofputil_table_features *, bool loose);
|
||||
int ofputil_decode_table_features(
|
||||
struct ofpbuf *, struct ofputil_table_features *,
|
||||
struct ofpbuf *raw_properties);
|
||||
|
||||
struct ofpbuf *ofputil_encode_table_features_request(enum ofp_version);
|
||||
|
||||
struct ofpbuf *ofputil_encode_table_desc_request(enum ofp_version);
|
||||
|
||||
void ofputil_append_table_features_reply(
|
||||
const struct ofputil_table_features *tf, struct ovs_list *replies);
|
||||
void ofputil_append_table_features(
|
||||
const struct ofputil_table_features *tf,
|
||||
const struct ofpbuf *raw_properties,
|
||||
struct ovs_list *msgs);
|
||||
|
||||
void ofputil_table_features_format(
|
||||
struct ds *, const struct ofputil_table_features *features,
|
||||
@ -290,6 +293,10 @@ void ofputil_table_features_format(
|
||||
void ofputil_table_features_format_finish(struct ds *,
|
||||
int first_ditto, int last_ditto);
|
||||
|
||||
bool ofputil_table_features_are_superset(
|
||||
const struct ofputil_table_features *super,
|
||||
const struct ofputil_table_features *sub);
|
||||
|
||||
/* Abstract table stats.
|
||||
*
|
||||
* This corresponds to the OpenFlow 1.3 table statistics structure, which only
|
||||
|
@ -58,6 +58,8 @@ int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **);
|
||||
int vconn_transact_noreply(struct vconn *, struct ofpbuf *, struct ofpbuf **);
|
||||
int vconn_transact_multiple_noreply(struct vconn *, struct ovs_list *requests,
|
||||
struct ofpbuf **replyp);
|
||||
int vconn_transact_multipart(struct vconn *, struct ovs_list *request,
|
||||
struct ovs_list *replies);
|
||||
|
||||
int vconn_dump_flows(struct vconn *, const struct ofputil_flow_stats_request *,
|
||||
enum ofputil_protocol,
|
||||
|
15
lib/bitmap.h
15
lib/bitmap.h
@ -251,6 +251,21 @@ found:
|
||||
return end;
|
||||
}
|
||||
|
||||
/* Returns true if the 1-bits in 'super' are a superset of the 1-bits in 'sub',
|
||||
* false otherwise. 'super' and 'sub' both have 'n_bits' bits. */
|
||||
static inline bool
|
||||
bitmap_is_superset(const unsigned long int *super,
|
||||
const unsigned long int *sub, size_t n_bits)
|
||||
{
|
||||
size_t n_longs = bitmap_n_longs(n_bits);
|
||||
for (size_t i = 0; i < n_longs; i++) {
|
||||
if (!uint_is_superset(super[i], sub[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns true if all of the 'n' bits in 'bitmap' are 0,
|
||||
* false if at least one bit is a 1.*/
|
||||
static inline bool
|
||||
|
@ -3528,3 +3528,12 @@ mf_vl_mff_mf_from_nxm_header(uint32_t header,
|
||||
mf_vl_mff_set_tlv_bitmap(*field, tlv_bitmap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns true if the 1-bits in 'super' are a superset of the 1-bits in 'sub',
|
||||
* false otherwise. */
|
||||
bool
|
||||
mf_bitmap_is_superset(const struct mf_bitmap *super,
|
||||
const struct mf_bitmap *sub)
|
||||
{
|
||||
return bitmap_is_superset(super->bm, sub->bm, MFF_N_IDS);
|
||||
}
|
||||
|
@ -234,9 +234,8 @@ ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh)
|
||||
int first_ditto = -1, last_ditto = -1;
|
||||
for (int i = 0; ; i++) {
|
||||
struct ofputil_table_features tf;
|
||||
int retval;
|
||||
|
||||
retval = ofputil_decode_table_features(&b, &tf, true);
|
||||
struct ofpbuf raw_properties;
|
||||
int retval = ofputil_decode_table_features(&b, &tf, &raw_properties);
|
||||
if (retval) {
|
||||
ofputil_table_features_format_finish(s, first_ditto, last_ditto);
|
||||
return retval != EOF ? retval : 0;
|
||||
|
118
lib/ofp-table.c
118
lib/ofp-table.c
@ -350,11 +350,16 @@ parse_oxms(struct ofpbuf *payload, bool loose,
|
||||
/* 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.
|
||||
* If 'raw_properties' is nonnull, 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. In this mode, on success,
|
||||
* it initializes 'raw_properties' to contain the properties that were parsed;
|
||||
* this allows the caller to later re-serialize the same properties without
|
||||
* change.
|
||||
*
|
||||
* If 'raw_properties' is null, 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
|
||||
@ -365,8 +370,11 @@ parse_oxms(struct ofpbuf *payload, bool loose,
|
||||
* a positive "enum ofperr" value. */
|
||||
int
|
||||
ofputil_decode_table_features(struct ofpbuf *msg,
|
||||
struct ofputil_table_features *tf, bool loose)
|
||||
struct ofputil_table_features *tf,
|
||||
struct ofpbuf *raw_properties)
|
||||
{
|
||||
bool loose = raw_properties != NULL;
|
||||
|
||||
memset(tf, 0, sizeof *tf);
|
||||
|
||||
if (!msg->header) {
|
||||
@ -420,6 +428,9 @@ ofputil_decode_table_features(struct ofpbuf *msg,
|
||||
len);
|
||||
ofpbuf_pull(&properties, sizeof *otf);
|
||||
tf->any_properties = properties.size > 0;
|
||||
if (raw_properties) {
|
||||
*raw_properties = properties;
|
||||
}
|
||||
uint32_t seen = 0;
|
||||
while (properties.size > 0) {
|
||||
struct ofpbuf payload;
|
||||
@ -642,16 +653,21 @@ put_table_instruction_features(
|
||||
OFPTFPT13_APPLY_SETFIELD, miss_offset, version);
|
||||
}
|
||||
|
||||
/* Appends a table features record to 'msgs', which must be a
|
||||
* OFPT_TABLE_FEATURES request or reply. If 'raw_properties' is nonnull, then
|
||||
* it uses its contents verbatim as the table features properties, ignoring the
|
||||
* corresponding members of 'tf'. */
|
||||
void
|
||||
ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
|
||||
struct ovs_list *replies)
|
||||
ofputil_append_table_features(const struct ofputil_table_features *tf,
|
||||
const struct ofpbuf *raw_properties,
|
||||
struct ovs_list *msgs)
|
||||
{
|
||||
struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies));
|
||||
enum ofp_version version = ofpmp_version(replies);
|
||||
size_t start_ofs = reply->size;
|
||||
struct ofpbuf *msg = ofpbuf_from_list(ovs_list_back(msgs));
|
||||
enum ofp_version version = ofpmp_version(msgs);
|
||||
size_t start_ofs = msg->size;
|
||||
struct ofp13_table_features *otf;
|
||||
|
||||
otf = ofpbuf_put_zeros(reply, sizeof *otf);
|
||||
otf = ofpbuf_put_zeros(msg, sizeof *otf);
|
||||
otf->table_id = tf->table_id;
|
||||
otf->command = version >= OFP15_VERSION ? tf->command : 0;
|
||||
ovs_strlcpy_arrays(otf->name, tf->name);
|
||||
@ -667,17 +683,21 @@ ofputil_append_table_features_reply(const struct ofputil_table_features *tf,
|
||||
}
|
||||
otf->max_entries = htonl(tf->max_entries);
|
||||
|
||||
put_table_instruction_features(reply, &tf->nonmiss, 0, version);
|
||||
put_table_instruction_features(reply, &tf->miss, 1, version);
|
||||
if (raw_properties) {
|
||||
ofpbuf_put(msg, raw_properties->data, raw_properties->size);
|
||||
} else if (tf->any_properties) {
|
||||
put_table_instruction_features(msg, &tf->nonmiss, 0, version);
|
||||
put_table_instruction_features(msg, &tf->miss, 1, version);
|
||||
|
||||
put_fields_property(reply, &tf->match, &tf->mask,
|
||||
OFPTFPT13_MATCH, version);
|
||||
put_fields_property(reply, &tf->wildcard, NULL,
|
||||
OFPTFPT13_WILDCARDS, version);
|
||||
put_fields_property(msg, &tf->match, &tf->mask,
|
||||
OFPTFPT13_MATCH, version);
|
||||
put_fields_property(msg, &tf->wildcard, NULL,
|
||||
OFPTFPT13_WILDCARDS, version);
|
||||
}
|
||||
|
||||
otf = ofpbuf_at_assert(reply, start_ofs, sizeof *otf);
|
||||
otf->length = htons(reply->size - start_ofs);
|
||||
ofpmp_postappend(replies, start_ofs);
|
||||
otf = ofpbuf_at_assert(msg, start_ofs, sizeof *otf);
|
||||
otf->length = htons(msg->size - start_ofs);
|
||||
ofpmp_postappend(msgs, start_ofs);
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
@ -1236,7 +1256,8 @@ exit:
|
||||
|
||||
/* Convert 'table_id' and 'setting' (as described for the "mod-table" command
|
||||
* in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a
|
||||
* switch.
|
||||
* switch. If 'setting' sets the name of the table, puts the new name in
|
||||
* '*namep' (a suffix of 'setting'), otherwise stores NULL.
|
||||
*
|
||||
* Stores a bitmap of the OpenFlow versions that are usable for 'tm' into
|
||||
* '*usable_versions'.
|
||||
@ -1244,12 +1265,13 @@ exit:
|
||||
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
||||
* error. The caller is responsible for freeing the returned string. */
|
||||
char * OVS_WARN_UNUSED_RESULT
|
||||
parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
|
||||
const char *setting,
|
||||
parse_ofp_table_mod(struct ofputil_table_mod *tm, const char **namep,
|
||||
const char *table_id, const char *setting,
|
||||
const struct ofputil_table_map *table_map,
|
||||
uint32_t *usable_versions)
|
||||
{
|
||||
*usable_versions = 0;
|
||||
*namep = NULL;
|
||||
if (!strcasecmp(table_id, "all")) {
|
||||
tm->table_id = OFPTT_ALL;
|
||||
} else if (!ofputil_table_from_string(table_id, table_map,
|
||||
@ -1293,6 +1315,10 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
|
||||
} else if (!strcmp(setting, "novacancy")) {
|
||||
tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF;
|
||||
*usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
|
||||
} else if (tm->table_id != OFPTT_ALL && !strncmp(setting, "name:", 5)) {
|
||||
*namep = setting + 5;
|
||||
*usable_versions = ((1 << OFP13_VERSION) | (1u << OFP14_VERSION)
|
||||
| (1u << OFP15_VERSION));
|
||||
} else {
|
||||
return xasprintf("invalid table_mod setting %s", setting);
|
||||
}
|
||||
@ -1644,6 +1670,50 @@ ofputil_table_features_format_finish(struct ds *s,
|
||||
ds_put_format(s, " tables %d...%d: ditto\n", first_ditto, last_ditto);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if 'super' is a superset of 'sub', false otherwise. */
|
||||
static bool
|
||||
ofputil_table_action_features_is_superset(
|
||||
const struct ofputil_table_action_features *super,
|
||||
const struct ofputil_table_action_features *sub)
|
||||
{
|
||||
return (uint_is_superset(super->ofpacts, sub->ofpacts)
|
||||
&& mf_bitmap_is_superset(&super->set_fields, &sub->set_fields));
|
||||
}
|
||||
|
||||
/* Returns true if 'super' is a superset of 'sub', false otherwise. */
|
||||
static bool
|
||||
ofputil_table_instruction_features_is_superset(
|
||||
const struct ofputil_table_instruction_features *super,
|
||||
const struct ofputil_table_instruction_features *sub)
|
||||
{
|
||||
return (bitmap_is_superset(super->next, sub->next, 255)
|
||||
&& uint_is_superset(super->instructions, sub->instructions)
|
||||
&& ofputil_table_action_features_is_superset(&super->write,
|
||||
&sub->write)
|
||||
&& ofputil_table_action_features_is_superset(&super->apply,
|
||||
&sub->apply));
|
||||
}
|
||||
|
||||
/* Returns true if 'super' is a superset of 'sub', false otherwise. */
|
||||
bool
|
||||
ofputil_table_features_are_superset(
|
||||
const struct ofputil_table_features *super,
|
||||
const struct ofputil_table_features *sub)
|
||||
{
|
||||
return (be64_is_superset(super->metadata_match, sub->metadata_match)
|
||||
&& be64_is_superset(super->metadata_write, sub->metadata_write)
|
||||
&& super->max_entries >= sub->max_entries
|
||||
&& super->supports_eviction >= sub->supports_eviction
|
||||
&& super->supports_vacancy_events >= sub->supports_vacancy_events
|
||||
&& ofputil_table_instruction_features_is_superset(&super->nonmiss,
|
||||
&sub->nonmiss)
|
||||
&& ofputil_table_instruction_features_is_superset(&super->miss,
|
||||
&sub->miss)
|
||||
&& mf_bitmap_is_superset(&super->match, &sub->match)
|
||||
&& mf_bitmap_is_superset(&super->mask, &sub->mask)
|
||||
&& mf_bitmap_is_superset(&super->wildcard, &sub->wildcard));
|
||||
}
|
||||
|
||||
/* Table stats. */
|
||||
|
||||
|
46
lib/util.h
46
lib/util.h
@ -436,6 +436,38 @@ static inline ovs_be32 be32_prefix_mask(int plen)
|
||||
return htonl((uint64_t)UINT32_MAX << (32 - plen));
|
||||
}
|
||||
|
||||
/* Returns true if the 1-bits in 'super' are a superset of the 1-bits in 'sub',
|
||||
* false otherwise. */
|
||||
static inline bool
|
||||
uint_is_superset(uintmax_t super, uintmax_t sub)
|
||||
{
|
||||
return (super & sub) == sub;
|
||||
}
|
||||
|
||||
/* Returns true if the 1-bits in 'super' are a superset of the 1-bits in 'sub',
|
||||
* false otherwise. */
|
||||
static inline bool
|
||||
be16_is_superset(ovs_be16 super, ovs_be16 sub)
|
||||
{
|
||||
return (super & sub) == sub;
|
||||
}
|
||||
|
||||
/* Returns true if the 1-bits in 'super' are a superset of the 1-bits in 'sub',
|
||||
* false otherwise. */
|
||||
static inline bool
|
||||
be32_is_superset(ovs_be32 super, ovs_be32 sub)
|
||||
{
|
||||
return (super & sub) == sub;
|
||||
}
|
||||
|
||||
/* Returns true if the 1-bits in 'super' are a superset of the 1-bits in 'sub',
|
||||
* false otherwise. */
|
||||
static inline bool
|
||||
be64_is_superset(ovs_be64 super, ovs_be64 sub)
|
||||
{
|
||||
return (super & sub) == sub;
|
||||
}
|
||||
|
||||
bool is_all_zeros(const void *, size_t);
|
||||
bool is_all_ones(const void *, size_t);
|
||||
bool is_all_byte(const void *, size_t, uint8_t byte);
|
||||
@ -509,6 +541,20 @@ ovs_u128_and(const ovs_u128 a, const ovs_u128 b)
|
||||
return dst;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ovs_be128_is_superset(ovs_be128 super, ovs_be128 sub)
|
||||
{
|
||||
return (be64_is_superset(super.be64.hi, sub.be64.hi) &&
|
||||
be64_is_superset(super.be64.lo, sub.be64.lo));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ovs_u128_is_superset(ovs_u128 super, ovs_u128 sub)
|
||||
{
|
||||
return (uint_is_superset(super.u64.hi, sub.u64.hi) &&
|
||||
uint_is_superset(super.u64.lo, sub.u64.lo));
|
||||
}
|
||||
|
||||
void xsleep(unsigned int seconds);
|
||||
void xnanosleep(uint64_t nanoseconds);
|
||||
|
||||
|
40
lib/vconn.c
40
lib/vconn.c
@ -946,6 +946,46 @@ vconn_transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sends 'requests' (which should be a multipart request) on 'vconn' and waits
|
||||
* for the replies, which are put into 'replies'. Returns 0 if successful,
|
||||
* otherwise an errno value. */
|
||||
int
|
||||
vconn_transact_multipart(struct vconn *vconn,
|
||||
struct ovs_list *requests,
|
||||
struct ovs_list *replies)
|
||||
{
|
||||
struct ofpbuf *rq = ofpbuf_from_list(ovs_list_front(requests));
|
||||
ovs_be32 send_xid = ((struct ofp_header *) rq->data)->xid;
|
||||
|
||||
ovs_list_init(replies);
|
||||
|
||||
/* Send all the requests. */
|
||||
struct ofpbuf *b, *next;
|
||||
LIST_FOR_EACH_SAFE (b, next, list_node, requests) {
|
||||
ovs_list_remove(&b->list_node);
|
||||
int error = vconn_send_block(vconn, b);
|
||||
if (error) {
|
||||
ofpbuf_delete(b);
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive all the replies. */
|
||||
bool more;
|
||||
do {
|
||||
struct ofpbuf *reply;
|
||||
int error = vconn_recv_xid__(vconn, send_xid, &reply, NULL);
|
||||
if (error) {
|
||||
ofpbuf_list_delete(replies);
|
||||
return error;
|
||||
}
|
||||
|
||||
ovs_list_push_back(replies, &reply->list_node);
|
||||
more = ofpmsg_is_stat_reply(reply->data) && ofpmp_more(reply->data);
|
||||
} while (more);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
|
||||
struct ofpbuf **replyp,
|
||||
|
@ -6029,6 +6029,7 @@ const struct ofproto_class ofproto_dpif_class = {
|
||||
type_get_memory_usage,
|
||||
flush,
|
||||
query_tables,
|
||||
NULL, /* modify_tables */
|
||||
set_tables_version,
|
||||
port_alloc,
|
||||
port_construct,
|
||||
|
@ -221,6 +221,7 @@ struct oftable {
|
||||
enum oftable_flags flags;
|
||||
struct classifier cls; /* Contains "struct rule"s. */
|
||||
char *name; /* Table name exposed via OpenFlow, or NULL. */
|
||||
int name_level; /* 0=name unset, 1=via OF, 2=via OVSDB. */
|
||||
|
||||
/* Maximum number of flows or UINT_MAX if there is no limit besides any
|
||||
* limit imposed by resource limitations. */
|
||||
@ -934,6 +935,24 @@ struct ofproto_class {
|
||||
struct ofputil_table_features *features,
|
||||
struct ofputil_table_stats *stats);
|
||||
|
||||
/* Helper for the OFPT_TABLE_FEATURES request.
|
||||
*
|
||||
* A controller is requesting that the table features be updated from 'old'
|
||||
* to 'new', where 'old' is the features currently in use as previously
|
||||
* initialized by 'query_tables'.
|
||||
*
|
||||
* If this function is nonnull, then it should either update the table
|
||||
* features or return an OpenFlow error. The update should be
|
||||
* all-or-nothing.
|
||||
*
|
||||
* If this function is null, then only updates that eliminate table
|
||||
* features will be allowed. Such updates have no actual effect. This
|
||||
* implementation is acceptable because OpenFlow says that a table's
|
||||
* features may be a superset of those requested. */
|
||||
enum ofperr (*modify_tables)(struct ofproto *ofproto,
|
||||
const struct ofputil_table_features *old,
|
||||
const struct ofputil_table_features *new);
|
||||
|
||||
/* Sets the current tables version the provider should use for classifier
|
||||
* lookups. This must be called with a new version number after each set
|
||||
* of flow table changes has been completed, so that datapath revalidation
|
||||
|
@ -85,7 +85,9 @@ const enum mf_field_id default_prefix_fields[2] =
|
||||
static void oftable_init(struct oftable *);
|
||||
static void oftable_destroy(struct oftable *);
|
||||
|
||||
static void oftable_set_name(struct oftable *, const char *name);
|
||||
static void oftable_set_name(struct oftable *, const char *name, int level);
|
||||
static bool oftable_may_set_name(const struct oftable *,
|
||||
const char *name, int level);
|
||||
|
||||
static enum ofperr evict_rules_from_table(struct oftable *)
|
||||
OVS_REQUIRES(ofproto_mutex);
|
||||
@ -1473,7 +1475,7 @@ ofproto_configure_table(struct ofproto *ofproto, int table_id,
|
||||
ovs_assert(table_id >= 0 && table_id < ofproto->n_tables);
|
||||
table = &ofproto->tables[table_id];
|
||||
|
||||
oftable_set_name(table, s->name);
|
||||
oftable_set_name(table, s->name, 2);
|
||||
|
||||
if (table->flags & OFTABLE_READONLY) {
|
||||
return;
|
||||
@ -3249,6 +3251,8 @@ query_tables(struct ofproto *ofproto,
|
||||
atomic_read_relaxed(&ofproto->tables[i].miss_config, &f->miss_config);
|
||||
f->max_entries = 1000000;
|
||||
|
||||
f->any_properties = true;
|
||||
|
||||
bool more_tables = false;
|
||||
for (int j = i + 1; j < ofproto->n_tables; j++) {
|
||||
if (!(ofproto->tables[j].flags & OFTABLE_HIDDEN)) {
|
||||
@ -3784,33 +3788,237 @@ handle_table_stats_request(struct ofconn *ofconn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
handle_table_features_request(struct ofconn *ofconn,
|
||||
const struct ofp_header *request)
|
||||
/* OpenFlow 1.5.1 section 7.3.5.18.1 "Table Features request and reply"
|
||||
* says:
|
||||
*
|
||||
* If a table feature included in the request has an empty list of
|
||||
* properties, the list of properties for that flow table is unchanged and
|
||||
* only the other features of that flow table are updated.
|
||||
*
|
||||
* This function copies the "list of properties" from '*src' to '*dst'. */
|
||||
static void
|
||||
copy_properties(struct ofputil_table_features *dst,
|
||||
const struct ofputil_table_features *src)
|
||||
{
|
||||
dst->any_properties = src->any_properties;
|
||||
if (src->any_properties) {
|
||||
dst->nonmiss = src->nonmiss;
|
||||
dst->miss = src->miss;
|
||||
dst->match = src->match;
|
||||
dst->mask = src->mask;
|
||||
dst->wildcard = src->wildcard;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempts to change the table features of the ofproto backing 'ofconn' to
|
||||
* those specified in the table features request in 'msgs', given that the
|
||||
* features are currently those in 'old'.
|
||||
*
|
||||
* Returns 0 if successful, an OpenFlow error if the caller should send an
|
||||
* error message for the request as a whole, or -1 if the function already sent
|
||||
* an error message for some message in 'msgs'. */
|
||||
static int
|
||||
handle_table_features_change(struct ofconn *ofconn,
|
||||
const struct ovs_list *msgs,
|
||||
const struct ofputil_table_features old[])
|
||||
{
|
||||
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
|
||||
struct ofpbuf msg = ofpbuf_const_initializer(request,
|
||||
ntohs(request->length));
|
||||
ofpraw_pull_assert(&msg);
|
||||
if (msg.size || ofpmp_more(request)) {
|
||||
|
||||
enum ofp15_table_features_command command = OFPTFC15_REPLACE;
|
||||
struct ofputil_table_features new[255];
|
||||
|
||||
unsigned long int seen[BITMAP_N_LONGS(255)];
|
||||
memset(seen, 0, sizeof seen);
|
||||
|
||||
struct ofpbuf *msg;
|
||||
int n = 0;
|
||||
LIST_FOR_EACH (msg, list_node, msgs) {
|
||||
for (;;) {
|
||||
struct ofputil_table_features tf;
|
||||
int retval = ofputil_decode_table_features(msg, &tf, NULL);
|
||||
if (retval == EOF) {
|
||||
break;
|
||||
} else if (retval) {
|
||||
ofconn_send_error(ofconn, msg->header, retval);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get command from first request. */
|
||||
if (!n) {
|
||||
command = tf.command;
|
||||
}
|
||||
n++;
|
||||
|
||||
/* Avoid duplicate tables. */
|
||||
if (bitmap_is_set(seen, tf.table_id)) {
|
||||
VLOG_INFO_RL(&rl, "duplicate table %"PRIu8, tf.table_id);
|
||||
ofconn_send_error(ofconn, msg->header,
|
||||
OFPERR_NXTFFC_DUP_TABLE);
|
||||
return -1;
|
||||
}
|
||||
bitmap_set1(seen, tf.table_id);
|
||||
|
||||
/* Save table. */
|
||||
new[tf.table_id] = tf;
|
||||
}
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ofproto->n_tables; i++) {
|
||||
if (ofproto->tables[i].flags & OFTABLE_HIDDEN) {
|
||||
if (bitmap_is_set(seen, i)) {
|
||||
VLOG_INFO_RL(&rl, "can't modify hidden table %"PRIuSIZE, i);
|
||||
return OFPERR_OFPTFFC_EPERM;
|
||||
}
|
||||
|
||||
new[i] = old[i];
|
||||
bitmap_set1(seen, i);
|
||||
}
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case OFPTFC15_REPLACE:
|
||||
break;
|
||||
|
||||
case OFPTFC15_MODIFY:
|
||||
for (size_t i = 0; i < ofproto->n_tables; i++) {
|
||||
if (!bitmap_is_set(seen, i)) {
|
||||
new[i] = old[i];
|
||||
bitmap_set1(seen, i);
|
||||
} else if (!new[i].any_properties) {
|
||||
copy_properties(&new[i], &old[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OFPTFC15_ENABLE:
|
||||
case OFPTFC15_DISABLE:
|
||||
/* It really isn't clear what these commands are supposed to do in an
|
||||
* Open vSwitch context. OVS doesn't have a concept of tables that
|
||||
* exist but are not in the pipeline, and OVS table ids are always
|
||||
* sequential from 0. */
|
||||
return OFPERR_OFPTFFC_EPERM;
|
||||
}
|
||||
|
||||
/* Make sure that the new number of tables is the same as the old number,
|
||||
* because we don't support changing the number of tables or disabling
|
||||
* tables. */
|
||||
int n_tables = bitmap_scan(seen, 0, 0, 255);
|
||||
bool skipped_tables = bitmap_scan(seen, 1, n_tables, 255) != 255;
|
||||
if (n_tables != ofproto->n_tables || skipped_tables) {
|
||||
if (skipped_tables) {
|
||||
VLOG_INFO_RL(&rl, "can't disable table %d", n_tables);
|
||||
} else {
|
||||
VLOG_INFO_RL(&rl, "can't change number of tables from %d to %d",
|
||||
ofproto->n_tables, n_tables);
|
||||
}
|
||||
return (n_tables > ofproto->n_tables
|
||||
? OFPERR_OFPTFFC_TOO_MANY
|
||||
: OFPERR_OFPTFFC_EPERM);
|
||||
}
|
||||
|
||||
/* OpenFlow 1.5.1 section 7.3.5.18.1 "Table Features request and reply"
|
||||
* says:
|
||||
*
|
||||
* "All fields in ofp_table_features may be requested to be changed by
|
||||
* the controller with the exception of the max_entries field, this is
|
||||
* read only and returned by the switch."
|
||||
*
|
||||
* so forbid the controller from attempting to change it.
|
||||
*
|
||||
* (This seems like a particularly arbitrary prohibition since OVS could
|
||||
* easily implement such a feature. Whatever.) */
|
||||
for (size_t i = 0; i < n_tables; i++) {
|
||||
if (old[i].max_entries != new[i].max_entries) {
|
||||
VLOG_INFO_RL(&rl, "can't change max_entries");
|
||||
return OFPERR_OFPTFFC_EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that we can set table names. */
|
||||
for (size_t i = 0; i < n_tables; i++) {
|
||||
if (!oftable_may_set_name(&ofproto->tables[i], new[i].name, 1)) {
|
||||
const char *name = ofproto->tables[i].name;
|
||||
VLOG_INFO_RL(&rl, "can't change name of table %"PRIuSIZE" "
|
||||
"to %s because it is already set to %s via OVSDB",
|
||||
i, new[i].name, name ? name : "\"\"");
|
||||
return OFPERR_OFPTFFC_EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ask the provider to update its features.
|
||||
*
|
||||
* If the provider can't update features, just make sure that the
|
||||
* controller isn't asking to enable new features. OpenFlow says it's OK
|
||||
* if a superset of the requested features are actually enabled. */
|
||||
if (ofproto->ofproto_class->modify_tables) {
|
||||
enum ofperr error = ofproto->ofproto_class->modify_tables(ofproto,
|
||||
old, new);
|
||||
if (error) {
|
||||
VLOG_INFO_RL(&rl, "can't change table features");
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < n_tables; i++) {
|
||||
if (!ofputil_table_features_are_superset(&old[i], &new[i])) {
|
||||
VLOG_INFO_RL(&rl, "can't increase table features "
|
||||
"for table %"PRIuSIZE, i);
|
||||
return OFPERR_OFPTFFC_EPERM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update table names. */
|
||||
for (size_t i = 0; i < n_tables; i++) {
|
||||
oftable_set_name(&ofproto->tables[i], new[i].name, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_table_features_request(struct ofconn *ofconn,
|
||||
const struct ovs_list *msgs)
|
||||
{
|
||||
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
|
||||
|
||||
struct ofpbuf *msg = ofpbuf_from_list(ovs_list_back(msgs));
|
||||
const struct ofp_header *request = msg->data;
|
||||
ofpraw_pull_assert(msg);
|
||||
|
||||
/* Update the table features configuration, if requested. */
|
||||
struct ofputil_table_features *features;
|
||||
query_tables(ofproto, &features, NULL);
|
||||
if (!ovs_list_is_singleton(msgs) || msg->size) {
|
||||
int retval = handle_table_features_change(ofconn, msgs, features);
|
||||
free(features);
|
||||
if (retval) {
|
||||
if (retval < 0) {
|
||||
/* handle_table_features_change() already sent an error. */
|
||||
} else {
|
||||
ofconn_send_error(ofconn, request, retval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Features may have changed, re-query. */
|
||||
query_tables(ofproto, &features, NULL);
|
||||
}
|
||||
|
||||
/* Reply the controller with the table configuration. */
|
||||
struct ovs_list replies;
|
||||
ofpmp_init(&replies, request);
|
||||
for (size_t i = 0; i < ofproto->n_tables; i++) {
|
||||
if (!(ofproto->tables[i].flags & OFTABLE_HIDDEN)) {
|
||||
ofputil_append_table_features_reply(&features[i], &replies);
|
||||
ofputil_append_table_features(&features[i], NULL, &replies);
|
||||
}
|
||||
}
|
||||
ofconn_send_replies(ofconn, &replies);
|
||||
|
||||
free(features);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the vacancy of 'oftable', a number that ranges from 0 (if the table
|
||||
@ -8195,7 +8403,7 @@ handle_single_part_openflow(struct ofconn *ofconn, const struct ofp_header *oh,
|
||||
return handle_table_stats_request(ofconn, oh);
|
||||
|
||||
case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
|
||||
return handle_table_features_request(ofconn, oh);
|
||||
OVS_NOT_REACHED();
|
||||
|
||||
case OFPTYPE_TABLE_DESC_REQUEST:
|
||||
return handle_table_desc_request(ofconn, oh);
|
||||
@ -8306,7 +8514,9 @@ handle_openflow(struct ofconn *ofconn, const struct ovs_list *msgs)
|
||||
enum ofptype type;
|
||||
enum ofperr error = ofptype_decode(&type, msg->data);
|
||||
if (!error) {
|
||||
if (!ovs_list_is_short(msgs)) {
|
||||
if (type == OFPTYPE_TABLE_FEATURES_STATS_REQUEST) {
|
||||
handle_table_features_request(ofconn, msgs);
|
||||
} else if (!ovs_list_is_short(msgs)) {
|
||||
error = OFPERR_OFPBRC_BAD_STAT;
|
||||
} else {
|
||||
error = handle_single_part_openflow(ofconn, msg->data, type);
|
||||
@ -8631,26 +8841,50 @@ oftable_destroy(struct oftable *table)
|
||||
free(table->name);
|
||||
}
|
||||
|
||||
/* Changes the name of 'table' to 'name'. If 'name' is NULL or the empty
|
||||
* string, then 'table' will use its default name.
|
||||
/* Changes the name of 'table' to 'name'. Null or empty string 'name' unsets
|
||||
* the name.
|
||||
*
|
||||
* 'level' should be 1 if the name is being set via OpenFlow, or 2 if the name
|
||||
* is being set via OVSDB. Higher levels get precedence.
|
||||
*
|
||||
* This only affects the name exposed for a table exposed through the OpenFlow
|
||||
* OFPST_TABLE (as printed by "ovs-ofctl dump-tables"). */
|
||||
static void
|
||||
oftable_set_name(struct oftable *table, const char *name)
|
||||
oftable_set_name(struct oftable *table, const char *name, int level)
|
||||
{
|
||||
if (name && name[0]) {
|
||||
int len = strnlen(name, OFP_MAX_TABLE_NAME_LEN);
|
||||
if (!table->name || strncmp(name, table->name, len)) {
|
||||
int len = name ? strnlen(name, OFP_MAX_TABLE_NAME_LEN) : 0;
|
||||
if (level >= table->name_level) {
|
||||
if (name) {
|
||||
if (name[0]) {
|
||||
if (!table->name || strncmp(name, table->name, len)) {
|
||||
free(table->name);
|
||||
table->name = xmemdup0(name, len);
|
||||
}
|
||||
} else {
|
||||
free(table->name);
|
||||
table->name = NULL;
|
||||
}
|
||||
table->name_level = level;
|
||||
} else if (table->name_level == level) {
|
||||
free(table->name);
|
||||
table->name = xmemdup0(name, len);
|
||||
table->name = NULL;
|
||||
table->name_level = 0;
|
||||
}
|
||||
} else {
|
||||
free(table->name);
|
||||
table->name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if oftable_set_name(table, name, level) would be effective,
|
||||
* false otherwise. */
|
||||
static bool
|
||||
oftable_may_set_name(const struct oftable *table, const char *name, int level)
|
||||
{
|
||||
return (level >= table->name_level
|
||||
|| !name
|
||||
|| !table->name
|
||||
|| !strncmp(name, table->name,
|
||||
strnlen(name, OFP_MAX_TABLE_NAME_LEN)));
|
||||
}
|
||||
|
||||
/* oftables support a choice of two policies when adding a rule would cause the
|
||||
* number of flows in the table to exceed the configured maximum number: either
|
||||
* they can refuse to add the new flow or they can evict some existing flow.
|
||||
|
@ -2686,6 +2686,19 @@ AT_CLEANUP
|
||||
AT_SETUP([ofproto - flow table names])
|
||||
OVS_VSWITCHD_START
|
||||
add_of_ports br0 1 2
|
||||
|
||||
# Set a table name via OpenFlow 1.3 and one via OpenFlow 1.5.
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 0 name:xyzzy])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 mod-table br0 1 name:quux])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'],
|
||||
[0], [dnl
|
||||
table 0 ("xyzzy"):
|
||||
table 1 ("quux"): ditto
|
||||
tables 2...252: ditto
|
||||
table 253:
|
||||
])
|
||||
|
||||
# Set some table names via OVSDB.
|
||||
AT_CHECK(
|
||||
[ovs-vsctl \
|
||||
-- --id=@t0 create Flow_Table name=zero \
|
||||
@ -2697,6 +2710,16 @@ AT_CHECK(
|
||||
<1>
|
||||
<2>
|
||||
])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'],
|
||||
[0], [dnl
|
||||
table 0 ("zero"):
|
||||
table 1 ("one"): ditto
|
||||
table 2 ("two"): ditto
|
||||
tables 3...252: ditto
|
||||
table 253:
|
||||
])
|
||||
|
||||
# Check that flow table parsing and dumping uses the names.
|
||||
AT_DATA([flows.txt], [dnl
|
||||
table=zero in_port=p2 actions=p1,resubmit(,one)
|
||||
table=one,in_port=p1,ip,actions=ct(table=two)
|
||||
@ -2713,6 +2736,73 @@ AT_CHECK([ovs-ofctl --no-names --no-stats dump-flows br0], [0], [dnl
|
||||
table=1, ip,in_port=1 actions=ct(table=2)
|
||||
table=1, arp,in_port=1 actions=resubmit(,2)
|
||||
])
|
||||
|
||||
# Setting the same table names via OpenFlow 1.3 or OpenFlow 1.5 is a no-op.
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 0 name:zero])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 mod-table br0 1 name:one])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'],
|
||||
[0], [dnl
|
||||
table 0 ("zero"):
|
||||
table 1 ("one"): ditto
|
||||
table 2 ("two"): ditto
|
||||
tables 3...252: ditto
|
||||
table 253:
|
||||
])
|
||||
|
||||
# Setting different tables names via OpenFlow 1.3 or OpenFlow 1.5 yield errors.
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 0 name:xyzzy], 1, [], [stderr])
|
||||
AT_CHECK([head -1 stderr], [0], [OFPT_ERROR (OF1.3) (xid=0x5): OFPTFFC_EPERM
|
||||
])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 mod-table br0 1 name:quux], 1, [], [stderr])
|
||||
AT_CHECK([head -1 stderr], [0], [OFPT_ERROR (OF1.5) (xid=0x5): OFPTFFC_EPERM
|
||||
])
|
||||
|
||||
# But we can still set table names for those not set via OVSDB.
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 3 name:three])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'],
|
||||
[0], [dnl
|
||||
table 0 ("zero"):
|
||||
table 1 ("one"): ditto
|
||||
table 2 ("two"): ditto
|
||||
table 3 ("three"): ditto
|
||||
tables 4...252: ditto
|
||||
table 253:
|
||||
])
|
||||
|
||||
# Unsetting names via OVSDB then setting them via OpenFlow works too.
|
||||
AT_CHECK([ovs-vsctl remove bridge br0 Flow_Table 2])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'],
|
||||
[0], [dnl
|
||||
table 0 ("zero"):
|
||||
table 1 ("one"): ditto
|
||||
table 2: ditto
|
||||
table 3 ("three"): ditto
|
||||
tables 4...252: ditto
|
||||
table 253:
|
||||
])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 2 name:foobar])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'],
|
||||
[0], [dnl
|
||||
table 0 ("zero"):
|
||||
table 1 ("one"): ditto
|
||||
table 2 ("foobar"): ditto
|
||||
table 3 ("three"): ditto
|
||||
tables 4...252: ditto
|
||||
table 253:
|
||||
])
|
||||
|
||||
# We can clear names via OpenFlow, at least if they were set that way.
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow13 mod-table br0 2 name:])
|
||||
AT_CHECK([ovs-ofctl -O OpenFlow15 dump-table-features br0 |grep '^ table'],
|
||||
[0], [dnl
|
||||
table 0 ("zero"):
|
||||
table 1 ("one"): ditto
|
||||
table 2: ditto
|
||||
table 3 ("three"): ditto
|
||||
tables 4...252: ditto
|
||||
table 253:
|
||||
])
|
||||
|
||||
OVS_VSWITCHD_STOP
|
||||
AT_CLEANUP
|
||||
|
||||
|
@ -84,6 +84,18 @@ Send to controller. (This is how an OpenFlow 1.0 switch always
|
||||
handles packets that do not match any flow in the last table.)
|
||||
.RE
|
||||
.IP
|
||||
In OpenFlow 1.3 and later (which must be enabled with the \fB\-O\fR
|
||||
option) and Open vSwitch 2.11 and later only, \fBmod\-table\fR can
|
||||
change the name of a table:
|
||||
.RS
|
||||
.IP \fBname:\fInew-name\fR
|
||||
Changes the name of the table to \fInew-name\fR. Use an empty
|
||||
\fInew-name\fR to clear the name. (This will be ineffective if the
|
||||
name is set via the \fBname\fR column in the \fBFlow_Table\fR table in
|
||||
the \fBOpen_vSwitch\fR database as described in
|
||||
\fBovs\-vswitchd.conf.db\fR(5).)
|
||||
.RE
|
||||
.IP
|
||||
In OpenFlow 1.4 and later (which must be enabled with the \fB\-O\fR
|
||||
option) only, \fBmod\-table\fR configures the behavior when a
|
||||
controller attempts to add a flow to a flow table that is full. The
|
||||
|
@ -913,9 +913,9 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
|
||||
done = !ofpmp_more(reply->data);
|
||||
for (;;) {
|
||||
struct ofputil_table_features tf;
|
||||
int retval;
|
||||
|
||||
retval = ofputil_decode_table_features(reply, &tf, true);
|
||||
struct ofpbuf raw_properties;
|
||||
int retval = ofputil_decode_table_features(
|
||||
reply, &tf, &raw_properties);
|
||||
if (retval) {
|
||||
if (retval != EOF) {
|
||||
ovs_fatal(0, "decode error: %s",
|
||||
@ -1207,6 +1207,7 @@ struct table_iterator {
|
||||
bool more;
|
||||
|
||||
struct ofputil_table_features features;
|
||||
struct ofpbuf raw_properties;
|
||||
};
|
||||
|
||||
/* Initializes 'ti' to prepare for iterating through all of the tables on the
|
||||
@ -1248,7 +1249,7 @@ table_iterator_next(struct table_iterator *ti)
|
||||
ovs_assert(ti->variant == TI_FEATURES);
|
||||
retval = ofputil_decode_table_features(ti->reply,
|
||||
&ti->features,
|
||||
true);
|
||||
&ti->raw_properties);
|
||||
}
|
||||
if (!retval) {
|
||||
return &ti->features;
|
||||
@ -2580,16 +2581,95 @@ fetch_table_desc(struct vconn *vconn, struct ofputil_table_mod *tm,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
change_table_name(struct vconn *vconn, uint8_t table_id, const char *new_name)
|
||||
{
|
||||
/* Get all tables' features and properties. */
|
||||
struct table {
|
||||
struct ofputil_table_features tf;
|
||||
struct ofpbuf *raw_properties;
|
||||
} *tables[256];
|
||||
memset(tables, 0, sizeof tables);
|
||||
|
||||
struct table_iterator ti;
|
||||
table_iterator_init(&ti, vconn);
|
||||
while (table_iterator_next(&ti)) {
|
||||
struct table *t = tables[ti.features.table_id] = xmalloc(sizeof *t);
|
||||
t->tf = ti.features;
|
||||
t->raw_properties = ofpbuf_clone(&ti.raw_properties);
|
||||
}
|
||||
table_iterator_destroy(&ti);
|
||||
|
||||
/* Change the name for table 'table_id'. */
|
||||
struct table *t = tables[table_id];
|
||||
if (!t) {
|
||||
ovs_fatal(0, "switch does not have table %"PRIu8, table_id);
|
||||
}
|
||||
ovs_strlcpy(t->tf.name, new_name, OFP_MAX_TABLE_NAME_LEN);
|
||||
|
||||
/* Compose the transaction. */
|
||||
enum ofp_version version = vconn_get_version(vconn);
|
||||
struct ovs_list requests = OVS_LIST_INITIALIZER(&requests);
|
||||
struct ofpbuf *tfr = ofputil_encode_table_features_request(version);
|
||||
ovs_list_push_back(&requests, &tfr->list_node);
|
||||
if (version >= OFP15_VERSION) {
|
||||
/* For OpenFlow 1.5, we can use a single OFPTFC15_MODIFY without any
|
||||
* properties. */
|
||||
t->tf.command = OFPTFC15_MODIFY;
|
||||
t->tf.any_properties = false;
|
||||
ofputil_append_table_features(&t->tf, NULL, &requests);
|
||||
} else {
|
||||
/* For OpenFlow 1.3 and 1.4, we have to regurgitate all of the tables
|
||||
* and their properties. */
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
if (tables[i]) {
|
||||
ofputil_append_table_features(&tables[i]->tf,
|
||||
tables[i]->raw_properties,
|
||||
&requests);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Transact.
|
||||
*
|
||||
* The reply repeats the entire new configuration of the tables, so we
|
||||
* don't bother printing it unless there's an error. */
|
||||
struct ovs_list replies;
|
||||
struct ofpbuf *reply;
|
||||
vconn_transact_multipart(vconn, &requests, &replies);
|
||||
LIST_FOR_EACH (reply, list_node, &replies) {
|
||||
enum ofptype type;
|
||||
enum ofperr error = ofptype_decode(&type, reply->data);
|
||||
if (error) {
|
||||
ovs_fatal(0, "decode error: %s", ofperr_get_name(error));
|
||||
} else if (type == OFPTYPE_ERROR) {
|
||||
ofp_print(stderr, reply->data, reply->size, NULL, NULL,
|
||||
verbosity + 1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
ofpbuf_list_delete(&replies);
|
||||
|
||||
/* Clean up. */
|
||||
for (size_t i = 0; i < ARRAY_SIZE(tables); i++) {
|
||||
if (tables[i]) {
|
||||
ofpbuf_delete(tables[i]->raw_properties);
|
||||
free(tables[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ofctl_mod_table(struct ovs_cmdl_context *ctx)
|
||||
{
|
||||
uint32_t usable_versions;
|
||||
struct ofputil_table_mod tm;
|
||||
const char *name;
|
||||
struct vconn *vconn;
|
||||
char *error;
|
||||
int i;
|
||||
|
||||
error = parse_ofp_table_mod(&tm, ctx->argv[2], ctx->argv[3],
|
||||
error = parse_ofp_table_mod(&tm, &name, ctx->argv[2], ctx->argv[3],
|
||||
tables_to_accept(ctx->argv[1]),
|
||||
&usable_versions);
|
||||
if (error) {
|
||||
@ -2607,27 +2687,33 @@ ofctl_mod_table(struct ovs_cmdl_context *ctx)
|
||||
mask_allowed_ofp_versions(usable_versions);
|
||||
enum ofputil_protocol protocol = open_vconn(ctx->argv[1], &vconn);
|
||||
|
||||
/* For OpenFlow 1.4+, ovs-ofctl mod-table should not affect table-config
|
||||
* properties that the user didn't ask to change, so it is necessary to
|
||||
* restore the current configuration of table-config parameters using
|
||||
* OFPMP14_TABLE_DESC request. */
|
||||
if ((allowed_versions & (1u << OFP14_VERSION)) ||
|
||||
(allowed_versions & (1u << OFP15_VERSION))) {
|
||||
struct ofputil_table_desc td;
|
||||
if (name) {
|
||||
change_table_name(vconn, tm.table_id, name);
|
||||
} else {
|
||||
/* For OpenFlow 1.4+, ovs-ofctl mod-table should not affect
|
||||
* table-config properties that the user didn't ask to change, so it is
|
||||
* necessary to restore the current configuration of table-config
|
||||
* parameters using OFPMP14_TABLE_DESC request. */
|
||||
if (allowed_versions & ((1u << OFP14_VERSION) |
|
||||
(1u << OFP15_VERSION) |
|
||||
(1u << OFP16_VERSION))) {
|
||||
struct ofputil_table_desc td;
|
||||
|
||||
if (tm.table_id == OFPTT_ALL) {
|
||||
for (i = 0; i < OFPTT_MAX; i++) {
|
||||
tm.table_id = i;
|
||||
if (tm.table_id == OFPTT_ALL) {
|
||||
for (i = 0; i < OFPTT_MAX; i++) {
|
||||
tm.table_id = i;
|
||||
fetch_table_desc(vconn, &tm, &td);
|
||||
transact_noreply(vconn,
|
||||
ofputil_encode_table_mod(&tm, protocol));
|
||||
}
|
||||
} else {
|
||||
fetch_table_desc(vconn, &tm, &td);
|
||||
transact_noreply(vconn,
|
||||
ofputil_encode_table_mod(&tm, protocol));
|
||||
transact_noreply(vconn, ofputil_encode_table_mod(&tm,
|
||||
protocol));
|
||||
}
|
||||
} else {
|
||||
fetch_table_desc(vconn, &tm, &td);
|
||||
transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
|
||||
}
|
||||
} else {
|
||||
transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
|
||||
}
|
||||
vconn_close(vconn);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user