#!/bin/awk -f # # Copyright (c) 2004 InMon Corp. ALL RIGHTS RESERVED # # This script is used in conjunction with the sflowtool # to test implementations of an sFlow Version 5 agent. # # Instructions: # 1. First configure the sFlow agent to send sFlow packets # to the test host on a specified port (6343 in this example). # 2. Start the test using the following command: # sflowtool -p 6343 | ./sFlow5Test.awk # 3. Monitor the test results using a web browser: # mozilla stats.html # # $Header: /usr/src/redhat/SOURCES/sflowtest/RCS/sflow5test.awk,v 1.1 2004/04/19 18:58:27 root Exp root $ function cdiff(c1,c2) { if(c1 == 0) return 0; if(c1 <= c2) return c2 - c1; if(c1 < 2**32) return c2 + 2**32 - c1; return c2 + 2**64 - c1; } function error(str) { errorLog[++lastError] = str; } function resetFlowKeys() { inputPort = ""; outputPort = ""; in_vlan = ""; out_vlan = ""; in_priority = ""; out_priority = ""; headerProtocol = ""; srcMAC = ""; dstMAC = ""; srcIP = ""; dstIP = ""; nextHop = ""; srcSubnetMask = ""; dstSubnetMask = ""; my_as = ""; src_as = ""; src_peer_as = ""; dst_as_path = ""; } function recordFlow() { count[sourceId,inputPort,outputPort,in_vlan,out_vlan,in_priority,out_priority,headerProtocol,srcMAC,dstMAC,srcIP,dstIP,nextHop,srcSubnetMask,dstSubnetMask,my_as,src_as,src_peer_as,dst_as_path] += 1; countBytes[sourceId,inputPort,outputPort,in_vlan,out_vlan,in_priority,out_priority,headerProtocol,srcMAC,dstMAC,srcIP,dstIP,nextHop,srcSubnetMask,dstSubnetMask,my_as,src_as,src_peer_as,dst_as_path] += sampledPacketSize; } function printIntervalResults() { print "" >statsFile; print "" >>statsFile; print "" >>statsFile; print "sFlow Test Results" >>statsFile; print "" >>statsFile; print "" >>statsFile; # Interval print "

interval no. = " ++intervalCount "

" >>statsFile; # Error Table if(lastError > 0) { print "

WARNING Test run failed because of errors. All other data should be treated as unreliable.

