]> git.proxmox.com Git - mirror_ovs.git/commitdiff
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 bc0ff5f4eb02ea1cb632cf08dc2c905d49b90bf9..5ba8f4d3f397cbf8604ae5dbefd99456f0a61ca0 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 3b50d46fe4e72640097b826a136edf94f5bf03ed..ce5343b768c8b63d62db212481b04275d3113cd9 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 593b80d0cdd4ffed919bf141a744d39d3c555a2d..4295f7b32f2b1d573dee8da3f46e615591e5a5fc 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 c6cde7f23b4d676bbcc5d7af1aa21fe670a5a4ac..dfe138f10d950fecd51b5ebc4a004d61398e2e24 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 817420d836f83182fb397c36346dfefeee90429e..9c2e0282c9f4e6d443cb65b001955f4dacc89860 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 3cc060b3b3f23bad7fa8b49e8711cdad16288f2c..2730a4cf4a27f235369ddc89c0b91de40f3c3bfc 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 e6fc9a7f9bfe49048532595f5063dfeed5aa7ed5..aff62fed12a9f7082114c9c899c441719eef865e 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 92e2ae81b3776d2d69431a22fff2db55603ea213..60b65a349068122d2d81f9724bbf921ca67fc441 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 9c6d386ded3de4d6a2f136516ddc817e27fb7179..08612548eeaf765cb8ab2379055710aac833a6cf 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 94dbbe9ac85f64b15ebe8c6ac581e275ae5be7c5..09113337ece2c73c13eaf4535a97e28e65461e24 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 36e44ae86b83c0caf789dba49ecbdadbdc5c74bf..947969c68a4dd507d5288af73a511be8c8f1d2df 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 40bb3b7d23c311ecd3f606c079d6e5253e196438..f2c8bf41969db35c0188fd8b24d70393df6c5bc5 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 9d8efdc5a6b6150a0d634efc2c03d8004b616ea1..d1f6977d3559b28f480c10e8f3147dd756b1e059 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 c84a9fa120acdeeaab6de4971f2f16e5b0499346..c37288f7eb357ab0674db31ac27db64c65e541f9 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... */
                 }