ICMP插座插座的问题

发布于 2025-02-08 13:32:43 字数 5029 浏览 3 评论 0原文

我已经写了一个申请,以发送和接收ICMP数据包(可以这么说)。

我已经在不同的计算机上对此进行了测试,发现代码仅在我的MacOS上运行。其他Linux机器(我在Linux的许多不同口味上进行了测试)给出了错误的结果,我什至不知道在哪里调试。

出现的内容

[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 : [],  }

预期输出,这是我的Macos 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);
        }
    }
}

这是货物的依赖部分

[dependencies]
pnet_packet = "0.29"
log = "0.4"
env_logger = "0.9"
socket2 = "0.4"

。 其次,我想帮助弄清楚什么问题。

谢谢

I've written an application to send and receive ICMP packets (a ping redo, so to speak).

I've tested this on different computers and found that the code only runs on my MacOS. Other linux machines (I tested on many different flavors of Linux) gave wrong results, and I don't even know where to debug anymore.

Expected output, this is what comes out of my 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 output (wrong output):

[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 : [],  }

Not only the number of bytes read is different, but the parsing is wrong.
Intercepting in wireshark shows that the packets that are received back are indeed the same on my MacOS and Linux machines.

Here's the minimal version that presents the problem:

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);
        }
    }
}

Here's the dependencies part of Cargo.toml:

[dependencies]
pnet_packet = "0.29"
log = "0.4"
env_logger = "0.9"
socket2 = "0.4"

First, I would like someone to confirm this behavior.
Second, I would like help in figuring out what's wrong.

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

半枫 2025-02-15 13:32:44

我。
要接收IP标头,我必须使用原始插座,而不是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

关闭问题。

Dummy me.
To receive the IP header I had to use a RAW socket and not the DGRAM.
Changing the socket creation to:

let socket2_ipv4_socket = socket2::Socket::new(
        socket2::Domain::IPV4,
        socket2::Type::RAW,
        Some(socket2::Protocol::ICMPV4),
    )
    .unwrap();

That comes with the caveat that you have to give the right capabilities to your binary. Something like this, for example:

cargo build && sudo sudo setcap cap_net_raw+ep target/debug/ping_playground && target/debug/ping_playground

Closing the question.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文