From 0037ae72497e576808d69c9ef3fe55dabd4b64fe Mon Sep 17 00:00:00 2001 From: Peter Hedenskog Date: Mon, 23 Mar 2026 22:57:05 +0100 Subject: [PATCH 1/2] Remove retry for default interface and cache the used one --- lib/tc.js | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/lib/tc.js b/lib/tc.js index 00d14a1..557d143 100644 --- a/lib/tc.js +++ b/lib/tc.js @@ -1,30 +1,21 @@ import shell from './shell.js'; import sudo from './sudo.js'; -const delay = (ms) => new Promise((result) => setTimeout(result, ms)); +let cachedInterface; async function getDefaultInterface() { const command = "sudo ip route | awk '/default/ {print $5; exit}' | tr -d '\n'"; + const result = await shell(command); - // Retry a few times since GitHub Actions sometimes temporarily loses the default route - for (let attempt = 0; attempt < 3; attempt++) { - const result = await shell(command); - if (result.stdout.length > 0) { - return result.stdout; - } - if (result.stderr.length > 0) { - throw new Error( - 'There was an error getting the default interface:\n\n' + result.stderr - ); - } - await delay(1000); + if (result.stdout.length === 0) { + const detail = result.stderr || ''; + throw new Error( + 'There was an error getting the default interface:\n\n' + detail + ); } - const result = await shell('sudo ip route show'); - throw new Error( - `There was an error getting the default interface ${result.stdout}` - ); + return result.stdout; } async function moduleProbe() { @@ -181,13 +172,31 @@ export async function start(up, down, rtt = 0, packetLoss = 0) { } const indexFace = await getDefaultInterface(); + cachedInterface = indexFace; await moduleProbe(); await setupifb0(); await setup(indexFace); await setLimits(up, down, halfWayRTT, packetLoss, indexFace); } +async function getThrottledInterface() { + // Find the interface that has our ingress qdisc, since the default route + // may not be available while throttling is active + const result = await shell( + "tc qdisc show | awk '/ingress/ {print $5; exit}'" + ); + return result.stdout.trim(); +} + export async function stop() { - const indexFace = await getDefaultInterface(); + let indexFace = cachedInterface; + if (!indexFace) { + // Try to find the interface with our tc rules first, fall back to default route + indexFace = await getThrottledInterface(); + if (!indexFace) { + indexFace = await getDefaultInterface(); + } + } + cachedInterface = undefined; try { try { From 76ee5cb8676a34e162bab49943493e51835458d3 Mon Sep 17 00:00:00 2001 From: Peter Hedenskog Date: Mon, 23 Mar 2026 23:04:23 +0100 Subject: [PATCH 2/2] new try --- lib/tc.js | 48 +++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/lib/tc.js b/lib/tc.js index 557d143..745fb59 100644 --- a/lib/tc.js +++ b/lib/tc.js @@ -1,21 +1,25 @@ import shell from './shell.js'; import sudo from './sudo.js'; -let cachedInterface; - async function getDefaultInterface() { - const command = - "sudo ip route | awk '/default/ {print $5; exit}' | tr -d '\n'"; - const result = await shell(command); - - if (result.stdout.length === 0) { - const detail = result.stderr || ''; - throw new Error( - 'There was an error getting the default interface:\n\n' + detail - ); + // Try the default route first + const routeResult = await shell( + "sudo ip route | awk '/default/ {print $5; exit}' | tr -d '\n'" + ); + if (routeResult.stdout.length > 0) { + return routeResult.stdout; + } + + // Fall back to finding the interface with a global IP address, + // since the default route may be gone while throttling is active + const addrResult = await shell( + "ip -o -4 addr show scope global | awk '{print $2; exit}'" + ); + if (addrResult.stdout.trim().length > 0) { + return addrResult.stdout.trim(); } - return result.stdout; + throw new Error('Could not find the default network interface'); } async function moduleProbe() { @@ -172,31 +176,13 @@ export async function start(up, down, rtt = 0, packetLoss = 0) { } const indexFace = await getDefaultInterface(); - cachedInterface = indexFace; await moduleProbe(); await setupifb0(); await setup(indexFace); await setLimits(up, down, halfWayRTT, packetLoss, indexFace); } -async function getThrottledInterface() { - // Find the interface that has our ingress qdisc, since the default route - // may not be available while throttling is active - const result = await shell( - "tc qdisc show | awk '/ingress/ {print $5; exit}'" - ); - return result.stdout.trim(); -} - export async function stop() { - let indexFace = cachedInterface; - if (!indexFace) { - // Try to find the interface with our tc rules first, fall back to default route - indexFace = await getThrottledInterface(); - if (!indexFace) { - indexFace = await getDefaultInterface(); - } - } - cachedInterface = undefined; + const indexFace = await getDefaultInterface(); try { try {