Skip to content

linuxcncrsh: Allow IPv4 as fallback when IPv6 is disabled - #4403

Open
BsAtHome wants to merge 1 commit into
LinuxCNC:masterfrom
BsAtHome:fix_emcrsh-ipv4
Open

linuxcncrsh: Allow IPv4 as fallback when IPv6 is disabled#4403
BsAtHome wants to merge 1 commit into
LinuxCNC:masterfrom
BsAtHome:fix_emcrsh-ipv4

Conversation

@BsAtHome

@BsAtHome BsAtHome commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The new linuxcncrsh server used v4-mapped-on-v6 to have one socket accept both IPv6 and IPv4 connections. However, if IPv6 is disabled at the kernel command line (or not compiled in), then it would fail to create a listening socket. Now there is a IPv4-only fallback in case IPv6 fails. This PR also fixes a lockup in the processing of SET commands when a client disconnects while a SET command is in flight.

Fixes #4400
Fixes #4401

return -1;
}
}
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Dual-stack here relies on net.ipv6.bindv6only defaulting to 0. With that sysctl
set to 1, socket(AF_INET6, ...) still succeeds, so the fallback never fires and
IPv4 clients get ECONNREFUSED. Measured:

v6only=0  IPv4 client: connected OK
v6only=1  IPv4 client: connect FAILED (Connection refused)

Worth pinning the assumption rather than inheriting it, on the IPv6 branch only?

int zero = 0;
setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero));

addr.addr6.sin6_port = htons(port);
socklen_t slen = sizeof(addr.addr6);

sockfd = socket(AF_INET6, SOCK_STREAM, 0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is EAFNOSUPPORT the only way the IPv6 listener can be unavailable in practice?
A restricted netns or an LSM/seccomp policy returns EACCES or EPERM here, and
there are setups where socket() succeeds but bind() to :: is what fails. Since
IPv4 is a strict fallback, any downside to taking it on any IPv6 failure?

struct sockaddr_in6 csa6;
struct sockaddr_in csa4;
} csa;
socklen_t csal = isipv4 ? sizeof(csa.csa4) : sizeof(csa.csa6);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

accept() writes the real length back, so does the family need to be known here?
sizeof(csa) is correct for both.

Broader: would sockaddr_storage plus getnameinfo(..., NI_NUMERICHOST |
NI_NUMERICSERV) let the union, isipv4, and the duplicated logging branch below
all disappear?

{
if (ctx.sock < 0)
return;
if(ctx.cmdtimeout > 0.0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The latch clears, but the NML command is still in flight, so its completion or
error lands on whichever client polls next. Intended trade-off, or would
recording emcCommandSerialNumber at close time and discarding status up to it
be cheap enough to close the desync?

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

Labels

None yet

Projects

None yet

2 participants