From 69751d1943b05e3220cd0edcd382426829e502a0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 19:06:45 +0000 Subject: [PATCH] fix: add FreeBSD support for local address handling - Add FreeBSD-specific packet info handling in ENet unix.c - Use IP_RECVDSTADDR/IP_SENDSRCADDR for FreeBSD instead of IP_PKTINFO - Enable IP_RECVDSTADDR socket option on video/audio sockets for FreeBSD - Remove incorrect IP_PKTINFO macro redefinition from misc.cpp - Add FreeBSD-specific send code in Sunshine's UDP streaming functions Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> --- patches/enet_freebsd.patch | 63 +++++++++++++++++++++++++++++++++++++ src/platform/linux/misc.cpp | 43 +++++++++++++++++++------ src/stream.cpp | 51 +++++++++++++++++++++++------- 3 files changed, 137 insertions(+), 20 deletions(-) create mode 100644 patches/enet_freebsd.patch diff --git a/patches/enet_freebsd.patch b/patches/enet_freebsd.patch new file mode 100644 index 000000000..51e65706e --- /dev/null +++ b/patches/enet_freebsd.patch @@ -0,0 +1,63 @@ +diff --git a/unix.c b/unix.c +index 1234567..abcdefg 100644 +--- a/unix.c ++++ b/unix.c +@@ -373,6 +373,14 @@ enet_socket_create (ENetSocketType type, int af) + } + #endif + ++#if defined(__FreeBSD__) && defined(IP_RECVDSTADDR) ++ { ++ // Enable IP_RECVDSTADDR for FreeBSD to receive destination address ++ int on = 1; ++ setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, (char *)&on, sizeof(on)); ++ } ++#endif ++ + #ifdef IP_PKTINFO + { + // We turn this on for all sockets because it may be required for IPv4 +@@ -617,6 +625,21 @@ enet_socket_send (ENetSocket socket, + // from this peer to ensure it correctly recognizes our responses as + // coming from the expected host. + if (localAddress != NULL) { ++#if defined(__FreeBSD__) && defined(IP_SENDSRCADDR) ++ if (localAddress->address.ss_family == AF_INET) { ++ struct in_addr srcAddr = ((struct sockaddr_in*)&localAddress->address)->sin_addr; ++ ++ msgHdr.msg_control = controlBufData; ++ msgHdr.msg_controllen = CMSG_SPACE(sizeof(srcAddr)); ++ ++ struct cmsghdr *chdr = CMSG_FIRSTHDR(&msgHdr); ++ chdr->cmsg_level = IPPROTO_IP; ++ chdr->cmsg_type = IP_SENDSRCADDR; ++ chdr->cmsg_len = CMSG_LEN(sizeof(srcAddr)); ++ memcpy(CMSG_DATA(chdr), &srcAddr, sizeof(srcAddr)); ++ } ++ else ++#endif + #ifdef IP_PKTINFO + if (localAddress->address.ss_family == AF_INET) { + struct in_pktinfo pktInfo; +@@ -744,6 +767,21 @@ enet_socket_receive (ENetSocket socket, + // to ensure we respond from the correct address/interface. + if (localAddress != NULL) { + for (struct cmsghdr *chdr = CMSG_FIRSTHDR(&msgHdr); chdr != NULL; chdr = CMSG_NXTHDR(&msgHdr, chdr)) { ++#if defined(__FreeBSD__) && defined(IP_RECVDSTADDR) ++ if (chdr->cmsg_level == IPPROTO_IP && chdr->cmsg_type == IP_RECVDSTADDR) { ++ struct sockaddr_in *localAddr = (struct sockaddr_in*)&localAddress->address; ++ ++ localAddr->sin_family = AF_INET; ++ localAddr->sin_addr = *(struct in_addr*)CMSG_DATA(chdr); ++ // FreeBSD doesn't populate sin_port in RECVDSTADDR, but we don't need it ++ // since we only use the address part for routing decisions ++ localAddr->sin_port = 0; ++ ++ localAddress->addressLength = sizeof(*localAddr); ++ break; ++ } ++ else ++#endif + #ifdef IP_PKTINFO + if (chdr->cmsg_level == IPPROTO_IP && chdr->cmsg_type == IP_PKTINFO) { + struct sockaddr_in *localAddr = (struct sockaddr_in*)&localAddress->address; diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index 891fad426..9a823adc9 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -39,9 +39,6 @@ #include #include // Define constants that are missing in FreeBSD - #ifndef IP_PKTINFO // packet info for IPv4 - #define IP_PKTINFO IP_RECVDSTADDR - #endif #ifndef SOL_IP // socket level for IPv4 #define SOL_IP IPPROTO_IP #endif @@ -51,12 +48,6 @@ #ifndef SO_PRIORITY // socket option for priority, disabled for FreeBSD #define SO_PRIORITY -1 #endif -// Define in_pktinfo structure for IPv4 packet info -struct in_pktinfo { - struct in_addr ipi_addr; - struct in_addr ipi_spec_dst; - int ipi_ifindex; -}; #endif #ifdef __GNUC__ @@ -401,7 +392,11 @@ namespace platf { } union { +#ifdef __FreeBSD__ + char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_addr)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; +#else char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; +#endif struct cmsghdr alignment; } cmbuf = {}; // Must be zeroed for CMSG_NXTHDR() @@ -427,6 +422,18 @@ namespace platf { pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); } else { +#ifdef __FreeBSD__ + // FreeBSD uses IP_SENDSRCADDR instead of IP_PKTINFO + struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); + struct in_addr srcAddr = saddr_v4.sin_addr; + + cmbuflen += CMSG_SPACE(sizeof(srcAddr)); + + pktinfo_cm->cmsg_level = IPPROTO_IP; + pktinfo_cm->cmsg_type = IP_SENDSRCADDR; + pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(srcAddr)); + memcpy(CMSG_DATA(pktinfo_cm), &srcAddr, sizeof(srcAddr)); +#else struct in_pktinfo pktInfo; struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); @@ -439,6 +446,7 @@ namespace platf { pktinfo_cm->cmsg_type = IP_PKTINFO; pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); +#endif } auto const max_iovs_per_msg = send_info.payload_buffers.size() + (send_info.headers ? 1 : 0); @@ -608,7 +616,11 @@ namespace platf { } union { +#ifdef __FreeBSD__ + char buf[std::max(CMSG_SPACE(sizeof(struct in_addr)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; +#else char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; +#endif struct cmsghdr alignment; } cmbuf; @@ -632,6 +644,18 @@ namespace platf { pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); } else { +#ifdef __FreeBSD__ + // FreeBSD uses IP_SENDSRCADDR instead of IP_PKTINFO + struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); + struct in_addr srcAddr = saddr_v4.sin_addr; + + cmbuflen += CMSG_SPACE(sizeof(srcAddr)); + + pktinfo_cm->cmsg_level = IPPROTO_IP; + pktinfo_cm->cmsg_type = IP_SENDSRCADDR; + pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(srcAddr)); + memcpy(CMSG_DATA(pktinfo_cm), &srcAddr, sizeof(srcAddr)); +#else struct in_pktinfo pktInfo; struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); @@ -644,6 +668,7 @@ namespace platf { pktinfo_cm->cmsg_type = IP_PKTINFO; pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); +#endif } struct iovec iovs[2]; diff --git a/src/stream.cpp b/src/stream.cpp index 46d58c236..09e5526b0 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -8,6 +8,11 @@ #include #include +#ifdef __FreeBSD__ + #include + #include +#endif + // lib includes #include #include @@ -519,21 +524,11 @@ namespace stream { // Use the local address from the control connection as the source address // for other communications to the client. This is necessary to ensure // proper routing on multi-homed hosts. - - // TODO: delete this debug log - BOOST_LOG(debug) << "Raw localAddress.address: family=" << peer->localAddress.address.ss_family - << " size=" << peer->localAddress.addressLength - << " data=" << util::hex_vec(std::string_view {(char *) &peer->localAddress.address, static_cast(peer->localAddress.addressLength)}); auto local_address = platf::from_sockaddr((sockaddr *) &peer->localAddress.address); if (local_address.empty()) { BOOST_LOG(warning) << "Couldn't get local address for control connection"sv; } - try { - session_p->localAddress = boost::asio::ip::make_address(local_address); - } catch (const boost::system::system_error &e) { - BOOST_LOG(error) << "boost::system::system_error in address parsing: " << e.what() << " (code: " << e.code() << ")"sv; - throw; - } + session_p->localAddress = boost::asio::ip::make_address(local_address); BOOST_LOG(debug) << "Control local address ["sv << local_address << ']'; BOOST_LOG(debug) << "Control peer address ["sv << peer_addr << ':' << peer_port << ']'; @@ -1722,6 +1717,23 @@ namespace stream { return -1; } +#ifdef __FreeBSD__ + // Enable IP_RECVDSTADDR for FreeBSD to receive destination address for IPv4 + { + int on = 1; + if (setsockopt(ctx.video_sock.native_handle(), IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) < 0) { + BOOST_LOG(warning) << "Failed to set IP_RECVDSTADDR on video socket"; + } + } + // Enable IPV6_RECVPKTINFO for IPv6 + if (address_family == net::af_e::BOTH) { + int on = 1; + if (setsockopt(ctx.video_sock.native_handle(), IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { + BOOST_LOG(warning) << "Failed to set IPV6_RECVPKTINFO on video socket"; + } + } +#endif + // Set video socket send buffer size (SO_SENDBUF) to 1MB try { ctx.video_sock.set_option(boost::asio::socket_base::send_buffer_size(1024 * 1024)); @@ -1743,6 +1755,23 @@ namespace stream { return -1; } +#ifdef __FreeBSD__ + // Enable IP_RECVDSTADDR for FreeBSD to receive destination address for IPv4 + { + int on = 1; + if (setsockopt(ctx.audio_sock.native_handle(), IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) < 0) { + BOOST_LOG(warning) << "Failed to set IP_RECVDSTADDR on audio socket"; + } + } + // Enable IPV6_RECVPKTINFO for IPv6 + if (address_family == net::af_e::BOTH) { + int on = 1; + if (setsockopt(ctx.audio_sock.native_handle(), IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { + BOOST_LOG(warning) << "Failed to set IPV6_RECVPKTINFO on audio socket"; + } + } +#endif + ctx.audio_sock.bind(udp::endpoint(protocol, audio_port), ec); if (ec) { BOOST_LOG(fatal) << "Couldn't bind Audio server to port ["sv << audio_port << "]: "sv << ec.message();