#!/bin/awk -f
#
# Copyright (c) 2001 InMon Corp. ALL RIGHTS RESERVED
#
# This script is used in conjunction with the sflowtool
# to test implementations of the sFlow 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 | ./sFlowTest.awk
# 3. Monitor the test results using a web browser:
# netscape stats.html
#
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() {
newFlow = 0;
sourceId = "";
inputPort = "";
outputPort = "";
in_vlan = "";
out_vlan = "";
in_priority = "";
out_priority = "";
headerProtocol = "";
srcMAC = "";
dstMAC = "";
srcIP = "";
dstIP = "";
IPProtocol = "";
IPTOS = "";
TCPSrcPort = "";
TCPDstPort = "";
TCPFlags = "";
nextHop = "";
srcSubnetMask = "";
dstSubnetMask = "";
my_as = "";
src_as = "";
src_peer_as = "";
dst_as_path = "";
}
function recordFlow() {
if(newFlow == 1) {
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;
}
resetFlowKeys();
}
function printIntervalResults() {
print "" >statsFile;
print "
" >>statsFile;
print "" >>statsFile;
print "sFlow Test Results" >>statsFile;
print "" >>statsFile;
print "" >>statsFile;
# Time
print "" strftime("%R", lastInt) "
" >>statsFile;
# Error Table
if(lastError > 0) {
print "Test run failed because of errors. All other data should be treated as unreliable.
" >>statsFile;
}
# General Information Table
print "" >>statsFile;
print "| Agent | " agent " | sysUpTime | " sysUpTime " |
" >>statsFile;
packetDelta = cdiff(oldPacketSequenceNo,packetSequenceNo);
lostDelta = cdiff(oldPacketsLost,packetsLost);
lostPercentage = 0;
if(packetDelta > 0) lostPercentage = 100 * lostDelta / (lostDelta + packetDelta);
printf "| sFlow packets/s | %.2f | percentage lost | %.2f |
", packetDelta / intervalSeconds, lostPercentage >>statsFile;
packetSamplesDelta = cdiff(oldPacketSamples,packetSamples);
packetSamplesLostDelta = cdiff(oldPacketSamplesLost,packetSamplesLost);
packetSamplesLostPercentage = 0;
if(packetSamplesDelta > 0) packetSamplesLostPercentage = 100 * packetSamplesLostDelta / (packetSamplesLostDelta + packetSamplesDelta);
printf "| packet samples/s | %.2f | percentage lost | %.2f |
", packetSamplesDelta / intervalSeconds, packetSamplesLostPercentage >> statsFile;
counterSamplesDelta = cdiff(oldCounterSamples,counterSamples);
counterSamplesLostDelta = cdiff(oldCounterSamplesLost,counterSamplesLost);
counterSamplesLostPercentage = 0;
if(counterSamplesDelta > 0) counterSamplesLostPercentage = 100 * counterSamplesLostDelta / (counterSamplesLostDelta + counterSamplesDelta);
printf "| counter samples/s | %.2f | percentage lost | %.2f |
", counterSamplesDelta / intervalSeconds, counterSamplesLostPercentage >> statsFile;
print "
" >>statsFile;
print "
" >>statsFile;
# Top Flows Table
print "" >>statsFile;
print "| Top " maxFlows " Flows |
" >>statsFile;
print "| ID | ifIndex | vlan | priority | Protocol | MAC | IP | next hop | subnet | BGP AS | Frames/s | Bits/s |
" >>statsFile;
print "| in | out | in | out | in | out | src | dest | src | dest | src | dest | my | src | src peer | dest path |
" >>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 "| " a[j] " | " >>statsFile;
}
printf "%.2f | %.2f | ", (maxCount / samplesDelta) * poolDelta / intervalSeconds, (maxCount / samplesDelta) * poolDelta * (maxBytes / maxCount) * 8 / intervalSeconds >>statsFile;
print "
" >>statsFile;
}
delete count[maxKey];
}
print "
" >>statsFile;
print "
" >>statsFile;
# Counters Table
print "" >>statsFile;
print "| Counters |
" >>statsFile;
print "| ID | ifIndex | ifSpeed | ifDirection | ifStatus | Octets | Ucast | Multicast | Broadcast | Discards | Errors |
" >>statsFile;
print "in | out | in | out | in | out | in | out | in | out | in | out | " >>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 "" maxID " | " maxIdx " | " counters[maxID,maxIdx,"ifSpeed"] " | " counters[maxID,maxIdx,"ifDirection"] " | " counters[maxID,maxIdx,"ifStatus"] " | " >>statsFile;
n = split("ifInOctets,ifOutOctets,ifInUcastPkts,ifOutUcastPkts,ifInMulticastPkts,ifOutMulticastPkts,ifInBroadcastPkts,ifOutBroadcastPkts,ifInDiscards,ifOutDiscards,ifInErrors,ifOutErrors", a, ",");
for (i = 1; i <= n; i++) {
print "" >>statsFile;
printf "%.2f", cdiff(oldCounters[maxID,maxIdx,a[i]],counters[maxID,maxIdx,a[i]]) / intervalSeconds >>statsFile;
print " | " >>statsFile;
}
print "" >>statsFile;
}
} while (found == 1);
print "
" >>statsFile;
print "
" >>statsFile;
# Error Table
if(lastError > 0) {
print "" >>statsFile;
print "| Errors |
" >>statsFile;
for(i = 1; i <= lastError; i++) {
print "| " i " | " errorLog[i] " |
" >>statsFile;
}
print "
" >>statsFile;
print "
" >>statsFile;
}
print "" >>statsFile;
print "" >>statsFile;
close(statsFile);
system("mv " statsFile " " finalStatsFile);
oldPacketSequenceNo = packetSequenceNo;
oldPacketsLost = packetsLost;
oldPacketSamples = packetSamples;
oldPacketSamplesLost = packetSamplesLost;
oldCounterSamples = counterSamples;
oldCounterSamplesLost = counterSamplesLost;
delete count;
delete countBytes;
for(i in samplePool) oldSamplePool[i] = samplePool[i];
for(i in samples) oldSamples[i] = samples[i];
for(i in counters) oldCounters[i] = counters[i];
}
BEGIN{
statsFile = "stats.bld";
finalStatsFile = "stats.html";
system("rm " statsFile);
resetFlowKeys();
maxFlows = 5;
maxSeqNoDelta = 10;
lastInt = 0;
intervalSeconds = 60;
lastError = 0;
}
/startDatagram/{}
/unixSecondsUTC/{
currentInt = $2 - ($2 % intervalSeconds);
if(currentInt != lastInt) {
recordFlow();
printIntervalResults();
lastInt = currentInt;
}
}
/datagramVersion/{datagramVersion = $2;}
/agent/{
if(agent) {
if(agent != $2) error("Agent IP address changed from " agent " to " $2);
} else {agent = $2;}
}
/sysUpTime/{
if(sysUptime > $2) error("sysUptime went backwards ( old value = " sysUpTime " new value = " $2 " )");
sysUpTime = $2;
}
/packetSequenceNo/{
if(packetSequenceNo) {
delta = cdiff(packetSequenceNo, $2);
packetsLost += delta - 1;
if(delta == 0) error("packet seq. no. not incrementing (" packetSequenceNo ")");
if((packetSequenceNo > 0) && (delta > maxSeqNoDelta)) error("excessive sflow packet loss, or seq. no. reset (old = " packetSequenceNo " new " $2 " )");
}
packetSequenceNo = $2;
}
/samplesInPacket/{
samplesInPacket = $2;
if(samplesInPacket == 0) error("no samples in packet");
}
/sampleSequenceNo/{sampleSequenceNo = $2; recordFlow(); }
/sourceId/{sourceId = $2;}
/sampleType/{
sampleType = $2;
oldSeqNo = sSeqNos[sourceId, sampleType];
if(oldSeqNo) {
delta = cdiff(oldSeqNo, sampleSequenceNo);
if(sampleType == "FLOWSAMPLE") {
packetSamples++;
packetSamplesLost += delta - 1;
}
else if(sampleType == "COUNTERSSAMPLE") {
counterSamples++;
counterSamplesLost += delta - 1;
}
if(delta == 0) error("sample seq. no. not incrementing (src = " sourceId " type = " sampleType " seq. no. = " sampleSequenceNo ")");
if((packetSequenceNo > 0) && (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") newFlow = 1;
}
/meanSkipCount/{meanSkipCount[sourceId] = $2;}
/samplePool/{samplePool[sourceId] = $2; samples[sourceId] += 1; }
/dropEvents/{dropEvents[sourceId] = $2;}
/inputPort/{inputPort = $2;}
/outputPort/{outputPort = $2;}
/headerProtocol/{headerProtocol = $2;}
/sampledPacketSize/{sampledPacketSize = $2;}
/headerLen/{headerLen = $2;}
/headerBytes/{headerBytes = $2;}
/dstMAC/{dstMAC = $2;}
/srcMAC/{srcMAC = $2;}
/srcIP/{srcIP = $2;}
/dstIP/{dstIP = $2;}
/IPProtocol/{ IPProtocol = $2;}
/IPTOS/{IPTOS = $2;}
/TCPSrcPort/{TCPSrcPort = $2;}
/TCPDstPort/{TCPDstPort = $2;}
/TCPFlags/{TCPFlags = $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;}
/statsSamplingInterval/{statsSamplingInterval = $2;}
/counterBlockVersion/{counterBlockVersion = $2;}
/networkType/{networkType = $2;}
/if/{
counterName = $1;
counterValue = $2;
if(counterName == "ifIndex") ifIndex = counterValue;
else {
counters[sourceId,ifIndex,counterName] = counterValue;
}
}
END{}