关于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_ethdr、ndpi_iphdr、ndpi_tcphdr、ndpi_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"
#endifu_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;    //源IPu_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"
#endifu_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__))
#endifu_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 == 1tcp 序列号链接跟踪*/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 - 65000u_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  //是否预定义,支持IPV6const struct ndpi_ipv6hdr *iphv6; //IPV6报头
#endifconst 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__))
#endifu_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_MESSAGESvoid *user_data;
#endifchar 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;//基于udpstruct 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_HYPERSCANstruct hs *hostnames;unsigned int num_to_load;struct hs_list *to_load;
#elsendpi_automa hostnames, hostnames_shadow;
#endifvoid *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 40char ip_string[NDPI_IP_STRING_SIZE];
#endifu_int8_t ip_version_limit;/* NDPI_PROTOCOL_BITTORRENT */struct hash_ip4p_table *bt_ht;
#ifdef NDPI_DETECTION_SUPPORT_IPV6struct 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 */
};

5.ndpi_api.h 源代码分析及注释

/*相关宏定义*/
#ifndef __NDPI_API_H__
#define __NDPI_API_H__#include "ndpi_main.h"#ifdef __cplusplus
extern "C" {
#endif/* 与nDPI动态链接的app可以通过使用下面的宏定义来确认数据结构,跨版本适配。*/
#define NDPI_API_VERSION                      1#define SIZEOF_ID_STRUCT                      ( sizeof(struct ndpi_id_struct)   )
#define SIZEOF_FLOW_STRUCT                    ( sizeof(struct ndpi_flow_struct) )#define NDPI_DETECTION_ONLY_IPV4              ( 1 << 0 )
#define NDPI_DETECTION_ONLY_IPV6              ( 1 << 1 )#define ADD_TO_DETECTION_BITMASK              1
#define NO_ADD_TO_DETECTION_BITMASK           0
#define SAVE_DETECTION_BITMASK_AS_UNKNOWN     1
#define NO_SAVE_DETECTION_BITMASK_AS_UNKNOWN  0/***查看字符串是否使用了域名码的编码方式的函数* ( https://tools.ietf.org/html/rfc3492 )*   参数含义:* @par    buff = 指向检查的字符串的指针* @par    len  = 字符串长度* @return 1 如果这个字符串是域名码;*         else 0**/int check_punycode_string(char *buff, int len);/*** 获得流结构的长度大小** @返回ndpi_flow_struct长度大小**/u_int32_t ndpi_detection_get_sizeof_ndpi_flow_struct(void);/*** 获得id struct长度大小** @返回其长度大小**/u_int32_t ndpi_detection_get_sizeof_ndpi_id_struct(void);/*** nDPI 分配内存跟释放函数**/void * ndpi_malloc(size_t size);//动态分配长度为size的内存空间,返回指向这块内存的指针void * ndpi_calloc(unsigned long count, size_t size);  //分配内存,初始化,用于存数组void * ndpi_realloc(void *ptr, size_t old_size, size_t new_size);//增加内存大小char * ndpi_strdup(const char *s);   //拷贝字符串void   ndpi_free(void *ptr);//释放内存void * ndpi_flow_malloc(size_t size);void   ndpi_flow_free(void *ptr);/*** 搜索子字符串第一次出现的地方 -find- in -s-* 搜索仅限于字符串的第一个-slen-字符** @par    s     = 解析字符串* @par    find  = 要与-s匹配的字符串* @par    slen  =匹配 -s- and -find-的最大长度* @返回一个指针,指向定位到的子字符串;*        返回空指针则表示子字符串未找到**/char* ndpi_strnstr(const char *s, const char *find, size_t slen);/*** 与 ndpi_strnstr函数功能相似但是不区分大小写** @par    s     =解析的字符串* @par    find  = 要与 -s-匹配的字符串* @par    slen  = max length to match between -s- and -find-* @返回一个指针,指向定位到的子字符串;*        返回空指针则表示子字符串未找到**/char* ndpi_strncasestr(const char *s, const char *find, size_t slen);/***返回nDPI协议ID用于基于IP的协议检测** @par    ndpi_struct  = 创建这个结构体用于协议检测* @par    pin          =IP主机地址(必须按网络字节顺序):*详情见Man(7)IP* @返回nDPI协议ID**/u_int16_t ndpi_network_ptree_match(struct ndpi_detection_module_struct *ndpi_struct,struct in_addr *pin);/*** 初始化单个协议的匹配** @par ndpi_mod  = 创建这个结构体用于协议检测* @par match     = 结构传递进函数,用于协议匹配**/void ndpi_init_protocol_match(struct ndpi_detection_module_struct *ndpi_mod,ndpi_protocol_match *match);/*** 模块初始化函数,返回一个新的初始化过的检测模块** @返回一个初始化的检测检测模块**/struct ndpi_detection_module_struct *ndpi_init_detection_module(void);/*** 释放指定流中分配的内存** @par flow  = 需要被释放内存的流**/void ndpi_free_flow(struct ndpi_flow_struct *flow);/***使能存储器支持.* In nDPI is used for some protocol用于支持一些协议 (i.e. Skype)** @par ndpi_mod  =用于协议检测的结构体* @par host      = 域(主机)名字符串* @par port      = 端口名(无符号整型)**/void ndpi_enable_cache(struct ndpi_detection_module_struct *ndpi_mod,char* host, u_int port);/*** 清除检测模块** @par ndpi_struct  = 需要清除的检测模块**/void ndpi_exit_detection_module(struct ndpi_detection_module_struct *ndpi_struct);/*** 设置单个协议的位掩码* 此函数不会增加回调缓冲区的索引** @par label                    = 协议名字符串* @par ndpi_struct              = 检测模块* @par detection_bitmask        = 检测位掩码* @par idx                      = 每个协议的回调函数的索引* @par func                     = 协议搜索的函数指针* @par ndpi_selection_bitmask   = 被选择的位掩码* @par b_save_bitmask_unknow    = 如果被设置为“true”,将检测的位掩码保存为unknown* @par b_add_detection_bitmask  = 如果被设置为“true”添加这个协议的bitmask到detection bitmask中**/void ndpi_set_bitmask_protocol_detection(char *label,struct ndpi_detection_module_struct *ndpi_struct,const NDPI_PROTOCOL_BITMASK *detection_bitmask,const u_int32_t idx,u_int16_t ndpi_protocol_id,void (*func) (struct ndpi_detection_module_struct *,struct ndpi_flow_struct *flow),const NDPI_SELECTION_BITMASK_PROTOCOL_SIZE ndpi_selection_bitmask,u_int8_t b_save_bitmask_unknow,u_int8_t b_add_detection_bitmask);/***设置协议的 bitmask2** @par ndpi_struct        =检测模块* @par detection_bitmask  = the protocol bitmask to set**/void ndpi_set_protocol_detection_bitmask2(struct ndpi_detection_module_struct *ndpi_struct,const NDPI_PROTOCOL_BITMASK * detection_bitmask);/*** 在未知匹配的情况下要调用的函数,以查看当前ndpi首选项配置是否阻止了部分匹配** @par    ndpi_struct  = 检测模块* @par    flow         = 检测模块检测的流* 即使流不是完整的,也返回检测到的协议;**/ndpi_protocol ndpi_get_partial_detection(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow);/***  在放弃对给定流的检测之前要调用的函数*  这个函数减少了对NDPI_UNKNOWN_PROTOCOL未知协议的检测** @par    ndpi_struct  = the detection module* @par    flow         = the flow given for the detection module* @par    enable_guess = 如果协议未知,猜测协议* @即使流不是完整的,也返回检测到的协议**/ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,u_int8_t enable_guess);/*** 处理一个额外的数据包,以便获得给定协议的更多信息* (就像ssl获取客户端和服务器证书一样,即使在看到客户端证书之后我们已经知道这个协议是什么。)** @par    ndpi_struct   = the detection module* @par    flow          = pointer to the connection state machine指向连接状态机的指针。* @par    packet        = unsigned char pointer to the Layer 3 (IP header)* @par    packetlen     = 数据包长度* @par    current_tick  = 包的时间戳* @par    src           = pointer to the source subscriber state machine*                          指向源订阅状态机* @par    dst           = pointer to the destination subscriber state machine*                          指向目的订阅状态机* @return void**/void ndpi_process_extra_packet(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,const unsigned char *packet,const unsigned short packetlen,const u_int64_t current_tick,struct ndpi_id_struct *src,struct ndpi_id_struct *dst);/*** 处理一个数据包,并且返回检测到的协议ID* This is the MAIN PACKET PROCESSING FUNCTION.*            主要的包处理函数,很重要,之后我们会分析到* @par    ndpi_struct   = the detection module* @par    flow          = pointer to the connection state machine指向连接状态机的指针* @par    packet        = unsigned char pointer to the Layer 3 (IP header)* @par    packetlen     = the length of the packet* @par    current_tick  = the current timestamp for the packet* @par    src           = pointer to the source subscriber state machine* @par    dst           = pointer to the destination subscriber state machine* @return the detected ID of the protocol**/ndpi_protocol ndpi_detection_process_packet(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,const unsigned char *packet,const unsigned short packetlen,const u_int64_t current_tick,struct ndpi_id_struct *src,struct ndpi_id_struct *dst);/*** 获取检测模块检测到传递流的主协议*** @par    ndpi_struct  = the detection module* @par    flow         = the flow given for the detection module* @return the ID of the master protocol detected主协议**/u_int16_t ndpi_get_flow_masterprotocol(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow);/***ndpi_detection_process_packet函数或应用程序在内部调用的API calls* 希望避免调用ndpi_detection_process_packet函数,因为它们已经解析了数据包,因此希望避免这种情况,这说明app在外部并不能调用ndpi_detection_process_packet函数来处理数据包*** @par    ndpi_struct              = the detection module* @par    flow                     = the flow given for the detection module* @par    ndpi_selection_bitmask   = the protocol selected bitmask**/void ndpi_check_flow_func(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,NDPI_SELECTION_BITMASK_PROTOCOL_SIZE *ndpi_selection_packet);/*** 查询指向第4层数据包的指针** @par    l3 = pointer to the layer 3 data* @par    l3_len = length of the layer 3 data* @par    l4_return = address to the pointer of the layer 4 data if return value == 0, else undefined 如果返回值=0,则指向第4层数据指针的地址,否则未定义* @par    l4_len_return = length of the layer 4 data if return value == 0, else undefined* @par    l4_protocol_return = protocol of the layer 4 data if return value == 0, undefined otherwise* @par    flags = limit operation on ipv4 or ipv6 packets. Possible values: NDPI_DETECTION_ONLY_IPV4 - NDPI_DETECTION_ONLY_IPV6 - 0 (any)限制IPV4或者IPV6* @return 0 if layer 4 data could be found correctly; 正确找到了第四层数据,返回0,否则不返回0else != 0**/u_int8_t ndpi_detection_get_l4(const u_int8_t *l3, u_int16_t l3_len, const u_int8_t **l4_return, u_int16_t *l4_len_return,u_int8_t *l4_protocol_return, u_int32_t flags);/*** 根据匹配的端口搜索并返回协议** @par    ndpi_struct  = the detection module* @par    shost        = source address in host byte order 主机字节顺序的源地址* @par    sport        = source port number* @par    dhost        = destination address in host byte order* @par    dport        = destination port number* @return the struct ndpi_protocol that match the port base protocol* 注意此处返回的是一个结构ndpi_protocol*/ndpi_protocol ndpi_find_port_based_protocol(struct ndpi_detection_module_struct *ndpi_struct/* , u_int8_t proto */,u_int32_t shost,u_int16_t sport,u_int32_t dhost,u_int16_t dport);/*** Search and return the protocol guessed that is undetected*  搜索并返回未检测出但是被猜测的协议* @par    ndpi_struct  = the detection module* @par    flow         = the flow we're trying to guess, NULL if not available需要猜测的流* @par    proto        = the l4 protocol number* @par    shost        = source address in host byte order* @par    sport        = source port number* @par    dhost        = destination address in host byte order* @par    dport        = destination port number* @return the struct ndpi_protocol that match the port base protocol**/ndpi_protocol ndpi_guess_undetected_protocol(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,u_int8_t proto,u_int32_t shost,u_int16_t sport,u_int32_t dhost,u_int16_t dport);/**下面三个函数对应了三个方法,检测子协议**//**检查传递的字符串是否与协议匹配, 用于子协议的检测** @par    ndpi_struct         = the detection module* @par    string_to_match     = the string to match* @par    string_to_match_len = the length of the string* @par    ret_match           = completed returned match information* @par    is_host_match       = value of the second field of struct ndpi_automa* @return the ID of the matched subprotocol 返回匹配的子协议ID**/int ndpi_match_string_subprotocol(struct ndpi_detection_module_struct *ndpi_struct,char *string_to_match,u_int string_to_match_len,ndpi_protocol_match_result *ret_match,u_int8_t is_host_match);/*** 检查传递的域名(主机)是否与协议匹配** @par    ndpi_struct         = the detection module* @par    flow                = the flow where match the host* @par    string_to_match     = the string to match 匹配的字符串* @par    string_to_match_len = the length of the string* @par    ret_match           = completed returned match information 匹配结果信息* @par    master_protocol_id  = value of the ID associated to the master protocol detected    主协议ID* @return the ID of the matched subprotocol  返回子协议ID**/int ndpi_match_host_subprotocol(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,char *string_to_match,u_int string_to_match_len,ndpi_protocol_match_result *ret_match,u_int16_t master_protocol_id);/*** 检查传递的字符串内容是否与协议匹配** @par    ndpi_struct         = the detection module* @par    flow                = the flow where match the host* @par    string_to_match     = the string to match* @par    string_to_match_len = the length of the string* @par    ret_match           = completed returned match information* @par    master_protocol_id  = value of the ID associated to the master protocol detected* @return the ID of the matched subprotocol**/int ndpi_match_content_subprotocol(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,char *string_to_match,u_int string_to_match_len,ndpi_protocol_match_result *ret_match,u_int16_t master_protocol_id);/*** 从搜索中排除协议** @par    ndpi_struct         = the detection module* @par    flow                = the flow where match the host* @par    master_protocol_id  = value of the ID associated to the master protocol detected**/void ndpi_exclude_protocol(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,u_int16_t master_protocol_id,const char *_file, const char *_func,int _line);/***检查字符串-bigram-to-u是否匹配-automa的bigram- 双字节匹配** @par     ndpi_mod         = the detection module* @par     automa           = the struct ndpi_automa for the bigram* @par     bigram_to_match  = the bigram string to match* @return  0**/int ndpi_match_bigram(struct ndpi_detection_module_struct *ndpi_mod,ndpi_automa *automa,char *bigram_to_match);/*** Write the protocol name in the buffer -buf- as master_protocol.protocol*在数组-buf- as master_protocol.protocol中写入协议名* @par     ndpi_mod      = the detection module* @par     proto         = the struct ndpi_protocol contain the protocols name* @par     buf           = the buffer to write the name of the protocols* @par     buf_len       = the length of the buffer* @return  the buffer contains the master_protocol and protocol name**/char* ndpi_protocol2name(struct ndpi_detection_module_struct *ndpi_mod,ndpi_protocol proto, char *buf, u_int buf_len);/*** Same as ndpi_protocol2name() with the difference that the numeric protocol* name is returned  * 返回数字协议名* @par     ndpi_mod      = the detection module* @par     proto         = the struct ndpi_protocol contain the protocols name* @par     buf           = the buffer to write the name of the protocols* @par     buf_len       = the length of the buffer* @return  the buffer contains the master_protocol and protocol name**/char* ndpi_protocol2id(struct ndpi_detection_module_struct *ndpi_mod,ndpi_protocol proto, char *buf, u_int buf_len);/*** 找出给定的类别是否是自定义/用户定义的** @par     category      = the category associated to the protocol* @return  1 if this is a custom user category, 0 otherwise* 如果是自定义则返回1*/int ndpi_is_custom_category(ndpi_protocol_category_t category);/*** 用自定义类型覆盖由NDPI定义的协议类型** @par     ndpi_mod      = the detection module* @par     protoId       = the protocol identifier to overwrite 用于覆盖的协议分析器* @par     breed         = the breed to be associated to the protocol**/void ndpi_set_proto_breed(struct ndpi_detection_module_struct *ndpi_mod,u_int16_t protoId, ndpi_protocol_breed_t breed);/*** 用自定义类别覆盖由NDPI定义的协议类别** @par     ndpi_mod      = the detection module* @par     protoId       = the protocol identifier to overwrite* @par     category      = the category associated to the protocol**/void ndpi_set_proto_category(struct ndpi_detection_module_struct *ndpi_mod,u_int16_t protoId, ndpi_protocol_category_t protoCategory);/*** 检查指定主协议的子协议是否只是信息性的(而不是真实的)** @par     mod           = the detection module* @par     protoId       = the (master) protocol identifier to query* @return  1 = the subprotocol is informative, 0 otherwise.**/u_int8_t ndpi_is_subprotocol_informative(struct ndpi_detection_module_struct *ndpi_mod,u_int16_t protoId);/*** 将协议类别作为字符串获取** @par     mod           = the detection module* @par     category      = the category associated to the protocol* @return  the string name of the category**/const char* ndpi_category_get_name(struct ndpi_detection_module_struct *ndpi_mod,ndpi_protocol_category_t category);/*** 设置协议类别字符串** @par     mod           = the detection module* @par     category      = the category associated to the protocol* @paw     name          = the string name of the category**/void ndpi_category_set_name(struct ndpi_detection_module_struct *ndpi_mod,ndpi_protocol_category_t category, char *name);/*** 获得协议种类** @par     ndpi_mod      = the detection module* @par     proto         = the struct ndpi_protocol contain the protocols name* @return  the protocol category*/ndpi_protocol_category_t ndpi_get_proto_category(struct ndpi_detection_module_struct *ndpi_mod,ndpi_protocol proto);/*** 获取与ID关联的协议名** @par     mod           = the detection module* @par     proto_id      = the ID of the protocol* @return  the buffer contains the master_protocol and protocol name**/char* ndpi_get_proto_name(struct ndpi_detection_module_struct *mod, u_int16_t proto_id);/*** 返回与协议相关联的协议类型ID** @par     ndpi_struct   = the detection module* @par     proto         = the ID of the protocol* @return  the breed ID associated to the protocol**/ndpi_protocol_breed_t ndpi_get_proto_breed(struct ndpi_detection_module_struct *ndpi_struct,u_int16_t proto);/*** Return the string name of the protocol breed*返回协议类型的字符串名* @par     ndpi_struct   = the detection module* @par     breed_id      = the breed ID associated to the protocol* @return  the string name of the breed ID**/char* ndpi_get_proto_breed_name(struct ndpi_detection_module_struct *ndpi_struct,ndpi_protocol_breed_t breed_id);/*** Return the ID of the protocol返回协议ID,整型** @par     ndpi_mod   = the detection module* @par     proto      = the protocol name* @return  the ID of the protocol**/int ndpi_get_protocol_id(struct ndpi_detection_module_struct *ndpi_mod, char *proto);/*** Return the ID of the category返回类型ID** @par     ndpi_mod   = the detection module* @par     proto      = the category name* @return  the ID of the category**/int ndpi_get_category_id(struct ndpi_detection_module_struct *ndpi_mod, char *cat);/*** 生成子协议列表Write the list of the supported protocols** @par  ndpi_mod = the detection module*/void ndpi_dump_protocols(struct ndpi_detection_module_struct *mod);/*** 读文件加载协议 Read a file and load the protocols** Format: <tcp|udp>:<port>,<tcp|udp>:<port>,.....@<proto>** Example:* tcp:80,tcp:3128@HTTP* udp:139@NETBIOS** @par     ndpi_mod = the detection module* @par     path     = the path of the file* @return  0 if the file is loaded correctly;*          -1 else**/int ndpi_load_protocols_file(struct ndpi_detection_module_struct *ndpi_mod,const char* path);/*** 获得子协议的总数Get the total number of the supported protocols** @par     ndpi_mod = the detection module* @return  the number of protocols**/u_int ndpi_get_num_supported_protocols(struct ndpi_detection_module_struct *ndpi_mod);/*** Get the nDPI version release 获取 nDPI 版本** @return the NDPI_GIT_RELEASE**/char* ndpi_revision(void);/*** 为协议搜索设置自动匹配 Set the automa for the protocol search ** @par ndpi_struct = the detection module* @par automa      = the automa to match**/void ndpi_set_automa(struct ndpi_detection_module_struct *ndpi_struct,void* automa);/* NDPI_PROTOCOL_HTTP *//*** 检索HTTP流的信息** @par     ndpi_mod = the detection module* @par     flow     = the detected flow* @return  the HTTP method information about the flow**/ndpi_http_method ndpi_get_http_method(struct ndpi_detection_module_struct *ndpi_mod,struct ndpi_flow_struct *flow);/*** Get the HTTP url 获取HTTP URL** @par     ndpi_mod = the detection module* @par     flow     = the detected flow* @return  the HTTP method information about the flow**/char* ndpi_get_http_url(struct ndpi_detection_module_struct *ndpi_mod,struct ndpi_flow_struct *flow);/*** Get the HTTP content-type  内容类型** @par     ndpi_mod = the detection module* @par     flow     = the detected flow* @return  the HTTP method information about the flow**/char* ndpi_get_http_content_type(struct ndpi_detection_module_struct *ndpi_mod,struct ndpi_flow_struct *flow);/* NDPI_PROTOCOL_TOR *//*** 检查流是否可以作为Tor协议检测到Check if the flow could be detected as TOR protocol** @par     ndpi_struct = the detection module* @par     flow = the detected flow* @par     certificate = the ssl certificate* @return  1 if the flow is TOR;*          0 else**/int ndpi_is_ssl_tor(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow, char *certificate);/* Wrappers functions *//*** Init Aho-Corasick automata  初始化AC自动机** @return  The requested automata, or NULL if an error occurred**/void* ndpi_init_automa(void);/*** Free Aho-Corasick automata allocated with ndpi_init_automa();** @par     The automata initialized with ndpi_init_automa();**/void ndpi_free_automa(void *_automa);/*** Add a string to match to an automata** @par     The automata initialized with ndpi_init_automa();* @par     The (sub)string to search* @par     The number associated with this string* @return  0 in case of no error, or -1 if an error occurred.**/int ndpi_add_string_value_to_automa(void *_automa, char *str, unsigned long num);/*** Add a string to match to an automata. Same as ndpi_add_string_value_to_automa() with num set to 1** @par     The automata initialized with ndpi_init_automa();* @par     The (sub)string to search* @return  0 in case of no error, or -1 if an error occurred.**/int ndpi_add_string_to_automa(void *_automa, char *str);/*** Finalize the automa (necessary before start searching)开始搜索必须设置好自动机** @par     The automata initialized with ndpi_init_automa();**/void ndpi_finalize_automa(void *_automa);/** * Add a string to match to an automata  添加与自动机匹配的字符串** @par     The automata initialized with ndpi_init_automa();* @par     The (sub)string to search* @return  0 in case of match, or -1 if no match, or -2 if an error occurred.**/int ndpi_match_string(void *_automa, char *string_to_match);void ndpi_load_ip_category(struct ndpi_detection_module_struct *ndpi_struct,char *ip_address_and_mask, ndpi_protocol_category_t category);int ndpi_load_hostname_category(struct ndpi_detection_module_struct *ndpi_struct,char *name, ndpi_protocol_category_t category);int ndpi_enable_loaded_categories(struct ndpi_detection_module_struct *ndpi_struct);int ndpi_fill_ip_protocol_category(struct ndpi_detection_module_struct *ndpi_struct,u_int32_t saddr,u_int32_t daddr,ndpi_protocol *ret);int ndpi_match_custom_category(struct ndpi_detection_module_struct *ndpi_struct,char *name, unsigned long *id);void ndpi_fill_protocol_category(struct ndpi_detection_module_struct *ndpi_struct,struct ndpi_flow_struct *flow,ndpi_protocol *ret);int ndpi_get_custom_category_match(struct ndpi_detection_module_struct *ndpi_struct,char *name_or_ip, unsigned long *id);int ndpi_set_detection_preferences(struct ndpi_detection_module_struct *ndpi_mod,ndpi_detection_preference pref,int value);ndpi_proto_defaults_t* ndpi_get_proto_defaults(struct ndpi_detection_module_struct *ndpi_mod);u_int ndpi_get_ndpi_num_supported_protocols(struct ndpi_detection_module_struct *ndpi_mod);u_int ndpi_get_ndpi_num_custom_protocols(struct ndpi_detection_module_struct *ndpi_mod);u_int ndpi_get_ndpi_detection_module_size();void ndpi_set_log_level(struct ndpi_detection_module_struct *ndpi_mod, u_int l);/* LRU cache */struct ndpi_lru_cache* ndpi_lru_cache_init(u_int32_t num_entries);void ndpi_lru_free_cache(struct ndpi_lru_cache *c);u_int8_t ndpi_lru_find_cache(struct ndpi_lru_cache *c, u_int32_t key, u_int8_t clean_key_when_found);void ndpi_lru_add_to_cache(struct ndpi_lru_cache *c, u_int32_t key);/*** Add a string to match to an automata  //ID字符串** @par     The automata initialized with ndpi_init_automa();* @par     The (sub)string to search* @par     The id associated with the matched string or 0 id not found.* @return  0 in case of match, or -1 if no match, or -2 if an error occurred.**/int ndpi_match_string_id(void *_automa, char *string_to_match, unsigned long *id);/* Utility functions to set ndpi malloc/free/print wrappers*设置ndpi malloc/free/print wrappers的实用程序函数 */void set_ndpi_malloc(void* (*__ndpi_malloc)(size_t size));void set_ndpi_free(void  (*__ndpi_free)(void *ptr));void set_ndpi_flow_malloc(void* (*__ndpi_flow_malloc)(size_t size));void set_ndpi_flow_free(void  (*__ndpi_flow_free)(void *ptr));void set_ndpi_debug_function(struct ndpi_detection_module_struct *ndpi_str,ndpi_debug_function_ptr ndpi_debug_printf);void * ndpi_malloc(size_t size);void * ndpi_calloc(unsigned long count, size_t size);void ndpi_free(void *ptr);u_int8_t ndpi_get_api_version();/* https://github.com/corelight/community-id-spec */int ndpi_flowv4_flow_hash(u_int8_t l4_proto, u_int32_t src_ip, u_int32_t dst_ip, u_int16_t src_port, u_int16_t dst_port,u_int8_t icmp_type, u_int8_t icmp_code, u_char *hash_buf, u_int8_t hash_buf_len);int ndpi_flowv6_flow_hash(u_int8_t l4_proto, struct ndpi_in6_addr *src_ip, struct ndpi_in6_addr *dst_ip,u_int16_t src_port, u_int16_t dst_port, u_int8_t icmp_type, u_int8_t icmp_code,u_char *hash_buf, u_int8_t hash_buf_len);#ifdef __cplusplus
}
#endif#endif    /* __NDPI_API_H__ */

