Jnetpcap,准备UDP/TCP/IP/ICMP数据包。

6

最近,我正在使用Jnetpcap在网络上发送/接收原始数据包。

Jnetpcap通过Pcap.sendPacket()提供发送数据包的方法。该方法获取要发送的原始缓冲区或字节。

另一方面,有org.jnetpcap.protocol.*类来封装协议头,我们可以使用它们来解码捕获的数据包。

当我使用以下代码创建Ip4数据包时,它会导致NullPointerException:

import org.jnetpcap.protocol.network.Ip4;

public class Test {

    public static void main(String[] args) {

        Ip4 ip4 = new Ip4();

        ip4.ttl(10);

    }
}

错误:

Exception in thread "main" java.lang.NullPointerException
    at org.jnetpcap.nio.JBuffer.check(Unknown Source)
    at org.jnetpcap.nio.JBuffer.setUByte(Unknown Source)
    at org.jnetpcap.protocol.network.Ip4.ttl(Unknown Source)
    at jaeger.Test.main(Test.java:17)

如何构建数据包并通过Pcap.sendPacket()发送?

注意:我真的不想逐字节准备数据包... C/C++ libpcap 和 Jpcap 已经有了可用的功能,但我想使用 Jnetpcap!

2个回答

9

1) 代码会抛出异常,因为包装类仅在使用先前分配的缓冲区时才能正常工作。由于该库的主要目的是分析捕获的数据包的缓冲区,因此在使用它们之前需要分配一个缓冲区。

2) 数据包必须构建并提供所有必要的字节。但是可以编写代码,以便只有少量字节是真正必要的(请参阅上文)。

3) sendPacket 需要一个完整的数据包,即完整的以太网帧。因此,必须将以太网、IP、TCP头和有效负载写入缓冲区。

4) 主要思想是允许您使用包装类,即分配一个缓冲区,然后让库扫描它以发现标头,但必须提供最少量的信息(字节)。

JMemoryPacket packet = new JMemoryPacket(packetSize);
packet.order(ByteOrder.BIG_ENDIAN); 

在第12个位置,以太网帧需要一个协议类型(0x0800):
packet.setUShort(12, 0x0800);
packet.scan(JProtocol.ETHERNET_ID); 
< p >在 scan 后,可以检索到以太网实例并使用 setters

Ethernet ethernet = packet.getHeader( new Ethernet() );  
ethernet.destination(...);
...

IPv4头部需要在第14位包含版本号(0x04)和大小(0x05)信息:

packet.setUByte(14, 0x40 | 0x05);
packet.scan(JProtocol.ETHERNET_ID);  

Ip4 ip4 = packet.getHeader( new Ip4() );
ip4.type(0x06); //TCP
ip4.length( packetSize - ethernet.size() );
ip4.ttl(...);  
...

TCP头部需要大小为0x50:

packet.setUByte(46, 0x50);
packet.scan(JProtocol.ETHERNET_ID);  

Tcp tcp = packet.getHeader( new Tcp() );  
tcp.seq(...); 
...

所以,Payload:

Payload payload = packet.getHeader( new Payload() );
payload.set...(...);
...

最后:
pcap.sendPacket( ByteBuffer.wrap( packet.getByteArray(0, packet.size() )  );

5) 为了避免频繁调用扫描方法,可以一次写入所有必要的字节。


2
完美!那么packetSize呢?我如何预测合适的大小? - masoud
1
@MasoudM. 协议头长度和数据大小之和。System.out.print(packet.toString())System.out.print(packet.toHexdump()) 对于查看缓冲区和数据包非常有用。 - Juan Mellado

1

你是否遇到了如何在jNetPcap上编写子标题的问题?是的,JNetPcap需要使用字节进行发送,但可以使用帮助来填充内容。如果想在数据包内部发送一些数据,则许多Java类型都有toBytes()函数或类似函数。

编辑:
该特定Icmp类的API在这里。对于给定链接上的其他类别头,也可以做类似的操作。只需打开Google并输入“jnetpcap Icmp类引用”或其他头类型即可。

编辑2:
创建一个ICMP数据包的更简单方法是通过ICMPPacket类,它具有用于在单个构造函数调用中创建标头的简单构造函数。根据类的参考,它执行以下操作:

扩展一个IP数据包,添加一个ICMP头和ICMP数据负载。

谢谢您的回答。但是,您提供的链接是关于测试字段而不是填写它们的。 - masoud
主要的想法是找出用于测试的Icmp对象,并展示它不仅仅是手动设置位。当然,关于这方面的教程需要在其他地方寻找。 - mico
我修改了答案。现在是否回答正确的问题/是否足够完整? - mico
有一些问题,你不能简单地使用 new 来填充它。如果你能使用它,请告诉我方法。 - masoud
好的,我还没有在实践中测试过这些。我的知识仅依赖于给定的文档,这就是为什么我主要提供文档链接的原因。- 对于那个内存问题,我需要更多关于错误的具体信息。 - mico
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接