+ odp_port_t odp_port) OVS_EXCLUDED(mutex)
+{
+ struct dpif_sflow_port *dsp;
+ int ret;
+
+ ovs_mutex_lock(&mutex);
+ dsp = dpif_sflow_find_port(ds, odp_port);
+ ret = dsp ? SFL_DS_INDEX(dsp->dsi) : 0;
+ ovs_mutex_unlock(&mutex);
+ return ret;
+}
+
+static void
+dpif_sflow_tunnel_v4(uint8_t tunnel_ipproto,
+ const struct flow_tnl *tunnel,
+ SFLSampled_ipv4 *ipv4)
+
+{
+ ipv4->protocol = tunnel_ipproto;
+ ipv4->tos = tunnel->ip_tos;
+ ipv4->src_ip.addr = (OVS_FORCE uint32_t) tunnel->ip_src;
+ ipv4->dst_ip.addr = (OVS_FORCE uint32_t) tunnel->ip_dst;
+ ipv4->src_port = (OVS_FORCE uint16_t) tunnel->tp_src;
+ ipv4->dst_port = (OVS_FORCE uint16_t) tunnel->tp_dst;
+}
+
+static void
+dpif_sflow_push_mpls_lse(struct dpif_sflow_actions *sflow_actions,
+ ovs_be32 lse)
+{
+ if (sflow_actions->mpls_stack_depth >= FLOW_MAX_MPLS_LABELS) {
+ sflow_actions->mpls_err = true;
+ return;
+ }
+
+ /* Record the new lse in host-byte-order. */
+ /* BOS flag will be fixed later when we send stack to sFlow library. */
+ sflow_actions->mpls_lse[sflow_actions->mpls_stack_depth++] = ntohl(lse);
+}
+
+static void
+dpif_sflow_pop_mpls_lse(struct dpif_sflow_actions *sflow_actions)
+{
+ if (sflow_actions->mpls_stack_depth == 0) {
+ sflow_actions->mpls_err = true;
+ return;
+ }
+ sflow_actions->mpls_stack_depth--;
+}
+
+static void
+dpif_sflow_set_mpls(struct dpif_sflow_actions *sflow_actions,
+ const struct ovs_key_mpls *mpls_key, int n)
+{
+ int ii;
+ if (n > FLOW_MAX_MPLS_LABELS) {
+ sflow_actions->mpls_err = true;
+ return;
+ }
+
+ for (ii = 0; ii < n; ii++) {
+ /* Reverse stack order, and use host-byte-order for each lse. */
+ sflow_actions->mpls_lse[n - ii - 1] = ntohl(mpls_key[ii].mpls_lse);
+ }
+ sflow_actions->mpls_stack_depth = n;
+}
+
+static void
+sflow_read_tnl_push_action(const struct nlattr *attr,
+ struct dpif_sflow_actions *sflow_actions)
+{
+ /* Modeled on lib/odp-util.c: format_odp_tnl_push_header */
+ const struct ovs_action_push_tnl *data = nl_attr_get(attr);
+ const struct eth_header *eth = (const struct eth_header *) data->header;
+ const struct ip_header *ip
+ = ALIGNED_CAST(const struct ip_header *, eth + 1);
+
+ sflow_actions->out_port = u32_to_odp(data->out_port);
+
+ /* Ethernet. */
+ /* TODO: SFlow does not currently define a MAC-in-MAC
+ * encapsulation structure. We could use an extension
+ * structure to report this.
+ */
+
+ /* IPv4 */
+ /* Cannot assume alignment so just use memcpy. */
+ sflow_actions->tunnel.ip_src = get_16aligned_be32(&ip->ip_src);
+ sflow_actions->tunnel.ip_dst = get_16aligned_be32(&ip->ip_dst);
+ sflow_actions->tunnel.ip_tos = ip->ip_tos;
+ sflow_actions->tunnel.ip_ttl = ip->ip_ttl;
+ /* The tnl_push action can supply the ip_protocol too. */
+ sflow_actions->tunnel_ipproto = ip->ip_proto;
+
+ /* Layer 4 */
+ if (data->tnl_type == OVS_VPORT_TYPE_VXLAN
+ || data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
+ const struct udp_header *udp = (const struct udp_header *) (ip + 1);
+ sflow_actions->tunnel.tp_src = udp->udp_src;
+ sflow_actions->tunnel.tp_dst = udp->udp_dst;
+
+ if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
+ const struct vxlanhdr *vxh = (const struct vxlanhdr *) (udp + 1);
+ uint64_t tun_id = ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8;
+ sflow_actions->tunnel.tun_id = htonll(tun_id);
+ } else {
+ const struct genevehdr *gnh = (const struct genevehdr *) (udp + 1);
+ uint64_t tun_id = ntohl(get_16aligned_be32(&gnh->vni)) >> 8;
+ sflow_actions->tunnel.tun_id = htonll(tun_id);
+ }
+ } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
+ const void *l4 = ip + 1;
+ const struct gre_base_hdr *greh = (const struct gre_base_hdr *) l4;
+ ovs_16aligned_be32 *options = (ovs_16aligned_be32 *)(greh + 1);
+ if (greh->flags & htons(GRE_CSUM)) {
+ options++;
+ }
+ if (greh->flags & htons(GRE_KEY)) {
+ uint64_t tun_id = ntohl(get_16aligned_be32(options));
+ sflow_actions->tunnel.tun_id = htonll(tun_id);
+ }
+ }
+}
+
+static void
+sflow_read_set_action(const struct nlattr *attr,
+ struct dpif_sflow_actions *sflow_actions)
+{
+ enum ovs_key_attr type = nl_attr_type(attr);
+ switch (type) {
+ case OVS_KEY_ATTR_ENCAP:
+ if (++sflow_actions->encap_depth > 1) {
+ /* Do not handle multi-encap for now. */
+ sflow_actions->tunnel_err = true;
+ } else {
+ dpif_sflow_read_actions(NULL,
+ nl_attr_get(attr), nl_attr_get_size(attr),
+ sflow_actions);
+ }
+ break;
+ case OVS_KEY_ATTR_PRIORITY:
+ case OVS_KEY_ATTR_SKB_MARK:
+ case OVS_KEY_ATTR_DP_HASH:
+ case OVS_KEY_ATTR_RECIRC_ID:
+ break;
+
+ case OVS_KEY_ATTR_TUNNEL: {
+ if (++sflow_actions->encap_depth > 1) {
+ /* Do not handle multi-encap for now. */
+ sflow_actions->tunnel_err = true;
+ } else {
+ if (odp_tun_key_from_attr(attr, false, &sflow_actions->tunnel)
+ == ODP_FIT_ERROR) {
+ /* Tunnel parsing error. */
+ sflow_actions->tunnel_err = true;
+ }
+ }
+ break;
+ }
+
+ case OVS_KEY_ATTR_IN_PORT:
+ case OVS_KEY_ATTR_ETHERNET:
+ case OVS_KEY_ATTR_VLAN:
+ break;
+
+ case OVS_KEY_ATTR_MPLS: {
+ const struct ovs_key_mpls *mpls_key = nl_attr_get(attr);
+ size_t size = nl_attr_get_size(attr);
+ dpif_sflow_set_mpls(sflow_actions, mpls_key, size / sizeof *mpls_key);
+ break;
+ }
+
+ case OVS_KEY_ATTR_ETHERTYPE:
+ case OVS_KEY_ATTR_IPV4:
+ if (sflow_actions->encap_depth == 1) {
+ const struct ovs_key_ipv4 *key = nl_attr_get(attr);
+ if (key->ipv4_src) {
+ sflow_actions->tunnel.ip_src = key->ipv4_src;
+ }
+ if (key->ipv4_dst) {
+ sflow_actions->tunnel.ip_dst = key->ipv4_dst;
+ }
+ if (key->ipv4_proto) {
+ sflow_actions->tunnel_ipproto = key->ipv4_proto;
+ }
+ if (key->ipv4_tos) {
+ sflow_actions->tunnel.ip_tos = key->ipv4_tos;
+ }
+ if (key->ipv4_ttl) {
+ sflow_actions->tunnel.ip_tos = key->ipv4_ttl;
+ }
+ }
+ break;
+
+ case OVS_KEY_ATTR_IPV6:
+ /* TODO: parse IPv6 encap. */
+ break;
+
+ /* These have the same structure and format. */
+ case OVS_KEY_ATTR_TCP:
+ case OVS_KEY_ATTR_UDP:
+ case OVS_KEY_ATTR_SCTP:
+ if (sflow_actions->encap_depth == 1) {
+ const struct ovs_key_tcp *key = nl_attr_get(attr);
+ if (key->tcp_src) {
+ sflow_actions->tunnel.tp_src = key->tcp_src;
+ }
+ if (key->tcp_dst) {
+ sflow_actions->tunnel.tp_dst = key->tcp_dst;
+ }
+ }
+ break;
+
+ case OVS_KEY_ATTR_TCP_FLAGS:
+ case OVS_KEY_ATTR_ICMP:
+ case OVS_KEY_ATTR_ICMPV6:
+ case OVS_KEY_ATTR_ARP:
+ case OVS_KEY_ATTR_ND:
+ case OVS_KEY_ATTR_CT_STATE:
+ case OVS_KEY_ATTR_CT_ZONE:
+ case OVS_KEY_ATTR_CT_MARK:
+ case OVS_KEY_ATTR_CT_LABELS:
+ case OVS_KEY_ATTR_UNSPEC:
+ case __OVS_KEY_ATTR_MAX:
+ default:
+ break;
+ }
+}
+
+static void
+dpif_sflow_capture_input_mpls(const struct flow *flow,
+ struct dpif_sflow_actions *sflow_actions)