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

论坛 期权论坛 脚本     
匿名技术用户   2021-1-10 06:34   262   0

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

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP