@gavinaiken/netflowv9
Version:
NetFlow Version 1,5,7,9 compatible library (with support for NetFlow v9 options template & data) for Node.JS. It also has experimental support for IPFIX (NetFlow v10).
207 lines (194 loc) • 7.7 kB
JavaScript
/**
* This version support only compiled code and works with streams API
*/
//require('debug').enable('NetFlowV9');
var debug = require('debug')('NetFlowV9');
var dgram = require('dgram');
var clone = require('clone');
var util = require('util');
var e = require('events').EventEmitter;
var Dequeue = require('dequeue');
var nft = require('./js/nf9/nftypes');
var enterprise = require('./js/nf10/nfEnterpriseTypes');
var nf1PktDecode = require('./js/nf1/nf1decode');
var nf5PktDecode = require('./js/nf5/nf5decode');
var nf7PktDecode = require('./js/nf7/nf7decode');
var nf9PktDecode = require('./js/nf9/nf9decode');
var nfInfoTemplates = require('./js/nf9/nfinfotempl');
var nf10PktDecode = require('./js/nf10/nf10decode');
function nfPktDecode(msg,rinfo) {
var version = msg.readUInt16BE(0);
switch (version) {
case 1:
return this.nf1PktDecode(msg,rinfo);
break;
case 5:
return this.nf5PktDecode(msg,rinfo);
break;
case 7:
return this.nf7PktDecode(msg,rinfo);
break;
case 9:
return this.nf9PktDecode(msg,rinfo);
break;
case 10:
return this.nf10PktDecode(msg,rinfo);
break;
default:
debug('bad header version %d', version);
return;
}
}
function NetFlowV9(options) {
if (!(this instanceof NetFlowV9)) return new NetFlowV9(options);
var me = this;
this.templates = {};
this.nfTypes = clone(nft.nfTypes);
this.nfScope = clone(nft.nfScope);
this.enterpriseTypes = clone(enterprise.enterpriseTypes);
this.cb = null;
this.templateCb = null;
this.socketType = 'udp4';
this.port = null;
this.proxy = null;
this.fifo = new Dequeue();
if (typeof options == 'function') this.cb = options; else
if (typeof options.cb == 'function') this.cb = options.cb;
if (typeof options.templateCb == 'function') this.templateCb = options.templateCb;
if (typeof options == 'object') {
if (options.ipv4num) decIpv4Rule[4] = "o['$name']=buf.readUInt32BE($pos);";
if (options.nfTypes) this.nfTypes = util._extend(this.nfTypes,options.nfTypes); // Inherit nfTypes
if (options.nfScope) this.nfScope = util._extend(this.nfScope,options.nfScope); // Inherit nfScope
if (options.enterpriseTypes) this.enterpriseTypes = util._extend(this.enterpriseTypes,options.enterpriseTypes); // Inherit enterpriseTypes
if (options.socketType) this.socketType = options.socketType;
if (options.port) this.port = options.port;
if (options.templates) this.templates = options.templates;
if (options.fwd) this.fwd = options.fwd;
if (typeof options.proxy == 'object' ||
typeof options.proxy == 'string') {
this.proxy = [];
if (typeof options.proxy == 'string') {
debug('Defining proxy destination %s',options.proxy);
var m = options.proxy.match(/^(.*)(\:(\d+))$/);
if (m) {
this.proxy.push({host: m[1], port: m[3]||5555});
debug('Proxy added %s:%s',m[1],m[3]||5555);
}
} else {
for (var k in options.proxy) {
var v = options.proxy[k];
if (typeof v == 'string') {
debug('Defining proxy destination %s = %s',k,v);
var m = v.match(/^(.*)(\:(\d+))$/);
if (m) {
this.proxy.push({host: m[1], port: m[3]||5555});
debug('Proxy added %s:%s',m[1],m[3]||5555);
}
}
}
}
if (this.proxy.length == 0) this.proxy = null;
}
if (options.skipDuplicateSequence) {
this.skipDuplicateSequence = options.skipDuplicateSequence;
this.sequencesSeen = {};
// once a minute make new cache of sequences seen
let interval = setInterval(() => {
let twentySecondsAgo = Date.now() - 20000;
let oldSequences = this.sequencesSeen;
this.sequencesSeen = {};
Object.keys(oldSequences).forEach(key => {
if (oldSequences[key] > twentySecondsAgo) {
this.sequencesSeen[key] = oldSequences[key];
}
});
}, 60000);
interval.unref();
}
e.call(this,options);
}
this.server = dgram.createSocket(this.socketType);
this.server.on('message',function(msg,rinfo){
me.fifo.push([msg, rinfo]);
if (!me.closed && me.set) {
me.set = false;
setImmediate(me.fetch);
}
if (me.proxy) { // Resend the traffic
me.proxy.forEach(function(p) {
me.server.send(msg,0,msg.length,p.port,p.host,function() {});
});
}
});
this.server.on('close', function() {
this.closed = true;
});
this.listen = function(port,host,cb) {
me.fetch();
setTimeout(function() {
if (host && typeof host === 'function')
me.server.bind(port,host);
else if (host && typeof host === 'string' && cb)
me.server.bind(port,host,cb);
else if (host && typeof host === 'string' && !cb)
me.server.bind(port,host);
else if (!host && cb)
me.server.bind(port, cb);
else
me.server.bind(port);
},50);
};
this.fetch = function() {
while (me.fifo.length > 0 && !this.closed) {
var data = me.fifo.shift();
var msg = data[0];
var rinfo = data[1];
var startTime = new Date().getTime();
if (me.fwd) {
var data = JSON.parse(msg.toString());
msg = new Buffer(data.buffer);
rinfo = data.rinfo;
}
if (rinfo.size<20) continue;
var o = me.nfPktDecode(msg,rinfo);
var timeMs = (new Date().getTime()) - startTime;
if (o && o.flows.length > 0) { // If the packet does not contain flows, only templates we do not decode
o.rinfo = rinfo;
o.packet = msg;
o.decodeMs = timeMs;
if (me.cb)
me.cb(o);
else
me.emit('data',o);
} else if (o && o.templates) {
o.rinfo = rinfo;
o.packet = msg;
o.decodeMs = timeMs;
if (me.templateCb)
me.templateCb(o);
else
me.emit('template', o);
} else {
debug('Undecoded flows',o);
}
if (o && o.errors && o.errors.length) {
let data = { rinfo, buffer: msg.toJSON() };
o.errors.forEach(err => {
err.data = data;
me.emit('error', err);
});
}
}
me.set = true;
};
if (this.port) this.listen(options.port, options.host);
}
util.inherits(NetFlowV9,e);
NetFlowV9.prototype.nfInfoTemplates = nfInfoTemplates;
NetFlowV9.prototype.nfPktDecode = nfPktDecode;
NetFlowV9.prototype.nf1PktDecode = nf1PktDecode;
NetFlowV9.prototype.nf5PktDecode = nf5PktDecode;
NetFlowV9.prototype.nf7PktDecode = nf7PktDecode;
NetFlowV9.prototype.nf9PktDecode = nf9PktDecode;
NetFlowV9.prototype.nf10PktDecode = nf10PktDecode;
module.exports = NetFlowV9;