Jnetpcap抓取IP数据包分析
实验目的
- 捕获IP数据包并解析,显示并写入日志文件。我是自己边学边写的奥,在做TCP/IP的课程设计,一直到验收完我才会发布。
- 一些基础知识大家自己去了解吧包括网络编程,以太网对数据帧的封装,IP数据报格式。
- 我的实验环境是:Windows,Eclipse,Java,Jnetpcap。
基础知识
- 以太网帧结构:数据帧(Frame)是数据链路层的数据单元,以太网的帧结构注意帧头的信息(MAC地址,协议类型等)。
- 数据包(Packet):TCP/IP协议通信传输中的数据单位,封装在Frame中。
实验步骤
一、查找并获取网络接口名称,以便我们可以告诉jNetPcap打开一个或多个以进行读取。因此,首先我们查询系统上的接口列表:
public static int findAllDevs(java.util.List < PcapIf > alldevs,
java.lang.StringBuilder errbuf)
参数:
alldevs-列表中填充了PcapIf接口对象;列表一定不能一成不变
errbuf -错误缓冲区,包含错误消息,失败时为字符串
返回值:
失败时返回-1,在这种情况下,errbuf会填入适当的错误消息;成功返回0
- alldevs列表中的每个元素的类型为pcap_if_t,其结构体和pcap_if相同,用来描述网络设备结构,结构成员如下:
struct pcap_if {
struct pcap_if *next; //指向列表中下一个元素的指针
char *name; //该字符串为设备传递给pcap_open_live()的名称
char *description; //如果不为NULL,则为指向字符串的指针,该字符串给出了设备可读的描述
struct pcap_addr *addresses; //指向接口地址列表中第一个元素的指针
bpf_u_int32 flags; //如果接口是回送接口,则设置PCAP_IF_LOOPBACK
};
- 上面结构体的 pcap_addr 的数据结构,同样有五个属性:ip地址、子网掩码、广播地址、目标地址
struct pcap_addr {
struct pcap_addr *next; //指向列表中下一个元素的指针;列表的最后一个元素为NULL
struct sockaddr *addr; //指向包含地址的struct sockaddr的指针
struct sockaddr *netmask; //如果不为NULL,则指向结构sockaddr的指针,该结构包含与addr指向的地址相对应的网络掩码
struct sockaddr *broadaddr; //指向结构sockaddr的指针,该结构包含与addr指向的地址相对应的广播地址;如果接口不支持广播,则可以为null
struct sockaddr *dstaddr; //指向结构sockaddr的指针,其中包含与addr指向的地址相对应的目标地址;如果接口不是点对点接口,则可以为null
};
- 还有关于sockaddr,sockaddr_in,in_addr的结构体,可以理解为就是网络编程时表示IP地址,具体的IP地址有多种表示方法:网络字节序,点分十进制等等,也有具体的函数实现各种表示方式之间的转换。
二、打开与指定的网络接口设备关联的实时捕获。
public static Pcap openLive(java.lang.String device,
int snaplen,
int promisc,
int timeout,
java.lang.StringBuilder errbuf)
参数:
device:指定要打开的网络设备的字符串
snaplen:指定要捕获的最大字节数
promisc:指定是否将接口置于混杂模式
timeout:以毫秒为单位指定读取超时
errbuf:返回错误或警告文本
返回值:
原始结构pcap_t原始libpcap调用返回的C结构数据打开
-
snaplen指定的是可以捕获的最大的字节数,如果 snaplen的值比我们捕获的包的大小要小, 那么只有snaplen大小的数据会被捕获并以packet data的形式提供。
-
IP数据报的最大长度是65535,而以太网的MTU为1500 字节,为了使IP数据包能够通过,必须要进行分片。下图是使用ping命令发送5000字节ICMP请求时,使用wireshark抓的包。(IP数据包中和分片有关的字段:标识、标志、分片偏移)
-
promisc -1表示处于混杂模式,0表示非混杂。Pcap.MODE_PROMISCUOUS,该模式用于网络信息监视捕捉,监视所有经过网卡的数据包,包括不是发给本机的数据包。
三、编译过滤器并将其应用于网络接口
通常我们只对特定网络通信感兴趣。可以通过compile()和setfilter()来设置数据流过滤规则。
- 编译一个数据包过滤器,将高级过滤表达式转换为可由内核级过滤引擎解释的程序。
public int compile(PcapBpfProgram program,
java.lang.String str,
int optimize,
int netmask)
参数:
program :PcapBpfProgram过滤器对象
str :包含要编译的文本表达式的字符串
optimize :1表示进行优化,任何其他值表示没有
netmask :确定广播地址所需的网络掩码
返回值:
1表示错误,在这种情况下getErr() 可用于显示错误文本。
- PcapBpfProgram对象遵循BPF过滤规则,使用str过滤表达式来指定过滤规则。
PcapBpfProgram filter = new PcapBpfProgram();
String expression = "tcp";
int res = pcap.compile(filter, expression, 1, 0);
pcap.setFilter(filter);
if (res != 0) {
System.out.println("Filter error:" + pcap.getErr());
}
- 指定过滤程序
public int setFilter(PcapBpfProgram program)
四、捕获数据包
public <T> int loop(int cnt,
PcapPacketHandler<T> handler,
T user)
参数:
cnt -要处理的数据包数量
handler -用户提供的数据包处理程序
user -自定义不透明用户对象
返回值:
成功时为0,错误时为-1,如果使用了breakloop,则为-2中断验证
pcap_loop()一直读取数据包,直到处理cnt数据包或发生错误为止。
五、关闭pcap
必须调用close()释放所有Libpcap资源。