summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil McKee <neil.mckee@inmon.com>2014-06-27 11:19:59 -0700
committerBen Pfaff <blp@nicira.com>2014-11-11 13:28:40 -0800
commit50b9699fe49b66de64d0d0b1803bb13e95ca2c2e (patch)
tree97b6c4d020511efe85a9f6f6596d6a1e03e26f86
parentafc63bb4bc0692af8bfa16d2a573b8b3bce52895 (diff)
downloadopenvswitch-50b9699fe49b66de64d0d0b1803bb13e95ca2c2e.tar.gz
sflow: Export LAG, PORTNAME, and OPENFLOWPORT information also.
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>
-rw-r--r--NEWS2
-rw-r--r--lib/lacp.c62
-rw-r--r--lib/lacp.h23
-rw-r--r--lib/sflow.h69
-rw-r--r--lib/sflow_agent.c15
-rw-r--r--lib/sflow_api.h1
-rw-r--r--lib/sflow_receiver.c43
-rw-r--r--ofproto/ofproto-dpif-sflow.c58
-rw-r--r--ofproto/ofproto-dpif.c13
-rw-r--r--ofproto/ofproto-provider.h8
-rw-r--r--ofproto/ofproto.c15
-rw-r--r--ofproto/ofproto.h2
-rw-r--r--tests/ofproto-dpif.at90
-rw-r--r--tests/test-sflow.c138
14 files changed, 534 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index bc0ff5f4e..5ba8f4d3f 100644
--- 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
diff --git a/lib/lacp.c b/lib/lacp.c
index 3b50d46fe..ce5343b76 100644
--- a/lib/lacp.c
+++ b/lib/lacp.c
@@ -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;
+
+}
diff --git a/lib/lacp.h b/lib/lacp.h
index 593b80d0c..4295f7b32 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/sflow.h b/lib/sflow.h
index c6cde7f23..dfe138f10 100644
--- a/lib/sflow.h
+++ b/lib/sflow.h
@@ -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 {
diff --git a/lib/sflow_agent.c b/lib/sflow_agent.c
index 817420d83..9c2e0282c 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 3cc060b3b..2730a4cf4 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 e6fc9a7f9..aff62fed1 100644
--- a/lib/sflow_receiver.c
+++ b/lib/sflow_receiver.c
@@ -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;
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 92e2ae81b..60b65a349 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -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);
}
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 9c6d386de..08612548e 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -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,
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 94dbbe9ac..09113337e 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -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 ## */
/* ## ----------------------- ## */
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 36e44ae86..947969c68 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -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;
+}
/* Bundles. */
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 40bb3b7d2..f2c8bf419 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -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,
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 9d8efdc5a..d1f6977d3 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -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:
diff --git a/tests/test-sflow.c b/tests/test-sflow.c
index c84a9fa12..c37288f7e 100644
--- a/tests/test-sflow.c
+++ b/tests/test-sflow.c
@@ -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... */
}