diff --git a/src/metrics.js b/src/metrics.js index 362e99515..99286ede9 100644 --- a/src/metrics.js +++ b/src/metrics.js @@ -14,6 +14,7 @@ const apps = require('./apps.js'), docker = require('./docker.js'), fs = require('node:fs'), net = require('node:net'), + network = require('./network.js'), os = require('node:os'), { Readable } = require('node:stream'), safe = require('safetydance'), @@ -141,18 +142,7 @@ async function readDiskMetrics() { } async function readNetworkMetrics() { - const contents = await fs.promises.readFile('/proc/net/route', { encoding: 'utf8' }); - const lines = contents.trim().split('\n').slice(1); // skip header - - let defaultIface = null; - for (const line of lines) { - const [iface, destination] = line.split(/\s+/); - if (destination === '00000000') { - defaultIface = iface; // default route - break; - } - } - if (!defaultIface) throw new BoxError(BoxError.EXTERNAL_ERROR, 'Could not detect default interface'); + const defaultIface = await network.getDefaultInterface(); const [rx, tx] = await Promise.all([ fs.promises.readFile(`/sys/class/net/${defaultIface}/statistics/rx_bytes`, { encoding: 'utf8' }), diff --git a/src/network.js b/src/network.js index a55a6bb95..5b246dbee 100644 --- a/src/network.js +++ b/src/network.js @@ -19,7 +19,9 @@ exports = module.exports = { getIPv4, hasIPv6, getIPv6, - detectIP + detectIP, + + getDefaultInterface }; const assert = require('node:assert'), @@ -36,6 +38,8 @@ const assert = require('node:assert'), const SET_BLOCKLIST_CMD = path.join(__dirname, 'scripts/setblocklist.sh'); +let gDefaultIface = null; // cache + function api(provider) { assert.strictEqual(typeof provider, 'string'); @@ -171,3 +175,31 @@ async function detectIP() { ipv6: error6 ? null : ipv6 }; } + +async function getDefaultInterface() { + if (gDefaultIface) return gDefaultIface; + + const contents4 = await fs.promises.readFile('/proc/net/route', { encoding: 'utf8' }); + const lines4 = contents4.trim().split('\n').slice(1); // skip header + + for (const line of lines4) { + const cols = line.trim().split(/\s+/); // Iface, dest, gw, flags, refcount, use, metric, mask, mtu, window, irtt + if (cols[1] === '00000000') { // && cols[7] === '00000000' + gDefaultIface = cols[0]; + return gDefaultIface; + } + } + + const contents6 = await fs.promises.readFile('/proc/net/ipv6_route', { encoding: 'utf8' }); + const lines6 = contents6.trim().split('\n'); // no header! + + for (const line of lines6) { + const cols = line.trim().split(/\s+/); // dest, dest_prefix_len, src, src_prefix_len, next_hop, metric, refcount, use, flags, iface + if (cols[0] === '00000000000000000000000000000000' && cols[1] === '00') { + gDefaultIface = cols.at(-1); + return gDefaultIface; + } + } + + throw new BoxError(BoxError.EXTERNAL_ERROR, 'Could not detect default interface'); +} diff --git a/src/test/network-test.js b/src/test/network-test.js index f6b0c73a0..bda2628ca 100644 --- a/src/test/network-test.js +++ b/src/test/network-test.js @@ -141,4 +141,10 @@ describe('Network', function () { expect(await network.getDynamicDns()).to.be(true); }); }); + + describe('Default interface', function () { + it('can get default interface', async function () { + expect(await network.getDefaultInterface()).to.be.ok(); + }); + }); });