+handle_ofpt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
+{
+ struct ofp_match orig_match;
+ struct ofp_flow_mod *ofm;
+ struct flow_mod fm;
+ struct ofpbuf b;
+ int error;
+
+ b.data = oh;
+ b.size = ntohs(oh->length);
+
+ /* Dissect the message. */
+ ofm = ofpbuf_try_pull(&b, sizeof *ofm);
+ if (!ofm) {
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+ }
+ error = ofputil_pull_actions(&b, b.size, &fm.actions, &fm.n_actions);
+ if (error) {
+ return error;
+ }
+
+ /* Normalize ofm->match. If normalization actually changes anything, then
+ * log the differences. */
+ ofm->match.pad1[0] = ofm->match.pad2[0] = 0;
+ orig_match = ofm->match;
+ normalize_match(&ofm->match);
+ if (memcmp(&ofm->match, &orig_match, sizeof orig_match)) {
+ static struct vlog_rate_limit normal_rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ if (!VLOG_DROP_INFO(&normal_rl)) {
+ char *old = ofp_match_to_literal_string(&orig_match);
+ char *new = ofp_match_to_literal_string(&ofm->match);
+ VLOG_INFO("%s: normalization changed ofp_match, details:",
+ rconn_get_name(ofconn->rconn));
+ VLOG_INFO(" pre: %s", old);
+ VLOG_INFO("post: %s", new);
+ free(old);
+ free(new);
+ }
+ }
+
+ /* Translate the message. */
+ cls_rule_from_match(&ofm->match, ntohs(ofm->priority), ofconn->flow_format,
+ ofm->cookie, &fm.cr);
+ fm.cookie = ofm->cookie;
+ fm.command = ntohs(ofm->command);
+ fm.idle_timeout = ntohs(ofm->idle_timeout);
+ fm.hard_timeout = ntohs(ofm->hard_timeout);
+ fm.buffer_id = ntohl(ofm->buffer_id);
+ fm.out_port = ntohs(ofm->out_port);
+ fm.flags = ntohs(ofm->flags);
+
+ /* Execute the command. */
+ return flow_mod_core(ofconn, &fm);
+}
+
+static int
+handle_nxt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh)
+{
+ struct nx_flow_mod *nfm;
+ struct flow_mod fm;
+ struct ofpbuf b;
+ int error;
+
+ b.data = oh;
+ b.size = ntohs(oh->length);
+
+ /* Dissect the message. */
+ nfm = ofpbuf_try_pull(&b, sizeof *nfm);
+ if (!nfm) {
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+ }
+ error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
+ &fm.cr);
+ if (error) {
+ return error;
+ }
+ error = ofputil_pull_actions(&b, b.size, &fm.actions, &fm.n_actions);
+ if (error) {
+ return error;
+ }
+
+ /* Translate the message. */
+ fm.cookie = nfm->cookie;
+ fm.command = ntohs(nfm->command);
+ fm.idle_timeout = ntohs(nfm->idle_timeout);
+ fm.hard_timeout = ntohs(nfm->hard_timeout);
+ fm.buffer_id = ntohl(nfm->buffer_id);
+ fm.out_port = ntohs(nfm->out_port);
+ fm.flags = ntohs(nfm->flags);
+
+ /* Execute the command. */
+ return flow_mod_core(ofconn, &fm);
+}
+
+static int
+handle_tun_id_from_cookie(struct ofconn *ofconn, struct nxt_tun_id_cookie *msg)
+{
+ int error;
+
+ error = check_ofp_message(&msg->header, OFPT_VENDOR, sizeof *msg);
+ if (error) {
+ return error;
+ }
+
+ ofconn->flow_format = msg->set ? NXFF_TUN_ID_FROM_COOKIE : NXFF_OPENFLOW10;
+ return 0;
+}
+
+static int
+handle_role_request(struct ofconn *ofconn, struct nicira_header *msg)
+{
+ struct nx_role_request *nrr;
+ struct nx_role_request *reply;
+ struct ofpbuf *buf;
+ uint32_t role;
+
+ if (ntohs(msg->header.length) != sizeof *nrr) {
+ VLOG_WARN_RL(&rl, "received role request of length %u (expected %zu)",
+ ntohs(msg->header.length), sizeof *nrr);
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+ }
+ nrr = (struct nx_role_request *) msg;
+
+ if (ofconn->type != OFCONN_PRIMARY) {
+ VLOG_WARN_RL(&rl, "ignoring role request on non-controller "
+ "connection");
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
+ }
+
+ role = ntohl(nrr->role);
+ if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
+ && role != NX_ROLE_SLAVE) {
+ VLOG_WARN_RL(&rl, "received request for unknown role %"PRIu32, role);
+
+ /* There's no good error code for this. */
+ return ofp_mkerr(OFPET_BAD_REQUEST, -1);
+ }
+
+ if (role == NX_ROLE_MASTER) {
+ struct ofconn *other;
+
+ HMAP_FOR_EACH (other, hmap_node, &ofconn->ofproto->controllers) {
+ if (other->role == NX_ROLE_MASTER) {
+ other->role = NX_ROLE_SLAVE;
+ }
+ }
+ }
+ ofconn->role = role;
+
+ reply = make_nxmsg_xid(sizeof *reply, NXT_ROLE_REPLY, msg->header.xid,
+ &buf);
+ reply->role = htonl(role);
+ queue_tx(buf, ofconn, ofconn->reply_counter);
+
+ return 0;
+}
+
+static int
+handle_nxt_set_flow_format(struct ofconn *ofconn,
+ struct nxt_set_flow_format *msg)
+{
+ uint32_t format;
+ int error;
+
+ error = check_ofp_message(&msg->header, OFPT_VENDOR, sizeof *msg);
+ if (error) {
+ return error;
+ }
+
+ format = ntohl(msg->format);
+ if (format == NXFF_OPENFLOW10
+ || format == NXFF_TUN_ID_FROM_COOKIE
+ || format == NXFF_NXM) {
+ ofconn->flow_format = format;
+ return 0;
+ } else {
+ return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
+ }
+}
+
+static int
+handle_vendor(struct ofconn *ofconn, void *msg)