mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-22 17:51:16 +00:00
ptp: Listen with different sockets on individual interfaces
This allows us to portably know on which interface a multicast packet arrived, and to send back any packets for that clock on the correct interface. Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2728 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5232>
This commit is contained in:
parent
c161eb973d
commit
967aa2abca
5 changed files with 603 additions and 240 deletions
|
@ -41,6 +41,7 @@ pub mod unix {
|
|||
pub const POLLNVAL: c_short = 0x20;
|
||||
|
||||
pub const IPPROTO_IP: c_int = 0;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
|
@ -54,6 +55,19 @@ pub mod unix {
|
|||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
pub const IP_ADD_MEMBERSHIP: c_int = 19;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "macos",
|
||||
))]
|
||||
pub const IP_MULTICAST_IF: c_int = 9;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const IP_MULTICAST_IF: c_int = 32;
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
pub const IP_MULTICAST_IF: c_int = 16;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
|
@ -822,7 +836,9 @@ pub mod windows {
|
|||
}
|
||||
|
||||
pub const IPPROTO_IP: u32 = 0u32;
|
||||
|
||||
pub const IP_ADD_MEMBERSHIP: u32 = 12u32;
|
||||
pub const IP_MULTICAST_IF: u32 = 9u32;
|
||||
|
||||
pub const SOL_SOCKET: u32 = 65535;
|
||||
pub const SO_REUSEADDR: u32 = 4;
|
||||
|
|
|
@ -8,25 +8,59 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::net::UdpSocket;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum SocketType {
|
||||
EventSocket,
|
||||
GeneralSocket,
|
||||
}
|
||||
|
||||
/// Result of polling the inputs of the `Poll`.
|
||||
///
|
||||
/// Any input that has data available for reading will be set to `true`, potentially multiple
|
||||
/// at once.
|
||||
///
|
||||
/// Note that reading from the sockets is non-blocking but reading from stdin is blocking so
|
||||
/// special care has to be taken to only read as much as is available.
|
||||
pub struct PollResult {
|
||||
pub event_socket: bool,
|
||||
pub general_socket: bool,
|
||||
pub stdin: bool,
|
||||
pub struct PollResult<'a> {
|
||||
ready_sockets: &'a [(usize, SocketType, &'a UdpSocket)],
|
||||
sockets: &'a [(UdpSocket, UdpSocket)],
|
||||
stdin: Option<&'a Stdin>,
|
||||
stdout: &'a Stdout,
|
||||
}
|
||||
|
||||
impl<'a> PollResult<'a> {
|
||||
/// Returns the sockets that are currently ready for reading.
|
||||
pub fn ready_sockets(&self) -> &[(usize, SocketType, &UdpSocket)] {
|
||||
&self.ready_sockets
|
||||
}
|
||||
|
||||
/// Returns the event socket.
|
||||
pub fn event_socket(&self, idx: usize) -> &UdpSocket {
|
||||
&self.sockets[idx].0
|
||||
}
|
||||
|
||||
/// Returns the general socket.
|
||||
pub fn general_socket(&self, idx: usize) -> &UdpSocket {
|
||||
&self.sockets[idx].1
|
||||
}
|
||||
|
||||
/// Returns standard input if there is data to read.
|
||||
pub fn stdin(&self) -> Option<&Stdin> {
|
||||
self.stdin
|
||||
}
|
||||
|
||||
/// Returns standard output.
|
||||
pub fn stdout(&self) -> &Stdout {
|
||||
self.stdout
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod imp {
|
||||
use super::PollResult;
|
||||
use super::{PollResult, SocketType};
|
||||
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
mem,
|
||||
net::UdpSocket,
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
};
|
||||
|
@ -37,10 +71,11 @@ mod imp {
|
|||
///
|
||||
/// This carries the event/general UDP socket and stdin/stdout.
|
||||
pub struct Poll {
|
||||
event_socket: UdpSocket,
|
||||
general_socket: UdpSocket,
|
||||
sockets: Vec<(UdpSocket, UdpSocket)>,
|
||||
stdin: Stdin,
|
||||
stdout: Stdout,
|
||||
pollfd: Vec<pollfd>,
|
||||
results_cache: Vec<(usize, SocketType, &'static UdpSocket)>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -83,7 +118,7 @@ mod imp {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Read for Pipe {
|
||||
impl<'a> Read for &'a Pipe {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// SAFETY: read() requires a valid fd and a mutable buffer with the given size.
|
||||
// The fd is valid by construction as is the buffer.
|
||||
|
@ -122,25 +157,27 @@ mod imp {
|
|||
|
||||
impl Poll {
|
||||
/// Name of the input based on the `struct pollfd` index.
|
||||
fn fd_name(idx: usize) -> &'static str {
|
||||
fn fd_name(idx: usize, len: usize) -> &'static str {
|
||||
match idx {
|
||||
0 => "event socket",
|
||||
1 => "general socket",
|
||||
2 => "stdin",
|
||||
i if i == len - 1 => "stdin",
|
||||
i if i % 2 == 0 => "event socket",
|
||||
i if i % 2 == 1 => "general socket",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Poll` instance from the two sockets.
|
||||
pub fn new(event_socket: UdpSocket, general_socket: UdpSocket) -> Result<Self, Error> {
|
||||
/// Create a new `Poll` instance from the pairs of sockets.
|
||||
pub fn new(sockets: Vec<(UdpSocket, UdpSocket)>) -> Result<Self, Error> {
|
||||
let stdin = Stdin::acquire();
|
||||
let stdout = Stdout::acquire();
|
||||
let n_sockets = sockets.len();
|
||||
|
||||
Ok(Self {
|
||||
event_socket,
|
||||
general_socket,
|
||||
sockets,
|
||||
stdin,
|
||||
stdout,
|
||||
pollfd: Vec::with_capacity(n_sockets * 2 + 1),
|
||||
results_cache: Vec::with_capacity(n_sockets * 2),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -157,57 +194,63 @@ mod imp {
|
|||
|
||||
Ok((
|
||||
Self {
|
||||
event_socket,
|
||||
general_socket,
|
||||
sockets: vec![(event_socket, general_socket)],
|
||||
stdin: Stdin(stdin.read),
|
||||
stdout: Stdout(stdout.write),
|
||||
pollfd: Vec::with_capacity(3),
|
||||
results_cache: Vec::with_capacity(2),
|
||||
},
|
||||
stdin,
|
||||
stdout,
|
||||
))
|
||||
}
|
||||
|
||||
/// Mutable reference to the event socket.
|
||||
pub fn event_socket(&mut self) -> &mut UdpSocket {
|
||||
&mut self.event_socket
|
||||
/// Reference to the event socket.
|
||||
#[allow(unused)]
|
||||
pub fn event_socket(&self, iface: usize) -> &UdpSocket {
|
||||
&self.sockets[iface].0
|
||||
}
|
||||
|
||||
/// Mutable reference to the general socket.
|
||||
pub fn general_socket(&mut self) -> &mut UdpSocket {
|
||||
&mut self.general_socket
|
||||
/// Reference to the general socket.
|
||||
#[allow(unused)]
|
||||
pub fn general_socket(&self, iface: usize) -> &UdpSocket {
|
||||
&self.sockets[iface].1
|
||||
}
|
||||
|
||||
/// Mutable reference to stdin for reading.
|
||||
pub fn stdin(&mut self) -> &mut Stdin {
|
||||
&mut self.stdin
|
||||
/// Reference to stdin for reading.
|
||||
#[allow(unused)]
|
||||
pub fn stdin(&self) -> &Stdin {
|
||||
&self.stdin
|
||||
}
|
||||
|
||||
/// Mutable reference to stdout for writing.
|
||||
pub fn stdout(&mut self) -> &mut Stdout {
|
||||
&mut self.stdout
|
||||
/// Reference to stdout for writing.
|
||||
pub fn stdout(&self) -> &Stdout {
|
||||
&self.stdout
|
||||
}
|
||||
|
||||
/// Poll the event socket, general socket and stdin for available data to read.
|
||||
///
|
||||
/// This blocks until at least one input has data available.
|
||||
pub fn poll(&mut self) -> Result<PollResult, Error> {
|
||||
let mut pollfd = [
|
||||
pollfd {
|
||||
fd: self.event_socket.as_raw_fd(),
|
||||
pub fn poll<'a>(&'a mut self) -> Result<PollResult<'a>, Error> {
|
||||
self.pollfd.clear();
|
||||
for (event_socket, general_socket) in &self.sockets {
|
||||
self.pollfd.push(pollfd {
|
||||
fd: event_socket.as_raw_fd(),
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
pollfd {
|
||||
fd: self.general_socket.as_raw_fd(),
|
||||
});
|
||||
self.pollfd.push(pollfd {
|
||||
fd: general_socket.as_raw_fd(),
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
pollfd {
|
||||
});
|
||||
}
|
||||
|
||||
self.pollfd.push(pollfd {
|
||||
fd: self.stdin.0,
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
// SAFETY: Polls the given pollfds above and requires a valid number to be passed.
|
||||
// A negative timeout means that it will wait until at least one of the pollfds is
|
||||
|
@ -219,7 +262,7 @@ mod imp {
|
|||
// On EINTR polling should be retried.
|
||||
unsafe {
|
||||
loop {
|
||||
let res = poll(pollfd[..].as_mut_ptr(), pollfd.len() as _, -1);
|
||||
let res = poll(self.pollfd[..].as_mut_ptr(), self.pollfd.len() as _, -1);
|
||||
if res == -1 {
|
||||
let err = std::io::Error::last_os_error();
|
||||
if err.kind() == std::io::ErrorKind::Interrupted {
|
||||
|
@ -233,20 +276,60 @@ mod imp {
|
|||
}
|
||||
|
||||
// Check for errors or hangup first
|
||||
for (idx, pfd) in pollfd.iter().enumerate() {
|
||||
for (idx, pfd) in self.pollfd.iter().enumerate() {
|
||||
if pfd.revents & (POLLERR | POLLNVAL) != 0 {
|
||||
bail!("Poll error on {}", Self::fd_name(idx));
|
||||
bail!(
|
||||
"Poll error on {} for interface {}",
|
||||
Self::fd_name(idx, self.pollfd.len()),
|
||||
idx / 2
|
||||
);
|
||||
}
|
||||
|
||||
if pfd.revents & POLLHUP != 0 {
|
||||
bail!("Hang up during polling on {}", Self::fd_name(idx));
|
||||
bail!(
|
||||
"Hang up during polling on {} for interface {}",
|
||||
Self::fd_name(idx, self.pollfd.len()),
|
||||
idx / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.results_cache.clear();
|
||||
// SAFETY: References have the same memory representation independent of lifetime
|
||||
let ready_sockets = unsafe {
|
||||
mem::transmute::<
|
||||
&mut Vec<(usize, SocketType, &'static UdpSocket)>,
|
||||
&mut Vec<(usize, SocketType, &'a UdpSocket)>,
|
||||
>(&mut self.results_cache)
|
||||
};
|
||||
|
||||
for (idx, pfd) in self.pollfd.iter().enumerate() {
|
||||
if pfd.revents & POLLIN != 0 {
|
||||
if idx == self.pollfd.len() - 1 {
|
||||
break;
|
||||
}
|
||||
if idx % 2 == 0 {
|
||||
ready_sockets.push((
|
||||
idx / 2,
|
||||
SocketType::EventSocket,
|
||||
&self.sockets[idx / 2].0,
|
||||
));
|
||||
} else {
|
||||
ready_sockets.push((
|
||||
idx / 2,
|
||||
SocketType::GeneralSocket,
|
||||
&self.sockets[idx / 2].1,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PollResult {
|
||||
event_socket: pollfd[0].revents & POLLIN != 0,
|
||||
general_socket: pollfd[1].revents & POLLIN != 0,
|
||||
stdin: pollfd[2].revents & POLLIN != 0,
|
||||
ready_sockets: &*ready_sockets,
|
||||
sockets: &self.sockets,
|
||||
stdin: (self.pollfd[self.pollfd.len() - 1].revents & POLLIN != 0)
|
||||
.then_some(&self.stdin),
|
||||
stdout: &self.stdout,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +346,12 @@ mod imp {
|
|||
}
|
||||
|
||||
impl Read for Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
<&Stdin as Read>::read(&mut &*self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Read for &'a Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// SAFETY: read() requires a valid fd and a mutable buffer with the given size.
|
||||
// The fd is valid by construction as is the buffer.
|
||||
|
@ -290,6 +379,16 @@ mod imp {
|
|||
}
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
<&Stdout as Write>::write(&mut &*self, buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
<&Stdout as Write>::flush(&mut &*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Write for &Stdout {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// SAFETY: write() requires a valid fd and a mutable buffer with the given size.
|
||||
// The fd is valid by construction as is the buffer.
|
||||
|
@ -326,6 +425,16 @@ mod imp {
|
|||
}
|
||||
|
||||
impl Write for Stderr {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
<&Stderr as Write>::write(&mut &*self, buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
<&Stderr as Write>::flush(&mut &*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Write for &'a Stderr {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// SAFETY: write() requires a valid fd and a mutable buffer with the given size.
|
||||
// The fd is valid by construction as is the buffer.
|
||||
|
@ -348,7 +457,7 @@ mod imp {
|
|||
|
||||
#[cfg(windows)]
|
||||
mod imp {
|
||||
use super::PollResult;
|
||||
use super::{PollResult, SocketType};
|
||||
|
||||
use std::{
|
||||
cmp,
|
||||
|
@ -371,12 +480,12 @@ mod imp {
|
|||
///
|
||||
/// This carries the event/general UDP socket and stdin/stdout.
|
||||
pub struct Poll {
|
||||
event_socket: UdpSocket,
|
||||
event_socket_event: EventHandle,
|
||||
general_socket: UdpSocket,
|
||||
general_socket_event: EventHandle,
|
||||
sockets: Vec<(UdpSocket, UdpSocket)>,
|
||||
events: Vec<(EventHandle, EventHandle)>,
|
||||
stdin: Stdin,
|
||||
stdout: Stdout,
|
||||
handles: Vec<HANDLE>,
|
||||
results_cache: Vec<(usize, SocketType, &'static UdpSocket)>,
|
||||
}
|
||||
|
||||
/// Helper struct for a WSA event.
|
||||
|
@ -456,7 +565,7 @@ mod imp {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Read for Pipe {
|
||||
impl<'a> Read for &'a Pipe {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// SAFETY: Reads the given number of bytes into the buffer from the stdin handle.
|
||||
unsafe {
|
||||
|
@ -510,21 +619,31 @@ mod imp {
|
|||
impl Poll {
|
||||
/// Internal constructor.
|
||||
pub fn new_internal(
|
||||
event_socket: UdpSocket,
|
||||
general_socket: UdpSocket,
|
||||
sockets: Vec<(UdpSocket, UdpSocket)>,
|
||||
stdin: Stdin,
|
||||
stdout: Stdout,
|
||||
) -> Result<Self, Error> {
|
||||
// Create event objects for the readability of the sockets.
|
||||
let event_socket_event = EventHandle::new().context("Failed creating WSA event")?;
|
||||
let general_socket_event = EventHandle::new().context("Failed creating WSA event")?;
|
||||
let events = sockets
|
||||
.iter()
|
||||
.map(|(_event_socket, _general_socket)| -> Result<_, Error> {
|
||||
Ok((
|
||||
EventHandle::new().context("Failed creating WSA event")?,
|
||||
EventHandle::new().context("Failed creating WSA event")?,
|
||||
))
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
|
||||
for ((event_socket, general_socket), (event_socket_event, general_socket_event)) in
|
||||
Iterator::zip(sockets.iter(), events.iter())
|
||||
{
|
||||
// SAFETY: WSAEventSelect() requires a valid socket and WSA event, which are both
|
||||
// passed here, and the bitflag of events that should be selected for.
|
||||
//
|
||||
// On error a non-zero value is returned.
|
||||
unsafe {
|
||||
if WSAEventSelect(event_socket.as_raw_socket(), event_socket_event.0, FD_READ) != 0
|
||||
if WSAEventSelect(event_socket.as_raw_socket(), event_socket_event.0, FD_READ)
|
||||
!= 0
|
||||
{
|
||||
bail!(
|
||||
source: io::Error::from_raw_os_error(WSAGetLastError()),
|
||||
|
@ -544,23 +663,26 @@ mod imp {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let n_sockets = sockets.len();
|
||||
|
||||
Ok(Self {
|
||||
event_socket,
|
||||
event_socket_event,
|
||||
general_socket,
|
||||
general_socket_event,
|
||||
sockets,
|
||||
events,
|
||||
stdin,
|
||||
stdout,
|
||||
handles: Vec::with_capacity(n_sockets * 2 + 1),
|
||||
results_cache: Vec::with_capacity(1),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new `Poll` instance from the two sockets.
|
||||
pub fn new(event_socket: UdpSocket, general_socket: UdpSocket) -> Result<Self, Error> {
|
||||
pub fn new(sockets: Vec<(UdpSocket, UdpSocket)>) -> Result<Self, Error> {
|
||||
let stdin = Stdin::acquire().context("Failure acquiring stdin handle")?;
|
||||
let stdout = Stdout::acquire().context("Failed acquiring stdout handle")?;
|
||||
|
||||
Self::new_internal(event_socket, general_socket, stdin, stdout)
|
||||
Self::new_internal(sockets, stdin, stdout)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -579,45 +701,53 @@ mod imp {
|
|||
let stdout =
|
||||
Stdout::from_handle(stdout_pipe.write).context("Failed acquiring stdout handle")?;
|
||||
|
||||
let poll = Self::new_internal(event_socket, general_socket, stdin, stdout)?;
|
||||
let poll = Self::new_internal(vec![(event_socket, general_socket)], stdin, stdout)?;
|
||||
|
||||
Ok((poll, stdin_pipe, stdout_pipe))
|
||||
}
|
||||
|
||||
/// Mutable reference to the event socket.
|
||||
pub fn event_socket(&mut self) -> &mut UdpSocket {
|
||||
&mut self.event_socket
|
||||
/// Reference to the event socket.
|
||||
#[allow(unused)]
|
||||
pub fn event_socket(&self, iface: usize) -> &UdpSocket {
|
||||
&self.sockets[iface].0
|
||||
}
|
||||
|
||||
/// Mutable reference to the general socket.
|
||||
pub fn general_socket(&mut self) -> &mut UdpSocket {
|
||||
&mut self.general_socket
|
||||
/// Reference to the general socket.
|
||||
#[allow(unused)]
|
||||
pub fn general_socket(&self, iface: usize) -> &UdpSocket {
|
||||
&self.sockets[iface].1
|
||||
}
|
||||
|
||||
/// Mutable reference to stdin for reading.
|
||||
pub fn stdin(&mut self) -> &mut Stdin {
|
||||
&mut self.stdin
|
||||
/// Reference to stdin for reading.
|
||||
#[allow(unused)]
|
||||
pub fn stdin(&self) -> &Stdin {
|
||||
&self.stdin
|
||||
}
|
||||
|
||||
/// Mutable reference to stdout for writing.
|
||||
pub fn stdout(&mut self) -> &mut Stdout {
|
||||
&mut self.stdout
|
||||
/// Reference to stdout for writing.
|
||||
pub fn stdout(&self) -> &Stdout {
|
||||
&self.stdout
|
||||
}
|
||||
|
||||
/// Poll the event socket, general socket and stdin for available data to read.
|
||||
///
|
||||
/// This blocks until at least one input has data available.
|
||||
pub fn poll(&mut self) -> Result<PollResult, Error> {
|
||||
let handles = [
|
||||
self.event_socket_event.0,
|
||||
self.general_socket_event.0,
|
||||
pub fn poll<'a>(&'a mut self) -> Result<PollResult<'a>, Error> {
|
||||
self.handles.clear();
|
||||
|
||||
for (event_socket_event, general_socket_event) in &self.events {
|
||||
self.handles.push(event_socket_event.0);
|
||||
self.handles.push(general_socket_event.0);
|
||||
}
|
||||
|
||||
self.handles.push(
|
||||
// If stdin is a pipe then we use the signalling event, otherwise stdin itself.
|
||||
if let Some(ref thread_state) = self.stdin.thread_state {
|
||||
thread_state.event
|
||||
} else {
|
||||
self.stdin.handle
|
||||
},
|
||||
];
|
||||
);
|
||||
|
||||
// If stdin is a pipe and currently no data is pending on it then signal
|
||||
// the reading thread to try reading one byte and blocking for that long.
|
||||
|
@ -641,8 +771,12 @@ mod imp {
|
|||
// On error u32::MAX is returned, otherwise an index into the array of handles is
|
||||
// returned for the handle that became ready.
|
||||
let res = unsafe {
|
||||
let res =
|
||||
WaitForMultipleObjects(handles.len() as _, handles[..].as_ptr(), 0, u32::MAX);
|
||||
let res = WaitForMultipleObjects(
|
||||
self.handles.len() as _,
|
||||
self.handles[..].as_ptr(),
|
||||
0,
|
||||
u32::MAX,
|
||||
);
|
||||
if res == u32::MAX {
|
||||
bail!(
|
||||
source: io::Error::from_raw_os_error(WSAGetLastError()),
|
||||
|
@ -651,21 +785,30 @@ mod imp {
|
|||
}
|
||||
|
||||
assert!(
|
||||
(0..=2).contains(&res),
|
||||
(0..self.handles.len()).contains(&(res as usize)),
|
||||
"Unexpected WaitForMultipleObjects() return value {}",
|
||||
res,
|
||||
);
|
||||
|
||||
res
|
||||
res as usize
|
||||
};
|
||||
|
||||
self.results_cache.clear();
|
||||
// SAFETY: References have the same memory representation independent of lifetime
|
||||
let ready_sockets = unsafe {
|
||||
mem::transmute::<
|
||||
&mut Vec<(usize, SocketType, &'static UdpSocket)>,
|
||||
&mut Vec<(usize, SocketType, &'a UdpSocket)>,
|
||||
>(&mut self.results_cache)
|
||||
};
|
||||
|
||||
// For the sockets, enumerate the events that woke up the waiting, collect any errors
|
||||
// and reset the event objects.
|
||||
if (0..=1).contains(&res) {
|
||||
let (socket, event) = if res == 0 {
|
||||
(&self.event_socket, &self.event_socket_event)
|
||||
if res < self.handles.len() - 1 {
|
||||
let (socket, event) = if res % 2 == 0 {
|
||||
(&self.sockets[res / 2].0, &self.events[res / 2].0)
|
||||
} else {
|
||||
(&self.general_socket, &self.general_socket_event)
|
||||
(&self.sockets[res / 2].1, &self.events[res / 2].1)
|
||||
};
|
||||
|
||||
// SAFETY: Requires a valid socket and event, which is given by construction here.
|
||||
|
@ -681,8 +824,9 @@ mod imp {
|
|||
{
|
||||
bail!(
|
||||
source: io::Error::from_raw_os_error(WSAGetLastError()),
|
||||
"Failed enumerating network events on {} socket",
|
||||
if res == 0 { "event" } else { "general" },
|
||||
"Failed enumerating network events on {} socket for interface {}",
|
||||
if res % 2 == 0 { "event" } else { "general" },
|
||||
res / 2,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -692,8 +836,9 @@ mod imp {
|
|||
if networkevents.ierrorcode[FD_READ_BIT] != 0 {
|
||||
bail!(
|
||||
source: io::Error::from_raw_os_error(networkevents.ierrorcode[FD_READ_BIT]),
|
||||
"Error on {} socket while waiting for events",
|
||||
"Error on {} socket for interface {} while waiting for events",
|
||||
if res == 0 { "event" } else { "general" },
|
||||
res / 2,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -707,12 +852,22 @@ mod imp {
|
|||
res
|
||||
);
|
||||
}
|
||||
ready_sockets.push((
|
||||
res / 2,
|
||||
if res % 2 == 0 {
|
||||
SocketType::EventSocket
|
||||
} else {
|
||||
SocketType::GeneralSocket
|
||||
},
|
||||
socket,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(PollResult {
|
||||
event_socket: res == 0,
|
||||
general_socket: res == 1,
|
||||
stdin: res == 2,
|
||||
ready_sockets: &*ready_sockets,
|
||||
sockets: &self.sockets,
|
||||
stdin: (res == self.handles.len() - 1).then_some(&self.stdin),
|
||||
stdout: &self.stdout,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -918,6 +1073,12 @@ mod imp {
|
|||
}
|
||||
|
||||
impl Read for Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
<&Stdin as Read>::read(&mut &*self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Read for &'a Stdin {
|
||||
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
|
||||
if buf.is_empty() {
|
||||
return Ok(0);
|
||||
|
@ -926,7 +1087,7 @@ mod imp {
|
|||
// If a read byte is pending from the readiness signalling thread then
|
||||
// read that first here before reading any remaining data.
|
||||
let mut already_read = 0;
|
||||
if let Some(ref mut thread_state) = self.thread_state {
|
||||
if let Some(ref thread_state) = self.thread_state {
|
||||
let mut guard = thread_state.buffer.lock().unwrap();
|
||||
assert!(!guard.fill_buffer);
|
||||
if guard.buffer_filled {
|
||||
|
@ -1013,6 +1174,16 @@ mod imp {
|
|||
}
|
||||
|
||||
impl Write for Stdout {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
<&Stdout as Write>::write(&mut &*self, buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
<&Stdout as Write>::flush(&mut &*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Write for &'a Stdout {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// SAFETY: Writes the given number of bytes to stdout or at most u32::MAX. On error
|
||||
// zero is returned, otherwise the number of bytes written is set accordingly and
|
||||
|
@ -1107,6 +1278,16 @@ mod imp {
|
|||
}
|
||||
|
||||
impl Write for Stderr {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
<&Stderr as Write>::write(&mut &*self, buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
<&Stderr as Write>::flush(&mut &*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Write for &'a Stderr {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if self.0 == INVALID_HANDLE_VALUE {
|
||||
return Ok(buf.len());
|
||||
|
@ -1145,6 +1326,8 @@ pub use self::imp::{Poll, Stderr, Stdin, Stdout};
|
|||
mod test {
|
||||
#[test]
|
||||
fn test_poll() {
|
||||
use super::{Poll, SocketType};
|
||||
|
||||
use std::io::prelude::*;
|
||||
|
||||
let event_socket = std::net::UdpSocket::bind(std::net::SocketAddr::from((
|
||||
|
@ -1167,8 +1350,7 @@ mod test {
|
|||
)))
|
||||
.unwrap();
|
||||
|
||||
let (mut poll, mut stdin, _stdout) =
|
||||
super::Poll::new_test(event_socket, general_socket).unwrap();
|
||||
let (mut poll, mut stdin, _stdout) = Poll::new_test(event_socket, general_socket).unwrap();
|
||||
|
||||
let mut buf = [0u8; 4];
|
||||
|
||||
|
@ -1177,30 +1359,33 @@ mod test {
|
|||
.send_to(&[1, 2, 3, 4], (std::net::Ipv4Addr::LOCALHOST, event_port))
|
||||
.unwrap();
|
||||
let res = poll.poll().unwrap();
|
||||
assert!(res.event_socket);
|
||||
assert!(!res.general_socket);
|
||||
assert!(!res.stdin);
|
||||
assert_eq!(poll.event_socket().recv(&mut buf).unwrap(), 4);
|
||||
assert_eq!(res.ready_sockets().len(), 1);
|
||||
assert_eq!(res.ready_sockets()[0].0, 0);
|
||||
assert_eq!(res.ready_sockets()[0].1, SocketType::EventSocket);
|
||||
assert_eq!(res.ready_sockets()[0].2.recv(&mut buf).unwrap(), 4);
|
||||
assert_eq!(buf, [1, 2, 3, 4]);
|
||||
|
||||
send_socket
|
||||
.send_to(&[1, 2, 3, 4], (std::net::Ipv4Addr::LOCALHOST, general_port))
|
||||
.unwrap();
|
||||
let res = poll.poll().unwrap();
|
||||
assert!(!res.event_socket);
|
||||
assert!(res.general_socket);
|
||||
assert!(!res.stdin);
|
||||
assert_eq!(poll.general_socket().recv(&mut buf).unwrap(), 4);
|
||||
|
||||
assert_eq!(res.ready_sockets().len(), 1);
|
||||
assert_eq!(res.ready_sockets()[0].0, 0);
|
||||
assert_eq!(res.ready_sockets()[0].1, SocketType::GeneralSocket);
|
||||
assert_eq!(res.ready_sockets()[0].2.recv(&mut buf).unwrap(), 4);
|
||||
assert_eq!(buf, [1, 2, 3, 4]);
|
||||
|
||||
stdin.write_all(&[1, 2, 3, 4]).unwrap();
|
||||
let res = poll.poll().unwrap();
|
||||
assert!(!res.event_socket);
|
||||
assert!(!res.general_socket);
|
||||
assert!(res.stdin);
|
||||
poll.stdin().read_exact(&mut buf).unwrap();
|
||||
assert!(res.ready_sockets().is_empty());
|
||||
{
|
||||
let mut stdin = res.stdin();
|
||||
let stdin = stdin.as_mut().unwrap();
|
||||
stdin.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(buf, [1, 2, 3, 4]);
|
||||
}
|
||||
}
|
||||
|
||||
drop(poll);
|
||||
}
|
||||
|
|
|
@ -73,12 +73,8 @@ fn create_socket(port: u16) -> Result<UdpSocket, Error> {
|
|||
Ok(socket)
|
||||
}
|
||||
|
||||
/// Join the multicast groups for PTP on the configured interfaces.
|
||||
fn join_multicast(
|
||||
args: &args::Args,
|
||||
event_socket: &UdpSocket,
|
||||
general_socket: &UdpSocket,
|
||||
) -> Result<[u8; 8], Error> {
|
||||
/// Retrieve the list of interfaces based on the available ones and the arguments.
|
||||
fn list_interfaces(args: &args::Args) -> Result<Vec<net::InterfaceInfo>, Error> {
|
||||
let mut ifaces = net::query_interfaces().context("Failed to query network interfaces")?;
|
||||
if ifaces.is_empty() {
|
||||
bail!("No suitable network interfaces for PTP found");
|
||||
|
@ -115,15 +111,28 @@ fn join_multicast(
|
|||
}
|
||||
}
|
||||
|
||||
for iface in &ifaces {
|
||||
info!("Binding to interface {}", iface.name);
|
||||
Ok(ifaces)
|
||||
}
|
||||
|
||||
for socket in [&event_socket, &general_socket].iter() {
|
||||
fn run() -> Result<(), Error> {
|
||||
let args = args::parse_args().context("Failed parsing commandline parameters")?;
|
||||
|
||||
let ifaces = list_interfaces(&args).context("Failed listing interfaces")?;
|
||||
|
||||
let mut sockets = vec![];
|
||||
for iface in &ifaces {
|
||||
info!("Binding to interface {}", iface.name);
|
||||
|
||||
let event_socket = create_socket(PTP_EVENT_PORT).context("Failed creating event socket")?;
|
||||
let general_socket =
|
||||
create_socket(PTP_GENERAL_PORT).context("Failed creating general socket")?;
|
||||
|
||||
for socket in [&event_socket, &general_socket] {
|
||||
net::join_multicast_v4(socket, &PTP_MULTICAST_ADDR, iface)
|
||||
.context("Failed to join multicast group")?;
|
||||
}
|
||||
|
||||
sockets.push((event_socket, general_socket));
|
||||
}
|
||||
|
||||
let clock_id = if args.clock_id == 0 {
|
||||
|
@ -140,27 +149,13 @@ fn join_multicast(
|
|||
} else {
|
||||
args.clock_id.to_be_bytes()
|
||||
};
|
||||
|
||||
info!("Using clock ID {:?}", clock_id);
|
||||
|
||||
Ok(clock_id)
|
||||
}
|
||||
|
||||
fn run() -> Result<(), Error> {
|
||||
let args = args::parse_args().context("Failed parsing commandline parameters")?;
|
||||
|
||||
let event_socket = create_socket(PTP_EVENT_PORT).context("Failed creating event socket")?;
|
||||
let general_socket =
|
||||
create_socket(PTP_GENERAL_PORT).context("Failed creating general socket")?;
|
||||
|
||||
thread::set_priority().context("Failed to set thread priority")?;
|
||||
|
||||
privileges::drop().context("Failed dropping privileges")?;
|
||||
|
||||
let clock_id = join_multicast(&args, &event_socket, &general_socket)
|
||||
.context("Failed joining multicast groups")?;
|
||||
|
||||
let mut poll = io::Poll::new(event_socket, general_socket).context("Failed creating poller")?;
|
||||
let mut poll = io::Poll::new(sockets).context("Failed creating poller")?;
|
||||
|
||||
// Write clock ID first
|
||||
{
|
||||
|
@ -184,27 +179,20 @@ fn run() -> Result<(), Error> {
|
|||
// We assume that stdout never blocks and stdin receives a complete valid packet whenever it is
|
||||
// ready and never blocks in the middle of a packet.
|
||||
let mut socket_buffer = [0u8; 8192];
|
||||
let mut stdinout_buffer = [0u8; 8192 + 3 + 8];
|
||||
let mut stdinout_buffer = [0u8; 8192 + 4 + 8];
|
||||
|
||||
loop {
|
||||
let poll_res = poll.poll().context("Failed polling")?;
|
||||
|
||||
// If any of the sockets are ready, continue reading packets from them until no more
|
||||
// packets are left and directly forward them to stdout.
|
||||
'next_socket: for idx in [poll_res.event_socket, poll_res.general_socket]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, r)| if *r { Some(idx) } else { None })
|
||||
{
|
||||
let idx = idx as u8;
|
||||
'next_socket: for (idx, type_, socket) in poll_res.ready_sockets() {
|
||||
let idx = *idx;
|
||||
let type_ = *type_;
|
||||
|
||||
// Read all available packets from the socket before going to the next socket.
|
||||
'next_packet: loop {
|
||||
let res = match idx {
|
||||
MSG_TYPE_EVENT => poll.event_socket().recv_from(&mut socket_buffer),
|
||||
MSG_TYPE_GENERAL => poll.general_socket().recv_from(&mut socket_buffer),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let res = socket.recv_from(&mut socket_buffer);
|
||||
|
||||
let (read, addr) = match res {
|
||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
|
@ -213,12 +201,7 @@ fn run() -> Result<(), Error> {
|
|||
Err(err) => {
|
||||
bail!(
|
||||
source: err,
|
||||
"Failed reading from {} socket",
|
||||
if idx == MSG_TYPE_EVENT {
|
||||
"event"
|
||||
} else {
|
||||
"general"
|
||||
}
|
||||
"Failed reading from {:?} socket for interface {}", type_, idx,
|
||||
);
|
||||
}
|
||||
Ok((read, addr)) => (read, addr),
|
||||
|
@ -227,13 +210,10 @@ fn run() -> Result<(), Error> {
|
|||
let recv_time = clock::time();
|
||||
if args.verbose {
|
||||
trace!(
|
||||
"Received {} bytes from {} socket from {} at {}",
|
||||
"Received {} bytes from {:?} socket for interface {} from {} at {}",
|
||||
read,
|
||||
if idx == MSG_TYPE_EVENT {
|
||||
"event"
|
||||
} else {
|
||||
"general"
|
||||
},
|
||||
type_,
|
||||
idx,
|
||||
addr,
|
||||
recv_time,
|
||||
);
|
||||
|
@ -275,18 +255,25 @@ fn run() -> Result<(), Error> {
|
|||
}
|
||||
|
||||
{
|
||||
let mut buf = &mut stdinout_buffer[..(read + 3 + 8)];
|
||||
buf.write_u16be(read as u16 + 8)
|
||||
let mut buf = &mut stdinout_buffer[..(read + 4 + 8)];
|
||||
buf.write_u16be(read as u16 + 1 + 8)
|
||||
.expect("Too small stdout buffer");
|
||||
buf.write_u8(idx).expect("Too small stdout buffer");
|
||||
buf.write_u8(if type_ == io::SocketType::EventSocket {
|
||||
MSG_TYPE_EVENT
|
||||
} else {
|
||||
MSG_TYPE_GENERAL
|
||||
})
|
||||
.expect("Too small stdout buffer");
|
||||
buf.write_u8(idx as u8).expect("Too small stdout buffer");
|
||||
buf.write_u64be(recv_time).expect("Too small stdout buffer");
|
||||
buf.write_all(&socket_buffer[..read])
|
||||
.expect("Too small stdout buffer");
|
||||
assert!(buf.is_empty(), "Too big stdout buffer",);
|
||||
}
|
||||
|
||||
let buf = &stdinout_buffer[..(read + 3 + 8)];
|
||||
poll.stdout()
|
||||
let buf = &stdinout_buffer[..(read + 4 + 8)];
|
||||
poll_res
|
||||
.stdout()
|
||||
.write_all(buf)
|
||||
.context("Failed writing to stdout")?;
|
||||
}
|
||||
|
@ -294,8 +281,8 @@ fn run() -> Result<(), Error> {
|
|||
|
||||
// After handling the sockets check if a packet is available on stdin, read it and forward
|
||||
// it to the corresponding socket.
|
||||
if poll_res.stdin {
|
||||
poll.stdin()
|
||||
if let Some(ref mut stdin) = poll_res.stdin() {
|
||||
stdin
|
||||
.read_exact(&mut stdinout_buffer[0..3])
|
||||
.context("Failed reading packet header from stdin")?;
|
||||
|
||||
|
@ -305,7 +292,7 @@ fn run() -> Result<(), Error> {
|
|||
}
|
||||
let type_ = stdinout_buffer[2];
|
||||
|
||||
poll.stdin()
|
||||
stdin
|
||||
.read_exact(&mut stdinout_buffer[0..size as usize])
|
||||
.context("Failed reading packet body from stdin")?;
|
||||
|
||||
|
@ -314,23 +301,31 @@ fn run() -> Result<(), Error> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if size < 1 + 8 + 34 {
|
||||
bail!("Invalid packet body size");
|
||||
}
|
||||
|
||||
let buf = &mut &stdinout_buffer[..(size as usize)];
|
||||
|
||||
let idx = buf.read_u8().expect("Too small stdin buffer");
|
||||
if idx as usize >= ifaces.len() {
|
||||
warn!("Unexpected stdin message interface index {}", idx);
|
||||
continue;
|
||||
}
|
||||
|
||||
if args.verbose {
|
||||
trace!(
|
||||
"Received {} bytes for {} socket from stdin",
|
||||
"Received {} bytes for {} socket for interface {} from stdin",
|
||||
size,
|
||||
if type_ == MSG_TYPE_EVENT {
|
||||
"event"
|
||||
} else {
|
||||
"general"
|
||||
},
|
||||
idx,
|
||||
);
|
||||
}
|
||||
|
||||
if size < 8 + 34 {
|
||||
bail!("Invalid packet body size");
|
||||
}
|
||||
|
||||
let buf = &mut &stdinout_buffer[..(size as usize)];
|
||||
let main_send_time = buf.read_u64be().expect("Too small stdin buffer");
|
||||
|
||||
// We require that the main process only ever sends valid PTP messages with the clock
|
||||
|
@ -347,11 +342,11 @@ fn run() -> Result<(), Error> {
|
|||
|
||||
let send_time = clock::time();
|
||||
match type_ {
|
||||
MSG_TYPE_EVENT => poll
|
||||
.event_socket()
|
||||
MSG_TYPE_EVENT => poll_res
|
||||
.event_socket(idx as usize)
|
||||
.send_to(buf, (PTP_MULTICAST_ADDR, PTP_EVENT_PORT)),
|
||||
MSG_TYPE_GENERAL => poll
|
||||
.general_socket()
|
||||
MSG_TYPE_GENERAL => poll_res
|
||||
.general_socket(idx as usize)
|
||||
.send_to(buf, (PTP_MULTICAST_ADDR, PTP_GENERAL_PORT)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -393,7 +388,8 @@ fn run() -> Result<(), Error> {
|
|||
}
|
||||
|
||||
let buf = &stdinout_buffer[..(3 + 12)];
|
||||
poll.stdout()
|
||||
poll_res
|
||||
.stdout()
|
||||
.write_all(buf)
|
||||
.context("Failed writing to stdout")?;
|
||||
}
|
||||
|
|
|
@ -399,6 +399,93 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "openbsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "netbsd",
|
||||
target_os = "macos"
|
||||
)))]
|
||||
{
|
||||
let mreqn = ip_mreqn {
|
||||
imr_multiaddr: in_addr {
|
||||
s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
|
||||
},
|
||||
imr_address: in_addr {
|
||||
s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
|
||||
},
|
||||
imr_ifindex: iface.index as _,
|
||||
};
|
||||
|
||||
// SAFETY: Requires a valid ip_mreq or ip_mreqn struct to be passed together
|
||||
// with its size for checking which of the two it is. On errors a negative
|
||||
// integer is returned.
|
||||
unsafe {
|
||||
if setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
IPPROTO_IP,
|
||||
IP_MULTICAST_IF,
|
||||
&mreqn as *const _ as *const _,
|
||||
mem::size_of_val(&mreqn) as _,
|
||||
) < 0
|
||||
{
|
||||
bail!(
|
||||
source: io::Error::last_os_error(),
|
||||
"Failed joining multicast group for interface {}",
|
||||
iface.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(any(target_os = "openbsd", target_os = "dragonfly", target_os = "macos"))]
|
||||
{
|
||||
let addr = in_addr {
|
||||
s_addr: u32::from_ne_bytes(iface.ip_addr.octets()),
|
||||
};
|
||||
|
||||
// SAFETY: Requires a valid in_addr struct to be passed together with its size for
|
||||
// checking which of the two it is. On errors a negative integer is returned.
|
||||
unsafe {
|
||||
if setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
IPPROTO_IP,
|
||||
IP_MULTICAST_IF,
|
||||
&addr as *const _ as *const _,
|
||||
mem::size_of_val(&addr) as _,
|
||||
) < 0
|
||||
{
|
||||
bail!(
|
||||
source: io::Error::last_os_error(),
|
||||
"Failed joining multicast group for interface {}",
|
||||
iface.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "netbsd")]
|
||||
{
|
||||
let idx = (iface.index as u32).to_be();
|
||||
|
||||
// SAFETY: Requires a valid in_addr struct or interface index in network byte order
|
||||
// to be passed together with its size for checking which of the two it is. On
|
||||
// errors a negative integer is returned.
|
||||
unsafe {
|
||||
if setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
IPPROTO_IP,
|
||||
IP_MULTICAST_IF,
|
||||
&idx as *const _ as *const _,
|
||||
mem::size_of_val(&idx) as _,
|
||||
) < 0
|
||||
{
|
||||
bail!(
|
||||
source: io::Error::last_os_error(),
|
||||
"Failed joining multicast group for interface {}",
|
||||
iface.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
|
@ -414,6 +501,29 @@ mod imp {
|
|||
)
|
||||
})?;
|
||||
|
||||
let addr = in_addr {
|
||||
s_addr: u32::from_ne_bytes(iface.ip_addr.octets()),
|
||||
};
|
||||
|
||||
// SAFETY: Requires a valid in_addr struct to be passed together with its size for
|
||||
// checking which of the two it is. On errors a negative integer is returned.
|
||||
unsafe {
|
||||
if setsockopt(
|
||||
socket.as_raw_fd(),
|
||||
IPPROTO_IP,
|
||||
IP_MULTICAST_IF,
|
||||
&addr as *const _ as *const _,
|
||||
mem::size_of_val(&addr) as _,
|
||||
) < 0
|
||||
{
|
||||
bail!(
|
||||
source: io::Error::last_os_error(),
|
||||
"Failed setting multicast interface {}",
|
||||
iface.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -838,6 +948,31 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
let addr = IN_ADDR {
|
||||
S_un: IN_ADDR_0 {
|
||||
S_addr: u32::from_ne_bytes(Ipv4Addr::new(0, 0, 0, iface.index as u8).octets()),
|
||||
},
|
||||
};
|
||||
|
||||
// SAFETY: Requires a valid IN_ADDR struct to be passed together with its size for checking
|
||||
// which of the two it is. On errors a negative integer is returned.
|
||||
unsafe {
|
||||
if setsockopt(
|
||||
socket.as_raw_socket(),
|
||||
IPPROTO_IP as i32,
|
||||
IP_MULTICAST_IF as i32,
|
||||
&addr as *const _ as *const _,
|
||||
mem::size_of_val(&addr) as _,
|
||||
) < 0
|
||||
{
|
||||
bail!(
|
||||
source: io::Error::last_os_error(),
|
||||
"Failed joining multicast group for interface {}",
|
||||
iface.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -239,8 +239,8 @@ typedef struct
|
|||
|
||||
typedef enum
|
||||
{
|
||||
TYPE_EVENT = 0, /* 64-bit monotonic clock time and PTP message is payload */
|
||||
TYPE_GENERAL = 1, /* 64-bit monotonic clock time and PTP message is payload */
|
||||
TYPE_EVENT = 0, /* 8-bit interface index, 64-bit monotonic clock time and PTP message is payload */
|
||||
TYPE_GENERAL = 1, /* 8-bit interface index, 64-bit monotonic clock time and PTP message is payload */
|
||||
TYPE_CLOCK_ID = 2, /* 64-bit clock ID is payload */
|
||||
TYPE_SEND_TIME_ACK = 3, /* 64-bit monotonic clock time, 8-bit message type, 8-bit domain number and 16-bit sequence number is payload */
|
||||
} StdIOMessageType;
|
||||
|
@ -292,6 +292,7 @@ typedef struct
|
|||
GstClockTime receive_time;
|
||||
|
||||
PtpClockIdentity master_clock_identity;
|
||||
guint8 iface_idx;
|
||||
|
||||
guint8 grandmaster_priority_1;
|
||||
PtpClockQuality grandmaster_clock_quality;
|
||||
|
@ -306,6 +307,7 @@ typedef struct
|
|||
typedef struct
|
||||
{
|
||||
PtpClockIdentity master_clock_identity;
|
||||
guint8 iface_idx;
|
||||
|
||||
GstClockTime announce_interval; /* last interval we received */
|
||||
GQueue announce_messages;
|
||||
|
@ -322,6 +324,7 @@ typedef struct
|
|||
GstClockTime follow_up_recv_time_local;
|
||||
|
||||
GSource *timeout_source;
|
||||
guint8 iface_idx;
|
||||
guint16 delay_req_seqnum;
|
||||
GstClockTime delay_req_send_time_local; /* t3, -1 if we wait for FOLLOW_UP */
|
||||
GstClockTime delay_req_recv_time_remote; /* t4, -1 if we wait */
|
||||
|
@ -355,6 +358,7 @@ typedef struct
|
|||
/* Last selected master clock */
|
||||
gboolean have_master_clock;
|
||||
PtpClockIdentity master_clock_identity;
|
||||
guint8 iface_idx;
|
||||
guint64 grandmaster_identity;
|
||||
|
||||
/* Last SYNC or FOLLOW_UP timestamp we received */
|
||||
|
@ -722,8 +726,11 @@ compare_announce_message (const PtpAnnounceMessage * a,
|
|||
else if (a->master_clock_identity.port_number >
|
||||
b->master_clock_identity.port_number)
|
||||
return 1;
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
if (a->iface_idx < b->iface_idx)
|
||||
return -1;
|
||||
else if (a->iface_idx > b->iface_idx)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -819,13 +826,15 @@ select_best_master_clock (PtpDomainData * domain, GstClockTime now)
|
|||
|
||||
if (domain->have_master_clock
|
||||
&& compare_clock_identity (&domain->master_clock_identity,
|
||||
&best->master_clock_identity) == 0) {
|
||||
&best->master_clock_identity) == 0
|
||||
&& domain->iface_idx == best->iface_idx) {
|
||||
GST_DEBUG ("Master clock in domain %u did not change", domain->domain);
|
||||
} else {
|
||||
GST_DEBUG ("Selected master clock for domain %u: 0x%016" G_GINT64_MODIFIER
|
||||
"x %u with grandmaster clock 0x%016" G_GINT64_MODIFIER "x",
|
||||
domain->domain, best->master_clock_identity.clock_identity,
|
||||
best->master_clock_identity.port_number, best->grandmaster_identity);
|
||||
"x %u on interface %u with grandmaster clock 0x%016" G_GINT64_MODIFIER
|
||||
"x", domain->domain, best->master_clock_identity.clock_identity,
|
||||
best->master_clock_identity.port_number, best->iface_idx,
|
||||
best->grandmaster_identity);
|
||||
|
||||
domain->have_master_clock = TRUE;
|
||||
domain->grandmaster_identity = best->grandmaster_identity;
|
||||
|
@ -833,9 +842,11 @@ select_best_master_clock (PtpDomainData * domain, GstClockTime now)
|
|||
/* Opportunistic master clock selection likely gave us the same master
|
||||
* clock before, no need to reset all statistics */
|
||||
if (compare_clock_identity (&domain->master_clock_identity,
|
||||
&best->master_clock_identity) != 0) {
|
||||
&best->master_clock_identity) != 0
|
||||
|| domain->iface_idx != best->iface_idx) {
|
||||
memcpy (&domain->master_clock_identity, &best->master_clock_identity,
|
||||
sizeof (PtpClockIdentity));
|
||||
domain->iface_idx = best->iface_idx;
|
||||
domain->mean_path_delay = 0;
|
||||
domain->last_delay_req = 0;
|
||||
domain->last_path_delays_missing = 9;
|
||||
|
@ -865,7 +876,8 @@ select_best_master_clock (PtpDomainData * domain, GstClockTime now)
|
|||
}
|
||||
|
||||
static void
|
||||
handle_announce_message (PtpMessage * msg, GstClockTime receive_time)
|
||||
handle_announce_message (PtpMessage * msg, guint8 iface_idx,
|
||||
GstClockTime receive_time)
|
||||
{
|
||||
GList *l;
|
||||
PtpDomainData *domain = NULL;
|
||||
|
@ -925,7 +937,7 @@ handle_announce_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
PtpAnnounceSender *tmp = l->data;
|
||||
|
||||
if (compare_clock_identity (&tmp->master_clock_identity,
|
||||
&msg->source_port_identity) == 0) {
|
||||
&msg->source_port_identity) == 0 && tmp->iface_idx == iface_idx) {
|
||||
sender = tmp;
|
||||
break;
|
||||
}
|
||||
|
@ -936,6 +948,7 @@ handle_announce_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
|
||||
memcpy (&sender->master_clock_identity, &msg->source_port_identity,
|
||||
sizeof (PtpClockIdentity));
|
||||
sender->iface_idx = iface_idx;
|
||||
g_queue_init (&sender->announce_messages);
|
||||
domain->announce_senders =
|
||||
g_list_prepend (domain->announce_senders, sender);
|
||||
|
@ -968,6 +981,7 @@ handle_announce_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
announce->sequence_id = msg->sequence_id;
|
||||
memcpy (&announce->master_clock_identity, &msg->source_port_identity,
|
||||
sizeof (PtpClockIdentity));
|
||||
announce->iface_idx = iface_idx;
|
||||
announce->grandmaster_identity =
|
||||
msg->message_specific.announce.grandmaster_identity;
|
||||
announce->grandmaster_priority_1 =
|
||||
|
@ -991,7 +1005,7 @@ handle_announce_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
static gboolean
|
||||
send_delay_req_timeout (PtpPendingSync * sync)
|
||||
{
|
||||
guint8 message[STDIO_MESSAGE_HEADER_SIZE + 8 + 44] = { 0, };
|
||||
guint8 message[STDIO_MESSAGE_HEADER_SIZE + 1 + 8 + 44] = { 0, };
|
||||
GstByteWriter writer;
|
||||
gsize written;
|
||||
GError *err = NULL;
|
||||
|
@ -1003,8 +1017,9 @@ send_delay_req_timeout (PtpPendingSync * sync)
|
|||
gst_clock_get_time (observation_system_clock);
|
||||
|
||||
gst_byte_writer_init_with_data (&writer, message, sizeof (message), FALSE);
|
||||
gst_byte_writer_put_uint16_be_unchecked (&writer, 8 + 44);
|
||||
gst_byte_writer_put_uint16_be_unchecked (&writer, 1 + 8 + 44);
|
||||
gst_byte_writer_put_uint8_unchecked (&writer, TYPE_EVENT);
|
||||
gst_byte_writer_put_uint8_unchecked (&writer, sync->iface_idx);
|
||||
gst_byte_writer_put_uint64_be_unchecked (&writer, send_time);
|
||||
gst_byte_writer_put_uint8_unchecked (&writer, PTP_MESSAGE_TYPE_DELAY_REQ);
|
||||
gst_byte_writer_put_uint8_unchecked (&writer, 2);
|
||||
|
@ -1058,6 +1073,7 @@ send_delay_req (PtpDomainData * domain, PtpPendingSync * sync)
|
|||
}
|
||||
|
||||
domain->last_delay_req = now;
|
||||
sync->iface_idx = domain->iface_idx;
|
||||
sync->delay_req_seqnum = domain->last_delay_req_seqnum++;
|
||||
|
||||
/* IEEE 1588 9.5.11.2 */
|
||||
|
@ -1481,7 +1497,8 @@ out:
|
|||
}
|
||||
|
||||
static void
|
||||
handle_sync_message (PtpMessage * msg, GstClockTime receive_time)
|
||||
handle_sync_message (PtpMessage * msg, guint8 iface_idx,
|
||||
GstClockTime receive_time)
|
||||
{
|
||||
GList *l;
|
||||
PtpDomainData *domain = NULL;
|
||||
|
@ -1523,15 +1540,20 @@ handle_sync_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
|
||||
/* If we have a master clock, ignore this message if it's not coming from there */
|
||||
if (domain->have_master_clock
|
||||
&& compare_clock_identity (&domain->master_clock_identity,
|
||||
&msg->source_port_identity) != 0)
|
||||
&& (compare_clock_identity (&domain->master_clock_identity,
|
||||
&msg->source_port_identity) != 0
|
||||
|| domain->iface_idx != iface_idx)) {
|
||||
GST_TRACE ("SYNC msg not from current clock master. Ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_OPPORTUNISTIC_CLOCK_SELECTION
|
||||
/* Opportunistic selection of master clock */
|
||||
if (!domain->have_master_clock)
|
||||
if (!domain->have_master_clock) {
|
||||
memcpy (&domain->master_clock_identity, &msg->source_port_identity,
|
||||
sizeof (PtpClockIdentity));
|
||||
domain->iface_idx = iface_idx;
|
||||
}
|
||||
#else
|
||||
if (!domain->have_master_clock)
|
||||
return;
|
||||
|
@ -1613,7 +1635,8 @@ handle_sync_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
}
|
||||
|
||||
static void
|
||||
handle_follow_up_message (PtpMessage * msg, GstClockTime receive_time)
|
||||
handle_follow_up_message (PtpMessage * msg, guint8 iface_idx,
|
||||
GstClockTime receive_time)
|
||||
{
|
||||
GList *l;
|
||||
PtpDomainData *domain = NULL;
|
||||
|
@ -1643,8 +1666,9 @@ handle_follow_up_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
|
||||
/* If we have a master clock, ignore this message if it's not coming from there */
|
||||
if (domain->have_master_clock
|
||||
&& compare_clock_identity (&domain->master_clock_identity,
|
||||
&msg->source_port_identity) != 0) {
|
||||
&& (compare_clock_identity (&domain->master_clock_identity,
|
||||
&msg->source_port_identity) != 0
|
||||
|| domain->iface_idx != iface_idx)) {
|
||||
GST_TRACE ("FOLLOW_UP msg not from current clock master. Ignoring");
|
||||
return;
|
||||
}
|
||||
|
@ -1709,7 +1733,8 @@ handle_follow_up_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
}
|
||||
|
||||
static void
|
||||
handle_delay_resp_message (PtpMessage * msg, GstClockTime receive_time)
|
||||
handle_delay_resp_message (PtpMessage * msg, guint8 iface_idx,
|
||||
GstClockTime receive_time)
|
||||
{
|
||||
GList *l;
|
||||
PtpDomainData *domain = NULL;
|
||||
|
@ -1740,9 +1765,12 @@ handle_delay_resp_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
|
||||
/* If we have a master clock, ignore this message if it's not coming from there */
|
||||
if (domain->have_master_clock
|
||||
&& compare_clock_identity (&domain->master_clock_identity,
|
||||
&msg->source_port_identity) != 0)
|
||||
&& (compare_clock_identity (&domain->master_clock_identity,
|
||||
&msg->source_port_identity) != 0
|
||||
|| domain->iface_idx != iface_idx)) {
|
||||
GST_TRACE ("DELAY_RESP msg not from current clock master. Ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg->log_message_interval == 0x7f) {
|
||||
domain->min_delay_req_interval = GST_SECOND;
|
||||
|
@ -1809,7 +1837,8 @@ handle_delay_resp_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
}
|
||||
|
||||
static void
|
||||
handle_ptp_message (PtpMessage * msg, GstClockTime receive_time)
|
||||
handle_ptp_message (PtpMessage * msg, guint8 iface_idx,
|
||||
GstClockTime receive_time)
|
||||
{
|
||||
/* Ignore our own messages */
|
||||
if (msg->source_port_identity.clock_identity == ptp_clock_id.clock_identity &&
|
||||
|
@ -1818,20 +1847,20 @@ handle_ptp_message (PtpMessage * msg, GstClockTime receive_time)
|
|||
return;
|
||||
}
|
||||
|
||||
GST_TRACE ("Message type %d receive_time %" GST_TIME_FORMAT,
|
||||
msg->message_type, GST_TIME_ARGS (receive_time));
|
||||
GST_TRACE ("Message type %d iface idx %d receive_time %" GST_TIME_FORMAT,
|
||||
msg->message_type, iface_idx, GST_TIME_ARGS (receive_time));
|
||||
switch (msg->message_type) {
|
||||
case PTP_MESSAGE_TYPE_ANNOUNCE:
|
||||
handle_announce_message (msg, receive_time);
|
||||
handle_announce_message (msg, iface_idx, receive_time);
|
||||
break;
|
||||
case PTP_MESSAGE_TYPE_SYNC:
|
||||
handle_sync_message (msg, receive_time);
|
||||
handle_sync_message (msg, iface_idx, receive_time);
|
||||
break;
|
||||
case PTP_MESSAGE_TYPE_FOLLOW_UP:
|
||||
handle_follow_up_message (msg, receive_time);
|
||||
handle_follow_up_message (msg, iface_idx, receive_time);
|
||||
break;
|
||||
case PTP_MESSAGE_TYPE_DELAY_RESP:
|
||||
handle_delay_resp_message (msg, receive_time);
|
||||
handle_delay_resp_message (msg, iface_idx, receive_time);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1941,12 +1970,14 @@ have_stdout_body (GInputStream * stdout_pipe, GAsyncResult * res,
|
|||
case TYPE_EVENT:
|
||||
case TYPE_GENERAL:{
|
||||
GstClockTime receive_time = gst_clock_get_time (observation_system_clock);
|
||||
guint8 iface_idx;
|
||||
GstClockTime helper_receive_time;
|
||||
PtpMessage msg;
|
||||
|
||||
helper_receive_time = GST_READ_UINT64_BE (stdout_buffer);
|
||||
iface_idx = GST_READ_UINT8 (stdout_buffer);
|
||||
helper_receive_time = GST_READ_UINT64_BE (stdout_buffer + 1);
|
||||
|
||||
if (parse_ptp_message (&msg, (const guint8 *) stdout_buffer + 8,
|
||||
if (parse_ptp_message (&msg, (const guint8 *) stdout_buffer + 1 + 8,
|
||||
CUR_STDIO_HEADER_SIZE)) {
|
||||
dump_ptp_message (&msg);
|
||||
if (helper_receive_time != 0) {
|
||||
|
@ -1955,7 +1986,7 @@ have_stdout_body (GInputStream * stdout_pipe, GAsyncResult * res,
|
|||
helper_receive_time)));
|
||||
receive_time = helper_receive_time;
|
||||
}
|
||||
handle_ptp_message (&msg, receive_time);
|
||||
handle_ptp_message (&msg, iface_idx, receive_time);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue