From 8d4843fb5562056f5ca5848e704a33cc3c100b53 Mon Sep 17 00:00:00 2001 From: Zhang Jingqiang Date: Tue, 24 Sep 2024 10:52:53 +0800 Subject: [PATCH] g3-socket: support windows SO_REUSE_UNICASTPORT --- lib/g3-socket/Cargo.toml | 4 +- lib/g3-socket/src/bind.rs | 12 +++-- lib/g3-socket/src/lib.rs | 1 - lib/g3-socket/src/sockopt/mod.rs | 25 ++++++++++ .../src/{sockopt.rs => sockopt/unix.rs} | 15 +++--- lib/g3-socket/src/sockopt/windows.rs | 50 +++++++++++++++++++ 6 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 lib/g3-socket/src/sockopt/mod.rs rename lib/g3-socket/src/{sockopt.rs => sockopt/unix.rs} (64%) create mode 100644 lib/g3-socket/src/sockopt/windows.rs diff --git a/lib/g3-socket/Cargo.toml b/lib/g3-socket/Cargo.toml index d233bddb..420307f1 100644 --- a/lib/g3-socket/Cargo.toml +++ b/lib/g3-socket/Cargo.toml @@ -10,8 +10,6 @@ rust-version = "1.80.0" [dependencies] tokio = { workspace = true, features = ["net"] } socket2 = { version = "0.5", features = ["all"] } +libc.workspace = true fastrand.workspace = true g3-types.workspace = true - -[target.'cfg(unix)'.dependencies] -libc.workspace = true diff --git a/lib/g3-socket/src/bind.rs b/lib/g3-socket/src/bind.rs index 1a0e2531..29baf12a 100644 --- a/lib/g3-socket/src/bind.rs +++ b/lib/g3-socket/src/bind.rs @@ -16,8 +16,6 @@ use std::io; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -#[cfg(any(target_os = "linux", target_os = "android"))] -use std::os::unix::io::AsRawFd; use socket2::{SockAddr, Socket}; @@ -26,6 +24,8 @@ use g3_types::net::InterfaceName; #[cfg(any(target_os = "linux", target_os = "android"))] use super::sockopt::set_bind_address_no_port; +#[cfg(windows)] +use super::sockopt::set_reuse_unicastport; use crate::util::AddressFamily; #[derive(Clone, Copy, Debug, Default)] @@ -65,13 +65,17 @@ impl BindAddr { )); } #[cfg(any(target_os = "linux", target_os = "android"))] - set_bind_address_no_port(socket.as_raw_fd(), true)?; + set_bind_address_no_port(socket, true)?; + #[cfg(windows)] + set_reuse_unicastport(socket, true)?; let addr: SockAddr = SocketAddr::new(*ip, 0).into(); socket.bind(&addr) } #[cfg(any(target_os = "linux", target_os = "android"))] BindAddr::Interface(name) => { - set_bind_address_no_port(socket.as_raw_fd(), true)?; + set_bind_address_no_port(socket, true)?; + #[cfg(windows)] + set_reuse_unicastport(socket, true)?; socket.bind_device(Some(name.as_bytes())) } } diff --git a/lib/g3-socket/src/lib.rs b/lib/g3-socket/src/lib.rs index 7cf2fd77..2016d429 100644 --- a/lib/g3-socket/src/lib.rs +++ b/lib/g3-socket/src/lib.rs @@ -14,7 +14,6 @@ * limitations under the License. */ -#[cfg(any(target_os = "linux", target_os = "android"))] mod sockopt; mod raw; diff --git a/lib/g3-socket/src/sockopt/mod.rs b/lib/g3-socket/src/sockopt/mod.rs new file mode 100644 index 00000000..5cf8475d --- /dev/null +++ b/lib/g3-socket/src/sockopt/mod.rs @@ -0,0 +1,25 @@ +/* + * Copyright 2024 ByteDance and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod unix; +#[cfg(any(target_os = "linux", target_os = "android"))] +pub(crate) use unix::set_bind_address_no_port; + +#[cfg(windows)] +mod windows; +#[cfg(windows)] +pub(crate) use windows::set_reuse_unicastport; diff --git a/lib/g3-socket/src/sockopt.rs b/lib/g3-socket/src/sockopt/unix.rs similarity index 64% rename from lib/g3-socket/src/sockopt.rs rename to lib/g3-socket/src/sockopt/unix.rs index 7458e1eb..af338f49 100644 --- a/lib/g3-socket/src/sockopt.rs +++ b/lib/g3-socket/src/sockopt/unix.rs @@ -1,5 +1,5 @@ /* - * Copyright 2023 ByteDance and/or its affiliates. + * Copyright 2024 ByteDance and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,25 +15,26 @@ */ use std::io; +use std::os::unix::io::AsRawFd; -use libc::{c_int, c_void}; +use libc::{c_int, c_void, socklen_t}; -unsafe fn setsockopt(fd: c_int, opt: c_int, val: c_int, payload: T) -> io::Result<()> +unsafe fn setsockopt(fd: c_int, level: c_int, name: c_int, value: T) -> io::Result<()> where T: Copy, { - let payload = &payload as *const T as *const c_void; - let ret = libc::setsockopt(fd, opt, val, payload, size_of::() as libc::socklen_t); + let payload = &value as *const T as *const c_void; + let ret = libc::setsockopt(fd, level, name, payload, size_of::() as socklen_t); if ret == -1 { return Err(io::Error::last_os_error()); } Ok(()) } -pub(crate) fn set_bind_address_no_port(fd: c_int, enable: bool) -> io::Result<()> { +pub(crate) fn set_bind_address_no_port(fd: &T, enable: bool) -> io::Result<()> { unsafe { setsockopt( - fd, + fd.as_raw_fd(), libc::IPPROTO_IP, libc::IP_BIND_ADDRESS_NO_PORT, enable as c_int, diff --git a/lib/g3-socket/src/sockopt/windows.rs b/lib/g3-socket/src/sockopt/windows.rs new file mode 100644 index 00000000..d51f3c14 --- /dev/null +++ b/lib/g3-socket/src/sockopt/windows.rs @@ -0,0 +1,50 @@ +/* + * Copyright 2024 ByteDance and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::io; +use std::os::windows::io::AsRawSocket; + +use libc::{c_char, c_int, SOCKET}; + +// windows_sys::Win32::Networking::WinSock::SOL_SOCKET +const SOL_SOCKET: i32 = 65535i32; + +// windows_sys::Win32::Networking::WinSock::SO_REUSE_UNICASTPORT +const SO_REUSE_UNICASTPORT: i32 = 12295i32; + +unsafe fn setsockopt(socket: SOCKET, level: c_int, name: c_int, value: T) -> io::Result<()> +where + T: Copy, +{ + let payload = &value as *const T as *const c_char; + let ret = libc::setsockopt(socket, level, name, payload, size_of::() as c_int); + if ret == -1 { + return Err(io::Error::last_os_error()); + } + Ok(()) +} + +pub(crate) fn set_reuse_unicastport(socket: &T, enable: bool) -> io::Result<()> { + unsafe { + setsockopt( + socket.as_raw_socket(), + SOL_SOCKET, + SO_REUSE_UNICASTPORT, + enable as c_int, + )?; + Ok(()) + } +}