linux nDPI 协议检测 源码分析相关推荐

  1. linux显示启动logo源码分析以及修改显示logo

    1.linux显示启动logo整个流程分析 (1)logo图片在内核源码中是以ppm格式的文件保存,在编译内核时会把ppm格式的文件自动转换成.c文件,在c文件中会构造一个struct linux_l ...

  2. Linux brk(),mmap()系统调用源码分析3:brk()的内存申请流程

    Linux brk(),mmap()系统调用源码分析 brk()的内存申请流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...

  3. Linux系统编程 / triggerhappy 源码分析(3.select 的应用)

    哈喽,我是老吴,继续记录我的学习心得. 一.进步的滞后性 我们期望进步是线性: 每一个人付出一些努力后,都希望它有立竿见影的效果. 现实是: 做出努力后,结果的显现往往滞后. 只有在几个月或几年后,我 ...

  4. 【OpenCV流程+代码分析】Opencv HOG行人检测 源码分析

    [原文:http://blog.csdn.net/ttransposition/article/details/11874285] OpenCV demo演示见本人的另一篇灌水博客 http://bl ...

  5. KCP 协议与源码分析(一)

    简介 这部分摘自https://github.com/skywind3000/kcp,这是源码的官方网站,有一些使用介绍,其他各种衍生版本,在实际中的使用情况以及一些测试比较分析. KCP是一个快速可 ...

  6. 连续arq协议的利用率_KCP 协议与源码分析

    文章来源于 CSDN-专业IT技术社区-登录 简介 这部分摘自https://github.com/skywind3000/kcp,这是源码的官方网站,有一些使用介绍,其他各种衍生版本,在实际中的使用 ...

  7. linux wifi 源代码,Linux无线认证----wifidog源码分析

    wifidog wifidog开源模块,通过iptable对报文进行重定向到端口2060接口,对报文进行拦截,利用iptable实现用户上网行为管理功能,目前市面上的无线多采用此模块进行portale ...

  8. 车道检测源码分析系列

    http://www.voidcn.com/blog/qq535033459/article/p-1939538.html 车道线检测是辅助驾驶和自动驾驶的重要研究内容,相关的算法研究已经延续了20多 ...

  9. linux之虚拟文件系统源码分析(详解)

    文章目录 前言 基础知识 VFS的数据结构 正篇 前言 ​ 虚拟文件系统是一个很庞大的架构,如果要分析的面面俱到,会显得特别复杂而笨拙,让人看着看着,就不知所云了(当然主要还是笔者太菜),所以这篇博客 ...

最新文章

  1. 操作-《oracle入门到精通》第六章开始
  2. 026_图书管理案例
  3. ScrollView中嵌入Listview,当item高度不一样的时候,item展示不全问题
  4. python时间序列数据分析统计服_python数据分析之:时间序列二
  5. windows 64位 安装mvn提示 不是内部或外部命令
  6. Python3 hex() 函数
  7. 快乐学习 Ionic Framework+PhoneGap 手册1-5 {IO开关}
  8. linux 笔记服务器,Linux服务器 CentOS7.5 操作小笔记
  9. php 检查语法命令,php语法检查的方法有哪些?(代码示例)
  10. linux 比较两个文件夹不同 (diff命令, md5列表)
  11. Python 一个无限 重复生成器的实现 和一个简洁的 quicksort
  12. caffe linux下面的调试mnist遇到的问题
  13. python 3维图形库_Python 绘制 3 维以上的高维图
  14. gitlab 屏蔽注册功能
  15. form表单中id与name的区别
  16. AV终结者技术大曝光(另附AV终结者10大死法)
  17. symbian os 通讯录引擎
  18. Day13 推导式、推导式试题、集合推导式、生成器函数、生成器表达式
  19. java的大o_学习算法前你需要了解的‘大O表示法’
  20. 开源的晶体管测试仪版本注意事项

热门文章

  1. 5G LAN — 解决方案示例
  2. Simulink仿真教程2---一些基本操作
  3. Bootloader的分区和启动
  4. 【C++】 18_对象的构造 (中)
  5. sails框架发送邮件
  6. 在C#中使用官方驱动操作MongoDB
  7. Linux常用命令汇总(二)
  8. 细说JavaScript异步函数发展历程
  9. angular-fullstack test
  10. android 程序安装路径选择