Skip to content

Commit 01115e4

Browse files
committed
Initial implementation of networking for CSPTP/PTP
1 parent f2a3a30 commit 01115e4

12 files changed

Lines changed: 1529 additions & 3 deletions

File tree

Cargo.lock

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
members = [
33
"ntp-proto",
44
"ntpd"
5-
, "statime-wire"]
5+
, "statime-wire", "statime-netptp"]
66
exclude = [ ]
77

88
# Properly take compiler version into account when resolving crates.

statime-netptp/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "statime-netptp"
3+
version.workspace = true
4+
edition.workspace = true
5+
license.workspace = true
6+
repository.workspace = true
7+
homepage.workspace = true
8+
readme.workspace = true
9+
description.workspace = true
10+
publish.workspace = true
11+
rust-version.workspace = true
12+
13+
[dependencies]
14+
timestamped-socket.workspace = true
15+
tokio = { workspace = true, features = ["sync"] }
16+
tracing.workspace = true

statime-netptp/src/addresses.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use std::{
2+
io::Result,
3+
task::{Context, Poll},
4+
};
5+
6+
use timestamped_socket::{
7+
interface::InterfaceName,
8+
socket::{FullTimestampData, SendTimestampToken},
9+
};
10+
11+
mod ipv4;
12+
mod ipv6;
13+
14+
/// An address type which can be used for PTP/CSPTP traffic.
15+
#[expect(private_bounds)]
16+
pub trait PtpAddressFamily: SealedPtpAddressFamily + Copy + Eq + 'static {}
17+
18+
pub(crate) trait SealedPtpAddressFamily {
19+
type BoundInterface: BoundInterface<Addr = Self>;
20+
}
21+
22+
pub(crate) trait BoundInterface: Sized {
23+
type Addr;
24+
25+
fn open(interface: Option<InterfaceName>, hardware_clock: Option<u32>) -> Result<Self>;
26+
27+
fn poll_send_event(
28+
&self,
29+
buf: &[u8],
30+
from: Option<Self::Addr>,
31+
to: Self::Addr,
32+
cx: &mut Context,
33+
) -> Poll<Result<SendTimestampToken>>;
34+
fn poll_send_general(
35+
&self,
36+
buf: &[u8],
37+
from: Option<Self::Addr>,
38+
to: Self::Addr,
39+
cx: &mut Context,
40+
) -> Poll<Result<()>>;
41+
fn poll_recv(
42+
&self,
43+
buf: &mut [u8],
44+
cx: &mut Context,
45+
) -> Poll<Result<timestamped_socket::socket::RecvResult<Self::Addr>>>;
46+
fn poll_recv_timestamp(
47+
&self,
48+
cx: &mut Context,
49+
) -> Poll<Result<(SendTimestampToken, FullTimestampData)>>;
50+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
use std::{
2+
io::Result,
3+
net::{Ipv4Addr, SocketAddrV4},
4+
pin::Pin,
5+
sync::Mutex,
6+
task::{Context, Poll},
7+
};
8+
9+
use timestamped_socket::socket::{
10+
FullTimestampData, GeneralTimestampMode, InterfaceTimestampMode, Open, RecvResult,
11+
SendTimestampToken, Socket, open_ipv4,
12+
};
13+
14+
use crate::{
15+
PtpAddressFamily,
16+
addresses::{BoundInterface, SealedPtpAddressFamily},
17+
};
18+
19+
const EVENT_PORT: u16 = 319;
20+
const GENERAL_PORT: u16 = 320;
21+
22+
type RecvTimestampFuture =
23+
dyn Future<Output = Result<(SendTimestampToken, FullTimestampData)>> + Sync + Send + 'static;
24+
25+
pub(crate) struct Ipv4BoundInterface {
26+
general_socket: Socket<SocketAddrV4, Open>,
27+
event_socket: Socket<SocketAddrV4, Open>,
28+
recv_timestamp_future: Mutex<Pin<Box<RecvTimestampFuture>>>,
29+
}
30+
31+
impl BoundInterface for Ipv4BoundInterface {
32+
type Addr = Ipv4Addr;
33+
34+
fn open(
35+
interface: Option<timestamped_socket::interface::InterfaceName>,
36+
hardware_clock: Option<u32>,
37+
) -> Result<Self> {
38+
let (event_socket, general_socket) = if let Some(interface) = interface {
39+
use timestamped_socket::socket::open_interface_udp4;
40+
41+
let event_socket = open_interface_udp4(
42+
interface,
43+
EVENT_PORT,
44+
match hardware_clock {
45+
Some(_) => InterfaceTimestampMode::SoftwareAll,
46+
None => InterfaceTimestampMode::HardwarePTPAll,
47+
},
48+
hardware_clock,
49+
)?;
50+
let general_socket =
51+
open_interface_udp4(interface, GENERAL_PORT, InterfaceTimestampMode::None, None)?;
52+
(event_socket, general_socket)
53+
} else {
54+
let event_socket = open_ipv4(
55+
SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, EVENT_PORT),
56+
GeneralTimestampMode::SoftwareAll,
57+
true,
58+
)?;
59+
let general_socket = open_ipv4(
60+
SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, GENERAL_PORT),
61+
GeneralTimestampMode::None,
62+
true,
63+
)?;
64+
(event_socket, general_socket)
65+
};
66+
67+
let recv_timestamp_future = Mutex::new(Box::into_pin(Box::new(
68+
event_socket.get_send_timestamp(),
69+
) as Box<_>));
70+
71+
Ok(Self {
72+
general_socket,
73+
event_socket,
74+
recv_timestamp_future,
75+
})
76+
}
77+
78+
fn poll_send_event(
79+
&self,
80+
buf: &[u8],
81+
from: Option<Self::Addr>,
82+
to: Self::Addr,
83+
cx: &mut Context,
84+
) -> Poll<Result<SendTimestampToken>> {
85+
if let Some(from) = from {
86+
self.event_socket.poll_send_from_to(
87+
buf,
88+
SocketAddrV4::new(from, EVENT_PORT),
89+
SocketAddrV4::new(to, EVENT_PORT),
90+
cx,
91+
)
92+
} else {
93+
self.event_socket
94+
.poll_send_to(buf, SocketAddrV4::new(to, EVENT_PORT), cx)
95+
}
96+
// The unwrap here will always succeed as the event socket will have timestamping enabled.
97+
.map_ok(Option::unwrap)
98+
}
99+
100+
fn poll_send_general(
101+
&self,
102+
buf: &[u8],
103+
from: Option<Self::Addr>,
104+
to: Self::Addr,
105+
cx: &mut Context,
106+
) -> Poll<Result<()>> {
107+
if let Some(from) = from {
108+
self.general_socket.poll_send_from_to(
109+
buf,
110+
SocketAddrV4::new(from, GENERAL_PORT),
111+
SocketAddrV4::new(to, GENERAL_PORT),
112+
cx,
113+
)
114+
} else {
115+
self.general_socket
116+
.poll_send_to(buf, SocketAddrV4::new(to, GENERAL_PORT), cx)
117+
}
118+
.map_ok(|_| ())
119+
}
120+
121+
fn poll_recv(&self, buf: &mut [u8], cx: &mut Context) -> Poll<Result<RecvResult<Self::Addr>>> {
122+
if let Poll::Ready(result) = self.event_socket.poll_recv(buf, cx) {
123+
Poll::Ready(result)
124+
} else {
125+
self.general_socket.poll_recv(buf, cx)
126+
}
127+
.map_ok(|result| RecvResult {
128+
bytes_read: result.bytes_read,
129+
remote_addr: *result.remote_addr.ip(),
130+
local_addr: *result.local_addr.ip(),
131+
timestamp: result.timestamp,
132+
full_timestamp_data: result.full_timestamp_data,
133+
})
134+
}
135+
136+
fn poll_recv_timestamp(
137+
&self,
138+
cx: &mut Context,
139+
) -> Poll<Result<(SendTimestampToken, FullTimestampData)>> {
140+
// The mutex can only be poisoned from an earlier panic. It is ok for
141+
// us to propagate that to all the threads.
142+
let mut recv_timestamp_future = self.recv_timestamp_future.lock().unwrap();
143+
let result = recv_timestamp_future.as_mut().poll(cx);
144+
if result.is_ready() {
145+
*recv_timestamp_future =
146+
Box::into_pin(Box::new(self.event_socket.get_send_timestamp()) as Box<_>);
147+
}
148+
149+
result
150+
}
151+
}
152+
153+
impl SealedPtpAddressFamily for Ipv4Addr {
154+
type BoundInterface = Ipv4BoundInterface;
155+
}
156+
157+
impl PtpAddressFamily for Ipv4Addr {}

0 commit comments

Comments
 (0)