+/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
+ * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
+ * number rather than a datapath port number). Instead, if 'odp_in_port'
+ * is anything other than ODPP_NONE, it is included in 'buf' as the input
+ * port.
+ *
+ * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
+ * capable of being expanded to allow for that much space. */
+void
+odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
+ const struct flow *mask, odp_port_t odp_in_port)
+{
+ odp_flow_key_from_flow__(buf, flow, mask, odp_in_port, SIZE_MAX, false);
+}
+
+/* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
+ * 'buf'. 'flow' is used as a template to determine how to interpret
+ * 'mask'. For example, the 'dl_type' of 'mask' describes the mask, but
+ * it doesn't indicate whether the other fields should be interpreted as
+ * ARP, IPv4, IPv6, etc.
+ *
+ * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
+ * capable of being expanded to allow for that much space. */
+void
+odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
+ const struct flow *flow, uint32_t odp_in_port_mask,
+ size_t max_mpls_depth)
+{
+ odp_flow_key_from_flow__(buf, flow, mask,
+ u32_to_odp(odp_in_port_mask), max_mpls_depth, true);
+}
+
+/* Generate ODP flow key from the given packet metadata */
+void
+odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md)
+{
+ nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
+
+ if (md->tunnel.ip_dst) {
+ tun_key_to_attr(buf, &md->tunnel);
+ }
+
+ nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark);
+
+ /* Add an ingress port attribute if 'odp_in_port' is not the magical
+ * value "ODPP_NONE". */
+ if (md->in_port.odp_port != ODPP_NONE) {
+ nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port.odp_port);
+ }
+}
+
+/* Generate packet metadata from the given ODP flow key. */
+void
+odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
+ struct pkt_metadata *md)
+{
+ const struct nlattr *nla;
+ size_t left;
+ uint32_t wanted_attrs = 1u << OVS_KEY_ATTR_PRIORITY |
+ 1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
+ 1u << OVS_KEY_ATTR_IN_PORT;
+
+ *md = PKT_METADATA_INITIALIZER(ODPP_NONE);
+
+ NL_ATTR_FOR_EACH (nla, left, key, key_len) {
+ uint16_t type = nl_attr_type(nla);
+ size_t len = nl_attr_get_size(nla);
+ int expected_len = odp_flow_key_attr_len(type);
+
+ if (len != expected_len && expected_len >= 0) {
+ continue;
+ }
+
+ switch (type) {
+ case OVS_KEY_ATTR_RECIRC_ID:
+ md->recirc_id = nl_attr_get_u32(nla);
+ wanted_attrs &= ~(1u << OVS_KEY_ATTR_RECIRC_ID);
+ break;
+ case OVS_KEY_ATTR_DP_HASH:
+ md->dp_hash = nl_attr_get_u32(nla);
+ wanted_attrs &= ~(1u << OVS_KEY_ATTR_DP_HASH);
+ break;
+ case OVS_KEY_ATTR_PRIORITY:
+ md->skb_priority = nl_attr_get_u32(nla);
+ wanted_attrs &= ~(1u << OVS_KEY_ATTR_PRIORITY);
+ break;
+ case OVS_KEY_ATTR_SKB_MARK:
+ md->pkt_mark = nl_attr_get_u32(nla);
+ wanted_attrs &= ~(1u << OVS_KEY_ATTR_SKB_MARK);
+ break;
+ case OVS_KEY_ATTR_TUNNEL: {
+ enum odp_key_fitness res;
+
+ res = odp_tun_key_from_attr(nla, &md->tunnel);
+ if (res == ODP_FIT_ERROR) {
+ memset(&md->tunnel, 0, sizeof md->tunnel);
+ } else if (res == ODP_FIT_PERFECT) {
+ wanted_attrs &= ~(1u << OVS_KEY_ATTR_TUNNEL);
+ }
+ break;
+ }
+ case OVS_KEY_ATTR_IN_PORT:
+ md->in_port.odp_port = nl_attr_get_odp_port(nla);
+ wanted_attrs &= ~(1u << OVS_KEY_ATTR_IN_PORT);
+ break;
+ default:
+ break;
+ }
+
+ if (!wanted_attrs) {
+ return; /* Have everything. */
+ }
+ }
+}
+