};
OFP_ASSERT(sizeof(struct nx_flow_monitor_cancel) == 4);
+/* Geneve option table maintainance commands.
+ *
+ * In order to work with Geneve options, we need to maintain a mapping
+ * table between an option (defined by <class, type, length>) and
+ * an NXM field that can be operated on for the purposes of matches,
+ * actions, etc. This mapping must be explicitly specified by the
+ * user.
+ *
+ * There are two primary groups of OpenFlow messages that are introduced
+ * as Nicira extensions: modification commands (add, delete, clear mappings)
+ * and table status request/reply to dump the current table along with switch
+ * information.
+ *
+ * Note that mappings should not be changed while they are in active use by
+ * a flow. The result of doing so is undefined. */
+
+/* Geneve table commands */
+enum nx_geneve_table_mod_command {
+ NXGTMC_ADD, /* New mappings (fails if an option is already
+ mapped). */
+ NXGTMC_DELETE, /* Delete mappings, identified by index
+ * (unmapped options are ignored). */
+ NXGTMC_CLEAR, /* Clear all mappings. Additional information
+ in this command is ignored. */
+};
+
+/* Map between a Geneve option and an NXM field. */
+struct nx_geneve_map {
+ ovs_be16 option_class; /* Geneve option class. */
+ uint8_t option_type; /* Geneve option type. */
+ uint8_t option_len; /* Geneve option length (multiple of 4). */
+ ovs_be16 index; /* NXM_NX_TUN_METADATA<n> index */
+ uint8_t pad[2];
+};
+OFP_ASSERT(sizeof(struct nx_geneve_map) == 8);
+
+/* NXT_GENEVE_TABLE_MOD.
+ *
+ * Use to configure a mapping between Geneve options (class, type, length)
+ * and NXM fields (NXM_NX_TUN_METADATA<n> where 'index' is <n>).
+ *
+ * This command is atomic: all operations on different options will
+ * either succeed or fail. */
+struct nx_geneve_table_mod {
+ ovs_be16 command; /* One of NTGTMC_* */
+ uint8_t pad[6];
+ /* struct nx_geneve_map[0]; Array of maps between indicies and Geneve
+ options. The number of elements is
+ inferred from the length field in the
+ header. */
+};
+OFP_ASSERT(sizeof(struct nx_geneve_table_mod) == 8);
+
+/* NXT_GENEVE_TABLE_REPLY.
+ *
+ * Issued in reponse to an NXT_GENEVE_TABLE_REQUEST to give information
+ * about the current status of the Geneve table in the switch. Provides
+ * both static information about the switch's capabilities as well as
+ * the configured Geneve option table. */
+struct nx_geneve_table_reply {
+ ovs_be32 max_option_space; /* Maximum total of option sizes supported. */
+ ovs_be16 max_fields; /* Maximum number of match fields supported. */
+ uint8_t pad[2];
+ /* struct nx_geneve_map[0]; Array of maps between indicies and Geneve
+ options. The number of elements is
+ inferred from the length field in the
+ header. */
+};
+OFP_ASSERT(sizeof(struct nx_geneve_table_reply) == 8);
+
#endif /* openflow/nicira-ext.h */
lib/tnl-ports.c \
lib/tnl-ports.h \
lib/token-bucket.c \
+ lib/tun-metadata.h \
lib/type-props.h \
lib/unaligned.h \
lib/unicode.c \
case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
case OFPTYPE_BUNDLE_CONTROL:
case OFPTYPE_BUNDLE_ADD_MESSAGE:
+ case OFPTYPE_NXT_GENEVE_TABLE_MOD:
+ case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
+ case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
default:
if (VLOG_IS_DBG_ENABLED()) {
char *s = ofp_to_string(msg->data, msg->size, 2);
/* OF1.4+(16,7). Error in output port/group. */
OFPERR_OFPMOFC_BAD_OUT,
+/* ## ----------------------------- ## */
+/* ## OFPET_GENEVE_TABLE_MOD_FAILED ## */
+/* ## ----------------------------- ## */
+
+ /* NX1.0-1.1(1,527), NX1.2+(16). The Geneve table mod command is not
+ * recognized as a valid operation. */
+ OFPERR_NXGTMFC_BAD_COMMAND,
+
+ /* NX1.0-1.1(1,528), NX1.2+(17). The option length is not a valid
+ * option size for Geneve. */
+ OFPERR_NXGTMFC_BAD_OPT_LEN,
+
+ /* NX1.0-1.1(1,529), NX1.2+(18). The field index is out of range for
+ * the supported NX_TUN_METADATA<n> match. */
+ OFPERR_NXGTMFC_BAD_FIELD_IDX,
+
+ /* NX1.0-1.1(1,530), NX1.2+(19). The total set of configured options
+ * exceeds the maximum supported by the switch. */
+ OFPERR_NXGTMFC_TABLE_FULL,
+
+ /* NX1.0-1.1(1,531), NX1.2+(20). The controller issued an NXGTMC_ADD
+ * command for a field index that is already mapped. */
+ OFPERR_NXGTMFC_ALREADY_MAPPED,
+
+ /* NX1.0-1.1(1,532), NX1.2+(21). The Geneve option that is attempting
+ * to be mapped is the same as one assigned to a different field. */
+ OFPERR_NXGTMFC_DUP_ENTRY,
+
/* ## ------------------ ## */
/* ## OFPET_EXPERIMENTER ## */
/* ## ------------------ ## */
/* NXT 1.0+ (23): void. */
OFPRAW_NXT_FLOW_MONITOR_RESUMED,
+
+ /* NXT 1.0+ (24): struct nx_geneve_table_mod, struct nx_geneve_map[]. */
+ OFPRAW_NXT_GENEVE_TABLE_MOD,
+
+ /* NXT 1.0+ (25): void. */
+ OFPRAW_NXT_GENEVE_TABLE_REQUEST,
+
+ /* NXT 1.0+ (26): struct nx_geneve_table_reply, struct nx_geneve_map[]. */
+ OFPRAW_NXT_GENEVE_TABLE_REPLY,
};
/* Decoding messages into OFPRAW_* values. */
OFPTYPE_SET_PACKET_IN_FORMAT, /* OFPRAW_NXT_SET_PACKET_IN_FORMAT. */
OFPTYPE_FLOW_AGE, /* OFPRAW_NXT_FLOW_AGE. */
OFPTYPE_SET_CONTROLLER_ID, /* OFPRAW_NXT_SET_CONTROLLER_ID. */
+ OFPTYPE_NXT_GENEVE_TABLE_MOD, /* OFPRAW_NXT_GENEVE_TABLE_MOD. */
+ OFPTYPE_NXT_GENEVE_TABLE_REQUEST, /* OFPRAW_NXT_GENEVE_TABLE_REQUEST. */
+ OFPTYPE_NXT_GENEVE_TABLE_REPLY, /* OFPRAW_NXT_GENEVE_TABLE_REPLY. */
/* Flow monitor extension. */
OFPTYPE_FLOW_MONITOR_CANCEL, /* OFPRAW_NXT_FLOW_MONITOR_CANCEL. */
}
return NULL;
}
+
+char * OVS_WARN_UNUSED_RESULT
+parse_ofp_geneve_table_mod_str(struct ofputil_geneve_table_mod *gtm,
+ uint16_t command, const char *s,
+ enum ofputil_protocol *usable_protocols)
+{
+ *usable_protocols = OFPUTIL_P_NXM_OXM_ANY;
+
+ gtm->command = command;
+ list_init(>m->mappings);
+
+ while (*s) {
+ struct ofputil_geneve_map *map = xmalloc(sizeof *map);
+ int n;
+
+ if (*s == ',') {
+ s++;
+ }
+
+ list_push_back(>m->mappings, &map->list_node);
+
+ if (!ovs_scan(s, "{class=%"SCNi16",type=%"SCNi8",len=%"SCNi8"}->tun_metadata%"SCNi16"%n",
+ &map->option_class, &map->option_type, &map->option_len,
+ &map->index, &n)) {
+ ofputil_uninit_geneve_table(>m->mappings);
+ return xstrdup("invalid geneve mapping");
+ }
+
+ s += n;
+ }
+
+ return NULL;
+}
struct ofputil_group_mod;
struct ofputil_meter_mod;
struct ofputil_table_mod;
+struct ofputil_geneve_table_mod;
struct simap;
enum ofputil_protocol;
enum ofputil_protocol *usable_protocols)
OVS_WARN_UNUSED_RESULT;
+char *parse_ofp_geneve_table_mod_str(struct ofputil_geneve_table_mod *,
+ uint16_t command, const char *string,
+ enum ofputil_protocol *usable_protocols)
+ OVS_WARN_UNUSED_RESULT;
+
char *str_to_u8(const char *str, const char *name, uint8_t *valuep)
OVS_WARN_UNUSED_RESULT;
char *str_to_u16(const char *str, const char *name, uint16_t *valuep)
}
}
+static void
+print_geneve_table(struct ds *s, struct ovs_list *mappings)
+{
+ struct ofputil_geneve_map *map;
+
+ ds_put_cstr(s, " mapping table:\n");
+ ds_put_cstr(s, " class\ttype\tlength\tmatch field\n");
+ ds_put_cstr(s, " -----\t----\t------\t-----------");
+
+ LIST_FOR_EACH (map, list_node, mappings) {
+ ds_put_char(s, '\n');
+ ds_put_format(s, " 0x%"PRIx16"\t0x%"PRIx8"\t%"PRIu8"\ttun_metadata%"PRIu16,
+ map->option_class, map->option_type, map->option_len,
+ map->index);
+ }
+}
+
+static void
+ofp_print_geneve_table_mod(struct ds *s, const struct ofp_header *oh)
+{
+ int error;
+ struct ofputil_geneve_table_mod gtm;
+
+ error = ofputil_decode_geneve_table_mod(oh, >m);
+ if (error) {
+ ofp_print_error(s, error);
+ return;
+ }
+
+ ds_put_cstr(s, "\n ");
+
+ switch (gtm.command) {
+ case NXGTMC_ADD:
+ ds_put_cstr(s, "ADD");
+ break;
+ case NXGTMC_DELETE:
+ ds_put_cstr(s, "DEL");
+ break;
+ case NXGTMC_CLEAR:
+ ds_put_cstr(s, "CLEAR");
+ break;
+ }
+
+ if (gtm.command != NXGTMC_CLEAR) {
+ print_geneve_table(s, >m.mappings);
+ }
+
+ ofputil_uninit_geneve_table(>m.mappings);
+}
+
+static void
+ofp_print_geneve_table_reply(struct ds *s, const struct ofp_header *oh)
+{
+ int error;
+ struct ofputil_geneve_table_reply gtr;
+ struct ofputil_geneve_map *map;
+ int allocated_space = 0;
+
+ error = ofputil_decode_geneve_table_reply(oh, >r);
+ if (error) {
+ ofp_print_error(s, error);
+ return;
+ }
+
+ ds_put_char(s, '\n');
+
+ LIST_FOR_EACH (map, list_node, >r.mappings) {
+ allocated_space += map->option_len;
+ }
+
+ ds_put_format(s, " max option space=%"PRIu32" max fields=%"PRIu16"\n",
+ gtr.max_option_space, gtr.max_fields);
+ ds_put_format(s, " allocated option space=%d\n", allocated_space);
+ ds_put_char(s, '\n');
+ print_geneve_table(s, >r.mappings);
+
+ ofputil_uninit_geneve_table(>r.mappings);
+}
+
static void
ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
struct ds *string, int verbosity)
case OFPTYPE_BUNDLE_ADD_MESSAGE:
ofp_print_bundle_add(string, msg, verbosity);
break;
+
+ case OFPTYPE_NXT_GENEVE_TABLE_MOD:
+ ofp_print_geneve_table_mod(string, msg);
+ break;
+
+ case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
+ break;
+
+ case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
+ ofp_print_geneve_table_reply(string, msg);
+ break;
+
}
}
#include "openflow/netronome-ext.h"
#include "packets.h"
#include "random.h"
+#include "tun-metadata.h"
#include "unaligned.h"
#include "type-props.h"
#include "openvswitch/vlog.h"
case OFPTYPE_TABLE_MOD:
case OFPTYPE_METER_MOD:
case OFPTYPE_PACKET_OUT:
+ case OFPTYPE_NXT_GENEVE_TABLE_MOD:
/* Not to be bundlable. */
case OFPTYPE_ECHO_REQUEST:
case OFPTYPE_METER_FEATURES_STATS_REPLY:
case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
case OFPTYPE_ROLE_STATUS:
+ case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
+ case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
break;
}
return request;
}
+
+static void
+encode_geneve_table_mappings(struct ofpbuf *b, struct ovs_list *mappings)
+{
+ struct ofputil_geneve_map *map;
+
+ LIST_FOR_EACH (map, list_node, mappings) {
+ struct nx_geneve_map *nx_map;
+
+ nx_map = ofpbuf_put_zeros(b, sizeof *nx_map);
+ nx_map->option_class = htons(map->option_class);
+ nx_map->option_type = map->option_type;
+ nx_map->option_len = map->option_len;
+ nx_map->index = htons(map->index);
+ }
+}
+
+struct ofpbuf *
+ofputil_encode_geneve_table_mod(enum ofp_version ofp_version,
+ struct ofputil_geneve_table_mod *gtm)
+{
+ struct ofpbuf *b;
+ struct nx_geneve_table_mod *nx_gtm;
+
+ b = ofpraw_alloc(OFPRAW_NXT_GENEVE_TABLE_MOD, ofp_version, 0);
+ nx_gtm = ofpbuf_put_zeros(b, sizeof *nx_gtm);
+ nx_gtm->command = htons(gtm->command);
+ encode_geneve_table_mappings(b, >m->mappings);
+
+ return b;
+}
+
+static enum ofperr
+decode_geneve_table_mappings(struct ofpbuf *msg, struct ovs_list *mappings)
+{
+ list_init(mappings);
+
+ while (msg->size) {
+ struct nx_geneve_map *nx_map;
+ struct ofputil_geneve_map *map;
+
+ nx_map = ofpbuf_pull(msg, sizeof *nx_map);
+ map = xmalloc(sizeof *map);
+ list_push_back(mappings, &map->list_node);
+
+ map->option_class = ntohs(nx_map->option_class);
+ map->option_type = nx_map->option_type;
+
+ map->option_len = nx_map->option_len;
+ if (map->option_len == 0 || map->option_len % 4 ||
+ map->option_len > GENEVE_MAX_OPT_SIZE) {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "geneve table option length (%u) is not a valid option size",
+ map->option_len);
+ ofputil_uninit_geneve_table(mappings);
+ return OFPERR_NXGTMFC_BAD_OPT_LEN;
+ }
+
+ map->index = ntohs(nx_map->index);
+ if (map->index >= TUN_METADATA_NUM_OPTS) {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "geneve table field index (%u) is too large (max %u)",
+ map->index, TUN_METADATA_NUM_OPTS - 1);
+ ofputil_uninit_geneve_table(mappings);
+ return OFPERR_NXGTMFC_BAD_FIELD_IDX;
+ }
+ }
+
+ return 0;
+}
+
+enum ofperr
+ofputil_decode_geneve_table_mod(const struct ofp_header *oh,
+ struct ofputil_geneve_table_mod *gtm)
+{
+ struct ofpbuf msg;
+ struct nx_geneve_table_mod *nx_gtm;
+
+ ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&msg);
+
+ nx_gtm = ofpbuf_pull(&msg, sizeof *nx_gtm);
+ gtm->command = ntohs(nx_gtm->command);
+ if (gtm->command > NXGTMC_CLEAR) {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "geneve table mod command (%u) is out of range",
+ gtm->command);
+ return OFPERR_NXGTMFC_BAD_COMMAND;
+ }
+
+ return decode_geneve_table_mappings(&msg, >m->mappings);
+}
+
+struct ofpbuf *
+ofputil_encode_geneve_table_reply(const struct ofp_header *oh,
+ struct ofputil_geneve_table_reply *gtr)
+{
+ struct ofpbuf *b;
+ struct nx_geneve_table_reply *nx_gtr;
+
+ b = ofpraw_alloc_reply(OFPRAW_NXT_GENEVE_TABLE_REPLY, oh, 0);
+ nx_gtr = ofpbuf_put_zeros(b, sizeof *nx_gtr);
+ nx_gtr->max_option_space = htonl(gtr->max_option_space);
+ nx_gtr->max_fields = htons(gtr->max_fields);
+
+ encode_geneve_table_mappings(b, >r->mappings);
+
+ return b;
+}
+
+enum ofperr
+ofputil_decode_geneve_table_reply(const struct ofp_header *oh,
+ struct ofputil_geneve_table_reply *gtr)
+{
+ struct ofpbuf msg;
+ struct nx_geneve_table_reply *nx_gtr;
+
+ ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&msg);
+
+ nx_gtr = ofpbuf_pull(&msg, sizeof *nx_gtr);
+ gtr->max_option_space = ntohl(nx_gtr->max_option_space);
+ gtr->max_fields = ntohs(nx_gtr->max_fields);
+
+ return decode_geneve_table_mappings(&msg, >r->mappings);
+}
+
+void
+ofputil_uninit_geneve_table(struct ovs_list *mappings)
+{
+ struct ofputil_geneve_map *map;
+
+ LIST_FOR_EACH_POP (map, list_node, mappings) {
+ free(map);
+ }
+}
enum ofperr ofputil_decode_bundle_add(const struct ofp_header *,
struct ofputil_bundle_add_msg *,
enum ofptype *type);
+
+struct ofputil_geneve_map {
+ struct ovs_list list_node;
+
+ uint16_t option_class;
+ uint8_t option_type;
+ uint8_t option_len;
+ uint16_t index;
+};
+
+struct ofputil_geneve_table_mod {
+ uint16_t command;
+ struct ovs_list mappings; /* Contains "struct ofputil_geneve_map"s. */
+};
+
+struct ofputil_geneve_table_reply {
+ uint32_t max_option_space;
+ uint16_t max_fields;
+ struct ovs_list mappings; /* Contains "struct ofputil_geneve_map"s. */
+};
+
+struct ofpbuf *ofputil_encode_geneve_table_mod(enum ofp_version ofp_version,
+ struct ofputil_geneve_table_mod *);
+enum ofperr ofputil_decode_geneve_table_mod(const struct ofp_header *,
+ struct ofputil_geneve_table_mod *);
+struct ofpbuf *ofputil_encode_geneve_table_reply(const struct ofp_header *,
+ struct ofputil_geneve_table_reply *);
+enum ofperr ofputil_decode_geneve_table_reply(const struct ofp_header *,
+ struct ofputil_geneve_table_reply *);
+void ofputil_uninit_geneve_table(struct ovs_list *mappings);
+
#endif /* ofp-util.h */
}
/* Tunnel header */
+#define GENEVE_MAX_OPT_SIZE 124
+
#define GENEVE_CRIT_OPT_TYPE (1 << 7)
+
struct geneve_opt {
ovs_be16 opt_class;
uint8_t type;
case OFPTYPE_FLOW_MONITOR_RESUMED:
case OFPTYPE_BUNDLE_CONTROL:
case OFPTYPE_BUNDLE_ADD_MESSAGE:
+ case OFPTYPE_NXT_GENEVE_TABLE_MOD:
+ case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
+ case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
default:
return true;
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TUN_METADATA_H
+#define TUN_METADATA_H 1
+
+#define TUN_METADATA_NUM_OPTS 0
+
+#endif /* tun-metadata.h */
return error;
}
+static enum ofperr
+handle_geneve_table_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+ struct ofputil_geneve_table_mod gtm;
+ enum ofperr error;
+
+ error = reject_slave_controller(ofconn);
+ if (error) {
+ return error;
+ }
+
+ error = ofputil_decode_geneve_table_mod(oh, >m);
+ if (error) {
+ return error;
+ }
+
+ ofputil_uninit_geneve_table(>m.mappings);
+ return OFPERR_OFPBRC_BAD_TYPE;
+}
+
+static enum ofperr
+handle_geneve_table_request(struct ofconn *ofconn OVS_UNUSED,
+ const struct ofp_header *oh OVS_UNUSED)
+{
+ return OFPERR_OFPBRC_BAD_TYPE;
+}
+
static enum ofperr
handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
OVS_EXCLUDED(ofproto_mutex)
case OFPTYPE_BUNDLE_ADD_MESSAGE:
return handle_bundle_add(ofconn, oh);
+ case OFPTYPE_NXT_GENEVE_TABLE_MOD:
+ return handle_geneve_table_mod(ofconn, oh);
+
+ case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
+ return handle_geneve_table_request(ofconn, oh);
+
case OFPTYPE_HELLO:
case OFPTYPE_ERROR:
case OFPTYPE_FEATURES_REPLY:
case OFPTYPE_METER_FEATURES_STATS_REPLY:
case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
case OFPTYPE_ROLE_STATUS:
+ case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
default:
if (ofpmsg_is_stat_request(oh)) {
return OFPERR_OFPBRC_BAD_STAT;
case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
case OFPTYPE_BUNDLE_CONTROL:
case OFPTYPE_BUNDLE_ADD_MESSAGE:
+ case OFPTYPE_NXT_GENEVE_TABLE_MOD:
+ case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
+ case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
default:
/* Messages that are generally unexpected. */
if (VLOG_IS_DBG_ENABLED()) {
If no \fIcommand_bucket_id\fR is present in the group specification then all
buckets of the group are removed.
.
+.SS "OpenFlow Switch Geneve Option Table Commands"
+.
+In order to work with Geneve options, it is necessary to maintain a mapping
+table between an option (defined by <class, type, length>) and an NXM field
+that can be operated on for the purposes of matches, actions, etc. This
+mapping must be explicitly specified by the user through the following
+commands. The format for \fIoptions\fR is given in \fBOption Syntax\fR below.
+
+Note that a given mapping should not be changed while it is in active use by
+a flow. The result of doing so is undefined.
+
+These commands are Nicira extensions to OpenFlow and require Open vSwitch
+2.5 or later.
+
+.IP "\fBadd\-geneve\-map \fIswitch options\fR"
+Add each option entry to \fIswitch\fR's tables. Duplicate fields are
+rejected.
+.
+.IP "\fBdel\-geneve\-map \fIswitch \fR[\fIoptions\fR]"
+Delete each option entry in \fIswitch\fR's tables based on its field index.
+Fields that aren't already mapped will be ignored. If no options are
+specified then the entire table will be cleared.
+.
+.IP "\fBdump\-geneve\-map \fIswitch\fR"
+Show the currently mapped fields in the switch's option table as well
+as switch capabilities.
+.
+.IP "\fBOption Syntax\fR"
+\fB{class=\fIclass\fB,type=\fItype\fB,len=\fIlength\fB}->tun_metadata\fIn\fR
+
+An option can be specified in this form (repeating as necessary and
+separated by commas). For example, the follow is used to map a new option:
+
+.RS
+add-geneve-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"
+.RE
+.
.SS "OpenFlow Switch Monitoring Commands"
.
.IP "\fBsnoop \fIswitch\fR"
" dump-meters SWITCH print all meter configuration\n"
" meter-stats SWITCH [METER] print meter statistics\n"
" meter-features SWITCH print meter features\n"
+ " add-geneve-map SWITCH MAP add Geneve option MAPpings\n"
+ " del-geneve-map SWITCH [MAP] delete Geneve option MAPpings\n"
+ " dump-geneve-map SWITCH print Geneve option mappings\n"
"\nFor OpenFlow switches and controllers:\n"
" probe TARGET probe whether TARGET is up\n"
" ping TARGET [N] latency of N-byte echos\n"
vconn_close(vconn);
}
+static void
+ofctl_geneve_mod(struct ovs_cmdl_context *ctx, uint16_t command)
+{
+ enum ofputil_protocol usable_protocols;
+ enum ofputil_protocol protocol;
+ struct ofputil_geneve_table_mod gtm;
+ char *error;
+ enum ofp_version version;
+ struct ofpbuf *request;
+ struct vconn *vconn;
+
+ error = parse_ofp_geneve_table_mod_str(>m, command, ctx->argc > 2 ?
+ ctx->argv[2] : "",
+ &usable_protocols);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+
+ protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols);
+ version = ofputil_protocol_to_ofp_version(protocol);
+
+ request = ofputil_encode_geneve_table_mod(version, >m);
+ if (request) {
+ transact_noreply(vconn, request);
+ }
+
+ vconn_close(vconn);
+ ofputil_uninit_geneve_table(>m.mappings);
+}
+
+static void
+ofctl_add_geneve_map(struct ovs_cmdl_context *ctx)
+{
+ ofctl_geneve_mod(ctx, NXGTMC_ADD);
+}
+
+static void
+ofctl_del_geneve_map(struct ovs_cmdl_context *ctx)
+{
+ ofctl_geneve_mod(ctx, ctx->argc > 2 ? NXGTMC_DELETE : NXGTMC_CLEAR);
+}
+
+static void
+ofctl_dump_geneve_map(struct ovs_cmdl_context *ctx)
+{
+ dump_trivial_transaction(ctx->argv[1], OFPRAW_NXT_GENEVE_TABLE_REQUEST);
+}
+
static void
ofctl_help(struct ovs_cmdl_context *ctx OVS_UNUSED)
{
1, 2, ofctl_dump_group_stats },
{ "dump-group-features", "switch",
1, 1, ofctl_dump_group_features },
+ { "add-geneve-map", "switch map",
+ 2, 2, ofctl_add_geneve_map },
+ { "del-geneve-map", "switch [map]",
+ 1, 2, ofctl_del_geneve_map },
+ { "dump-geneve-map", "switch",
+ 1, 1, ofctl_dump_geneve_map },
{ "help", NULL, 0, INT_MAX, ofctl_help },
{ "list-commands", NULL, 0, INT_MAX, ofctl_list_commands },