+ 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)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+ 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;
+
+ /* Calulate fitness of outer attributes. */
+ expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
+ (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 (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
+ return ODP_FIT_TOO_LITTLE;
+ }
+ tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
+ 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->vlan_tci = tci;
+ flow->dl_type = htons(0);
+
+ /* 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)) {
+ return ODP_FIT_ERROR;
+ }
+ encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len);
+
+ /* The overall fitness is the worse of the outer and inner attributes. */
+ return MAX(fitness, encap_fitness);
+}
+
+/* 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)
+{
+ const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
+ uint64_t expected_attrs;
+ uint64_t present_attrs;
+ int out_of_range_attr;
+
+ 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->skb_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 = 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 = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]);
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
+ } else {
+ flow->in_port = OVSP_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);
+ }
+ 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)) {
+ return ODP_FIT_ERROR;
+ }
+
+ if (flow->dl_type == htons(ETH_TYPE_VLAN)) {
+ return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len);
+ }
+ return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len);
+}
+
+/* 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_skb_mark_action(const uint32_t skb_mark,
+ struct ofpbuf *odp_actions)
+{
+ commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK, &skb_mark,
+ sizeof(skb_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)
+{
+ if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
+ return;
+ }
+ memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel);
+
+ /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
+ if (flow->tunnel.ip_dst) {
+ 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 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;
+ }
+
+ 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)
+{
+ if (base->vlan_tci == flow->vlan_tci) {
+ return;
+ }
+
+ if (base->vlan_tci & htons(VLAN_CFI)) {
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+ }
+
+ if (flow->vlan_tci & htons(VLAN_CFI)) {
+ struct ovs_action_push_vlan vlan;
+
+ vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
+ vlan.vlan_tci = flow->vlan_tci;
+ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
+ &vlan, sizeof vlan);
+ }
+ base->vlan_tci = flow->vlan_tci;
+}
+
+static void
+commit_mpls_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ if (flow->mpls_lse == base->mpls_lse &&
+ flow->mpls_depth == base->mpls_depth) {
+ return;
+ }
+
+ if (flow->mpls_depth < base->mpls_depth) {
+ if (base->mpls_depth - flow->mpls_depth > 1) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
+ VLOG_WARN_RL(&rl, "Multiple mpls_pop actions reduced to "
+ " a single mpls_pop action");
+ }
+
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, flow->dl_type);
+ } else if (flow->mpls_depth > base->mpls_depth) {
+ struct ovs_action_push_mpls *mpls;
+
+ if (flow->mpls_depth - base->mpls_depth > 1) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
+ VLOG_WARN_RL(&rl, "Multiple mpls_push actions reduced to "
+ " a single mpls_push action");
+ }
+
+ mpls = nl_msg_put_unspec_uninit(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
+ sizeof *mpls);
+ memset(mpls, 0, sizeof *mpls);
+ mpls->mpls_ethertype = flow->dl_type;
+ mpls->mpls_lse = flow->mpls_lse;
+ } else {
+ struct ovs_key_mpls mpls_key;
+
+ mpls_key.mpls_top_lse = flow->mpls_lse;
+ commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
+ &mpls_key, sizeof(mpls_key));
+ }
+
+ base->dl_type = flow->dl_type;
+ base->mpls_lse = flow->mpls_lse;
+ base->mpls_depth = flow->mpls_depth;
+}
+
+static void
+commit_set_ipv4_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ struct ovs_key_ipv4 ipv4_key;
+
+ if (base->nw_src == flow->nw_src &&
+ base->nw_dst == flow->nw_dst &&
+ base->nw_tos == flow->nw_tos &&
+ base->nw_ttl == flow->nw_ttl &&
+ base->nw_frag == flow->nw_frag) {
+ return;
+ }
+
+ ipv4_key.ipv4_src = base->nw_src = flow->nw_src;
+ ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst;
+ ipv4_key.ipv4_tos = base->nw_tos = flow->nw_tos;
+ ipv4_key.ipv4_ttl = base->nw_ttl = flow->nw_ttl;
+ ipv4_key.ipv4_proto = base->nw_proto;
+ ipv4_key.ipv4_frag = ovs_to_odp_frag(base->nw_frag);
+
+ commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4,
+ &ipv4_key, sizeof(ipv4_key));
+}
+
+static void
+commit_set_ipv6_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ struct ovs_key_ipv6 ipv6_key;
+
+ if (ipv6_addr_equals(&base->ipv6_src, &flow->ipv6_src) &&
+ ipv6_addr_equals(&base->ipv6_dst, &flow->ipv6_dst) &&
+ base->ipv6_label == flow->ipv6_label &&
+ base->nw_tos == flow->nw_tos &&
+ base->nw_ttl == flow->nw_ttl &&
+ base->nw_frag == flow->nw_frag) {
+ return;
+ }
+
+ base->ipv6_src = flow->ipv6_src;
+ memcpy(&ipv6_key.ipv6_src, &base->ipv6_src, sizeof(ipv6_key.ipv6_src));
+ base->ipv6_dst = flow->ipv6_dst;
+ memcpy(&ipv6_key.ipv6_dst, &base->ipv6_dst, sizeof(ipv6_key.ipv6_dst));
+
+ ipv6_key.ipv6_label = base->ipv6_label = flow->ipv6_label;
+ ipv6_key.ipv6_tclass = base->nw_tos = flow->nw_tos;
+ ipv6_key.ipv6_hlimit = base->nw_ttl = flow->nw_ttl;
+ ipv6_key.ipv6_proto = base->nw_proto;
+ ipv6_key.ipv6_frag = ovs_to_odp_frag(base->nw_frag);
+
+ commit_set_action(odp_actions, OVS_KEY_ATTR_IPV6,
+ &ipv6_key, sizeof(ipv6_key));
+}
+
+static void
+commit_set_nw_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ /* Check if flow really have an IP header. */
+ if (!flow->nw_proto) {
+ return;
+ }
+
+ if (base->dl_type == htons(ETH_TYPE_IP)) {
+ commit_set_ipv4_action(flow, base, odp_actions);
+ } else if (base->dl_type == htons(ETH_TYPE_IPV6)) {
+ commit_set_ipv6_action(flow, base, odp_actions);
+ }
+}
+
+static void
+commit_set_port_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ if (!is_ip_any(base) || (!base->tp_src && !base->tp_dst)) {
+ return;
+ }
+
+ if (base->tp_src == flow->tp_src &&
+ base->tp_dst == flow->tp_dst) {
+ return;
+ }
+
+ if (flow->nw_proto == IPPROTO_TCP) {
+ struct ovs_key_tcp port_key;
+
+ port_key.tcp_src = base->tp_src = flow->tp_src;
+ port_key.tcp_dst = base->tp_dst = flow->tp_dst;
+
+ commit_set_action(odp_actions, OVS_KEY_ATTR_TCP,
+ &port_key, sizeof(port_key));
+
+ } else if (flow->nw_proto == IPPROTO_UDP) {
+ struct ovs_key_udp port_key;
+
+ port_key.udp_src = base->tp_src = flow->tp_src;
+ port_key.udp_dst = base->tp_dst = flow->tp_dst;
+
+ commit_set_action(odp_actions, OVS_KEY_ATTR_UDP,
+ &port_key, sizeof(port_key));
+ }
+}
+
+static void
+commit_set_priority_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ if (base->skb_priority == flow->skb_priority) {
+ return;
+ }
+ base->skb_priority = flow->skb_priority;
+
+ commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
+ &base->skb_priority, sizeof(base->skb_priority));
+}
+
+static void
+commit_set_skb_mark_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ if (base->skb_mark == flow->skb_mark) {
+ return;
+ }
+ base->skb_mark = flow->skb_mark;
+
+ odp_put_skb_mark_action(base->skb_mark, odp_actions);
+}
+/* If any of the flow key data that ODP actions can modify are different in
+ * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
+ * key from 'base' into 'flow', and then changes 'base' the same way. Does not
+ * commit set_tunnel actions. Users should call commit_odp_tunnel_action()
+ * in addition to this function if needed. */
+void
+commit_odp_actions(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ commit_set_ether_addr_action(flow, base, odp_actions);
+ commit_vlan_action(flow, base, odp_actions);
+ commit_set_nw_action(flow, base, odp_actions);
+ commit_set_port_action(flow, base, odp_actions);
+ /* Commiting MPLS actions should occur after committing nw and port
+ * actions. This is because committing MPLS actions may alter a packet so
+ * that it is no longer IP and thus nw and port actions are no longer valid.
+ */
+ commit_mpls_action(flow, base, odp_actions);
+ commit_set_priority_action(flow, base, odp_actions);
+ commit_set_skb_mark_action(flow, base, odp_actions);