[英]Problems with ICMP socket socket2 crate
我編寫了一個應用程序來發送和接收 ICMP 數據包(可以說是 ping 重做)。
我已經在不同的計算機上對此進行了測試,發現代碼只能在我的 MacOS 上運行。 其他 linux 機器(我在許多不同風格的 Linux 上測試過)給出了錯誤的結果,我什至不知道在哪里調試了。
預期的輸出,這是我的 MacOS 的結果
[2022-06-17T18:37:47Z INFO layer4_ip] Received 84 bytes from 142.250.69.206:0
[2022-06-17T18:37:47Z INFO layer4_ip] Received Ipv4Packet { version : 4, header_length : 5, dscp : 0, ecn : 0, total_length : 16384, identification : 0, flags : 0, fragment_offset : 0, ttl : 119, next_level_protocol : IpNextHeaderProtocol(1), checksum : 44214, source : 142.250.69.206, destination : 192.168.1.130, options : [], }
[2022-06-17T18:37:48Z INFO layer4_ip] Received 84 bytes from 142.250.69.206:0
[2022-06-17T18:37:48Z INFO layer4_ip] Received Ipv4Packet { version : 4, header_length : 5, dscp : 0, ecn : 0, total_length : 16384, identification : 0, flags : 0, fragment_offset : 0, ttl : 119, next_level_protocol : IpNextHeaderProtocol(1), checksum : 44214, source : 142.250.69.206, destination : 192.168.1.130, options : [], }
Linux 輸出(錯誤輸出):
[2022-06-17T18:32:54Z INFO ping_playground] Received 64 bytes from 142.250.69.206:0
[2022-06-17T18:32:54Z INFO ping_playground] Received Ipv4Packet { version : 0, header_length : 0, dscp : 0, ecn : 0, total_length : 65454, identification : 80, flags : 0, fragment_offset : 1, ttl : 0, next_level_protocol : IpNextHeaderProtocol(0), checksum : 0, source : 0.0.0.0, destination : 0.0.0.0, options : [], }
[2022-06-17T18:32:55Z INFO ping_playground] Received 64 bytes from 142.250.69.206:0
[2022-06-17T18:32:55Z INFO ping_playground] Received Ipv4Packet { version : 0, header_length : 0, dscp : 0, ecn : 0, total_length : 65454, identification : 80, flags : 0, fragment_offset : 1, ttl : 0, next_level_protocol : IpNextHeaderProtocol(0), checksum : 0, source : 0.0.0.0, destination : 0.0.0.0, options : [], }
不僅讀取的字節數不同,而且解析錯誤。 在wireshark中截取顯示,在我的MacOS和Linux機器上,接收回來的數據包確實是一樣的。
這是出現問題的最小版本:
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
os::unix::prelude::{AsRawFd, FromRawFd},
sync::Arc,
time::Duration,
};
use env_logger::Env;
use log::info;
use pnet_packet::{
icmp::{self},
Packet,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
// SOURCE IP ADDRESS
// let localhost = Ipv4Addr::LOCALHOST;
let localhost = Ipv4Addr::UNSPECIFIED;
let socket_ip_address = SocketAddr::new(IpAddr::V4(localhost), 80);
let socket2_ip_address = socket_ip_address.into();
// CREATE ICMP SOCKET
let socket2_ipv4_socket = socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::DGRAM,
Some(socket2::Protocol::ICMPV4),
)
.unwrap();
// BIND TO LOCAL ADDRESS
socket2_ipv4_socket
.bind(&socket2_ip_address)
.expect(&format!(
"Failed binding to Ipv4 address {:?}",
&socket_ip_address
));
// CREATE STD SOCKET FROM SOCKET2 SOCKET
let raw_ipv4_socket = socket2_ipv4_socket.as_raw_fd();
let std_ipv4_socket = unsafe { std::net::UdpSocket::from_raw_fd(raw_ipv4_socket) };
std_ipv4_socket.set_read_timeout(Some(Duration::from_millis(100)))?;
let socket_arc = Arc::new(std_ipv4_socket);
let dest = "142.250.69.206:0";
let mut buffer = [0; 1024];
let socket_clone = Arc::clone(&socket_arc);
std::thread::spawn(move || {
let packet_slice = &mut [0; 56];
let mut buf = vec![0; 8 + 56]; // 8 bytes of header, then payload
let mut packet = icmp::echo_request::MutableEchoRequestPacket::new(&mut buf[..]).unwrap();
packet.set_icmp_type(icmp::IcmpTypes::EchoRequest);
packet.set_identifier(1);
packet.set_sequence_number(1);
packet.set_payload(packet_slice);
// Calculate and set the checksum
let icmp_packet = icmp::IcmpPacket::new(packet.packet()).unwrap();
let checksum = icmp::checksum(&icmp_packet);
packet.set_checksum(checksum);
loop {
socket_clone.send_to(&mut packet.packet(), dest).unwrap();
std::thread::sleep(Duration::from_millis(1000));
}
});
loop {
if let Ok((bytes_read, from)) = socket_arc.recv_from(&mut buffer) {
info!("Received {} bytes from {:?}", bytes_read, from);
let ipv4_packet = pnet_packet::ipv4::Ipv4Packet::new(&buffer).unwrap();
let _icmp_packet = pnet_packet::icmp::IcmpPacket::new(ipv4_packet.payload()).unwrap();
let _udp_packet = pnet_packet::udp::UdpPacket::new(&ipv4_packet.payload()).unwrap();
info!("Received {:?}", ipv4_packet);
}
}
}
這是 Cargo.toml 的依賴部分:
[dependencies]
pnet_packet = "0.29"
log = "0.4"
env_logger = "0.9"
socket2 = "0.4"
首先,我希望有人確認這種行為。 其次,我想幫助找出問題所在。
謝謝
騙我。 要接收 IP 標頭,我必須使用 RAW 套接字而不是 DGRAM。 將套接字創建更改為:
let socket2_ipv4_socket = socket2::Socket::new(
socket2::Domain::IPV4,
socket2::Type::RAW,
Some(socket2::Protocol::ICMPV4),
)
.unwrap();
隨之而來的警告是,您必須為二進制文件提供正確的功能。 像這樣的東西,例如:
cargo build && sudo sudo setcap cap_net_raw+ep target/debug/ping_playground && target/debug/ping_playground
結束問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.