+int
+ofputil_flow_format_from_string(const char *s)
+{
+ return (!strcmp(s, "openflow10") ? NXFF_OPENFLOW10
+ : !strcmp(s, "tun_id_from_cookie") ? NXFF_TUN_ID_FROM_COOKIE
+ : !strcmp(s, "nxm") ? NXFF_NXM
+ : -1);
+}
+
+static bool
+regs_fully_wildcarded(const struct flow_wildcards *wc)
+{
+ int i;
+
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ if (wc->reg_masks[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool
+is_nxm_required(const struct cls_rule *rule, bool cookie_support,
+ ovs_be64 cookie)
+{
+ const struct flow_wildcards *wc = &rule->wc;
+ uint32_t cookie_hi;
+ uint64_t tun_id;
+
+ /* Only NXM supports separately wildcards the Ethernet multicast bit. */
+ if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
+ return true;
+ }
+
+ /* Only NXM supports matching ARP hardware addresses. */
+ if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
+ return true;
+ }
+
+ /* Only NXM supports matching IPv6 traffic. */
+ if (!(wc->wildcards & FWW_DL_TYPE)
+ && (rule->flow.dl_type == htons(ETH_TYPE_IPV6))) {
+ return true;
+ }
+
+ /* Only NXM supports matching registers. */
+ if (!regs_fully_wildcarded(wc)) {
+ return true;
+ }
+
+ switch (wc->tun_id_mask) {
+ case CONSTANT_HTONLL(0):
+ /* Other formats can fully wildcard tun_id. */
+ break;
+
+ case CONSTANT_HTONLL(UINT64_MAX):
+ /* Only NXM supports tunnel ID matching without a cookie. */
+ if (!cookie_support) {
+ return true;
+ }
+
+ /* Only NXM supports 64-bit tunnel IDs. */
+ tun_id = ntohll(rule->flow.tun_id);
+ if (tun_id > UINT32_MAX) {
+ return true;
+ }
+
+ /* Only NXM supports a cookie whose top 32 bits conflict with the
+ * tunnel ID. */
+ cookie_hi = ntohll(cookie) >> 32;
+ if (cookie_hi && cookie_hi != tun_id) {
+ return true;
+ }
+ break;
+
+ default:
+ /* Only NXM supports partial matches on tunnel ID. */
+ return true;
+ }
+
+ /* Other formats can express this rule. */
+ return false;
+}
+
+/* Returns the minimum nx_flow_format to use for sending 'rule' to a switch
+ * (e.g. to add or remove a flow). 'cookie_support' should be true if the
+ * command to be sent includes a flow cookie (as OFPT_FLOW_MOD does, for
+ * example) or false if the command does not (OFPST_FLOW and OFPST_AGGREGATE do
+ * not, for example). If 'cookie_support' is true, then 'cookie' should be the
+ * cookie to be sent; otherwise its value is ignored.
+ *
+ * The "best" flow format is chosen on this basis:
+ *
+ * - It must be capable of expressing the rule. NXFF_OPENFLOW10 flows can't
+ * handle tunnel IDs. NXFF_TUN_ID_FROM_COOKIE flows can't handle registers
+ * or fixing the Ethernet multicast bit, and can't handle tunnel IDs that
+ * conflict with the high 32 bits of the cookie or commands that don't
+ * support cookies.
+ *
+ * - Otherwise, the chosen format should be as backward compatible as
+ * possible. (NXFF_OPENFLOW10 is more backward compatible than
+ * NXFF_TUN_ID_FROM_COOKIE, which is more backward compatible than
+ * NXFF_NXM.)
+ */
+enum nx_flow_format
+ofputil_min_flow_format(const struct cls_rule *rule, bool cookie_support,
+ ovs_be64 cookie)
+{
+ if (is_nxm_required(rule, cookie_support, cookie)) {
+ return NXFF_NXM;
+ } else if (rule->wc.tun_id_mask != htonll(0)) {
+ return NXFF_TUN_ID_FROM_COOKIE;
+ } else {
+ return NXFF_OPENFLOW10;
+ }
+}
+
+/* Returns an OpenFlow message that can be used to set the flow format to
+ * 'flow_format'. */
+struct ofpbuf *
+ofputil_make_set_flow_format(enum nx_flow_format flow_format)
+{
+ struct ofpbuf *msg;
+
+ if (flow_format == NXFF_OPENFLOW10
+ || flow_format == NXFF_TUN_ID_FROM_COOKIE) {
+ struct nxt_tun_id_cookie *tic;
+
+ tic = make_nxmsg(sizeof *tic, NXT_TUN_ID_FROM_COOKIE, &msg);
+ tic->set = flow_format == NXFF_TUN_ID_FROM_COOKIE;
+ } else {
+ struct nxt_set_flow_format *sff;
+
+ sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg);
+ sff->format = htonl(flow_format);
+ }
+
+ return msg;
+}
+