当前位置: 首页 > 工具软件 > jNetPcap > 使用案例 >

Jnetpcap实现捕获过滤器和显示过滤器

钱欣然
2023-12-01

目前在使用Jnetpcap开发一款实时流量监控系统,其中需要实现捕获过滤器和显示过滤器,记录一下,也很简单,更多是调用API。

 

一、捕获过滤器

主要是利用PcapBpfProgram这个类

使用方法:

PcapBpfProgram filter = new PcapBpfProgram();    
int res = pcap.compile(filter, expression, 1, 0);
pcap.setFilter(filter);

1.PcapBpfProgram filter = new PcapBpfProgram();用于创建一个过滤器对象,(PcapBpfProgram 遵循bpf过滤规则,详细规则参考https://www.wireshark.org/docs/man-pages/pcap-filter.html)此时仅仅创建此对象并不够,他并不知道要按照什么规则过滤,需要将过滤器与表达式对应起来。

2.对应起来的方法并不是操作filter对象,而是使用pcap.compile(PcapBpfProgram program, String expression, int optimize, int netmask)   参数一是第一步创建的filter,参数二是expression过滤表达式,参数三是是否优化(1表示进行优化,任何其他值表示否),参数四是确定广播地址所需的网络掩码(仅仅过滤可以无视),重点是前两个参数,返回值用于确定表达式是否符合规范,返回-1表示有错误。

3.为Pcap对象设置过滤器。

完整示例代码:


import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapBpfProgram;
import org.jnetpcap.PcapIf;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.PcapPacketHandler;



public class Testt {

	public static void main(String[] args) {
		
		// 获取当前机器的网卡信息
		StringBuilder errbuf = new StringBuilder();

		List<PcapIf> alldevs = new ArrayList<PcapIf>();

		int r = Pcap.findAllDevs(alldevs, errbuf);
		if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
			System.err.printf("Can't read list of devices, error is %s", errbuf.toString());
			return;
		}
		
		// 输出网卡信息
		System.out.println("Network devices found:");

		int i = 0;
		for (PcapIf device : alldevs) {
			System.out.printf("#%d: %s [%s]\n", i++, device.getName(), device.getDescription());
		}

		// 选择要监控的网卡
		PcapIf device = alldevs.get(1);

		// 打开设备

		// openlive方法:这个方法打开一个和指定网络设备有关的,活跃的捕获器

		// 参数:snaplen指定的是可以捕获的最大的byte数,
		// 如果 snaplen的值 比 我们捕获的包的大小要小的话,
		// 那么只有snaplen大小的数据会被捕获并以packet data的形式提供。
		// IP协议用16位来表示IP的数据包长度,所有最大长度是65535的长度
		// 这个长度对于大多数的网络是足够捕获全部的数据包的

		// 参数:flags promisc指定了接口是promisc模式的,也就是混杂模式,
		// 混杂模式是网卡几种工作模式之一,比较于直接模式:
		// 直接模式只接收mac地址是自己的帧,
		// 但是混杂模式是让网卡接收所有的,流过网卡的帧,达到了网络信息监视捕捉的目的

		// 参数:timeout 这个参数使得捕获报后等待一定的时间,来捕获更多的数据包,
		// 然后一次操作读多个包,不过不是所有的平台都支持,不支持的会自动忽略这个参数

		// 参数:errbuf pcap_open_live()失败返回NULL的错误信息,或者成功时候的警告信息

		int snaplen = 64 * 1024;
		int flags = Pcap.MODE_PROMISCUOUS;// 混杂模式 接受所有经过网卡的帧
		int timeout = 10 * 1000;
		Pcap pcap = Pcap.openLive(device.getName(), snaplen, flags, timeout, errbuf);

		if (pcap == null) {
			System.err.printf("Error while opening device for capture: " + errbuf.toString());
			return;
		}

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

		// 创建一个数据包处理器
		PcapPacketHandler<String> jpacketHandler = new PcapPacketHandler<String>() {

			public void nextPacket(PcapPacket packet, String user) {

				System.out.printf("%s Received at %s caplen=%-4d len=%-4d %s\n", PacketParse.parseProtocol(packet),
						new Date(packet.getCaptureHeader().timestampInMillis()), packet.getCaptureHeader().caplen(), // Length
						// actually
						// captured
						packet.getCaptureHeader().wirelen(), // Original length
						user // User supplied object
				);
			}
		};
		// 循环监听
		pcap.loop(Integer.MAX_VALUE, jpacketHandler, "jNetPcap rocks!");
		// 监听结束后关闭
		pcap.close();
	}
}

如果过滤表达式有错误,可使用pcap.getErr()获取详细错误


二、显示过滤器

Pacp并没有直接提供显示过滤器接口,wireshark显示过滤器也是和捕获过滤器不同的另一套过滤系统。

但仔细查看jnetpcap API可以发现

	/**
	 * Returns if a given filter applies to an offline packet. This function is
	 * used to apply a filter to a packet that is currently in memory. This
	 * process does not need to open an adapter; we need just to create the proper
	 * filter (by settings parameters like the snapshot length, or the link-layer
	 * type) by means of the pcap_compile_nopcap(). The current API of libpcap
	 * does not allow to receive a packet and to filter the packet after it has
	 * been received. However, this can be useful in case you want to filter
	 * packets in the application, instead of into the receiving process. This
	 * function allows you to do the job.
	 * 
	 * @param program
	 *          bpf filter
	 * @param header
	 *          packets header
	 * @param buffer
	 *          buffer containing packet data
	 * @return snaplen of the packet or 0 if packet should be rejected
	 * @since 1.2
	 */
	@LibraryMember("pcap_offline_filter")
	public static native int offlineFilter(PcapBpfProgram program,
			PcapHeader header,
			JBuffer buffer);

