-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
os: add CIDR support #14307
os: add CIDR support #14307
Conversation
If it is agreed that we should add this, I think it would best be done in C++ land for performance reasons. |
I think a benchmark would be useful before deciding to put this in C++ land. |
test/parallel/test-os.js
Outdated
'ffff:ffff:ffff:ffff::': 64, | ||
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff': 128 | ||
})); | ||
flatten(Object.values(interfaces)).forEach((v) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not do a map:
flatten(Object.values(interfaces))
.map((v) => ({v, mask: netmaskToCIDRSuffixMap.get(v.netmask)}))
.forEach(({v, mask}) => {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only because not all netmasks returned by networkInterfaces()
might be in the map above. That's also the cause for the if condition.
What do you think of the following?
flatten(Object.values(interfaces))
.map((v) => {
assert.ok('cidr' in v, `"cidr" prop not found in ${inspect(v)}`);
return v;
})
.filter((v) => netmaskToCIDRSuffixMap.has(v.netmask))
.map((v) => [v.cidr, `${v.address}/${netmaskToCIDRSuffixMap.get(v.netmask)}`])
.forEach(([actual, expected]) => assert.strictEqual(actual, expected));
test/parallel/test-os.js
Outdated
|
||
return arr.reduce(_flatten, []); | ||
}; | ||
const netmaskToCIDRSuffixMap = new Map(Object.entries({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the use of entries
but it will exclude this from backporting to v6.x
, and it's a nice feature.
So be ready to backport.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, should I make a more backport friendly version or leave this be till we decide to backport it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping @gibfahn
lib/os.js
Outdated
acc[key] = val.map((v) => { | ||
const protocol = v.family.toLowerCase(); | ||
|
||
return Object.assign({}, v, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style nit: assign getCIDRSuffix(v.netmask, protocol)
to a const, and do this in one line.
Don't know why but Functional Programing makes me happy. |
AFAICT It's a very cold code-path. So maybe for reusability.
https://github.com/nodejs/node/blob/master/doc/guides/writing-and-running-benchmarks.md |
'255.0.0.0': 8, | ||
'255.255.255.0': 24, | ||
'ffff:ffff:ffff:ffff::': 64, | ||
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff': 128 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also add some tests for non-byte-aligned subnet masks, e.g. 255.255.224.0
-> 19
and ffff:ffff:ffff:ff80::
-> 57
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is hard as I rely on the output of networkInterfaces
method and that gets data from the os
binding. So, I don't really know where I can inject some mock data with a custom non-aligned netmask
and have the js land networkInterfaces
method consume that. Any ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, you could potential define the parsing method in a internal os
module and test that separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@silverwind So I moved the code out but requiring Found this registry internal/os
fails with a module not found. Do I need to register it somewhere?node.gyp
.
Secondly, where do tests for internal modules go? I see thetest
folder in lib/internal
only has tests for unicode
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zeusdeux In test
, just like everything else. You would need to specify a // Flags: --expose-internals
to make the testing harness expose internal modules for testing though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved the CIDR logic to internal/os
and added tests for it @silverwind @TimothyGu.
lib/os.js
Outdated
.map((v) => parseInt(v, 10)) | ||
.reduce((acc, v) => acc += v, 0); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can remove temp
altogether and return .join('').length
after then map
s above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't work as temp.join('')
could be a string of the form 11111111000
for 255.0.0.0
and called .length
would return 11
which is wrong. Hence the parse and reduce.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, here's a slight modification I came up with that includes validation for invalid masks:
function getCIDRSuffix(subnetMask, proto) {
const v6 = proto === 'ipv6';
const parts = subnetMask
.split(v6 ? ':' : '.')
.map((part) => parseInt(part || 0, v6 ? 16 : 10));
const isInvalidSubnetMask = parts.some(function(_, i) {
if (i > 1 && parts[i - 1] < parts[i]) {
return true;
}
});
if (isInvalidSubnetMask) {
return null;
}
return parts
.map((dec) => dec.toString(2))
.join('')
.split('')
.map((bin) => parseInt(bin, 10))
.reduce((acc, v) => acc += v, 0);
}
There's probably a mathematical way to count the number of binary 1s in a number that eludes me right now.
We also got to consider invalid subnet masks, for example |
I thought of adding this validation in, but I decided not to as that I believe should lie in C++ land. I just wanted this to be a js land "transform" on data coming in from the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need to write a few words in the documentation
test/parallel/test-os.js
Outdated
}); | ||
// assert cidr prop present all interface info | ||
flatten(Object.values(interfaces)) | ||
.map((v) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't have to care that the map.get
returns undefined
. It's not pure FP, but it's susinct
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)}`)
});
92a4eec
to
9604dfb
Compare
Sorry, was away for a bit. Pulled in suggested changes :) |
Would you mind taking another look folks? @refack @silverwind @TimothyGu |
One more approval and I'll squash my commits. |
@zeusdeux no need to squash, the person landing the PR will squash. |
This patch adds support for CIDR notation to the output of the `networkInterfaces()` method PR-URL: nodejs#14307 Fixes: nodejs#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>
9604dfb
to
4a6b678
Compare
Extra sanity on |
This patch adds support for CIDR notation to the output of the `networkInterfaces()` method PR-URL: #14307 Fixes: #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>
Notable Changes * build: * Snapshots are now re-enabled in V8 #14875 * console: * Implement minimal `console.group()`. #14910 * deps: * upgrade libuv to 1.14.1 #14866 * update nghttp2 to v1.25.0 #14955 * dns: * Add `verbatim` option to dns.lookup(). When true, results from the DNS resolver are passed on as-is, without the reshuffling that Node.js otherwise does that puts IPv4 addresses before IPv6 addresses. #14731 * fs: * add fs.copyFile and fs.copyFileSync which allows for more efficient copying of files. #15034 * inspector: * Enable async stack traces #13870 * module: * Add support for ESM. This is currently behind the `--experimental-modules` flag and requires the .mjs extension. `node --experimental-modules index.mjs` #14369 * napi: * implement promise #14365 * os: * Add support for CIDR notation to the output of the networkInterfaces() method. #14307 * perf_hooks: * An initial implementation of the Performance Timing API for Node.js. This is the same Performance Timing API implemented by modern browsers with a number of Node.js specific properties. The User Timing mark() and measure() APIs are implemented, as is a Node.js specific flavor of the Frame Timing for measuring event loop duration. #14680 * tls: * multiple PFX in createSecureContext [#14793](#14793) * Added new collaborators: * BridgeAR – Ruben Bridgewater PR-URL: #15308
Notable Changes * build: * Snapshots are now re-enabled in V8 #14875 * console: * Implement minimal `console.group()`. #14910 * deps: * upgrade libuv to 1.14.1 #14866 * update nghttp2 to v1.25.0 #14955 * dns: * Add `verbatim` option to dns.lookup(). When true, results from the DNS resolver are passed on as-is, without the reshuffling that Node.js otherwise does that puts IPv4 addresses before IPv6 addresses. #14731 * fs: * add fs.copyFile and fs.copyFileSync which allows for more efficient copying of files. #15034 * inspector: * Enable async stack traces #13870 * module: * Add support for ESM. This is currently behind the `--experimental-modules` flag and requires the .mjs extension. `node --experimental-modules index.mjs` #14369 * napi: * implement promise #14365 * os: * Add support for CIDR notation to the output of the networkInterfaces() method. #14307 * perf_hooks: * An initial implementation of the Performance Timing API for Node.js. This is the same Performance Timing API implemented by modern browsers with a number of Node.js specific properties. The User Timing mark() and measure() APIs are implemented, as is a Node.js specific flavor of the Frame Timing for measuring event loop duration. #14680 * tls: * multiple PFX in createSecureContext [#14793](#14793) * Added new collaborators: * BridgeAR – Ruben Bridgewater PR-URL: #15308
Notable Changes * build: * Snapshots are now re-enabled in V8 nodejs#14875 * console: * Implement minimal `console.group()`. nodejs#14910 * deps: * upgrade libuv to 1.14.1 nodejs#14866 * update nghttp2 to v1.25.0 nodejs#14955 * dns: * Add `verbatim` option to dns.lookup(). When true, results from the DNS resolver are passed on as-is, without the reshuffling that Node.js otherwise does that puts IPv4 addresses before IPv6 addresses. nodejs#14731 * fs: * add fs.copyFile and fs.copyFileSync which allows for more efficient copying of files. nodejs#15034 * inspector: * Enable async stack traces nodejs#13870 * module: * Add support for ESM. This is currently behind the `--experimental-modules` flag and requires the .mjs extension. `node --experimental-modules index.mjs` nodejs#14369 * napi: * implement promise nodejs#14365 * os: * Add support for CIDR notation to the output of the networkInterfaces() method. nodejs#14307 * perf_hooks: * An initial implementation of the Performance Timing API for Node.js. This is the same Performance Timing API implemented by modern browsers with a number of Node.js specific properties. The User Timing mark() and measure() APIs are implemented, as is a Node.js specific flavor of the Frame Timing for measuring event loop duration. nodejs#14680 * tls: * multiple PFX in createSecureContext [nodejs#14793](nodejs#14793) * Added new collaborators: * BridgeAR – Ruben Bridgewater PR-URL: nodejs#15308
Release team decided not to land on v6.x, if you disagree let us know. |
os.networkInterfaces() does support a new .cidr property since version 8.5.0 which is below the minimum 10.0.0 required by this module so use it. Ref: nodejs/node#14307
This patch adds support for CIDR notation to the output of the
networkInterfaces()
methodFixes: #14006
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
os