Last active
March 28, 2024 15:09
-
-
Save duncansmart/ed19dab6780e3f4db88c048c25b2b7d3 to your computer and use it in GitHub Desktop.
Revisions
-
duncansmart revised this gist
Nov 26, 2020 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,6 +6,7 @@ Usage: * Run `node edgemax-dump-traffic-stats.js` To write to a file, pipe to a file like so: node edgemax-dump-traffic-stats.js > edgerouter-stats.csv Only tested on EdgeRouter X v2.0.8-hotfix.1 -
duncansmart revised this gist
Nov 26, 2020 . 1 changed file with 11 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,11 @@ Usage: * Download both files: `edgemax-dump-traffic-stats.js` and `package.json` to the same directory. * Run `npm install` in that dir to get all the dependencies. * Update the URL/username/password in `edgemax-dump-traffic-stats.js` for your environment * Run `node edgemax-dump-traffic-stats.js` To write to a file, pipe to a file like so: node edgemax-dump-traffic-stats.js > edgerouter-stats.csv Only tested on EdgeRouter X v2.0.8-hotfix.1 -
duncansmart revised this gist
Nov 26, 2020 . No changes.There are no files selected for viewing
-
duncansmart revised this gist
Nov 26, 2020 . No changes.There are no files selected for viewing
-
duncansmart revised this gist
Jul 8, 2020 . 1 changed file with 6 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,6 @@ { "dependencies": { "axios": "^0.19.2", "ws": "^7.3.0" } } -
duncansmart created this gist
Jul 8, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,192 @@ const url = require('url'); const axios = require('axios'); const qs = require('querystring') const WebSocket = require('ws'); // disable certificate verification process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; const baseUrl = 'https://192.168.1.1'; const username = 'ubnt'; const password = 'Pa55w0rd'; (async () => { //console.error('Start'); const sessionCookie = await logon(baseUrl, username, password); //console.error('Logged on'); //console.error({ sessionCookie }); await refreshHostNames(sessionCookie) const { protocol, hostname } = url.parse(baseUrl) const wsproto = protocol == 'https:' ? 'wss:' : 'ws:'; const hostnameIsIp = (/\d+\.\d+\.\d+\.\d+/).test(hostname) const ws = new WebSocket(`${wsproto}//${hostname}/ws/stats`, { servername: hostnameIsIp ? '' : undefined // suppress "[DEP0123] DeprecationWarning: Setting the TLS ServerName to an IP address is not permitted by RFC 6066. This will be ignored in a future version." }); ws.on('open', function open(x) { //console.error("Opening websocket"); const initMessage = JSON.stringify({ SUBSCRIBE: [{ name: "export" }], SESSION_ID: sessionCookie }); //console.error("Sending: ", initMessage); ws.send(initMessage.length + '\n' + initMessage, function (e) { if (e) console.error('init message error', e); }); //console.error('sent init message'); }); let messageLength = 0; let messageContent = ''; ws.on('message', async (data) => { if (messageLength == 0) { //console.log('... new msg'); const newlinepos = data.indexOf('\n'); messageLength = ~~data.slice(0, newlinepos); messageContent = data.slice(newlinepos + 1); } else { //... append messageContent += data; } if (messageContent.length < messageLength) { // incomplete, wait for next part return; } await handleMessage(messageContent); try { await refreshHostNames(sessionCookie); } catch (error) { console.error('ERROR: refreshHostNames ', error); } messageLength = 0; messageContent = ""; }); ws.on('error', (code, reason) => { console.error('WS ERROR', { code, reason }); }) ws.on('close', (code, reason) => { console.error('WS CLOSE', { code, reason }); }) })(); const _hostnames = {}; let _messageCount = 0 async function handleMessage(messageContent) { // header row if (_messageCount++ == 0) { console.log('time,categoryName,appName,hostname,ip,tx_bytes,tx_rate,rx_bytes,rx_rate'); } /* { "export": { "192.168.1.66": { "Google Static Content(SSL)|Network protocols": { "tx_bytes": "2355", "tx_rate": "0", "rx_bytes": "1500", "rx_rate": "0" }, "Youtube|Media streaming services": { "tx_bytes": "85079", "tx_rate": "0", "rx_bytes": "688716", "rx_rate": "0" }, }, ... */ //console.error(messageContent); const message = JSON.parse(messageContent); if (!message["export"]) return; const time = new Date().toISOString().substring(0, 19).replace('T', ' '); const exportItems = message['export']; for (const ip in exportItems) { const exportItem = exportItems[ip]; for (const appAndCategory in exportItem) { const stat = exportItem[appAndCategory] const hostname = _hostnames[ip] || ip; const [appName, categoryName] = appAndCategory.split(/\|/); console.log(`${time},${csvEncode(categoryName)},${csvEncode(appName)},${csvEncode(hostname)},${ip},${stat.tx_bytes},${stat.tx_rate},${stat.rx_bytes},${stat.rx_rate}`); } } } function csvEncode(str) { if (str == null) return ''; if (str.indexOf(",") > -1 || str.indexOf("\"") > -1) str = '"' + str.replace(/"/g, '""') + '"'; return str; } async function logon(baseUrl, username, password) { const form = { username: username, password: password }; const response = await axios.post(baseUrl, qs.stringify(form), { maxRedirects: 0, validateStatus: () => true // accept all certs }); if (!response.headers['set-cookie']){ throw new Error('Logon failed, please check username/password') } const cookies = response.headers['set-cookie'].reduce((obj, item) => { const [name, value] = item.split(/=/); obj[name] = value.split(/;/)[0]; return obj; }, {}); //console.log({cookies}); const sessionCookie = cookies['beaker.session.id']; return sessionCookie; } async function refreshHostNames(sessionCookie) { // only run every minute if ((new Date() - (refreshHostNames.lastUpdated || 0)) / 1000 < 60) return; //console.error('Refresh host names') const response = await axios.get(baseUrl.replace(/\/$/, '') + "/api/edge/data.json?data=dhcp_leases", { headers: { "Cookie": `beaker.session.id=${sessionCookie}` } }); //console.log("refreshHostNames:", response.data.output['dhcp-server-leases'].LAN); const leases = response.data.output['dhcp-server-leases'].LAN; for (const ip in leases) { _hostnames[ip] = leases[ip]['client-hostname'].replace(/\?/g, ''); } refreshHostNames.lastUpdated = new Date(); }