【NDPI】源码解析之深度包检测分析(一)

3 篇文章 3 订阅

(Albert、2019.4.28)

文章目录:

前言:

正文:

一、nDPI深度包检测流程:

二、重要结构体的源码分析

1、ndpi_ethdr、ndpi_iphdr、ndpi_tcphdr、ndpi_udphdr

2、ndpi_flow_struct

3、ndpi_packet_struct

4、ndpi_detection_module_struct


前言:

关于nDPI的基本功能就不在这介绍了,有兴趣了解的读者可以阅读官方的快速入门指南:https://github.com/ntop/nDPI/blob/dev/doc/nDPI_QuickStartGuide.pdf,也可以阅读我翻译过来的文章:NDPI快速入门指南(翻译自官方文档)

nDPI是在OpenDPI基础上扩展的一个库,解决了Opendpi的许多问题,并且具有比较完善的应用层协议识别功能。所以简单来说nDPI就是一个网络协议分析器,可以分析出抓取的流量是什么应用类型,比如来自facebook、ntop、qq还是推特等应用。当然不限于这些它自带的协议分析器,我们同样可以通过向引擎添加自定义的协议分析器,理论上可以分析任何我们想分析的协议。

nDPI源代码在编译后会生成两个部分,第一个部分是通过向Linux内核中的插件netfilter中注册nDPI的协议分析引擎,生成内核层的xt_ndpi.ko模块,用来分析实时的流量信息。编译的另一个结果就是生成了一个应用层的lib库,给ndpiReader这个工具提供一个函数库,可以用来分析抓包工具提供的pcap文件或者底层网卡提供的数据包,这个之后会有一系列文章向大家展示如何使用gdb工具来调试ndpiReader,用来研究ndpiReader分析流量的过程以及它是如何引用nDPI这个库的。

在这个系列呢主要是解读生成内核层模块的源代码,也就是研究这个库是如何进行深度包检测的。下面是正式分析部分:


正文:

一、nDPI深度包检测流程:

在分析nDPI源码之前我们首先应该弄清楚其分析数据的整个流程,它是怎么通过检测一个个数据包,最后生成我们所看到的流量信息的。ndpi的报文分析过程大致有如下几步:

  • 首先是从网络上抓包,不同系统下数据包的格式可能不尽相同,在Linux下数据格式为sk_buff;
  • 抓取到数据包后,搜集其layer3、layer4这两层的报头信息;
  • 查看链接跟踪(如果是之前已经标示过的流,我们可以直接获取其数据包所属的协议类型,更新协议包的数量);
  • 如果从链接跟踪中没有获取到流的协议类型,表示之前未标示,即这是第一次检测到该类型的数据包,下面进入ndpi的深度报文检测过程;
  • 首先是对数据包进行协议猜测,调用相应的协议分析器进行分析,成功后可返回其协议类型;
  • 猜测协议错误、此时ndpi将会分类遍历所有相关的协议分析器进行分析,直至分析出结果或者遍历完所有协议分析器停止;
  • 将分析出的协议类型标记到链接跟踪中,这样便于之后直接从连接跟踪的数据包中获取其协议类型。

检测流程简单总结就是,第一步是抓取数据包,从数据链路层开始进行解析,逐层往上走,到传输层,检测数据包是基于tcp或者udp或者两者都不是的协议传输,在到应用层,依靠端口来分析,通过分析包的内容来提取特征码等手段来判断是何种协议类型。显然,某个确定的应用流往往拥有其特定的特征,比如特定的IP地址,域名、url等等,对这些信息进行识别,可以较为准确地判断出数据包所属应用类型。例如nDPI中列举的一个数组就包含了这些内容:

