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