├── nodejs ├── config.json ├── run_node_pcap ├── run_node ├── README.md ├── package.json ├── client.js ├── main.js └── pcap.js ├── lua ├── run_lua ├── README.md └── main.lua ├── nodebeat ├── run_nodebeat ├── README.md ├── package.json └── nodebeat.js ├── pcap └── lamernews.pcap ├── install.sh ├── Makefile ├── README.md ├── LICENSE ├── ndpiexlib.c └── ndpiex.c /nodejs/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "serverAddr":"" 3 | } -------------------------------------------------------------------------------- /lua/run_lua: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | luajit main.lua 4 | -------------------------------------------------------------------------------- /nodejs/run_node_pcap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node pcap.js 4 | -------------------------------------------------------------------------------- /nodebeat/run_nodebeat: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node nodebeat.js 127.0.0.1:9200 4 | -------------------------------------------------------------------------------- /nodejs/run_node: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node main.js ../pcap/lamernews.pcap 4 | -------------------------------------------------------------------------------- /pcap/lamernews.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QXIP/nDPIex/HEAD/pcap/lamernews.pcap -------------------------------------------------------------------------------- /nodebeat/README.md: -------------------------------------------------------------------------------- 1 | # NDPI Toy Beat 2 | 3 | NodeJS + nDPI + Elasticsearch 4 | 5 | ![](http://i.imgur.com/2sToP5i.png) 6 | -------------------------------------------------------------------------------- /nodejs/README.md: -------------------------------------------------------------------------------- 1 | # nDPI Node FFI example 2 | 3 | * Requires ```ndpiexlib``` 4 | 5 | ### Example using static file 6 | ``` 7 | ./run_node 8 | ``` 9 | 10 | ### Example using live network 11 | ``` 12 | ./run_node_pcap 13 | ``` 14 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -d "nDPI" ]; then 4 | git clone https://github.com/ntop/nDPI.git 5 | fi 6 | cd nDPI 7 | git pull 8 | ./autogen.sh && ./configure -with-pic 9 | make && make install 10 | cd .. 11 | make 12 | make lib 13 | 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: ndpiex 2 | 3 | ndpiex: ndpiex.c 4 | gcc -Wall -g -o ndpiex ndpiex.c -I./nDPI/src/include/ ./nDPI/src/lib/.libs/libndpi.a -lpcap 5 | 6 | lib: 7 | gcc -g ndpiexlib.c -fPIC -shared -o ndpiexlib.so -I./nDPI/src/include/ ./nDPI/src/lib/.libs/libndpi.a -lpcap 8 | 9 | ndpi: 10 | @echo [ -d nDPI ] || git clone http://github.com/ntop/nDPI; 11 | cd nDPI; ./autogen.sh && ./configure && make 12 | 13 | clean: 14 | rm -f ndpiex ndpiexlib.so *.o *~ 15 | -------------------------------------------------------------------------------- /nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ndpi-node", 3 | "description": "nDPI Node", 4 | "author": "Lorenzo Mangani ", 5 | "dependencies": { 6 | "ffi-napi": "^2.4.2", 7 | "node-cache": "^3.2.1", 8 | "pcap": "git+https://github.com/mranney/node_pcap.git", 9 | "pcap-parser": "*", 10 | "ref": "*", 11 | "ref-array": "*", 12 | "ref-struct": "*", 13 | "websocket": "^1.0.23" 14 | }, 15 | "config": { 16 | "unsafe-perm": true 17 | }, 18 | "scripts": { 19 | "start": "node main.js ../pcap/lamernews.pcap" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /nodebeat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ndpi-nodebeat", 3 | "description": "nDPI NodeBeat for Elasticsearch", 4 | "author": "Lorenzo Mangani ", 5 | "version": "0.0.1", 6 | "dependencies": { 7 | "elastic-queue": "*", 8 | "ffi-napi": "^2.4.2", 9 | "node-cache": "^3.2.1", 10 | "pcap": "git+https://github.com/mranney/node_pcap.git", 11 | "pcap-parser": "*", 12 | "ref": "*", 13 | "ref-array": "*", 14 | "ref-struct": "*" 15 | }, 16 | "config": { 17 | "unsafe-perm": true 18 | }, 19 | "scripts": { 20 | "start": "node nodebeat.js" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nDPIex 2 | nDPI example collection 3 | 4 | | Version | Status | 5 | | ------------- |:------------:| 6 | | 0.1 | experimental | 7 | 8 | 9 | 10 | # Installation 11 | Compile latest nDPI from github: 12 | ```bash 13 | cd ndpiex 14 | git clone https://github.com/ntop/nDPI 15 | cd nDPI 16 | ./configure -with-pic 17 | make 18 | cd .. 19 | ``` 20 | Compile stand-alone ndpiex: 21 | ```bash 22 | make 23 | ``` 24 | 25 | Compile FFI binding ndpiexlib: 26 | ```bash 27 | make lib 28 | ``` 29 | 30 | ## Usage 31 | ```bash 32 | ./ndpiex -f file.pcap 33 | ``` 34 | 35 | ### Lua FFI example 36 | ```bash 37 | cd lua; ./run_lua 38 | ``` 39 | 40 | ### Node FFI example 41 | ```bash 42 | cd nodejs; npm install && npm start 43 | ``` 44 | -------------------------------------------------------------------------------- /lua/README.md: -------------------------------------------------------------------------------- 1 | # nDPI Lua FFI example 2 | 3 | * Requires ```ndpiexlib``` 4 | 5 | ``` 6 | ./run_lua 7 | ``` 8 | 9 | ### nDPIex FFI Binding 10 | ``` 11 | ffi.cdef([[ 12 | /* Pcap */ 13 | typedef struct pcap pcap_t; 14 | struct pcap_pkthdr { 15 | uint64_t ts_sec; /* timestamp seconds */ 16 | uint64_t ts_usec; /* timestamp microseconds */ 17 | uint32_t incl_len; /* number of octets of packet saved in file */ 18 | uint32_t orig_len; /* actual length of packet */ 19 | }; 20 | 21 | int printf(const char *format, ...); 22 | pcap_t *pcap_open_offline(const char *fname, char *errbuf); 23 | void pcap_close(pcap_t *p); 24 | const uint8_t *pcap_next(pcap_t *p, struct pcap_pkthdr *h); 25 | 26 | typedef void (*callback)(int, const uint8_t *packet); 27 | 28 | void addProtocolHandler(callback handler); 29 | void init(); 30 | void setDatalinkType(pcap_t *handle); 31 | void processPacket(const struct pcap_pkthdr *header, const uint8_t *packet); 32 | void finish(); 33 | 34 | ]]) 35 | 36 | ``` 37 | -------------------------------------------------------------------------------- /nodejs/client.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /** 3 | * Created by yaodong.hyd on 2016/8/23. 4 | */ 5 | var WebSocketClient = require("websocket").client; 6 | 7 | class BaseClient { 8 | constructor(serverAddress){ 9 | this.client = new WebSocketClient(); 10 | var self = this; 11 | this.client.on("connectFailed",function(error){ 12 | console.log("connect failed : "+error.toString()); 13 | self.handleConnectionFailed(error); 14 | }); 15 | this.client.on("connect",function(connection){ 16 | self.connection = connection; 17 | connection.on("error",function(error){ 18 | console.log("connection error : "+error.toString()); 19 | self.handleError(error); 20 | }); 21 | 22 | connection.on("message",function(msg){ 23 | if (msg.type == 'utf-8'){ 24 | self.handleStringMessage(msg); 25 | }else{ 26 | //TODO:handle other message 27 | } 28 | }); 29 | 30 | connection.on('close',function(){ 31 | console.log('connection closed'); 32 | self.handleConnectionClose(); 33 | }); 34 | }); 35 | this.client.connect(serverAddress,'echo-protocol'); 36 | } 37 | 38 | sendString(msg){ 39 | if (this.connection && this.connection.connected){ 40 | this.connection.sendUTF(msg); 41 | } 42 | } 43 | 44 | handleConnectionFailed(){} 45 | 46 | handleStringMessage(msg){} 47 | 48 | handleError(error){} 49 | 50 | handleConnectionClose(){} 51 | } 52 | 53 | exports.BaseClient = BaseClient; 54 | -------------------------------------------------------------------------------- /nodejs/main.js: -------------------------------------------------------------------------------- 1 | /* nDPI Node.js Binding PoC */ 2 | /* (c) 2015 L. Mangani, QXIP BV */ 3 | /* http://qxip.net */ 4 | 5 | var VERSION = "0.1.4"; 6 | 7 | /* NODE REQs */ 8 | 9 | var ffi = require('ffi-napi'); 10 | //var ffi = require('ffi'); 11 | var ref = require("ref"); 12 | var Struct = require('ref-struct'); 13 | var ArrayType = require('ref-array'); 14 | 15 | /* PCAP Parser */ 16 | 17 | var pcapp = require('pcap-parser'); 18 | if (process.argv[2]) { 19 | var pcap_parser = pcapp.parse(process.argv[2]); 20 | } else { 21 | console.error("usage: node pcap.js /path/to/file.pcap"); 22 | console.error(); 23 | process.exit(); 24 | } 25 | 26 | /* NDPI Types */ 27 | 28 | var voidPtr = exports.voidPtr = ref.refType(ref.types.void); 29 | var u_char = exports.u_char = Struct({ 30 | __u_char: ref.types.uchar, 31 | }); 32 | var u_charPtr = exports.u_charPtr = ref.refType(u_char); 33 | 34 | var pcap_t = exports.pcap_t = voidPtr; 35 | var pcap_tPtr = exports.pcap_tPtr = ref.refType(pcap_t); 36 | var pcap_handler = exports.pcap_handler = ffi.Function(ref.types.void, [ 37 | ref.refType(ref.types.uchar), 38 | voidPtr, 39 | ref.refType(ref.types.uchar), 40 | ]); 41 | var pcap_handlerPtr = exports.pcap_handlerPtr = ref.refType(pcap_handler); 42 | 43 | var uint8_t = exports.uint8_t = voidPtr; 44 | var uint8_tPtr = exports.uint8_tPtr = ref.refType(uint8_t); 45 | 46 | 47 | /* callback */ 48 | const onProto = function(id, packet) { 49 | if (id > 0) { console.log("Proto: "+packet+" ("+id+")") } 50 | } 51 | 52 | var callback = exports.callback = ffi.Function(ref.types.void, [ 53 | ref.types.int32, 54 | ref.refType(uint8_t), 55 | ]); 56 | 57 | var ndpi = ffi.Library('../ndpiexlib.so', { 58 | init: [ref.types.void, [ 59 | ]], 60 | setDatalinkType: [ref.types.void, [ 61 | pcap_tPtr, 62 | ]], 63 | processPacket: [ref.types.void, [ 64 | voidPtr, 65 | uint8_t, 66 | ]], 67 | finish: [ref.types.void, [ 68 | ]], 69 | addProtocolHandler: [ref.types.void, [ 70 | callback 71 | ]], 72 | }); 73 | 74 | /* PCAP Header */ 75 | 76 | var pcap_pkthdr = Struct({ 77 | 'ts_sec': 'uint64', 78 | 'ts_usec': 'uint64', 79 | 'incl_len': 'uint32', 80 | 'orig_len': 'uint32' 81 | }); 82 | 83 | var pcap_pkthdr_ptr = ref.refType(pcap_pkthdr); 84 | 85 | /* APP */ 86 | 87 | console.log("nDPI Node v"+VERSION); 88 | counter = 0; 89 | 90 | /* NDPI LOOP */ 91 | 92 | // ndpi.addProtocolHandler(onProto); 93 | ndpi.init(); 94 | console.log("INIT"); 95 | 96 | pcap_parser.on('globalHeader', function (globalHeader) { 97 | var ltype = new Buffer(globalHeader.linkLayerType); 98 | // ltype.type = ref.refType(pcap_t); 99 | ndpi.setDatalinkType(ltype.ref()) 100 | }); 101 | 102 | console.log("SET DATALINK"); 103 | 104 | function ndpiPipe(h,p){ 105 | if(p===undefined) return; 106 | try { 107 | ndpi.addProtocolHandler(onProto); 108 | ndpi.processPacket(h, p); 109 | } catch(e) { console.log(e); console.log(h,p); } 110 | 111 | } 112 | 113 | 114 | pcap_parser.on('packet', function (raw_packet) { 115 | counter++; 116 | var header = raw_packet.header; 117 | // Build PCAP Hdr Struct 118 | var newHdr = new pcap_pkthdr(); 119 | newHdr.ts_sec=header.timestampSeconds; 120 | newHdr.ts_usec=header.timestampMicroseconds; 121 | newHdr.incl_len=header.capturedLength; 122 | newHdr.orig_len=header.originalLength; 123 | 124 | ndpiPipe(newHdr.ref(), raw_packet.data ); 125 | 126 | }); 127 | 128 | pcap_parser.on('end', function () { 129 | ndpi.finish(); 130 | }); 131 | 132 | var exit = false; 133 | 134 | process.on('exit', function() { 135 | exports.callback; 136 | console.log('Total Packets: '+counter); 137 | }); 138 | 139 | process.on('SIGINT', function() { 140 | console.log(); 141 | if (exit) { 142 | console.log("Exiting..."); 143 | ndpi.finish(); 144 | process.exit(); 145 | } else { 146 | console.log("Press CTRL-C within 2 seconds to Exit..."); 147 | exit = true; 148 | setTimeout(function () { 149 | exit = false; 150 | }, 2000) 151 | } 152 | }); 153 | 154 | 155 | -------------------------------------------------------------------------------- /lua/main.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/luajit 2 | 3 | -- ndpi lua binding 4 | -- Usage: luajit main.lua /path/to/file.pcap 5 | 6 | local arg = ... 7 | local pfile = "" 8 | 9 | if arg==nil then 10 | pfile = "../pcap/lamernews.pcap" 11 | else 12 | pfile = arg 13 | end 14 | 15 | print("Loading " .. pfile ) 16 | 17 | local ffi = require('ffi') 18 | local C = ffi.C 19 | 20 | local ndpi = ffi.load("../ndpiexlib.so") 21 | local pcap = ffi.load("pcap") 22 | 23 | ffi.cdef([[ 24 | /* Pcap */ 25 | typedef struct pcap pcap_t; 26 | struct pcap_pkthdr { 27 | uint64_t ts_sec; /* timestamp seconds */ 28 | uint64_t ts_usec; /* timestamp microseconds */ 29 | uint32_t incl_len; /* number of octets of packet saved in file */ 30 | uint32_t orig_len; /* actual length of packet */ 31 | }; 32 | 33 | int printf(const char *format, ...); 34 | pcap_t *pcap_open_offline(const char *fname, char *errbuf); 35 | void pcap_close(pcap_t *p); 36 | const uint8_t *pcap_next(pcap_t *p, struct pcap_pkthdr *h); 37 | 38 | /* NDPIReader */ 39 | typedef void (*callback)(int, const uint8_t *packet); 40 | 41 | void addProtocolHandler(callback handler); 42 | void init(); 43 | void setDatalinkType(pcap_t *handle); 44 | void processPacket(const struct pcap_pkthdr *header, const uint8_t *packet); 45 | void finish(); 46 | 47 | ]]) 48 | 49 | local L7PROTO = { 50 | "Unknown","FTP_CONTROL","POP3","SMTP","IMAP","DNS","IPP","HTTP","MDNS","NTP","NetBIOS","NFS","SSDP","BGP","SNMP","XDMCP","SMB","Syslog","DHCP","PostgreSQL","MySQL","TDS","Direct_Download_Link","POPS","AppleJuice","DirectConnect","Socrates","WinMX","VMware","SMTPS","Filetopia","iMESH","Kontiki","OpenFT","FastTrack","Gnutella","eDonkey","BitTorrent","EPP","AVI","Flash","OggVorbis","MPEG","QuickTime","RealMedia","WindowsMedia","MMS","Xbox","QQ","Move","RTSP","IMAPS","IceCast","PPLive","PPStream","Zattoo","ShoutCast","Sopcast","Tvants","TVUplayer","HTTP_APPLICATION_VEOHTV","QQLive","Thunder","Soulseek","SSL_No_Cert","IRC","Ayiya","Unencryped_Jabber","MSN","Oscar","Yahoo","BattleField","Quake","VRRP","Steam","HalfLife2","WorldOfWarcraft","Telnet","STUN","IPsec","GRE","ICMP","IGMP","EGP","SCTP","OSPF","IP_in_IP","RTP","RDP","VNC","PcAnywhere","SSL","SSH","Usenet","MGCP","IAX","TFTP","AFP","Stealthnet","Aimini","SIP","TruPhone","ICMPV6","DHCPV6","Armagetron","Crossfire","Dofus","Fiesta","Florensia","Guildwars","HTTP_Application_ActiveSync","Kerberos","LDAP","MapleStory","MsSQL","PPTP","Warcraft3","WorldOfKungFu","Meebo","Facebook","Twitter","DropBox","GMail","GoogleMaps","YouTube","Skype","Google","DCE_RPC","NetFlow","sFlow","HTTP_Connect","HTTP_Proxy","Citrix","NetFlix","LastFM","GrooveShark","SkyFile_PrePaid","SkyFile_Rudics","SkyFile_PostPaid","Citrix_Online","Apple","Webex","WhatsApp","AppleiCloud","Viber","AppleiTunes","Radius","WindowsUpdate","TeamViewer","Tuenti","LotusNotes","SAP","GTP","UPnP","LLMNR","RemoteScan","Spotify","WebM","H323","OpenVPN","NOE","CiscoVPN","TeamSpeak","TOR","CiscoSkinny","RTCP","RSYNC","Oracle","Corba","UbuntuONE","Whois-DAS","Collectd","SOCKS5","SOCKS4","RTMP","FTP_DATA","Wikipedia","ZeroMQ","Amazon","eBay","CNN","Megaco","Redis","Pando_Media_Booster","VHUA","Telegram","FacebookChat","Pandora","Vevo", 51 | } 52 | 53 | function onProtocol(id, packet) 54 | if id >= 2 then 55 | io.write("Proto: ") 56 | print( ffi.string(packet), "ID:", id) 57 | end 58 | end 59 | 60 | -- Register protocol handler 61 | ndpi.addProtocolHandler(onProtocol) 62 | 63 | local pcap = ffi.load("pcap") 64 | 65 | local filename = pfile 66 | local fname = ffi.new("char[?]", #filename, filename) 67 | local errbuf = ffi.new("char[512]") 68 | 69 | -- Read pcap file 70 | local handle = pcap.pcap_open_offline(fname, errbuf) 71 | if handle == nil then 72 | C.printf(errbuf) 73 | end 74 | 75 | ndpi.init() 76 | ndpi.setDatalinkType(handle) 77 | 78 | local header = ffi.new("struct pcap_pkthdr") 79 | -- Inspect each packet 80 | local total_packets = 0 81 | while (1) do 82 | local packet = pcap.pcap_next(handle, header) 83 | if packet == nil then break end 84 | ndpi.processPacket(header, packet) 85 | total_packets = total_packets + 1 86 | end 87 | pcap.pcap_close(handle) 88 | 89 | -- Print results 90 | ndpi.finish() 91 | 92 | print("Total packets: "..total_packets) 93 | 94 | -------------------------------------------------------------------------------- /nodebeat/nodebeat.js: -------------------------------------------------------------------------------- 1 | /* nDPI Node.js Binding PoC */ 2 | /* (c) 2015 QXIP BV */ 3 | /* http://qxip.net */ 4 | 5 | var VERSION = "0.2"; 6 | console.log("nDPI NodeBeat v"+VERSION); 7 | console.log("CTRL-C to exit!"); 8 | var counts = { task: 0, batch: 0, drain: 0, pkts: 0 }; 9 | var debug = false; 10 | 11 | if(process.argv.indexOf("-d") != -1){ 12 | debug = true; 13 | } 14 | 15 | if(process.argv.indexOf("-s") != -1){ 16 | var elastic = process.argv[process.argv.indexOf("-s") + 1]; 17 | } 18 | 19 | if (!elastic) { console.log('missing argument! -s ');process.exit(0);} 20 | 21 | // Create Client Config 22 | var client = { host: elastic }; 23 | if (debug) client.log = 'trace'; 24 | 25 | /* NODE REQs */ 26 | 27 | var ref = require('ref'); 28 | var ffi = require('ffi-napi'); 29 | var Struct = require('ref-struct'); 30 | // var ArrayType = require('ref-array'); 31 | var pcap = require("pcap"); 32 | var pcap_session = pcap.createSession("", ""); 33 | 34 | /* Elastic Queue */ 35 | 36 | var ElasticQueue, Queue; 37 | var ElasticQueue = require('elastic-queue'); 38 | 39 | Queue = new ElasticQueue({ 40 | elasticsearch: { client: client }, 41 | batchSize: 50, 42 | commitTimeout: 1000, 43 | rateLimit: 1000 44 | }); 45 | 46 | Queue.on('task', function(batch) { 47 | counts.task++; 48 | return; 49 | }); 50 | 51 | Queue.on('batchComplete', function(resp) { 52 | counts.batch++; 53 | return; 54 | }); 55 | 56 | Queue.on('drain', function() { 57 | counts.drain++; 58 | return; 59 | // console.log("\n\nQueue is Empty\n\n"); 60 | // Queue.close(); 61 | // return process.exit(); 62 | }); 63 | 64 | 65 | /* NDPI CALLBACK */ 66 | 67 | 68 | /* APP VARS */ 69 | 70 | var voidPtr = exports.voidPtr = ref.refType(ref.types.void); 71 | var u_char = exports.u_char = Struct({ 72 | __u_char: ref.types.uchar, 73 | }); 74 | var u_charPtr = exports.u_charPtr = ref.refType(u_char); 75 | 76 | var uint8_t = exports.uint8_t = voidPtr; 77 | var uint8_tPtr = exports.uint8_tPtr = ref.refType(uint8_t); 78 | 79 | // var callbackPtr = ffi.Callback(ref.types.void, [ ref.types.int32, ref.refType(ref.types.uchar) ], onProto); 80 | // var callbackF = ffi.ForeignFunction(callbackPtr, ref.types.void, [ ref.types.int32, ref.refType(ref.types.uchar) ]); 81 | 82 | var callback = exports.callback = ffi.Function(ref.types.void, [ 83 | ref.types.int32, 84 | ref.refType(ref.types.uchar), 85 | ]); 86 | 87 | var pcap_t = exports.pcap_t = voidPtr; 88 | var pcap_tPtr = exports.pcap_tPtr = ref.refType(pcap_t); 89 | var pcap_handler = exports.pcap_handler = ffi.Function(ref.types.void, [ 90 | ref.refType(ref.types.uchar), 91 | voidPtr, 92 | ref.refType(ref.types.uchar), 93 | ]); 94 | var pcap_handlerPtr = exports.pcap_handlerPtr = ref.refType(pcap_handler); 95 | 96 | // PCAP Header 97 | var pcap_pkthdr = exports.pcap_pkthdr = Struct({ 98 | 'ts_sec': 'long', 99 | 'ts_usec': 'long', 100 | 'incl_len': 'int', 101 | 'orig_len': 'int' 102 | }); 103 | 104 | var pktHdr = exports.pktHdr = new pcap_pkthdr; 105 | pktHdr = ref.refType(ref.types.void); 106 | 107 | /* NDPI Hook */ 108 | var ndpi = exports.ndpi = new ffi.Library('../ndpiexlib.so', { 109 | init: [ref.types.void, [ 110 | ]], 111 | getResults: [ref.types.void, [ 112 | ]], 113 | setDatalinkType: [ref.types.void, [ 114 | pcap_tPtr, 115 | ]], 116 | processPacket: [ref.types.void, [ 117 | voidPtr, 118 | uint8_t, 119 | ]], 120 | finish: [ref.types.void, [ 121 | ]], 122 | addProtocolHandler: [ref.types.void, [ 123 | callback 124 | ]], 125 | }); 126 | 127 | /* PCAP LOOP */ 128 | 129 | var getIndex = exports.getIndex = function(){ 130 | var now = new Date(); 131 | return "ndpibeat-"+new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())).toISOString().slice(0, 10).replace(/-/g, '.'); 132 | } 133 | 134 | const onProto = exports.onProto = function(id, packet) { 135 | try { 136 | 137 | if (id > 0) { 138 | var doc = { 139 | index: getIndex(), 140 | type: 'ndpi', 141 | body: { 142 | ts: (new Date()).toISOString(), 143 | proto_name: packet, 144 | proto_id: id 145 | } 146 | }; 147 | 148 | Queue.push(doc, function(err, resp) { 149 | if (err) { 150 | return console.log(err); 151 | } 152 | // return console.log(resp); 153 | }); 154 | } 155 | } catch(e) { console.log(e); } 156 | } 157 | 158 | /* APP */ 159 | 160 | var init = ndpi.init(); 161 | 162 | console.log("Listening on " + pcap_session.device_name); 163 | 164 | var ndpiPipe = exports.ndpiPipe = function(h,p){ 165 | try { 166 | ndpi.addProtocolHandler(onProto); 167 | ndpi.processPacket(h, p ); 168 | } catch(e) { console.log(e); } 169 | } 170 | 171 | pcap_session.on('packet', function (raw_packet) { 172 | if (raw_packet.header) { 173 | counts.pkts++; 174 | ndpiPipe(raw_packet.header.ref(), raw_packet.buf ); 175 | } return; 176 | }); 177 | 178 | var exit = false; 179 | 180 | process.on('exit', function() { 181 | callback; onProto; ndpiPipe;pcap_session;pcap; 182 | console.log('Total Packets: '+counts.pkts); 183 | }); 184 | 185 | /* Exit */ 186 | 187 | process.on('SIGINT', function() { 188 | console.log(); 189 | console.log('Stats:',counts); 190 | console.log('Packets Captured:'+counts.pkts); 191 | if (exit) { 192 | console.log("Exiting..."); 193 | ndpi.finish(); 194 | process.exit(); 195 | } else { 196 | console.log("Press CTRL-C within 2 seconds to Exit..."); 197 | ndpi.getResults(); 198 | exit = true; 199 | setTimeout(function () { 200 | // console.log("Continuing..."); 201 | exit = false; 202 | }, 2000) 203 | } 204 | }); 205 | -------------------------------------------------------------------------------- /nodejs/pcap.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* nDPI Node.js Binding PoC */ 3 | /* (c) 2015 QXIP BV */ 4 | /* http://qxip.net */ 5 | 6 | var VERSION = "0.1.1"; 7 | 8 | /* NODE REQs */ 9 | 10 | var ref = require('ref'); 11 | var ffi = require('ffi'); 12 | var Struct = require('ref-struct'); 13 | var ArrayType = require('ref-array'); 14 | var fs = require('fs'); 15 | var pcap = require("pcap"), 16 | pcap_session = pcap.createSession("", ""); 17 | 18 | //var CONF = JSON.parse(fs.readFileSync("./config.json","r+")); 19 | var CONF = {}; 20 | 21 | var BaseClient = require('./client').BaseClient; 22 | 23 | class MyClient extends BaseClient{ 24 | handleStringMessage(msg){ 25 | try{ 26 | var message = JSON.parse(msg); 27 | //TODO:do some message 28 | }catch (e){ 29 | console.log(e.toString()); 30 | } 31 | } 32 | } 33 | 34 | //var client = new MyClient(CONF['serverAddr']); 35 | var client = null; 36 | var IPv4 = require('pcap/decode/ipv4'); 37 | var TCP = require('pcap/decode/tcp'); 38 | var UDP = require('pcap/decode/udp'); 39 | var runner = {}; 40 | 41 | /* NDPI CALLBACK */ 42 | 43 | // On Windows UTF-16 (2-bytes), Unix UTF-32 (4-bytes) 44 | runner.wchar_size = process.platform == 'win32' ? 2 : 4 45 | 46 | runner.wchar_t = Object.create(ref.types.CString); 47 | runner.wchar_t.get = function get (buf, offset) { 48 | var _buf = buf.readPointer(offset) 49 | if (_buf.isNull()) { 50 | return; 51 | } 52 | var stringBuf = _buf.reinterpretUntilZeros(runner.wchar_size) 53 | return stringBuf.toString('win32' ? 'utf16le' : 'utf32li') // TODO: decode UTF-32 on Unix 54 | }; 55 | 56 | runner.wchar_t.set = function set (buf, offset, val) { 57 | // TODO: better UTF-16 and UTF-32 encoding 58 | var _buf = new Buffer((val.length + 1) * runner.wchar_size) 59 | _buf.fill(0) 60 | var l = 0 61 | for (var i = runner.wchar_size - 1; i < _buf.length; i += runner.wchar_size) { 62 | _buf[i] = val.charCodeAt(l++) 63 | } 64 | return buf.writePointer(_buf, offset) 65 | }; 66 | 67 | runner.callback_Ptr = ArrayType(runner.wchar_t); 68 | 69 | /* APP VARS */ 70 | 71 | runner.voidPtr = exports.voidPtr = ref.refType(ref.types.void); 72 | runner.uint8_t = exports.uint8_t = runner.voidPtr; 73 | runner.uint8_tPtr = exports.uint8_tPtr = ref.refType(runner.uint8_t); 74 | runner.callback = exports.callback = ffi.Function(ref.types.void, [ 75 | ref.types.int32, 76 | ref.refType(ref.types.uchar) 77 | ]); 78 | runner.pcap_t = exports.pcap_t = runner.voidPtr; 79 | runner.pcap_tPtr = exports.pcap_tPtr = ref.refType(runner.pcap_t); 80 | runner.pcap_handler = exports.pcap_handler = ffi.Function(ref.types.void, [ 81 | ref.refType(ref.types.uchar), 82 | runner.voidPtr, 83 | ref.refType(ref.types.uchar) 84 | ]); 85 | runner.pcap_handlerPtr = exports.pcap_handlerPtr = ref.refType(runner.pcap_handler); 86 | 87 | // PCAP Header 88 | var pcap_pkthdr = Struct({ 89 | 'ts_sec': 'long', 90 | 'ts_usec': 'long', 91 | 'incl_len': 'int', 92 | 'orig_len': 'int' 93 | }); 94 | 95 | var pktHdr = new pcap_pkthdr; 96 | pktHdr = ref.refType(ref.types.void); 97 | 98 | runner.gcallback = ffi.Callback('void', [ref.types.int32, ref.refType(ref.types.uchar)], 99 | function(id) { 100 | console.log("id: ", id); 101 | }); 102 | 103 | runner.ndpi = exports.ndpi = new ffi.Library('../ndpiexlib.so', { 104 | init: [ref.types.void, [ 105 | ]], 106 | setDatalinkType: [ref.types.void, [ 107 | runner.pcap_tPtr, 108 | ]], 109 | processPacket: [ref.types.void, [ 110 | runner.voidPtr, 111 | runner.uint8_t, 112 | ]], 113 | finish: [ref.types.void, [ 114 | ]], 115 | addProtocolHandler: [ref.types.void, [ 116 | runner.callback 117 | ]], 118 | }); 119 | 120 | 121 | /* APP */ 122 | 123 | console.log("nDPI Node v"+VERSION); 124 | 125 | var counter = 0; 126 | var init = runner.ndpi.init(); 127 | 128 | var reboot = function(){ 129 | runner.ndpi.finish(); 130 | runner.ndpi.init(); 131 | //console.log('nDPI restarted!'); 132 | } 133 | 134 | /* PCAP LOOP */ 135 | 136 | console.log("Listening on " + pcap_session.device_name); 137 | 138 | runner.onProto = function(id, packet) { 139 | if (id > 0) console.log("Proto: "+id+" "+packet); 140 | } 141 | 142 | 143 | runner.getFlowInfo = function(packet,l7_protocol){ 144 | if(packet.payload.payload instanceof IPv4){ 145 | var ip = packet.payload.payload; 146 | var saddr = ip.saddr; 147 | var daddr = ip.daddr; 148 | var sport = null; 149 | var dport = null; 150 | var tsl_packet = packet.payload.payload.payload; 151 | var tsl_protocol = ''; 152 | if(tsl_packet instanceof TCP){ 153 | tsl_protocol = 'tcp'; 154 | sport = tsl_packet.sport; 155 | dport = tsl_packet.dport; 156 | }else if (tsl_packet instanceof UDP){ 157 | tsl_protocol = 'udp'; 158 | sport = tsl_packet.sport; 159 | dport = tsl_packet.dport; 160 | }else{ 161 | tsl_protocol = 'unknown'; 162 | sport = tsl_packet.sport; 163 | dport = tsl_packet.dport; 164 | console.log('skip!'); 165 | } 166 | return {l7_protocol,tsl_protocol,saddr,daddr,sport,dport}; 167 | } 168 | } 169 | 170 | 171 | runner.onPacketAnalyzedCallback = function(flow_info){ 172 | console.log("flow from "+flow_info.saddr+":"+flow_info.sport+" to "+flow_info.daddr+":"+flow_info.dport+" with protocol : "+flow_info.l7_protocol); 173 | } 174 | 175 | runner.ndpi.addProtocolHandler(runner.onProto); 176 | runner.ndpiPipe = function(header,packet,callback){ 177 | try { 178 | runner.ndpi.addProtocolHandler(function(id,p){ 179 | if(id > 0){ 180 | callback(runner.getFlowInfo(pcap.decode.packet(packet),p)); 181 | } 182 | }); 183 | runner.ndpi.processPacket(header, packet.buf); 184 | } catch(e) { console.log(e); } 185 | } 186 | 187 | pcap_session.on('packet', function (raw_packet) { 188 | if (raw_packet.header) { 189 | counter++; 190 | runner.ndpiPipe(raw_packet.header.ref(), raw_packet, runner.onPacketAnalyzedCallback ); 191 | if (counter % 200 === 0) { reboot(); } 192 | } 193 | }); 194 | 195 | 196 | 197 | var exit = false; 198 | 199 | process.on('exit', function() { 200 | console.log('Total Packets: '+counter); 201 | runner; 202 | }); 203 | 204 | process.on('SIGINT', function() { 205 | console.log(); 206 | if (exit) { 207 | console.log("Exiting..."); 208 | runner.ndpi.finish(); 209 | process.exit(); 210 | } else { 211 | console.log("Press CTRL-C within 2 seconds to Exit..."); 212 | exit = true; 213 | setTimeout(function () { 214 | // console.log("Continuing..."); 215 | exit = false; 216 | }, 2000) 217 | } 218 | }); 219 | 220 | 221 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /ndpiexlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ndpiexlib.c 3 | * Copyright (C) 2018 by QXIP BV 4 | * Copyright (C) 2018 by ntop 5 | * 6 | * Author: Lorenzo Mangani - Michele Campus 7 | * based on code of ndpiReader and nDPI 8 | * 9 | * This is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * ndpiex is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public License 20 | * along with nDPI. If not, see . 21 | * 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define __STDC_FORMAT_MACROS 31 | #include 32 | #include 33 | #include 34 | 35 | #include "ndpi_api.h" 36 | 37 | /** 38 | NOTE: in case we want to use nDPI data struct, 39 | link ./nDPI/example/ndpi_util.h in Makefile 40 | */ 41 | //#include "nDPI/example/ndpi_util.h" 42 | 43 | #define MAX_OSDPI_IDS 100000 44 | #define MAX_OSDPI_FLOWS 100000 45 | #define TICK_RESOLUTION 1000 46 | 47 | typedef void (*callback)(int, const u_int8_t *packet); 48 | typedef void (*databack)(int, int, int, int, int, char ); 49 | 50 | // prototypes used function 51 | void init(); 52 | void setDatalinkType(pcap_t *handle); 53 | void processPacket(const struct pcap_pkthdr *header, const u_char *packet); 54 | void finish(); 55 | void addProtocolHandler(callback handler); 56 | 57 | 58 | // time 59 | struct timeval begin, end; 60 | u_int64_t tot_usec; 61 | 62 | // pcap 63 | static char _pcap_error_buffer[PCAP_ERRBUF_SIZE]; 64 | static pcap_t *_pcap_handle = NULL; 65 | static int _pcap_datalink_type; 66 | 67 | // detection 68 | static struct ndpi_detection_module_struct *ndpi_info_mod = NULL; 69 | static u_int32_t detection_tick_resolution = 1000; 70 | 71 | #ifdef NDPI_ENABLE_DEBUG_MESSAGES 72 | 73 | static NDPI_PROTOCOL_BITMASK debug_messages_bitmask; 74 | #endif 75 | 76 | // results 77 | static u_int64_t raw_packet_count; 78 | static u_int64_t ip_packet_count; 79 | static u_int64_t total_bytes; 80 | static u_int64_t protocol_counter[NDPI_MAX_SUPPORTED_PROTOCOLS + 1]; 81 | static u_int64_t protocol_counter_bytes[NDPI_MAX_SUPPORTED_PROTOCOLS + 1]; 82 | 83 | 84 | /** id tracking **/ 85 | struct osdpi_id { 86 | u_int8_t ip[4]; 87 | struct ndpi_id_struct *ndpi_id; 88 | }; 89 | 90 | static u_int32_t size_id_struct; 91 | static struct osdpi_id *osdpi_ids; 92 | static u_int32_t osdpi_id_count; 93 | 94 | 95 | /** flow tracking **/ 96 | struct osdpi_flow { 97 | 98 | u_int32_t hashval; 99 | u_int32_t src_ip; 100 | u_int32_t dst_ip; 101 | u_int16_t src_port; 102 | u_int16_t dst_port; 103 | u_int8_t detection_completed, protocol, bidirectional, check_extra_packets; 104 | u_int16_t vlan_id; 105 | struct ndpi_flow_struct *ndpi_flow; 106 | char src_name[48], dst_name[48]; 107 | u_int8_t ip_version; 108 | u_int64_t last_seen; 109 | u_int64_t src2dst_bytes, dst2src_bytes; 110 | u_int32_t src2dst_packets, dst2src_packets; 111 | ndpi_protocol detected_protocol; 112 | char info[96]; 113 | char host_server_name[192]; 114 | char bittorent_hash[41]; 115 | void *src_id, *dst_id; 116 | }; 117 | 118 | static u_int32_t size_flow_struct; 119 | static struct osdpi_flow *osdpi_flows; 120 | static u_int32_t osdpi_flow_count; 121 | 122 | /* ***************************************************** */ 123 | 124 | callback protocolHandler; 125 | 126 | void addProtocolHandler(callback handler) { 127 | protocolHandler = handler; 128 | } 129 | 130 | void onProtocol(u_int16_t id, const u_int8_t *packet) { 131 | if (protocolHandler && id) { 132 | protocolHandler(id, packet); 133 | } 134 | } 135 | 136 | /* ***************************************************** */ 137 | 138 | #ifdef NDPI_ENABLE_DEBUG_MESSAGES 139 | static int string_to_detection_bitmask(char *str, NDPI_PROTOCOL_BITMASK * dbm) 140 | { 141 | u_int32_t a; 142 | u_int32_t oldptr = 0; 143 | u_int32_t ptr = 0; 144 | NDPI_BITMASK_RESET(*dbm); 145 | 146 | printf("Protocol parameter given: %s\n", str); 147 | 148 | if (strcmp(str, "all") == 0) { 149 | printf("Protocol parameter all parsed\n"); 150 | NDPI_BITMASK_SET_ALL(*dbm); 151 | printf("Bitmask is: " NDPI_BITMASK_DEBUG_OUTPUT_BITMASK_STRING " \n", 152 | NDPI_BITMASK_DEBUG_OUTPUT_BITMASK_VALUE(*dbm)); 153 | return 0; 154 | } 155 | // parse bitmask 156 | while (1) { 157 | if (str[ptr] == 0 || str[ptr] == ' ') { 158 | printf("Protocol parameter: parsed: %.*s,\n", ptr - oldptr, &str[oldptr]); 159 | for (a = 1; a <= NDPI_MAX_SUPPORTED_PROTOCOLS; a++) { 160 | 161 | if (strlen(prot_short_str[a]) == (ptr - oldptr) && 162 | (memcmp(&str[oldptr], prot_short_str[a], ptr - oldptr) == 0)) { 163 | NDPI_ADD_PROTOCOL_TO_BITMASK(*dbm, a); 164 | printf("Protocol parameter detected as protocol %s\n", prot_long_str[a]); 165 | } 166 | } 167 | oldptr = ptr + 1; 168 | if (str[ptr] == 0) 169 | break; 170 | } 171 | ptr++; 172 | } 173 | return 0; 174 | } 175 | #endif 176 | 177 | static void debug_printf(u_int32_t protocol, void *id_struct, ndpi_log_level_t log_level, const char *format, ...) 178 | { 179 | #ifdef NDPI_ENABLE_DEBUG_MESSAGES 180 | if (NDPI_COMPARE_PROTOCOL_TO_BITMASK(debug_messages_bitmask, protocol) != 0) { 181 | const char *protocol_string; 182 | const char *file; 183 | const char *func; 184 | u_int32_t line; 185 | va_list ap; 186 | va_start(ap, format); 187 | 188 | protocol_string = prot_short_str[protocol.app_protocol]; 189 | 190 | ndpi_debug_get_last_log_function_line(ndpi_info_mod, &file, &func, &line); 191 | 192 | printf("\nDEBUG: %s:%s:%u Prot: %s, level: %u packet: %"PRIu64" :", file, func, line, protocol_string, 193 | log_level, raw_packet_count); 194 | vprintf(format, ap); 195 | va_end(ap); 196 | } 197 | #endif 198 | } 199 | 200 | /** 201 | function to return the ID of protocol 202 | */ 203 | static void *get_id(const u_int8_t * ip) 204 | { 205 | u_int32_t i; 206 | for (i = 0; i < osdpi_id_count; i++) { 207 | if (memcmp(osdpi_ids[i].ip, ip, sizeof(u_int8_t) * 4) == 0) { 208 | return osdpi_ids[i].ndpi_id; 209 | } 210 | } 211 | if (osdpi_id_count == MAX_OSDPI_IDS) { 212 | printf("ERROR: maximum unique id count (%u) has been exceeded\n", MAX_OSDPI_IDS); 213 | exit(-1); 214 | } 215 | else { 216 | struct ndpi_id_struct *ndpi_id; 217 | memcpy(osdpi_ids[osdpi_id_count].ip, ip, sizeof(u_int8_t) * 4); 218 | ndpi_id = osdpi_ids[osdpi_id_count].ndpi_id; 219 | 220 | osdpi_id_count += 1; 221 | return ndpi_id; 222 | } 223 | } 224 | 225 | 226 | /** 227 | function to return the flow of protocol 228 | */ 229 | static struct osdpi_flow *get_osdpi_flow(const struct iphdr *iph, u_int16_t ipsize) 230 | { 231 | u_int32_t i; 232 | u_int16_t l4_packet_len; 233 | struct tcphdr *tcph = NULL; 234 | struct udphdr *udph = NULL; 235 | 236 | u_int32_t src_ip; 237 | u_int32_t dst_ip; 238 | u_int16_t src_port; 239 | u_int16_t dst_port; 240 | 241 | if(ipsize < 20) 242 | return NULL; 243 | 244 | if((iph->ihl * 4) > ipsize || ipsize < ntohs(iph->tot_len) 245 | || (iph->frag_off & htons(0x1FFF)) != 0) 246 | return NULL; 247 | 248 | l4_packet_len = ntohs(iph->tot_len) - (iph->ihl * 4); 249 | 250 | if(iph->saddr < iph->daddr) { 251 | src_ip = iph->saddr; 252 | dst_ip = iph->daddr; 253 | } 254 | else { 255 | src_ip = iph->daddr; 256 | dst_ip = iph->saddr; 257 | } 258 | 259 | // TCP 260 | if(iph->protocol == 6 && l4_packet_len >= 20) { 261 | tcph = (struct tcphdr *) ((u_int8_t *) iph + iph->ihl * 4); 262 | if(iph->saddr < iph->daddr) { 263 | src_port = tcph->source; 264 | dst_port = tcph->dest; 265 | } 266 | else { 267 | src_port = tcph->dest; 268 | dst_port = tcph->source; 269 | } 270 | } 271 | // UDP 272 | else if(iph->protocol == 17 && l4_packet_len >= 8) { 273 | udph = (struct udphdr *) ((u_int8_t *) iph + iph->ihl * 4); 274 | if(iph->saddr < iph->daddr) { 275 | src_port = udph->source; 276 | dst_port = udph->dest; 277 | } 278 | else { 279 | src_port = udph->dest; 280 | dst_port = udph->source; 281 | } 282 | } 283 | else { 284 | // non tcp/udp protocols 285 | src_port = 0; 286 | dst_port = 0; 287 | } 288 | 289 | /*** CHECK this for (maybe must be changed) ***/ 290 | for(i = 0; i < osdpi_flow_count; i++) { 291 | if(osdpi_flows[i].protocol == iph->protocol && 292 | osdpi_flows[i].src_ip == src_ip && 293 | osdpi_flows[i].dst_ip == dst_ip && 294 | osdpi_flows[i].src_port == src_port && osdpi_flows[i].dst_port == dst_port) { 295 | return &osdpi_flows[i]; 296 | } 297 | } 298 | if(osdpi_flow_count == MAX_OSDPI_FLOWS) { 299 | printf("ERROR: maximum flow count (%u) has been exceeded\n", MAX_OSDPI_FLOWS); 300 | exit(-1); 301 | } 302 | else { 303 | 304 | // new flow allocated to be returned from the function 305 | struct osdpi_flow *new_flow; 306 | 307 | osdpi_flows[osdpi_flow_count].protocol = iph->protocol; 308 | osdpi_flows[osdpi_flow_count].src_ip = src_ip; 309 | osdpi_flows[osdpi_flow_count].dst_ip = dst_ip; 310 | osdpi_flows[osdpi_flow_count].src_port = src_port; 311 | osdpi_flows[osdpi_flow_count].dst_port = dst_port; 312 | 313 | // new flow 314 | new_flow = &osdpi_flows[osdpi_flow_count]; 315 | 316 | osdpi_flow_count += 1; 317 | return new_flow; 318 | } 319 | } 320 | 321 | 322 | /** 323 | Initialize all structures necessary for detection 324 | */ 325 | static void setupDetection(void) 326 | { 327 | u_int32_t i; 328 | NDPI_PROTOCOL_BITMASK all; 329 | 330 | // init global detection structure 331 | ndpi_info_mod = ndpi_init_detection_module(); 332 | if(ndpi_info_mod == NULL) { 333 | printf("ERROR: global structure initialization failed\n"); 334 | exit(-1); 335 | } 336 | 337 | // enable all protocols 338 | NDPI_BITMASK_SET_ALL(all); 339 | ndpi_set_protocol_detection_bitmask2(ndpi_info_mod, &all); 340 | 341 | // allocate memory for id and flow tracking 342 | size_id_struct = ndpi_detection_get_sizeof_ndpi_id_struct(); 343 | size_flow_struct = ndpi_detection_get_sizeof_ndpi_flow_struct(); 344 | 345 | // allocate memory for ids struct 346 | osdpi_ids = calloc(MAX_OSDPI_IDS, sizeof(struct osdpi_id)); 347 | if(osdpi_ids == NULL) { 348 | printf("ERROR: malloc for osdpi_ids failed\n"); 349 | exit(-1); 350 | } 351 | for(i = 0; i < MAX_OSDPI_IDS; i++) { 352 | /* memset(&osdpi_ids[i], 0, sizeof(struct osdpi_id)); */ 353 | osdpi_ids[i].ndpi_id = calloc(1, size_id_struct); 354 | if(osdpi_ids[i].ndpi_id == NULL) { 355 | printf("ERROR: malloc for ndpi_id struct inside osdpi_ids failed\n"); 356 | exit(-1); 357 | } 358 | } 359 | 360 | // allocate memory for flow struct 361 | osdpi_flows = calloc(MAX_OSDPI_FLOWS, sizeof(struct osdpi_flow)); 362 | if(osdpi_flows == NULL) { 363 | printf("ERROR: malloc for osdpi_flows failed\n"); 364 | exit(-1); 365 | } 366 | for (i = 0; i < MAX_OSDPI_FLOWS; i++) { 367 | /* memset(&osdpi_flows[i], 0, sizeof(struct osdpi_flow)); */ 368 | osdpi_flows[i].ndpi_flow = calloc(1, size_flow_struct); 369 | if(osdpi_flows[i].ndpi_flow == NULL) { 370 | printf("ERROR: malloc for ndpi_flow_struct failed\n"); 371 | exit(-1); 372 | } 373 | } 374 | 375 | // clear memory for results 376 | memset(protocol_counter, 0, (NDPI_MAX_SUPPORTED_PROTOCOLS + 1) * sizeof(u_int64_t)); 377 | memset(protocol_counter_bytes, 0, (NDPI_MAX_SUPPORTED_PROTOCOLS + 1) * sizeof(u_int64_t)); 378 | } 379 | 380 | 381 | /** 382 | Function to terminate detection 383 | */ 384 | static void terminateDetection(void) 385 | { 386 | u_int32_t i; 387 | 388 | // free ids 389 | for(i = 0; i < MAX_OSDPI_IDS; i++) { 390 | free(osdpi_ids[i].ndpi_id); 391 | } 392 | 393 | free(osdpi_ids); 394 | 395 | // free flows 396 | for (i = 0; i < MAX_OSDPI_FLOWS; i++) { 397 | free(osdpi_flows[i].ndpi_flow); 398 | } 399 | 400 | free(osdpi_flows); 401 | } 402 | 403 | 404 | /** 405 | Function to process the packet: 406 | determine the flow of a packet and try to decode it 407 | @return: 0 if success; != 0 fail 408 | 409 | @Note: ipsize = header->len - ip_offset ; rawsize = header->len 410 | */ 411 | static unsigned int packet_processing(const u_int64_t time, 412 | const struct iphdr *iph, 413 | uint16_t ipsize, 414 | uint16_t rawsize) 415 | { 416 | struct ndpi_id_struct *src = NULL; 417 | struct ndpi_id_struct *dst = NULL; 418 | struct osdpi_flow *flow = NULL; 419 | struct ndpi_flow_struct *ipq_flow = NULL; 420 | ndpi_protocol protocol; 421 | 422 | src = get_id((u_int8_t *) &iph->saddr); 423 | dst = get_id((u_int8_t *) &iph->daddr); 424 | 425 | flow = get_osdpi_flow(iph, ipsize); 426 | if(flow != NULL) { 427 | ipq_flow = flow->ndpi_flow; 428 | } 429 | 430 | // update stats for ip pkts and bytes 431 | ip_packet_count++; 432 | total_bytes += rawsize; 433 | 434 | // only handle unfragmented packets 435 | if((iph->frag_off & htons(0x1FFF)) == 0) { 436 | 437 | // here the actual detection is performed 438 | protocol = ndpi_detection_process_packet(ndpi_info_mod, ipq_flow, (uint8_t *) iph, ipsize, time, src, dst); 439 | } 440 | else { 441 | printf("\n\nWARNING: fragmented ip packets are not supported and will be skipped \n\n"); 442 | sleep(2); 443 | return -1; 444 | } 445 | 446 | /* This is necessary to avoid different detection from ndpiex and ndpiReader; 447 | Inside nDPI there is a change from master and app protocol */ 448 | if(protocol.master_protocol != 0) 449 | protocol.app_protocol = protocol.master_protocol; 450 | 451 | protocol_counter[protocol.app_protocol]++; 452 | protocol_counter_bytes[protocol.app_protocol] += rawsize; 453 | 454 | if(flow != NULL) { 455 | flow->detected_protocol = protocol; 456 | /* printf("\n\nPROTO: %u %s\n\n",protocol.app_protocol, ndpi_get_proto_name(ndpi_info_mod, flow->detected_protocol.app_protocol)); */ 457 | onProtocol(flow->detected_protocol.app_protocol, ndpi_get_proto_name(ndpi_info_mod, flow->detected_protocol.app_protocol) ); 458 | } 459 | return 0; 460 | } 461 | 462 | 463 | /** 464 | Function to print the results obtained 465 | */ 466 | static void printResults(void) 467 | { 468 | u_int32_t i, j, protocol_flows = 0; 469 | char *proto_name; 470 | 471 | printf("\x1b[2K\n"); 472 | printf("pcap file contains\n"); 473 | printf("\tip packets: \x1b[33m%-13"PRIu64"\x1b[0m of %"PRIu64" packets total\n", ip_packet_count, raw_packet_count); 474 | printf("\tip bytes: \x1b[34m%-13"PRIu64"\x1b[0m\n", total_bytes); 475 | printf("\tunique ids: \x1b[35m%-13u\x1b[0m\n", osdpi_id_count); 476 | printf("\tunique flows: \x1b[36m%-13u\x1b[0m\n", osdpi_flow_count); 477 | 478 | printf("\n\ndetected protocols:\n"); 479 | 480 | for(i = 0; i <= NDPI_MAX_SUPPORTED_PROTOCOLS; i++) { 481 | 482 | // count flows for that protocol 483 | for (j = 0; j < osdpi_flow_count; j++) { 484 | if (osdpi_flows[j].detected_protocol.app_protocol == i) { 485 | protocol_flows++; 486 | } 487 | } 488 | 489 | // call the nDPI function to get the protocol name 490 | proto_name = ndpi_get_proto_name(ndpi_info_mod, i); 491 | 492 | // if a protocol is detected, print it 493 | if(protocol_counter[i] > 0) { 494 | printf("\t\x1b[31m%-20s\x1b[0m packets: \x1b[33m%-13"PRIu64"\x1b[0m bytes: \x1b[34m%-13"PRIu64"\x1b[0m " 495 | "flows: \x1b[36m%-13u\x1b[0m\n", 496 | proto_name, protocol_counter[i], protocol_counter_bytes[i], protocol_flows); 497 | } 498 | } 499 | printf("\n"); 500 | } 501 | 502 | 503 | /** 504 | function to dump memory results 505 | */ 506 | static void dumpResults(void) 507 | { 508 | u_int32_t i; 509 | char * proto_name; 510 | 511 | printf("\x1b[2K\n"); 512 | printf("pcap file contains\n"); 513 | printf("\tip packets: \x1b[33m%-13"PRIu64"\x1b[0m of %"PRIu64" packets total\n", ip_packet_count, raw_packet_count); 514 | printf("\tip bytes: \x1b[34m%-13"PRIu64"\x1b[0m\n", total_bytes); 515 | printf("\tunique ids: \x1b[35m%-13u\x1b[0m\n", osdpi_id_count); 516 | printf("\tunique flows: \x1b[36m%-13u\x1b[0m\n", osdpi_flow_count); 517 | 518 | printf("\n\ndetected protocols:\n"); 519 | for (i = 0; i <= NDPI_MAX_SUPPORTED_PROTOCOLS; i++) { 520 | u_int32_t protocol_flows = 0; 521 | u_int32_t j; 522 | 523 | // count flows for that protocol 524 | for (j = 0; j < osdpi_flow_count; j++) { 525 | if (osdpi_flows[j].detected_protocol.app_protocol == i) { 526 | protocol_flows++; 527 | } 528 | } 529 | 530 | // call the nDPI function to get the protocol name 531 | proto_name = ndpi_get_proto_name(ndpi_info_mod, i); 532 | 533 | if (protocol_counter[i] > 0) { 534 | printf("\t\x1b[31m%-20s\x1b[0m packets: \x1b[33m%-13"PRIu64"\x1b[0m bytes: \x1b[34m%-13"PRIu64"\x1b[0m " 535 | "flows: \x1b[36m%-13u\x1b[0m\n", 536 | proto_name, protocol_counter[i], protocol_counter_bytes[i], protocol_flows); 537 | } 538 | protocol_counter[i] = 0; 539 | protocol_counter_bytes[i] = 0; 540 | 541 | } 542 | printf("\n\n"); 543 | 544 | raw_packet_count = 0; 545 | ip_packet_count = 0; 546 | total_bytes = 0; 547 | size_flow_struct = 0; 548 | osdpi_flow_count = 0; 549 | } 550 | 551 | 552 | /** 553 | Callback function for each packet in the pcap file 554 | */ 555 | static void pcap_packet_callback(u_char * args, const struct pcap_pkthdr *header, const u_char * packet) 556 | { 557 | 558 | u_int64_t time; 559 | static u_int64_t lasttime = 0; 560 | u_int16_t type; 561 | 562 | // check datalink type 563 | 564 | 565 | /* --- Ethernet header --- */ 566 | const struct ethhdr *ethernet = (struct ethhdr *) packet; 567 | /* --- IP header --- */ 568 | struct iphdr *iph = (struct iphdr *) &packet[sizeof(struct ethhdr)]; 569 | 570 | // increment packet count stat 571 | raw_packet_count++; 572 | 573 | time = ((u_int64_t) header->ts.tv_sec) * TICK_RESOLUTION + 574 | header->ts.tv_usec / (1000000 / TICK_RESOLUTION); 575 | 576 | if(lasttime > time) { 577 | // printf("\nWARNING: timestamp bug in the pcap file (ts delta: %"PRIu64", repairing)\n", lasttime - time); 578 | time = lasttime; 579 | } 580 | lasttime = time; 581 | 582 | // IP type 583 | type = ethernet->h_proto; 584 | 585 | // just work on Ethernet packets that contain IP 586 | /*** TODO: extend datalink type support ***/ 587 | if (type == htons(ETH_P_IP) && 588 | header->caplen >= sizeof(struct ethhdr)) { 589 | 590 | if(header->caplen < header->len) { 591 | fprintf(stderr, "\n\nWARNING: packet capture size is smaller than packet size, DETECTION MIGHT NOT WORK CORRECTLY OR EVEN CRASH\n\n"); 592 | sleep(2); 593 | } 594 | 595 | /*** TODO: extend for IPv6 ***/ 596 | if(iph->version != 4) { 597 | printf("\n\nWARNING: only IPv4 packets are supported, all other packets will be discarded\n\n"); 598 | sleep(2); 599 | return; 600 | } 601 | // process the packet 602 | packet_processing(time, iph, header->len - sizeof(struct ethhdr), header->len); 603 | } 604 | } 605 | 606 | 607 | 608 | /*************************************************/ 609 | 610 | void init() { 611 | setupDetection(); 612 | gettimeofday(&begin, NULL); 613 | } 614 | 615 | void getResults() { 616 | dumpResults(); 617 | } 618 | 619 | void setDatalinkType(pcap_t *handle) { 620 | _pcap_datalink_type = pcap_datalink(handle); 621 | 622 | } 623 | 624 | void processPacket(const struct pcap_pkthdr *header, const u_char *packet) { 625 | pcap_packet_callback(NULL, header, packet); 626 | } 627 | 628 | void finish() { 629 | gettimeofday(&end, NULL); 630 | tot_usec = end.tv_sec*1000000 + end.tv_usec - (begin.tv_sec*1000000 + begin.tv_usec); 631 | terminateDetection(); 632 | } 633 | -------------------------------------------------------------------------------- /ndpiex.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ndpiex.c 3 | * Copyright (C) 2018 by QXIP BV 4 | * Copyright (C) 2018 by ntop 5 | * 6 | * Author: Lorenzo Mangani - Michele Campus 7 | * based on code of ndpiReader 8 | * 9 | * This is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * ndpiex is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public License 20 | * along with nDPI. If not, see . 21 | * 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define __STDC_FORMAT_MACROS 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include "ndpi_api.h" 37 | 38 | /** 39 | NOTE: in case we want to use nDPI data struct, 40 | link ./nDPI/example/ndpi_util.h in Makefile 41 | */ 42 | //#include "nDPI/example/ndpi_util.h" 43 | 44 | #define MAX_OSDPI_IDS 100000 45 | #define MAX_OSDPI_FLOWS 100000 46 | #define TICK_RESOLUTION 1000 47 | 48 | typedef void (*callback)(int, const uint8_t *packet); 49 | 50 | // prototypes used function 51 | void init(); 52 | void setDatalinkType(pcap_t *handle); 53 | void processPacket(const struct pcap_pkthdr *header, const uint8_t *packet); 54 | void finish(); 55 | void addProtocolHandler(callback handler); 56 | 57 | 58 | // cli options 59 | static char *_pcap_file = NULL; 60 | static char *results_path = NULL; 61 | 62 | // pcap 63 | static char _pcap_error_buffer[PCAP_ERRBUF_SIZE]; 64 | static pcap_t *_pcap_handle = NULL; 65 | static int _pcap_datalink_type; 66 | 67 | // detection 68 | static struct ndpi_detection_module_struct *ndpi_info_mod = NULL; 69 | 70 | #ifdef NDPI_ENABLE_DEBUG_MESSAGES 71 | static NDPI_PROTOCOL_BITMASK debug_messages_bitmask; 72 | #endif 73 | 74 | // results 75 | static u_int64_t raw_packet_count; 76 | static u_int64_t ip_packet_count; 77 | static u_int64_t total_bytes; 78 | static u_int64_t protocol_counter[NDPI_MAX_SUPPORTED_PROTOCOLS + 1]; 79 | static u_int64_t protocol_counter_bytes[NDPI_MAX_SUPPORTED_PROTOCOLS + 1]; 80 | 81 | 82 | /** id tracking **/ 83 | struct osdpi_id { 84 | u_int8_t ip[4]; 85 | struct ndpi_id_struct *ndpi_id; 86 | }; 87 | 88 | static u_int32_t size_id_struct; 89 | static struct osdpi_id *osdpi_ids; 90 | static u_int32_t osdpi_id_count; 91 | 92 | 93 | /** flow tracking **/ 94 | struct osdpi_flow { 95 | 96 | u_int32_t hashval; 97 | u_int32_t src_ip; 98 | u_int32_t dst_ip; 99 | u_int16_t src_port; 100 | u_int16_t dst_port; 101 | u_int8_t detection_completed, protocol, bidirectional, check_extra_packets; 102 | u_int16_t vlan_id; 103 | struct ndpi_flow_struct *ndpi_flow; 104 | char src_name[48], dst_name[48]; 105 | u_int8_t ip_version; 106 | u_int64_t last_seen; 107 | u_int64_t src2dst_bytes, dst2src_bytes; 108 | u_int32_t src2dst_packets, dst2src_packets; 109 | ndpi_protocol detected_protocol; 110 | char info[96]; 111 | char host_server_name[192]; 112 | char bittorent_hash[41]; 113 | void *src_id, *dst_id; 114 | }; 115 | 116 | static u_int32_t size_flow_struct; 117 | static struct osdpi_flow *osdpi_flows; 118 | static u_int32_t osdpi_flow_count; 119 | 120 | #ifdef NDPI_ENABLE_DEBUG_MESSAGES 121 | static int string_to_detection_bitmask(char *str, NDPI_PROTOCOL_BITMASK * dbm) 122 | { 123 | u_int32_t a; 124 | u_int32_t oldptr = 0; 125 | u_int32_t ptr = 0; 126 | NDPI_BITMASK_RESET(*dbm); 127 | 128 | printf("Protocol parameter given: %s\n", str); 129 | 130 | if (strcmp(str, "all") == 0) { 131 | printf("Protocol parameter all parsed\n"); 132 | NDPI_BITMASK_SET_ALL(*dbm); 133 | printf("Bitmask is: " NDPI_BITMASK_DEBUG_OUTPUT_BITMASK_STRING " \n", 134 | NDPI_BITMASK_DEBUG_OUTPUT_BITMASK_VALUE(*dbm)); 135 | return 0; 136 | } 137 | // parse bitmask 138 | while (1) { 139 | if (str[ptr] == 0 || str[ptr] == ' ') { 140 | printf("Protocol parameter: parsed: %.*s,\n", ptr - oldptr, &str[oldptr]); 141 | for (a = 1; a <= NDPI_MAX_SUPPORTED_PROTOCOLS; a++) { 142 | 143 | if (strlen(prot_short_str[a]) == (ptr - oldptr) && 144 | (memcmp(&str[oldptr], prot_short_str[a], ptr - oldptr) == 0)) { 145 | NDPI_ADD_PROTOCOL_TO_BITMASK(*dbm, a); 146 | printf("Protocol parameter detected as protocol %s\n", prot_long_str[a]); 147 | } 148 | } 149 | oldptr = ptr + 1; 150 | if (str[ptr] == 0) 151 | break; 152 | } 153 | ptr++; 154 | } 155 | return 0; 156 | } 157 | #endif 158 | 159 | 160 | /** 161 | * @brief Print help instructions 162 | */ 163 | static void help() { 164 | 165 | printf("Welcome to nDPIex\n\n"); 166 | 167 | printf("ndpiReader -f ] [-w ] \n\n" 168 | "Usage:\n" 169 | " -f | Specify a pcap file to read packets from or a\n" 170 | " -e | Write debug messages to file\n" 171 | " -j | Specify a file to write the content of packets in .json format\n"); 172 | 173 | } 174 | 175 | /** 176 | * @description: Option parser 177 | */ 178 | static void parseOptions(int argc, char **argv) 179 | { 180 | int opt; 181 | 182 | #ifdef NDPI_ENABLE_DEBUG_MESSAGES 183 | NDPI_BITMASK_SET_ALL(debug_messages_bitmask); 184 | #endif 185 | 186 | while ((opt = getopt(argc, argv, "f:e:j:w")) != EOF) { 187 | switch (opt) { 188 | 189 | case 'f': 190 | _pcap_file = optarg; 191 | break; 192 | 193 | case 'e': 194 | #ifdef NDPI_ENABLE_DEBUG_MESSAGES 195 | // set debug logging bitmask to all protocols 196 | if(string_to_detection_bitmask(optarg, &debug_messages_bitmask) != 0) { 197 | printf("ERROR option -e needs a valid list of protocols"); 198 | exit(-1); 199 | } 200 | printf("debug messages Bitmask is: " NDPI_BITMASK_DEBUG_OUTPUT_BITMASK_STRING "\n", 201 | NDPI_BITMASK_DEBUG_OUTPUT_BITMASK_VALUE(debug_messages_bitmask)); 202 | #else 203 | printf("ERROR: option -e : DEBUG MESSAGES DEACTIVATED\n"); 204 | exit(-1); 205 | #endif 206 | break; 207 | 208 | case 'j': 209 | #ifndef HAVE_JSON_C 210 | printf("WARNING: this code has been compiled without JSON-C: json export disabled\n"); 211 | #else 212 | _jsonFilePath = optarg; 213 | json_flag = 1; 214 | #endif 215 | break; 216 | 217 | default: 218 | help(); 219 | break; 220 | } 221 | } 222 | 223 | // check parameters 224 | if (_pcap_file == NULL || strcmp(_pcap_file, "") == 0) { 225 | printf("ERROR: no pcap file path provided; use option -f with the path to a valid pcap file\n"); 226 | exit(-1); 227 | } 228 | } 229 | 230 | /* static void debug_printf(u_int32_t protocol, void *id_struct, ndpi_log_level_t log_level, const char *format, ...) */ 231 | /* { */ 232 | /* #ifdef NDPI_ENABLE_DEBUG_MESSAGES */ 233 | /* if (NDPI_COMPARE_PROTOCOL_TO_BITMASK(debug_messages_bitmask, protocol) != 0) { */ 234 | /* const char *protocol_string; */ 235 | /* const char *file; */ 236 | /* const char *func; */ 237 | /* u_int32_t line; */ 238 | /* va_list ap; */ 239 | /* va_start(ap, format); */ 240 | 241 | /* protocol_string = prot_short_str[protocol.app_protocol]; */ 242 | 243 | /* ndpi_debug_get_last_log_function_line(ndpi_info_mod, &file, &func, &line); */ 244 | 245 | /* printf("\nDEBUG: %s:%s:%u Prot: %s, level: %u packet: %"PRIu64" :", file, func, line, protocol_string, */ 246 | /* log_level, raw_packet_count); */ 247 | /* vprintf(format, ap); */ 248 | /* va_end(ap); */ 249 | /* } */ 250 | /* #endif */ 251 | /* } */ 252 | 253 | /** 254 | function to return the ID of protocol 255 | */ 256 | static void *get_id(const u_int8_t * ip) 257 | { 258 | u_int32_t i; 259 | for (i = 0; i < osdpi_id_count; i++) { 260 | if (memcmp(osdpi_ids[i].ip, ip, sizeof(u_int8_t) * 4) == 0) { 261 | return osdpi_ids[i].ndpi_id; 262 | } 263 | } 264 | if (osdpi_id_count == MAX_OSDPI_IDS) { 265 | printf("ERROR: maximum unique id count (%u) has been exceeded\n", MAX_OSDPI_IDS); 266 | exit(-1); 267 | } 268 | else { 269 | struct ndpi_id_struct *ndpi_id; 270 | memcpy(osdpi_ids[osdpi_id_count].ip, ip, sizeof(u_int8_t) * 4); 271 | ndpi_id = osdpi_ids[osdpi_id_count].ndpi_id; 272 | 273 | osdpi_id_count += 1; 274 | return ndpi_id; 275 | } 276 | } 277 | 278 | 279 | /** 280 | function to return the flow of protocol 281 | */ 282 | static struct osdpi_flow *get_osdpi_flow(const struct iphdr *iph, u_int16_t ipsize) 283 | { 284 | u_int32_t i; 285 | u_int16_t l4_packet_len; 286 | struct tcphdr *tcph = NULL; 287 | struct udphdr *udph = NULL; 288 | 289 | u_int32_t src_ip; 290 | u_int32_t dst_ip; 291 | u_int16_t src_port; 292 | u_int16_t dst_port; 293 | 294 | if(ipsize < 20) 295 | return NULL; 296 | 297 | if((iph->ihl * 4) > ipsize || ipsize < ntohs(iph->tot_len) 298 | || (iph->frag_off & htons(0x1FFF)) != 0) 299 | return NULL; 300 | 301 | l4_packet_len = ntohs(iph->tot_len) - (iph->ihl * 4); 302 | 303 | if(iph->saddr < iph->daddr) { 304 | src_ip = iph->saddr; 305 | dst_ip = iph->daddr; 306 | } 307 | else { 308 | src_ip = iph->daddr; 309 | dst_ip = iph->saddr; 310 | } 311 | 312 | // TCP 313 | if(iph->protocol == 6 && l4_packet_len >= 20) { 314 | tcph = (struct tcphdr *) ((u_int8_t *) iph + iph->ihl * 4); 315 | if(iph->saddr < iph->daddr) { 316 | src_port = tcph->source; 317 | dst_port = tcph->dest; 318 | } 319 | else { 320 | src_port = tcph->dest; 321 | dst_port = tcph->source; 322 | } 323 | } 324 | // UDP 325 | else if(iph->protocol == 17 && l4_packet_len >= 8) { 326 | udph = (struct udphdr *) ((u_int8_t *) iph + iph->ihl * 4); 327 | if(iph->saddr < iph->daddr) { 328 | src_port = udph->source; 329 | dst_port = udph->dest; 330 | } 331 | else { 332 | src_port = udph->dest; 333 | dst_port = udph->source; 334 | } 335 | } 336 | else { 337 | // non tcp/udp protocols 338 | src_port = 0; 339 | dst_port = 0; 340 | } 341 | 342 | /*** CHECK this for (maybe must be changed) ***/ 343 | for(i = 0; i < osdpi_flow_count; i++) { 344 | if(osdpi_flows[i].protocol == iph->protocol && 345 | osdpi_flows[i].src_ip == src_ip && 346 | osdpi_flows[i].dst_ip == dst_ip && 347 | osdpi_flows[i].src_port == src_port && osdpi_flows[i].dst_port == dst_port) { 348 | return &osdpi_flows[i]; 349 | } 350 | } 351 | if(osdpi_flow_count == MAX_OSDPI_FLOWS) { 352 | printf("ERROR: maximum flow count (%u) has been exceeded\n", MAX_OSDPI_FLOWS); 353 | exit(-1); 354 | } 355 | else { 356 | 357 | // new flow allocated to be returned from the function 358 | struct osdpi_flow *new_flow; 359 | 360 | osdpi_flows[osdpi_flow_count].protocol = iph->protocol; 361 | osdpi_flows[osdpi_flow_count].src_ip = src_ip; 362 | osdpi_flows[osdpi_flow_count].dst_ip = dst_ip; 363 | osdpi_flows[osdpi_flow_count].src_port = src_port; 364 | osdpi_flows[osdpi_flow_count].dst_port = dst_port; 365 | 366 | // new flow 367 | new_flow = &osdpi_flows[osdpi_flow_count]; 368 | 369 | osdpi_flow_count += 1; 370 | return new_flow; 371 | } 372 | } 373 | 374 | 375 | /** 376 | Initialize all structures necessary for detection 377 | */ 378 | static void setupDetection(void) 379 | { 380 | u_int32_t i; 381 | NDPI_PROTOCOL_BITMASK all; 382 | 383 | // init global detection structure 384 | ndpi_info_mod = ndpi_init_detection_module(); 385 | if(ndpi_info_mod == NULL) { 386 | printf("ERROR: global structure initialization failed\n"); 387 | exit(-1); 388 | } 389 | 390 | // enable all protocols 391 | NDPI_BITMASK_SET_ALL(all); 392 | ndpi_set_protocol_detection_bitmask2(ndpi_info_mod, &all); 393 | 394 | // allocate memory for id and flow tracking 395 | size_id_struct = ndpi_detection_get_sizeof_ndpi_id_struct(); 396 | size_flow_struct = ndpi_detection_get_sizeof_ndpi_flow_struct(); 397 | 398 | // allocate memory for ids struct 399 | osdpi_ids = calloc(MAX_OSDPI_IDS, sizeof(struct osdpi_id)); 400 | if(osdpi_ids == NULL) { 401 | printf("ERROR: malloc for osdpi_ids failed\n"); 402 | exit(-1); 403 | } 404 | for(i = 0; i < MAX_OSDPI_IDS; i++) { 405 | /* memset(&osdpi_ids[i], 0, sizeof(struct osdpi_id)); */ 406 | osdpi_ids[i].ndpi_id = calloc(1, size_id_struct); 407 | if(osdpi_ids[i].ndpi_id == NULL) { 408 | printf("ERROR: malloc for ndpi_id struct inside osdpi_ids failed\n"); 409 | exit(-1); 410 | } 411 | } 412 | 413 | // allocate memory for flow struct 414 | osdpi_flows = calloc(MAX_OSDPI_FLOWS, sizeof(struct osdpi_flow)); 415 | if(osdpi_flows == NULL) { 416 | printf("ERROR: malloc for osdpi_flows failed\n"); 417 | exit(-1); 418 | } 419 | for (i = 0; i < MAX_OSDPI_FLOWS; i++) { 420 | /* memset(&osdpi_flows[i], 0, sizeof(struct osdpi_flow)); */ 421 | osdpi_flows[i].ndpi_flow = calloc(1, size_flow_struct); 422 | if(osdpi_flows[i].ndpi_flow == NULL) { 423 | printf("ERROR: malloc for ndpi_flow_struct failed\n"); 424 | exit(-1); 425 | } 426 | } 427 | 428 | // clear memory for results 429 | /* memset(protocol_counter, 0, (NDPI_MAX_SUPPORTED_PROTOCOLS + 1) * sizeof(u_int64_t)); */ 430 | /* memset(protocol_counter_bytes, 0, (NDPI_MAX_SUPPORTED_PROTOCOLS + 1) * sizeof(u_int64_t)); */ 431 | } 432 | 433 | /** 434 | Function to terminate detection 435 | */ 436 | static void terminateDetection(void) 437 | { 438 | u_int32_t i; 439 | 440 | // free ids 441 | for(i = 0; i < MAX_OSDPI_IDS; i++) { 442 | free(osdpi_ids[i].ndpi_id); 443 | } 444 | 445 | free(osdpi_ids); 446 | 447 | // free flows 448 | for (i = 0; i < MAX_OSDPI_FLOWS; i++) { 449 | free(osdpi_flows[i].ndpi_flow); 450 | } 451 | 452 | free(osdpi_flows); 453 | } 454 | 455 | 456 | /** 457 | Function to process the packet: 458 | determine the flow of a packet and try to decode it 459 | @return: 0 if success; != 0 fail 460 | 461 | @Note: ipsize = header->len - ip_offset ; rawsize = header->len 462 | */ 463 | static unsigned int packet_processing(const u_int64_t time, 464 | const struct iphdr *iph, 465 | uint16_t ipsize, 466 | uint16_t rawsize) 467 | { 468 | struct ndpi_id_struct *src = NULL; 469 | struct ndpi_id_struct *dst = NULL; 470 | struct osdpi_flow *flow = NULL; 471 | struct ndpi_flow_struct *ipq_flow = NULL; 472 | ndpi_protocol protocol; 473 | 474 | src = get_id((u_int8_t *) &iph->saddr); 475 | dst = get_id((u_int8_t *) &iph->daddr); 476 | 477 | flow = get_osdpi_flow(iph, ipsize); 478 | if(flow != NULL) { 479 | ipq_flow = flow->ndpi_flow; 480 | } 481 | 482 | // update stats for ip pkts and bytes 483 | ip_packet_count++; 484 | total_bytes += rawsize; 485 | 486 | // only handle unfragmented packets 487 | if((iph->frag_off & htons(0x1FFF)) == 0) { 488 | 489 | // here the actual detection is performed 490 | protocol = ndpi_detection_process_packet(ndpi_info_mod, ipq_flow, (uint8_t *) iph, ipsize, time, src, dst); 491 | } 492 | else { 493 | printf("\n\nWARNING: fragmented ip packets are not supported and will be skipped \n\n"); 494 | sleep(2); 495 | return -1; 496 | } 497 | 498 | /* This is necessary to avoid different detection from ndpiex and ndpiReader; 499 | Inside nDPI there is a change from master and app protocol */ 500 | if(protocol.master_protocol != 0) 501 | protocol.app_protocol = protocol.master_protocol; 502 | 503 | protocol_counter[protocol.app_protocol]++; 504 | protocol_counter_bytes[protocol.app_protocol] += rawsize; 505 | 506 | if(flow != NULL) { 507 | flow->detected_protocol = protocol; 508 | /// printf("\nproto: %u %s",protocol.app_protocol, ndpi_get_proto_name(ndpi_info_mod, flow->detected_protocol.app_protocol) ); 509 | } 510 | return 0; 511 | } 512 | 513 | 514 | /** 515 | Function to print the results obtained 516 | */ 517 | static void printResults(void) 518 | { 519 | u_int32_t i, j, protocol_flows = 0; 520 | char *proto_name; 521 | 522 | printf("\x1b[2K\n"); 523 | printf("pcap file contains\n"); 524 | printf("\tip packets: \x1b[33m%-13"PRIu64"\x1b[0m of %"PRIu64" packets total\n", ip_packet_count, raw_packet_count); 525 | printf("\tip bytes: \x1b[34m%-13"PRIu64"\x1b[0m\n", total_bytes); 526 | printf("\tunique ids: \x1b[35m%-13u\x1b[0m\n", osdpi_id_count); 527 | printf("\tunique flows: \x1b[36m%-13u\x1b[0m\n", osdpi_flow_count); 528 | 529 | printf("\n\ndetected protocols:\n"); 530 | 531 | for(i = 0; i <= NDPI_MAX_SUPPORTED_PROTOCOLS; i++) { 532 | 533 | // count flows for that protocol 534 | for (j = 0; j < osdpi_flow_count; j++) { 535 | if (osdpi_flows[j].detected_protocol.app_protocol == i) { 536 | protocol_flows++; 537 | } 538 | } 539 | 540 | // call the nDPI function to get the protocol name 541 | proto_name = ndpi_get_proto_name(ndpi_info_mod, i); 542 | 543 | // if a protocol is detected, print it 544 | if(protocol_counter[i] > 0) { 545 | printf("\t\x1b[31m%-20s\x1b[0m packets: \x1b[33m%-13"PRIu64"\x1b[0m bytes: \x1b[34m%-13"PRIu64"\x1b[0m " 546 | "flows: \x1b[36m%-13u\x1b[0m\n", 547 | proto_name, protocol_counter[i], protocol_counter_bytes[i], protocol_flows); 548 | } 549 | } 550 | printf("\n"); 551 | } 552 | 553 | /** 554 | function to open a pcap file 555 | */ 556 | static void openPcapFile(void) 557 | { 558 | /* trying to open a pcap file */ 559 | _pcap_handle = pcap_open_offline(_pcap_file, _pcap_error_buffer); 560 | 561 | if (_pcap_handle == NULL) { 562 | printf("ERROR: could not open pcap file: %s\n", _pcap_error_buffer); 563 | exit(-1); 564 | } 565 | } 566 | 567 | 568 | /** 569 | function to close a pcap file 570 | */ 571 | static void closePcapFile(void) 572 | { 573 | if (_pcap_handle != NULL) { 574 | pcap_close(_pcap_handle); 575 | } 576 | } 577 | 578 | 579 | /** 580 | Callback function for each packet in the pcap file 581 | */ 582 | static void pcap_packet_callback(u_char * args, const struct pcap_pkthdr *header, const u_char * packet) 583 | { 584 | 585 | u_int64_t time; 586 | static u_int64_t lasttime = 0; 587 | u_int16_t type; 588 | 589 | // check datalink type 590 | _pcap_datalink_type = pcap_datalink(_pcap_handle); 591 | 592 | 593 | /* --- Ethernet header --- */ 594 | const struct ethhdr *ethernet = (struct ethhdr *) packet; 595 | /* --- IP header --- */ 596 | struct iphdr *iph = (struct iphdr *) &packet[sizeof(struct ethhdr)]; 597 | 598 | // increment packet count stat 599 | raw_packet_count++; 600 | 601 | time = ((u_int64_t) header->ts.tv_sec) * TICK_RESOLUTION + 602 | header->ts.tv_usec / (1000000 / TICK_RESOLUTION); 603 | 604 | if(lasttime > time) { 605 | // printf("\nWARNING: timestamp bug in the pcap file (ts delta: %"PRIu64", repairing)\n", lasttime - time); 606 | time = lasttime; 607 | } 608 | lasttime = time; 609 | 610 | // IP type 611 | type = ethernet->h_proto; 612 | 613 | // just work on Ethernet packets that contain IP 614 | /*** TODO: extend datalink type support ***/ 615 | if (_pcap_datalink_type == DLT_EN10MB && 616 | type == htons(ETH_P_IP) && 617 | header->caplen >= sizeof(struct ethhdr)) { 618 | 619 | if(header->caplen < header->len) { 620 | fprintf(stderr, "\n\nWARNING: packet capture size is smaller than packet size, DETECTION MIGHT NOT WORK CORRECTLY OR EVEN CRASH\n\n"); 621 | sleep(2); 622 | } 623 | 624 | /*** TODO: extend for IPv6 ***/ 625 | if(iph->version != 4) { 626 | printf("\n\nWARNING: only IPv4 packets are supported, all other packets will be discarded\n\n"); 627 | sleep(2); 628 | return; 629 | } 630 | // process the packet 631 | packet_processing(time, iph, header->len - sizeof(struct ethhdr), header->len); 632 | } 633 | } 634 | 635 | 636 | /** 637 | Call pcap_loop() to process packets from a pcap_handle 638 | */ 639 | static void runPcapLoop(void) 640 | { 641 | if (_pcap_handle) { 642 | pcap_loop(_pcap_handle, -1, &pcap_packet_callback, NULL); 643 | } 644 | } 645 | 646 | 647 | /** 648 | @description: MAIN FUNCTION 649 | **/ 650 | int main(int argc, char **argv) 651 | { 652 | /* TODO: better to change some variables from global to local 653 | for more visibility */ 654 | 655 | parseOptions(argc, argv); 656 | 657 | setupDetection(); 658 | 659 | openPcapFile(); 660 | 661 | runPcapLoop(); 662 | 663 | closePcapFile(); 664 | 665 | printResults(); 666 | 667 | terminateDetection(); 668 | 669 | if(results_path) free(results_path); 670 | if(ndpi_info_mod) ndpi_exit_detection_module(ndpi_info_mod); 671 | 672 | return 0; 673 | } 674 | 675 | /* ***************************************************** */ 676 | 677 | callback protocolHandler; 678 | 679 | void addProtocolHandler(callback handler) { 680 | protocolHandler = handler; 681 | } 682 | 683 | void onProtocol(uint16_t id, const uint8_t *packet) { 684 | if (protocolHandler) { 685 | protocolHandler(id, packet); 686 | } 687 | } 688 | 689 | /*************************************************/ 690 | --------------------------------------------------------------------------------