" >>statsFile; } # General Information Table for(agentsub in sysUpTime) { n = split(agentsub,parts,SUBSEP); agt = parts[1]; subAgt = parts[2]; print "" >>statsFile; print "" >>statsFile; packetDelta = cdiff(oldPacketSequenceNo[agentsub],packetSequenceNo[agentsub]); lostDelta = cdiff(oldPacketsLost[agentsub],packetsLost[agentsub]); lostPercentage = 0; if(packetDelta > 0) lostPercentage = 100 * lostDelta / (lostDelta + packetDelta); printf "", packetDelta / intervalSeconds, lostPercentage >>statsFile; packetSamplesDelta = cdiff(oldPacketSamples[agentsub],packetSamples[agentsub]); packetSamplesLostDelta = cdiff(oldPacketSamplesLost[agentsub],packetSamplesLost[agentsub]); packetSamplesLostPercentage = 0; if(packetSamplesDelta > 0) packetSamplesLostPercentage = 100 * packetSamplesLostDelta / (packetSamplesLostDelta + packetSamplesDelta); printf "", packetSamplesDelta / intervalSeconds, packetSamplesLostPercentage >> statsFile; counterSamplesDelta = cdiff(oldCounterSamples[agentsub],counterSamples[agentsub]); counterSamplesLostDelta = cdiff(oldCounterSamplesLost[agentsub],counterSamplesLost[agentsub]); counterSamplesLostPercentage = 0; if(counterSamplesDelta > 0) counterSamplesLostPercentage = 100 * counterSamplesLostDelta / (counterSamplesLostDelta + counterSamplesDelta); printf "", counterSamplesDelta / intervalSeconds, counterSamplesLostPercentage >> statsFile; print "
Agent:Sub-agent" agent ":" subAgt "sysUpTime" sysUpTime[agentsub] "
sFlow packets/s%.2fpercentage lost%.2f
packet samples/s%.2fpercentage lost%.2f
counter samples/s%.2fpercentage lost%.2f
" >>statsFile; print "
" >>statsFile; } # Top Flows Table print "" >>statsFile; print "" >>statsFile; print "" >>statsFile; print "" >>statsFile; for(i = 1; i <= maxFlows; i++) { maxCount = 0; maxBytes = 0; maxKey = ""; for(key in count) { if(count[key] > maxCount) { maxCount = count[key]; maxKey = key; } } if(maxCount > 0) { maxBytes = countBytes[maxKey]; n = split(maxKey,a,SUBSEP); id = a[1]; poolDelta = cdiff(oldSamplePool[id],samplePool[id]); samplesDelta = cdiff(oldSamples[id],samples[id]); if(samplesDelta == 0) samplesDelta = 1; print "" >>statsFile; for(j = 1; j <= n; j++) { print "" >>statsFile; } printf "", (maxCount / samplesDelta) * poolDelta / intervalSeconds, (maxCount / samplesDelta) * poolDelta * (maxBytes / maxCount) * 8 / intervalSeconds >>statsFile; print "" >>statsFile; } delete count[maxKey]; } print "
Top " maxFlows " Flows
IDifIndexvlanpriorityProtocolMACIPnext hopsubnetBGP ASFrames/sBits/s
inoutinoutinoutsrcdestsrcdestsrcdestmysrcsrc peerdest path
" a[j] "%.2f%.2f
" >>statsFile; print "
" >>statsFile; # Counters Table print "" >>statsFile; print "" >>statsFile; print "" >>statsFile; print "" >>statsFile; maxID = ""; maxIdx = -1; do { found = 0; id = ""; idx = 1000000; for (i in counters) { n = split(i,a,SUBSEP); if((a[2] > maxIdx) && (a[2] < idx)) { idx = a[2]; id = a[1]; found = 1; } } maxID = id; maxIdx = idx; if(maxIdx < 100000) { print "" >>statsFile; n = split("ifInOctets,ifOutOctets,ifInUcastPkts,ifOutUcastPkts,ifInMulticastPkts,ifOutMulticastPkts,ifInBroadcastPkts,ifOutBroadcastPkts,ifInDiscards,ifOutDiscards,ifInErrors,ifOutErrors", a, ","); for (i = 1; i <= n; i++) { print "" >>statsFile; } print "" >>statsFile; } } while (found == 1); print "
Counters
IDifIndexifSpeedifDirectionifStatusOctets/sUcast/sMulticast/sBroadcast/sDiscards/sErrors/s
inoutinoutinoutinoutinoutinout
" maxID "" maxIdx "" counters[maxID,maxIdx,"ifSpeed"] "" counters[maxID,maxIdx,"ifDirection"] "" counters[maxID,maxIdx,"ifStatus"] "" >>statsFile; printf "%.2f", cdiff(oldCounters[maxID,maxIdx,a[i]],counters[maxID,maxIdx,a[i]]) / intervalSeconds >>statsFile; print "
" >>statsFile; print "
" >>statsFile; # Error Table if(lastError > 0) { print "" >>statsFile; print "" >>statsFile; for(i = 1; i <= lastError; i++) { print "" >>statsFile; } print "
Errors
" i "" errorLog[i] "
" >>statsFile; print "
" >>statsFile; } print "" >>statsFile; print "" >>statsFile; close(statsFile); system("mv " statsFile " " finalStatsFile); delete count; delete countBytes; delete instanceToSubAgent; for(i in samplePool) oldSamplePool[i] = samplePool[i]; for(i in samples) oldSamples[i] = samples[i]; for(i in counters) oldCounters[i] = counters[i]; for(i in packetSequenceNo) oldPacketSequenceNo[i] = packetSequenceNo[i]; for(i in packetsLost) oldPacketsLost[i] = packetsLost[i]; for(i in packetSamples) oldPacketSamples[i] = packetSamples[i]; for(i in packetSamplesLost) oldPacketSamplesLost[i] = packetSamplesLost[i]; for(i in counterSamples) oldCounterSamples[i] = counterSamples[i]; for(i in counterSamplesLost) oldCounterSamplesLost[i] = counterSamplesLost[i]; } BEGIN{ FS = "[ \t=]+"; statsFile = "stats.bld"; finalStatsFile = "stats.html"; resetFlowKeys(); maxFlows = 5; # top N flows maxSeqNoDelta = 10; lastInt = 0; intervalSeconds = 60; intervalCount = 0; lastError = 0; } /startDatagram/{} /unixSecondsUTC/{ # this value is generated by sflowtool and # is the time the sample was decoded, use # it to roll over measurement intervals currentInt = $2 - ($2 % intervalSeconds); if(currentInt != lastInt) { recordFlow(); printIntervalResults(); lastInt = currentInt; } } /datagramVersion/{ datagramVersion = $2; if(datagramVersion != 5) error("only tests sFlow v5 (reported version=" datagramVersion ")"); } /agentSubId/{ agentSubId = $2; } ($1 == "agent") { if(agent) { if(agent != $2) error("Agent IP address changed from " agent " to " $2); } else {agent = $2;} } /sysUpTime/{ if(sysUptime[agent,agentSubId] > $2) error("sysUptime went backwards ( old value = " sysUpTime[agent,agentSubId] " new value = " $2 " )"); sysUpTime[agent,agentSubId] = $2; } /packetSequenceNo/{ if(packetSequenceNo[agent,agentSubId]) { delta = cdiff(packetSequenceNo[agent,agentSubId], $2); packetsLost[agent,agentSubId] += delta - 1; if(delta == 0) error("packet seq. no. not incrementing (" packetSequenceNo ")"); if(delta > maxSeqNoDelta) error("excessive sflow packet loss, or seq. no. reset (old = " packetSequenceNo[agent,agentSubId] " new " $2 " )"); } packetSequenceNo[agent,agentSubId] = $2; } /samplesInPacket/{ samplesInPacket = $2; if(samplesInPacket == 0) error("no samples in packet"); } /startSample/ {} /sampleType/ { sampleType = $2; } /sampleSequenceNo/{sampleSequenceNo = $2;} /sourceId/{ sourceId = $2; # check to make sure that each sourceId,sampleType pair is associated # with a single sub-agent if(instanceToSubAgent[sourceId,sampleType]) { if(instanceToSubAgent[sourceId,sampleType] != agentSubId) error("sub-agent changed (sourceId=" sourceId " sampleType=" sampleType " old sub-agent=" instanceToSubAgent[sourceId,sampleType] " new sub-agent=" agentSubId); } else { instanceToSubAgent[sourceId,sampleType] = agentSubId; } # check sequence numbers for each sample type oldSeqNo = sSeqNos[sourceId, sampleType]; if(oldSeqNo) { delta = cdiff(oldSeqNo, sampleSequenceNo); if(sampleType == "FLOWSAMPLE") { packetSamples[agent,agentSubId]++; packetSamplesLost[agent,agentSubId] += delta - 1; } else if(sampleType == "COUNTERSSAMPLE") { counterSamples[agent,agentSubId]++; counterSamplesLost[agent,agentSubId] += delta - 1; } if(delta == 0) error("sample seq. no. not incrementing (src = " sourceId " type = " sampleType " seq. no. = " sampleSequenceNo ")"); if(delta > maxSeqNoDelta) error("excessive sflow sample loss, or seq. no. reset (src = " sourceId " type = " sampleType " old = " oldSeqNo " new = " sampleSequenceNo " )"); } sSeqNos[sourceId, sampleType] = sampleSequenceNo; if(sampleType == "FLOWSAMPLE") resetFlowKeys(); } # # Packet flow sample fields # /meanSkipCount/{meanSkipCount[sourceId] = $2;} /samplePool/{samplePool[sourceId] = $2; samples[sourceId]++; } /dropEvents/{dropEvents[sourceId] = $2;} /inputPort/{inputPort = $2;} /outputPort/{outputPort = $2;} /headerProtocol/{headerProtocol = $2;} /sampledPacketSize/{sampledPacketSize = $2;} /strippedBytes/{ strippedBytes = $2; if(headerProtocol == 1) { if(strippedBytes < 4) error("must strip CRC from Ethernet frames (stripped = " strippedBytes ")"); } } /headerLen/{ headerLen = $2; if(headerLen > (sampledPacketSize - strippedBytes)) error("incorrect packet size (" sampledPacketSize "), stripped (" strippedBytes "), or header length (" headerLen ")"); } /headerBytes/{headerBytes = $2;} /dstMAC/{dstMAC = $2;} /srcMAC/{srcMAC = $2;} /IPSize/{IPSize = $2;} /ip.tot_len/{ IPLen = $2; if(headerProtocol == 1) { if(headerLen + stripped >= 64) { # no padding if(IPSize != IPLen) { error("inconsistent packet size (" sampledPacketSize "), stripped (" strippedBytes ") or corrupt IP header (IPSize=" IPSize ", IPLen=" IPLen ")"); } } } } /srcIP/{srcIP = $2;} /dstIP/{dstIP = $2;} /in_vlan/{in_vlan = $2;} /in_priority/{in_priority = $2;} /out_vlan/{out_vlan = $2;} /out_priority/{out_priority = $2;} /nextHop/{nextHop = $2;} /srcSubnetMask/{srcSubnetMask = $2;} /dstSubnetMask/{dstSubnetMask = $2;} /my_as/{my_as = $2;} /src_as/{src_as = $2;} /src_peer_as/{src_peer_as = $2;} /dst_as_path/{dst_as_path = $2;} # # Counter sample fields # /if/{ counterName = $1; counterValue = $2; if(counterName == "ifIndex") ifIndex = counterValue; else { counters[sourceId,ifIndex,counterName] = counterValue; } } /endSample/ { if(sampleType == "FLOWSAMPLE") recordFlow(); } /endDatagram/ {} END{}