static ndpi_network host_protocol_list[] = {
/*
    SoundCloud 
  */
  { 0x22FB2FEE /* 34.251.47.238 */, 32, NDPI_PROTOCOL_SOUNDCLOUD },
  { 0x23A06456 /* 35.160.100.86 */, 32, NDPI_PROTOCOL_SOUNDCLOUD },
  { 0x36C0CA58 /* 54.192.202.88 */, 32, NDPI_PROTOCOL_SOUNDCLOUD },
  ... ...
  /*
    WeChat。//微信
    origin AS132203, AS132591, AS45090
  */

   { 0xCBCD93AB /* 203.205.147.171/32 */, 32, NDPI_PROTOCOL_WECHAT },
   { 0xCBCD93AD /* 203.205.147.173/32 */, 32, NDPI_PROTOCOL_WECHAT },
   { 0xCBCD97A2 /* 203.205.151.162/32 */, 32, NDPI_PROTOCOL_WECHAT },
   { 0x67071E25 /* 103.7.30.37/32 */, 32, NDPI_PROTOCOL_WECHAT },
   ... ...
   /*
    GitHub, Inc.
    origin AS36459
  */

  { 0xC01EFC00 /* 192.30.252.0/22 */, 22, NDPI_PROTOCOL_GITHUB },
  ... ...
  };
