1050 lines
39 KiB
JavaScript
1050 lines
39 KiB
JavaScript
'use strict';
|
|
// @ts-check
|
|
// ==================================================================================
|
|
// network.js
|
|
// ----------------------------------------------------------------------------------
|
|
// Description: System Information - library
|
|
// for Node.js
|
|
// Copyright: (c) 2014 - 2019
|
|
// Author: Sebastian Hildebrandt
|
|
// ----------------------------------------------------------------------------------
|
|
// License: MIT
|
|
// ==================================================================================
|
|
// 9. Network
|
|
// ----------------------------------------------------------------------------------
|
|
|
|
const os = require('os');
|
|
const exec = require('child_process').exec;
|
|
const execSync = require('child_process').execSync;
|
|
const fs = require('fs');
|
|
const util = require('./util');
|
|
|
|
let _platform = process.platform;
|
|
|
|
const _linux = (_platform === 'linux');
|
|
const _darwin = (_platform === 'darwin');
|
|
const _windows = (_platform === 'win32');
|
|
const _freebsd = (_platform === 'freebsd');
|
|
const _openbsd = (_platform === 'openbsd');
|
|
const _netbsd = (_platform === 'netbsd');
|
|
const _sunos = (_platform === 'sunos');
|
|
|
|
let _network = {};
|
|
let _default_iface = '';
|
|
let _ifaces = [];
|
|
let _networkInterfaces = [];
|
|
let _mac = {};
|
|
let pathToIp;
|
|
|
|
function getDefaultNetworkInterface() {
|
|
|
|
let ifaces = os.networkInterfaces();
|
|
let ifacename = '';
|
|
let ifacenameFirst = '';
|
|
|
|
let scopeid = 9999;
|
|
|
|
// fallback - "first" external interface (sorted by scopeid)
|
|
for (let dev in ifaces) {
|
|
if (ifaces.hasOwnProperty(dev)) {
|
|
ifaces[dev].forEach(function (details) {
|
|
if (details && details.internal === false) {
|
|
ifacenameFirst = ifacenameFirst || dev; // fallback if no scopeid
|
|
if (details.scopeid && details.scopeid < scopeid) {
|
|
ifacename = dev;
|
|
scopeid = details.scopeid;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
ifacename = ifacename || ifacenameFirst || '';
|
|
|
|
if (_windows) {
|
|
// https://www.inetdaemon.com/tutorials/internet/ip/routing/default_route.shtml
|
|
const cmd = 'netstat -r';
|
|
const result = execSync(cmd);
|
|
const lines = result.toString().split(os.EOL);
|
|
let defaultIp = '';
|
|
lines.forEach(line => {
|
|
line = line.replace(/\s+/g, ' ').trim();
|
|
if (line.indexOf('0.0.0.0 0.0.0.0') > -1 && !(/[a-zA-Z]/.test(line))) {
|
|
const parts = line.split(' ');
|
|
if (parts.length >= 5) {
|
|
defaultIp = parts[parts.length - 2];
|
|
}
|
|
}
|
|
});
|
|
if (defaultIp) {
|
|
for (let dev in ifaces) {
|
|
if (ifaces.hasOwnProperty(dev)) {
|
|
ifaces[dev].forEach(function (details) {
|
|
if (details && details.address && details.address === defaultIp) {
|
|
ifacename = dev;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (_linux || _darwin || _freebsd || _openbsd || _netbsd || _sunos) {
|
|
let cmd = '';
|
|
if (_linux) cmd = 'ip route 2> /dev/null | grep default | awk \'{print $5}\'';
|
|
if (_darwin) cmd = 'route get 0.0.0.0 2>/dev/null | grep interface: | awk \'{print $2}\'';
|
|
if (_freebsd || _openbsd || _netbsd || _sunos) cmd = 'route get 0.0.0.0 | grep interface:';
|
|
let result = execSync(cmd);
|
|
ifacename = result.toString().split('\n')[0];
|
|
if (ifacename.indexOf(':') > -1) {
|
|
ifacename = ifacename.split(':')[1].trim();
|
|
}
|
|
}
|
|
|
|
if (ifacename) _default_iface = ifacename;
|
|
return _default_iface;
|
|
}
|
|
|
|
exports.getDefaultNetworkInterface = getDefaultNetworkInterface;
|
|
|
|
function getMacAddresses() {
|
|
let iface = '';
|
|
let mac = '';
|
|
let result = {};
|
|
if (_linux || _freebsd || _openbsd || _netbsd) {
|
|
if (typeof pathToIp === 'undefined') {
|
|
try {
|
|
const lines = execSync('which ip').toString().split('\n');
|
|
if (lines.length && lines[0].indexOf(':') === -1 && lines[0].indexOf('/') === 0) {
|
|
pathToIp = lines[0];
|
|
} else {
|
|
pathToIp = '';
|
|
}
|
|
} catch (e) {
|
|
pathToIp = '';
|
|
}
|
|
}
|
|
const cmd = 'export LC_ALL=C; ' + ((pathToIp) ? pathToIp + ' link show up' : '/sbin/ifconfig') + '; unset LC_ALL';
|
|
let res = execSync(cmd);
|
|
const lines = res.toString().split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (lines[i] && lines[i][0] !== ' ') {
|
|
if (pathToIp) {
|
|
let nextline = lines[i + 1].trim().split(' ');
|
|
if (nextline[0] === 'link/ether') {
|
|
iface = lines[i].split(' ')[1];
|
|
iface = iface.slice(0, iface.length - 1);
|
|
mac = nextline[1];
|
|
}
|
|
} else {
|
|
iface = lines[i].split(' ')[0];
|
|
mac = lines[i].split('HWaddr ')[1];
|
|
}
|
|
|
|
if (iface && mac) {
|
|
result[iface] = mac.trim();
|
|
iface = '';
|
|
mac = '';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (_darwin) {
|
|
const cmd = '/sbin/ifconfig';
|
|
let res = execSync(cmd);
|
|
const lines = res.toString().split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (lines[i] && lines[i][0] !== '\t' && lines[i].indexOf(':') > 0) {
|
|
iface = lines[i].split(':')[0];
|
|
} else if (lines[i].indexOf('\tether ') === 0) {
|
|
mac = lines[i].split('\tether ')[1];
|
|
if (iface && mac) {
|
|
result[iface] = mac.trim();
|
|
iface = '';
|
|
mac = '';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function networkInterfaceDefault(callback) {
|
|
|
|
return new Promise((resolve) => {
|
|
process.nextTick(() => {
|
|
let result = getDefaultNetworkInterface();
|
|
if (callback) { callback(result); }
|
|
resolve(result);
|
|
});
|
|
});
|
|
}
|
|
|
|
exports.networkInterfaceDefault = networkInterfaceDefault;
|
|
|
|
// --------------------------
|
|
// NET - interfaces
|
|
|
|
function parseLinesWindowsNics(sections, nconfigsections) {
|
|
let nics = [];
|
|
for (let i in sections) {
|
|
if (sections.hasOwnProperty(i)) {
|
|
|
|
if (sections[i].trim() !== '') {
|
|
|
|
let lines = sections[i].trim().split('\r\n');
|
|
let linesNicConfig = nconfigsections[i].trim().split('\r\n');
|
|
let netEnabled = util.getValue(lines, 'NetEnabled', '=');
|
|
let dnsSuffixes = util.getValue(linesNicConfig, 'DNSDomainSuffixSearchOrder', '=').replace(/{|}/g, '');
|
|
if(dnsSuffixes !== '') {
|
|
dnsSuffixes = dnsSuffixes.replace(/;/g, ',');
|
|
dnsSuffixes = dnsSuffixes.replace(/"/g, '');
|
|
dnsSuffixes = dnsSuffixes.split(",");
|
|
}
|
|
|
|
if (netEnabled) {
|
|
const speed = parseInt(util.getValue(lines, 'speed', '=').trim(), 10) / 1000000;
|
|
nics.push({
|
|
mac: util.getValue(lines, 'MACAddress', '=').toLowerCase(),
|
|
dhcp: util.getValue(linesNicConfig, 'dhcpEnabled', '=').toLowerCase(),
|
|
name: util.getValue(lines, 'Name', '=').replace(/\]/g, ')').replace(/\[/g, '('),
|
|
dnsSuffixes: dnsSuffixes === '' ? [] : dnsSuffixes,
|
|
netEnabled: netEnabled === 'TRUE',
|
|
speed: isNaN(speed) ? -1 : speed,
|
|
operstate: util.getValue(lines, 'NetConnectionStatus', '=') === '2' ? 'up' : 'down',
|
|
type: util.getValue(lines, 'AdapterTypeID', '=') === '9' ? 'wireless' : 'wired'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nics;
|
|
}
|
|
|
|
function getWindowsNics() {
|
|
const cmd = util.getWmic() + ' nic get MACAddress, name, NetEnabled, Speed, NetConnectionStatus, AdapterTypeId /value';
|
|
const cmdnicconfig = util.getWmic() + ' nicconfig get dhcpEnabled, DNSDomainSuffixSearchOrder /value';
|
|
try {
|
|
const nsections = execSync(cmd, util.execOptsWin).split(/\n\s*\n/);
|
|
const nconfigsections = execSync(cmdnicconfig, util.execOptsWin).split(/\n\s*\n/);
|
|
return (parseLinesWindowsNics(nsections, nconfigsections));
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function splitSectionsNics(lines) {
|
|
const result = [];
|
|
let section = [];
|
|
lines.forEach(function (line) {
|
|
if (!line.startsWith('\t')) {
|
|
if (section.length) {
|
|
result.push(section);
|
|
section = [];
|
|
}
|
|
}
|
|
section.push(line);
|
|
});
|
|
if (section.length) {
|
|
result.push(section);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function parseLinesDarwinNics(sections) {
|
|
let nics = [];
|
|
sections.forEach(section => {
|
|
let nic = {
|
|
iface: '',
|
|
mtu: -1,
|
|
mac: '',
|
|
ip6: '',
|
|
ip4: '',
|
|
speed: -1,
|
|
type: '',
|
|
operstate: '',
|
|
duplex: '',
|
|
internal: false
|
|
};
|
|
const first = section[0];
|
|
nic.iface = first.split(':')[0].trim();
|
|
let parts = first.split('> mtu');
|
|
nic.mtu = parts.length > 1 ? parseInt(parts[1], 10) : -1;
|
|
if (isNaN(nic.mtu)) {
|
|
nic.mtu = -1;
|
|
}
|
|
nic.internal = parts[0].indexOf('LOOPBACK') > -1;
|
|
section.forEach(line => {
|
|
if (line.trim().startsWith('ether ')) {
|
|
nic.mac = line.split('ether ')[1].toLowerCase().trim();
|
|
}
|
|
if (line.trim().startsWith('inet6 ') && !nic.ip6) {
|
|
nic.ip6 = line.split('inet6 ')[1].toLowerCase().split('%')[0].split(' ')[0];
|
|
}
|
|
if (line.trim().startsWith('inet ') && !nic.ip4) {
|
|
nic.ip4 = line.split('inet ')[1].toLowerCase().split(' ')[0];
|
|
}
|
|
});
|
|
let speed = util.getValue(section, 'link rate');
|
|
nic.speed = speed ? parseFloat(speed) : -1;
|
|
if (nic.speed === -1) {
|
|
speed = util.getValue(section, 'uplink rate');
|
|
nic.speed = speed ? parseFloat(speed) : -1;
|
|
if (nic.speed > -1 && speed.toLowerCase().indexOf('gbps') >= 0) {
|
|
nic.speed = nic.speed * 1000;
|
|
}
|
|
} else {
|
|
if (speed.toLowerCase().indexOf('gbps') >= 0) {
|
|
nic.speed = nic.speed * 1000;
|
|
}
|
|
}
|
|
nic.type = util.getValue(section, 'type').toLowerCase().indexOf('wi-fi') > -1 ? 'wireless' : 'wired';
|
|
nic.operstate = util.getValue(section, 'status').toLowerCase().indexOf('active') > -1 ? 'up' : 'down';
|
|
nic.duplex = util.getValue(section, 'media').toLowerCase().indexOf('half-duplex') > -1 ? 'half' : 'full';
|
|
if (nic.ip6 || nic.ip4 || nic.mac) {
|
|
nics.push(nic);
|
|
}
|
|
});
|
|
return nics;
|
|
}
|
|
|
|
function getDarwinNics() {
|
|
const cmd = 'ifconfig -v';
|
|
try {
|
|
const lines = execSync(cmd, util.execOptsWin).toString().split('\n');
|
|
const nsections = splitSectionsNics(lines);
|
|
return (parseLinesDarwinNics(nsections));
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
|
|
function getNicDHCPstatus(interfaceName) {
|
|
const cmd = `nmcli con sh "${interfaceName}" | grep ipv4.method`;
|
|
console.log(cmd);
|
|
try {
|
|
let dhcpStatus = "";
|
|
const lines = execSync(cmd, util.execOptsWin).toString().split('\n');
|
|
const nsections = splitSectionsNics(lines);
|
|
console.log(nsections);
|
|
switch (key) {
|
|
case "auto":
|
|
dhcpStatus = "true";
|
|
break;
|
|
|
|
default:
|
|
dhcpStatus = "false";
|
|
break;
|
|
}
|
|
return dhcpStatus;
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
|
|
function testVirtualNic(iface, ifaceName, mac) {
|
|
const virtualMacs = ['00:00:00:00:00:00', '00:03:FF', '00:05:69', '00:0C:29', '00:0F:4B', '00:0F:4B', '00:13:07', '00:13:BE', '00:15:5d', '00:16:3E', '00:1C:42', '00:21:F6', '00:21:F6', '00:24:0B', '00:24:0B', '00:50:56', '00:A0:B1', '00:E0:C8', '08:00:27', '0A:00:27', '18:92:2C', '16:DF:49', '3C:F3:92', '54:52:00', 'FC:15:97'];
|
|
if (mac) {
|
|
return virtualMacs.filter(item => { return mac.toUpperCase().toUpperCase().startsWith(item.substr(0, mac.length)); }).length > 0 ||
|
|
iface.toLowerCase().indexOf(' virtual ') > -1 ||
|
|
ifaceName.toLowerCase().indexOf(' virtual ') > -1 ||
|
|
iface.toLowerCase().indexOf('vethernet ') > -1 ||
|
|
ifaceName.toLowerCase().indexOf('vethernet ') > -1 ||
|
|
iface.toLowerCase().startsWith('veth') ||
|
|
ifaceName.toLowerCase().startsWith('veth') ||
|
|
iface.toLowerCase().startsWith('vboxnet') ||
|
|
ifaceName.toLowerCase().startsWith('vboxnet');
|
|
} else return false;
|
|
}
|
|
|
|
function networkInterfaces(callback) {
|
|
|
|
return new Promise((resolve) => {
|
|
process.nextTick(() => {
|
|
let ifaces = os.networkInterfaces();
|
|
let result = [];
|
|
let nics = [];
|
|
// seperate handling in OSX
|
|
if (_darwin || _freebsd || _openbsd || _netbsd) {
|
|
nics = getDarwinNics();
|
|
|
|
nics.forEach(nic => {
|
|
|
|
dhcpStatus = getNicDHCPstatus(nic.iface);
|
|
|
|
result.push({
|
|
iface: nic.iface,
|
|
ifaceName: nic.iface,
|
|
ip4: nic.ip4,
|
|
ip6: nic.ip6,
|
|
mac: nic.mac,
|
|
internal: nic.internal,
|
|
virtual: nic.internal ? false : testVirtualNic(nic.iface, nic.iface, nic.mac),
|
|
operstate: nic.operstate,
|
|
type: nic.type,
|
|
duplex: nic.duplex,
|
|
dhcpStatus: dhcpStatus,
|
|
mtu: nic.mtu,
|
|
speed: nic.speed,
|
|
carrierChanges: 0
|
|
});
|
|
});
|
|
if (callback) { callback(result); }
|
|
resolve(result);
|
|
} else {
|
|
if (JSON.stringify(ifaces) === JSON.stringify(_ifaces)) {
|
|
// no changes - just return object
|
|
result = _networkInterfaces;
|
|
|
|
if (callback) { callback(result); }
|
|
resolve(result);
|
|
} else {
|
|
_ifaces = ifaces;
|
|
if (_windows) {
|
|
nics = getWindowsNics();
|
|
}
|
|
for (let dev in ifaces) {
|
|
let ip4 = '';
|
|
let ip6 = '';
|
|
let mac = '';
|
|
let duplex = '';
|
|
let mtu = '';
|
|
let speed = -1;
|
|
let carrierChanges = 0;
|
|
let operstate = 'down';
|
|
let dhcp = false;
|
|
let dnsSuffixes = [];
|
|
let type = '';
|
|
|
|
if (ifaces.hasOwnProperty(dev)) {
|
|
let ifaceName = dev;
|
|
ifaces[dev].forEach(function (details) {
|
|
if (details.family === 'IPv4') {
|
|
ip4 = details.address;
|
|
}
|
|
if (details.family === 'IPv6') {
|
|
if (!ip6 || ip6.match(/^fe80::/i)) {
|
|
ip6 = details.address;
|
|
}
|
|
}
|
|
mac = details.mac;
|
|
// fallback due to https://github.com/nodejs/node/issues/13581 (node 8.1 - node 8.2)
|
|
if (mac.indexOf('00:00:0') > -1 && (_linux || _darwin)) {
|
|
if (Object.keys(_mac).length === 0) {
|
|
_mac = getMacAddresses();
|
|
}
|
|
mac = _mac[dev] || '';
|
|
}
|
|
});
|
|
if (_linux) {
|
|
let iface = dev.split(':')[0].trim().toLowerCase();
|
|
const cmd = `echo -n "addr_assign_type: "; cat /sys/class/net/${iface}/addr_assign_type 2>/dev/null; echo;
|
|
echo -n "address: "; cat /sys/class/net/${iface}/address 2>/dev/null; echo;
|
|
echo -n "addr_len: "; cat /sys/class/net/${iface}/addr_len 2>/dev/null; echo;
|
|
echo -n "broadcast: "; cat /sys/class/net/${iface}/broadcast 2>/dev/null; echo;
|
|
echo -n "carrier: "; cat /sys/class/net/${iface}/carrier 2>/dev/null; echo;
|
|
echo -n "carrier_changes: "; cat /sys/class/net/${iface}/carrier_changes 2>/dev/null; echo;
|
|
echo -n "dev_id: "; cat /sys/class/net/${iface}/dev_id 2>/dev/null; echo;
|
|
echo -n "dev_port: "; cat /sys/class/net/${iface}/dev_port 2>/dev/null; echo;
|
|
echo -n "dormant: "; cat /sys/class/net/${iface}/dormant 2>/dev/null; echo;
|
|
echo -n "duplex: "; cat /sys/class/net/${iface}/duplex 2>/dev/null; echo;
|
|
echo -n "flags: "; cat /sys/class/net/${iface}/flags 2>/dev/null; echo;
|
|
echo -n "gro_flush_timeout: "; cat /sys/class/net/${iface}/gro_flush_timeout 2>/dev/null; echo;
|
|
echo -n "ifalias: "; cat /sys/class/net/${iface}/ifalias 2>/dev/null; echo;
|
|
echo -n "ifindex: "; cat /sys/class/net/${iface}/ifindex 2>/dev/null; echo;
|
|
echo -n "iflink: "; cat /sys/class/net/${iface}/iflink 2>/dev/null; echo;
|
|
echo -n "link_mode: "; cat /sys/class/net/${iface}/link_mode 2>/dev/null; echo;
|
|
echo -n "mtu: "; cat /sys/class/net/${iface}/mtu 2>/dev/null; echo;
|
|
echo -n "netdev_group: "; cat /sys/class/net/${iface}/netdev_group 2>/dev/null; echo;
|
|
echo -n "operstate: "; cat /sys/class/net/${iface}/operstate 2>/dev/null; echo;
|
|
echo -n "proto_down: "; cat /sys/class/net/${iface}/proto_down 2>/dev/null; echo;
|
|
echo -n "speed: "; cat /sys/class/net/${iface}/speed 2>/dev/null; echo;
|
|
echo -n "tx_queue_len: "; cat /sys/class/net/${iface}/tx_queue_len 2>/dev/null; echo;
|
|
echo -n "type: "; cat /sys/class/net/${iface}/type 2>/dev/null; echo;
|
|
echo -n "wireless: "; cat /proc/net/wireless 2>/dev/null \| grep ${iface}; echo
|
|
echo -n "wirelessspeed: "; iw dev ${iface} link 2>&1 \| grep bitrate; echo;`;
|
|
let lines = [];
|
|
try {
|
|
lines = execSync(cmd).toString().split('\n');
|
|
|
|
} catch (e) {
|
|
util.noop();
|
|
}
|
|
duplex = util.getValue(lines, 'duplex');
|
|
duplex = duplex.startsWith('cat') ? '' : duplex;
|
|
mtu = parseInt(util.getValue(lines, 'mtu'), 10);
|
|
let myspeed = parseInt(util.getValue(lines, 'speed'), 10);
|
|
speed = isNaN(myspeed) ? -1 : myspeed;
|
|
let wirelessspeed = util.getValue(lines, 'wirelessspeed').split('tx bitrate: ');
|
|
if (speed === -1 && wirelessspeed.length === 2) {
|
|
myspeed = parseFloat(wirelessspeed[1]);
|
|
speed = isNaN(myspeed) ? -1 : myspeed;
|
|
}
|
|
carrierChanges = parseInt(util.getValue(lines, 'carrier_changes'), 10);
|
|
operstate = util.getValue(lines, 'operstate');
|
|
type = operstate === 'up' ? (util.getValue(lines, 'wireless').trim() ? 'wireless' : 'wired') : 'unknown';
|
|
if (iface === 'lo' || iface.startsWith('bond')) { type = 'virtual'; }
|
|
}
|
|
if (_windows) {
|
|
nics.forEach(detail => {
|
|
if (detail.mac === mac) {
|
|
ifaceName = detail.name;
|
|
dhcp = detail.dhcp;
|
|
dnsSuffixes = detail.dnsSuffixes;
|
|
operstate = detail.operstate;
|
|
speed = detail.speed;
|
|
type = detail.type;
|
|
}
|
|
});
|
|
if (dev.toLowerCase().indexOf('wlan') >= 0 || ifaceName.toLowerCase().indexOf('wlan') >= 0 || ifaceName.toLowerCase().indexOf('wireless') >= 0) {
|
|
type = 'wireless';
|
|
}
|
|
}
|
|
let internal = (ifaces[dev] && ifaces[dev][0]) ? ifaces[dev][0].internal : null;
|
|
const virtual = internal ? false : testVirtualNic(dev, ifaceName, mac);
|
|
result.push({
|
|
iface: dev,
|
|
ifaceName,
|
|
ip4,
|
|
ip6,
|
|
mac,
|
|
internal,
|
|
virtual,
|
|
operstate,
|
|
type,
|
|
duplex,
|
|
mtu,
|
|
speed,
|
|
dhcp,
|
|
dnsSuffixes,
|
|
carrierChanges,
|
|
});
|
|
}
|
|
}
|
|
_networkInterfaces = result;
|
|
if (callback) { callback(result); }
|
|
resolve(result);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
exports.networkInterfaces = networkInterfaces;
|
|
|
|
// --------------------------
|
|
// NET - Speed
|
|
|
|
function calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors) {
|
|
let result = {
|
|
iface,
|
|
operstate,
|
|
rx_bytes,
|
|
rx_dropped,
|
|
rx_errors,
|
|
tx_bytes,
|
|
tx_dropped,
|
|
tx_errors,
|
|
rx_sec: -1,
|
|
tx_sec: -1,
|
|
ms: 0
|
|
};
|
|
|
|
if (_network[iface] && _network[iface].ms) {
|
|
result.ms = Date.now() - _network[iface].ms;
|
|
result.rx_sec = (rx_bytes - _network[iface].rx_bytes) >= 0 ? (rx_bytes - _network[iface].rx_bytes) / (result.ms / 1000) : 0;
|
|
result.tx_sec = (tx_bytes - _network[iface].tx_bytes) >= 0 ? (tx_bytes - _network[iface].tx_bytes) / (result.ms / 1000) : 0;
|
|
_network[iface].rx_bytes = rx_bytes;
|
|
_network[iface].tx_bytes = tx_bytes;
|
|
_network[iface].rx_sec = result.rx_sec;
|
|
_network[iface].tx_sec = result.tx_sec;
|
|
_network[iface].ms = Date.now();
|
|
_network[iface].last_ms = result.ms;
|
|
_network[iface].operstate = operstate;
|
|
} else {
|
|
if (!_network[iface]) _network[iface] = {};
|
|
_network[iface].rx_bytes = rx_bytes;
|
|
_network[iface].tx_bytes = tx_bytes;
|
|
_network[iface].rx_sec = -1;
|
|
_network[iface].tx_sec = -1;
|
|
_network[iface].ms = Date.now();
|
|
_network[iface].last_ms = 0;
|
|
_network[iface].operstate = operstate;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function networkStats(ifaces, callback) {
|
|
|
|
let ifacesArray = [];
|
|
// fallback - if only callback is given
|
|
if (util.isFunction(ifaces) && !callback) {
|
|
callback = ifaces;
|
|
ifacesArray = [getDefaultNetworkInterface()];
|
|
} else {
|
|
ifaces = ifaces || getDefaultNetworkInterface();
|
|
ifaces = ifaces.trim().toLowerCase().replace(/,+/g, '|');
|
|
ifacesArray = ifaces.split('|');
|
|
}
|
|
|
|
return new Promise((resolve) => {
|
|
process.nextTick(() => {
|
|
|
|
const result = [];
|
|
|
|
const workload = [];
|
|
if (ifacesArray.length && ifacesArray[0].trim() === '*') {
|
|
ifacesArray = [];
|
|
networkInterfaces().then(allIFaces => {
|
|
for (let iface of allIFaces) {
|
|
ifacesArray.push(iface.iface);
|
|
}
|
|
networkStats(ifacesArray.join(',')).then(result => {
|
|
if (callback) { callback(result); }
|
|
resolve(result);
|
|
});
|
|
});
|
|
} else {
|
|
for (let iface of ifacesArray) {
|
|
workload.push(networkStatsSingle(iface.trim()));
|
|
}
|
|
if (workload.length) {
|
|
Promise.all(
|
|
workload
|
|
).then(data => {
|
|
if (callback) { callback(data); }
|
|
resolve(data);
|
|
});
|
|
} else {
|
|
if (callback) { callback(result); }
|
|
resolve(result);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function networkStatsSingle(iface) {
|
|
|
|
function parseLinesWindowsPerfData(sections) {
|
|
let perfData = [];
|
|
for (let i in sections) {
|
|
if (sections.hasOwnProperty(i)) {
|
|
if (sections[i].trim() !== '') {
|
|
|
|
let lines = sections[i].trim().split('\r\n');
|
|
perfData.push({
|
|
name: util.getValue(lines, 'Name', '=').replace(/[()\[\] ]+/g, '').toLowerCase(),
|
|
rx_bytes: parseInt(util.getValue(lines, 'BytesReceivedPersec', '='), 10),
|
|
rx_errors: parseInt(util.getValue(lines, 'PacketsReceivedErrors', '='), 10),
|
|
rx_dropped: parseInt(util.getValue(lines, 'PacketsReceivedDiscarded', '='), 10),
|
|
tx_bytes: parseInt(util.getValue(lines, 'BytesSentPersec', '='), 10),
|
|
tx_errors: parseInt(util.getValue(lines, 'PacketsOutboundErrors', '='), 10),
|
|
tx_dropped: parseInt(util.getValue(lines, 'PacketsOutboundDiscarded', '='), 10)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return perfData;
|
|
}
|
|
|
|
return new Promise((resolve) => {
|
|
process.nextTick(() => {
|
|
|
|
let result = {
|
|
iface: iface,
|
|
operstate: 'unknown',
|
|
rx_bytes: 0,
|
|
rx_dropped: 0,
|
|
rx_errors: 0,
|
|
tx_bytes: 0,
|
|
tx_dropped: 0,
|
|
tx_errors: 0,
|
|
rx_sec: -1,
|
|
tx_sec: -1,
|
|
ms: 0
|
|
};
|
|
|
|
let operstate = 'unknown';
|
|
let rx_bytes = 0;
|
|
let tx_bytes = 0;
|
|
let rx_dropped = 0;
|
|
let rx_errors = 0;
|
|
let tx_dropped = 0;
|
|
let tx_errors = 0;
|
|
|
|
let cmd, lines, stats;
|
|
if (!_network[iface] || (_network[iface] && !_network[iface].ms) || (_network[iface] && _network[iface].ms && Date.now() - _network[iface].ms >= 500)) {
|
|
if (_linux) {
|
|
if (fs.existsSync('/sys/class/net/' + iface)) {
|
|
cmd =
|
|
'cat /sys/class/net/' + iface + '/operstate; ' +
|
|
'cat /sys/class/net/' + iface + '/statistics/rx_bytes; ' +
|
|
'cat /sys/class/net/' + iface + '/statistics/tx_bytes; ' +
|
|
'cat /sys/class/net/' + iface + '/statistics/rx_dropped; ' +
|
|
'cat /sys/class/net/' + iface + '/statistics/rx_errors; ' +
|
|
'cat /sys/class/net/' + iface + '/statistics/rx_dropped; ' +
|
|
'cat /sys/class/net/' + iface + '/statistics/tx_errors; ';
|
|
exec(cmd, function (error, stdout) {
|
|
if (!error) {
|
|
lines = stdout.toString().split('\n');
|
|
operstate = lines[0].trim();
|
|
rx_bytes = parseInt(lines[1], 10);
|
|
tx_bytes = parseInt(lines[2], 10);
|
|
rx_dropped = parseInt(lines[3], 10);
|
|
rx_errors = parseInt(lines[4], 10);
|
|
tx_dropped = parseInt(lines[5], 10);
|
|
tx_errors = parseInt(lines[6], 10);
|
|
|
|
result = calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
|
|
|
|
}
|
|
resolve(result);
|
|
});
|
|
} else {
|
|
resolve(result);
|
|
}
|
|
}
|
|
if (_freebsd || _openbsd || _netbsd) {
|
|
cmd = 'netstat -ibndI ' + iface;
|
|
exec(cmd, function (error, stdout) {
|
|
if (!error) {
|
|
lines = stdout.toString().split('\n');
|
|
for (let i = 1; i < lines.length; i++) {
|
|
const line = lines[i].replace(/ +/g, ' ').split(' ');
|
|
if (line && line[0] && line[7] && line[10]) {
|
|
rx_bytes = rx_bytes + parseInt(line[7]);
|
|
if (stats[6].trim() !== '-') { rx_dropped = rx_dropped + parseInt(stats[6]); }
|
|
if (stats[5].trim() !== '-') { rx_errors = rx_errors + parseInt(stats[5]); }
|
|
tx_bytes = tx_bytes + parseInt(line[10]);
|
|
if (stats[12].trim() !== '-') { tx_dropped = tx_dropped + parseInt(stats[12]); }
|
|
if (stats[9].trim() !== '-') { tx_errors = tx_errors + parseInt(stats[9]); }
|
|
operstate = 'up';
|
|
}
|
|
}
|
|
result = calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
|
|
}
|
|
resolve(result);
|
|
});
|
|
}
|
|
if (_darwin) {
|
|
cmd = 'ifconfig ' + iface + ' | grep "status"';
|
|
exec(cmd, function (error, stdout) {
|
|
result.operstate = (stdout.toString().split(':')[1] || '').trim();
|
|
result.operstate = (result.operstate || '').toLowerCase();
|
|
result.operstate = (result.operstate === 'active' ? 'up' : (result.operstate === 'inactive' ? 'down' : 'unknown'));
|
|
cmd = 'netstat -bdI ' + iface;
|
|
exec(cmd, function (error, stdout) {
|
|
if (!error) {
|
|
lines = stdout.toString().split('\n');
|
|
// if there is less than 2 lines, no information for this interface was found
|
|
if (lines.length > 1 && lines[1].trim() !== '') {
|
|
// skip header line
|
|
// use the second line because it is tied to the NIC instead of the ipv4 or ipv6 address
|
|
stats = lines[1].replace(/ +/g, ' ').split(' ');
|
|
rx_bytes = parseInt(stats[6]);
|
|
rx_dropped = parseInt(stats[11]);
|
|
rx_errors = parseInt(stats[5]);
|
|
tx_bytes = parseInt(stats[9]);
|
|
tx_dropped = parseInt(stats[11]);
|
|
tx_errors = parseInt(stats[8]);
|
|
|
|
result = calcNetworkSpeed(iface, rx_bytes, tx_bytes, result.operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
|
|
}
|
|
}
|
|
resolve(result);
|
|
});
|
|
});
|
|
}
|
|
if (_windows) {
|
|
let perfData = [];
|
|
let ifaceName = iface;
|
|
|
|
// Performance Data
|
|
util.wmic('path Win32_PerfRawData_Tcpip_NetworkInterface Get name,BytesReceivedPersec,BytesSentPersec,BytesTotalPersec,PacketsOutboundDiscarded,PacketsOutboundErrors,PacketsReceivedDiscarded,PacketsReceivedErrors /value').then((stdout, error) => {
|
|
if (!error) {
|
|
const psections = stdout.toString().split(/\n\s*\n/);
|
|
perfData = parseLinesWindowsPerfData(psections);
|
|
}
|
|
|
|
// Network Interfaces
|
|
networkInterfaces().then(interfaces => {
|
|
// get bytes sent, received from perfData by name
|
|
rx_bytes = 0;
|
|
tx_bytes = 0;
|
|
perfData.forEach(detail => {
|
|
interfaces.forEach(det => {
|
|
if ((det.iface.toLowerCase() === iface.toLowerCase() ||
|
|
det.mac.toLowerCase() === iface.toLowerCase() ||
|
|
det.ip4.toLowerCase() === iface.toLowerCase() ||
|
|
det.ip6.toLowerCase() === iface.toLowerCase() ||
|
|
(det.ifaceName.replace(/[()\[\] ]+/g, '').toLowerCase() === iface.replace(/[()\[\] ]+/g, '').toLowerCase()) &&
|
|
det.ifaceName.replace(/[()\[\] ]+/g, '').toLowerCase() === detail.name)) {
|
|
ifaceName = det.iface;
|
|
rx_bytes = detail.rx_bytes;
|
|
rx_dropped = detail.rx_dropped;
|
|
rx_errors = detail.rx_errors;
|
|
tx_bytes = detail.tx_bytes;
|
|
tx_dropped = detail.tx_dropped;
|
|
tx_errors = detail.tx_errors;
|
|
operstate = det.operstate;
|
|
}
|
|
});
|
|
});
|
|
|
|
if (rx_bytes && tx_bytes) {
|
|
result = calcNetworkSpeed(ifaceName, parseInt(rx_bytes), parseInt(tx_bytes), operstate, rx_dropped, rx_errors, tx_dropped, tx_errors);
|
|
}
|
|
resolve(result);
|
|
});
|
|
});
|
|
}
|
|
} else {
|
|
result.rx_bytes = _network[iface].rx_bytes;
|
|
result.tx_bytes = _network[iface].tx_bytes;
|
|
result.rx_sec = _network[iface].rx_sec;
|
|
result.tx_sec = _network[iface].tx_sec;
|
|
result.ms = _network[iface].last_ms;
|
|
result.operstate = _network[iface].operstate;
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
exports.networkStats = networkStats;
|
|
|
|
// --------------------------
|
|
// NET - connections (sockets)
|
|
|
|
function networkConnections(callback) {
|
|
|
|
return new Promise((resolve) => {
|
|
process.nextTick(() => {
|
|
let result = [];
|
|
if (_linux || _freebsd || _openbsd || _netbsd) {
|
|
let cmd = 'export LC_ALL=C; netstat -tunap | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"; unset LC_ALL';
|
|
if (_freebsd || _openbsd || _netbsd) cmd = 'export LC_ALL=C; netstat -na | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"; unset LC_ALL';
|
|
exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) {
|
|
if (!error) {
|
|
let lines = stdout.toString().split('\n');
|
|
lines.forEach(function (line) {
|
|
line = line.replace(/ +/g, ' ').split(' ');
|
|
if (line.length >= 6) {
|
|
let localip = line[3];
|
|
let localport = '';
|
|
let localaddress = line[3].split(':');
|
|
if (localaddress.length > 1) {
|
|
localport = localaddress[localaddress.length - 1];
|
|
localaddress.pop();
|
|
localip = localaddress.join(':');
|
|
}
|
|
let peerip = line[4];
|
|
let peerport = '';
|
|
let peeraddress = line[4].split(':');
|
|
if (peeraddress.length > 1) {
|
|
peerport = peeraddress[peeraddress.length - 1];
|
|
peeraddress.pop();
|
|
peerip = peeraddress.join(':');
|
|
}
|
|
let connstate = line[5];
|
|
// if (connstate === 'VERBUNDEN') connstate = 'ESTABLISHED';
|
|
let proc = line[6].split('/');
|
|
|
|
if (connstate) {
|
|
result.push({
|
|
protocol: line[0],
|
|
localaddress: localip,
|
|
localport: localport,
|
|
peeraddress: peerip,
|
|
peerport: peerport,
|
|
state: connstate,
|
|
pid: proc[0] && proc[0] !== '-' ? parseInt(proc[0], 10) : -1,
|
|
process: proc[1] ? proc[1].split(' ')[0] : ''
|
|
});
|
|
}
|
|
}
|
|
});
|
|
if (callback) {
|
|
callback(result);
|
|
}
|
|
resolve(result);
|
|
} else {
|
|
cmd = 'ss -tunap | grep "ESTAB\\|SYN-SENT\\|SYN-RECV\\|FIN-WAIT1\\|FIN-WAIT2\\|TIME-WAIT\\|CLOSE\\|CLOSE-WAIT\\|LAST-ACK\\|LISTEN\\|CLOSING"';
|
|
exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) {
|
|
|
|
if (!error) {
|
|
let lines = stdout.toString().split('\n');
|
|
lines.forEach(function (line) {
|
|
line = line.replace(/ +/g, ' ').split(' ');
|
|
if (line.length >= 6) {
|
|
let localip = line[4];
|
|
let localport = '';
|
|
let localaddress = line[4].split(':');
|
|
if (localaddress.length > 1) {
|
|
localport = localaddress[localaddress.length - 1];
|
|
localaddress.pop();
|
|
localip = localaddress.join(':');
|
|
}
|
|
let peerip = line[5];
|
|
let peerport = '';
|
|
let peeraddress = line[5].split(':');
|
|
if (peeraddress.length > 1) {
|
|
peerport = peeraddress[peeraddress.length - 1];
|
|
peeraddress.pop();
|
|
peerip = peeraddress.join(':');
|
|
}
|
|
let connstate = line[1];
|
|
if (connstate === 'ESTAB') connstate = 'ESTABLISHED';
|
|
if (connstate === 'TIME-WAIT') connstate = 'TIME_WAIT';
|
|
let pid = -1;
|
|
let process = '';
|
|
if (line.length >= 7 && line[6].indexOf('users:') > -1) {
|
|
let proc = line[6].replace('users:(("', '').replace(/"/g, '').split(',');
|
|
if (proc.length > 2) {
|
|
process = proc[0].split(' ')[0];
|
|
pid = parseInt(proc[1], 10);
|
|
}
|
|
}
|
|
if (connstate) {
|
|
result.push({
|
|
protocol: line[0],
|
|
localaddress: localip,
|
|
localport: localport,
|
|
peeraddress: peerip,
|
|
peerport: peerport,
|
|
state: connstate,
|
|
pid,
|
|
process
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (callback) {
|
|
callback(result);
|
|
}
|
|
resolve(result);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
if (_darwin) {
|
|
let cmd = 'netstat -natv | grep "ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN"';
|
|
exec(cmd, { maxBuffer: 1024 * 20000 }, function (error, stdout) {
|
|
if (!error) {
|
|
|
|
let lines = stdout.toString().split('\n');
|
|
|
|
lines.forEach(function (line) {
|
|
line = line.replace(/ +/g, ' ').split(' ');
|
|
if (line.length >= 8) {
|
|
let localip = line[3];
|
|
let localport = '';
|
|
let localaddress = line[3].split('.');
|
|
if (localaddress.length > 1) {
|
|
localport = localaddress[localaddress.length - 1];
|
|
localaddress.pop();
|
|
localip = localaddress.join('.');
|
|
}
|
|
let peerip = line[4];
|
|
let peerport = '';
|
|
let peeraddress = line[4].split('.');
|
|
if (peeraddress.length > 1) {
|
|
peerport = peeraddress[peeraddress.length - 1];
|
|
peeraddress.pop();
|
|
peerip = peeraddress.join('.');
|
|
}
|
|
let connstate = line[5];
|
|
let pid = parseInt(line[8], 10);
|
|
if (connstate) {
|
|
result.push({
|
|
protocol: line[0],
|
|
localaddress: localip,
|
|
localport: localport,
|
|
peeraddress: peerip,
|
|
peerport: peerport,
|
|
state: connstate,
|
|
pid: pid,
|
|
process: ''
|
|
});
|
|
}
|
|
}
|
|
});
|
|
if (callback) {
|
|
callback(result);
|
|
}
|
|
resolve(result);
|
|
}
|
|
});
|
|
}
|
|
if (_windows) {
|
|
let cmd = 'netstat -nao';
|
|
try {
|
|
exec(cmd, util.execOptsWin, function (error, stdout) {
|
|
if (!error) {
|
|
|
|
let lines = stdout.toString().split('\r\n');
|
|
|
|
lines.forEach(function (line) {
|
|
line = line.trim().replace(/ +/g, ' ').split(' ');
|
|
if (line.length >= 4) {
|
|
let localip = line[1];
|
|
let localport = '';
|
|
let localaddress = line[1].split(':');
|
|
if (localaddress.length > 1) {
|
|
localport = localaddress[localaddress.length - 1];
|
|
localaddress.pop();
|
|
localip = localaddress.join(':');
|
|
}
|
|
let peerip = line[2];
|
|
let peerport = '';
|
|
let peeraddress = line[2].split(':');
|
|
if (peeraddress.length > 1) {
|
|
peerport = peeraddress[peeraddress.length - 1];
|
|
peeraddress.pop();
|
|
peerip = peeraddress.join(':');
|
|
}
|
|
let pid = line[4];
|
|
let connstate = line[3];
|
|
if (connstate === 'HERGESTELLT') connstate = 'ESTABLISHED';
|
|
if (connstate.startsWith('ABH')) connstate = 'LISTEN';
|
|
if (connstate === 'SCHLIESSEN_WARTEN') connstate = 'CLOSE_WAIT';
|
|
if (connstate === 'WARTEND') connstate = 'TIME_WAIT';
|
|
if (connstate === 'SYN_GESENDET') connstate = 'SYN_SENT';
|
|
|
|
if (connstate === 'LISTENING') connstate = 'LISTEN';
|
|
if (connstate === 'SYN_RECEIVED') connstate = 'SYN_RECV';
|
|
if (connstate === 'FIN_WAIT_1') connstate = 'FIN_WAIT1';
|
|
if (connstate === 'FIN_WAIT_2') connstate = 'FIN_WAIT2';
|
|
if (connstate) {
|
|
result.push({
|
|
protocol: line[0].toLowerCase(),
|
|
localaddress: localip,
|
|
localport: localport,
|
|
peeraddress: peerip,
|
|
peerport: peerport,
|
|
state: connstate,
|
|
pid,
|
|
process: ''
|
|
});
|
|
}
|
|
}
|
|
});
|
|
if (callback) {
|
|
callback(result);
|
|
}
|
|
resolve(result);
|
|
}
|
|
});
|
|
} catch (e) {
|
|
if (callback) { callback(result); }
|
|
resolve(result);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
exports.networkConnections = networkConnections;
|