+
+static void
+encode_tlv_table_mappings(struct ofpbuf *b, struct ovs_list *mappings)
+{
+ struct ofputil_tlv_map *map;
+
+ LIST_FOR_EACH (map, list_node, mappings) {
+ struct nx_tlv_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_tlv_table_mod(enum ofp_version ofp_version,
+ struct ofputil_tlv_table_mod *ttm)
+{
+ struct ofpbuf *b;
+ struct nx_tlv_table_mod *nx_ttm;
+
+ b = ofpraw_alloc(OFPRAW_NXT_TLV_TABLE_MOD, ofp_version, 0);
+ nx_ttm = ofpbuf_put_zeros(b, sizeof *nx_ttm);
+ nx_ttm->command = htons(ttm->command);
+ encode_tlv_table_mappings(b, &ttm->mappings);
+
+ return b;
+}
+
+static enum ofperr
+decode_tlv_table_mappings(struct ofpbuf *msg, unsigned int max_fields,
+ struct ovs_list *mappings)
+{
+ list_init(mappings);
+
+ while (msg->size) {
+ struct nx_tlv_map *nx_map;
+ struct ofputil_tlv_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 % 4 || map->option_len > TLV_MAX_OPT_SIZE) {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "tlv table option length (%u) is not a valid option size",
+ map->option_len);
+ ofputil_uninit_tlv_table(mappings);
+ return OFPERR_NXTTMFC_BAD_OPT_LEN;
+ }
+
+ map->index = ntohs(nx_map->index);
+ if (map->index >= max_fields) {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "tlv table field index (%u) is too large (max %u)",
+ map->index, max_fields - 1);
+ ofputil_uninit_tlv_table(mappings);
+ return OFPERR_NXTTMFC_BAD_FIELD_IDX;
+ }
+ }
+
+ return 0;
+}
+
+enum ofperr
+ofputil_decode_tlv_table_mod(const struct ofp_header *oh,
+ struct ofputil_tlv_table_mod *ttm)
+{
+ struct ofpbuf msg;
+ struct nx_tlv_table_mod *nx_ttm;
+
+ ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&msg);
+
+ nx_ttm = ofpbuf_pull(&msg, sizeof *nx_ttm);
+ ttm->command = ntohs(nx_ttm->command);
+ if (ttm->command > NXTTMC_CLEAR) {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "tlv table mod command (%u) is out of range",
+ ttm->command);
+ return OFPERR_NXTTMFC_BAD_COMMAND;
+ }
+
+ return decode_tlv_table_mappings(&msg, TUN_METADATA_NUM_OPTS,
+ &ttm->mappings);
+}
+
+struct ofpbuf *
+ofputil_encode_tlv_table_reply(const struct ofp_header *oh,
+ struct ofputil_tlv_table_reply *ttr)
+{
+ struct ofpbuf *b;
+ struct nx_tlv_table_reply *nx_ttr;
+
+ b = ofpraw_alloc_reply(OFPRAW_NXT_TLV_TABLE_REPLY, oh, 0);
+ nx_ttr = ofpbuf_put_zeros(b, sizeof *nx_ttr);
+ nx_ttr->max_option_space = htonl(ttr->max_option_space);
+ nx_ttr->max_fields = htons(ttr->max_fields);
+
+ encode_tlv_table_mappings(b, &ttr->mappings);
+
+ return b;
+}
+
+/* Decodes the NXT_TLV_TABLE_REPLY message in 'oh' into '*ttr'. Returns 0
+ * if successful, otherwise an ofperr.
+ *
+ * The decoder verifies that the indexes in 'ttr->mappings' are less than
+ * 'ttr->max_fields', but the caller must ensure, if necessary, that they are
+ * less than TUN_METADATA_NUM_OPTS. */
+enum ofperr
+ofputil_decode_tlv_table_reply(const struct ofp_header *oh,
+ struct ofputil_tlv_table_reply *ttr)
+{
+ struct ofpbuf msg;
+ struct nx_tlv_table_reply *nx_ttr;
+
+ ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&msg);
+
+ nx_ttr = ofpbuf_pull(&msg, sizeof *nx_ttr);
+ ttr->max_option_space = ntohl(nx_ttr->max_option_space);
+ ttr->max_fields = ntohs(nx_ttr->max_fields);
+
+ return decode_tlv_table_mappings(&msg, ttr->max_fields, &ttr->mappings);
+}
+
+void
+ofputil_uninit_tlv_table(struct ovs_list *mappings)
+{
+ struct ofputil_tlv_map *map;
+
+ LIST_FOR_EACH_POP (map, list_node, mappings) {
+ free(map);
+ }
+}
+
+/* Decodes the OpenFlow "set async config" request and "get async config
+ * reply" message in '*oh' into an abstract form in 'master' and 'slave'.
+ *
+ * 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.
+ *
+ * Returns 0 if successful, otherwise an OFPERR_* value.
+ *
+ * Returns error code OFPERR_OFPACFC_INVALID if the value of mask is not in
+ * the valid range of mask.
+ *
+ * Returns error code OFPERR_OFPACFC_UNSUPPORTED if the configuration is not
+ * supported.*/
+enum ofperr
+ofputil_decode_set_async_config(const struct ofp_header *oh,
+ uint32_t master[OAM_N_TYPES],
+ uint32_t slave[OAM_N_TYPES],
+ bool loose)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ if (raw == OFPRAW_OFPT13_SET_ASYNC ||
+ raw == OFPRAW_NXT_SET_ASYNC_CONFIG ||
+ raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) {
+ const struct nx_async_config *msg = ofpmsg_body(oh);
+
+ master[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[0]);
+ master[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[0]);
+ master[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[0]);
+
+ slave[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[1]);
+ slave[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[1]);
+ slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]);
+
+ } else if (raw == OFPRAW_OFPT14_SET_ASYNC ||
+ raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) {
+
+ while (b.size > 0) {
+ struct ofpbuf property;
+ enum ofperr error;
+ uint64_t type;
+ uint32_t mask;
+
+ error = ofpprop_pull__(&b, &property, 8, 0xfffe, &type);
+ if (error) {
+ return error;
+ }
+
+ error = ofpprop_parse_u32(&property, &mask);
+ if (error) {
+ return error;
+ }
+
+ if (!loose) {
+ error = ofputil_check_mask(type, mask);
+ if (error) {
+ return error;
+ }
+ }
+
+ switch (type) {
+ case OFPACPT_PACKET_IN_SLAVE:
+ slave[OAM_PACKET_IN] = mask;
+ break;
+
+ case OFPACPT_PACKET_IN_MASTER:
+ master[OAM_PACKET_IN] = mask;
+ break;
+
+ case OFPACPT_PORT_STATUS_SLAVE:
+ slave[OAM_PORT_STATUS] = mask;
+ break;
+
+ case OFPACPT_PORT_STATUS_MASTER:
+ master[OAM_PORT_STATUS] = mask;
+ break;
+
+ case OFPACPT_FLOW_REMOVED_SLAVE:
+ slave[OAM_FLOW_REMOVED] = mask;
+ break;
+
+ case OFPACPT_FLOW_REMOVED_MASTER:
+ master[OAM_FLOW_REMOVED] = mask;
+ break;
+
+ case OFPACPT_ROLE_STATUS_SLAVE:
+ slave[OAM_ROLE_STATUS] = mask;
+ break;
+
+ case OFPACPT_ROLE_STATUS_MASTER:
+ master[OAM_ROLE_STATUS] = mask;
+ break;
+
+ case OFPACPT_TABLE_STATUS_SLAVE:
+ slave[OAM_TABLE_STATUS] = mask;
+ break;
+
+ case OFPACPT_TABLE_STATUS_MASTER:
+ master[OAM_TABLE_STATUS] = mask;
+ break;
+
+ case OFPACPT_REQUESTFORWARD_SLAVE:
+ slave[OAM_REQUESTFORWARD] = mask;
+ break;
+
+ case OFPACPT_REQUESTFORWARD_MASTER:
+ master[OAM_REQUESTFORWARD] = mask;
+ break;
+
+ default:
+ error = loose ? 0 : OFPERR_OFPACFC_UNSUPPORTED;
+ return error;
+ }
+ }
+ } else {
+ return OFPERR_OFPBRC_BAD_VERSION;
+ }
+ return 0;
+}
+
+/* Append all asynchronous configuration properties in GET_ASYNC_REPLY
+ * message, describing if various set of asynchronous messages are enabled
+ * or not. */
+static enum ofperr
+ofputil_get_async_reply(struct ofpbuf *buf, const uint32_t master_mask,
+ const uint32_t slave_mask, const uint32_t type)
+{
+ int role;
+
+ for (role = 0; role < 2; role++) {
+ enum ofp14_async_config_prop_type prop_type;
+
+ switch (type) {
+ case OAM_PACKET_IN:
+ prop_type = (role ? OFPACPT_PACKET_IN_SLAVE
+ : OFPACPT_PACKET_IN_MASTER);
+ break;
+
+ case OAM_PORT_STATUS:
+ prop_type = (role ? OFPACPT_PORT_STATUS_SLAVE
+ : OFPACPT_PORT_STATUS_MASTER);
+ break;
+
+ case OAM_FLOW_REMOVED:
+ prop_type = (role ? OFPACPT_FLOW_REMOVED_SLAVE
+ : OFPACPT_FLOW_REMOVED_MASTER);
+ break;
+
+ case OAM_ROLE_STATUS:
+ prop_type = (role ? OFPACPT_ROLE_STATUS_SLAVE
+ : OFPACPT_ROLE_STATUS_MASTER);
+ break;
+
+ case OAM_TABLE_STATUS:
+ prop_type = (role ? OFPACPT_TABLE_STATUS_SLAVE
+ : OFPACPT_TABLE_STATUS_MASTER);
+ break;
+
+ case OAM_REQUESTFORWARD:
+ prop_type = (role ? OFPACPT_REQUESTFORWARD_SLAVE
+ : OFPACPT_REQUESTFORWARD_MASTER);
+ break;
+
+ default:
+ return OFPERR_OFPBRC_BAD_TYPE;
+ }
+ ofpprop_put_u32(buf, prop_type, role ? slave_mask : master_mask);
+ }
+
+ return 0;
+}
+
+/* Returns a OpenFlow message that encodes 'asynchronous configuration' properly
+ * as a reply to get async config request. */
+struct ofpbuf *
+ofputil_encode_get_async_config(const struct ofp_header *oh,
+ uint32_t master[OAM_N_TYPES],
+ uint32_t slave[OAM_N_TYPES])
+{
+ struct ofpbuf *buf;
+ uint32_t type;
+
+ buf = ofpraw_alloc_reply((oh->version < OFP14_VERSION
+ ? OFPRAW_OFPT13_GET_ASYNC_REPLY
+ : OFPRAW_OFPT14_GET_ASYNC_REPLY), oh, 0);
+
+ if (oh->version < OFP14_VERSION) {
+ struct nx_async_config *msg;
+ msg = ofpbuf_put_zeros(buf, sizeof *msg);
+
+ msg->packet_in_mask[0] = htonl(master[OAM_PACKET_IN]);
+ msg->port_status_mask[0] = htonl(master[OAM_PORT_STATUS]);
+ msg->flow_removed_mask[0] = htonl(master[OAM_FLOW_REMOVED]);
+
+ msg->packet_in_mask[1] = htonl(slave[OAM_PACKET_IN]);
+ msg->port_status_mask[1] = htonl(slave[OAM_PORT_STATUS]);
+ msg->flow_removed_mask[1] = htonl(slave[OAM_FLOW_REMOVED]);
+ } else if (oh->version == OFP14_VERSION) {
+ for (type = 0; type < OAM_N_TYPES; type++) {
+ ofputil_get_async_reply(buf, master[type], slave[type], type);
+ }
+ }
+
+ return buf;
+}