+ if (is_mask && expected_bit != OVS_KEY_ATTR_UNSPEC) {
+ if ((flow->tp_src || flow->tp_dst) && flow->nw_proto != 0xff) {
+ return ODP_FIT_ERROR;
+ } else {
+ expected_attrs |= UINT64_C(1) << expected_bit;
+ }
+ }
+
+done:
+ return check_expectations(present_attrs, out_of_range_attr, expected_attrs,
+ key, key_len);
+}
+
+/* Parse 802.1Q header then encapsulated L3 attributes. */
+static enum odp_key_fitness
+parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+ uint64_t present_attrs, int out_of_range_attr,
+ uint64_t expected_attrs, struct flow *flow,
+ const struct nlattr *key, size_t key_len,
+ const struct flow *src_flow)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ bool is_mask = src_flow != flow;
+
+ const struct nlattr *encap
+ = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)
+ ? attrs[OVS_KEY_ATTR_ENCAP] : NULL);
+ enum odp_key_fitness encap_fitness;
+ enum odp_key_fitness fitness;
+ ovs_be16 tci;
+
+ /* Calculate fitness of outer attributes. */
+ if (!is_mask) {
+ expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
+ (UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
+ } else {
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) {
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP);
+ }
+ }
+ fitness = check_expectations(present_attrs, out_of_range_attr,
+ expected_attrs, key, key_len);
+
+ /* Get the VLAN TCI value. */
+ if (!is_mask && !(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
+ return ODP_FIT_TOO_LITTLE;
+ } else {
+ tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
+ if (!is_mask) {
+ if (tci == htons(0)) {
+ /* Corner case for a truncated 802.1Q header. */
+ if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
+ return ODP_FIT_TOO_MUCH;
+ }
+ return fitness;
+ } else if (!(tci & htons(VLAN_CFI))) {
+ VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
+ "but CFI bit is not set", ntohs(tci));
+ return ODP_FIT_ERROR;
+ }
+ }
+ /* Set vlan_tci.
+ * Remove the TPID from dl_type since it's not the real Ethertype. */
+ flow->dl_type = htons(0);
+ flow->vlan_tci = tci;
+ }
+
+ if (is_mask && !(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) {
+ return fitness;
+ }
+ /* Now parse the encapsulated attributes. */
+ if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
+ attrs, &present_attrs, &out_of_range_attr)) {
+ return ODP_FIT_ERROR;
+ }
+ expected_attrs = 0;
+
+ if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) {
+ return ODP_FIT_ERROR;
+ }
+ encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len,
+ src_flow);
+
+ /* The overall fitness is the worse of the outer and inner attributes. */
+ return MAX(fitness, encap_fitness);
+}
+
+static enum odp_key_fitness
+odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
+ struct flow *flow, const struct flow *src_flow)
+{
+ const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
+ uint64_t expected_attrs;
+ uint64_t present_attrs;
+ int out_of_range_attr;
+ bool is_mask = src_flow != flow;
+
+ memset(flow, 0, sizeof *flow);
+
+ /* Parse attributes. */
+ if (!parse_flow_nlattrs(key, key_len, attrs, &present_attrs,
+ &out_of_range_attr)) {
+ return ODP_FIT_ERROR;
+ }
+ expected_attrs = 0;
+
+ /* Metadata. */
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_PRIORITY)) {
+ flow->skb_priority = nl_attr_get_u32(attrs[OVS_KEY_ATTR_PRIORITY]);
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_PRIORITY;
+ }
+
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK)) {
+ flow->pkt_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_SKB_MARK]);
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK;
+ }
+
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
+ enum odp_key_fitness res;
+
+ res = odp_tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
+ if (res == ODP_FIT_ERROR) {
+ return ODP_FIT_ERROR;
+ } else if (res == ODP_FIT_PERFECT) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL;
+ }
+ }
+
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) {
+ flow->in_port.odp_port
+ = nl_attr_get_odp_port(attrs[OVS_KEY_ATTR_IN_PORT]);
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
+ } else if (!is_mask) {
+ flow->in_port.odp_port = ODPP_NONE;
+ }
+
+ /* Ethernet header. */
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) {
+ const struct ovs_key_ethernet *eth_key;
+
+ eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]);
+ memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN);
+ memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN);
+ if (is_mask) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
+ }
+ }
+ if (!is_mask) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
+ }
+
+ /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */
+ if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow,
+ src_flow)) {
+ return ODP_FIT_ERROR;
+ }
+
+ if ((is_mask && (src_flow->vlan_tci & htons(VLAN_CFI))) ||
+ (!is_mask && src_flow->dl_type == htons(ETH_TYPE_VLAN))) {
+ return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len, src_flow);
+ }
+ if (is_mask) {
+ flow->vlan_tci = htons(0xffff);
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
+ flow->vlan_tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
+ }
+ }
+ return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len, src_flow);
+}
+
+/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow
+ * structure in 'flow'. Returns an ODP_FIT_* value that indicates how well
+ * 'key' fits our expectations for what a flow key should contain.
+ *
+ * The 'in_port' will be the datapath's understanding of the port. The
+ * caller will need to translate with odp_port_to_ofp_port() if the
+ * OpenFlow port is needed.
+ *
+ * This function doesn't take the packet itself as an argument because none of
+ * the currently understood OVS_KEY_ATTR_* attributes require it. Currently,
+ * it is always possible to infer which additional attribute(s) should appear
+ * by looking at the attributes for lower-level protocols, e.g. if the network
+ * protocol in OVS_KEY_ATTR_IPV4 or OVS_KEY_ATTR_IPV6 is IPPROTO_TCP then we
+ * know that a OVS_KEY_ATTR_TCP attribute must appear and that otherwise it
+ * must be absent. */
+enum odp_key_fitness
+odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
+ struct flow *flow)
+{
+ return odp_flow_key_to_flow__(key, key_len, flow, flow);
+}
+
+/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a mask
+ * structure in 'mask'. 'flow' must be a previously translated flow
+ * corresponding to 'mask'. Returns an ODP_FIT_* value that indicates how well
+ * 'key' fits our expectations for what a flow key should contain. */
+enum odp_key_fitness
+odp_flow_key_to_mask(const struct nlattr *key, size_t key_len,
+ struct flow *mask, const struct flow *flow)
+{
+ return odp_flow_key_to_flow__(key, key_len, mask, flow);
+}
+
+/* Returns 'fitness' as a string, for use in debug messages. */
+const char *
+odp_key_fitness_to_string(enum odp_key_fitness fitness)
+{
+ switch (fitness) {
+ case ODP_FIT_PERFECT:
+ return "OK";
+ case ODP_FIT_TOO_MUCH:
+ return "too_much";
+ case ODP_FIT_TOO_LITTLE:
+ return "too_little";
+ case ODP_FIT_ERROR:
+ return "error";
+ default:
+ return "<unknown>";
+ }
+}
+
+/* Appends an OVS_ACTION_ATTR_USERSPACE action to 'odp_actions' that specifies
+ * Netlink PID 'pid'. If 'userdata' is nonnull, adds a userdata attribute
+ * whose contents are the 'userdata_size' bytes at 'userdata' and returns the
+ * offset within 'odp_actions' of the start of the cookie. (If 'userdata' is
+ * null, then the return value is not meaningful.) */
+size_t
+odp_put_userspace_action(uint32_t pid,
+ const void *userdata, size_t userdata_size,
+ struct ofpbuf *odp_actions)
+{
+ size_t userdata_ofs;
+ size_t offset;
+
+ offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_USERSPACE);
+ nl_msg_put_u32(odp_actions, OVS_USERSPACE_ATTR_PID, pid);
+ if (userdata) {
+ userdata_ofs = odp_actions->size + NLA_HDRLEN;
+ nl_msg_put_unspec(odp_actions, OVS_USERSPACE_ATTR_USERDATA,
+ userdata, userdata_size);
+ } else {
+ userdata_ofs = 0;
+ }
+ nl_msg_end_nested(odp_actions, offset);
+
+ return userdata_ofs;
+}
+
+void
+odp_put_tunnel_action(const struct flow_tnl *tunnel,
+ struct ofpbuf *odp_actions)
+{
+ size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
+ tun_key_to_attr(odp_actions, tunnel);
+ nl_msg_end_nested(odp_actions, offset);
+}
+\f
+/* The commit_odp_actions() function and its helpers. */
+
+static void
+commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
+ const void *key, size_t key_size)
+{
+ size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
+ nl_msg_put_unspec(odp_actions, key_type, key, key_size);
+ nl_msg_end_nested(odp_actions, offset);
+}
+
+void
+odp_put_pkt_mark_action(const uint32_t pkt_mark,
+ struct ofpbuf *odp_actions)
+{
+ commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK, &pkt_mark,
+ sizeof(pkt_mark));
+}
+
+/* If any of the flow key data that ODP actions can modify are different in
+ * 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
+ * 'odp_actions' that change the flow tunneling information in key from
+ * 'base->tunnel' into 'flow->tunnel', and then changes 'base->tunnel' in the
+ * same way. In other words, operates the same as commit_odp_actions(), but
+ * only on tunneling information. */
+void
+commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
+ if (flow->tunnel.ip_dst) {
+ if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
+ return;
+ }
+ memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel);
+ odp_put_tunnel_action(&base->tunnel, odp_actions);
+ }
+}
+
+static void
+commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions,
+ struct flow_wildcards *wc)
+{
+ struct ovs_key_ethernet eth_key;
+
+ if (eth_addr_equals(base->dl_src, flow->dl_src) &&
+ eth_addr_equals(base->dl_dst, flow->dl_dst)) {
+ return;
+ }
+
+ memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+ memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+
+ memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
+ memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+
+ memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN);
+ memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN);
+
+ commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET,
+ ð_key, sizeof(eth_key));
+}
+
+static void
+commit_vlan_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+{
+ if (base->vlan_tci == flow->vlan_tci) {
+ return;
+ }
+
+ memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);