[ovs-dev] [PATCH] sFlow LAG and Tunnel export

Neil McKee neil.mckee at inmon.com
Thu Nov 28 13:20:26 UTC 2013


I rebased and cleaned up this patch, and included a longer description
to capture the reasons why it's OK to leave some fields unpopulated.

Please consider.

Neil


>From 4d9113d6e15d195e43c0472a2951ecde09c1e46c Mon Sep 17 00:00:00 2001
From: Neil McKee <neil.mckee at inmon.com>
Date: Thu, 28 Nov 2013 05:46:44 -0700
Subject: [PATCH] sFlow LAG and Tunnel export

Eliminates hash-table from ofproto-dpif-sflow. Instead a callback
to ofproto-dpif is used to look up counter and packet info. The
ofp-port is now supplied in the sFlow cookie for this purpose,
rather than the odp-port as it was before.  This brings it into
line with other upcall consumers.  The cookie is split
into two variants: one for single-output and one for multi-output,
which serves to keep it under 64-bits in both cases. The
sFlow LAG and Tunnel structures are added.  Not all
fields are populated, but this is allowed by the standard.
The simple presence of the Tunnel structure would be worthwhile
even if no fields were populated because it signals the presence
of a tunnel to the sFlow collector.  More details can always be
filled in later if performance and complexity considerations
allow. It is more likely that this can happen on the receive
path than the transmit path, because the transmit path may
include tunnels defined in openflow rules.  With sFlow
monitoring the expectation is that observations from the
upstream switch port can always be used to fill in any gaps,
and the preference is always to minimize the impact on
performance, so this approach is within the spirit of the
standard.
Finally, new unit tests are added for the Tunnel and LAG export.

Signed-off-by: Neil McKee <neil.mckee at inmon.com>
---
 lib/lacp.c                    |   64 ++++++
 lib/lacp.h                    |   23 ++
 lib/odp-util.c                |   53 +++--
 lib/odp-util.h                |   19 +-
 lib/sflow.h                   |   45 +++-
 lib/sflow_agent.c             |   15 ++
 lib/sflow_api.h               |    1 +
 lib/sflow_receiver.c          |   30 +++
 ofproto/ofproto-dpif-sflow.c  |  496
+++++++++++++++++++++++++++++++----------
 ofproto/ofproto-dpif-sflow.h  |   25 ++-
 ofproto/ofproto-dpif-upcall.c |    6 +-
 ofproto/ofproto-dpif-xlate.c  |   58 ++---
 ofproto/ofproto-dpif.c        |   39 +++-
 tests/odp.at                  |    2 +-
 tests/ofproto-dpif.at         |  211 ++++++++++++++++++
 tests/test-sflow.c            |  104 +++++++++
 16 files changed, 992 insertions(+), 199 deletions(-)

diff --git a/lib/lacp.c b/lib/lacp.c
index fce65b3..256ce75 100644
--- a/lib/lacp.c
+++ b/lib/lacp.c
@@ -123,6 +123,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;
@@ -323,9 +328,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;
     }
@@ -538,6 +545,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
@@ -967,3 +975,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 593b80d..4295f7b 100644
--- a/lib/lacp.h
+++ b/lib/lacp.h
@@ -70,4 +70,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 0fd1c51..3112e33 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -286,12 +286,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) {
                 ds_put_cstr(ds, ",slow_path(");
@@ -317,8 +326,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]);
             }
