Skip to content

Conversation

@amyssnippet
Copy link

@amyssnippet amyssnippet commented Jan 24, 2026

this PR implements socket.setTOS(tos) and socket.getTOS() in net.Socket. it needed this to support DSCP tagging (QoS) for traffic prioritization, which wasn't previously exposed in the JS API. for the implementation:

  • I added the logic in tcp_wrap.cc to attempt IP_TOS first, and fallback to IPV6_TCLASS if that fails. This handles both IPv4 and IPv6 sockets automatically without needing a separate flag.
  • Windows returns UV_ENOSYS for now since the headers/implementation differ there.
  • Added a new test file (test-net-socket-tos.js) to verify input validation and ensure the values are actually set on supported platforms.

Fixes: #61489

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/net

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Jan 24, 2026
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work. Can you add the documentation?

@mcollina mcollina requested a review from ronag January 24, 2026 13:16
@mcollina mcollina added the semver-minor PRs that contain new features and should be released in the next minor version. label Jan 24, 2026
@amyssnippet amyssnippet requested a review from mcollina January 24, 2026 13:41
@amyssnippet amyssnippet mentioned this pull request Jan 24, 2026
@amyssnippet amyssnippet requested a review from mcollina January 24, 2026 14:34
@amyssnippet
Copy link
Author

Thanks @mcollina , is this ready to be merged? Please let me know if there is anything else you need me to adjust.

@ronag ronag added the request-ci Add this label to start a Jenkins CI on a PR. label Jan 25, 2026
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jan 25, 2026
@nodejs-github-bot
Copy link
Collaborator

@ronag
Copy link
Member

ronag commented Jan 25, 2026

@mcollina I'm confused by the CI failures. Any ideas? Or just flaky?

@amyssnippet
Copy link
Author

@ronag and @mcollina , it's all my fault, as things work for the linux better, on which i worked on, and i observed most of failures are for windows and very few or i guess only 1 or 2 for macos, and thats completely the code fault written by me. And the reason is platform incompatiblity. Some constants worked for Linux and macos but not for windows. Gimme some time I will fix all of them and get back with solutions soon. Thanks!

@amyssnippet
Copy link
Author

@ronag and @mcollina i guess all errors are fixed, its because of windows incompatibility, so now lets rerun the checks.

and rest this below picture of macos failures are flake i read the console output

https://ci.nodejs.org/job/node-test-commit-osx/68706/nodes=macos15-arm64/

image

@amyssnippet amyssnippet requested a review from ronag January 25, 2026 12:18
@ronag ronag added the request-ci Add this label to start a Jenkins CI on a PR. label Jan 25, 2026
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jan 25, 2026
@nodejs-github-bot
Copy link
Collaborator

@amyssnippet
Copy link
Author

@ryan and @mcollina , i think all changes are good, please have a look to the changes, i have setted up everything to be proper for all machine OS. if anything needs change, i am here. and many test runs in ci pipeline are flaky, and not proper, so have a look at them too. Thanks!

@amyssnippet amyssnippet requested a review from ronag January 25, 2026 18:07
@MaheshThakur9152 MaheshThakur9152 force-pushed the fix/61489 branch 2 times, most recently from 20e5908 to 57f36ad Compare January 26, 2026 06:14
@ronag ronag requested review from Copilot and joyeecheung and removed request for mcollina January 26, 2026 06:33
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds setTOS() and getTOS() methods to net.Socket and dgram.Socket to enable DSCP tagging for QoS traffic prioritization. The implementation handles both IPv4 (IP_TOS) and IPv6 (IPV6_TCLASS) sockets automatically with fallback logic.

Changes:

  • Implemented native C++ methods in tcp_wrap.cc and udp_wrap.cc to set/get TOS values using appropriate socket options
  • Added JavaScript API methods in lib/net.js and lib/dgram.js with validation and deferred application logic
  • Created comprehensive test coverage across multiple scenarios (validation, implicit bind/connect, connected state)

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/parallel/test-tcp-setTOS.js Tests basic TCP setTOS/getTOS functionality with validation
test/parallel/test-tcp-setTOS-implicit-connect.js Tests TCP TOS setting before connect
test/parallel/test-net-socket-tos.js Comprehensive TCP socket TOS validation tests
test/parallel/test-dgram-setTOS.js Tests basic UDP setTOS/getTOS with validation
test/parallel/test-dgram-setTOS-implicit-bind.js Tests UDP TOS setting before bind
test/parallel/test-dgram-setTOS-connect.js Tests UDP TOS with connected datagram socket
src/udp_wrap.h Declares SetTOS and GetTOS methods for UDP
src/udp_wrap.cc Implements UDP TOS get/set with IPv4/IPv6 handling
src/tcp_wrap.h Declares SetTOS and GetTOS methods for TCP
src/tcp_wrap.cc Implements TCP TOS get/set with IPv4/IPv6 handling and header includes
lib/net.js Adds Socket.prototype.setTOS/getTOS with deferred application logic
lib/dgram.js Adds Socket.prototype.setTOS/getTOS with deferred application logic
doc/api/net.md Documents the new setTOS and getTOS methods

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/tcp_wrap.cc Outdated
Comment on lines 41 to 45
#ifdef __linux__
#include <netinet/ip6.h>
#else
#include <netinet/ip6.h>
#endif
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional include is redundant - both branches include the same header <netinet/ip6.h>. Either remove the conditional entirely if the header is universally available, or use it to conditionally include different headers if platform-specific handling is needed.

Suggested change
#ifdef __linux__
#include <netinet/ip6.h>
#else
#include <netinet/ip6.h>
#endif
#include <netinet/ip6.h>

