+tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
+{
+ size_t tun_key_ofs;
+
+ tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
+
+ if (tun_key->flags & FLOW_TNL_F_KEY) {
+ nl_msg_put_be64(a, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id);
+ }
+ if (tun_key->ip_src) {
+ nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ip_src);
+ }
+ if (tun_key->ip_dst) {
+ nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ip_dst);
+ }
+ if (tun_key->ip_tos) {
+ nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ip_tos);
+ }
+ nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ip_ttl);
+ if (tun_key->flags & FLOW_TNL_F_DONT_FRAGMENT) {
+ nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT);
+ }
+ if (tun_key->flags & FLOW_TNL_F_CSUM) {
+ nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM);
+ }
+
+ nl_msg_end_nested(a, tun_key_ofs);
+}
+
+static bool
+odp_mask_attr_is_wildcard(const struct nlattr *ma)
+{
+ return is_all_zeros(nl_attr_get(ma), nl_attr_get_size(ma));
+}
+
+static bool
+odp_mask_attr_is_exact(const struct nlattr *ma)
+{
+ bool is_exact = false;
+ enum ovs_key_attr attr = nl_attr_type(ma);
+
+ if (attr == OVS_KEY_ATTR_TUNNEL) {
+ /* XXX this is a hack for now. Should change
+ * the exact match dection to per field
+ * instead of per attribute.
+ */
+ struct flow_tnl tun_mask;
+ memset(&tun_mask, 0, sizeof tun_mask);
+ odp_tun_key_from_attr(ma, &tun_mask);
+ if (tun_mask.flags == (FLOW_TNL_F_KEY
+ | FLOW_TNL_F_DONT_FRAGMENT
+ | FLOW_TNL_F_CSUM)) {
+ /* The flags are exact match, check the remaining fields. */
+ tun_mask.flags = 0xffff;
+ is_exact = is_all_ones((uint8_t *)&tun_mask,
+ offsetof(struct flow_tnl, ip_ttl));
+ }
+ } else {
+ is_exact = is_all_ones(nl_attr_get(ma), nl_attr_get_size(ma));
+ }
+
+ return is_exact;
+}
+
+void
+odp_portno_names_set(struct hmap *portno_names, odp_port_t port_no,
+ char *port_name)
+{
+ struct odp_portno_names *odp_portno_names;
+
+ odp_portno_names = xmalloc(sizeof *odp_portno_names);
+ odp_portno_names->port_no = port_no;
+ odp_portno_names->name = xstrdup(port_name);
+ hmap_insert(portno_names, &odp_portno_names->hmap_node,
+ hash_odp_port(port_no));
+}
+
+static char *
+odp_portno_names_get(const struct hmap *portno_names, odp_port_t port_no)
+{
+ struct odp_portno_names *odp_portno_names;
+
+ HMAP_FOR_EACH_IN_BUCKET (odp_portno_names, hmap_node,
+ hash_odp_port(port_no), portno_names) {
+ if (odp_portno_names->port_no == port_no) {
+ return odp_portno_names->name;
+ }
+ }
+ return NULL;
+}
+
+void
+odp_portno_names_destroy(struct hmap *portno_names)
+{
+ struct odp_portno_names *odp_portno_names, *odp_portno_names_next;
+ HMAP_FOR_EACH_SAFE (odp_portno_names, odp_portno_names_next,
+ hmap_node, portno_names) {
+ hmap_remove(portno_names, &odp_portno_names->hmap_node);
+ free(odp_portno_names->name);
+ free(odp_portno_names);
+ }
+}
+
+static void
+format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
+ const struct hmap *portno_names, struct ds *ds,
+ bool verbose)
+{
+ struct flow_tnl tun_key;