UNPKG

@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).

206 lines (188 loc) 7.37 kB
var debug = require('debug')('NetFlowV9'); const { decMacRule } = require('./fieldRules'); function nf9PktDecode(msg,rinfo) { var templates = this.nfInfoTemplates(rinfo); var nfTypes = this.nfTypes || {}; var nfScope = this.nfScope || {}; var out = { header: { version: msg.readUInt16BE(0), count: msg.readUInt16BE(2), uptime: msg.readUInt32BE(4), seconds: msg.readUInt32BE(8), sequence: msg.readUInt32BE(12), sourceId: msg.readUInt32BE(16) }, flows: [] }; if (this.skipDuplicateSequence) { let id = `${rinfo.address}:${rinfo.port}:${out.header.sequence}:${msg.length}`; if (this.sequencesSeen[id]) { return null; } this.sequencesSeen[id] = Date.now(); } function appendTemplate(tId) { var id = rinfo.address + ':' + rinfo.port; out.templates = out.templates || {}; out.templates[id] = out.templates[id] || {}; out.templates[id][tId] = templates[tId]; } function compileStatement(type, pos, len) { var nf = nfTypes[type]; var cr = null; if (nf && nf.compileRule) { cr = nf.compileRule[len] || nf.compileRule[0]; if (cr) { return cr.toString().replace(/(\$pos)/g, function (n) { return pos }).replace(/(\$len)/g, function (n) { return len }).replace(/(\$name)/g, function (n) { return nf.name }); } } debug('Unknown compile rule TYPE: %d POS: %d LEN: %d',type,pos,len); return ""; } function compileTemplate(list) { var i, z, nf, n; var f = "var o = Object.create(null); var t;\n"; var listLen = list ? list.length : 0; for (i = 0, n = 0; i < listLen; i++, n += z.len) { z = list[i]; nf = nfTypes[z.type]; if (!nf) { debug('Unknown NF type %d', z.type); nf = nfTypes[z.type] = { name: 'unknown_type_'+ z.type, compileRule: decMacRule }; } f += compileStatement(z.type, n, z.len) + ";\n"; } f += "return o;\n"; debug('The template will be compiled to %s',f); return new Function('buf', 'nfTypes', f); } function readTemplate(buffer) { // var fsId = buffer.readUInt16BE(0); var len = buffer.readUInt16BE(2); var buf = buffer.slice(4, len); while (buf.length > 0) { var tId = buf.readUInt16BE(0); var cnt = buf.readUInt16BE(2); var list = []; var len = 0; for (var i = 0; i < cnt; i++) { list.push({type: buf.readUInt16BE(4 + 4 * i), len: buf.readUInt16BE(6 + 4 * i)}); len += buf.readUInt16BE(6 + 4 * i); } debug('compile template %s for %s:%d', tId, rinfo.address, rinfo.port); templates[tId] = {len: len, list: list, compiled: compileTemplate(list)}; appendTemplate(tId); buf = buf.slice(4 + cnt * 4); } } function decodeTemplate(fsId, buf) { try { if (typeof templates[fsId].compiled !== 'function') { templates[fsId].compiled = compileTemplate(templates[fsId].list); } var o = templates[fsId].compiled(buf, nfTypes); o.fsId = fsId; return o; } catch (err) { return { error: err }; } } function compileScope(type,pos,len) { if (!nfScope[type]) { nfScope[type] = { name: 'unknown_scope_'+type, compileRule: decMacRule }; debug('Unknown scope TYPE: %d POS: %d LEN: %d',type,pos,len); } var nf = nfScope[type]; var cr = null; if (nf.compileRule) { cr = nf.compileRule[len] || nf.compileRule[0]; if (cr) { return cr.toString().replace(/(\$pos)/g, function (n) { return pos }).replace(/(\$len)/g, function (n) { return len }).replace(/(\$name)/g, function (n) { return nf.name }); } } debug('Unknown compile scope rule TYPE: %d POS: %d LEN: %d',type,pos,len); return ""; } function readOptions(buffer) { var len = buffer.readUInt16BE(2); var tId = buffer.readUInt16BE(4); var osLen = buffer.readUInt16BE(6); var oLen = buffer.readUInt16BE(8); var buff = buffer.slice(10,len); debug('readOptions: len:%d tId:%d osLen:%d oLen:%d for %s:%d',len,tId,osLen,oLen,buff,rinfo.address,rinfo.port); var plen = 0; var cr = "var o={ isOption: true }; var t;\n"; var type; var tlen; // Read the SCOPE var buf = buff.slice(0,osLen); while (buf.length > 3) { type = buf.readUInt16BE(0); tlen = buf.readUInt16BE(2); debug(' SCOPE type: %d (%s) len: %d, plen: %d', type,nfTypes[type] ? nfTypes[type].name : 'unknown',tlen,plen); if (type>0) cr+=compileScope(type, plen, tlen); buf = buf.slice(4); plen += tlen; } // Read the Fields buf = buff.slice(osLen); while (buf.length > 3) { type = buf.readUInt16BE(0); tlen = buf.readUInt16BE(2); debug(' FIELD type: %d (%s) len: %d, plen: %d', type,nfTypes[type] ? nfTypes[type].name : 'unknown',tlen,plen); if (type>0) cr+=compileStatement(type, plen, tlen); buf = buf.slice(4); plen += tlen; } cr+="// option "+tId+"\n"; cr+="return o;"; debug('option template compiled to %s',cr); templates[tId] = { len: plen, compiled: new Function('buf','nfTypes',cr) }; appendTemplate(tId); } var buf = msg.slice(20); while (buf.length > 3) { // length > 3 allows us to skip padding var fsId = buf.readUInt16BE(0); var len = buf.readUInt16BE(2); if (fsId == 0) readTemplate(buf); else if (fsId == 1) readOptions(buf); else if (fsId > 1 && fsId < 256) { debug('Unknown Flowset ID %d!', fsId); } else if (fsId > 255 && typeof templates[fsId] != 'undefined') { var tbuf = buf.slice(4, len); while (tbuf.length >= templates[fsId].len) { let result = decodeTemplate(fsId, tbuf); if (result.error) { // we threw an error in decode template so stop parsing this set as we // don't know what the correct offset should be out.errors = out.errors || []; out.errors.push(result.error); break; } out.flows.push(result); tbuf = tbuf.slice(templates[fsId].len); } } else if (fsId > 255) { debug('Unknown template/option data with flowset id %d for %s:%d',fsId,rinfo.address,rinfo.port); } buf = buf.slice(len); if (len == 0) { break; } } return out; } module.exports = nf9PktDecode;