diff --git a/Cargo.lock b/Cargo.lock index 4a753c1..72b353a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,7 @@ dependencies = [ "pnet", "pnet_transport", "serde", + "serde_bytes", ] [[package]] @@ -291,6 +292,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.144" diff --git a/Cargo.toml b/Cargo.toml index 1438e57..49a0e9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ nix = { version = "0.25.0", features = ["net", "socket", "time"] } pnet = "0.31.0" pnet_transport = "0.31.0" serde = { version = "1.0.144", features = ["derive"] } +serde_bytes = "0.11" [patch.crates-io] nix = { path = "../nix" } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7272e2b..ffc7e11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -extern crate core; - mod cake; mod config; mod netlink; @@ -13,8 +11,8 @@ use std::{process, thread, time}; use crate::config::Config; use crate::netlink::{find_interface, find_qdisc, get_interface_stats, set_qdisc_rate}; -use crate::pinger::Pinger; -use crate::pinger_icmp::PingerICMPEcho; +use crate::pinger::{PingListener, PingSender, Pinger}; +use crate::pinger_icmp::{PingerICMPEcho, PingerICMPEchoListener, PingerICMPEchoSender}; use crate::pinger_icmp_ts::PingerICMPTimestamps; #[macro_export] @@ -43,18 +41,24 @@ fn main() { get_interface_stats(config.upload_interface.as_str()).unwrap(); - let mut pinger_receiver = + /*let mut pinger_receiver = PingerICMPTimestamps::new((process::id() & 0xFFFF) as u16, reflectors.clone()); let mut pinger_sender = - PingerICMPTimestamps::new((process::id() & 0xFFFF) as u16, reflectors.clone()); + PingerICMPTimestamps::new((process::id() & 0xFFFF) as u16, reflectors.clone());*/ + + let mut pinger_receiver = + PingerICMPEchoListener::new((process::id() & 0xFFF) as u16, reflectors.clone()); + + let mut pinger_sender = + PingerICMPEchoSender::new((process::id() & 0xFFF) as u16, reflectors.clone()); let receiver_handle = thread::Builder::new() .name("receiver".to_string()) - .spawn(move || pinger_receiver.receive_loop()) + .spawn(move || pinger_receiver.listen()) .unwrap(); let sender_handle = thread::Builder::new() .name("sender".to_string()) - .spawn(move || pinger_sender.sender_loop()) + .spawn(move || pinger_sender.send()) .unwrap(); let threads = vec![receiver_handle, sender_handle]; diff --git a/src/pinger.rs b/src/pinger.rs index d062a75..72a2648 100644 --- a/src/pinger.rs +++ b/src/pinger.rs @@ -1,5 +1,106 @@ +use nix::sys::socket::{ + recvfrom, sendto, socket, AddressFamily, MsgFlags, SockFlag, SockProtocol, SockType, + SockaddrIn, SockaddrIn6, SockaddrLike, +}; use std::error::Error; -use std::net::IpAddr; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}; +use std::str::FromStr; +use std::thread; +use std::time::Duration; + +pub trait PingListener { + fn new(id: u16, reflectors: Vec) -> Self; + + fn get_id(&self) -> u16; + fn get_reflectors(&self) -> Vec; + + fn listen(&mut self) { + let socket = socket( + AddressFamily::Inet, + SockType::Raw, + SockFlag::empty(), /* value */ + SockProtocol::ICMP, + ) + .expect("Couldn't open socket"); + + loop { + let mut v: Vec = vec![0; 128]; + let buf = v.as_mut_slice(); + + let (size, sender) = match recvfrom::(socket, buf) { + Ok(val) => val, + Err(_) => continue, + }; + + let (rtt, down_time, up_time) = match Self::parse_packet(buf, size) { + Ok(val) => val, + Err(e) => { + println!("Shit went to hell: {}", e.to_string()); + continue; + } + }; + } + } + + fn parse_packet(buf: &[u8], len: usize) -> Result<(i64, i64, i64), Box>; +} + +pub trait PingSender { + fn new(id: u16, reflectors: Vec) -> Self; + + fn get_id(&self) -> u16; + fn get_reflectors(&self) -> Vec; + + fn send(&mut self) { + let socket = socket( + AddressFamily::Inet, + SockType::Raw, + SockFlag::empty(), /* value */ + SockProtocol::ICMP, + ) + .expect("Couldn't open socket"); + + let mut seq: u16 = 0; + let tick_duration_ms: u16 = 500; + let sleep_duration = Duration::from_millis(tick_duration_ms as u64); + + loop { + let reflectors = Self::get_reflectors(self); + + for reflector in reflectors.iter() { + let addr: Box; + + match reflector.is_ipv4() { + true => { + let ip4 = Ipv4Addr::from_str(&*reflector.to_string()).unwrap(); + let sock4 = SocketAddrV4::new(ip4, 0); + addr = Box::new(SockaddrIn::from(sock4)); + } + false => { + let ip6 = Ipv6Addr::from_str(&*reflector.to_string()).unwrap(); + let sock6 = SocketAddrV6::new(ip6, 0, 0, 0); + addr = Box::new(SockaddrIn6::from(sock6)); + } + } + + let buf_v = self.craft_packet(seq); + let buf = buf_v.as_slice(); + + sendto(socket, buf, addr.as_ref(), MsgFlags::empty()).expect("Couldn't send ping"); + } + + thread::sleep(sleep_duration); + + seq += 1; + + if seq >= u16::MAX { + seq = 0; + } + } + } + + fn craft_packet(&self, seq: u16) -> Vec; +} pub trait Pinger { fn new(id: u16, reflectors: Vec) -> Self; diff --git a/src/pinger_icmp.rs b/src/pinger_icmp.rs index 75ef559..08bab89 100644 --- a/src/pinger_icmp.rs +++ b/src/pinger_icmp.rs @@ -1,9 +1,12 @@ +use bincode::{deserialize, serialize}; use std::error::Error; use std::net::IpAddr; use std::{thread, time}; +use crate::pinger::{PingListener, PingSender}; use crate::Pinger; use byteorder::*; +use neli::err::NlError; use nix::sys::time::TimeValLike; use nix::time::{clock_gettime, ClockId}; use pnet::packet::icmp::IcmpTypes::EchoReply; @@ -12,6 +15,19 @@ use pnet::packet::*; use pnet_transport::TransportChannelType::Layer4; use pnet_transport::TransportProtocol::Ipv4; use pnet_transport::*; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Clone, Default, Debug)] +//#[repr(C)] +pub struct ICMPEcho { + type_: u8, + code: u8, + checksum: u16, + identifier: u16, + sequence: u16, + #[serde(with = "serde_bytes")] + payload: Vec, +} pub struct PingerICMPEcho { id: u16, @@ -20,6 +36,33 @@ pub struct PingerICMPEcho { tx: TransportSender, } +pub struct PingerICMPEchoListener { + id: u16, + reflectors: Vec, +} + +pub struct PingerICMPEchoSender { + id: u16, + reflectors: Vec, +} + +fn calculate_checksum(buffer: &mut [u8]) -> u16 { + let mut sum = 0u32; + for word in buffer.chunks(2) { + let mut part = u16::from(word[0]) << 8; + if word.len() > 1 { + part += u16::from(word[1]); + } + sum = sum.wrapping_add(u32::from(part)); + } + + while (sum >> 16) > 0 { + sum = (sum & 0xffff) + (sum >> 16); + } + + !sum as u16 +} + impl Pinger for PingerICMPEcho { fn new(id: u16, reflectors: Vec) -> Self { let protocol = Layer4(Ipv4(IpNextHeaderProtocols::Icmp)); @@ -121,3 +164,65 @@ impl Pinger for PingerICMPEcho { Ok(()) } } + +impl PingListener for PingerICMPEchoListener { + fn new(id: u16, reflectors: Vec) -> Self { + PingerICMPEchoListener { id, reflectors } + } + + fn get_id(&self) -> u16 { + self.id + } + + fn get_reflectors(&self) -> Vec { + self.reflectors.clone() + } + + fn parse_packet(buf: &[u8], len: usize) -> Result<(i64, i64, i64), Box> { + if len != 44 { + println!("len: {}", len); + return Err(Box::new(NlError::msg("Wrong length"))); + } + + let hdr: ICMPEcho = deserialize(buf).expect("Failed to parse ICMP packet"); + + todo!() + } +} + +impl PingSender for PingerICMPEchoSender { + fn new(id: u16, reflectors: Vec) -> Self { + PingerICMPEchoSender { id, reflectors } + } + + fn get_id(&self) -> u16 { + self.id + } + + fn get_reflectors(&self) -> Vec { + self.reflectors.clone() + } + + fn craft_packet(&self, seq: u16) -> Vec { + let time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap(); + let time_u64: u64 = time.num_milliseconds() as u64; + + let mut hdr = ICMPEcho { + type_: 8, + code: 0, + checksum: 0, + identifier: self.get_id(), + sequence: seq, + payload: time_u64.to_ne_bytes().to_vec(), + }; + + println!("id: {}", hdr.identifier); + println!("seq: {}", hdr.sequence); + + let mut tmp_buf_v = serialize(&hdr).unwrap(); + let mut tmp_buf = tmp_buf_v.as_mut_slice(); + hdr.checksum = calculate_checksum(tmp_buf).to_be(); + + serialize(&hdr).unwrap() + } +}