Allow selection of measurement type in config
This commit is contained in:
1 parent
d73693c25f
commit
f20ff59e78
6 files changed
+105
-77
No files matched your search
+24
-2
@@ -1,4 +1,4 @@
|
|||||||
use crate::error::{ConfigParseError, MissingConfigError};
|
use crate::error::{ConfigParseError, InvalidMeasurementTypeError, MissingConfigError};
|
||||||
use log::Level;
|
use log::Level;
|
||||||
#[cfg(feature = "uci")]
|
#[cfg(feature = "uci")]
|
||||||
use rust_uci::Uci;
|
use rust_uci::Uci;
|
||||||
@@ -19,13 +19,29 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) enum AutorateType {
|
pub(crate) enum MeasurementType {
|
||||||
ICMP = 1,
|
ICMP = 1,
|
||||||
ICMPTimestamps,
|
ICMPTimestamps,
|
||||||
NTP,
|
NTP,
|
||||||
TCPTimestamps,
|
TCPTimestamps,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for MeasurementType {
|
||||||
|
type Err = InvalidMeasurementTypeError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
return match s.to_lowercase().as_str() {
|
||||||
|
"icmp" => Ok(MeasurementType::ICMP),
|
||||||
|
"icmp-timestamps" => Ok(MeasurementType::ICMPTimestamps),
|
||||||
|
"ntp" => Ok(MeasurementType::NTP),
|
||||||
|
"tcp-timestamps" => Ok(MeasurementType::TCPTimestamps),
|
||||||
|
&_ => Err(InvalidMeasurementTypeError {
|
||||||
|
type_: s.to_string(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct Config {
|
pub(crate) struct Config {
|
||||||
// Network section
|
// Network section
|
||||||
@@ -46,6 +62,7 @@ pub(crate) struct Config {
|
|||||||
pub(crate) download_delay_ms: f64,
|
pub(crate) download_delay_ms: f64,
|
||||||
pub(crate) high_load_level: f64,
|
pub(crate) high_load_level: f64,
|
||||||
pub(crate) min_change_interval: f64,
|
pub(crate) min_change_interval: f64,
|
||||||
|
pub(crate) measurement_type: MeasurementType,
|
||||||
pub(crate) num_reflectors: u8,
|
pub(crate) num_reflectors: u8,
|
||||||
pub(crate) reflector_list_file: String,
|
pub(crate) reflector_list_file: String,
|
||||||
pub(crate) speed_hist_size: u32,
|
pub(crate) speed_hist_size: u32,
|
||||||
@@ -119,6 +136,11 @@ impl Config {
|
|||||||
"sqm-autorate.@advanced_settings[0].high_load_level",
|
"sqm-autorate.@advanced_settings[0].high_load_level",
|
||||||
Some(0.8),
|
Some(0.8),
|
||||||
)?,
|
)?,
|
||||||
|
measurement_type: Self::get::<MeasurementType>(
|
||||||
|
"SQMA_MEASUREMENT_TYPE",
|
||||||
|
"sqm-autorate.@advanced_settings[0].measurement_type",
|
||||||
|
Some(MeasurementType::ICMPTimestamps),
|
||||||
|
)?,
|
||||||
min_change_interval: Self::get::<f64>(
|
min_change_interval: Self::get::<f64>(
|
||||||
"SQMA_MIN_CHANGE_INTERVAL",
|
"SQMA_MIN_CHANGE_INTERVAL",
|
||||||
"sqm-autorate.@advanced_settings[0].min_change_interval",
|
"sqm-autorate.@advanced_settings[0].min_change_interval",
|
||||||
|
|||||||
+21
-2
@@ -24,6 +24,23 @@ impl Display for ConfigParseError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct InvalidMeasurementTypeError {
|
||||||
|
pub(crate) type_: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for InvalidMeasurementTypeError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InvalidMeasurementTypeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Couldn't find measurement type: {}", self.type_)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct MissingConfigError {
|
pub struct MissingConfigError {
|
||||||
pub(crate) config_key: String,
|
pub(crate) config_key: String,
|
||||||
@@ -63,7 +80,9 @@ impl Display for NoQdiscFoundError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct PingParseError;
|
pub struct PingParseError {
|
||||||
|
pub(crate) msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Error for PingParseError {
|
impl Error for PingParseError {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
@@ -74,6 +93,6 @@ impl Error for PingParseError {
|
|||||||
// Implement std::fmt::Display for AppError
|
// Implement std::fmt::Display for AppError
|
||||||
impl Display for PingParseError {
|
impl Display for PingParseError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "Couldn't parse received ping packet") // user-facing output
|
write!(f, "Couldn't parse received ping packet: {}", self.msg) // user-facing output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+21
-7
@@ -26,11 +26,12 @@ use std::thread::sleep;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{panic, process, thread};
|
use std::{panic, process, thread};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::{Config, MeasurementType};
|
||||||
use crate::log::SimpleLogger;
|
use crate::log::SimpleLogger;
|
||||||
use crate::netlink::{Netlink, Qdisc};
|
use crate::netlink::{Netlink, Qdisc};
|
||||||
use crate::pinger::{PingListener, PingSender, SocketType};
|
use crate::pinger::{PingListener, PingSender, SocketType};
|
||||||
use crate::pinger_icmp::{PingerICMPEchoListener, PingerICMPEchoSender};
|
use crate::pinger_icmp::{PingerICMPEchoListener, PingerICMPEchoSender};
|
||||||
|
use crate::pinger_icmp_ts::{PingerICMPTimestampListener, PingerICMPTimestampSender};
|
||||||
use crate::ratecontroller::{Ratecontroller, StatsDirection};
|
use crate::ratecontroller::{Ratecontroller, StatsDirection};
|
||||||
use crate::reflector_selector::ReflectorSelector;
|
use crate::reflector_selector::ReflectorSelector;
|
||||||
use crate::utils::Utils;
|
use crate::utils::Utils;
|
||||||
@@ -51,7 +52,7 @@ fn main() -> ExitCode {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
crate::log::init(config.log_level).expect("Couldn't initialize logger");
|
log::init(config.log_level).expect("Couldn't initialize logger");
|
||||||
|
|
||||||
let mut reflectors = match config.load_reflectors() {
|
let mut reflectors = match config.load_reflectors() {
|
||||||
Ok(refl) => refl,
|
Ok(refl) => refl,
|
||||||
@@ -96,10 +97,22 @@ fn main() -> ExitCode {
|
|||||||
let (baseliner_stats_sender, baseliner_stats_receiver) = channel();
|
let (baseliner_stats_sender, baseliner_stats_receiver) = channel();
|
||||||
let (reselect_sender, reselect_receiver) = channel();
|
let (reselect_sender, reselect_receiver) = channel();
|
||||||
|
|
||||||
//let mut pinger_receiver = PingerICMPTimestampListener::new(id);
|
let (mut pinger_receiver, mut pinger_sender) = match config.measurement_type {
|
||||||
//let mut pinger_sender = PingerICMPTimestampSender::new(id);
|
MeasurementType::ICMP => (
|
||||||
let mut pinger_receiver = PingerICMPEchoListener::new(id);
|
Box::new(PingerICMPEchoListener {}) as Box<dyn PingListener + Send>,
|
||||||
let mut pinger_sender = PingerICMPEchoSender::new(id);
|
Box::new(PingerICMPEchoSender {}) as Box<dyn PingSender + Send>,
|
||||||
|
),
|
||||||
|
MeasurementType::ICMPTimestamps => (
|
||||||
|
Box::new(PingerICMPTimestampListener {}) as Box<dyn PingListener + Send>,
|
||||||
|
Box::new(PingerICMPTimestampSender {}) as Box<dyn PingSender + Send>,
|
||||||
|
),
|
||||||
|
MeasurementType::NTP => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
MeasurementType::TCPTimestamps => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let baseliner = Baseliner {
|
let baseliner = Baseliner {
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
@@ -137,6 +150,7 @@ fn main() -> ExitCode {
|
|||||||
.name("receiver".to_string())
|
.name("receiver".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
match pinger_receiver.listen(
|
match pinger_receiver.listen(
|
||||||
|
id,
|
||||||
SocketType::ICMP,
|
SocketType::ICMP,
|
||||||
reflector_peers_lock_clone,
|
reflector_peers_lock_clone,
|
||||||
baseliner_stats_sender,
|
baseliner_stats_sender,
|
||||||
@@ -157,7 +171,7 @@ fn main() -> ExitCode {
|
|||||||
let sender_handle = thread::Builder::new()
|
let sender_handle = thread::Builder::new()
|
||||||
.name("sender".to_string())
|
.name("sender".to_string())
|
||||||
.spawn(
|
.spawn(
|
||||||
move || match pinger_sender.send(SocketType::ICMP, reflector_peers_lock_clone) {
|
move || match pinger_sender.send(id, SocketType::ICMP, reflector_peers_lock_clone) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error occured in sender thread: {}", e);
|
error!("Error occured in sender thread: {}", e);
|
||||||
|
|||||||
+13
-12
@@ -8,7 +8,7 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
|||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -53,11 +53,9 @@ fn open_socket(type_: SocketType) -> nix::Result<RawFd> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait PingListener {
|
pub trait PingListener {
|
||||||
fn new(id: u16) -> Self;
|
|
||||||
fn get_id(&self) -> u16;
|
|
||||||
|
|
||||||
fn listen(
|
fn listen(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
id: u16,
|
||||||
type_: SocketType,
|
type_: SocketType,
|
||||||
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||||
stats_sender: Sender<PingReply>,
|
stats_sender: Sender<PingReply>,
|
||||||
@@ -85,7 +83,7 @@ pub trait PingListener {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reply = match Self::parse_packet(addr, buf, size) {
|
let reply = match self.parse_packet(id, addr, buf, size) {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!(
|
||||||
@@ -101,16 +99,19 @@ pub trait PingListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_packet(reflector: IpAddr, buf: &[u8], len: usize)
|
fn parse_packet(
|
||||||
-> Result<PingReply, Box<dyn Error>>;
|
&self,
|
||||||
|
id: u16,
|
||||||
|
reflector: IpAddr,
|
||||||
|
buf: &[u8],
|
||||||
|
len: usize,
|
||||||
|
) -> Result<PingReply, Box<dyn Error>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PingSender {
|
pub trait PingSender {
|
||||||
fn new(id: u16) -> Self;
|
|
||||||
fn get_id(&self) -> u16;
|
|
||||||
|
|
||||||
fn send(
|
fn send(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
id: u16,
|
||||||
type_: SocketType,
|
type_: SocketType,
|
||||||
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
@@ -141,7 +142,7 @@ pub trait PingSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let buf_v = self.craft_packet(seq);
|
let buf_v = self.craft_packet(id, seq);
|
||||||
let buf = buf_v.as_slice();
|
let buf = buf_v.as_slice();
|
||||||
|
|
||||||
sendto(socket, buf, addr.as_ref(), MsgFlags::empty()).expect("Couldn't send ping");
|
sendto(socket, buf, addr.as_ref(), MsgFlags::empty()).expect("Couldn't send ping");
|
||||||
@@ -157,5 +158,5 @@ pub trait PingSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn craft_packet(&self, seq: u16) -> Vec<u8>;
|
fn craft_packet(&self, id: u16, seq: u16) -> Vec<u8>;
|
||||||
}
|
}
|
||||||
+9
-28
@@ -9,25 +9,15 @@ use etherparse::{IcmpEchoHeader, Icmpv4Header, Icmpv4Type, SlicedPacket};
|
|||||||
use nix::sys::time::TimeValLike;
|
use nix::sys::time::TimeValLike;
|
||||||
use nix::time::{clock_gettime, ClockId};
|
use nix::time::{clock_gettime, ClockId};
|
||||||
|
|
||||||
pub struct PingerICMPEchoListener {
|
pub struct PingerICMPEchoListener {}
|
||||||
id: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PingerICMPEchoSender {
|
pub struct PingerICMPEchoSender {}
|
||||||
id: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PingListener for PingerICMPEchoListener {
|
impl PingListener for PingerICMPEchoListener {
|
||||||
fn new(id: u16) -> Self {
|
|
||||||
PingerICMPEchoListener { id }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_id(&self) -> u16 {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result: RTT, down time, up time
|
// Result: RTT, down time, up time
|
||||||
fn parse_packet(
|
fn parse_packet(
|
||||||
|
&self,
|
||||||
|
id: u16,
|
||||||
reflector: IpAddr,
|
reflector: IpAddr,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
len: usize,
|
len: usize,
|
||||||
@@ -70,30 +60,21 @@ impl PingListener for PingerICMPEchoListener {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Box::new(PingParseError {}))
|
Err(Box::new(PingParseError {
|
||||||
|
msg: "Reached end of parsing function".to_string(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PingSender for PingerICMPEchoSender {
|
impl PingSender for PingerICMPEchoSender {
|
||||||
fn new(id: u16) -> Self {
|
fn craft_packet(&self, id: u16, seq: u16) -> Vec<u8> {
|
||||||
PingerICMPEchoSender { id }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_id(&self) -> u16 {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn craft_packet(&self, seq: u16) -> Vec<u8> {
|
|
||||||
let time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
|
let time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
|
||||||
let time_u64: u64 = time.num_milliseconds() as u64;
|
let time_u64: u64 = time.num_milliseconds() as u64;
|
||||||
let payload = time_u64.to_ne_bytes();
|
let payload = time_u64.to_ne_bytes();
|
||||||
|
|
||||||
// Construct a header with checksum based on the payload
|
// Construct a header with checksum based on the payload
|
||||||
let hdr = Icmpv4Header::with_checksum(
|
let hdr = Icmpv4Header::with_checksum(
|
||||||
Icmpv4Type::EchoRequest(IcmpEchoHeader {
|
Icmpv4Type::EchoRequest(IcmpEchoHeader { id, seq }),
|
||||||
id: self.get_id(),
|
|
||||||
seq,
|
|
||||||
}),
|
|
||||||
&payload,
|
&payload,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+17
-26
@@ -7,36 +7,33 @@ use crate::Utils;
|
|||||||
use etherparse::icmpv4::TimestampMessage;
|
use etherparse::icmpv4::TimestampMessage;
|
||||||
use etherparse::TransportSlice::Icmpv4;
|
use etherparse::TransportSlice::Icmpv4;
|
||||||
use etherparse::{Icmpv4Header, Icmpv4Type, SlicedPacket};
|
use etherparse::{Icmpv4Header, Icmpv4Type, SlicedPacket};
|
||||||
|
use log::warn;
|
||||||
use nix::time::{clock_gettime, ClockId};
|
use nix::time::{clock_gettime, ClockId};
|
||||||
|
|
||||||
pub struct PingerICMPTimestampListener {
|
pub struct PingerICMPTimestampListener {}
|
||||||
id: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PingerICMPTimestampSender {
|
pub struct PingerICMPTimestampSender {}
|
||||||
id: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PingListener for PingerICMPTimestampListener {
|
impl PingListener for PingerICMPTimestampListener {
|
||||||
fn new(id: u16) -> Self {
|
|
||||||
PingerICMPTimestampListener { id }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_id(&self) -> u16 {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result: RTT, down time, up time
|
// Result: RTT, down time, up time
|
||||||
fn parse_packet(
|
fn parse_packet(
|
||||||
|
&self,
|
||||||
|
id: u16,
|
||||||
reflector: IpAddr,
|
reflector: IpAddr,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> Result<PingReply, Box<dyn Error>> {
|
) -> Result<PingReply, Box<dyn Error>> {
|
||||||
match SlicedPacket::from_ip(buf) {
|
match SlicedPacket::from_ip(buf) {
|
||||||
Err(value) => println!("Err {:?}", value),
|
Err(value) => warn!("Error parsing packet: {:?}", value),
|
||||||
Ok(value) => match value.transport {
|
Ok(value) => match value.transport {
|
||||||
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
|
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
|
||||||
Icmpv4Type::TimestampReply(reply) => {
|
Icmpv4Type::TimestampReply(reply) => {
|
||||||
|
if reply.id != id {
|
||||||
|
return Err(Box::new(PingParseError {
|
||||||
|
msg: "Wrong ID".to_string(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
let time_now = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();
|
let time_now = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();
|
||||||
let time_since_midnight: i64 = (time_now.tv_sec() as i64 % 86400 * 1000)
|
let time_since_midnight: i64 = (time_now.tv_sec() as i64 % 86400 * 1000)
|
||||||
+ (time_now.tv_nsec() as i64 / 1000000);
|
+ (time_now.tv_nsec() as i64 / 1000000);
|
||||||
@@ -70,20 +67,14 @@ impl PingListener for PingerICMPTimestampListener {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Box::new(PingParseError {}))
|
Err(Box::new(PingParseError {
|
||||||
|
msg: "Reached end of parsing function".to_string(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PingSender for PingerICMPTimestampSender {
|
impl PingSender for PingerICMPTimestampSender {
|
||||||
fn new(id: u16) -> Self {
|
fn craft_packet(&self, id: u16, seq: u16) -> Vec<u8> {
|
||||||
PingerICMPTimestampSender { id }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_id(&self) -> u16 {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn craft_packet(&self, seq: u16) -> Vec<u8> {
|
|
||||||
let time = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();
|
let time = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();
|
||||||
let time_since_midnight: u32 =
|
let time_since_midnight: u32 =
|
||||||
((time.tv_sec() % 86400 * 1000) + (time.tv_nsec() / 1000000)) as u32;
|
((time.tv_sec() % 86400 * 1000) + (time.tv_nsec() / 1000000)) as u32;
|
||||||
@@ -93,7 +84,7 @@ impl PingSender for PingerICMPTimestampSender {
|
|||||||
// Construct a header with checksum based on the payload
|
// Construct a header with checksum based on the payload
|
||||||
let hdr = Icmpv4Header::with_checksum(
|
let hdr = Icmpv4Header::with_checksum(
|
||||||
Icmpv4Type::TimestampRequest(TimestampMessage {
|
Icmpv4Type::TimestampRequest(TimestampMessage {
|
||||||
id: self.id,
|
id,
|
||||||
seq,
|
seq,
|
||||||
originate_timestamp: time_since_midnight,
|
originate_timestamp: time_since_midnight,
|
||||||
receive_timestamp: 0,
|
receive_timestamp: 0,
|
||||||
|
|||||||
Reference in new issue
Block a user