WinPcap.offlineFilter()提供了用于显示过滤器的接口

 

使用方法

PcapBpfProgram filter = new PcapBpfProgram();
int res = winPcap.compile(filter, expression, 1, 0);
if (res != 0) {
	System.out.println("Filter error:" + winPcap.getErr());
	return -1;
}
int flag = WinPcap.offlineFilter(filter, packet.getCaptureHeader(), packet);

代码和前边捕获过滤器很相似,可将单个PcapPacket对象进行过滤. flag==0表示没有通过过滤规则的数据包(并不是过滤表达式出错)

 

示例代码:



import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapBpfProgram;
import org.jnetpcap.PcapIf;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.PcapPacketHandler;
import org.jnetpcap.winpcap.WinPcap;



public class Testt {

	public static void main(String[] args) {

		// 获取当前机器的网卡信息
		StringBuilder errbuf = new StringBuilder();

		List<PcapIf> alldevs = new ArrayList<PcapIf>();

		int r = Pcap.findAllDevs(alldevs, errbuf);
		if (r == Pcap.NOT_OK || alldevs.isEmpty()) {
			System.err.printf("Can't read list of devices, error is %s", errbuf.toString());
			return;
		}

		// 输出网卡信息
		System.out.println("Network devices found:");

		int i = 0;
		for (PcapIf device : alldevs) {
			System.out.printf("#%d: %s [%s]\n", i++, device.getName(), device.getDescription());
		}

		// 选择要监控的网卡
		PcapIf device = alldevs.get(1);

		// 打开设备

		// openlive方法:这个方法打开一个和指定网络设备有关的,活跃的捕获器

		// 参数:snaplen指定的是可以捕获的最大的byte数,
		// 如果 snaplen的值 比 我们捕获的包的大小要小的话,
		// 那么只有snaplen大小的数据会被捕获并以packet data的形式提供。
		// IP协议用16位来表示IP的数据包长度,所有最大长度是65535的长度
		// 这个长度对于大多数的网络是足够捕获全部的数据包的

		// 参数:flags promisc指定了接口是promisc模式的,也就是混杂模式,
		// 混杂模式是网卡几种工作模式之一,比较于直接模式:
		// 直接模式只接收mac地址是自己的帧,
		// 但是混杂模式是让网卡接收所有的,流过网卡的帧,达到了网络信息监视捕捉的目的

		// 参数:timeout 这个参数使得捕获报后等待一定的时间,来捕获更多的数据包,
		// 然后一次操作读多个包,不过不是所有的平台都支持,不支持的会自动忽略这个参数

		// 参数:errbuf pcap_open_live()失败返回NULL的错误信息,或者成功时候的警告信息

		int snaplen = 64 * 1024;
		int flags = Pcap.MODE_PROMISCUOUS;// 混杂模式 接受所有经过网卡的帧
		int timeout = 10 * 1000;
		Pcap pcap = Pcap.openLive(device.getName(), snaplen, flags, timeout, errbuf);

		if (pcap == null) {
			System.err.printf("Error while opening device for capture: " + errbuf.toString());
			return;
		}

		PcapBpfProgram filter = new PcapBpfProgram();
		String expression = "tcpp";
		WinPcap winPcap = WinPcap.openDead(1, snaplen);
		int res = winPcap.compile(filter, expression, 1, 0);
		if (res != 0) {
			System.out.println("Filter error:" + winPcap.getErr());
		}
		
		// 创建一个数据包处理器
		PcapPacketHandler<String> jpacketHandler = new PcapPacketHandler<String>() {

			public void nextPacket(PcapPacket packet, String user) {

				int flag = WinPcap.offlineFilter(filter, packet.getCaptureHeader(), packet);

				if (flag != 0) {
					System.out.printf("%s Received at %s caplen=%-4d len=%-4d %s\n", PacketParse.parseProtocol(packet),
							new Date(packet.getCaptureHeader().timestampInMillis()), packet.getCaptureHeader().caplen(), // Length
							// actually
							// captured
							packet.getCaptureHeader().wirelen(), // Original length
							user // User supplied object
					);
				}
			}
		};
		// 循环监听
		pcap.loop(Integer.MAX_VALUE, jpacketHandler, "jNetPcap rocks!");
		// 监听结束后关闭
		pcap.close();
	}
}

三、最后提供一个用于过滤检测表达式是否正确的方法(需要一个用于测试的test.pcap文件)



import java.util.ArrayList;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapBpfProgram;
import org.jnetpcap.PcapIf;



public class ExpressionCheck {
	
	private static PcapBpfProgram filter = new PcapBpfProgram();
	
	//检查表达式是否正确
	/**
	 * @param expression
	 * @return	0表示表达式正确
	 * 			-1表示表达式错误
	 * 			-2表示其他错误
	 */
	public static int checkFilterExpression(String expression) {

		// 获取当前机器的网卡信息
		StringBuilder errbuf = new StringBuilder();

		List<PcapIf> alldevs = new ArrayList<PcapIf>();

		//用于测试表达式
		String filepath = "test.pcap";
		
		Pcap pcap = Pcap.openOffline(filepath, errbuf);

		if (pcap == null) {
			System.err.printf("Error while opening device for capture: " + errbuf.toString());
			return -2;
		}

		int res = pcap.compile(filter, expression, 0, 0);
		pcap.setFilter(filter);

		if (res != 0) {
			System.out.println("Filter error:" + pcap.getErr());
			return -1;
		}
		return 0;
	}
}

 

 类似资料: