sflow: Export LAG, PORTNAME, and OPENFLOWPORT information also.
authorNeil McKee <neil.mckee@inmon.com>
Fri, 27 Jun 2014 18:19:59 +0000 (11:19 -0700)
committerBen Pfaff <blp@nicira.com>
Tue, 11 Nov 2014 21:28:40 +0000 (13:28 -0800)
Export standard sFlow LAG, PORTNAME and OPENFLOWPORT structures with each
counter-sample. Add unit-test for sFlow-LAG. Adjust other unit-tests to
accommodate these new annotations.

The sFlow-LAG structures are important for topology discovery, for
troubleshooting LAG instability,  and for correctly combining
sFlow feeds from multiple sources.

The OPENFLOWPORT and PORTNAME structures are important for systems that
aim to combine sFlow monitoring with OpenFlow controls,  as they
provide straightforward mapping (1) between sFlow agent IP and OpenFlow
datapath-id,  and (2) between interface name,ifIndex and OpenFlow
port number.

Signed-off-by: Neil McKee <neil.mckee@inmon.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
14 files changed:
NEWS
lib/lacp.c
lib/lacp.h
lib/sflow.h
lib/sflow_agent.c
lib/sflow_api.h
lib/sflow_receiver.c
ofproto/ofproto-dpif-sflow.c
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
tests/ofproto-dpif.at
tests/test-sflow.c

diff --git a/NEWS b/NEWS
index bc0ff5f..5ba8f4d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,8 @@ Post-v2.3.0
      release. The protocol is documented at
      http://tools.ietf.org/html/draft-gross-geneve-00
    - The OVS database now reports controller rate limiting statistics.
+   - sflow now exports information about LACP-based bonds, port names, and
+     OpenFlow port numbers.
    - ovs-dpctl functionality is now available for datapaths integrated
      into ovs-vswitchd, via ovs-appctl.  Some existing ovs-appctl
      commands are now redundant and will be removed in a future
index 3b50d46..ce5343b 100644 (file)
@@ -125,6 +125,10 @@ 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. */
+
+    uint32_t count_rx_pdus;       /* dot3adAggPortStatsLACPDUsRx */
+    uint32_t count_rx_pdus_bad;   /* dot3adAggPortStatsIllegalRx */
+    uint32_t count_tx_pdus;       /* dot3adAggPortStatsLACPDUsTx */
 };
 
 static struct ovs_mutex mutex;
@@ -328,9 +332,11 @@ lacp_process_packet(struct lacp *lacp, const void *slave_,
     if (!slave) {
         goto out;
     }
+    slave->count_rx_pdus++;
 
     pdu = parse_lacp_packet(packet);
     if (!pdu) {
+       slave->count_rx_pdus_bad++;
         VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name);
         goto out;
     }
@@ -548,6 +554,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->count_tx_pdus++;
 
             duration = (slave->partner.state & LACP_STATE_TIME
                         ? LACP_FAST_TIME_TX
@@ -978,3 +985,58 @@ lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
 out:
     lacp_unlock();
 }
+
+/* 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->count_rx_pdus;
+       stats->dot3adAggPortStatsIllegalRx = slave->count_rx_pdus_bad;
+       stats->dot3adAggPortStatsLACPDUsTx = slave->count_tx_pdus;
+    } else {
+        ret = false;
+    }
+    ovs_mutex_unlock(&mutex);
+    return ret;
+
+}
index 593b80d..4295f7b 100644 (file)
@@ -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 */
index c6cde7f..dfe138f 100644 (file)
@@ -271,6 +271,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 */
@@ -289,6 +293,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 {
@@ -308,6 +316,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 {
@@ -386,6 +395,9 @@ typedef struct _SFLFlow_sample_expanded {
 
 /* Counter types */
 
+#define SFL_UNDEF_COUNTER(c) c=-1
+#define SFL_UNDEF_GAUGE(c) c=0
+
 /* Generic interface counters - see RFC 1573, 2233 */
 
 typedef struct _SFLIf_counters {
@@ -414,6 +426,8 @@ typedef struct _SFLIf_counters {
     u_int32_t ifPromiscuousMode;
 } SFLIf_counters;
 
+#define SFL_CTR_GENERIC_XDR_SIZE 88
+
 /* Ethernet interface counters - see RFC 2358 */
 typedef struct _SFLEthernet_counters {
     u_int32_t dot3StatsAlignmentErrors;
@@ -431,6 +445,8 @@ typedef struct _SFLEthernet_counters {
     u_int32_t dot3StatsSymbolErrors;
 } SFLEthernet_counters;
 
+#define SFL_CTR_ETHERNET_XDR_SIZE 52
+
 /* Token ring counters - see RFC 1748 */
 
 typedef struct _SFLTokenring_counters {
@@ -482,6 +498,51 @@ typedef struct _SFLVlan_counters {
     u_int32_t discards;
 } SFLVlan_counters;
 
+/* OpenFlow port */
+typedef struct {
+    u_int64_t datapath_id;
+    u_int32_t port_no;
+} SFLOpenFlowPort;
+
+#define SFL_CTR_OPENFLOWPORT_XDR_SIZE 12
+
+/* port name */
+typedef struct {
+    SFLString portName;
+} SFLPortName;
+
+#define SFL_MAX_PORTNAME_LEN 255
+
+/* 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;
+
+#define SFL_CTR_LACP_XDR_SIZE 56
+
 /* Counters data */
 
 enum SFLCounters_type_tag {
@@ -490,7 +551,10 @@ enum SFLCounters_type_tag {
     SFLCOUNTERS_ETHERNET     = 2,
     SFLCOUNTERS_TOKENRING    = 3,
     SFLCOUNTERS_VG           = 4,
-    SFLCOUNTERS_VLAN         = 5
+    SFLCOUNTERS_VLAN         = 5,
+    SFLCOUNTERS_LACP         = 7,
+    SFLCOUNTERS_OPENFLOWPORT = 1004,
+    SFLCOUNTERS_PORTNAME     = 1005
 };
 
 typedef union _SFLCounters_type {
@@ -499,6 +563,9 @@ typedef union _SFLCounters_type {
     SFLTokenring_counters tokenring;
     SFLVg_counters vg;
     SFLVlan_counters vlan;
+    SFLLACP_counters lacp;
+    SFLOpenFlowPort ofPort;
+    SFLPortName portName;
 } SFLCounters_type;
 
 typedef struct _SFLCounters_sample_element {
index 817420d..9c2e028 100644 (file)
@@ -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    __________________
   -----------------___________________________------------------
index 3cc060b..2730a4c 100644 (file)
@@ -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 */
index e6fc9a7..aff62fe 100644 (file)
@@ -464,6 +464,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;
@@ -560,6 +568,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);
@@ -591,6 +601,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;
@@ -629,11 +644,14 @@ static int computeCountersSampleSize(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_
        cs->num_elements++;
        siz += 8; /* tag, length */
        switch(elem->tag) {
-       case SFLCOUNTERS_GENERIC:  elemSiz = sizeof(elem->counterBlock.generic); break;
-       case SFLCOUNTERS_ETHERNET: elemSiz = sizeof(elem->counterBlock.ethernet); break;
+       case SFLCOUNTERS_GENERIC:  elemSiz = SFL_CTR_GENERIC_XDR_SIZE; break;
+       case SFLCOUNTERS_ETHERNET: elemSiz = SFL_CTR_ETHERNET_XDR_SIZE; break;
        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 = SFL_CTR_LACP_XDR_SIZE; break;
+       case SFLCOUNTERS_OPENFLOWPORT: elemSiz = SFL_CTR_OPENFLOWPORT_XDR_SIZE; break;
+       case SFLCOUNTERS_PORTNAME: elemSiz = stringEncodingLength(&elem->counterBlock.portName.portName); break;
        default:
            sflError(receiver, "unexpected counters_tag");
            return -1;
@@ -735,6 +753,27 @@ 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;
+           case SFLCOUNTERS_OPENFLOWPORT:
+               putNet64(receiver, elem->counterBlock.ofPort.datapath_id);
+               putNet32(receiver, elem->counterBlock.ofPort.port_no);
+               break;
+           case SFLCOUNTERS_PORTNAME:
+               putString(receiver, &elem->counterBlock.portName.portName);
+               break;
            default:
                sflError(receiver, "unexpected counters_tag");
                return -1;
index 92e2ae8..60b65a3 100644 (file)
@@ -40,6 +40,7 @@
 #include "vlog.h"
 #include "lib/odp-util.h"
 #include "ofproto-provider.h"
+#include "lacp.h"
 
 VLOG_DEFINE_THIS_MODULE(sflow);
 
@@ -166,12 +167,14 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
     OVS_REQUIRES(mutex)
 {
     struct dpif_sflow *ds = ds_;
-    SFLCounters_sample_element elem;
+    SFLCounters_sample_element elem, lacp_elem, of_elem, name_elem;
     enum netdev_features current;
     struct dpif_sflow_port *dsp;
     SFLIf_counters *counters;
     struct netdev_stats stats;
     enum netdev_flags flags;
+    struct lacp_slave_stats lacp_stats;
+    const char *ifName;
 
     dsp = dpif_sflow_find_port(ds, u32_to_odp(poller->bridgePort));
     if (!dsp) {
@@ -223,6 +226,59 @@ sflow_agent_get_counters(void *ds_, SFLPoller *poller,
     counters->ifPromiscuousMode = 0;
 
     SFLADD_ELEMENT(cs, &elem);
+
+    /* Include LACP counters and identifiers if this port is part of a LAG. */
+    if (ofproto_port_get_lacp_stats(dsp->ofport, &lacp_stats) == 0) {
+       memset(&lacp_elem, 0, sizeof lacp_elem);
+       lacp_elem.tag = SFLCOUNTERS_LACP;
+       memcpy(&lacp_elem.counterBlock.lacp.actorSystemID,
+              lacp_stats.dot3adAggPortActorSystemID,
+              ETH_ADDR_LEN);
+       memcpy(&lacp_elem.counterBlock.lacp.partnerSystemID,
+              lacp_stats.dot3adAggPortPartnerOperSystemID,
+              ETH_ADDR_LEN);
+       lacp_elem.counterBlock.lacp.attachedAggID =
+           lacp_stats.dot3adAggPortAttachedAggID;
+       lacp_elem.counterBlock.lacp.portState.v.actorAdmin =
+           lacp_stats.dot3adAggPortActorAdminState;
+       lacp_elem.counterBlock.lacp.portState.v.actorOper =
+           lacp_stats.dot3adAggPortActorOperState;
+       lacp_elem.counterBlock.lacp.portState.v.partnerAdmin =
+           lacp_stats.dot3adAggPortPartnerAdminState;
+       lacp_elem.counterBlock.lacp.portState.v.partnerOper =
+           lacp_stats.dot3adAggPortPartnerOperState;
+       lacp_elem.counterBlock.lacp.LACPDUsRx =
+           lacp_stats.dot3adAggPortStatsLACPDUsRx;
+       SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerPDUsRx);
+       SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerResponsePDUsRx);
+       SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.unknownRx);
+       lacp_elem.counterBlock.lacp.illegalRx =
+           lacp_stats.dot3adAggPortStatsIllegalRx;
+       lacp_elem.counterBlock.lacp.LACPDUsTx =
+           lacp_stats.dot3adAggPortStatsLACPDUsTx;
+       SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerPDUsTx);
+       SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerResponsePDUsTx);
+       SFLADD_ELEMENT(cs, &lacp_elem);
+    }
+
+    /* Include Port name. */
+    if ((ifName = netdev_get_name(dsp->ofport->netdev)) != NULL) {
+       memset(&name_elem, 0, sizeof name_elem);
+       name_elem.tag = SFLCOUNTERS_PORTNAME;
+       name_elem.counterBlock.portName.portName.str = (char *)ifName;
+       name_elem.counterBlock.portName.portName.len = strlen(ifName);
+       SFLADD_ELEMENT(cs, &name_elem);
+    }
+
+    /* Include OpenFlow DPID and openflow port number. */
+    memset(&of_elem, 0, sizeof of_elem);
+    of_elem.tag = SFLCOUNTERS_OPENFLOWPORT;
+    of_elem.counterBlock.ofPort.datapath_id =
+       ofproto_get_datapath_id(dsp->ofport->ofproto);
+    of_elem.counterBlock.ofPort.port_no =
+      (OVS_FORCE uint32_t)dsp->ofport->ofp_port;
+    SFLADD_ELEMENT(cs, &of_elem);
+
     sfl_poller_writeCountersSample(poller, cs);
 }
 
index 9c6d386..0861254 100644 (file)
@@ -3310,6 +3310,18 @@ port_get_stats(const struct ofport *ofport_, struct netdev_stats *stats)
     return error;
 }
 
+static int
+port_get_lacp_stats(const struct ofport *ofport_, struct lacp_slave_stats *stats)
+{
+    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+    if (ofport->bundle && ofport->bundle->lacp) {
+        if (lacp_get_slave_stats(ofport->bundle->lacp, ofport, stats)) {
+            return 0;
+        }
+    }
+    return -1;
+}
+
 struct port_dump_state {
     uint32_t bucket;
     uint32_t offset;
@@ -5378,6 +5390,7 @@ const struct ofproto_class ofproto_dpif_class = {
     port_poll,
     port_poll_wait,
     port_is_lacp_current,
+    port_get_lacp_stats,
     NULL,                       /* rule_choose_table */
     rule_alloc,
     rule_construct,
index 94dbbe9..0911333 100644 (file)
@@ -1036,6 +1036,14 @@ struct ofproto_class {
      * not support LACP. */
     int (*port_is_lacp_current)(const struct ofport *port);
 
+    /* Get LACP port stats. Returns -1 if LACP is not enabled on 'port'.
+     *
+     * This function may be a null pointer if the ofproto implementation does
+     * not support LACP. */
+    int (*port_get_lacp_stats)(const struct ofport *port,
+                              struct lacp_slave_stats *stats);
+
+
 /* ## ----------------------- ## */
 /* ## OpenFlow Rule Functions ## */
 /* ## ----------------------- ## */
index 36e44ae..947969c 100644 (file)
@@ -1147,6 +1147,21 @@ ofproto_port_is_lacp_current(struct ofproto *ofproto, ofp_port_t ofp_port)
             ? ofproto->ofproto_class->port_is_lacp_current(ofport)
             : -1);
 }
+
+int
+ofproto_port_get_lacp_stats(const struct ofport *port, struct lacp_slave_stats *stats)
+{
+    struct ofproto *ofproto = port->ofproto;
+    int error;
+
+    if (ofproto->ofproto_class->port_get_lacp_stats) {
+        error = ofproto->ofproto_class->port_get_lacp_stats(port, stats);
+    } else {
+        error = EOPNOTSUPP;
+    }
+
+    return error;
+}
 \f
 /* Bundles. */
 
index 40bb3b7..f2c8bf4 100644 (file)
@@ -31,6 +31,7 @@
 #include "smap.h"
 #include "sset.h"
 #include "stp.h"
+#include "lacp.h"
 
 #ifdef  __cplusplus
 extern "C" {
@@ -330,6 +331,7 @@ bool ofproto_port_bfd_status_changed(struct ofproto *, ofp_port_t ofp_port);
 int ofproto_port_get_bfd_status(struct ofproto *, ofp_port_t ofp_port,
                                 struct smap *);
 int ofproto_port_is_lacp_current(struct ofproto *, ofp_port_t ofp_port);
+int ofproto_port_get_lacp_stats(const struct ofport *, struct lacp_slave_stats *);
 int ofproto_port_set_stp(struct ofproto *, ofp_port_t ofp_port,
                          const struct ofproto_port_stp_settings *);
 int ofproto_port_get_stp_status(struct ofproto *, ofp_port_t ofp_port,
index 9d8efdc..d1f6977 100644 (file)
@@ -4370,7 +4370,7 @@ HEADER
        hdr=50-54-00-00-00-05-50-54-00-00-00-07-86-DD-67-00-00-00-00-00-0A-80-FE-80-00-00-00-00-00-00-00-00-00-00-00-00-00-01-FE-80-00-00-00-00-00-00-00-00-00-00-00-00-00-02-00-00-00-00-00-00
 ])
 
-  AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'IFCOUNTERS|ERROR' | head -6 | sed 's/ /\
+  AT_CHECK_UNQUOTED([[sort sflow.log | $EGREP 'IFCOUNTERS|ERROR|PORTNAME|OPENFLOWPORT' | head -18 | sed 's/ /\
        /g']], [0], [dnl
 IFCOUNTERS
        dgramSeqNo=2
@@ -4510,12 +4510,100 @@ IFCOUNTERS
        out_discards=0
        out_errors=0
        promiscuous=0
+OPENFLOWPORT
+       datapath_id=18364758544493064720
+       port_no=1
+OPENFLOWPORT
+       datapath_id=18364758544493064720
+       port_no=1
+OPENFLOWPORT
+       datapath_id=18364758544493064720
+       port_no=2
+OPENFLOWPORT
+       datapath_id=18364758544493064720
+       port_no=2
+OPENFLOWPORT
+       datapath_id=18364758544493064720
+       port_no=65534
+OPENFLOWPORT
+       datapath_id=18364758544493064720
+       port_no=65534
+PORTNAME
+       portName=br0
+PORTNAME
+       portName=br0
+PORTNAME
+       portName=p1
+PORTNAME
+       portName=p1
+PORTNAME
+       portName=p2
+PORTNAME
+       portName=p2
 ])
   AT_CLEANUP])
 
 CHECK_SFLOW_SAMPLING_PACKET([127.0.0.1], [IPv4])
 CHECK_SFLOW_SAMPLING_PACKET([[[::1]]], [IPv6])
 
+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([ovstest 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
+
 # CHECK_NETFLOW_EXPIRATION(LOOPBACK_ADDR, IP_VERSION_TYPE)
 #
 # Test that basic NetFlow reports flow statistics correctly:
index c84a9fa..c37288f 100644 (file)
@@ -54,8 +54,18 @@ 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_CTR_OPENFLOWPORT 1004
+#define SFLOW_TAG_CTR_PORTNAME 1005
 #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
+
+/* string sizes */
+#define SFL_MAX_PORTNAME_LEN 255
 
 struct sflow_addr {
     enum {
@@ -99,7 +109,14 @@ 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;
+       uint32_t OPENFLOWPORT;
+       uint32_t PORTNAME;
     } offset;
 
     /* Flow sample fields. */
@@ -221,6 +238,63 @@ process_counter_sample(struct sflow_xdr *x)
         printf(" promiscuous=%"PRIu32, sflowxdr_next(x));
         printf("\n");
     }
+    if (x->offset.LACPCOUNTERS) {
+       uint8_t *mac;
+       union {
+           ovs_be32 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");
+    }
+    if (x->offset.OPENFLOWPORT) {
+        sflowxdr_setc(x, x->offset.OPENFLOWPORT);
+        printf("OPENFLOWPORT");
+        printf(" datapath_id=%"PRIu64, sflowxdr_next_int64(x));
+        printf(" port_no=%"PRIu32, sflowxdr_next(x));
+       printf("\n");
+    }
+    if (x->offset.PORTNAME) {
+       uint32_t pnLen;
+       const char *pnBytes;
+       char portName[SFL_MAX_PORTNAME_LEN + 1];
+        sflowxdr_setc(x, x->offset.PORTNAME);
+        printf("PORTNAME");
+       pnLen = sflowxdr_next(x);
+       SFLOWXDR_assert(x, (pnLen <= SFL_MAX_PORTNAME_LEN));
+       pnBytes = sflowxdr_str(x);
+       memcpy(portName, pnBytes, pnLen);
+       portName[pnLen] = '\0';
+       printf(" portName=%s", portName);
+       printf("\n");
+    }
 }
 
 static char
@@ -251,6 +325,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)
+{
+    ovs_be32 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 +359,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));
@@ -372,6 +485,15 @@ 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;
+                case SFLOW_TAG_CTR_PORTNAME:
+                    sflowxdr_mark_unique(x, &x->offset.PORTNAME);
+                    break;
+                case SFLOW_TAG_CTR_OPENFLOWPORT:
+                    sflowxdr_mark_unique(x, &x->offset.OPENFLOWPORT);
+                    break;
 
                     /* Add others here... */
                 }
@@ -440,6 +562,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... */
                 }