[ovs-dev] [PATCH] sFlow export: include standard tunnel structures (for GRE, VXLAN etc.)

Romain Lenglet rlenglet at vmware.com
Wed Oct 9 17:30:17 UTC 2013


Hi Neil,

Thanks for your patch.

I concur with Jesse that making this feature optional is essential.
Tunnel header information is not always desirable.

On Oct 8, 2013, at 10:09 PM, Neil Mckee <neil.mckee at inmon.com> wrote:

> 
> On Oct 7, 2013, at 2:57 PM, Ben Pfaff <blp at nicira.com> wrote:
> 
>> On Sun, Oct 06, 2013 at 10:11:11PM -0700, Neil Mckee wrote:
>>> Please comment on this proposed patch.   It adds the standard
>>> sFlow-TUNNEL structures to the sFlow export,  which will be helpful
>>> for tracing tunneled traffic through a network fabric in real-time.
>>> 
>>> As part of doing that,  it switches over from using odp_port to ofp_port
>>> as the port reference used in dpif-sflow,  which allows some only-for-sflow
>>> code to be removed elsewhere.  It also completely removes the private
>>> port-lookup hash table that dpif-sflow was maintaining and replaces
>>> it with a simple callback to the parent dpif - presenting the parent with
>>> a structure to fill in with just the necessary details about the port.
>>> This makes it  easier for the parent dpif to control what port state is 
>>> exposed to the sFlow module during the processing of a packet-sample.
>>> 
>>> (This same mechanism can now be extended to expose some
>>> bundle/LAG information,  so the standard sFlow-LAG structures can
>>> be exported too,  but that can be in another patch).
>>> 
>>> This patch also cleans up the way sFlow is treated in ofproto-xlate.c, so
>>> that the upcall cookie just contains the essential details and the
>>> sFlow-specific encoding is migrated into ofproto-dpif-sflow.c.
>>> 
>>> Finally a new test for sFlow tunnel structures was added that exercises
>>> the new code.  So 'make check TESTSUITEFLAGS="-k sflow"' now runs
>>> two separate tests.
>>> 
>>> One very specific question is asked in an XXX comment in ofproto-dpif.c,
>>> regarding which lock should be acquired during the new callback.  A little
>>> guidance there would be most appreciated.
>> 
>> This commit increases the size of the sflow userdata (the 'sflow'
>> member in struct user_action_cookie) from 8 bytes to 12.  If the size
>> increase can be avoided, then it is desirable to avoid it, because
>> only recent versions of the Open vSwitch kernel module support
>> userdata longer than 8 bytes.
>> 
>> I'd like to see a full discussion of Jesse's comments.
>> 
>> I see various other minor issues but nothing that will be much trouble.
> 
> It seemed like the easiest way to reduce the cookie to 8 bytes was to have
> two sFlow cookie types:
> 
> USER_ACTION_COOKIE_SFLOW_SINGLEPORT
> USER_ACTION_COOKIE_SFLOW_MULTIPORT
> 
> That way there will still be room in case ofp_port_t goes from 16 to 32 bits.
> 
> If cookie->type were uint8_t you would recover 8 more bits, if ever needed.
> 
> The revised patch is inlined below for your comments.  It now also has the
> LAG export (and a test for it).  It goes out with the periodic counter-push
> -- not with every packet-sample.  This LAG information can be important for
> synthesizing network-wide traffic matrices accurately,  and for detecting
> instability promptly:
> http://sflow.org/sflow_lag.txt
> 
> Unresolved questions are marked with XXX comments.
> 
> Neil
> 
> 
> Add sFlow Tunnel and LAG structures.
> Signed-off-by: Neil McKee<neil.mckee at inmon.com>
> 
> ---
> lib/lacp.c                    |  64 +++++++++
> lib/lacp.h                    |  23 +++
> lib/odp-util.c                |  51 +++++--
> lib/odp-util.h                |  19 ++-
> lib/sflow.h                   |  38 ++++-
> lib/sflow_agent.c             |  15 ++
> lib/sflow_api.h               |   1 +
> lib/sflow_receiver.c          |  19 +++
> manpages.mk                   |   8 +-
> ofproto/ofproto-dpif-sflow.c  | 326 ++++++++++++++++++++++++++----------------
> ofproto/ofproto-dpif-sflow.h  |  25 +++-
> ofproto/ofproto-dpif-upcall.c |   3 +-
> ofproto/ofproto-dpif-xlate.c  |  61 ++++----
> ofproto/ofproto-dpif.c        |  42 ++++--
> tests/odp.at                  |   2 +-
> tests/ofproto-dpif.at         | 138 ++++++++++++++++++
> tests/test-sflow.c            |  82 +++++++++++
> 17 files changed, 714 insertions(+), 203 deletions(-)
> 
> diff --git a/lib/lacp.c b/lib/lacp.c
> index 5421e2a..ff83850 100644
> --- a/lib/lacp.c
> +++ b/lib/lacp.c
> @@ -122,6 +122,11 @@ struct slave {
>     struct lacp_info ntt_actor;   /* Used to decide if we Need To Transmit. */
>     struct timer tx;              /* Next message transmission timer. */
>     struct timer rx;              /* Expected message receive timer. */
> +    struct {
> +	uint32_t rx_pdus;         /* dot3adAggPortStatsLACPDUsRx */
> +	uint32_t rx_pdus_bad;     /* dot3adAggPortStatsIllegalRx */
> +	uint32_t tx_pdus;         /* dot3adAggPortStatsLACPDUsTx */
> +    } counters;
> };
> 
> static struct ovs_mutex mutex;
> @@ -316,9 +321,11 @@ lacp_process_packet(struct lacp *lacp, const void *slave_,
>     if (!slave) {
>         goto out;
>     }
> +    slave->counters.rx_pdus++;
> 
>     pdu = parse_lacp_packet(packet);
>     if (!pdu) {
> +	slave->counters.rx_pdus_bad++;
>         VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name);
>         goto out;
>     }
> @@ -529,6 +536,7 @@ lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) OVS_EXCLUDED(mutex)
>             slave->ntt_actor = actor;
>             compose_lacp_pdu(&actor, &slave->partner, &pdu);
>             send_pdu(slave->aux, &pdu, sizeof pdu);
> +	    slave->counters.tx_pdus++;
> 
>             duration = (slave->partner.state & LACP_STATE_TIME
>                         ? LACP_FAST_TIME_TX
> @@ -954,3 +962,59 @@ lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
> out:
>     ovs_mutex_unlock(&mutex);
> }
> +
> +/* Extract a snapshot of the current state and counters for a slave port.
> +   Return false if the slave is not active. */
> +bool
> +lacp_get_slave_stats(const struct lacp *lacp, const void *slave_, struct lacp_slave_stats *stats)
> +    OVS_EXCLUDED(mutex)
> +{
> +    struct slave *slave;
> +    struct lacp_info actor;
> +    bool ret;
> +    
> +    ovs_mutex_lock(&mutex);
> +
> +    slave = slave_lookup(lacp, slave_);
> +    if(slave) {
> +	ret = true;
> +	slave_get_actor(slave, &actor);
> +	memcpy(&stats->dot3adAggPortActorSystemID,
> +	       actor.sys_id,
> +	       ETH_ADDR_LEN);
> +	memcpy(&stats->dot3adAggPortPartnerOperSystemID,
> +	       slave->partner.sys_id,
> +	       ETH_ADDR_LEN);
> +	stats->dot3adAggPortAttachedAggID = (lacp->key_slave->key ?
> +					     lacp->key_slave->key :
> +					     lacp->key_slave->port_id);
> +	
> +	/* Construct my admin-state.  Assume aggregation is configured on. */
> +	stats->dot3adAggPortActorAdminState = LACP_STATE_AGG;
> +	if(lacp->active) {
> +	    stats->dot3adAggPortActorAdminState |= LACP_STATE_ACT;
> +	}
> +	if(lacp->fast) {
> +	    stats->dot3adAggPortActorAdminState |= LACP_STATE_TIME;
> +	}
> +	/* XXX Not sure how to know the partner admin state. It
> +	 * might have to be captured and remembered during the
> +	 * negotiation phase.
> +	 */
> +	stats->dot3adAggPortPartnerAdminState = 0;
> +	
> +	stats->dot3adAggPortActorOperState = actor.state;
> +	stats->dot3adAggPortPartnerOperState = slave->partner.state;
> +	
> +	/* Read out the latest counters */
> +	stats->dot3adAggPortStatsLACPDUsRx = slave->counters.rx_pdus;
> +	stats->dot3adAggPortStatsIllegalRx = slave->counters.rx_pdus_bad;
> +	stats->dot3adAggPortStatsLACPDUsTx = slave->counters.tx_pdus;
> +    }
> +    else {
> +	ret = false;
> +    }
> +    ovs_mutex_unlock(&mutex);
> +    return ret;
> +
> +}
> diff --git a/lib/lacp.h b/lib/lacp.h
> index 89b0e0a..f5fcc96 100644
> --- a/lib/lacp.h
> +++ b/lib/lacp.h
> @@ -69,4 +69,27 @@ typedef void lacp_send_pdu(void *slave, const void *pdu, size_t pdu_size);
> void lacp_run(struct lacp *, lacp_send_pdu *);
> void lacp_wait(struct lacp *);
> 
> +struct lacp_slave_stats {
> +    /* id */
> +    uint8_t dot3adAggPortActorSystemID[ETH_ADDR_LEN];
> +    uint8_t dot3adAggPortPartnerOperSystemID[ETH_ADDR_LEN];
> +    uint32_t dot3adAggPortAttachedAggID;
> +    /* state */
> +    uint8_t dot3adAggPortActorAdminState;
> +    uint8_t dot3adAggPortActorOperState;
> +    uint8_t dot3adAggPortPartnerAdminState;
> +    uint8_t dot3adAggPortPartnerOperState;
> +    /* counters */
> +    uint32_t dot3adAggPortStatsLACPDUsRx;
> +    /* uint32_t dot3adAggPortStatsMarkerPDUsRx; */
> +    /* uint32_t dot3adAggPortStatsMarkerResponsePDUsRx; */
> +    /* uint32_t dot3adAggPortStatsUnknownRx; */
> +    uint32_t dot3adAggPortStatsIllegalRx;
> +    uint32_t dot3adAggPortStatsLACPDUsTx;
> +    /* uint32_t dot3adAggPortStatsMarkerPDUsTx; */
> +    /* uint32_t dot3adAggPortStatsMarkerResponsePDUsTx; */
> +};
> +
> +bool lacp_get_slave_stats(const struct lacp *, const void *slave_, struct lacp_slave_stats *);
> +
> #endif /* lacp.h */
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index 5ca8baf..de66ed5 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -294,12 +294,21 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
>             userdata_unspec = false;
> 
>             if (userdata_len == sizeof cookie.sflow
> -                && cookie.type == USER_ACTION_COOKIE_SFLOW) {
> +                && cookie.type == USER_ACTION_COOKIE_SFLOW_SINGLEPORT) {
>                 ds_put_format(ds, ",sFlow("
> -                              "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
> -                              vlan_tci_to_vid(cookie.sflow.vlan_tci),
> -                              vlan_tci_to_pcp(cookie.sflow.vlan_tci),
> -                              cookie.sflow.output);
> +                              "vid=%"PRIu16",pcp=%"PRIu8
> +			      ",n_outputs=1,output_port=%"PRIu32")",
> +                              vlan_tci_to_vid(cookie.sflow.single.vlan_tci),
> +                              vlan_tci_to_pcp(cookie.sflow.single.vlan_tci),
> +                              cookie.sflow.single.output_port);
> +	    } else if (userdata_len == sizeof cookie.sflow
> +		       && cookie.type == USER_ACTION_COOKIE_SFLOW_MULTIPORT) {
> +                ds_put_format(ds, ",sFlow("
> +                              "vid=%"PRIu16",pcp=%"PRIu8
> +			      ",n_outputs=%"PRIu32",output_port=0)",
> +                              vlan_tci_to_vid(cookie.sflow.multi.vlan_tci),
> +                              vlan_tci_to_pcp(cookie.sflow.multi.vlan_tci),
> +                              cookie.sflow.multi.n_outputs);
>             } else if (userdata_len == sizeof cookie.slow_path
>                        && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
>                 const char *reason;
> @@ -325,8 +334,8 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
>         }
> 
>         if (userdata_unspec) {
> -            size_t i;
> -            ds_put_format(ds, ",userdata(");
> +	    size_t i;
> +	    ds_put_format(ds, ",userdata(");
>             for (i = 0; i < userdata_len; i++) {
>                 ds_put_format(ds, "%02x", userdata[i]);
>             }
> @@ -507,7 +516,8 @@ parse_odp_action(const char *s, const struct simap *port_names,
> 
>     {
>         unsigned long long int pid;
> -        unsigned long long int output;
> +        unsigned long long int n_outputs;
> +        unsigned long long int output_port;
>         unsigned long long int probability;
>         unsigned long long int collector_set_id;
>         unsigned long long int obs_domain_id;
> @@ -519,8 +529,8 @@ parse_odp_action(const char *s, const struct simap *port_names,
>             odp_put_userspace_action(pid, NULL, 0, actions);
>             return n;
>         } else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i,"
> -                          "pcp=%i,output=%lli))%n",
> -                          &pid, &vid, &pcp, &output, &n) > 0 && n > 0) {
> +                          "pcp=%i,n_outputs=%lli,output_port=%lli))%n",
> +                          &pid, &vid, &pcp, &n_outputs, &output_port, &n) > 0 && n > 0) {
>             union user_action_cookie cookie;
>             uint16_t tci;
> 
> @@ -529,11 +539,22 @@ parse_odp_action(const char *s, const struct simap *port_names,
>                 tci |= VLAN_CFI;
>             }
> 
> -            cookie.type = USER_ACTION_COOKIE_SFLOW;
> -            cookie.sflow.vlan_tci = htons(tci);
> -            cookie.sflow.output = output;
> -            odp_put_userspace_action(pid, &cookie, sizeof cookie.sflow,
> -                                     actions);
> +	    if(n_outputs == 1) {
> +	      cookie.type = USER_ACTION_COOKIE_SFLOW_SINGLEPORT;
> +	      cookie.sflow.single.vlan_tci = htons(tci);
> +	      cookie.sflow.single.output_port = output_port;
> +	      odp_put_userspace_action(pid, &cookie,
> +				       sizeof cookie.sflow.single,
> +				       actions);
> +	    }
> +	    else {
> +	      cookie.type = USER_ACTION_COOKIE_SFLOW_MULTIPORT;
> +	      cookie.sflow.multi.vlan_tci = htons(tci);
> +	      cookie.sflow.multi.n_outputs = n_outputs;
> +	      odp_put_userspace_action(pid, &cookie,
> +				       sizeof cookie.sflow.multi,
> +				       actions);
> +	    }
>             return n;
>         } else if (sscanf(s, "userspace(pid=%lli,slow_path(%n", &pid, &n) > 0
>                    && n > 0) {
> diff --git a/lib/odp-util.h b/lib/odp-util.h
> index 192cfa0..9dc63bb 100644
> --- a/lib/odp-util.h
> +++ b/lib/odp-util.h
> @@ -142,7 +142,8 @@ void commit_odp_actions(const struct flow *, struct flow *base,
> 
> enum user_action_cookie_type {
>     USER_ACTION_COOKIE_UNSPEC,
> -    USER_ACTION_COOKIE_SFLOW,        /* Packet for per-bridge sFlow sampling. */
> +    USER_ACTION_COOKIE_SFLOW_SINGLEPORT,  /* sFlow sample (1 output port). */
> +    USER_ACTION_COOKIE_SFLOW_MULTIPORT,   /* sFlow sample (N output ports). */
>     USER_ACTION_COOKIE_SLOW_PATH,    /* Userspace must process this flow. */
>     USER_ACTION_COOKIE_FLOW_SAMPLE,  /* Packet for per-flow sampling. */
>     USER_ACTION_COOKIE_IPFIX,        /* Packet for per-bridge IPFIX sampling. */
> @@ -153,10 +154,18 @@ enum user_action_cookie_type {
> union user_action_cookie {
>     uint16_t type;              /* enum user_action_cookie_type. */
> 
> -    struct {
> -        uint16_t type;          /* USER_ACTION_COOKIE_SFLOW. */
> -        ovs_be16 vlan_tci;      /* Destination VLAN TCI. */
> -        uint32_t output;        /* SFL_FLOW_SAMPLE_TYPE 'output' value. */
> +    union {
> +	struct {
> +	    uint16_t type;          /* USER_ACTION_COOKIE_SFLOW_SINGLEPORT. */
> +	    ovs_be16 vlan_tci;      /* Destination VLAN TCI. */
> +	    ofp_port_t output_port; /* Output port */
> +	    /* XXX do we need to pad to 8 bytes -- while sizeof(ofp_port_t) is still 2 ?*/
> +	} single;
> +	struct {
> +	    uint16_t type;          /* USER_ACTION_COOKIE_SFLOW_MULTIPORT. */
> +	    ovs_be16 vlan_tci;      /* Destination VLAN TCI. */
> +	    uint32_t n_outputs;     /* Number of output ports. */
> +	} multi;
>     } sflow;
> 
>     struct {
> diff --git a/lib/sflow.h b/lib/sflow.h
> index 0d1f2b9..0038c27 100644
> --- a/lib/sflow.h
> +++ b/lib/sflow.h
> @@ -285,6 +285,8 @@ enum SFLFlow_type_tag {
>     SFLFLOW_EX_MPLS_FTN     = 1010,
>     SFLFLOW_EX_MPLS_LDP_FEC = 1011,
>     SFLFLOW_EX_VLAN_TUNNEL  = 1012,   /* VLAN stack */
> +    SFLFLOW_EX_IPV4_TUNNEL_EGRESS  = 1023, /* http://sflow.org/sflow_tunnels.txt */
> +    SFLFLOW_EX_IPV4_TUNNEL_INGRESS = 1024,
> };
> 
> typedef union _SFLFlow_type {
> @@ -382,6 +384,9 @@ typedef struct _SFLFlow_sample_expanded {
> 
> /* Counter types */
> 
> +#define SFL_UNDEF_COUNTER(c) c=(typeof(c))-1
> +#define SFL_UNDEF_GAUGE(c) c=0
> +
> /* Generic interface counters - see RFC 1573, 2233 */
> 
> typedef struct _SFLIf_counters {
> @@ -478,6 +483,35 @@ typedef struct _SFLVlan_counters {
>     u_int32_t discards;
> } SFLVlan_counters;
> 
> +
> +/* LAG Port Statistics - see http://sflow.org/sflow_lag.txt */
> +/* opaque = counter_data; enterprise = 0; format = 7 */
> +
> +typedef  union _SFLLACP_portState {
> +    uint32_t all;
> +    struct {
> +	uint8_t actorAdmin;
> +	uint8_t actorOper;
> +	uint8_t partnerAdmin;
> +	uint8_t partnerOper;
> +    } v;
> +} SFLLACP_portState;
> +
> +typedef struct _SFLLACP_counters {
> +    uint8_t actorSystemID[8];   /* 6 bytes + 2 pad */
> +    uint8_t partnerSystemID[8]; /* 6 bytes + 2 pad */
> +    uint32_t attachedAggID;
> +    SFLLACP_portState portState;
> +    uint32_t LACPDUsRx;
> +    uint32_t markerPDUsRx;
> +    uint32_t markerResponsePDUsRx;
> +    uint32_t unknownRx;
> +    uint32_t illegalRx;
> +    uint32_t LACPDUsTx;
> +    uint32_t markerPDUsTx;
> +    uint32_t markerResponsePDUsTx;
> +} SFLLACP_counters;
> +
> /* Counters data */
> 
> enum SFLCounters_type_tag {
> @@ -486,7 +520,8 @@ enum SFLCounters_type_tag {
>     SFLCOUNTERS_ETHERNET     = 2,
>     SFLCOUNTERS_TOKENRING    = 3,
>     SFLCOUNTERS_VG           = 4,
> -    SFLCOUNTERS_VLAN         = 5
> +    SFLCOUNTERS_VLAN         = 5,
> +    SFLCOUNTERS_LACP         = 7
> };
> 
> typedef union _SFLCounters_type {
> @@ -495,6 +530,7 @@ typedef union _SFLCounters_type {
>     SFLTokenring_counters tokenring;
>     SFLVg_counters vg;
>     SFLVlan_counters vlan;
> +    SFLLACP_counters lacp;
> } SFLCounters_type;
> 
> typedef struct _SFLCounters_sample_element {
> diff --git a/lib/sflow_agent.c b/lib/sflow_agent.c
> index 817420d..9c2e028 100644
> --- a/lib/sflow_agent.c
> +++ b/lib/sflow_agent.c
> @@ -363,6 +363,21 @@ SFLPoller *sfl_agent_getPoller(SFLAgent *agent, SFLDataSource_instance *pdsi)
>     return NULL;
> }
> 
> +/*_________________-----------------------------------__________________
> +  _________________  sfl_agent_getPollerByBridgePort  __________________
> +  -----------------___________________________________------------------
> +*/
> +
> +SFLPoller *sfl_agent_getPollerByBridgePort(SFLAgent *agent, uint32_t port_no)
> +{
> +  /* find it and return it */
> +    SFLPoller *pl = agent->pollers;
> +    for(; pl != NULL; pl = pl->nxt)
> +	if(pl->bridgePort == port_no) return pl;
> +    /* not found */
> +    return NULL;
> +}
> +
> /*_________________---------------------------__________________
>   _________________  sfl_agent_getReceiver    __________________
>   -----------------___________________________------------------
> diff --git a/lib/sflow_api.h b/lib/sflow_api.h
> index 3cc060b..2730a4c 100644
> --- a/lib/sflow_api.h
> +++ b/lib/sflow_api.h
> @@ -281,6 +281,7 @@ void sfl_agent_set_agentSubId(SFLAgent *agent, u_int32_t subId);
>    to get counters if it is not the same as the global ifIndex */
> void sfl_poller_set_bridgePort(SFLPoller *poller, u_int32_t port_no);
> u_int32_t sfl_poller_get_bridgePort(SFLPoller *poller);
> +SFLPoller *sfl_agent_getPollerByBridgePort(SFLAgent *agent, u_int32_t port_no);
> 
> /* call this to indicate a discontinuity with a counter like samplePool so that the
>    sflow collector will ignore the next delta */
> diff --git a/lib/sflow_receiver.c b/lib/sflow_receiver.c
> index 3e5a67a..8b3ffad 100644
> --- a/lib/sflow_receiver.c
> +++ b/lib/sflow_receiver.c
> @@ -460,6 +460,8 @@ static int computeFlowSampleSize(SFLReceiver *receiver, SFL_FLOW_SAMPLE_TYPE *fs
> 	case SFLFLOW_EX_MPLS_FTN: elemSiz = mplsFtnEncodingLength(&elem->flowType.mpls_ftn); break;
> 	case SFLFLOW_EX_MPLS_LDP_FEC: elemSiz = mplsLdpFecEncodingLength(&elem->flowType.mpls_ldp_fec); break;
> 	case SFLFLOW_EX_VLAN_TUNNEL: elemSiz = vlanTunnelEncodingLength(&elem->flowType.vlan_tunnel); break;
> +	case SFLFLOW_EX_IPV4_TUNNEL_EGRESS: elemSiz = sizeof(SFLSampled_ipv4); break;
> +	case SFLFLOW_EX_IPV4_TUNNEL_INGRESS: elemSiz = sizeof(SFLSampled_ipv4); break;
> 	default:
> 	    sflError(receiver, "unexpected packet_data_tag");
> 	    return -1;
> @@ -556,6 +558,8 @@ int sfl_receiver_writeFlowSample(SFLReceiver *receiver, SFL_FLOW_SAMPLE_TYPE *fs
> 		putNet32(receiver, elem->flowType.ethernet.eth_type);
> 		break;
> 	    case SFLFLOW_IPV4:
> +	    case SFLFLOW_EX_IPV4_TUNNEL_EGRESS:
> +	    case SFLFLOW_EX_IPV4_TUNNEL_INGRESS:
> 		putNet32(receiver, elem->flowType.ipv4.length);
> 		putNet32(receiver, elem->flowType.ipv4.protocol);
> 		put32(receiver, elem->flowType.ipv4.src_ip.addr);
> @@ -630,6 +634,7 @@ static int computeCountersSampleSize(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_
> 	case SFLCOUNTERS_TOKENRING: elemSiz = sizeof(elem->counterBlock.tokenring); break;
> 	case SFLCOUNTERS_VG: elemSiz = sizeof(elem->counterBlock.vg); break;
> 	case SFLCOUNTERS_VLAN: elemSiz = sizeof(elem->counterBlock.vlan); break;
> +	case SFLCOUNTERS_LACP: elemSiz = sizeof(elem->counterBlock.lacp); break;
> 	default:
> 	    sflError(receiver, "unexpected counters_tag");
> 	    return -1;
> @@ -731,6 +736,20 @@ int sfl_receiver_writeCountersSample(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_
> 		putNet32(receiver, elem->counterBlock.vlan.broadcastPkts);
> 		putNet32(receiver, elem->counterBlock.vlan.discards);
> 		break;
> +	    case SFLCOUNTERS_LACP:
> +		putMACAddress(receiver, elem->counterBlock.lacp.actorSystemID);
> +		putMACAddress(receiver, elem->counterBlock.lacp.partnerSystemID);
> +		putNet32(receiver, elem->counterBlock.lacp.attachedAggID);
> +		put32(receiver, elem->counterBlock.lacp.portState.all);
> +		putNet32(receiver, elem->counterBlock.lacp.LACPDUsRx);
> +		putNet32(receiver, elem->counterBlock.lacp.markerPDUsRx);
> +		putNet32(receiver, elem->counterBlock.lacp.markerResponsePDUsRx);
> +		putNet32(receiver, elem->counterBlock.lacp.unknownRx);
> +		putNet32(receiver, elem->counterBlock.lacp.illegalRx);
> +		putNet32(receiver, elem->counterBlock.lacp.LACPDUsTx);
> +		putNet32(receiver, elem->counterBlock.lacp.markerPDUsTx);
> +		putNet32(receiver, elem->counterBlock.lacp.markerResponsePDUsTx);
> +		break;
> 	    default:
> 		sflError(receiver, "unexpected counters_tag");
> 		return -1;
> diff --git a/manpages.mk b/manpages.mk
> index 811d2f9..2a34f04 100644
> --- a/manpages.mk
> +++ b/manpages.mk
> @@ -116,6 +116,10 @@ lib/vconn-active.man:
> lib/vconn-passive.man:
> lib/vlog.man:
> 
> +utilities/ovs-dpctl-top.8: \
> +	utilities/ovs-dpctl-top.8.in
> +utilities/ovs-dpctl-top.8.in:
> +
> utilities/ovs-dpctl.8: \
> 	utilities/ovs-dpctl.8.in \
> 	lib/common.man \
> @@ -124,10 +128,6 @@ utilities/ovs-dpctl.8.in:
> lib/common.man:
> lib/vlog.man:
> 
> -utilities/ovs-dpctl-top.8: \
> -	utilities/ovs-dpctl-top.8.in
> -utilities/ovs-dpctl-top.8.in:
> -
> utilities/ovs-l3ping.8: \
> 	utilities/ovs-l3ping.8.in \
> 	lib/common-syn.man \
> diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
> index 158887f..1e90a29 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -24,8 +24,6 @@
> #include "collectors.h"
> #include "compiler.h"
> #include "dpif.h"
> -#include "hash.h"
> -#include "hmap.h"
> #include "netdev.h"
> #include "netlink.h"
> #include "ofpbuf.h"
> @@ -39,32 +37,24 @@
> #include "vlog.h"
> #include "lib/odp-util.h"
> #include "ofproto-provider.h"
> +#include "lacp.h"
> 
> VLOG_DEFINE_THIS_MODULE(sflow);
> 
> static struct ovs_mutex mutex;
> 
> -struct dpif_sflow_port {
> -    struct hmap_node hmap_node; /* In struct dpif_sflow's "ports" hmap. */
> -    SFLDataSource_instance dsi; /* sFlow library's notion of port number. */
> -    struct ofport *ofport;      /* To retrive port stats. */
> -    odp_port_t odp_port;
> -};
> -
> struct dpif_sflow {
>     struct collectors *collectors;
>     SFLAgent *sflow_agent;
>     struct ofproto_sflow_options *options;
>     time_t next_tick;
>     size_t n_flood, n_all;
> -    struct hmap ports;          /* Contains "struct dpif_sflow_port"s. */
>     uint32_t probability;
>     atomic_int ref_cnt;
> +    dpif_sflow_callback port_callback;
> +    void *port_callback_magic;
> };
> 
> -static void dpif_sflow_del_port__(struct dpif_sflow *,
> -                                  struct dpif_sflow_port *);
> -
> #define RECEIVER_INDEX 1
> 
> static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> @@ -144,21 +134,6 @@ sflow_agent_send_packet_cb(void *ds_, SFLAgent *agent OVS_UNUSED,
>     collectors_send(ds->collectors, pkt, pktLen);
> }
> 
> -static struct dpif_sflow_port *
> -dpif_sflow_find_port(const struct dpif_sflow *ds, odp_port_t odp_port)
> -    OVS_REQUIRES(mutex)
> -{
> -    struct dpif_sflow_port *dsp;
> -
> -    HMAP_FOR_EACH_IN_BUCKET (dsp, hmap_node, hash_odp_port(odp_port),
> -                             &ds->ports) {
> -        if (dsp->odp_port == odp_port) {
> -            return dsp;
> -        }
> -    }
> -    return NULL;
> -}
> -
> static void
> sflow_agent_get_counters(void *ds_, SFLPoller *poller,
>                          SFL_COUNTERS_SAMPLE_TYPE *cs)
> @@ -167,21 +142,27 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
>     struct dpif_sflow *ds = ds_;
>     SFLCounters_sample_element elem;
>     enum netdev_features current;
> -    struct dpif_sflow_port *dsp;
>     SFLIf_counters *counters;
>     struct netdev_stats stats;
>     enum netdev_flags flags;
> -
> -    dsp = dpif_sflow_find_port(ds, u32_to_odp(poller->bridgePort));
> -    if (!dsp) {
> -        return;
> +    struct dpif_sflow_port_lookup port_lookup;
> +    SFLCounters_sample_element lacpElem;
> +    struct lacp_slave_stats lacp_stats;
> +    SFLLACP_counters *lacp;
> +
> +    memset(&port_lookup, 0, sizeof port_lookup);
> +    if(!(*ds->port_callback)(ds->port_callback_magic,
> +			     (ofp_port_t)(poller->bridgePort),
> +			     &port_lookup)) {
> +      return;
>     }
> -
> +    
> +    memset(&elem, 0, sizeof elem);
>     elem.tag = SFLCOUNTERS_GENERIC;
>     counters = &elem.counterBlock.generic;
>     counters->ifIndex = SFL_DS_INDEX(poller->dsi);
>     counters->ifType = 6;
> -    if (!netdev_get_features(dsp->ofport->netdev, &current, NULL, NULL, NULL)) {
> +    if (!netdev_get_features(port_lookup.ofport->netdev, &current, NULL, NULL, NULL)) {
>         /* The values of ifDirection come from MAU MIB (RFC 2668): 0 = unknown,
>            1 = full-duplex, 2 = half-duplex, 3 = in, 4=out */
>         counters->ifSpeed = netdev_features_to_bps(current, 0);
> @@ -191,9 +172,9 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
>         counters->ifSpeed = 100000000;
>         counters->ifDirection = 0;
>     }
> -    if (!netdev_get_flags(dsp->ofport->netdev, &flags) && flags & NETDEV_UP) {
> +    if (!netdev_get_flags(port_lookup.ofport->netdev, &flags) && flags & NETDEV_UP) {
>         counters->ifStatus = 1; /* ifAdminStatus up. */
> -        if (netdev_get_carrier(dsp->ofport->netdev)) {
> +        if (netdev_get_carrier(port_lookup.ofport->netdev)) {
>             counters->ifStatus |= 2; /* ifOperStatus us. */
>         }
>     } else {
> @@ -205,23 +186,57 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
>        2. Does the multicast counter include broadcasts?
>        3. Does the rx_packets counter include multicasts/broadcasts?
>     */
> -    ofproto_port_get_stats(dsp->ofport, &stats);
> +    ofproto_port_get_stats(port_lookup.ofport, &stats);
>     counters->ifInOctets = stats.rx_bytes;
>     counters->ifInUcastPkts = stats.rx_packets;
>     counters->ifInMulticastPkts = stats.multicast;
> -    counters->ifInBroadcastPkts = -1;
> +    SFL_UNDEF_COUNTER(counters->ifInBroadcastPkts);
>     counters->ifInDiscards = stats.rx_dropped;
>     counters->ifInErrors = stats.rx_errors;
> -    counters->ifInUnknownProtos = -1;
> +    SFL_UNDEF_COUNTER(counters->ifInUnknownProtos);
>     counters->ifOutOctets = stats.tx_bytes;
>     counters->ifOutUcastPkts = stats.tx_packets;
> -    counters->ifOutMulticastPkts = -1;
> -    counters->ifOutBroadcastPkts = -1;
> +    SFL_UNDEF_COUNTER(counters->ifOutMulticastPkts);
> +    SFL_UNDEF_COUNTER(counters->ifOutBroadcastPkts);
>     counters->ifOutDiscards = stats.tx_dropped;
>     counters->ifOutErrors = stats.tx_errors;
>     counters->ifPromiscuousMode = 0;
> 
>     SFLADD_ELEMENT(cs, &elem);
> +
> +    if(port_lookup.lacp) {
> +	memset(&lacp_stats, 0, sizeof lacp_stats);
> +	/* Implements sFlow standard: http://sflow.org/sflow_lag.txt */
> +	if(lacp_get_slave_stats(port_lookup.lacp,
> +				port_lookup.lacp_slave,
> +				&lacp_stats)) {
> +	    memset(&lacpElem, 0, sizeof lacpElem);
> +	    lacpElem.tag = SFLCOUNTERS_LACP;
> +	    lacp = &lacpElem.counterBlock.lacp;
> +	    memcpy(lacp->actorSystemID,
> +		   lacp_stats.dot3adAggPortActorSystemID,
> +		   ETH_ADDR_LEN);
> +	    memcpy(lacp->partnerSystemID,
> +		   lacp_stats.dot3adAggPortPartnerOperSystemID,
> +		   ETH_ADDR_LEN);
> +	    lacp->attachedAggID = lacp_stats.dot3adAggPortAttachedAggID;
> +	    lacp->portState.v.actorAdmin = lacp_stats.dot3adAggPortActorAdminState;
> +	    lacp->portState.v.actorOper = lacp_stats.dot3adAggPortActorOperState;
> +	    lacp->portState.v.partnerAdmin = lacp_stats.dot3adAggPortPartnerAdminState;
> +	    lacp->portState.v.partnerOper = lacp_stats.dot3adAggPortPartnerOperState;
> +	    lacp->LACPDUsRx = lacp_stats.dot3adAggPortStatsLACPDUsRx;
> +	    SFL_UNDEF_COUNTER(lacp->markerPDUsRx);
> +	    SFL_UNDEF_COUNTER(lacp->markerResponsePDUsRx);
> +	    SFL_UNDEF_COUNTER(lacp->unknownRx);
> +	    lacp->illegalRx = lacp_stats.dot3adAggPortStatsIllegalRx;
> +	    lacp->LACPDUsTx = lacp_stats.dot3adAggPortStatsLACPDUsTx;
> +	    SFL_UNDEF_COUNTER(lacp->markerPDUsTx);
> +	    SFL_UNDEF_COUNTER(lacp->markerResponsePDUsTx);
> +
> +	    SFLADD_ELEMENT(cs, &lacpElem);
> +	}
> +    }
> +
>     sfl_poller_writeCountersSample(poller, cs);
> }
> 
> @@ -324,7 +339,6 @@ dpif_sflow_create(void)
> 
>     ds = xcalloc(1, sizeof *ds);
>     ds->next_tick = time_now() + 1;
> -    hmap_init(&ds->ports);
>     ds->probability = 0;
>     route_table_register();
>     atomic_init(&ds->ref_cnt, 1);
> @@ -369,84 +383,54 @@ dpif_sflow_unref(struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
>     atomic_sub(&ds->ref_cnt, 1, &orig);
>     ovs_assert(orig > 0);
>     if (orig == 1) {
> -        struct dpif_sflow_port *dsp, *next;
> -
>         route_table_unregister();
>         dpif_sflow_clear(ds);
> -        HMAP_FOR_EACH_SAFE (dsp, next, hmap_node, &ds->ports) {
> -            dpif_sflow_del_port__(ds, dsp);
> -        }
> -        hmap_destroy(&ds->ports);
>         free(ds);
>     }
> }
> 
> static void
> -dpif_sflow_add_poller(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
> +dpif_sflow_add_poller(struct dpif_sflow *ds, uint32_t ifindex, ofp_port_t ofp_port)
>     OVS_REQUIRES(mutex)
> {
> -    SFLPoller *poller = sfl_agent_addPoller(ds->sflow_agent, &dsp->dsi, ds,
> -                                            sflow_agent_get_counters);
> -    sfl_poller_set_sFlowCpInterval(poller, ds->options->polling_interval);
> -    sfl_poller_set_sFlowCpReceiver(poller, RECEIVER_INDEX);
> -    sfl_poller_set_bridgePort(poller, odp_to_u32(dsp->odp_port));
> +    SFLDataSource_instance dsi;
> +    SFLPoller *poller;
> +    if(ds->sflow_agent) {
> +	SFL_DS_SET(dsi, SFL_DSCLASS_IFINDEX, ifindex, 0);
> +	poller = sfl_agent_addPoller(ds->sflow_agent, &dsi, ds,
> +				     sflow_agent_get_counters);
> +	sfl_poller_set_sFlowCpInterval(poller, ds->options->polling_interval);
> +	sfl_poller_set_sFlowCpReceiver(poller, RECEIVER_INDEX);
> +	sfl_poller_set_bridgePort(poller, (uint32_t)ofp_port);
> +    }
> }
> 
> void
> -dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport,
> -                    odp_port_t odp_port) OVS_EXCLUDED(mutex)
> +dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport) OVS_EXCLUDED(mutex)
> {
> -    struct dpif_sflow_port *dsp;
>     int ifindex;
> 
>     ovs_mutex_lock(&mutex);
> -    dpif_sflow_del_port(ds, odp_port);
> -
> +    dpif_sflow_del_port(ds, ofport->ofp_port);
>     ifindex = netdev_get_ifindex(ofport->netdev);
> -
> -    if (ifindex <= 0) {
> -        /* Not an ifindex port, so do not add a cross-reference to it here */
> -        goto out;
> +    if (ifindex > 0) {
> +        /* ifindex port, so add a poller for it here */
> +        dpif_sflow_add_poller(ds, ifindex, ofport->ofp_port);
>     }
> -
> -    /* Add to table of ports. */
> -    dsp = xmalloc(sizeof *dsp);
> -    dsp->ofport = ofport;
> -    dsp->odp_port = odp_port;
> -    SFL_DS_SET(dsp->dsi, SFL_DSCLASS_IFINDEX, ifindex, 0);
> -    hmap_insert(&ds->ports, &dsp->hmap_node, hash_odp_port(odp_port));
> -
> -    /* Add poller. */
> -    if (ds->sflow_agent) {
> -        dpif_sflow_add_poller(ds, dsp);
> -    }
> -
> -out:
>     ovs_mutex_unlock(&mutex);
> }
> 
> -static void
> -dpif_sflow_del_port__(struct dpif_sflow *ds, struct dpif_sflow_port *dsp)
> -    OVS_REQUIRES(mutex)
> -{
> -    if (ds->sflow_agent) {
> -        sfl_agent_removePoller(ds->sflow_agent, &dsp->dsi);
> -        sfl_agent_removeSampler(ds->sflow_agent, &dsp->dsi);
> -    }
> -    hmap_remove(&ds->ports, &dsp->hmap_node);
> -    free(dsp);
> -}
> -
> void
> -dpif_sflow_del_port(struct dpif_sflow *ds, odp_port_t odp_port)
> +dpif_sflow_del_port(struct dpif_sflow *ds, ofp_port_t ofp_port)
>     OVS_EXCLUDED(mutex)
> {
> -    struct dpif_sflow_port *dsp;
> -
> +    SFLPoller *poller;
>     ovs_mutex_lock(&mutex);
> -    dsp = dpif_sflow_find_port(ds, odp_port);
> -    if (dsp) {
> -        dpif_sflow_del_port__(ds, dsp);
> +    if(ds->sflow_agent) {
> +	poller = sfl_agent_getPollerByBridgePort(ds->sflow_agent, (uint32_t)ofp_port);
> +	if(poller) {
> +	    sfl_agent_removePoller(ds->sflow_agent, &poller->dsi);
> +	}
>     }
>     ovs_mutex_unlock(&mutex);
> }
> @@ -456,7 +440,6 @@ dpif_sflow_set_options(struct dpif_sflow *ds,
>                        const struct ofproto_sflow_options *options)
>     OVS_EXCLUDED(mutex)
> {
> -    struct dpif_sflow_port *dsp;
>     bool options_changed;
>     SFLReceiver *receiver;
>     SFLAddress agentIP;
> @@ -543,33 +526,65 @@ dpif_sflow_set_options(struct dpif_sflow *ds,
>     sfl_sampler_set_sFlowFsMaximumHeaderSize(sampler, ds->options->header_len);
>     sfl_sampler_set_sFlowFsReceiver(sampler, RECEIVER_INDEX);
> 
> -    /* Add pollers for the currently known ifindex-ports */
> -    HMAP_FOR_EACH (dsp, hmap_node, &ds->ports) {
> -        dpif_sflow_add_poller(ds, dsp);
> -    }
> -
> -
> out:
>     ovs_mutex_unlock(&mutex);
> }
> 
> -int
> -dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds,
> -                               odp_port_t odp_port) OVS_EXCLUDED(mutex)
> +void
> +dpif_sflow_set_port_lookup_callback(struct dpif_sflow *ds,
> +				    dpif_sflow_callback callback,
> +				    void *magic)
> +    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;
> +    ds->port_callback = callback;
> +    ds->port_callback_magic = magic;
>     ovs_mutex_unlock(&mutex);
> -    return ret;
> +}
> +
> +/* Implements sFlow standard:  http://sflow.org/sflow_tunnels.txt */
> +static bool
> +dpif_sflow_ipv4_tunnel_encode(SFLSampled_ipv4 *tunnel4,
> +			      struct dpif_sflow_port_lookup *port_lookup)
> +{
> +    const char *netdev_type;
> +    const struct netdev_tunnel_config *tunnel_config;
> +
> +    /* XXX will need the struct flow here too - for those tunnels that
> +       get their config dynamically from the flow. */
> +
> +    tunnel_config = netdev_get_tunnel_config(port_lookup->ofport->netdev);
> +    if(tunnel_config == NULL) {
> +	return false;
> +    }
> +    netdev_type = netdev_get_type(port_lookup->ofport->netdev);
> +    tunnel4->src_ip.addr = tunnel_config->ip_src;
> +    tunnel4->dst_ip.addr = tunnel_config->ip_dst;
> +    /* Indicate 0==unknown for the src_port. It may be set to a random
> +       number on a flow-by-flow basis to increase entropy for ECMP fabrics.
> +       The assumption being made here is that it is not so important to
> +       report this.  At least not important enough to justify the effort
> +       of making it accessible here. */

Exporting the source UDP source port is essential.
You also have to export the tunnel key: GRE key (32- or 64-bit), VNI (24-bit), etc.
I don't see how this feature could be useful without the UDP source port and tunnel key.

> +    tunnel4->src_port = 0;
> +    tunnel4->dst_port = tunnel_config->dst_port;
> +    tunnel4->tos = tunnel_config->tos;
> +    /* Use the netdev_type to determine the IP protocol
> +       that was (or will be) seen on the wire. */
> +    if(!strncmp(netdev_type, "gre", strlen("gre"))) {
> +	tunnel4->protocol = IPPROTO_GRE;
> +    }
> +    else if(!strncmp(netdev_type, "vxlan", strlen("vxlan"))) {
> +	tunnel4->protocol = IPPROTO_UDP;
> +    }
> +    else if(!strncmp(netdev_type, "ipsec", strlen("ipsec"))) {
> +	tunnel4->protocol = IPPROTO_ESP;
> +    }

This list is missing LISP.
And when IPsec is used, please report the encapsulated tunnel header type.
More generally, please accurately report all tunnel types supported by OVS, cf. vswitchd/vswitch.xml's documentation of supported port types: gre, ipsec_gre, gre64, ipsec_gre64, vxlan, and lisp.

> +    return true;
> }
> 
> void
> dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
> -                    const struct flow *flow, odp_port_t odp_in_port,
> +                    const struct flow *flow,
>                     const union user_action_cookie *cookie)
>     OVS_EXCLUDED(mutex)
> {
> @@ -578,10 +593,13 @@ dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
>     SFLSampled_header *header;
>     SFLFlow_sample_element switchElem;
>     SFLSampler *sampler;
> -    struct dpif_sflow_port *in_dsp;
>     ovs_be16 vlan_tci;
> +    struct dpif_sflow_port_lookup in_lookup, out_lookup;
> +    SFLFlow_sample_element in_tunnelElem, out_tunnelElem;
> +    int in_ifindex, out_ifindex;
> 
>     ovs_mutex_lock(&mutex);
> +
>     sampler = ds->sflow_agent->samplers;
>     if (!sampler) {
>         goto out;
> @@ -590,11 +608,26 @@ dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
>     /* Build a flow sample. */
>     memset(&fs, 0, sizeof fs);
> 
> -    /* Look up the input ifIndex if this port has one. Otherwise just
> -     * leave it as 0 (meaning 'unknown') and continue. */
> -    in_dsp = dpif_sflow_find_port(ds, odp_in_port);
> -    if (in_dsp) {
> -        fs.input = SFL_DS_INDEX(in_dsp->dsi);
> +    memset(&in_lookup, 0, sizeof in_lookup);
> +    if((*ds->port_callback)(ds->port_callback_magic,
> +			    flow->in_port.ofp_port,
> +			    &in_lookup)) {
> +	if(in_lookup.ofport->netdev) {
> +	    /* Look up the input ifIndex if this port has one. Otherwise just
> +	     * leave it as 0 (meaning 'unknown') and continue. */
> +	    in_ifindex = netdev_get_ifindex(in_lookup.ofport->netdev);
> +	    if(in_ifindex > 0) {
> +		fs.input = in_ifindex;
> +	    }
> +	}
> +	if(in_lookup.is_tunnel) {
> +	    memset(&in_tunnelElem, 0, sizeof in_tunnelElem);
> +	    in_tunnelElem.tag = SFLFLOW_EX_IPV4_TUNNEL_INGRESS;
> +	    if(dpif_sflow_ipv4_tunnel_encode(&in_tunnelElem.flowType.ipv4,
> +					     &in_lookup)) {
> +		SFLADD_ELEMENT(&fs, &in_tunnelElem);
> +	    }
> +	}
>     }
> 
>     /* Make the assumption that the random number generator in the datapath converges
> @@ -623,15 +656,62 @@ dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
>     switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow->vlan_tci);
> 
>     /* Retrieve data from user_action_cookie. */
> -    vlan_tci = cookie->sflow.vlan_tci;
> -    switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(vlan_tci);
> -    switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(vlan_tci);
> +    if(cookie->type == USER_ACTION_COOKIE_SFLOW_MULTIPORT) {
> +	VLOG_INFO("sFlow cookie multiport");
> +	vlan_tci = cookie->sflow.multi.vlan_tci;
> +	switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(vlan_tci);
> +	switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(vlan_tci);
> +	/* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output
> +	 * port information") */
> +	if(cookie->sflow.multi.n_outputs == 0) {
> +	    /* 0x40000000 | 256 means "packet dropped for unknown reason". */
> +	    fs.output = 0x40000000 | 256;
> +	}
> +	else {
> +	    /* 0x80000000 means "multiple output ports. */
> +	    fs.output = 0x80000000 | cookie->sflow.multi.n_outputs;
> +	}
> +    }
> +    else if(cookie->type == USER_ACTION_COOKIE_SFLOW_SINGLEPORT) {
> +	VLOG_INFO("sFlow cookie singleport");
> +	vlan_tci = cookie->sflow.single.vlan_tci;
> +	switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(vlan_tci);
> +	switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(vlan_tci);
> +
> +	if(cookie->sflow.single.output_port) {
> +	    memset(&out_lookup, 0, sizeof out_lookup);
> +	    if((*ds->port_callback)(ds->port_callback_magic,
> +				    cookie->sflow.single.output_port,
> +				    &out_lookup)) {
> +		if(out_lookup.ofport->netdev) {
> +		    /* Look up the output ifIndex if this port has one. Otherwise just
> +		     * leave it as 0 (meaning 'unknown') and continue. */
> +		    out_ifindex = netdev_get_ifindex(out_lookup.ofport->netdev);
> +		    if(out_ifindex > 0) {
> +			fs.output = out_ifindex;
> +		    }
> +		}
> +		if(out_lookup.is_tunnel) {
> +		    memset(&out_tunnelElem, 0, sizeof out_tunnelElem);
> +		    out_tunnelElem.tag = SFLFLOW_EX_IPV4_TUNNEL_EGRESS;
> +		    if(dpif_sflow_ipv4_tunnel_encode(&out_tunnelElem.flowType.ipv4,
> +						     &out_lookup)) {
> +			VLOG_INFO("sFlow add tunnel element");
> +			SFLADD_ELEMENT(&fs, &out_tunnelElem);
> +		    }
> +		}
> +	    }
> +	}
> +    }
> +    else {
> +	VLOG_WARN("unknown upcall cookie type: %"PRIu16, cookie->type);
> +    }
> 
> -    fs.output = cookie->sflow.output;
> 
>     /* Submit the flow sample to be encoded into the next datagram. */
>     SFLADD_ELEMENT(&fs, &hdrElem);
>     SFLADD_ELEMENT(&fs, &switchElem);
> +    VLOG_INFO("sFlow writeFlowSample");
>     sfl_sampler_writeFlowSample(sampler, &fs);
> 
> out:
> diff --git a/ofproto/ofproto-dpif-sflow.h b/ofproto/ofproto-dpif-sflow.h
> index d53c95c..736256a 100644
> --- a/ofproto/ofproto-dpif-sflow.h
> +++ b/ofproto/ofproto-dpif-sflow.h
> @@ -21,6 +21,7 @@
> #include <stdint.h>
> #include "svec.h"
> #include "lib/odp-util.h"
> +#include "lacp.h"
> 
> struct dpif;
> struct dpif_upcall;
> @@ -34,14 +35,28 @@ void dpif_sflow_unref(struct dpif_sflow *);
> 
> uint32_t dpif_sflow_get_probability(const struct dpif_sflow *);
> 
> +struct dpif_sflow_port_lookup {
> +    struct ofport *ofport;
> +    /* indicate tunnel */
> +    bool is_tunnel;
> +    /* LAG/bundle details */
> +    struct lacp *lacp;
> +    void *lacp_slave;
> +};
> +
> +typedef bool (*dpif_sflow_callback)(void *, ofp_port_t,  struct dpif_sflow_port_lookup *);
> +
> +void dpif_sflow_set_port_lookup_callback(struct dpif_sflow *, dpif_sflow_callback, void *);
> +
> void dpif_sflow_set_options(struct dpif_sflow *,
>                             const struct ofproto_sflow_options *);
> +				
> void dpif_sflow_clear(struct dpif_sflow *);
> bool dpif_sflow_is_enabled(const struct dpif_sflow *);
> 
> -void dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport,
> -                         odp_port_t odp_port);
> -void dpif_sflow_del_port(struct dpif_sflow *, odp_port_t odp_port);
> +void dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport);
> +
> +void dpif_sflow_del_port(struct dpif_sflow *, ofp_port_t ofp_port);
> 
> void dpif_sflow_run(struct dpif_sflow *);
> void dpif_sflow_wait(struct dpif_sflow *);
> @@ -49,10 +64,6 @@ void dpif_sflow_wait(struct dpif_sflow *);
> void dpif_sflow_received(struct dpif_sflow *,
>                          struct ofpbuf *,
>                          const struct flow *,
> -                         odp_port_t odp_port,
>                          const union user_action_cookie *);
> 
> -int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *,
> -                                   odp_port_t odp_port);
> -
> #endif /* ofproto/ofproto-dpif-sflow.h */
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index d75c61b..668d5f9 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -456,7 +456,8 @@ classify_upcall(const struct upcall *upcall)
>     memset(&cookie, 0, sizeof cookie);
>     memcpy(&cookie, nl_attr_get(dpif_upcall->userdata), userdata_len);
>     if (userdata_len == sizeof cookie.sflow
> -        && cookie.type == USER_ACTION_COOKIE_SFLOW) {
> +        && (cookie.type == USER_ACTION_COOKIE_SFLOW_SINGLEPORT
> +	    || cookie.type == USER_ACTION_COOKIE_SFLOW_MULTIPORT)) {
>         return SFLOW_UPCALL;
>     } else if (userdata_len == sizeof cookie.slow_path
>                && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index a5b6814..b3898e4 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -162,7 +162,7 @@ struct xlate_ctx {
>     uint32_t orig_skb_priority; /* Priority when packet arrived. */
>     uint8_t table_id;           /* OpenFlow table ID where flow was found. */
>     uint32_t sflow_n_outputs;   /* Number of output ports. */
> -    odp_port_t sflow_odp_port;  /* Output port for composing sFlow action. */
> +    odp_port_t sflow_ofp_port;  /* Output port for composing sFlow action. */
>     uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */
>     bool exit;                  /* No further actions should be processed. */
> };
> @@ -1350,34 +1350,19 @@ compose_sample_action(const struct xbridge *xbridge,
> }
> 
> static void
> -compose_sflow_cookie(const struct xbridge *xbridge, ovs_be16 vlan_tci,
> -                     odp_port_t odp_port, unsigned int n_outputs,
> +compose_sflow_cookie(ovs_be16 vlan_tci,
> +                     ofp_port_t ofp_port, unsigned int n_outputs,
>                      union user_action_cookie *cookie)
> {
> -    int ifindex;
> -
> -    cookie->type = USER_ACTION_COOKIE_SFLOW;
> -    cookie->sflow.vlan_tci = vlan_tci;
> -
> -    /* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output
> -     * port information") for the interpretation of cookie->output. */
> -    switch (n_outputs) {
> -    case 0:
> -        /* 0x40000000 | 256 means "packet dropped for unknown reason". */
> -        cookie->sflow.output = 0x40000000 | 256;
> -        break;
> -
> -    case 1:
> -        ifindex = dpif_sflow_odp_port_to_ifindex(xbridge->sflow, odp_port);
> -        if (ifindex) {
> -            cookie->sflow.output = ifindex;
> -            break;
> -        }
> -        /* Fall through. */
> -    default:
> -        /* 0x80000000 means "multiple output ports. */
> -        cookie->sflow.output = 0x80000000 | n_outputs;
> -        break;
> +    if(n_outputs == 1) {
> +	cookie->type = USER_ACTION_COOKIE_SFLOW_SINGLEPORT;
> +	cookie->sflow.single.vlan_tci = vlan_tci;
> +	cookie->sflow.single.output_port = ofp_port;
> +    }
> +    else {
> +	cookie->type = USER_ACTION_COOKIE_SFLOW_MULTIPORT;
> +	cookie->sflow.multi.vlan_tci = vlan_tci;
> +	cookie->sflow.multi.n_outputs = (uint32_t)n_outputs;
>     }
> }
> 
> @@ -1386,7 +1371,7 @@ static size_t
> compose_sflow_action(const struct xbridge *xbridge,
>                      struct ofpbuf *odp_actions,
>                      const struct flow *flow,
> -                     odp_port_t odp_port)
> +                     ofp_port_t ofp_port)
> {
>     uint32_t probability;
>     union user_action_cookie cookie;
> @@ -1396,8 +1381,11 @@ compose_sflow_action(const struct xbridge *xbridge,
>     }
> 
>     probability = dpif_sflow_get_probability(xbridge->sflow);
> -    compose_sflow_cookie(xbridge, htons(0), odp_port,
> -                         odp_port == ODPP_NONE ? 0 : 1, &cookie);
> +    // maybe we should be putting something like flow->out_port.ofp_port in here?
> +    // -- and flow->in_port.ofp_port too if that's the easiest way to get the tunnel
> +    // ids to the sFlow module?
> +    compose_sflow_cookie(htons(0), ofp_port,
> +                         ofp_port == OFPP_NONE ? 0 : 1, &cookie);
> 
>     return compose_sample_action(xbridge, odp_actions, flow,  probability,
>                                  &cookie, sizeof cookie.sflow);
> @@ -1449,8 +1437,8 @@ add_sflow_action(struct xlate_ctx *ctx)
> {
>     ctx->user_cookie_offset = compose_sflow_action(ctx->xbridge,
>                                                    &ctx->xout->odp_actions,
> -                                                   &ctx->xin->flow, ODPP_NONE);
> -    ctx->sflow_odp_port = 0;
> +                                                   &ctx->xin->flow, OFPP_NONE);
> +    ctx->sflow_ofp_port = 0;
>     ctx->sflow_n_outputs = 0;
> }
> 
> @@ -1478,10 +1466,11 @@ fix_sflow_action(struct xlate_ctx *ctx)
> 
>     cookie = ofpbuf_at(&ctx->xout->odp_actions, ctx->user_cookie_offset,
>                        sizeof cookie->sflow);
> -    ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
> +    ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW_SINGLEPORT
> +	       || cookie->type == USER_ACTION_COOKIE_SFLOW_MULTIPORT);
> 
> -    compose_sflow_cookie(ctx->xbridge, base->vlan_tci,
> -                         ctx->sflow_odp_port, ctx->sflow_n_outputs, cookie);
> +    compose_sflow_cookie(base->vlan_tci,
> +                         ctx->sflow_ofp_port, ctx->sflow_n_outputs, cookie);
> }
> 
> static enum slow_path_reason
> @@ -1649,7 +1638,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>         nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
>                             out_port);
> 
> -        ctx->sflow_odp_port = odp_port;
> +        ctx->sflow_ofp_port = ofp_port;
>         ctx->sflow_n_outputs++;
>         ctx->xout->nf_output_iface = ofp_port;
>     }
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index 80874b8..3bacd56 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -1763,7 +1763,7 @@ port_construct(struct ofport *port_)
>     dpif_port_destroy(&dpif_port);
> 
>     if (ofproto->sflow) {
> -        dpif_sflow_add_port(ofproto->sflow, port_, port->odp_port);
> +        dpif_sflow_add_port(ofproto->sflow, port_);
>     }
> 
>     return 0;
> @@ -1863,24 +1863,47 @@ port_reconfigured(struct ofport *port_, enum ofputil_port_config old_config)
>     }
> }
> 
> +static bool
> +dpif_sflow_port_lookup_callback(void *magic, ofp_port_t ofp_port, struct dpif_sflow_port_lookup *port_lookup)
> +{
> +    /* XXX do we need to acquire ofproto_mutex here?  Or should it be acquired in handle_sflow_upcall() below,
> +     * so that it will not be released until after the sFlow-module has followed the pointers we are giving
> +     * it here and the sflow sample has been fully processed? */
> +
> +    struct ofproto_dpif *ofproto = (struct ofproto_dpif *)magic;
> +    struct ofport_dpif *port = get_ofp_port(ofproto, ofp_port);
> +    if(port) {
> +	port_lookup->ofport = &port->up;
> +	port_lookup->is_tunnel = port->is_tunnel;
> +	if(port->bundle
> +	   && port->bundle->lacp) {
> +	  port_lookup->lacp = port->bundle->lacp;
> +	  port_lookup->lacp_slave = port;
> +	}
> +	return true;
> +    }
> +    return false;
> +}
> +
> +
> static int
> set_sflow(struct ofproto *ofproto_,
>           const struct ofproto_sflow_options *sflow_options)
> {
>     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
>     struct dpif_sflow *ds = ofproto->sflow;
> +    struct ofport_dpif *ofport;
> 
>     if (sflow_options) {
>         if (!ds) {
> -            struct ofport_dpif *ofport;
> -
> -            ds = ofproto->sflow = dpif_sflow_create();
> -            HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
> -                dpif_sflow_add_port(ds, &ofport->up, ofport->odp_port);
> -            }
> -            ofproto->backer->need_revalidate = REV_RECONFIGURE;
> +	    ds = ofproto->sflow = dpif_sflow_create();
> +	    ofproto->backer->need_revalidate = REV_RECONFIGURE;
>         }
> +	dpif_sflow_set_port_lookup_callback(ds, dpif_sflow_port_lookup_callback, (void *)ofproto);
>         dpif_sflow_set_options(ds, sflow_options);
> +	HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
> +	    dpif_sflow_add_port(ds, &ofport->up);
> +	}
>     } else {
>         if (ds) {
>             dpif_sflow_unref(ds);
> @@ -3444,8 +3467,7 @@ handle_sflow_upcall(struct dpif_backer *backer,
> 
>     memset(&cookie, 0, sizeof cookie);
>     memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof cookie.sflow);
> -    dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
> -                        odp_in_port, &cookie);
> +    dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, &cookie);
> }
> 
> static void
> diff --git a/tests/odp.at b/tests/odp.at
> index 469e120..7b5fef1 100644
> --- a/tests/odp.at
> +++ b/tests/odp.at
> @@ -155,7 +155,7 @@ AT_SETUP([OVS datapath actions parsing and formatting - valid forms])
> AT_DATA([actions.txt], [dnl
> 1,2,3
> userspace(pid=555666777)
> -userspace(pid=6633,sFlow(vid=9,pcp=7,output=10))
> +userspace(pid=6633,sFlow(vid=9,pcp=7,n_outputs=1,output_port=10))
> userspace(pid=9765,slow_path())
> userspace(pid=9765,slow_path(cfm))
> userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f))
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index f67c3ab..77bd6b7 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -1831,6 +1831,144 @@ IFCOUNTERS
> AT_CLEANUP
> 
> 
> +dnl Test sFlow tunnel structures.
> +AT_SETUP([ofproto-dpif - sFlow tunnel structures])
> +OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
> +                    options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \
> +                    options:key=5 ofport_request=1\
> +                    -- add-port br0 p2 -- set Interface p2 type=dummy \
> +                    options:ifindex=1002 ofport_request=2])
> +AT_DATA([flows.txt], [dnl
> +actions=output:1
> +])
> +
> +AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
> +
> +AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
> +		br0 65534/100: (dummy)
> +		p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1)
> +		p2 2/2: (dummy: ifindex=1002)
> +])
> +
> +dnl set up sFlow logging
> +dnl ON_EXIT([kill `cat test-sflow.pid`])
> +AT_CHECK([test-sflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > sflow.log], [0], [], [ignore])
> +AT_CAPTURE_FILE([sflow.log])
> +SFLOW_PORT=`parse_listening_port < test-sflow.log`
> +ovs-appctl time/stop
> +ovs-vsctl \
> +   set Bridge br0 sflow=@sf -- \
> +   --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
> +     header=128 sampling=1 polling=0
> +
> +dnl use ofproto/trace to check the actions
> +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: sample(sample=100.0%,actions(userspace(pid=0,sFlow(vid=0,pcp=0,n_outputs=1,output_port=1)))),set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
> +])
> +
> +dnl use netdev-dummy/receive to send a packet that will be sampled
> +AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
> +
> +dnl sleep long enough to get the sFlow datagram flushed out (may be delayed for up to 1 second)
> +for i in `seq 1 30`; do
> +    ovs-appctl time/warp 100
> +done
> +OVS_VSWITCHD_STOP
> +ovs-appctl -t test-sflow exit
> +
> +AT_CHECK([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\
> +	/g']], [0], [dnl
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>2:1000
> +	fsSeqNo=1
> +	tunnel4_out_length=0
> +	tunnel4_out_protocol=47
> +	tunnel4_out_src=2.2.2.2
> +	tunnel4_out_dst=1.1.1.1
> +	tunnel4_out_src_port=0
> +	tunnel4_out_dst_port=0
> +	tunnel4_out_tcp_flags=0
> +	tunnel4_out_tos=0
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=1
> +	dropEvents=0
> +	in_ifindex=1002
> +	in_format=0
> +	out_ifindex=0
> +	out_format=0
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=50-54-00-00-00-07-50-54-00-00-00-05-08-00-45-04-00-28-00-00-00-00-80-06-B9-78-C0-A8-00-01-C0-A8-00-02-00-08-00-09-00-00-00-00-00-00-00-00-50-00-00-00-00-00-00-00-00-00-00-00-00-00
> +])
> +AT_CLEANUP
> +
> +dnl Test sFlow LAG structures
> +AT_SETUP([ofproto-dpif - sFlow LACP structures])
> +
> +OVS_VSWITCHD_START([dnl
> +        add-bond br0 bond p1 p2 --\
> +        set Port bond lacp=active bond-mode=active-backup \
> +            other_config:lacp-time="fast" \
> +            other_config:lacp-system-id=11:22:33:44:55:66 \
> +            other_config:lacp-system-priority=54321 --\
> +        set Interface p1 type=dummy \
> +            other_config:lacp-port-id=11 \
> +            other_config:lacp-port-priority=111 \
> +            other_config:lacp-aggregation-key=3333 --\
> +        set Interface p2 type=dummy \
> +            other_config:lacp-port-id=22 \
> +            other_config:lacp-port-priority=222 \
> +            other_config:lacp-aggregation-key=3333 ])
> +
> +ON_EXIT([kill `cat test-sflow.pid`])
> +AT_CHECK([test-sflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > sflow.log], [0], [], [ignore])
> +AT_CAPTURE_FILE([sflow.log])
> +SFLOW_PORT=`parse_listening_port < test-sflow.log`
> +
> +ovs-appctl time/stop
> +
> +ovs-vsctl \
> +   set Interface p1 options:ifindex=1003 -- \
> +   set Bridge br0 sflow=@sf -- \
> +   --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
> +     header=128 sampling=1 polling=1
> +
> +dnl sleep long enough to get the sFlow datagram flushed out (may be delayed for up to 1 second)
> +for i in `seq 1 30`; do
> +    ovs-appctl time/warp 100
> +done
> +OVS_VSWITCHD_STOP
> +ovs-appctl -t test-sflow exit
> +
> +AT_CHECK([[sort sflow.log | $EGREP 'LACPCOUNTERS|ERROR' | head -n 1 | sed 's/ /\
> +	/g']], [0], [dnl
> +LACPCOUNTERS
> +	sysID=11:22:33:44:55:66
> +	partnerID=00:00:00:00:00:00
> +	aggID=3333
> +	actorAdmin=0x7
> +	actorOper=0xbf
> +	partnerAdmin=0x0
> +	partnerOper=0x2
> +	LACPUDsRx=0
> +	markerPDUsRx=4294967295
> +	markerRespPDUsRx=4294967295
> +	unknownRx=4294967295
> +	illegalRx=0
> +	LACPUDsTx=1
> +	markerPDUsTx=4294967295
> +	markerRespPDUsTx=4294967295
> +])
> +
> +AT_CLEANUP
> 
> dnl Test that basic NetFlow reports flow statistics correctly:
> dnl - The initial packet of a flow are correctly accounted.
> diff --git a/tests/test-sflow.c b/tests/test-sflow.c
> index cba01b9..c53e5ab 100644
> --- a/tests/test-sflow.c
> +++ b/tests/test-sflow.c
> @@ -54,8 +54,11 @@ static unixctl_cb_func test_sflow_exit;
> 
> /* Structure element tag numbers. */
> #define SFLOW_TAG_CTR_IFCOUNTERS 1
> +#define SFLOW_TAG_CTR_LACPCOUNTERS 7
> #define SFLOW_TAG_PKT_HEADER 1
> #define SFLOW_TAG_PKT_SWITCH 1001
> +#define SFLOW_TAG_PKT_TUNNEL4_OUT 1023
> +#define SFLOW_TAG_PKT_TUNNEL4_IN 1024
> 
> struct sflow_addr {
>     enum {
> @@ -99,7 +102,10 @@ struct sflow_xdr {
>     struct {
>         uint32_t HEADER;
>         uint32_t SWITCH;
> +	uint32_t TUNNEL4_OUT;
> +	uint32_t TUNNEL4_IN;
>         uint32_t IFCOUNTERS;
> +	uint32_t LACPCOUNTERS;
>     } offset;
> 
>     /* Flow sample fields. */
> @@ -221,6 +227,42 @@ process_counter_sample(struct sflow_xdr *x)
>         printf(" promiscuous=%"PRIu32, sflowxdr_next(x));
>         printf("\n");
>     }
> +    if(x->offset.LACPCOUNTERS) {
> +	uint8_t *mac;
> +	union {
> +	    uint32_t all;
> +	    struct {
> +		uint8_t actorAdmin;
> +		uint8_t actorOper;
> +		uint8_t partnerAdmin;
> +		uint8_t partnerOper;
> +	    } v;
> +	} state;
> +
> +        sflowxdr_setc(x, x->offset.LACPCOUNTERS);
> +        printf("LACPCOUNTERS");
> +	mac = (uint8_t *)sflowxdr_str(x);
> +	printf(" sysID="ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
> +	sflowxdr_skip(x, 2);
> +	mac = (uint8_t *)sflowxdr_str(x);
> +	printf(" partnerID="ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
> +	sflowxdr_skip(x, 2);
> +	printf(" aggID=%"PRIu32, sflowxdr_next(x));
> +	state.all = sflowxdr_next_n(x);
> +	printf(" actorAdmin=0x%"PRIx32, state.v.actorAdmin);
> +	printf(" actorOper=0x%"PRIx32, state.v.actorOper);
> +	printf(" partnerAdmin=0x%"PRIx32, state.v.partnerAdmin);
> +	printf(" partnerOper=0x%"PRIx32, state.v.partnerOper);
> +	printf(" LACPUDsRx=%"PRIu32, sflowxdr_next(x));
> +	printf(" markerPDUsRx=%"PRIu32, sflowxdr_next(x));
> +	printf(" markerRespPDUsRx=%"PRIu32, sflowxdr_next(x));
> +	printf(" unknownRx=%"PRIu32, sflowxdr_next(x));
> +	printf(" illegalRx=%"PRIu32, sflowxdr_next(x));
> +	printf(" LACPUDsTx=%"PRIu32, sflowxdr_next(x));
> +	printf(" markerPDUsTx=%"PRIu32, sflowxdr_next(x));
> +	printf(" markerRespPDUsTx=%"PRIu32, sflowxdr_next(x));
> +        printf("\n");
> +    }
> }
> 
> static char
> @@ -251,6 +293,25 @@ print_hex(const char *a, int len, char *buf, int bufLen)
>     return b;
> }
> 
> +static void
> +print_struct_ipv4(struct sflow_xdr *x, const char *prefix)
> +{
> +    uint32_t src, dst;
> +
> +    printf(" %s_length=%"PRIu32,    prefix, sflowxdr_next(x));
> +    printf(" %s_protocol=%"PRIu32,  prefix, sflowxdr_next(x));
> +
> +    src = sflowxdr_next_n(x);
> +    dst = sflowxdr_next_n(x);
> +    printf(" %s_src="IP_FMT,        prefix, IP_ARGS(src));
> +    printf(" %s_dst="IP_FMT,        prefix, IP_ARGS(dst));
> +
> +    printf(" %s_src_port=%"PRIu32,  prefix, sflowxdr_next(x));
> +    printf(" %s_dst_port=%"PRIu32,  prefix, sflowxdr_next(x));
> +    printf(" %s_tcp_flags=%"PRIu32, prefix, sflowxdr_next(x));
> +    printf(" %s_tos=%"PRIu32,       prefix, sflowxdr_next(x));
> +}
> +
> #define SFLOW_HEX_SCRATCH 1024
> 
> static void
> @@ -266,6 +327,16 @@ process_flow_sample(struct sflow_xdr *x)
>                x->agentIPStr, x->dsClass, x->dsIndex);
>         printf(" fsSeqNo=%"PRIu32, x->fsSeqNo);
> 
> +        if (x->offset.TUNNEL4_IN) {
> +            sflowxdr_setc(x, x->offset.TUNNEL4_IN);
> +	    print_struct_ipv4(x, "tunnel4_in");
> +        }
> +
> +        if (x->offset.TUNNEL4_OUT) {
> +            sflowxdr_setc(x, x->offset.TUNNEL4_OUT);
> +	    print_struct_ipv4(x, "tunnel4_out");
> +        }
> +
>         if (x->offset.SWITCH) {
>             sflowxdr_setc(x, x->offset.SWITCH);
>             printf(" in_vlan=%"PRIu32, sflowxdr_next(x));
> @@ -374,6 +445,9 @@ process_datagram(struct sflow_xdr *x)
>                 case SFLOW_TAG_CTR_IFCOUNTERS:
>                     sflowxdr_mark_unique(x, &x->offset.IFCOUNTERS);
>                     break;
> +                case SFLOW_TAG_CTR_LACPCOUNTERS:
> +                    sflowxdr_mark_unique(x, &x->offset.LACPCOUNTERS);
> +                    break;
> 
>                     /* Add others here... */
>                 }
> @@ -442,6 +516,14 @@ process_datagram(struct sflow_xdr *x)
>                     sflowxdr_mark_unique(x, &x->offset.SWITCH);
>                     break;
> 
> +		case SFLOW_TAG_PKT_TUNNEL4_OUT:
> +                    sflowxdr_mark_unique(x, &x->offset.TUNNEL4_OUT);
> +                    break;
> +
> +		case SFLOW_TAG_PKT_TUNNEL4_IN:
> +                    sflowxdr_mark_unique(x, &x->offset.TUNNEL4_IN);
> +                    break;
> +
>                     /* Add others here... */
>                 }
> 
> -- 
> 1.8.1.4
> 
> 
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list