如何使用pcap4j库实现ping功能



我想实现基本的网络检查功能,以测试所提供的url是否响应(例如:ping www.google.com)。它必须提供操作信息,例如,所请求的服务不可用或无法到达主机。我能够使用icmp4j库实现它。但我想实现相同的使用pcap4j库。我想把url放在文本框中,然后点击连接按钮,它将调用pcap4j api来检查主机是否响应。

可以使用IcmpV4EchoPacketIcmpV4CommonPacketIpV4PacketEthernetPacket的builder对以太网报文创建ICMPv4 Echo (ping),通过PcapHandle.sendPacket()发送。请参考pcap4j-sample项目中的org.pcap4j.sample.SendFragmentedEcho

您将需要实现ARP来解析IP地址到MAC地址,如pcap4j-sample项目中的org.pcap4j.sample.SendArpRequest

你还需要实现一个功能,以某种方式从给定的IP地址找到下一跳(默认网关或左右)。Pcap4J没有提供支持此实现的API。(Java不提供获取路由表的API…)

你最好用java.net.InetAddress#isReachable()代替。

我花了一年多的时间才弄清楚这一点,因为我想用pcap4j创建跟踪路由,所以我做了以下工作:

  1. 获取您的IPv4地址和Mac地址,这可以通过查询PcapNetworkInterface
  2. 轻松实现
  3. 获取目标IP地址,如果您有DNS名称,您需要事先解析它。
  4. 获取目标Mac地址。
    1. 目标在同一子网:发送ARP请求来解析mac(或者,mac广播也可能工作良好)。
    2. 目标是在不同的子网:你需要得到你的网关服务器的mac,这不是那么容易。假设你有其他网络流量正在进行,你可以监听传入的数据包并获得源mac,其中源IP地址来自不同的子网,这可能是你的网关服务器的mac地址。
  5. 创建IcmpV4EchoPacket并发送
  6. 监听传入的ICMP流量,您将得到以下三个中的一个:
    1. IcmpV4EchoReplyPacket,可能是对您请求的回答(检查标识符和序列号以确保)
    2. 如果在指定的生存时间内无法达到目标,则IcmpV4TimeExceededPacket
    3. 没有,路由器和ping目标可以自由忽略和不回答您的请求

需要填充的变量:

short IDENTIFIER; // identifer may be any 16 bit interger
short SEQUENCE; // sequence may be any 16 bit integer
byte TTL; // time to live (1-255)
Inet4Address IP_TARGET; // ip address of your ping target
Inet4Address IP_ORIGIN; // your own ip address
MacAddress MAC_TARGET; // target or gateway mac address
MacAddress MAC_SOURCE; // your own mac address
PcapNetworkInterface PCAP4J_NETWORK_INTERFACE; // network interface used to execute the ping

如何发送ICMP Echo Request报文(作为EthernetPacketIpV4PacketIcmpV4CommonPacket的有效载荷):

    public Packet buildPacket() {
        IcmpV4EchoPacket.Builder icmpV4Echo = new IcmpV4EchoPacket.Builder()
                .identifier(IDENTIFIER) // optional, default zero
                .sequenceNumber(SEQUENCE); // optional, default zero
        IcmpV4CommonPacket.Builder icmpV4Common = new IcmpV4CommonPacket.Builder()
                .type(IcmpV4Type.ECHO) // type is echo
                .code(IcmpV4Code.NO_CODE) // echo request doesn't need this
                .payloadBuilder(icmpV4Echo)
                .correctChecksumAtBuild(true);
        IpV4Packet.Builder ipv4Builder = new IpV4Packet.Builder()
                .correctChecksumAtBuild(true)
                .correctLengthAtBuild(true)
                .dstAddr(IP_TARGET) // IPv4 Address where tp send the request
                .payloadBuilder(icmpV4Common)
                .protocol(IpNumber.ICMPV4) // payload is ICMPV4
                .srcAddr(IP_ORIGIN) // Your own IPv4 Address
                .tos(IpV4Rfc1349Tos.newInstance((byte) 0))
                .ttl(TTL) // time to live (1-255)
                .version(IpVersion.IPV4); // IP Version is IPv4
        EthernetPacket.Builder etherBuilder = new EthernetPacket.Builder()
                .dstAddr(MAC_TARGET) // the targets mac address
                .srcAddr(MAC_SOURCE) // your own mac address
                .type(EtherType.IPV4) // payload protocl is IPv4
                .payloadBuilder(ipv4Builder)
                .paddingAtBuild(true);
        return etherBuilder.build(); // build your packet
    }

ICMP应答或超时的监听器:

    public PacketListener buildListener() {
        return new PacketListener() {
            @Override
            public void gotPacket(Packet packet) {
                if (!(packet instanceof EthernetPacket))
                    return;
                EthernetPacket ethernetPacket = (EthernetPacket) packet;
                packet = ethernetPacket.getPayload();
                if (!(packet instanceof IpV4Packet))
                    return;
                IpV4Packet ipV4Packet = (IpV4Packet) packet;
                IpV4Header ipV4Header = ipV4Packet.getHeader();
                packet = ipV4Packet.getPayload();
                if (!(packet instanceof IcmpV4CommonPacket))
                    return;
                IcmpV4CommonPacket icmpPacket = (IcmpV4CommonPacket) packet;
                packet = icmpPacket.getPayload();
                // successful reply just measure time and done
                if (packet instanceof IcmpV4EchoReplyPacket) {
                    IcmpV4EchoReplyPacket icmpV4EchoReplyPacket = (IcmpV4EchoReplyPacket) packet;
                    IcmpV4EchoReplyHeader icmpV4EchoReplyHeader = icmpV4EchoReplyPacket.getHeader();
                    if (icmpV4EchoReplyHeader.getIdentifier() != identifier)
                        return;
                    if (icmpV4EchoReplyHeader.getSequenceNumber() != sequence)
                        return;
                    // here you got an echo reply
                    System.out.println(packet);
                    return;
                }
                // try handle time to live exceeded messages
                if (packet instanceof IcmpV4TimeExceededPacket) {
                    packet = packet.getPayload(); // original IPv4
                    if (!(packet instanceof IpV4Packet))
                        return;
                    packet = packet.getPayload(); // original ICMP common
                    if (!(packet instanceof IcmpV4CommonPacket))
                        return;
                    packet = packet.getPayload(); // original ICMP echo
                    if (!(packet instanceof IcmpV4EchoPacket))
                        return;
                    IcmpV4EchoHeader icmpV4EchoHeader = ((IcmpV4EchoPacket)packet).getHeader();
                    if (icmpV4EchoHeader.getIdentifier() != IDENTIFIER)
                        return;
                    if(icmpV4EchoHeader.getSequenceNumber() != SEQUENCE)
                        return;
                    // HERE you got an answer, that the time to live has been used up
                    System.out.println(packet);
                    return;
                }
            };
        }

组合:

    public static void main(String[] args) throws IOException, PcapNativeException, NotOpenException, InterruptedException {
        try (PcapHandle handle = PCAP4J_NETWORK_INTERFACE.openLive(1024, PromiscuousMode.PROMISCUOUS, 1000)) {
            // set filter to only get incoming ICMP traffic
            handle.setFilter("icmp and dst host " + Pcaps.toBpfString(IP_ORIGIN), BpfCompileMode.OPTIMIZE);
            // send ARP request
            Packet p = buildPacket();
            handle.sendPacket(p);
            // wait (forever) for ARP answer
            PacketListener listener = buildListener();
            handle.loop(-1, listener);
        }
    }

相关内容

  • 没有找到相关文章

最新更新