#!/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 "Agent:Sub-agent | " agent ":" subAgt " | sysUpTime | " sysUpTime[agentsub] " |
" >>statsFile;
packetDelta = cdiff(oldPacketSequenceNo[agentsub],packetSequenceNo[agentsub]);
lostDelta = cdiff(oldPacketsLost[agentsub],packetsLost[agentsub]);
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[agentsub],packetSamples[agentsub]);
packetSamplesLostDelta = cdiff(oldPacketSamplesLost[agentsub],packetSamplesLost[agentsub]);
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[agentsub],counterSamples[agentsub]);
counterSamplesLostDelta = cdiff(oldCounterSamplesLost[agentsub],counterSamplesLost[agentsub]);
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/s | Ucast/s | Multicast/s | Broadcast/s | Discards/s | Errors/s |
" >>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);
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{}