openflow: Table maintenance commands for Geneve options.
authorJesse Gross <jesse@nicira.com>
Tue, 2 Jun 2015 22:11:00 +0000 (15:11 -0700)
committerJesse Gross <jesse@nicira.com>
Thu, 25 Jun 2015 18:08:58 +0000 (11:08 -0700)
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.

Conceptually, this table could be communicated using either OpenFlow
or OVSDB. Using OVSDB requires less code and definition of extensions
than OpenFlow but introduces the possibility that mapping table
updates and flow modifications are desynchronized from each other.
This is dangerous because the mapping table signifcantly impacts the
way that flows using Geneve options are installed and processed by
OVS. Therefore, the mapping table is maintained using OpenFlow commands
instead, which opens the possibility of using synchronization between
table changes and flow modifications through barriers, bundles, etc.

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.

This only adds the OpenFlow infrastructure but doesn't actually
do anything with the information yet after the messages have been
decoded.

Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
17 files changed:
include/openflow/nicira-ext.h
lib/automake.mk
lib/learning-switch.c
lib/ofp-errors.h
lib/ofp-msgs.h
lib/ofp-parse.c
lib/ofp-parse.h
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.h
lib/packets.h
lib/rconn.c
lib/tun-metadata.h [new file with mode: 0644]
ofproto/ofproto.c
ovn/controller/ofctrl.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c

index e749450..22325aa 100644 (file)
@@ -900,4 +900,74 @@ struct nx_flow_monitor_cancel {
 };
 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 */
index 706ff4b..e230dd0 100644 (file)
@@ -250,6 +250,7 @@ lib_libopenvswitch_la_SOURCES = \
        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 \
index 3c8536d..2fe38a0 100644 (file)
@@ -449,6 +449,9 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     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);
index e6c9211..fbcc641 100644 (file)
@@ -707,6 +707,34 @@ enum ofperr {
     /* 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 ## */
 /* ## ------------------ ## */
index 23c334a..e28bd50 100644 (file)
@@ -419,6 +419,15 @@ enum ofpraw {
 
     /* 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. */
@@ -620,6 +629,9 @@ enum ofptype {
     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. */
index 210feed..df32819 100644 (file)
@@ -1584,3 +1584,36 @@ parse_ofp_group_mod_file(const char *file_name, uint16_t command,
     }
     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(&gtm->mappings);
+
+    while (*s) {
+        struct ofputil_geneve_map *map = xmalloc(sizeof *map);
+        int n;
+
+        if (*s == ',') {
+            s++;
+        }
+
+        list_push_back(&gtm->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(&gtm->mappings);
+            return xstrdup("invalid geneve mapping");
+        }
+
+        s += n;
+    }
+
+    return NULL;
+}
index f112603..0749d5b 100644 (file)
@@ -34,6 +34,7 @@ struct ofputil_flow_stats_request;
 struct ofputil_group_mod;
 struct ofputil_meter_mod;
 struct ofputil_table_mod;
+struct ofputil_geneve_table_mod;
 struct simap;
 enum ofputil_protocol;
 
@@ -84,6 +85,11 @@ char *parse_ofp_group_mod_str(struct ofputil_group_mod *, uint16_t command,
                               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)
index 2ac11b1..1b25180 100644 (file)
@@ -2641,6 +2641,85 @@ ofp_print_bundle_add(struct ds *s, const struct ofp_header *oh, int verbosity)
     }
 }
 
+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, &gtm);
+    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, &gtm.mappings);
+    }
+
+    ofputil_uninit_geneve_table(&gtm.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, &gtr);
+    if (error) {
+        ofp_print_error(s, error);
+        return;
+    }
+
+    ds_put_char(s, '\n');
+
+    LIST_FOR_EACH (map, list_node, &gtr.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, &gtr.mappings);
+
+    ofputil_uninit_geneve_table(&gtr.mappings);
+}
+
 static void
 ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
                 struct ds *string, int verbosity)
@@ -2899,6 +2978,18 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     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;
+
     }
 }
 
index 89359c1..973f138 100644 (file)
@@ -41,6 +41,7 @@
 #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"
@@ -8759,6 +8760,7 @@ ofputil_is_bundlable(enum ofptype type)
     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:
@@ -8822,6 +8824,8 @@ ofputil_is_bundlable(enum ofptype type)
     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;
     }
 
@@ -8893,3 +8897,139 @@ ofputil_encode_bundle_add(enum ofp_version ofp_version,
 
     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, &gtm->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, &gtm->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, &gtr->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, &gtr->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);
+    }
+}
index 596c2e2..f90ac0c 100644 (file)
@@ -1125,4 +1125,35 @@ struct ofpbuf *ofputil_encode_bundle_add(enum ofp_version ofp_version,
 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 */
index b5cd6ab..23fb40a 100644 (file)
@@ -751,7 +751,10 @@ static inline bool dl_type_is_ip_any(ovs_be16 dl_type)
 }
 
 /* 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;
index a4a5dbf..776ab9e 100644 (file)
@@ -1414,6 +1414,9 @@ is_admitted_msg(const struct ofpbuf *b)
     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;
     }
diff --git a/lib/tun-metadata.h b/lib/tun-metadata.h
new file mode 100644 (file)
index 0000000..e7a6d3e
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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 */
index 08ba043..ae7edcb 100644 (file)
@@ -6908,6 +6908,33 @@ handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh)
     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, &gtm);
+    if (error) {
+        return error;
+    }
+
+    ofputil_uninit_geneve_table(&gtm.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)
@@ -7049,6 +7076,12 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     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:
@@ -7078,6 +7111,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     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;
index 2f1767a..8405001 100644 (file)
@@ -227,6 +227,9 @@ ofctrl_recv(const struct ofpbuf *msg)
     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()) {
index 63c2ecc..ead38e7 100644 (file)
@@ -407,6 +407,43 @@ Remove buckets to an existing group present in the \fIswitch\fR's group table.
 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"
index 8df79b8..5af1f13 100644 (file)
@@ -378,6 +378,9 @@ usage(void)
            "  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"
@@ -2325,6 +2328,54 @@ ofctl_dump_group_features(struct ovs_cmdl_context *ctx)
     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(&gtm, 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, &gtm);
+    if (request) {
+        transact_noreply(vconn, request);
+    }
+
+    vconn_close(vconn);
+    ofputil_uninit_geneve_table(&gtm.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)
 {
@@ -3645,6 +3696,12 @@ static const struct ovs_cmdl_command all_commands[] = {
       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 },