Copilot uses AI. Check for mistakes.
lib/net.js Outdated

// TOS must be an integer in the range 0..255.
if (!Number.isInteger(tos) || tos < 0 || tos > 255) {
throw new ErrnoException(UV_EINVAL, 'setTOS');
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validation error uses ErrnoException with UV_EINVAL, but validation errors for out-of-range values typically use ERR_OUT_OF_RANGE for consistency with the rest of the codebase and to provide clearer error messages. Consider using ERR_OUT_OF_RANGE for the range validation instead.

Suggested change
throw new ErrnoException(UV_EINVAL, 'setTOS');
throw new ERR_OUT_OF_RANGE('tos', '>= 0 && <= 255', tos);

Copilot uses AI. Check for mistakes.
lib/dgram.js Outdated
Comment on lines 891 to 893
if (!Number.isInteger(tos) || tos < 0 || tos > 255) {
throw new ErrnoException(UV_EINVAL, 'setTOS');
}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validation error uses ErrnoException with UV_EINVAL, but validation errors for out-of-range values typically use ERR_OUT_OF_RANGE for consistency with the rest of the codebase and to provide clearer error messages. Consider using ERR_OUT_OF_RANGE for the range validation instead.

Copilot uses AI. Check for mistakes.
* Returns: {net.Socket} The socket itself.

Sets the Type of Service (TOS) field for IPv4 packets or Traffic Class for IPv6
Packets sent from this socket. This can be used to prioritize network traffic.
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected capitalization of 'Packets' to 'packets'.

Suggested change
Packets sent from this socket. This can be used to prioritize network traffic.
packets sent from this socket. This can be used to prioritize network traffic.

Copilot uses AI. Check for mistakes.
lib/dgram.js Outdated
Comment on lines 208 to 214
if (socket[kSetTOS] !== undefined && state.handle && state.handle.setTOS) {
const err = state.handle.setTOS(socket[kSetTOS]);
if (err && err !== UV_EBADF) {
// Non-fatal: surface error to the socket
socket.emit('error', new ErrnoException(err, 'setTOS'));
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it better to always defer to on listening?

lib/dgram.js Outdated
Comment on lines 902 to 905
// Ignore EBADF: on some platforms the underlying fd may not be
// fully initialized yet even though a handle exists. In that case
// defer applying the setting to when the socket is actually bound
// (handled by bind/replaceHandle).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems rather hacky... isn't there a way to know when the handle is initialized?

@ronag ronag requested a review from mcollina January 26, 2026 06:48
@amyssnippet
Copy link
Author

@ronag, i request that this pr should only address the tcp or is udp required?? if we push the udp in next update, then it will be easy to rollback changes on crashes

@amyssnippet
Copy link
Author

as the errors were found only in the dgram which is an udp related work, so we can do 2 different work. one for tcp and one for udp

@ronag
Copy link
Member

ronag commented Jan 26, 2026

IMHO, tcp is sufficient

@ronag ronag requested a review from Copilot January 26, 2026 08:04
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

this[kSetNoDelay] = Boolean(options.noDelay);
this[kSetKeepAlive] = Boolean(options.keepAlive);
this[kSetKeepAliveInitialDelay] = ~~(options.keepAliveInitialDelay / 1000);
this[kSetTOS] = options.TOS;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option name 'TOS' uses inconsistent casing compared to other options ('noDelay', 'keepAlive'). Consider using 'tos' (lowercase) instead of 'TOS' for consistency with the method names and other socket options.

Suggested change
this[kSetTOS] = options.TOS;
this[kSetTOS] = options.tos;

Copilot uses AI. Check for mistakes.
}

if (!this._handle.getTOS) {
return this[kSetTOS];
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When getTOS() is called and the handle exists but doesn't support getTOS, the method returns the cached kSetTOS value which could be undefined. This creates inconsistent behavior compared to line 683 where undefined is explicitly converted to 0. The return should be: return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;

Suggested change
return this[kSetTOS];
// Return cached value if set, otherwise default to 0
return this[kSetTOS] !== undefined ? this[kSetTOS] : 0;

Copilot uses AI. Check for mistakes.
}

// If both failed, return the negative errno
args.GetReturnValue().Set(-errno);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using errno directly after failed getsockopt calls is unreliable because errno may have been modified by intervening operations. The errno value should be captured immediately after the getsockopt call fails before any other operations occur.

Copilot uses AI. Check for mistakes.
Comment on lines +237 to +263
#ifdef _WIN32
// Windows: Try IPv4
if (setsockopt(reinterpret_cast<SOCKET>(fd),
IPPROTO_IP,
IP_TOS,
reinterpret_cast<const char*>(&tos),
static_cast<int>(sizeof(tos))) == 0) {
successful_at_least_once = true;
}
// Windows: Try IPv6 (Best effort)
if (setsockopt(reinterpret_cast<SOCKET>(fd),
IPPROTO_IPV6,
IPV6_TCLASS,
reinterpret_cast<const char*>(&tos),
static_cast<int>(sizeof(tos))) == 0) {
successful_at_least_once = true;
}
#else
// POSIX (Linux/macOS): Try IPv4
if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == 0) {
successful_at_least_once = true;
}
// POSIX (Linux/macOS): Try IPv6
if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) == 0) {
successful_at_least_once = true;
}
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there a way to check if a socket is ipv4 or ipv6 before doing this? Rather than relying on fallback on any error?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you re absolutely right, i have refractored the implementation to detect whether the socket is ipv4 or 6, and performs a single targeted call with correct constant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. semver-minor PRs that contain new features and should be released in the next minor version.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

socket.setTOS

4 participants