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

Neil Mckee neil.mckee at inmon.com
Wed Oct 9 05:09:29 UTC 2013


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. */
+    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;
+    }
+    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





More information about the dev mailing list