+\f
+/* Returns the correct length of the payload for a flow key attribute of the
+ * specified 'type', or -1 if 'type' is unknown. */
+static int
+odp_flow_key_attr_len(uint16_t type)
+{
+ if (type > ODP_KEY_ATTR_MAX) {
+ return -1;
+ }
+
+ switch ((enum odp_key_type) type) {
+ case ODP_KEY_ATTR_TUN_ID: return 8;
+ case ODP_KEY_ATTR_IN_PORT: return 4;
+ case ODP_KEY_ATTR_ETHERNET: return sizeof(struct odp_key_ethernet);
+ case ODP_KEY_ATTR_8021Q: return sizeof(struct odp_key_8021q);
+ case ODP_KEY_ATTR_ETHERTYPE: return 2;
+ case ODP_KEY_ATTR_IPV4: return sizeof(struct odp_key_ipv4);
+ case ODP_KEY_ATTR_IPV6: return sizeof(struct odp_key_ipv6);
+ case ODP_KEY_ATTR_TCP: return sizeof(struct odp_key_tcp);
+ case ODP_KEY_ATTR_UDP: return sizeof(struct odp_key_udp);
+ case ODP_KEY_ATTR_ICMP: return sizeof(struct odp_key_icmp);
+ case ODP_KEY_ATTR_ICMPV6: return sizeof(struct odp_key_icmpv6);
+ case ODP_KEY_ATTR_ARP: return sizeof(struct odp_key_arp);
+ case ODP_KEY_ATTR_ND: return sizeof(struct odp_key_nd);
+
+ case ODP_KEY_ATTR_UNSPEC:
+ case __ODP_KEY_ATTR_MAX:
+ return -1;
+ }
+
+ return -1;
+}
+
+
+static void
+format_generic_odp_key(const struct nlattr *a, struct ds *ds)
+{
+ size_t len = nl_attr_get_size(a);
+
+ ds_put_format(ds, "key%"PRId16, nl_attr_type(a));
+ if (len) {
+ const uint8_t *unspec;
+ unsigned int i;
+
+ unspec = nl_attr_get(a);
+ for (i = 0; i < len; i++) {
+ ds_put_char(ds, i ? ' ': '(');
+ ds_put_format(ds, "%02x", unspec[i]);
+ }
+ ds_put_char(ds, ')');
+ }
+}
+
+static void
+format_odp_key_attr(const struct nlattr *a, struct ds *ds)
+{
+ const struct odp_key_ethernet *eth_key;
+ const struct odp_key_8021q *q_key;
+ const struct odp_key_ipv4 *ipv4_key;
+ const struct odp_key_ipv6 *ipv6_key;
+ const struct odp_key_tcp *tcp_key;
+ const struct odp_key_udp *udp_key;
+ const struct odp_key_icmp *icmp_key;
+ const struct odp_key_icmpv6 *icmpv6_key;
+ const struct odp_key_arp *arp_key;
+ const struct odp_key_nd *nd_key;
+
+ if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) {
+ ds_put_format(ds, "bad length %zu, expected %d for: ",
+ nl_attr_get_size(a),
+ odp_flow_key_attr_len(nl_attr_type(a)));
+ format_generic_odp_key(a, ds);
+ return;
+ }
+
+ switch (nl_attr_type(a)) {
+ case ODP_KEY_ATTR_TUN_ID:
+ ds_put_format(ds, "tun_id(%#"PRIx64")", ntohll(nl_attr_get_be64(a)));
+ break;
+
+ case ODP_KEY_ATTR_IN_PORT:
+ ds_put_format(ds, "in_port(%"PRIu32")", nl_attr_get_u32(a));
+ break;
+
+ case ODP_KEY_ATTR_ETHERNET:
+ eth_key = nl_attr_get(a);
+ ds_put_format(ds, "eth(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")",
+ ETH_ADDR_ARGS(eth_key->eth_src),
+ ETH_ADDR_ARGS(eth_key->eth_dst));
+ break;
+
+ case ODP_KEY_ATTR_8021Q:
+ q_key = nl_attr_get(a);
+ ds_put_cstr(ds, "vlan(");
+ if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) {
+ ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(q_key->q_tpid));
+ }
+ ds_put_format(ds, "vid%"PRIu16",pcp%d)",
+ vlan_tci_to_vid(q_key->q_tci),
+ vlan_tci_to_pcp(q_key->q_tci));
+ break;
+
+ case ODP_KEY_ATTR_ETHERTYPE:
+ ds_put_format(ds, "eth_type(0x%04"PRIx16")",
+ ntohs(nl_attr_get_be16(a)));
+ break;
+
+ case ODP_KEY_ATTR_IPV4:
+ ipv4_key = nl_attr_get(a);
+ ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT","
+ "proto=%"PRId8",tos=%"PRIu8")",
+ IP_ARGS(&ipv4_key->ipv4_src),
+ IP_ARGS(&ipv4_key->ipv4_dst),
+ ipv4_key->ipv4_proto, ipv4_key->ipv4_tos);
+ break;
+
+ case ODP_KEY_ATTR_IPV6: {
+ 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, "ipv6(src=%s,dst=%s,proto=%"PRId8",tos=%"PRIu8")",
+ src_str, dst_str, ipv6_key->ipv6_proto,
+ ipv6_key->ipv6_tos);
+ break;
+ }
+
+ case ODP_KEY_ATTR_TCP:
+ tcp_key = nl_attr_get(a);
+ ds_put_format(ds, "tcp(src=%"PRIu16",dst=%"PRIu16")",
+ ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst));
+ break;
+
+ case ODP_KEY_ATTR_UDP:
+ udp_key = nl_attr_get(a);
+ ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16")",
+ ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst));
+ break;
+
+ case ODP_KEY_ATTR_ICMP:
+ icmp_key = nl_attr_get(a);
+ ds_put_format(ds, "icmp(type=%"PRIu8",code=%"PRIu8")",
+ icmp_key->icmp_type, icmp_key->icmp_code);
+ break;
+
+ case ODP_KEY_ATTR_ICMPV6:
+ icmpv6_key = nl_attr_get(a);
+ ds_put_format(ds, "icmpv6(type=%"PRIu8",code=%"PRIu8")",
+ icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
+ break;
+
+ case ODP_KEY_ATTR_ARP:
+ arp_key = nl_attr_get(a);
+ ds_put_format(ds, "arp(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 ODP_KEY_ATTR_ND: {
+ char target[INET6_ADDRSTRLEN];
+
+ nd_key = nl_attr_get(a);
+ inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target);
+
+ ds_put_format(ds, "nd(target=%s", target);
+ if (!eth_addr_is_zero(nd_key->nd_sll)) {
+ ds_put_format(ds, ",sll="ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(nd_key->nd_sll));
+ }
+ if (!eth_addr_is_zero(nd_key->nd_tll)) {
+ ds_put_format(ds, ",tll="ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(nd_key->nd_tll));
+ }
+ ds_put_char(ds, ')');
+ break;
+ }
+
+ default:
+ format_generic_odp_key(a, ds);
+ break;
+ }
+}