@@ -489,7 +498,8 @@ parse_odp_action(const char *s, const struct simap
*port_names,

     {
         uint32_t pid;
-        uint32_t output;
+        uint32_t n_outputs;
+        uint32_t output_port;
         uint32_t probability;
         uint32_t collector_set_id;
         uint32_t obs_domain_id;
@@ -500,9 +510,9 @@ parse_odp_action(const char *s, const struct simap
*port_names,
         if (ovs_scan(s, "userspace(pid=%"SCNi32")%n", &pid, &n)) {
             odp_put_userspace_action(pid, NULL, 0, actions);
             return n;
-        } else if (ovs_scan(s, "userspace(pid=%"SCNi32",sFlow(vid=%i,"
-                            "pcp=%i,output=%"SCNi32"))%n",
-                            &pid, &vid, &pcp, &output, &n)) {
+        } else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i,"
+
 "pcp=%i,n_outputs=%"SCNu32",output_port=%"SCNu32"))%n",
+                          &pid, &vid, &pcp, &n_outputs, &output_port, &n)
> 0 && n > 0) {
             union user_action_cookie cookie;
             uint16_t tci;

@@ -511,11 +521,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 (ovs_scan(s, "userspace(pid=%"SCNi32",slow_path%n",
                             &pid, &n)) {
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 821b2c4..3c5ce02 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -186,7 +186,8 @@ enum slow_path_reason commit_odp_actions(const struct
flow *,

 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. */
@@ -197,10 +198,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..239e9a9 100644
--- a/lib/sflow.h
+++ b/lib/sflow.h
@@ -267,6 +267,10 @@ typedef struct _SFLExtended_vlan_tunnel {
     innermost. */
 } SFLExtended_vlan_tunnel;

+typedef struct _SFLExtended_vni {
+    uint32_t vni;            /* virtual network identifier */
+} SFLExtended_vni;
+
 enum SFLFlow_type_tag {
     /* enterprise = 0, format = ... */
     SFLFLOW_HEADER    = 1,      /* Packet headers are sampled */
@@ -285,6 +289,10 @@ 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,
+    SFLFLOW_EX_VNI_EGRESS          = 1029,
+    SFLFLOW_EX_VNI_INGRESS         = 1030,
 };

 typedef union _SFLFlow_type {
@@ -304,6 +312,7 @@ typedef union _SFLFlow_type {
     SFLExtended_mpls_FTN mpls_ftn;
     SFLExtended_mpls_LDP_FEC mpls_ldp_fec;
     SFLExtended_vlan_tunnel vlan_tunnel;
+    SFLExtended_vni tunnel_vni;
 } SFLFlow_type;

 typedef struct _SFLFlow_sample_element {
@@ -382,6 +391,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 +490,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 +527,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 +537,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..282b175 100644
--- a/lib/sflow_receiver.c
+++ b/lib/sflow_receiver.c
@@ -460,6 +460,14 @@ 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:
+ case SFLFLOW_EX_IPV4_TUNNEL_INGRESS:
+    elemSiz = sizeof(SFLSampled_ipv4);
+    break;
+ case SFLFLOW_EX_VNI_EGRESS:
+ case SFLFLOW_EX_VNI_INGRESS:
+    elemSiz = sizeof(SFLExtended_vni);
+    break;
  default:
     sflError(receiver, "unexpected packet_data_tag");
     return -1;
@@ -556,6 +564,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);
@@ -587,6 +597,11 @@ int sfl_receiver_writeFlowSample(SFLReceiver
*receiver, SFL_FLOW_SAMPLE_TYPE *fs
     case SFLFLOW_EX_MPLS_FTN: putMplsFtn(receiver,
&elem->flowType.mpls_ftn); break;
     case SFLFLOW_EX_MPLS_LDP_FEC: putMplsLdpFec(receiver,
&elem->flowType.mpls_ldp_fec); break;
     case SFLFLOW_EX_VLAN_TUNNEL: putVlanTunnel(receiver,
&elem->flowType.vlan_tunnel); break;
+    case SFLFLOW_EX_VNI_EGRESS:
+    case SFLFLOW_EX_VNI_INGRESS:
+ putNet32(receiver, elem->flowType.tunnel_vni.vni);
+ break;
+
     default:
  sflError(receiver, "unexpected packet_data_tag");
  return -1;
@@ -630,6 +645,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 +747,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/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 158887f..1ae2f10 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,25 @@
 #include "vlog.h"
 #include "lib/odp-util.h"
 #include "ofproto-provider.h"
+#include "lacp.h"
+#include "lib/byte-order.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 +135,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 +143,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 +173,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 +187,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 +340,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 +384,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 +441,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 +527,220 @@ 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;
+}
+
+enum sflow_tunnel_type {
+    SFLOW_TUNNEL_UNKNOWN,
+    SFLOW_TUNNEL_GRE,
+    SFLOW_TUNNEL_GRE64,
+    SFLOW_TUNNEL_IPSEC_GRE,
+    SFLOW_TUNNEL_IPSEC_GRE64,
+    SFLOW_TUNNEL_VXLAN,
+    SFLOW_TUNNEL_LISP
+};
+
+static enum sflow_tunnel_type
+dpif_sflow_tunnel_type(const char *netdev_type)
+{
+    if(!strcmp(netdev_type, "gre")) {
+ return SFLOW_TUNNEL_GRE;
+    }
+    if(!strcmp(netdev_type, "gre64")) {
+ return SFLOW_TUNNEL_GRE64;
+    }
+    if(!strcmp(netdev_type, "ipsec_gre")) {
+ return SFLOW_TUNNEL_IPSEC_GRE;
+    }
+    if(!strcmp(netdev_type, "ipsec_gre64")) {
+ return SFLOW_TUNNEL_IPSEC_GRE64;
+    }
+    if(!strcmp(netdev_type, "ipsec_gre64")) {
+ return SFLOW_TUNNEL_IPSEC_GRE64;
+    }
+    if(!strcmp(netdev_type, "vxlan")) {
+ return SFLOW_TUNNEL_VXLAN;
+    }
+    if(!strcmp(netdev_type, "lisp")) {
+ return SFLOW_TUNNEL_LISP;
+    }
+    return SFLOW_TUNNEL_UNKNOWN;
+}
+
+enum sflow_tunnel_direction {
+    SFLOW_TUNNEL_INGRESS=1,
+    SFLOW_TUNNEL_EGRESS=2
+};
+
+enum sflow_tunnel_flags {
+    SFLOW_TUNNEL_FLAGS_NONE=0,
+    SFLOW_TUNNEL_FLAGS_IPV4=1,
+    SFLOW_TUNNEL_FLAGS_VNI=2
+};
+
+/* Implements sFlow standard:  http://sflow.org/sflow_tunnels.txt.
+ * Returns bitmask of enum sflow_tunnel_flags.
+ */
+static int
+dpif_sflow_tunnel(SFLSampled_ipv4 *tunnel4,
+  SFLExtended_vni *tunnel_vni,
+  const enum sflow_tunnel_direction direction,
+  const struct dpif_sflow_port_lookup *port_lookup,
+  const struct flow *flow)
+{
+    const struct netdev_tunnel_config *tcfg;
+
+    tcfg = netdev_get_tunnel_config(port_lookup->ofport->netdev);
+    if(tcfg == NULL) {
+ return SFLOW_TUNNEL_FLAGS_NONE;
+    }
+    else {
+      const char *netdev_type;
+      enum sflow_tunnel_type tunnel_type;
+ int ret = SFLOW_TUNNEL_FLAGS_IPV4;
+
+ /* Infer the ip protocol that was (or will be)
+ * seen on the wire from the tunnel type.
+ */
+ netdev_type = netdev_get_type(port_lookup->ofport->netdev);
+ tunnel_type = dpif_sflow_tunnel_type(netdev_type);
+ switch(tunnel_type) {
+ case SFLOW_TUNNEL_GRE:
+ case SFLOW_TUNNEL_GRE64:
+    tunnel4->protocol = IPPROTO_GRE;
+    break;
+ case SFLOW_TUNNEL_VXLAN:
+ case SFLOW_TUNNEL_LISP:
+    tunnel4->protocol = IPPROTO_UDP;
+    break;
+ case SFLOW_TUNNEL_IPSEC_GRE:
+ case SFLOW_TUNNEL_IPSEC_GRE64:
+    tunnel4->protocol = IPPROTO_ESP;
+    break;
+ case SFLOW_TUNNEL_UNKNOWN:
+    break;
+ }
+
+ if(direction == SFLOW_TUNNEL_INGRESS) {
+    tunnel4->src_ip.addr = flow->tunnel.ip_src;
+    tunnel4->dst_ip.addr = flow->tunnel.ip_dst;
+    tunnel4->tos = flow->tunnel.ip_tos;
+
+    /* XXX For VXLAN and LISP where the
+     * l4_ports are relevant too, can we
+     * reach into the struct ofpbuf *packet to
+     * read them out for ingress tunnel packets?
+     * They doesn't seem to be recorded for
+     * us in the flow->tunnel struct.
+     */
+    /* tunnel4->src_port = flow->tunnel.src_port; */
+    /* tunnel4->dst_port = flow->tunnel.dst_port; */
+
+    if(tunnel_type == SFLOW_TUNNEL_GRE64
+       || tunnel_type == SFLOW_TUNNEL_IPSEC_GRE64) {
+ /* XXX The key is 64-bit (appears on the wire
+ * as 32-bit key + overload of 32-bit sequence_no
+ * field). However we only have 32-bits in the sFlow
+ * VNI field. If this key really can be treated exactly
+ * like a VNI then perhaps a new structure should be
+ * added to report it?
+ */
+    }
+    else {
+ if(flow->tunnel.flags & FLOW_TNL_F_KEY) {
+    /* This assumes the 32-bit key is the least-significant
+       32-bits of flow->tunnel.tun_id. */
+    tunnel_vni->vni = (uint32_t)(ntohll(flow->tunnel.tun_id));
+    ret |= SFLOW_TUNNEL_FLAGS_VNI;
+ }
+ else if(tcfg->in_key_present) {
+    /* If the netdev_tunnel_config specifies an
+     * "in_key" then it acts as a filter so this packet
+     * would only have reached here if it had this key.
+     */
+    /* XXX. This might be redundant if flow->tunnel.tun_id
+     * is always filled in on ingress tunnel packets. */
+    tunnel_vni->vni = tcfg->in_key_flow ?
+ ntohll(flow->tunnel.tun_id) :
+ ntohll(tcfg->in_key);
+    ret |= SFLOW_TUNNEL_FLAGS_VNI;
+ }
+    }
+ }
+ else {
+    /* The EGRESS path is rather different.  The
+     * details on the tunneling that is going to happen
+     * may only be encoded in the actions. */
+
+    /* XXX Is this what tcfg->ip_src_flow means?
+     * Probably not.  We may have to look at the
+     * openflow actions to figure out what the
+     * details are going to be.
+     */
+    tunnel4->src_ip.addr = tcfg->ip_src_flow ?
+ flow->tunnel.ip_src :
+ tcfg->ip_src;
+
+    tunnel4->dst_ip.addr = tcfg->ip_dst_flow ?
+ flow->tunnel.ip_dst :
+ tcfg->ip_dst;
+
+    /* The tos=inherit option means that the tos bits
+       are copied from the flow being tunneled. */
+    tunnel4->tos = tcfg->tos_inherit ?
+ flow->nw_tos :
+ tcfg->tos;
+
+    /* src_port, dst_port only relevant for VXLAN and LISP. */
+    if(tunnel_type == SFLOW_TUNNEL_VXLAN
+       || tunnel_type == SFLOW_TUNNEL_LISP) {
+ /* src_port is added down in the datapath just before sending.
+ * It is computed as a hash over the whole flow, including the
+ * tunneled addresses (to add entropy for ECMP load-balancing).
+ * Theoretically we could compute the exact same digest here,
+ * but that would represent an unwelcome dependency.
+ */
+ tunnel4->src_port = 0;
+ tunnel4->dst_port = tcfg->dst_port;
+    }
+
+    /* Where applicable, the VNI is the tunnel key. */
+    if(tunnel_type == SFLOW_TUNNEL_GRE64
+       || tunnel_type == SFLOW_TUNNEL_IPSEC_GRE64) {
+ /* ignore non-standard 64-bit key for now.
+ * See comment for ingress path above. */
+    }
+    else {
+ /* XXX what does tcfg->out_key_flow really mean? */
+ if(tcfg->out_key_present) {
+    tunnel_vni->vni = tcfg->out_key_flow ?
+ ntohll(flow->tunnel.tun_id) :
+ ntohll(tcfg->out_key);
+    ret |= SFLOW_TUNNEL_FLAGS_VNI;
+ }
+    }
+ }
+
+ return ret;
+    }
 }

 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 +749,15 @@ 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;
+    SFLFlow_sample_element in_vniElem, out_vniElem;
+    int in_ifindex, out_ifindex;
+    int in_tunnel_flags, out_tunnel_flags;

     ovs_mutex_lock(&mutex);
+
     sampler = ds->sflow_agent->samplers;
     if (!sampler) {
         goto out;
@@ -590,11 +766,35 @@ 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);
+    memset(&in_vniElem, 0, sizeof in_vniElem);
+    in_tunnel_flags = dpif_sflow_tunnel(&in_tunnelElem.flowType.ipv4,
+ &in_vniElem.flowType.tunnel_vni,
+ SFLOW_TUNNEL_INGRESS,
+ &in_lookup,
+ flow);
+    if(in_tunnel_flags) {
+ in_tunnelElem.tag = SFLFLOW_EX_IPV4_TUNNEL_INGRESS;
+ SFLADD_ELEMENT(&fs, &in_tunnelElem);
+ if(in_tunnel_flags & SFLOW_TUNNEL_FLAGS_VNI) {
+    in_vniElem.tag = SFLFLOW_EX_VNI_INGRESS;
+    SFLADD_ELEMENT(&fs, &in_vniElem);
+ }
+    }
+ }
     }

     /* Make the assumption that the random number generator in the
datapath converges
@@ -623,11 +823,63 @@ 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) {
+ 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) {
+ 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);
+    memset(&out_vniElem, 0, sizeof out_vniElem);
+    out_tunnel_flags = dpif_sflow_tunnel(&out_tunnelElem.flowType.ipv4,
+ &out_vniElem.flowType.tunnel_vni,
+ SFLOW_TUNNEL_EGRESS,
+ &out_lookup,
+ flow);
+    if(out_tunnel_flags) {
+ out_tunnelElem.tag = SFLFLOW_EX_IPV4_TUNNEL_EGRESS;
+ SFLADD_ELEMENT(&fs, &out_tunnelElem);
+ if(out_tunnel_flags & SFLOW_TUNNEL_FLAGS_VNI) {
+    out_vniElem.tag = SFLFLOW_EX_VNI_EGRESS;
+    SFLADD_ELEMENT(&fs, &out_vniElem);
+ }
+    }
+ }
+    }
+ }
+    }
+    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);
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 dde6430..9f54d0d 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -469,7 +469,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) {
@@ -711,8 +712,7 @@ handle_upcalls(struct udpif *udpif, struct list
*upcalls)
                 memset(&cookie, 0, sizeof cookie);
                 memcpy(&cookie, nl_attr_get(dupcall->userdata),
                        sizeof cookie.sflow);
-                dpif_sflow_received(sflow, dupcall->packet, &flow,
odp_in_port,
-                                    &cookie);
+                dpif_sflow_received(sflow, dupcall->packet, &flow,
&cookie);
                 dpif_sflow_unref(sflow);
             }
             break;
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index a331c0b..8c17d93 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -178,7 +178,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.
*/
+    ofp_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.
*/

@@ -1479,34 +1479,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;
     }
 }

@@ -1515,7 +1500,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;
@@ -1525,8 +1510,8 @@ 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);
+    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);
@@ -1578,8 +1563,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;
 }

