+ struct ofproto *ofproto = rule->ofproto;
+ struct ofopgroup *group;
+
+ assert(reason == OFPRR_HARD_TIMEOUT || reason == OFPRR_IDLE_TIMEOUT);
+
+ ofproto_rule_send_removed(rule, reason);
+
+ group = ofopgroup_create_unattached(ofproto);
+ ofoperation_create(group, rule, OFOPERATION_DELETE, reason);
+ oftable_remove_rule(rule);
+ ofproto->ofproto_class->rule_destruct(rule);
+ ofopgroup_submit(group);
+}
+\f
+static enum ofperr
+handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ struct ofputil_flow_mod fm;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+ long long int now;
+
+ error = reject_slave_controller(ofconn);
+ if (error) {
+ goto exit;
+ }
+
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
+ &ofpacts);
+ if (error) {
+ goto exit_free_ofpacts;
+ }
+
+ /* We do not support the OpenFlow 1.0 emergency flow cache, which is not
+ * required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. */
+ if (fm.flags & OFPFF_EMERG) {
+ /* We do not support the emergency flow cache. It will hopefully get
+ * dropped from OpenFlow in the near future. There is no good error
+ * code, so just state that the flow table is full. */
+ error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ } else {
+ error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
+ }
+ if (error) {
+ goto exit_free_ofpacts;
+ }
+
+ /* Record the operation for logging a summary report. */
+ switch (fm.command) {
+ case OFPFC_ADD:
+ ofproto->n_add++;
+ break;
+
+ case OFPFC_MODIFY:
+ case OFPFC_MODIFY_STRICT:
+ ofproto->n_modify++;
+ break;
+
+ case OFPFC_DELETE:
+ case OFPFC_DELETE_STRICT:
+ ofproto->n_delete++;
+ break;
+ }
+
+ now = time_msec();
+ if (ofproto->next_op_report == LLONG_MAX) {
+ ofproto->first_op = now;
+ ofproto->next_op_report = MAX(now + 10 * 1000,
+ ofproto->op_backoff);
+ ofproto->op_backoff = ofproto->next_op_report + 60 * 1000;
+ }
+ ofproto->last_op = now;
+
+exit_free_ofpacts:
+ ofpbuf_uninit(&ofpacts);
+exit:
+ return error;
+}
+
+static enum ofperr
+handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
+ const struct ofputil_flow_mod *fm,
+ const struct ofp_header *oh)
+{
+ if (ofproto->n_pending >= 50) {
+ assert(!list_is_empty(&ofproto->pending));
+ return OFPROTO_POSTPONE;
+ }
+
+ switch (fm->command) {
+ case OFPFC_ADD:
+ return add_flow(ofproto, ofconn, fm, oh);
+
+ case OFPFC_MODIFY:
+ return modify_flows_loose(ofproto, ofconn, fm, oh);
+
+ case OFPFC_MODIFY_STRICT:
+ return modify_flow_strict(ofproto, ofconn, fm, oh);
+
+ case OFPFC_DELETE:
+ return delete_flows_loose(ofproto, ofconn, fm, oh);
+
+ case OFPFC_DELETE_STRICT:
+ return delete_flow_strict(ofproto, ofconn, fm, oh);
+
+ default:
+ if (fm->command > 0xff) {
+ VLOG_WARN_RL(&rl, "%s: flow_mod has explicit table_id but "
+ "flow_mod_table_id extension is not enabled",
+ ofproto->name);
+ }
+ return OFPERR_OFPFMFC_BAD_COMMAND;
+ }
+}
+
+static enum ofperr
+handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+ struct nx_role_request *nrr = (struct nx_role_request *) oh;
+ struct nx_role_request *reply;
+ struct ofpbuf *buf;
+ uint32_t role;