/***域名匹配***/
ndpi_protocol_match host_match[] = {
  { "amazon.",                  "Amazon",           NDPI_PROTOCOL_AMAZON, NDPI_PROTOCOL_CATEGORY_WEB, NDPI_PROTOCOL_SAFE },
  { "amazon.com",               "Amazon",           NDPI_PROTOCOL_AMAZON, NDPI_PROTOCOL_CATEGORY_WEB, NDPI_PROTOCOL_SAFE },
  { "images-amazon.com",        "Amazon",           NDPI_PROTOCOL_AMAZON, NDPI_PROTOCOL_CATEGORY_WEB, NDPI_PROTOCOL_ACCEPTABLE },
  { "amazonaws.com",            "Amazon",           NDPI_PROTOCOL_AMAZON, NDPI_PROTOCOL_CATEGORY_WEB, NDPI_PROTOCOL_UNSAFE },
  { "amazon-adsystem.com",      "Amazon",           NDPI_PROTOCOL_AMAZON, 
  ... ...
  };


/****内容匹配*****/
ndpi_protocol_match content_match[] = {
  { "audio/mpeg",                   NULL,           NDPI_CONTENT_MPEG, NDPI_PROTOCOL_CATEGORY_MEDIA, NDPI_PROTOCOL_FUN },
  { "audio/x-mpeg",                 NULL,           NDPI_CONTENT_MPEG, NDPI_PROTOCOL_CATEGORY_MEDIA, NDPI_PROTOCOL_FUN },
  { "audio/mpeg3",                  NULL,           NDPI_CONTENT_MPEG, NDPI_PROTOCOL_CATEGORY_MEDIA, NDPI_PROTOCOL_FUN },
  { "audio/mp4a",                   NULL,           NDPI_CONTENT_MPEG, 
  ... ...
  };

二、重要结构体的源码分析

在了解了nDPI检测流量的基本过程之后,我们开始读源代码部分,在解读他的各种函数及功能之前,我们首先应该分析ndpi源码中的几个非常重要的结构体,他们通常作为非常重要的参数跟结构在各种函数之间传递,贯穿了整个工作流程。

1、ndpi_ethdrndpi_iphdrndpi_tcphdrndpi_udphdr

这几个结构体定义在ndpi_typedefs.h头文件中。这几个结构体比较简单,用于存储各层的数据包头信息(数据在传输时每经过一层都会加上其相应的报头信息,如IP报头)。

1.1、ndpi_ethdr 对应的是数据链路层(以太网)头信息:


/* +++++++++++++++ Ethernet header (IEEE 802.3) +++++++++++++++ */

PACK_ON
struct ndpi_ethhdr
{
  u_char h_dest[6];       /* 目的以太网地址*/
  u_char h_source[6];     /* 源以太网地址   */
  u_int16_t h_proto;      /* 数据长度 (<= 1500) or ID协议类型 (>=1536) */
} PACK_OFF;

1.2、ndpi_iphdr layer2(IP层)报头信息:


/* ++++++++++++++++++++++++ IP header ++++++++++++++++++++++++ */

PACK_ON
struct ndpi_iphdr {
#if defined(__LITTLE_ENDIAN__)
  u_int8_t ihl:4, version:4;
#elif defined(__BIG_ENDIAN__)
  u_int8_t version:4, ihl:4;
#else
# error "Byte order must be defined"
#endif
  u_int8_t tos;         //用于服务的区分
  u_int16_t tot_len;   //总长度
  u_int16_t id;         //ID标志
  u_int16_t frag_off;  //片偏移 
  u_int8_t ttl;        //当前跳数
  u_int8_t protocol;   //协议
  u_int16_t check;     //检查
  u_int32_t saddr;    //源IP
  u_int32_t daddr;    //目的IP
} PACK_OFF;

1.3、ndpi_tcphdr 、ndpi_udphdr   针对第三层协议报头信息


/* +++++++++++++++++++++++ TCP header +++++++++++++++++++++++ */

PACK_ON
struct ndpi_tcphdr
{
  u_int16_t source;   //源端口
  u_int16_t dest;     //目的端口
/****下面都是tcp建立连接所需要的设置***/
  u_int32_t seq;   //发送数据包的第一字节序列号   
  u_int32_t ack_seq;//确认序列号
#if defined(__LITTLE_ENDIAN__)
  u_int16_t res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1;
#elif defined(__BIG_ENDIAN__)
  u_int16_t doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1;
#else
# error "Byte order must be defined"
#endif
  u_int16_t window;   //接受缓冲区的空闲空间
  u_int16_t check;    
  u_int16_t urg_ptr;  //紧急指针
} PACK_OFF;

/* +++++++++++++++++++++++ UDP header +++++++++++++++++++++++ */

PACK_ON
struct ndpi_udphdr
{
  u_int16_t source;//源端口
  u_int16_t dest;  //目的端口
  u_int16_t len;   //数据传输长度
  u_int16_t check;
} PACK_OFF;

关于这两个协议报文详细的报头字段就不详细分析了,详细的可以去查阅tcp协议传输的相关知识。在这里可以贴出tcp协议的报文结构,用于分析:

2、ndpi_flow_struct

这个结构体用于存储一个数据流的相关信息,贯穿于整个流程中,从初始化,到流量的协议识别,再到最后生成识别结果都必须用到它。一个数据流可能会有多个数据包,在结构体中定义了很多标识变量,用于区别不同的数据包和减少重复多余的一些工作。还有一些其他的功能见源代码分析:

struct ndpi_flow_struct {
  u_int16_t detected_protocol_stack[NDPI_PROTOCOL_SIZE];//协议识别结果
#ifndef WIN32
  __attribute__ ((__packed__))
#endif
  u_int16_t protocol_stack_info;

  /* 初始化参数, 用于设置时间戳,... */
  u_int16_t guessed_protocol_id, guessed_host_protocol_id, guessed_category, guessed_header_category;      //用于协议猜测的变量
  u_int8_t protocol_id_already_guessed:1, host_already_guessed:1, init_finished:1, setup_packet_direction:1, packet_direction:1, check_extra_packets:1;

  /*
    如果 ndpi_struct->direction_detect_disable == 1
    tcp 序列号链接跟踪
  */
  u_int32_t next_tcp_seq_nr[2];  

  u_int8_t max_extra_packets_to_check;
  u_int8_t num_extra_packets_checked;
  u_int8_t num_processed_pkts; /* <= WARNING it can wrap but we do expect people to giveup earlier */
  
  int (*extra_packets_func) (struct ndpi_detection_module_struct *, struct ndpi_flow_struct *flow);

  /*
    the tcp / udp / other l4 value 共用体
    可以用来减少tcp或者udp协议传输统计时的bite数
  */
    //l4共用体,一个流只会使用两者当中的一种协议进行传输
  union {
    struct ndpi_flow_tcp_struct tcp;
    struct ndpi_flow_udp_struct udp;
  } l4;

  /*
    指向源或者目的的指针,用来识别这次连接的服务器
  */
  struct ndpi_id_struct *server_id;  //id
  /* HTTP主机or DNS 请求*/
  u_char host_server_name[256];  

  /*
    下面这个结构体并不会放入protos结构体内部,因为HTTP被许多子协议使用,如Facebook、Google……
    在平常生活中,很难知道他们什么时候会被使用,所以将这个结构体暂时放在外面。
  */
  struct {
    ndpi_http_method method;
    char *url, *content_type;
    u_int8_t num_request_headers, num_response_headers;
    u_int8_t request_version; /* 0=1.0 and 1=1.1. Create an enum for this? */
    u_int16_t response_status_code; /* 200, 404, etc. */
  } http;

  union {
    /* 仅有的一些字段,被nDPI and ntopng所使用*/
    struct {
      u_int8_t num_queries, num_answers, reply_code;
      u_int16_t query_type, query_class, rsp_type;
      ndpi_ip_addr_t rsp_addr; /* The first address in a DNS response packet */
    } dns;

    struct {
      u_int8_t request_code;
      u_int8_t version;
    } ntp;

    struct {
      struct {
	char client_certificate[64], server_certificate[64], server_organization[64];
      } ssl;
      
      struct {
	u_int8_t num_udp_pkts, num_processed_pkts, num_binding_requests, is_skype;
      } stun;

      /*有通过ssl的stun报文,这两个结构需要同时存在。*/
    } stun_ssl;  

    struct {
      char client_signature[48], server_signature[48];
    } ssh;

    struct {
      char answer[96];
    } mdns;

    struct {
      char version[96];
    } ubntac2;

    struct {
      /* Via HTTP User-Agent */
      u_char detected_os[32];
      /* Via HTTP X-Forwarded-For ,可以获得IP地址*/
      u_char nat_ip[24];
    } http;

    struct {
      /* Bittorrent hash 比特流散列 */
      u_char hash[20];
    } bittorrent;

    struct {
      char fingerprint[48];
      char class_ident[48];
    } dhcp;
  } protos;

  /*** ALL protocol specific 64 bit variables here ***/

  /* 标识连接的协议不能表示为“protocol xxx” protocols which have marked a connection as this connection cannot be protocol XXX, multiple u_int64_t */
  NDPI_PROTOCOL_BITMASK excluded_protocol_bitmask;

  ndpi_protocol_category_t category;
  
  /* NDPI_PROTOCOL_REDIS */
  u_int8_t redis_s2d_first_char, redis_d2s_first_char;
  
  u_int16_t packet_counter;		      // can be 0 - 65000
  u_int16_t packet_direction_counter[2];
  u_int16_t byte_counter[2];
  /* NDPI_PROTOCOL_BITTORRENT */
  u_int8_t bittorrent_stage;		      // can be 0 - 255

  /*下面都是一些标示协议的变量,在这就不展示出来了
    ……………………………………………………………………………………………………
    */

  /* NDPI_PROTOCOL_CSGO  csgo协议标识*/
  u_int8_t csgo_strid[18],csgo_state,csgo_s2;
  u_int32_t csgo_id2;

  /* NDPI_PROTOCOL_1KXUN || NDPI_PROTOCOL_IQIYI */
  u_int16_t kxun_counter, iqiyi_counter;

  /*下面是一些用来保存函数调用的内部结构体*/
  struct ndpi_packet_struct packet;
  struct ndpi_flow_struct *flow;
  struct ndpi_id_struct *src;
  struct ndpi_id_struct *dst;
};

3、ndpi_packet_struct

看结构体名称就知道,这是一个用来存储数据包相关信息的结构体

struct ndpi_packet_struct {
  const struct ndpi_iphdr *iph;  //IP报头信息
#ifdef NDPI_DETECTION_SUPPORT_IPV6  //是否预定义,支持IPV6
  const struct ndpi_ipv6hdr *iphv6; //IPV6报头
#endif
  const struct ndpi_tcphdr *tcp;    //tcp协议报头
  const struct ndpi_udphdr *udp;    //udp头
  const u_int8_t *generic_l4_ptr;	/* is set only for non tcp-udp traffic */
  const u_int8_t *payload;          //数据包中的负载信息

  u_int32_t tick_timestamp;         //时间戳
  u_int64_t tick_timestamp_l;

  u_int16_t detected_protocol_stack[NDPI_PROTOCOL_SIZE];
  u_int8_t detected_subprotocol_stack[NDPI_PROTOCOL_SIZE];

#ifndef WIN32
  __attribute__ ((__packed__))
#endif
  u_int16_t protocol_stack_info;

  struct ndpi_int_one_line_struct line[NDPI_MAX_PARSE_LINES_PER_PACKET];
  /* HTTP headers */
  /*下面是很多关于HTTP协议的变量定义,就不全部列出了*/
  struct ndpi_int_one_line_struct host_line;
  
  …………………………

  /*在这里定义数据包各层的信息,包括长度、字节数、协议等*/
  u_int16_t l3_packet_len;    //三层
  u_int16_t l4_packet_len;    //四层
  u_int16_t payload_packet_len;
  u_int16_t actual_payload_len;
  u_int16_t num_retried_bytes;
  u_int16_t parsed_lines;
  u_int16_t parsed_unix_lines;
  u_int16_t empty_line_position;
  u_int8_t tcp_retransmission;
  u_int8_t l4_protocol;

  u_int8_t ssl_certificate_detected:4, ssl_certificate_num_checks:4;
  u_int8_t packet_lines_parsed_complete:1,
    packet_direction:1,  //数据包传输方向
    empty_line_position_set:1;
};

4、ndpi_detection_module_struct

检测模块结构体,非常重要的一个结构体,可以说是整个检测过程中的核心结构体,所有协议分析器都将自己的一些信息保存在这个结构体中,也存储了一些全局变量,在初始化过程中,由ndpi_init_detection_module()函数生成并返回。

struct ndpi_detection_module_struct {
  NDPI_PROTOCOL_BITMASK detection_bitmask;  //位掩码,可以用于标识不同的协议
  NDPI_PROTOCOL_BITMASK generic_http_packet_bitmask;

  u_int32_t current_ts;           //时间戳
  u_int32_t ticks_per_second;     

#ifdef NDPI_ENABLE_DEBUG_MESSAGES
  void *user_data;
#endif
  char custom_category_labels[NUM_CUSTOM_CATEGORIES][CUSTOM_CATEGORY_LABEL_LEN];
  /* callback function buffer 回调函数数组,每个协议都会有其对应的回调函数,为每个协议绑定具体的处理函数,当进行协议检测时会逐个进行遍历,调用相应的协议检测函数*/
  struct ndpi_call_function_struct callback_buffer[NDPI_MAX_SUPPORTED_PROTOCOLS + 1];
  u_int32_t callback_buffer_size;   //回调函数数,也是支持的协议数

/*根据NDPI_PROTOCOL_BITMASK细分成tcp_no_payload、tcp_payload、udp、non_tcp_udp协议类型*/
//基于tcp协议,且无负载信息
  struct ndpi_call_function_struct callback_buffer_tcp_no_payload[NDPI_MAX_SUPPORTED_PROTOCOLS + 1];
  u_int32_t callback_buffer_size_tcp_no_payload;

//基于tcp,带有负载信息的协议
  struct ndpi_call_function_struct callback_buffer_tcp_payload[NDPI_MAX_SUPPORTED_PROTOCOLS + 1];
  u_int32_t callback_buffer_size_tcp_payload;


//基于udp
  struct ndpi_call_function_struct callback_buffer_udp[NDPI_MAX_SUPPORTED_PROTOCOLS + 1];
  u_int32_t callback_buffer_size_udp;

//既不是基于tcp也不是udp的协议
  struct ndpi_call_function_struct callback_buffer_non_tcp_udp[NDPI_MAX_SUPPORTED_PROTOCOLS + 1];
  u_int32_t callback_buffer_size_non_tcp_udp;

/*默认端口数节点,之后会构成二叉树,根据端口查找协议*/
  ndpi_default_ports_tree_node_t *tcpRoot, *udpRoot; 

  ndpi_log_level_t ndpi_log_level; /* default error */

#ifdef NDPI_ENABLE_DEBUG_MESSAGES
  /* debug callback, only set when debug is used */
  ndpi_debug_function_ptr ndpi_debug_printf;
  const char *ndpi_debug_print_file;
  const char *ndpi_debug_print_function;
  u_int32_t ndpi_debug_print_line;
  NDPI_PROTOCOL_BITMASK debug_bitmask;
#endif

  /* misc parameters */
  u_int32_t tcp_max_retransmission_window_size;

  u_int32_t directconnect_connection_ip_tick_timeout;

  /* subprotocol registration handler 注册子协议处理函数数组*/
  struct ndpi_subprotocol_conf_struct subprotocol_conf[NDPI_MAX_SUPPORTED_PROTOCOLS + 1];

  u_int ndpi_num_supported_protocols;
  u_int ndpi_num_custom_protocols;   //自定义协议数量

  /* HTTP/DNS/HTTPS host 匹配,此处在ndpi_content_match.c和proto.txt中定义 */
  ndpi_automa host_automa,                     /* Used for DNS/HTTPS域名匹配 */
    content_automa,                            /* Used for HTTP subprotocol_detection ,内容类型匹配,例如:NDPI_CONTENT_MPEG*/
    subprotocol_automa,                        /* Used for HTTP subprotocol_detection 文档自定义proto.txt的匹配*/
    bigrams_automa, impossible_bigrams_automa; /* TOR双字节的匹配 */

/*自定义的类别*/
  struct {
#ifdef HAVE_HYPERSCAN
    struct hs *hostnames;
    unsigned int num_to_load;
    struct hs_list *to_load;
#else
    ndpi_automa hostnames, hostnames_shadow;
#endif
    void *hostnames_hash;
    void *ipAddresses, *ipAddresses_shadow; /* Patricia */
    u_int8_t categories_loaded;
  } custom_categories;
  
  /* IP-based protocol detection */
  void *protocols_ptree;//此处是IP匹配的树


/*下面是部分协议的一些私有参数*/
  /* irc parameters */
  u_int32_t irc_timeout;
  /* gnutella parameters */
  u_int32_t gnutella_timeout;
  /* battlefield parameters */
  u_int32_t battlefield_timeout;
  /* thunder parameters */
  u_int32_t thunder_timeout;
  /* SoulSeek parameters */
  u_int32_t soulseek_connection_ip_tick_timeout;
  /* rtsp parameters */
  u_int32_t rtsp_connection_timeout;
  /* tvants parameters */
  u_int32_t tvants_connection_timeout;
  /* rstp */
  u_int32_t orb_rstp_ts_timeout;
  /* yahoo */
  u_int8_t yahoo_detect_http_connections;
  u_int32_t yahoo_lan_video_timeout;
  u_int32_t zattoo_connection_timeout;
  u_int32_t jabber_stun_timeout;
  u_int32_t jabber_file_transfer_timeout;

#ifdef NDPI_ENABLE_DEBUG_MESSAGES    //调试
#define NDPI_IP_STRING_SIZE 40
  char ip_string[NDPI_IP_STRING_SIZE];
#endif

  u_int8_t ip_version_limit;
  /* NDPI_PROTOCOL_BITTORRENT */
  struct hash_ip4p_table *bt_ht;
#ifdef NDPI_DETECTION_SUPPORT_IPV6
  struct hash_ip4p_table *bt6_ht;
#endif

  /* BT_ANNOUNCE */
  struct bt_announce *bt_ann;
  int    bt_ann_len;

  /* NDPI_PROTOCOL_OOKLA */
  struct ndpi_lru_cache *ookla_cache;

  /* NDPI_PROTOCOL_TINC */
  struct cache *tinc_cache;

/*默认协议结构体数组,不同的协议对应的端口信息*/
  ndpi_proto_defaults_t proto_defaults[NDPI_MAX_SUPPORTED_PROTOCOLS+NDPI_MAX_NUM_CUSTOM_PROTOCOLS];


/*HTTP、DNS、网络方向探测允许、数据导出、子字符串匹配标示位*/
  u_int8_t http_dont_dissect_response:1, dns_dont_dissect_response:1,
    direction_detect_disable:1, /* disable internal detection of packet direction */
    disable_metadata_export:1, /* No metadata is exported */
    enable_category_substring_match:1 /* Default is perfect match */
    ;
  
  void *hyperscan; /* Intel Hyperscan */
};
  

本文中分析了一些比较重要的结构体,但是nDPI库中的结构体数量很多,限于篇幅原因就不在这里挨个分析,详情请到官方页面:https://github.com/ntop/nDPI/tree/dev/src  阅读nDPI源码。之后的文章会根据nDPI的分析流程开始解读各种函数的源码,由于个人刚开始学习分析源码,水平有限,如果文中内容有错误,望读者留言指正,也可在评论区一起讨论问题,感谢。

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
根据你提供的代码片段,可以看出你正在使用 `nDPI` 库对 `DPDK` 抓取的数据进行协议识别,并将识别结果写入到新的 pcap 文件中。 在代码中,`$READER` 变量指向了一个文件路径,这个路径代表了 `nDPI` 库提供的用于读取和解析数据的程序。 首先,脚本中的 `build_results` 函数遍历了指定目录中所有以 `.pcap` 扩展名结尾的文件(通过 `PCAPS` 变量保存文件名列表),并使用 `$READER` 执行命令对每个 pcap 文件进行处理。 命令 `$READER -q -i pcap/$f -w result/$f.out` 的含义如下: - `-q` 参数表示以静默模式运行,即不输出额外的信息。 - `-i pcap/$f` 参数指定要读取的 pcap 文件路径。 - `-w result/$f.out` 参数指定要写入的结果文件路径。 这个命令的作用是将输入的 pcap 文件通过 nDPI 库进行解析,并将识别结果写入到 `result/$f.out` 文件中。 接下来,`check_results` 函数对已经生成的结果文件进行检查。它使用相似的逻辑遍历 `PCAPS` 列表中的每个 pcap 文件: - 首先,检查对应的结果文件是否存在。 - 如果结果文件存在,首先执行命令 `$READER -q -i pcap/$f -w /tmp/reader.out`,将输入文件通过 nDPI 库处理,并将结果写入到临时文件 `/tmp/reader.out` 中。 - 接着,使用 `diff` 命令比较结果文件和临时文件的差异,使用 `wc -l` 命令统计差异行数,并将结果存储在 `NUM_DIFF` 变量中。 - 如果差异行数为 0,则打印 `"$f OK"` 表示结果一致。 - 否则,打印 `"$f ERROR"` 表示结果不一致,并打印执行的命令和差异的内容。 - 最后,删除临时文件 `/tmp/reader.out`。 整个脚本的目的是使用 `nDPI` 库对抓取的数据进行协议识别,生成对应的结果文件,并在检查阶段验证识别结果是否正确。如果识别结果与期望结果不一致,则输出错误信息,并将退出状态码设为 1。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值