@@ -1607,10 +1592,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
@@ -1780,7 +1766,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 7df0450..2e153b1 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1753,7 +1753,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;
@@ -1856,24 +1856,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);
diff --git a/tests/odp.at b/tests/odp.at
index b505345..fd4e32d 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -220,7 +220,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 b78e156..6c5409e 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -2071,6 +2071,217 @@ 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=vxlan \
+                    options:local_ip=1.1.1.1 options:remote_ip=2.2.2.2 \
+                    options:key=5 options:tos=inherit 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])
+
+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 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=17
+ tunnel4_out_src=1.1.1.1
+ tunnel4_out_dst=2.2.2.2
+ tunnel4_out_src_port=0
+ tunnel4_out_dst_port=46354
+ tunnel4_out_tcp_flags=0
+ tunnel4_out_tos=4
+ tunnel_out_vni=5
+ 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 tunnel structures ingress
+AT_SETUP([ofproto-dpif - sFlow tunnel structures ingress])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
+                    options:local_ip=1.1.1.1 options:remote_ip=2.2.2.2 \
+                    options:key=5 options:tos=inherit ofport_request=1 \
+                    -- add-port br0 p2 -- set Interface p2 type=dummy \
+                    options:ifindex=1002 ofport_request=2 -- \
+    add-br br1 -- \
+    set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+    set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
+    fail-mode=secure -- \
+     -- add-port br1 p3 -- set interface p3 type=vxlan \
+    options:local_ip=2.2.2.2 options:remote_ip=1.1.1.1 \
+    options:key=5  options:tos=inherit ofport_request=3 \
+    -- add-port br1 p4 -- set Interface p4 type=dummy \
+                    options:ifindex=1004 ofport_request=4 ])
+
+AT_DATA([flows.txt], [dnl
+actions=output:1
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+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
+ovs-vsctl \
+   set Bridge br1 sflow=@sf -- \
+   --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
+     header=128 sampling=1 polling=0
+
+ifconfig -a
+ip route
+
+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=17
+ tunnel4_out_src=1.1.1.1
+ tunnel4_out_dst=2.2.2.2
+ tunnel4_out_src_port=0
+ tunnel4_out_dst_port=46354
+ tunnel4_out_tcp_flags=0
+ tunnel4_out_tos=4
+ tunnel_out_vni=5
+ 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..060864d 100644
--- a/tests/test-sflow.c
+++ b/tests/test-sflow.c
@@ -54,8 +54,13 @@ 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
+#define SFLOW_TAG_PKT_TUNNEL_VNI_OUT 1029
+#define SFLOW_TAG_PKT_TUNNEL_VNI_IN 1030

 struct sflow_addr {
     enum {
@@ -99,7 +104,12 @@ struct sflow_xdr {
     struct {
         uint32_t HEADER;
         uint32_t SWITCH;
+ uint32_t TUNNEL4_OUT;
+ uint32_t TUNNEL4_IN;
+ uint32_t TUNNEL_VNI_OUT;
+ uint32_t TUNNEL_VNI_IN;
         uint32_t IFCOUNTERS;
+ uint32_t LACPCOUNTERS;
     } offset;

     /* Flow sample fields. */
@@ -221,6 +231,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 +297,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 +331,26 @@ 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.TUNNEL_VNI_IN) {
+            sflowxdr_setc(x, x->offset.TUNNEL_VNI_IN);
+    printf( " tunnel_in_vni=%"PRIu32, sflowxdr_next(x));
+        }
+
+        if (x->offset.TUNNEL_VNI_OUT) {
+            sflowxdr_setc(x, x->offset.TUNNEL_VNI_OUT);
+    printf( " tunnel_out_vni=%"PRIu32, sflowxdr_next(x));
+        }
+
         if (x->offset.SWITCH) {
             sflowxdr_setc(x, x->offset.SWITCH);
             printf(" in_vlan=%"PRIu32, sflowxdr_next(x));
@@ -374,6 +459,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 +530,22 @@ 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;
+
+ case SFLOW_TAG_PKT_TUNNEL_VNI_OUT:
+                    sflowxdr_mark_unique(x, &x->offset.TUNNEL_VNI_OUT);
+                    break;
+
+ case SFLOW_TAG_PKT_TUNNEL_VNI_IN:
+                    sflowxdr_mark_unique(x, &x->offset.TUNNEL_VNI_IN);
+                    break;
+
                     /* Add others here... */
                 }

-- 
1.7.9.5

-- 
------
Neil McKee
InMon Corp.
http://www.inmon.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openvswitch.org/pipermail/ovs-dev/attachments/20131128/45109df2/attachment-0003.html>


More information about the dev mailing list