mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-18 16:26:31 +00:00
ready for merge in master (#964)
uevent should be format Enum of smoltcp socket should be optimized. need to add interface for routing subsys actix is still not abled to run. clean some casual added code to other places
This commit is contained in:
3
user/apps/ping/.gitignore
vendored
Normal file
3
user/apps/ping/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
/install/
|
18
user/apps/ping/Cargo.toml
Normal file
18
user/apps/ping/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "ping"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "ping for dragonOS"
|
||||
authors = [ "smallc <2628035541@qq.com>" ]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
clap = { version = "4.5.11", features = ["derive"] }
|
||||
crossbeam-channel = "0.5.13"
|
||||
pnet = "0.35.0"
|
||||
rand = "0.8.5"
|
||||
signal-hook = "0.3.17"
|
||||
socket2 = "0.5.7"
|
||||
thiserror = "1.0.63"
|
56
user/apps/ping/Makefile
Normal file
56
user/apps/ping/Makefile
Normal file
@ -0,0 +1,56 @@
|
||||
TOOLCHAIN=
|
||||
RUSTFLAGS=
|
||||
|
||||
ifdef DADK_CURRENT_BUILD_DIR
|
||||
# 如果是在dadk中编译,那么安装到dadk的安装目录中
|
||||
INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
|
||||
else
|
||||
# 如果是在本地编译,那么安装到当前目录下的install目录中
|
||||
INSTALL_DIR = ./install
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
export RUST_TARGET=x86_64-unknown-linux-musl
|
||||
else ifeq ($(ARCH), riscv64)
|
||||
export RUST_TARGET=riscv64gc-unknown-linux-gnu
|
||||
else
|
||||
# 默认为x86_86,用于本地编译
|
||||
export RUST_TARGET=x86_64-unknown-linux-musl
|
||||
endif
|
||||
|
||||
run:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET)
|
||||
|
||||
build:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET)
|
||||
|
||||
clean:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET)
|
||||
|
||||
test:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET)
|
||||
|
||||
doc:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET)
|
||||
|
||||
fmt:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt
|
||||
|
||||
fmt-check:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check
|
||||
|
||||
run-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release
|
||||
|
||||
build-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release
|
||||
|
||||
clean-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release
|
||||
|
||||
test-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force
|
23
user/apps/ping/README.md
Normal file
23
user/apps/ping/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# PING
|
||||
为DragonOS实现ping
|
||||
## NAME
|
||||
ping - 向网络主机发送ICMP ECHO_REQUEST
|
||||
## SYNOPSIS
|
||||
[-c count]: 指定 ping 的次数。例如,`-c 4` 会向目标主机发送 4 个 ping 请求。
|
||||
|
||||
[-i interval]:指定两次 ping 请求之间的时间间隔,单位是秒。例如,`-i 2` 会每 2 秒发送一次 ping 请求。
|
||||
|
||||
[-w timeout]: 指定等待 ping 响应的超时时间,单位是秒。例如,`-w 5` 会在 5 秒后超时。
|
||||
|
||||
[-s packetsize]:指定发送的 ICMP Packet 的大小,单位是字节。例如,`-s 64` 会发送大小为 64 字节的 ICMP Packet。
|
||||
|
||||
[-t ttl]:指定 ping 的 TTL (Time to Live)。例如,`-t 64` 会设置 TTL 为 64。
|
||||
|
||||
{destination}:指定要 ping 的目标主机。可以是 IP 地址或者主机名。例如,`192.168.1.1` 或 `www.example.com`。
|
||||
|
||||
## DESCRIPTION
|
||||
ping 使用 ICMP 协议的必需的 ECHO_REQUEST 数据报来引发主机或网关的 ICMP ECHO_RESPONSE。ECHO_REQUEST 数据报(“ping”)具有 IP 和 ICMP 头,后面跟着一个 struct timeval,然后是用于填充数据包的任意数量的“填充”字节。
|
||||
|
||||
ping 支持 IPv4 和 IPv6。可以通过指定 -4 或 -6 来强制只使用其中一个。
|
||||
|
||||
ping 还可以发送 IPv6 节点信息查询(RFC4620)。可能不允许中间跳跃,因为 IPv6 源路由已被弃用(RFC5095)。
|
50
user/apps/ping/src/args.rs
Normal file
50
user/apps/ping/src/args.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use clap::{arg, command, Parser};
|
||||
use rand::random;
|
||||
|
||||
use crate::config::{Config, IpAddress};
|
||||
|
||||
/// # Args结构体
|
||||
/// 使用clap库对命令行输入进行pasing,产生参数配置
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Args {
|
||||
// Count of ping times
|
||||
#[arg(short, default_value_t = 4)]
|
||||
count: u16,
|
||||
|
||||
// Ping packet size
|
||||
#[arg(short = 's', default_value_t = 64)]
|
||||
packet_size: usize,
|
||||
|
||||
// Ping ttl
|
||||
#[arg(short = 't', default_value_t = 64)]
|
||||
ttl: u32,
|
||||
|
||||
// Ping timeout seconds
|
||||
#[arg(short = 'w', default_value_t = 1)]
|
||||
timeout: u64,
|
||||
|
||||
// Ping interval duration milliseconds
|
||||
#[arg(short = 'i', default_value_t = 1000)]
|
||||
interval: u64,
|
||||
|
||||
// Ping destination, ip or domain
|
||||
#[arg(value_parser=IpAddress::parse)]
|
||||
destination: IpAddress,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
/// # 将Args结构体转换为config结构体
|
||||
pub fn as_config(&self) -> Config {
|
||||
Config {
|
||||
count: self.count,
|
||||
packet_size: self.packet_size,
|
||||
ttl: self.ttl,
|
||||
timeout: self.timeout,
|
||||
interval: self.interval,
|
||||
id: random::<u16>(),
|
||||
sequence: 1,
|
||||
address: self.destination.clone(),
|
||||
}
|
||||
}
|
||||
}
|
45
user/apps/ping/src/config.rs
Normal file
45
user/apps/ping/src/config.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use anyhow::bail;
|
||||
use std::{
|
||||
ffi::CString,
|
||||
net::{self},
|
||||
};
|
||||
|
||||
use crate::error;
|
||||
|
||||
///# Config结构体
|
||||
/// 记录ping指令的一些参数值
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub count: u16,
|
||||
pub packet_size: usize,
|
||||
pub ttl: u32,
|
||||
pub timeout: u64,
|
||||
pub interval: u64,
|
||||
pub id: u16,
|
||||
pub sequence: u16,
|
||||
pub address: IpAddress,
|
||||
}
|
||||
|
||||
///# 目标地址ip结构体
|
||||
/// ip负责提供给socket使用
|
||||
/// raw负责打印输出
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IpAddress {
|
||||
pub ip: net::IpAddr,
|
||||
pub raw: String,
|
||||
}
|
||||
|
||||
impl IpAddress {
|
||||
pub fn parse(host: &str) -> anyhow::Result<Self> {
|
||||
let raw = String::from(host);
|
||||
let opt = host.parse::<net::IpAddr>().ok();
|
||||
match opt {
|
||||
Some(ip) => Ok(Self { ip, raw }),
|
||||
None => {
|
||||
bail!(error::PingError::InvalidConfig(
|
||||
"Invalid Address".to_string()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
user/apps/ping/src/error.rs
Normal file
10
user/apps/ping/src/error.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
pub enum PingError {
|
||||
#[error("invaild config")]
|
||||
InvalidConfig(String),
|
||||
|
||||
#[error("invaild packet")]
|
||||
InvalidPacket,
|
||||
}
|
23
user/apps/ping/src/main.rs
Normal file
23
user/apps/ping/src/main.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use args::Args;
|
||||
use clap::Parser;
|
||||
use std::format;
|
||||
|
||||
mod args;
|
||||
mod config;
|
||||
mod error;
|
||||
mod ping;
|
||||
///# ping入口主函数
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
match ping::Ping::new(args.as_config()) {
|
||||
Ok(pinger) => pinger.run().unwrap_or_else(|e| {
|
||||
exit(format!("Error on run ping: {}", e));
|
||||
}),
|
||||
Err(e) => exit(format!("Error on init: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn exit(msg: String) {
|
||||
eprintln!("{}", msg);
|
||||
std::process::exit(1);
|
||||
}
|
151
user/apps/ping/src/ping.rs
Normal file
151
user/apps/ping/src/ping.rs
Normal file
@ -0,0 +1,151 @@
|
||||
use crossbeam_channel::{bounded, select, Receiver};
|
||||
use pnet::packet::{
|
||||
icmp::{
|
||||
echo_reply::{EchoReplyPacket, IcmpCodes},
|
||||
echo_request::MutableEchoRequestPacket,
|
||||
IcmpTypes,
|
||||
},
|
||||
util, Packet,
|
||||
};
|
||||
use signal_hook::consts::{SIGINT, SIGTERM};
|
||||
use socket2::{Domain, Protocol, Socket, Type};
|
||||
use std::{
|
||||
io,
|
||||
net::{self, Ipv4Addr, SocketAddr},
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread::{self},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{config::Config, error::PingError};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ping {
|
||||
config: Config,
|
||||
socket: Arc<Socket>,
|
||||
dest: SocketAddr,
|
||||
}
|
||||
|
||||
impl Ping {
|
||||
///# ping创建函数
|
||||
/// 使用config进行ping的配置
|
||||
pub fn new(config: Config) -> std::io::Result<Self> {
|
||||
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?;
|
||||
let src = SocketAddr::new(net::IpAddr::V4(Ipv4Addr::UNSPECIFIED), 12549);
|
||||
let dest = SocketAddr::new(config.address.ip, 12549);
|
||||
socket.bind(&src.into())?;
|
||||
// socket.set_ttl(64)?;
|
||||
// socket.set_read_timeout(Some(Duration::from_secs(config.timeout)))?;
|
||||
// socket.set_write_timeout(Some(Duration::from_secs(config.timeout)))?;
|
||||
Ok(Self {
|
||||
config,
|
||||
dest,
|
||||
socket: Arc::new(socket),
|
||||
})
|
||||
}
|
||||
///# ping主要执行逻辑
|
||||
/// 创建icmpPacket发送给socket
|
||||
pub fn ping(&self, seq_offset: u16) -> anyhow::Result<()> {
|
||||
//创建 icmp request packet
|
||||
let mut buf = vec![0; self.config.packet_size];
|
||||
let mut icmp = MutableEchoRequestPacket::new(&mut buf[..]).expect("InvalidBuffferSize");
|
||||
icmp.set_icmp_type(IcmpTypes::EchoRequest);
|
||||
icmp.set_icmp_code(IcmpCodes::NoCode);
|
||||
icmp.set_identifier(self.config.id);
|
||||
icmp.set_sequence_number(self.config.sequence + seq_offset);
|
||||
icmp.set_checksum(util::checksum(icmp.packet(), 1));
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
//发送 request
|
||||
|
||||
self.socket.send_to(icmp.packet(), &self.dest.into())?;
|
||||
|
||||
//处理 recv
|
||||
let mut mem_buf =
|
||||
unsafe { &mut *(buf.as_mut_slice() as *mut [u8] as *mut [std::mem::MaybeUninit<u8>]) };
|
||||
let (size, _) = self.socket.recv_from(&mut mem_buf)?;
|
||||
|
||||
let duration = start.elapsed().as_micros() as f64 / 1000.0;
|
||||
let reply = EchoReplyPacket::new(&buf).ok_or(PingError::InvalidPacket)?;
|
||||
println!(
|
||||
"{} bytes from {}: icmp_seq={} ttl={} time={:.2}ms",
|
||||
size,
|
||||
self.config.address.ip,
|
||||
reply.get_sequence_number(),
|
||||
self.config.ttl,
|
||||
duration
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
///# ping指令多线程运行
|
||||
/// 创建多个线程负责不同的ping函数的执行
|
||||
pub fn run(&self) -> io::Result<()> {
|
||||
println!(
|
||||
"PING {}({})",
|
||||
self.config.address.raw, self.config.address.ip
|
||||
);
|
||||
let _now = Instant::now();
|
||||
let send = Arc::new(AtomicU64::new(0));
|
||||
let _send = send.clone();
|
||||
let this = Arc::new(self.clone());
|
||||
|
||||
let success = Arc::new(AtomicU64::new(0));
|
||||
let _success = success.clone();
|
||||
|
||||
let mut handles = vec![];
|
||||
|
||||
for i in 0..this.config.count {
|
||||
let _this = this.clone();
|
||||
let handle = thread::spawn(move||{
|
||||
_this.ping(i).unwrap();
|
||||
});
|
||||
_send.fetch_add(1, Ordering::SeqCst);
|
||||
handles.push(handle);
|
||||
if i < this.config.count - 1 {
|
||||
thread::sleep(Duration::from_millis(this.config.interval));
|
||||
}
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
if handle.join().is_ok() {
|
||||
_success.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let total = _now.elapsed().as_micros() as f64 / 1000.0;
|
||||
let send = send.load(Ordering::SeqCst);
|
||||
let success = success.load(Ordering::SeqCst);
|
||||
let loss_rate = if send > 0 {
|
||||
(send - success) * 100 / send
|
||||
} else {
|
||||
0
|
||||
};
|
||||
println!("\n--- {} ping statistics ---", self.config.address.raw);
|
||||
println!(
|
||||
"{} packets transmitted, {} received, {}% packet loss, time {}ms",
|
||||
send, success, loss_rate, total,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: 等待添加ctrl+c发送信号后添加该特性
|
||||
// /// # 创建一个进程用于监听用户是否提前退出程序
|
||||
// fn signal_notify() -> std::io::Result<Receiver<i32>> {
|
||||
// let (s, r) = bounded(1);
|
||||
|
||||
// let mut signals = signal_hook::iterator::Signals::new(&[SIGINT, SIGTERM])?;
|
||||
|
||||
// thread::spawn(move || {
|
||||
// for signal in signals.forever() {
|
||||
// s.send(signal).unwrap();
|
||||
// break;
|
||||
// }
|
||||
// });
|
||||
// Ok(r)
|
||||
// }
|
Reference in New Issue
Block a user