+static int
+tunnel_key_attr_len(int type)
+{
+ switch (type) {
+ case OVS_TUNNEL_KEY_ATTR_ID: return 8;
+ case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: return 4;
+ case OVS_TUNNEL_KEY_ATTR_IPV4_DST: return 4;
+ case OVS_TUNNEL_KEY_ATTR_TOS: return 1;
+ case OVS_TUNNEL_KEY_ATTR_TTL: return 1;
+ case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: return 0;
+ case OVS_TUNNEL_KEY_ATTR_CSUM: return 0;
+ case __OVS_TUNNEL_KEY_ATTR_MAX:
+ return -1;
+ }
+ return -1;
+}
+
+enum odp_key_fitness
+odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+{
+ unsigned int left;
+ const struct nlattr *a;
+ bool ttl = false;
+ bool unknown = false;
+
+ NL_NESTED_FOR_EACH(a, left, attr) {
+ uint16_t type = nl_attr_type(a);
+ size_t len = nl_attr_get_size(a);
+ int expected_len = tunnel_key_attr_len(type);
+
+ if (len != expected_len && expected_len >= 0) {
+ return ODP_FIT_ERROR;
+ }
+
+ switch (type) {
+ case OVS_TUNNEL_KEY_ATTR_ID:
+ tun->tun_id = nl_attr_get_be64(a);
+ tun->flags |= FLOW_TNL_F_KEY;
+ break;
+ case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
+ tun->ip_src = nl_attr_get_be32(a);
+ break;
+ case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
+ tun->ip_dst = nl_attr_get_be32(a);
+ break;
+ case OVS_TUNNEL_KEY_ATTR_TOS:
+ tun->ip_tos = nl_attr_get_u8(a);
+ break;
+ case OVS_TUNNEL_KEY_ATTR_TTL:
+ tun->ip_ttl = nl_attr_get_u8(a);
+ ttl = true;
+ break;
+ case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
+ tun->flags |= FLOW_TNL_F_DONT_FRAGMENT;
+ break;
+ case OVS_TUNNEL_KEY_ATTR_CSUM:
+ tun->flags |= FLOW_TNL_F_CSUM;
+ break;
+ default:
+ /* Allow this to show up as unexpected, if there are unknown
+ * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
+ unknown = true;
+ break;
+ }
+ }
+
+ if (!ttl) {
+ return ODP_FIT_ERROR;
+ }
+ if (unknown) {
+ return ODP_FIT_TOO_MUCH;
+ }
+ return ODP_FIT_PERFECT;
+}
+
+static void
+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);
+
+ /* tun_id != 0 without FLOW_TNL_F_KEY is valid if tun_key is a mask. */
+ if (tun_key->tun_id || 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);