mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-30 07:42:32 +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;
|
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")]
|
#[cfg(target_os = "macos")]
|
||||||
pub const FIOCLEX: c_ulong = 0x20006601;
|
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;
|
const MSG_TYPE_SEND_TIME_ACK: u8 = 3;
|
||||||
|
|
||||||
/// Create a new `UdpSocket` for the given port and configure it for PTP.
|
/// Create a new `UdpSocket` for the given port and configure it for PTP.
|
||||||
fn create_socket(port: u16) -> Result<UdpSocket, Error> {
|
fn create_socket(port: u16, iface: &net::InterfaceInfo) -> Result<UdpSocket, Error> {
|
||||||
let socket = net::create_udp_socket(&Ipv4Addr::UNSPECIFIED, port)
|
let socket = net::create_udp_socket(&Ipv4Addr::UNSPECIFIED, port, Some(iface))
|
||||||
.with_context(|| format!("Failed to bind socket to port {}", port))?;
|
.with_context(|| format!("Failed to bind socket to port {}", port))?;
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|
@ -123,9 +123,10 @@ fn run() -> Result<(), Error> {
|
||||||
for iface in &ifaces {
|
for iface in &ifaces {
|
||||||
info!("Binding to interface {}", iface.name);
|
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 =
|
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() {
|
for socket in [&event_socket, &general_socket].iter() {
|
||||||
net::join_multicast_v4(socket, &PTP_MULTICAST_ADDR, iface)
|
net::join_multicast_v4(socket, &PTP_MULTICAST_ADDR, iface)
|
||||||
|
|
|
@ -266,7 +266,11 @@ mod imp {
|
||||||
/// `SO_REUSEPORT` before doing so.
|
/// `SO_REUSEPORT` before doing so.
|
||||||
///
|
///
|
||||||
/// `UdpSocket::bind()` does not allow setting custom options before binding.
|
/// `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;
|
use std::os::unix::io::FromRawFd;
|
||||||
|
|
||||||
/// Helper struct to keep a raw fd and close it on drop
|
/// 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.
|
// SAFETY: A valid socket fd is passed here.
|
||||||
unsafe {
|
unsafe {
|
||||||
set_reuse(fd.0);
|
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.
|
// SAFETY: A valid socket fd is passed together with a valid sockaddr_in and its size.
|
||||||
|
@ -488,6 +495,7 @@ mod imp {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||||
{
|
{
|
||||||
use crate::error::Context;
|
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)]
|
#[cfg(windows)]
|
||||||
|
@ -850,7 +915,11 @@ mod imp {
|
||||||
/// `SO_REUSEPORT` before doing so.
|
/// `SO_REUSEPORT` before doing so.
|
||||||
///
|
///
|
||||||
/// `UdpSocket::bind()` does not allow setting custom options before binding.
|
/// `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;
|
use std::os::windows::io::FromRawSocket;
|
||||||
|
|
||||||
// XXX: Make sure Rust std is calling WSAStartup()
|
// XXX: Make sure Rust std is calling WSAStartup()
|
||||||
|
@ -1026,7 +1095,7 @@ mod test {
|
||||||
&ifaces[0]
|
&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();
|
super::join_multicast_v4(&socket, &std::net::Ipv4Addr::new(224, 0, 0, 1), iface).unwrap();
|
||||||
|
|
||||||
let local_addr = socket.local_addr().unwrap();
|
let local_addr = socket.local_addr().unwrap();
|
||||||
|
|
Loading…
Reference in a new issue