Skip to content

Commit

Permalink
WSL: Handle eth0 being renamed
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Yen <mark.yen@suse.com>
  • Loading branch information
mook-as committed Dec 21, 2024
1 parent 5aea59b commit e625bd0
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 17 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ INSTALLPROPERTY
ioctl
ipaddr
iptable
IRTT
Isf
isthebestmeshuggahalbum
istio
Expand Down
9 changes: 5 additions & 4 deletions .github/actions/spelling/patterns.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
# ANSI color codes
(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m

# hit-count: 743 file-count: 13
# hex runs
\b[0-9a-fA-F]{16,}\b

# hit-count: 739 file-count: 6
# base64 encoded content, possibly wrapped in mime
(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
Expand Down Expand Up @@ -42,6 +38,11 @@ sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
# uuid:
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b

# hit-count: 743 file-count: 13
# hex runs
\b[0-9a-fA-F]{16,}\b
\b[0-9A-F]{8,}\b

# hit-count: 10 file-count: 1
# css url wrappings
\burl\([^)]+\)
Expand Down
75 changes: 75 additions & 0 deletions pkg/rancher-desktop/backend/__tests__/wsl.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import WSLBackend from '../wsl';

describe('WSLBackend', () => {
describe('getIPAddress', () => {
const route = `
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth0 00000000 01F019AC 0003 0 0 0 00000000 0 0 0
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
eth0 00F019AC 00000000 0001 0 0 0 00F0FFFF 0 0 0
`;
const trie = `
+-- 0.0.0.0/0 3 0 5
|-- 0.0.0.0
/0 universe UNICAST
+-- 127.0.0.0/8 2 0 2
+-- 127.0.0.0/31 1 0 0
|-- 127.0.0.0
/8 host LOCAL
|-- 127.0.0.1
/32 host LOCAL
|-- 127.255.255.255
/32 link BROADCAST
+-- 172.16.0.0/12 2 0 2
+-- 172.17.0.0/16 2 0 2
+-- 172.17.0.0/31 1 0 0
|-- 172.17.0.0
/16 link UNICAST
|-- 172.17.0.1
/32 host LOCAL
|-- 172.17.255.255
/32 link BROADCAST
+-- 172.25.240.0/20 2 0 2
+-- 172.25.240.0/23 2 0 2
|-- 172.25.240.0
/20 link UNICAST
|-- 172.25.241.207
/32 host LOCAL
|-- 172.25.255.255
/32 link BROADCAST
`;

it('should return an IP address', async() => {
function readFile(fileName: string): Promise<string> {
if (fileName === '/proc/net/route') {
return Promise.resolve(route);
}
if (fileName === '/proc/net/fib_trie') {
return Promise.resolve(`Main:\n${ trie }Local:\n${ trie }`);
}

return Promise.reject(new Error(`Read unexpected file ${ fileName }`));
}
const expected = '172.25.241.207';
const actual = WSLBackend.prototype['getIPAddress'].call(null, readFile);

await expect(actual).resolves.toEqual(expected);
});
it('should accept non-standard network interface name', async() => {
function readFile(fileName: string): Promise<string> {
if (fileName === '/proc/net/route') {
return Promise.resolve(route.replaceAll('eth0', 'eth3'));
}
if (fileName === '/proc/net/fib_trie') {
return Promise.resolve(`Main:\n${ trie }Local:\n${ trie }`);
}

return Promise.reject(new Error(`Read unexpected file ${ fileName }`));
}
const expected = '172.25.241.207';
const actual = WSLBackend.prototype['getIPAddress'].call(null, readFile);

await expect(actual).resolves.toEqual(expected);
});
});
});
66 changes: 53 additions & 13 deletions pkg/rancher-desktop/backend/wsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -950,23 +950,63 @@ export default class WSLBackend extends events.EventEmitter implements VMBackend
// wslinfo is missing (wsl < 2.0.4) - fall back to old behavior
}

// We need to locate the _local_ route (netmask) for eth0, and then
// look it up in /proc/net/fib_trie to find the local address.
const routesString = await this.captureCommand('cat', '/proc/net/route');
const routes = routesString.split(/\r?\n/).map(line => line.split(/\s+/));
const route = routes.find(route => route[0] === 'eth0' && route[1] !== '00000000');
return this.getIPAddress();
})();
}

// Get the IP address of the VM by reading files in /proc/net
// This is used to implement the `ipAddress` getter; it's only a separate
// method to make testing easier.
protected async getIPAddress(readFile?: (fileName: string) => Promise<string>): Promise<string | undefined> {
readFile ??= fileName => this.captureCommand('cat', fileName);

// Look at all local addresses in `/proc/net/fib_trie`, then try to find
// the best fit one (and blacklist `docker0`, `cni0`, `veth-rd-ns`).
const trieContents = await readFile('/proc/net/fib_trie');
const trieLines = trieContents.split(/\r?\n/).reverse();
// localIndices refer to the lines that say "/32 host LOCAL", i.e. interface addresses.
const localIndices = Array.from(trieLines.entries()).filter(([, line]) => line.includes('/32 host LOCAL')).map(([i]) => i);
// addresses is the actual IP addresses of the local interfaces.
const addresses = localIndices.map(i => trieLines[i + 1].trim().split(/\s+/).pop() ?? '127.0.0.1');
// masks is the network mask for each IP address.
const masks = localIndices.map((i) => {
return trieLines.find((line, j) => {
// Find the next line that starts with '+'.
return j > i && line.trim().startsWith('+');
})?.split(/\s+/).find(w => w.includes('/'))?.split('/')
.shift() ?? '127.0.0.1'; // Extract the IP address.
});

if (!route) {
const routeContents = await readFile('/proc/net/route');
const routes = routeContents.split(/\r?\n/).map((line) => {
const [iface, destination] = line.trim().split(/\s+/);

if (!iface || !destination) {
return undefined;
}
const net = Array.from(route[1].matchAll(/../g)).reverse().map(n => parseInt(n.toString(), 16)).join('.');
const trie = await this.captureCommand('cat', '/proc/net/fib_trie');
const lines = _.takeWhile(trie.split(/\r?\n/).slice(1), line => /^\s/.test(line));
const iface = _.dropWhile(lines, line => !line.includes(`${ net }/`));
const addr = iface.find((_, i, array) => array[i + 1]?.includes('/32 host LOCAL'));
const mask = Array.from(destination.matchAll(/../g)).reverse().map(([v]) => parseInt(v, 16)).join('.');

return addr?.split(/\s+/).pop();
})();
return [mask, iface];
}).filter(defined);

for (const i in localIndices) {
const mask = masks[i];

if (!mask || mask.startsWith('127.') || mask === '0.0.0.0') {
// Invalid mask, localhost mask, or external.
continue;
}
const iface = routes.find(([routeMask]) => mask === routeMask)?.[1];

if (!iface || /^(cni\d|docker\d|veth)/.test(iface)) {
continue;
}
const address = addresses[i];

if (address) {
return address;
}
}
}

async getBackendInvalidReason(): Promise<BackendError | null> {
Expand Down

0 comments on commit e625bd0

Please sign in to comment.