Subject: hurd: pfinet: Raw sockets reject MSG_NOSIGNAL flag with EINVAL Package: hurd X-Debbugs-Cc: [email protected] Version: 1:0.9.git20260919-1+b1 Severity: normal
Hi,While adding Debian GNU/Hurd support to HiPerConTracer (https://www.nntb.no/~dreibh/hipercontracer/), I encountered an issue when sending packets over raw sockets using Boost.Asio.
Boost.Asio automatically sets the MSG_NOSIGNAL flag on send operations (send() / send_to()). On Linux, POSIX, and standard Hurd UDP sockets, passing MSG_NOSIGNAL to non-stream sockets is accepted/ignored. However, on GNU/Hurd raw sockets, the call fails with "Invalid argument":
Invalid argument [system:1073741846 at /usr/include/boost/asio/detail/reactive_socket_service.hpp:261:5 in function 'size_t boost::asio::detail::reactive_socket_service<Protocol>::send_to(...)']
--- REPRODUCTION --- Attached are two minimal C++ test cases: - s1.cc: Raw socket (SOCK_RAW / IPPROTO_UDP) with IP_HDRINCL - s2.cc: Standard UDP socket (SOCK_DGRAM) Compilation: g++ s1.cc -o s1 -lboost_system g++ s2.cc -o s2 -lboost_system Run: sudo ./s1 127.0.0.1 12345 127.0.0.1 7777 ./s2 127.0.0.1 12345 127.0.0.1 7777 Result: - s2 (UDP socket) succeeds on GNU/Hurd. - s1 (Raw socket) fails on GNU/Hurd with "Invalid argument". Workaround in application code:Uncommenting the following block in s1.cc before Boost header inclusion allows s1 to work on Hurd:
#if defined(__GNU__) #include <sys/socket.h> #undef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif --- ROOT CAUSE IN PFINET ---Comparing pfinet's protocol handlers reveals an inconsistency in flag bitmask checking between UDP and raw sockets.
In pfinet/linux-src/net/ipv4/udp.c: if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL)) return -EINVAL; In pfinet/linux-src/net/ipv4/raw.c (raw_sendmsg): if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT)) return(-EINVAL);Because MSG_NOSIGNAL is omitted from the allowed flag mask in pfinet/linux-src/net/ipv4/raw.c, raw_sendmsg() returns -EINVAL whenever MSG_NOSIGNAL is set.
Adding MSG_NOSIGNAL to the permitted flag mask in pfinet/linux-src/net/ipv4/raw.c (and pfinet/linux-src/net/ipv6/raw.c if applicable) would probably align raw socket behavior with standard UDP sockets and resolve Boost.Asio compatibility on Hurd.
-- System Information: Debian Release: forky/sid APT prefers unreleased APT policy: (500, 'unreleased'), (500, 'unstable') Architecture: hurd-amd64 (x86_64) Kernel: GNU-Mach 1.8+git20260805-smp-amd64/Hurd-0.9Locale: LANG=nb_NO.UTF-8, LC_CTYPE=nb_NO.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash Init: sysvinit (via /sbin/init) Versions of packages hurd depends on: ii hurd-libs0.3 1:0.9.git20260919-1+b1 ii less 668-1 ii libacpica0 0~20220331-7 ii libblkid1 2.42.4-1 ii libc0.3 2.43-7~hurd.1 ii libdaemon0 0.14-7.1+b4 ii libgcrypt20 1.12.4-2 ii liblwip0t64 2.2.1+dfsg1-5 ii libncursesw6 6.6+20260608-2 ii libparted2t64 3.8-2 ii libpciaccess0 0.19-2 ii librump0 0~20250111-9 ii librumpdev-disk0 0~20250111-9 ii librumpdev-if0 0~20250111-9 ii librumpdev-pci0 0~20250111-9 ii librumpdev-usb0 0~20250111-9 ii librumpnet0 0~20250111-9 ii libtinfo6 6.6+20260608-2 ii libxkbcommon0 1.7.0-2.1 ii passwd 1:4.20.2-2 ii sysv-rc 3.18-1 ii xkb-data 2.48-1 Versions of packages hurd recommends: ii bf-utf-source 0.08+nmu2 ii netdde 0.0.20200330-13 Versions of packages hurd suggests: pn hurd-doc <none> -- no debconf information
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
/*
#if defined(__GNU__)
#include <sys/socket.h>
#undef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
*/
#include <boost/asio.hpp>
#define USE_BOOST_SENDTO
// IPv4 Pseudo Header required for UDP Checksum
struct __attribute__((packed)) pseudo_header {
uint32_t src_ip;
uint32_t dest_ip;
uint8_t zero;
uint8_t protocol;
uint16_t udp_len;
};
// Safe 16-bit word summation using memcpy (avoids unaligned pointer warnings)
uint32_t accumulate_sum(const void* buf, size_t len, uint32_t initial_sum = 0) {
const auto* ptr = static_cast<const uint8_t*>(buf);
uint32_t sum = initial_sum;
while (len > 1) {
uint16_t word;
std::memcpy(&word, ptr, sizeof(word));
sum += word;
ptr += 2;
len -= 2;
}
if (len == 1) {
uint16_t word = 0;
std::memcpy(&word, ptr, 1);
sum += word;
}
return sum;
}
// Standard Internet Checksum calculation (RFC 1071)
uint16_t in_cksum(const void* buf, size_t len, uint32_t initial_sum = 0) {
uint32_t sum = accumulate_sum(buf, len, initial_sum);
while (sum >> 16) {
sum = (sum & 0xFFFF) + (sum >> 16);
}
return static_cast<uint16_t>(~sum);
}
// Calculate UDP Checksum over pseudo-header, UDP header, and payload
uint16_t calculate_udp_checksum(const struct iphdr* ip, const struct udphdr* udp) {
struct pseudo_header psh{};
psh.src_ip = ip->saddr;
psh.dest_ip = ip->daddr;
psh.zero = 0;
psh.protocol = IPPROTO_UDP;
psh.udp_len = udp->len;
uint32_t psh_sum = accumulate_sum(&psh, sizeof(psh));
uint16_t check = in_cksum(udp, ntohs(udp->len), psh_sum);
return (check == 0) ? 0xFFFF : check;
}
int main(int argc, char* argv[]) {
if (argc < 5) {
std::cerr << "Usage: " << argv[0] << " <Source IP> <Source Port> <Destination IP> <Destination Port>\n";
return 1;
}
std::string src_ip = argv[1];
uint16_t src_port = static_cast<uint16_t>(std::stoi(argv[2]));
std::string dest_ip = argv[3];
uint16_t dest_port = static_cast<uint16_t>(std::stoi(argv[4]));
std::string message = "Hello from Boost raw UDP socket!";
try {
boost::asio::io_context io_context;
// 1. Construct Boost Endpoint directly (validates dest_ip string)
boost::asio::ip::udp::endpoint target_endpoint(
boost::asio::ip::make_address(dest_ip), dest_port
);
// 2. Create Boost raw socket (SOCK_RAW / IPPROTO_UDP)
boost::asio::generic::raw_protocol raw_udp(AF_INET, IPPROTO_UDP);
boost::asio::generic::raw_protocol::socket raw_socket(io_context, raw_udp);
int fd = raw_socket.native_handle();
// 3. Enable IP_HDRINCL
int on = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
perror("setsockopt IP_HDRINCL failed");
return 1;
}
// 4. Allocate buffer for IP Header + UDP Header + Payload
size_t packet_size = sizeof(struct iphdr) + sizeof(struct udphdr) + message.size();
std::vector<char> packet(packet_size, 0);
// 5. Populate IPv4 Header
auto* ip_header = reinterpret_cast<struct iphdr*>(packet.data());
ip_header->ihl = 5;
ip_header->version = 4;
ip_header->tos = 0;
ip_header->tot_len = htons(packet_size);
ip_header->id = htons(54321);
ip_header->frag_off = 0;
ip_header->ttl = 64;
ip_header->protocol = IPPROTO_UDP;
ip_header->check = 0;
if (inet_pton(AF_INET, src_ip.c_str(), &ip_header->saddr) <= 0 ||
inet_pton(AF_INET, dest_ip.c_str(), &ip_header->daddr) <= 0) {
std::cerr << "Invalid IP address format provided.\n";
return 1;
}
// 6. Populate UDP Header
auto* udp_header = reinterpret_cast<struct udphdr*>(packet.data() + sizeof(struct iphdr));
udp_header->source = htons(src_port);
udp_header->dest = htons(dest_port);
udp_header->len = htons(sizeof(struct udphdr) + message.size());
udp_header->check = 0;
// 7. Copy Payload
std::memcpy(packet.data() + sizeof(struct iphdr) + sizeof(struct udphdr), message.data(), message.size());
// 8. Calculate Checksums
ip_header->check = in_cksum(ip_header, sizeof(struct iphdr));
udp_header->check = calculate_udp_checksum(ip_header, udp_header);
#ifdef USE_BOOST_SENDTO
#warning Using BOOST send_to() call!
// 9. Send Raw Packet
size_t bytes_sent = raw_socket.send_to(boost::asio::buffer(packet), target_endpoint);
#else
#warning Using POSIX sendto() call!
// 9. Send via POSIX sendto()
ssize_t bytes_sent = ::sendto(fd, packet.data(), packet.size(), MSG_NOSIGNAL, target_endpoint.data(), target_endpoint.size());
if (bytes_sent < 0) {
perror("sendto() failed");
return 1;
}
#endif
std::cout << "Successfully sent " << bytes_sent << " bytes from "
<< src_ip << ":" << src_port << " to "
<< dest_ip << ":" << dest_port << "!\n";
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return 1;
}
return 0;
}
#include <iostream>
#include <string>
#include <unistd.h>
#include <boost/asio.hpp>
#define USE_BOOST_SENDTO
int main(int argc, char* argv[]) {
if (argc < 5) {
std::cerr << "Usage: " << argv[0] << " <Source IP> <Source Port> <Destination IP> <Destination Port>\n";
return 1;
}
std::string src_ip = argv[1];
uint16_t src_port = static_cast<uint16_t>(std::stoi(argv[2]));
std::string dest_ip = argv[3];
uint16_t dest_port = static_cast<uint16_t>(std::stoi(argv[4]));
std::string message = "Hello from Boost standard UDP socket!";
try {
boost::asio::io_context io_context;
// 1. Construct Source and Destination Endpoints
boost::asio::ip::udp::endpoint source_endpoint(
boost::asio::ip::make_address(src_ip), src_port
);
boost::asio::ip::udp::endpoint target_endpoint(
boost::asio::ip::make_address(dest_ip), dest_port
);
// 2. Create and open a standard UDP socket
boost::asio::ip::udp::socket udp_socket(io_context);
udp_socket.open(boost::asio::ip::udp::v4());
// 3. Allow address reuse and bind to the specified Source IP & Port
udp_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
udp_socket.bind(source_endpoint);
#ifdef USE_BOOST_SENDTO
#warning Using BOOST send_to() call!
// 4. Send payload using Boost socket send_to
size_t bytes_sent = udp_socket.send_to(boost::asio::buffer(message), target_endpoint);
#else
#warning Using POSIX sendto() call!
// 4. Send payload via POSIX sendto()
int fd = udp_socket.native_handle();
ssize_t bytes_sent = ::sendto(fd, message.data(), message.size(), MSG_NOSIGNAL, target_endpoint.data(), target_endpoint.size());
if (bytes_sent < 0) {
perror("sendto() failed");
return 1;
}
#endif
std::cout << "Successfully sent " << bytes_sent << " bytes from "
<< src_ip << ":" << src_port << " to "
<< dest_ip << ":" << dest_port << "!\n";
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return 1;
}
return 0;
}
OpenPGP_signature.asc
Description: OpenPGP digital signature

