mirror of https://github.com/nodejs/node.git
os: add CIDR support
This patch adds support for CIDR notation to the output of the `networkInterfaces()` method PR-URL: https://github.com/nodejs/node/pull/14307 Fixes: https://github.com/nodejs/node/issues/14006 Reviewed-By: Roman Reiss <me@silverwind.io> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com>pull/14307/head
parent
afe68c1868
commit
4a6b678070
|
@ -253,6 +253,9 @@ The properties available on the assigned network address object include:
|
|||
similar interface that is not remotely accessible; otherwise `false`
|
||||
* `scopeid` {number} The numeric IPv6 scope ID (only specified when `family`
|
||||
is `IPv6`)
|
||||
* `cidr` {string} The assigned IPv4 or IPv6 address with the routing prefix
|
||||
in CIDR notation. If the `netmask` is invalid, this property is set
|
||||
to `null`
|
||||
|
||||
<!-- eslint-skip -->
|
||||
```js
|
||||
|
@ -263,14 +266,16 @@ The properties available on the assigned network address object include:
|
|||
netmask: '255.0.0.0',
|
||||
family: 'IPv4',
|
||||
mac: '00:00:00:00:00:00',
|
||||
internal: true
|
||||
internal: true,
|
||||
cidr: '127.0.0.1/8'
|
||||
},
|
||||
{
|
||||
address: '::1',
|
||||
netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
|
||||
family: 'IPv6',
|
||||
mac: '00:00:00:00:00:00',
|
||||
internal: true
|
||||
internal: true,
|
||||
cidr: '::1/128'
|
||||
}
|
||||
],
|
||||
eth0: [
|
||||
|
@ -279,14 +284,16 @@ The properties available on the assigned network address object include:
|
|||
netmask: '255.255.255.0',
|
||||
family: 'IPv4',
|
||||
mac: '01:02:03:0a:0b:0c',
|
||||
internal: false
|
||||
internal: false,
|
||||
cidr: '192.168.1.108/24'
|
||||
},
|
||||
{
|
||||
address: 'fe80::a00:27ff:fe4e:66a1',
|
||||
netmask: 'ffff:ffff:ffff:ffff::',
|
||||
family: 'IPv6',
|
||||
mac: '01:02:03:0a:0b:0c',
|
||||
internal: false
|
||||
internal: false,
|
||||
cidr: 'fe80::a00:27ff:fe4e:66a1/64'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
function getCIDRSuffix(mask, protocol = 'ipv4') {
|
||||
const isV6 = protocol === 'ipv6';
|
||||
const bitsString = mask
|
||||
.split(isV6 ? ':' : '.')
|
||||
.filter((v) => !!v)
|
||||
.map((v) => pad(parseInt(v, isV6 ? 16 : 10).toString(2), isV6))
|
||||
.join('');
|
||||
|
||||
if (isValidMask(bitsString)) {
|
||||
return countOnes(bitsString);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function pad(binaryString, isV6) {
|
||||
const groupLength = isV6 ? 16 : 8;
|
||||
const binLen = binaryString.length;
|
||||
|
||||
return binLen < groupLength ?
|
||||
`${'0'.repeat(groupLength - binLen)}${binaryString}` : binaryString;
|
||||
}
|
||||
|
||||
function isValidMask(bitsString) {
|
||||
const firstIndexOfZero = bitsString.indexOf(0);
|
||||
const lastIndexOfOne = bitsString.lastIndexOf(1);
|
||||
|
||||
return firstIndexOfZero < 0 || firstIndexOfZero > lastIndexOfOne;
|
||||
}
|
||||
|
||||
function countOnes(bitsString) {
|
||||
return bitsString
|
||||
.split('')
|
||||
.reduce((acc, bit) => acc += parseInt(bit, 10), 0);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCIDRSuffix
|
||||
};
|
18
lib/os.js
18
lib/os.js
|
@ -24,6 +24,7 @@
|
|||
const pushValToArrayMax = process.binding('util').pushValToArrayMax;
|
||||
const constants = process.binding('constants').os;
|
||||
const deprecate = require('internal/util').deprecate;
|
||||
const getCIDRSuffix = require('internal/os').getCIDRSuffix;
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
const {
|
||||
|
@ -121,6 +122,21 @@ function endianness() {
|
|||
}
|
||||
endianness[Symbol.toPrimitive] = () => kEndianness;
|
||||
|
||||
function networkInterfaces() {
|
||||
const interfaceAddresses = getInterfaceAddresses();
|
||||
|
||||
return Object.entries(interfaceAddresses).reduce((acc, [key, val]) => {
|
||||
acc[key] = val.map((v) => {
|
||||
const protocol = v.family.toLowerCase();
|
||||
const suffix = getCIDRSuffix(v.netmask, protocol);
|
||||
const cidr = suffix ? `${v.address}/${suffix}` : null;
|
||||
|
||||
return Object.assign({}, v, { cidr });
|
||||
});
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
module.exports = exports = {
|
||||
arch,
|
||||
cpus,
|
||||
|
@ -130,7 +146,7 @@ module.exports = exports = {
|
|||
homedir: getHomeDirectory,
|
||||
hostname: getHostname,
|
||||
loadavg,
|
||||
networkInterfaces: getInterfaceAddresses,
|
||||
networkInterfaces,
|
||||
platform,
|
||||
release: getOSRelease,
|
||||
tmpdir,
|
||||
|
|
1
node.gyp
1
node.gyp
|
@ -91,6 +91,7 @@
|
|||
'lib/internal/linkedlist.js',
|
||||
'lib/internal/net.js',
|
||||
'lib/internal/module.js',
|
||||
'lib/internal/os.js',
|
||||
'lib/internal/process/next_tick.js',
|
||||
'lib/internal/process/promises.js',
|
||||
'lib/internal/process/stdio.js',
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const getCIDRSuffix = require('internal/os').getCIDRSuffix;
|
||||
|
||||
const specs = [
|
||||
// valid
|
||||
['128.0.0.0', 'ipv4', 1],
|
||||
['255.0.0.0', 'ipv4', 8],
|
||||
['255.255.255.128', 'ipv4', 25],
|
||||
['255.255.255.255', 'ipv4', 32],
|
||||
['ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'ipv6', 128],
|
||||
['ffff:ffff:ffff:ffff::', 'ipv6', 64],
|
||||
['ffff:ffff:ffff:ff80::', 'ipv6', 57],
|
||||
// invalid
|
||||
['255.0.0.1', 'ipv4', null],
|
||||
['255.255.9.0', 'ipv4', null],
|
||||
['255.255.1.0', 'ipv4', null],
|
||||
['ffff:ffff:43::', 'ipv6', null],
|
||||
['ffff:ffff:ffff:1::', 'ipv6', null]
|
||||
];
|
||||
|
||||
specs.forEach(([mask, protocol, expectedSuffix]) => {
|
||||
const actualSuffix = getCIDRSuffix(mask, protocol);
|
||||
|
||||
assert.strictEqual(
|
||||
actualSuffix, expectedSuffix,
|
||||
`Mask: ${mask}, expected: ${expectedSuffix}, actual: ${actualSuffix}`
|
||||
);
|
||||
});
|
|
@ -24,6 +24,7 @@ const common = require('../common');
|
|||
const assert = require('assert');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const is = {
|
||||
string: (value) => { assert.strictEqual(typeof value, 'string'); },
|
||||
|
@ -121,7 +122,7 @@ switch (platform) {
|
|||
const actual = interfaces.lo.filter(filter);
|
||||
const expected = [{ address: '127.0.0.1', netmask: '255.0.0.0',
|
||||
mac: '00:00:00:00:00:00', family: 'IPv4',
|
||||
internal: true }];
|
||||
internal: true, cidr: '127.0.0.1/8' }];
|
||||
assert.deepStrictEqual(actual, expected);
|
||||
break;
|
||||
}
|
||||
|
@ -131,11 +132,31 @@ switch (platform) {
|
|||
const actual = interfaces['Loopback Pseudo-Interface 1'].filter(filter);
|
||||
const expected = [{ address: '127.0.0.1', netmask: '255.0.0.0',
|
||||
mac: '00:00:00:00:00:00', family: 'IPv4',
|
||||
internal: true }];
|
||||
internal: true, cidr: '127.0.0.1/8' }];
|
||||
assert.deepStrictEqual(actual, expected);
|
||||
break;
|
||||
}
|
||||
}
|
||||
function flatten(arr) {
|
||||
return arr.reduce(
|
||||
(acc, c) => acc.concat(Array.isArray(c) ? flatten(c) : c),
|
||||
[]
|
||||
);
|
||||
}
|
||||
const netmaskToCIDRSuffixMap = new Map(Object.entries({
|
||||
'255.0.0.0': 8,
|
||||
'255.255.255.0': 24,
|
||||
'ffff:ffff:ffff:ffff::': 64,
|
||||
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff': 128
|
||||
}));
|
||||
flatten(Object.values(interfaces))
|
||||
.map((v) => ({ v, mask: netmaskToCIDRSuffixMap.get(v.netmask) }))
|
||||
.forEach(({ v, mask }) => {
|
||||
assert.ok('cidr' in v, `"cidr" prop not found in ${inspect(v)}`);
|
||||
if (mask) {
|
||||
assert.strictEqual(v.cidr, `${v.address}/${mask}`);
|
||||
}
|
||||
});
|
||||
|
||||
const EOL = os.EOL;
|
||||
assert.ok(EOL.length > 0);
|
||||
|
|
Loading…
Reference in New Issue