+ 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;
+ enum ovs_key_attr attr = nl_attr_type(a);
+ char namebuf[OVS_KEY_ATTR_BUFSIZE];
+ int expected_len;
+ bool is_exact;
+
+ is_exact = ma ? odp_mask_attr_is_exact(ma) : true;
+
+ ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf));
+
+ {
+ expected_len = odp_flow_key_attr_len(nl_attr_type(a));
+ if (expected_len != -2) {
+ bool bad_key_len = nl_attr_get_size(a) != expected_len;
+ bool bad_mask_len = ma && nl_attr_get_size(ma) != expected_len;
+
+ if (bad_key_len || bad_mask_len) {
+ if (bad_key_len) {
+ ds_put_format(ds, "(bad key length %"PRIuSIZE", expected %d)(",
+ nl_attr_get_size(a), expected_len);
+ }
+ format_generic_odp_key(a, ds);
+ if (ma) {
+ ds_put_char(ds, '/');
+ if (bad_mask_len) {
+ ds_put_format(ds, "(bad mask length %"PRIuSIZE", expected %d)(",
+ nl_attr_get_size(ma), expected_len);
+ }
+ format_generic_odp_key(ma, ds);
+ }
+ ds_put_char(ds, ')');
+ return;
+ }
+ }
+ }
+
+ ds_put_char(ds, '(');
+ switch (attr) {
+ case OVS_KEY_ATTR_ENCAP:
+ if (ma && nl_attr_get_size(ma) && nl_attr_get_size(a)) {
+ odp_flow_format(nl_attr_get(a), nl_attr_get_size(a),
+ nl_attr_get(ma), nl_attr_get_size(ma), NULL, ds,
+ verbose);
+ } else if (nl_attr_get_size(a)) {
+ odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), NULL, 0, NULL,
+ ds, verbose);
+ }
+ break;
+
+ case OVS_KEY_ATTR_PRIORITY:
+ case OVS_KEY_ATTR_SKB_MARK:
+ case OVS_KEY_ATTR_DP_HASH:
+ case OVS_KEY_ATTR_RECIRC_ID:
+ ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
+ if (!is_exact) {
+ ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
+ }
+ break;
+
+ case OVS_KEY_ATTR_TUNNEL:
+ memset(&tun_key, 0, sizeof tun_key);
+ if (odp_tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
+ ds_put_format(ds, "error");
+ } else if (!is_exact) {
+ struct flow_tnl tun_mask;
+
+ memset(&tun_mask, 0, sizeof tun_mask);
+ odp_tun_key_from_attr(ma, &tun_mask);
+ ds_put_format(ds, "tun_id=%#"PRIx64"/%#"PRIx64
+ ",src="IP_FMT"/"IP_FMT",dst="IP_FMT"/"IP_FMT
+ ",tos=%#"PRIx8"/%#"PRIx8",ttl=%"PRIu8"/%#"PRIx8
+ ",flags(",
+ ntohll(tun_key.tun_id), ntohll(tun_mask.tun_id),
+ IP_ARGS(tun_key.ip_src), IP_ARGS(tun_mask.ip_src),
+ IP_ARGS(tun_key.ip_dst), IP_ARGS(tun_mask.ip_dst),
+ tun_key.ip_tos, tun_mask.ip_tos,
+ tun_key.ip_ttl, tun_mask.ip_ttl);
+
+ format_flags(ds, flow_tun_flag_to_string, tun_key.flags, ',');
+
+ /* XXX This code is correct, but enabling it would break the unit
+ test. Disable it for now until the input parser is fixed.
+
+ ds_put_char(ds, '/');
+ format_flags(ds, flow_tun_flag_to_string, tun_mask.flags, ',');
+ */
+ ds_put_char(ds, ')');
+ } else {
+ ds_put_format(ds, "tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
+ "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
+ ntohll(tun_key.tun_id),
+ IP_ARGS(tun_key.ip_src),
+ IP_ARGS(tun_key.ip_dst),
+ tun_key.ip_tos, tun_key.ip_ttl);
+
+ format_flags(ds, flow_tun_flag_to_string, tun_key.flags, ',');
+ ds_put_char(ds, ')');
+ }
+ break;
+
+ case OVS_KEY_ATTR_IN_PORT:
+ if (portno_names && verbose && is_exact) {
+ char *name = odp_portno_names_get(portno_names,
+ u32_to_odp(nl_attr_get_u32(a)));
+ if (name) {
+ ds_put_format(ds, "%s", name);
+ } else {
+ ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
+ }
+ } else {
+ ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
+ if (!is_exact) {
+ ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
+ }
+ }
+ break;
+
+ case OVS_KEY_ATTR_ETHERNET:
+ if (!is_exact) {
+ const struct ovs_key_ethernet *eth_mask = nl_attr_get(ma);
+ const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src="ETH_ADDR_FMT"/"ETH_ADDR_FMT
+ ",dst="ETH_ADDR_FMT"/"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(eth_key->eth_src),
+ ETH_ADDR_ARGS(eth_mask->eth_src),
+ ETH_ADDR_ARGS(eth_key->eth_dst),
+ ETH_ADDR_ARGS(eth_mask->eth_dst));
+ } else {
+ const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(eth_key->eth_src),
+ ETH_ADDR_ARGS(eth_key->eth_dst));
+ }
+ break;
+
+ case OVS_KEY_ATTR_VLAN:
+ {
+ ovs_be16 vlan_tci = nl_attr_get_be16(a);
+ if (!is_exact) {
+ ovs_be16 mask = nl_attr_get_be16(ma);
+ ds_put_format(ds, "vid=%"PRIu16"/0x%"PRIx16",pcp=%d/0x%x,cfi=%d/%d",
+ vlan_tci_to_vid(vlan_tci),
+ vlan_tci_to_vid(mask),
+ vlan_tci_to_pcp(vlan_tci),
+ vlan_tci_to_pcp(mask),
+ vlan_tci_to_cfi(vlan_tci),
+ vlan_tci_to_cfi(mask));
+ } else {
+ format_vlan_tci(ds, vlan_tci);
+ }
+ }
+ break;
+
+ case OVS_KEY_ATTR_MPLS: {
+ const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
+ const struct ovs_key_mpls *mpls_mask = NULL;
+ size_t size = nl_attr_get_size(a);
+
+ if (!size || size % sizeof *mpls_key) {
+ ds_put_format(ds, "(bad key length %"PRIuSIZE")",
+ nl_attr_get_size(a));
+ return;
+ }
+ if (!is_exact) {
+ mpls_mask = nl_attr_get(ma);
+ if (nl_attr_get_size(a) != nl_attr_get_size(ma)) {
+ ds_put_format(ds, "(key length %"PRIuSIZE" != "
+ "mask length %"PRIuSIZE")",
+ nl_attr_get_size(a), nl_attr_get_size(ma));
+ return;
+ }
+ }
+ format_mpls(ds, mpls_key, mpls_mask, size / sizeof *mpls_key);
+ break;
+ }
+
+ case OVS_KEY_ATTR_ETHERTYPE:
+ ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a)));
+ if (!is_exact) {
+ ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma)));
+ }
+ break;
+
+ case OVS_KEY_ATTR_IPV4:
+ if (!is_exact) {
+ const struct ovs_key_ipv4 *ipv4_key = nl_attr_get(a);
+ const struct ovs_key_ipv4 *ipv4_mask = nl_attr_get(ma);
+
+ ds_put_format(ds, "src="IP_FMT"/"IP_FMT",dst="IP_FMT"/"IP_FMT
+ ",proto=%"PRIu8"/%#"PRIx8",tos=%#"PRIx8"/%#"PRIx8
+ ",ttl=%"PRIu8"/%#"PRIx8",frag=%s/%#"PRIx8,
+ IP_ARGS(ipv4_key->ipv4_src),
+ IP_ARGS(ipv4_mask->ipv4_src),
+ IP_ARGS(ipv4_key->ipv4_dst),
+ IP_ARGS(ipv4_mask->ipv4_dst),
+ ipv4_key->ipv4_proto, ipv4_mask->ipv4_proto,
+ ipv4_key->ipv4_tos, ipv4_mask->ipv4_tos,
+ ipv4_key->ipv4_ttl, ipv4_mask->ipv4_ttl,
+ ovs_frag_type_to_string(ipv4_key->ipv4_frag),
+ ipv4_mask->ipv4_frag);
+ } else {
+ const struct ovs_key_ipv4 *ipv4_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
+ ",tos=%#"PRIx8",ttl=%"PRIu8",frag=%s",
+ IP_ARGS(ipv4_key->ipv4_src),
+ IP_ARGS(ipv4_key->ipv4_dst),
+ ipv4_key->ipv4_proto, ipv4_key->ipv4_tos,
+ ipv4_key->ipv4_ttl,
+ ovs_frag_type_to_string(ipv4_key->ipv4_frag));
+ }
+ break;
+
+ case OVS_KEY_ATTR_IPV6:
+ if (!is_exact) {
+ const struct ovs_key_ipv6 *ipv6_key, *ipv6_mask;
+ char src_str[INET6_ADDRSTRLEN];
+ char dst_str[INET6_ADDRSTRLEN];
+ char src_mask[INET6_ADDRSTRLEN];
+ char dst_mask[INET6_ADDRSTRLEN];
+
+ ipv6_key = nl_attr_get(a);
+ inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
+ inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
+
+ ipv6_mask = nl_attr_get(ma);
+ inet_ntop(AF_INET6, ipv6_mask->ipv6_src, src_mask, sizeof src_mask);
+ inet_ntop(AF_INET6, ipv6_mask->ipv6_dst, dst_mask, sizeof dst_mask);
+
+ ds_put_format(ds, "src=%s/%s,dst=%s/%s,label=%#"PRIx32"/%#"PRIx32
+ ",proto=%"PRIu8"/%#"PRIx8",tclass=%#"PRIx8"/%#"PRIx8
+ ",hlimit=%"PRIu8"/%#"PRIx8",frag=%s/%#"PRIx8,
+ src_str, src_mask, dst_str, dst_mask,
+ ntohl(ipv6_key->ipv6_label),
+ ntohl(ipv6_mask->ipv6_label),
+ ipv6_key->ipv6_proto, ipv6_mask->ipv6_proto,
+ ipv6_key->ipv6_tclass, ipv6_mask->ipv6_tclass,
+ ipv6_key->ipv6_hlimit, ipv6_mask->ipv6_hlimit,
+ ovs_frag_type_to_string(ipv6_key->ipv6_frag),
+ ipv6_mask->ipv6_frag);
+ } else {
+ const struct ovs_key_ipv6 *ipv6_key;
+ char src_str[INET6_ADDRSTRLEN];
+ char dst_str[INET6_ADDRSTRLEN];
+
+ ipv6_key = nl_attr_get(a);
+ inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
+ inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
+
+ ds_put_format(ds, "src=%s,dst=%s,label=%#"PRIx32",proto=%"PRIu8
+ ",tclass=%#"PRIx8",hlimit=%"PRIu8",frag=%s",
+ src_str, dst_str, ntohl(ipv6_key->ipv6_label),
+ ipv6_key->ipv6_proto, ipv6_key->ipv6_tclass,
+ ipv6_key->ipv6_hlimit,
+ ovs_frag_type_to_string(ipv6_key->ipv6_frag));
+ }
+ break;
+
+ case OVS_KEY_ATTR_TCP:
+ if (!is_exact) {
+ const struct ovs_key_tcp *tcp_mask = nl_attr_get(ma);
+ const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+ ",dst=%"PRIu16"/%#"PRIx16,
+ ntohs(tcp_key->tcp_src), ntohs(tcp_mask->tcp_src),
+ ntohs(tcp_key->tcp_dst), ntohs(tcp_mask->tcp_dst));
+ } else {
+ const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
+ ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst));
+ }
+ break;
+
+ case OVS_KEY_ATTR_TCP_FLAGS:
+ ds_put_format(ds, "0x%03"PRIx16, ntohs(nl_attr_get_be16(a)));
+ if (!is_exact) {
+ ds_put_format(ds, "/0x%03"PRIx16, ntohs(nl_attr_get_be16(ma)));
+ }
+ break;
+
+ case OVS_KEY_ATTR_UDP:
+ if (!is_exact) {
+ const struct ovs_key_udp *udp_mask = nl_attr_get(ma);
+ const struct ovs_key_udp *udp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+ ",dst=%"PRIu16"/%#"PRIx16,
+ ntohs(udp_key->udp_src), ntohs(udp_mask->udp_src),
+ ntohs(udp_key->udp_dst), ntohs(udp_mask->udp_dst));
+ } else {
+ const struct ovs_key_udp *udp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
+ ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst));
+ }
+ break;
+
+ case OVS_KEY_ATTR_SCTP:
+ if (ma) {
+ const struct ovs_key_sctp *sctp_mask = nl_attr_get(ma);
+ const struct ovs_key_sctp *sctp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+ ",dst=%"PRIu16"/%#"PRIx16,
+ ntohs(sctp_key->sctp_src), ntohs(sctp_mask->sctp_src),
+ ntohs(sctp_key->sctp_dst), ntohs(sctp_mask->sctp_dst));
+ } else {
+ const struct ovs_key_sctp *sctp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
+ ntohs(sctp_key->sctp_src), ntohs(sctp_key->sctp_dst));
+ }
+ break;
+
+ case OVS_KEY_ATTR_ICMP:
+ if (!is_exact) {
+ const struct ovs_key_icmp *icmp_mask = nl_attr_get(ma);
+ const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "type=%"PRIu8"/%#"PRIx8",code=%"PRIu8"/%#"PRIx8,
+ icmp_key->icmp_type, icmp_mask->icmp_type,
+ icmp_key->icmp_code, icmp_mask->icmp_code);
+ } else {
+ const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "type=%"PRIu8",code=%"PRIu8,
+ icmp_key->icmp_type, icmp_key->icmp_code);
+ }
+ break;
+
+ case OVS_KEY_ATTR_ICMPV6:
+ if (!is_exact) {
+ const struct ovs_key_icmpv6 *icmpv6_mask = nl_attr_get(ma);
+ const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
+
+ ds_put_format(ds, "type=%"PRIu8"/%#"PRIx8",code=%"PRIu8"/%#"PRIx8,
+ icmpv6_key->icmpv6_type, icmpv6_mask->icmpv6_type,
+ icmpv6_key->icmpv6_code, icmpv6_mask->icmpv6_code);
+ } else {
+ const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
+
+ ds_put_format(ds, "type=%"PRIu8",code=%"PRIu8,
+ icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
+ }
+ break;
+
+ case OVS_KEY_ATTR_ARP:
+ if (!is_exact) {
+ const struct ovs_key_arp *arp_mask = nl_attr_get(ma);
+ const struct ovs_key_arp *arp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "sip="IP_FMT"/"IP_FMT",tip="IP_FMT"/"IP_FMT
+ ",op=%"PRIu16"/%#"PRIx16
+ ",sha="ETH_ADDR_FMT"/"ETH_ADDR_FMT
+ ",tha="ETH_ADDR_FMT"/"ETH_ADDR_FMT,
+ IP_ARGS(arp_key->arp_sip),
+ IP_ARGS(arp_mask->arp_sip),
+ IP_ARGS(arp_key->arp_tip),
+ IP_ARGS(arp_mask->arp_tip),
+ ntohs(arp_key->arp_op), ntohs(arp_mask->arp_op),
+ ETH_ADDR_ARGS(arp_key->arp_sha),
+ ETH_ADDR_ARGS(arp_mask->arp_sha),
+ ETH_ADDR_ARGS(arp_key->arp_tha),
+ ETH_ADDR_ARGS(arp_mask->arp_tha));
+ } else {
+ const struct ovs_key_arp *arp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
+ "sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT,
+ IP_ARGS(arp_key->arp_sip), IP_ARGS(arp_key->arp_tip),
+ ntohs(arp_key->arp_op),
+ ETH_ADDR_ARGS(arp_key->arp_sha),
+ ETH_ADDR_ARGS(arp_key->arp_tha));
+ }
+ break;
+
+ case OVS_KEY_ATTR_ND: {
+ const struct ovs_key_nd *nd_key, *nd_mask = NULL;
+ char target[INET6_ADDRSTRLEN];
+
+ nd_key = nl_attr_get(a);
+ if (!is_exact) {
+ nd_mask = nl_attr_get(ma);
+ }
+
+ inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target);
+ ds_put_format(ds, "target=%s", target);
+ if (!is_exact) {
+ inet_ntop(AF_INET6, nd_mask->nd_target, target, sizeof target);
+ ds_put_format(ds, "/%s", target);
+ }