簡體   English   中英

ICMP socket socket2 crate 的問題

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM