+ /* OpenFlow replies. */
+ case OFPUTIL_OFPT_ECHO_REPLY:
+ return 0;
+
+ /* Nicira extension requests. */
+ case OFPUTIL_NXT_ROLE_REQUEST:
+ return handle_role_request(ofconn, oh);
+
+ case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
+ return handle_nxt_flow_mod_table_id(ofconn, oh);
+
+ case OFPUTIL_NXT_SET_FLOW_FORMAT:
+ return handle_nxt_set_flow_format(ofconn, oh);
+
+ case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
+ return handle_nxt_set_packet_in_format(ofconn, oh);
+
+ case OFPUTIL_NXT_SET_CONTROLLER_ID:
+ return handle_nxt_set_controller_id(ofconn, oh);
+
+ case OFPUTIL_NXT_FLOW_MOD:
+ return handle_flow_mod(ofconn, oh);
+
+ case OFPUTIL_NXT_FLOW_AGE:
+ /* Nothing to do. */
+ return 0;
+
+ case OFPUTIL_NXT_SET_ASYNC_CONFIG:
+ return handle_nxt_set_async_config(ofconn, oh);
+
+ /* Statistics requests. */
+ case OFPUTIL_OFPST_DESC_REQUEST:
+ return handle_desc_stats_request(ofconn, msg->data);
+
+ case OFPUTIL_OFPST_FLOW_REQUEST:
+ case OFPUTIL_NXST_FLOW_REQUEST:
+ return handle_flow_stats_request(ofconn, msg->data);
+
+ case OFPUTIL_OFPST_AGGREGATE_REQUEST:
+ case OFPUTIL_NXST_AGGREGATE_REQUEST:
+ return handle_aggregate_stats_request(ofconn, msg->data);
+
+ case OFPUTIL_OFPST_TABLE_REQUEST:
+ return handle_table_stats_request(ofconn, msg->data);
+
+ case OFPUTIL_OFPST_PORT_REQUEST:
+ return handle_port_stats_request(ofconn, msg->data);
+
+ case OFPUTIL_OFPST_QUEUE_REQUEST:
+ return handle_queue_stats_request(ofconn, msg->data);
+
+ case OFPUTIL_MSG_INVALID:
+ case OFPUTIL_OFPT_HELLO:
+ case OFPUTIL_OFPT_ERROR:
+ case OFPUTIL_OFPT_FEATURES_REPLY:
+ case OFPUTIL_OFPT_GET_CONFIG_REPLY:
+ case OFPUTIL_OFPT_PACKET_IN:
+ case OFPUTIL_OFPT_FLOW_REMOVED:
+ case OFPUTIL_OFPT_PORT_STATUS:
+ case OFPUTIL_OFPT_BARRIER_REPLY:
+ case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
+ case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
+ case OFPUTIL_OFPST_DESC_REPLY:
+ case OFPUTIL_OFPST_FLOW_REPLY:
+ case OFPUTIL_OFPST_QUEUE_REPLY:
+ case OFPUTIL_OFPST_PORT_REPLY:
+ case OFPUTIL_OFPST_TABLE_REPLY:
+ case OFPUTIL_OFPST_AGGREGATE_REPLY:
+ case OFPUTIL_NXT_ROLE_REPLY:
+ case OFPUTIL_NXT_FLOW_REMOVED:
+ case OFPUTIL_NXT_PACKET_IN:
+ case OFPUTIL_NXST_FLOW_REPLY:
+ case OFPUTIL_NXST_AGGREGATE_REPLY:
+ default:
+ return (oh->type == OFPT10_STATS_REQUEST ||
+ oh->type == OFPT10_STATS_REPLY
+ ? OFPERR_OFPBRC_BAD_STAT
+ : OFPERR_OFPBRC_BAD_TYPE);
+ }
+}
+
+static bool
+handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg)
+{
+ int error = handle_openflow__(ofconn, ofp_msg);
+ if (error && error != OFPROTO_POSTPONE) {
+ ofconn_send_error(ofconn, ofp_msg->data, error);
+ }
+ COVERAGE_INC(ofproto_recv_openflow);
+ return error != OFPROTO_POSTPONE;
+}
+\f
+/* Asynchronous operations. */
+
+/* Creates and returns a new ofopgroup that is not associated with any
+ * OpenFlow connection.
+ *
+ * The caller should add operations to the returned group with
+ * ofoperation_create() and then submit it with ofopgroup_submit(). */
+static struct ofopgroup *
+ofopgroup_create_unattached(struct ofproto *ofproto)
+{
+ struct ofopgroup *group = xzalloc(sizeof *group);
+ group->ofproto = ofproto;
+ list_init(&group->ofproto_node);
+ list_init(&group->ops);
+ list_init(&group->ofconn_node);
+ return group;
+}
+
+/* Creates and returns a new ofopgroup for 'ofproto'.
+ *
+ * If 'ofconn' is NULL, the new ofopgroup is not associated with any OpenFlow
+ * connection. The 'request' and 'buffer_id' arguments are ignored.
+ *
+ * If 'ofconn' is nonnull, then the new ofopgroup is associated with 'ofconn'.
+ * If the ofopgroup eventually fails, then the error reply will include
+ * 'request'. If the ofopgroup eventually succeeds, then the packet with
+ * buffer id 'buffer_id' on 'ofconn' will be sent by 'ofconn''s ofproto.
+ *
+ * The caller should add operations to the returned group with
+ * ofoperation_create() and then submit it with ofopgroup_submit(). */
+static struct ofopgroup *
+ofopgroup_create(struct ofproto *ofproto, struct ofconn *ofconn,
+ const struct ofp_header *request, uint32_t buffer_id)
+{
+ struct ofopgroup *group = ofopgroup_create_unattached(ofproto);
+ if (ofconn) {
+ size_t request_len = ntohs(request->length);
+
+ assert(ofconn_get_ofproto(ofconn) == ofproto);
+
+ ofconn_add_opgroup(ofconn, &group->ofconn_node);
+ group->ofconn = ofconn;
+ group->request = xmemdup(request, MIN(request_len, 64));
+ group->buffer_id = buffer_id;
+ }
+ return group;
+}
+
+/* Submits 'group' for processing.
+ *
+ * If 'group' contains no operations (e.g. none were ever added, or all of the
+ * ones that were added completed synchronously), then it is destroyed
+ * immediately. Otherwise it is added to the ofproto's list of pending
+ * groups. */
+static void
+ofopgroup_submit(struct ofopgroup *group)
+{
+ if (list_is_empty(&group->ops)) {
+ ofopgroup_destroy(group);
+ } else {
+ list_push_back(&group->ofproto->pending, &group->ofproto_node);
+ group->ofproto->n_pending++;
+ }
+}
+
+static void
+ofopgroup_destroy(struct ofopgroup *group)
+{
+ assert(list_is_empty(&group->ops));
+ if (!list_is_empty(&group->ofproto_node)) {
+ assert(group->ofproto->n_pending > 0);
+ group->ofproto->n_pending--;
+ list_remove(&group->ofproto_node);
+ }
+ if (!list_is_empty(&group->ofconn_node)) {
+ list_remove(&group->ofconn_node);
+ if (group->error) {
+ ofconn_send_error(group->ofconn, group->request, group->error);
+ }
+ connmgr_retry(group->ofproto->connmgr);
+ }
+ free(group->request);
+ free(group);
+}
+
+/* Initiates a new operation on 'rule', of the specified 'type', within
+ * 'group'. Prior to calling, 'rule' must not have any pending operation. */
+static void
+ofoperation_create(struct ofopgroup *group, struct rule *rule,
+ enum ofoperation_type type)
+{
+ struct ofoperation *op;