mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +00:00
ptp: Use SO_BINDTOIFINDEX / SO_BINDTODEVICE on Linux
This makes sure we really really really only get packets from the desired interface as passing a device to IP_ADD_MEMBERSHIP apparently does not have this effect alone. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5584>
This commit is contained in:
parent
f6ffe34ad5
commit
ffa30637c4
3 changed files with 83 additions and 7 deletions
|
@ -99,6 +99,12 @@ pub mod unix {
|
|||
))]
|
||||
pub const SOL_SOCKET: c_int = 1;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const SO_BINDTODEVICE: c_int = 25;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const SO_BINDTOIFINDEX: c_int = 62;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub const FIOCLEX: c_ulong = 0x20006601;
|
||||
|
||||
|
|
|
@ -58,8 +58,8 @@ const MSG_TYPE_CLOCK_ID: u8 = 2;
|
|||
const MSG_TYPE_SEND_TIME_ACK: u8 = 3;
|
||||
|
||||
/// Create a new `UdpSocket` for the given port and configure it for PTP.
|
||||
fn create_socket(port: u16) -> Result<UdpSocket, Error> {
|
||||
let socket = net::create_udp_socket(&Ipv4Addr::UNSPECIFIED, port)
|
||||
fn create_socket(port: u16, iface: &net::InterfaceInfo) -> Result<UdpSocket, Error> {
|
||||
let socket = net::create_udp_socket(&Ipv4Addr::UNSPECIFIED, port, Some(iface))
|
||||
.with_context(|| format!("Failed to bind socket to port {}", port))?;
|
||||
|
||||
socket
|
||||
|
@ -123,9 +123,10 @@ fn run() -> Result<(), Error> {
|
|||
for iface in &ifaces {
|
||||
info!("Binding to interface {}", iface.name);
|
||||
|
||||
let event_socket = create_socket(PTP_EVENT_PORT).context("Failed creating event socket")?;
|
||||
let event_socket =
|
||||
create_socket(PTP_EVENT_PORT, iface).context("Failed creating event socket")?;
|
||||
let general_socket =
|
||||
create_socket(PTP_GENERAL_PORT).context("Failed creating general socket")?;
|
||||
create_socket(PTP_GENERAL_PORT, iface).context("Failed creating general socket")?;
|
||||
|
||||
for socket in [&event_socket, &general_socket].iter() {
|
||||
net::join_multicast_v4(socket, &PTP_MULTICAST_ADDR, iface)
|
||||
|
|
|
@ -266,7 +266,11 @@ mod imp {
|
|||
/// `SO_REUSEPORT` before doing so.
|
||||
///
|
||||
/// `UdpSocket::bind()` does not allow setting custom options before binding.
|
||||
pub fn create_udp_socket(addr: &Ipv4Addr, port: u16) -> Result<UdpSocket, io::Error> {
|
||||
pub fn create_udp_socket(
|
||||
addr: &Ipv4Addr,
|
||||
port: u16,
|
||||
iface: Option<&InterfaceInfo>,
|
||||
) -> Result<UdpSocket, io::Error> {
|
||||
use std::os::unix::io::FromRawFd;
|
||||
|
||||
/// Helper struct to keep a raw fd and close it on drop
|
||||
|
@ -328,6 +332,9 @@ mod imp {
|
|||
// SAFETY: A valid socket fd is passed here.
|
||||
unsafe {
|
||||
set_reuse(fd.0);
|
||||
if let Some(iface) = iface {
|
||||
bind_to_interface(fd.0, iface);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A valid socket fd is passed together with a valid sockaddr_in and its size.
|
||||
|
@ -488,6 +495,7 @@ mod imp {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
{
|
||||
use crate::error::Context;
|
||||
|
@ -576,6 +584,63 @@ mod imp {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bind the socket to a specific interface.
|
||||
///
|
||||
/// This is best-effort and might not actually do anything.
|
||||
///
|
||||
/// SAFETY: Must be called with a valid socket fd.
|
||||
#[cfg_attr(not(target_os = "linux"), allow(unused_variables))]
|
||||
unsafe fn bind_to_interface(socket: i32, iface: &InterfaceInfo) {
|
||||
// On Linux, go one step further and bind the socket completely to the socket if we
|
||||
// can, i.e. have the relevant permissions.
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// SAFETY: The socket passed in must be valid and the SO_BINDTOIFINDEX socket option
|
||||
// takes an `i32` that represents the interface index as parameter.
|
||||
let res = unsafe {
|
||||
let v = iface.index as i32;
|
||||
setsockopt(
|
||||
socket,
|
||||
SOL_SOCKET,
|
||||
SO_BINDTOIFINDEX,
|
||||
&v as *const _ as *const _,
|
||||
mem::size_of_val(&v) as u32,
|
||||
)
|
||||
};
|
||||
|
||||
if res < 0 {
|
||||
warn!("Failed to set SO_BINDTOIFINDEX on socket, trying SO_BINDTODEVICE");
|
||||
|
||||
if iface.name.len() >= 16 {
|
||||
warn!(
|
||||
"Interface name '{}' too long for SO_BINDTODEVICE",
|
||||
iface.name
|
||||
);
|
||||
} else {
|
||||
// SAFETY: The socket passed in must be valid and the SO_BINDTODEVICE socket option
|
||||
// takes a NUL-terminated byte array of up to 16 bytes as parameter.
|
||||
unsafe {
|
||||
let mut v = [0u8; 16];
|
||||
|
||||
v[..iface.name.len()].copy_from_slice(iface.name.as_bytes());
|
||||
|
||||
let res = setsockopt(
|
||||
socket,
|
||||
SOL_SOCKET,
|
||||
SO_BINDTODEVICE,
|
||||
&v as *const _ as *const _,
|
||||
(iface.name.len() + 1) as u32,
|
||||
);
|
||||
|
||||
if res < 0 {
|
||||
warn!("Failed to set SO_BINDTODEVICE on socket");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
@ -850,7 +915,11 @@ mod imp {
|
|||
/// `SO_REUSEPORT` before doing so.
|
||||
///
|
||||
/// `UdpSocket::bind()` does not allow setting custom options before binding.
|
||||
pub fn create_udp_socket(addr: &Ipv4Addr, port: u16) -> Result<UdpSocket, io::Error> {
|
||||
pub fn create_udp_socket(
|
||||
addr: &Ipv4Addr,
|
||||
port: u16,
|
||||
_iface: Option<&InterfaceInfo>,
|
||||
) -> Result<UdpSocket, io::Error> {
|
||||
use std::os::windows::io::FromRawSocket;
|
||||
|
||||
// XXX: Make sure Rust std is calling WSAStartup()
|
||||
|
@ -1026,7 +1095,7 @@ mod test {
|
|||
&ifaces[0]
|
||||
};
|
||||
|
||||
let socket = super::create_udp_socket(&std::net::Ipv4Addr::UNSPECIFIED, 0).unwrap();
|
||||
let socket = super::create_udp_socket(&std::net::Ipv4Addr::UNSPECIFIED, 0, None).unwrap();
|
||||
super::join_multicast_v4(&socket, &std::net::Ipv4Addr::new(224, 0, 0, 1), iface).unwrap();
|
||||
|
||||
let local_addr = socket.local_addr().unwrap();
|
||||
|
|
Loading…
Reference in a new issue