目前在使用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;
}
}