深入nDPI

前言

随着日益增加的网络应用数量,在网络上的流量种类越来越多,像QoS、安全性等方面的挑战,传统的方法已经开始乏力。比如P2P应用的带宽占用、使用随机端口的恶意网络应用等,过去单单基于IP、端口的工具很难识别这类应用的流量,因此有必要对报文进行深度解析,这就是DPI技术的必要性。

作为一个开源的DPI工具,nDPI不仅对多种主流协议进行支持,还能避免因随机端口导致的误判,本文就先介绍nDPI的部分技术细节,最后我们通过实践体验nDPI。

nDPI技术细节

nDPI从OpenDPI发展而来,当初OpenDPI只有付费版才有高级特性支持,nDPI作为开源软件提供了和OpenDPI一样的功能。在设计上,nDPI和OpenDPI是差不多的:

1.都是基于C的实现

2.一个两层的架构:核心库用来处理数据包,抽取基本信息;解析器用插件实现,用于解析报文检测协议类别。

和OpenDPI相比,改变了什么

由于OpenDPI存在一些问题,nDPI作为功能超集解决了那些问题:

1.OpenDPI在针对不同协议的插件中,针对协议的数据结构是静态的,比如在一个插件中,某些用来保存特征的变量都绑定到一个具体的值,导致检测协议时受限。

2.nDPI确保报文协议一旦被识别出来就返回结果。但是OpenDPI,即时已经有识别成功,仍会做额外的识别工作导致开销增加。

3.nDPI实现了对加密流量的解析,毕竟如果DPI工具没有办法处理加密流量其实作用甚微。

4.OpenDPI需要加信号量避免同一时间有多个实例修改同一数据。nDPI实现了线程安全。

5.改变OpenDPI的某些奇葩设计:比如每一个流都执行很多初始化工作。nDPI仅需要在startup阶段初始化一次,降低额外开销。

6.优化匹配的方式:nDPI根据端口尝试直接用一些有可能识别成功的协议解析器进行解析,如果猜错了,则按注册顺序用其他解析器解析。比如一个使用22端口的流,OpenDPI并不会尝试先用ssh解析器解析,而是用全部解析器解析一遍直到有一个解析成功(第2点说过,就算有一个识别成功,但也还会继续解析直到所有解析器都过一遍,的确是非常愚蠢的做法)。

7.过于死板的解析器机制:OpenDPI要求每一个支持的协议都要实现一个解析器,然而当用户在使用非标准端口用常规应用的时候就难以识别了。比如定义一个使用TCP承载的X协议,使用端口Y,其实只要改进一下支持在运行时进行配置,TCP解析器支持就可以做到对X协议的支持。这有点像docker image,用户下载一个基本镜像,自定义后保存为新的镜像,就可以满足不同环境的需求。但是OpenDPI做不到,想支持新的应用?自己用C语言写个解析器吧!

8.对提取流的元数据的支持。

nDPI除了解决了上述问题,还增加了通过利用已经识别的url来判断应用的功能,像那些会连接到固定服务器的应用,比如QQ等,这样一来对流的识别效率会有一定程度的提高。针对某些互联网应用,比如Dropbox,由于应用会连接到具体的服务器上(*.dropbox.com),通过解析HTTP头抽取Host字段,如果这个字段的内容和预先设置的url匹配,就可以初步判断为流属于dropbox。

可移植性

nDPI适合作为一些用于检测流量并判断流量属于何种应用的高层应用程序的基础,具有较好的移植性,现在有针对Linux、Windows、Mac和BSD类系统的支持,硬件架构上支持x86,MIPS和ARM。

如何注册一个新的协议

nDPI中,每一个支持的协议都要用一个唯一的数字和一个名称注册、定义。下面通过一个例子介绍下:

用来注册的具体代码是https://github.com/ntop/nDPI/blob/dev/src/include/ndpi_protocol_ids.h

可以看到代码中用宏定义定义了所有支持的协议:

而所支持的又分为三类:protocol、content、service:

看看yahoo这个例子,在ndpi_protocol_ids.h文件中的第118行,定义了一个叫NDPI_PROTOCOL_YAHOO的宏,其值为70:

这个宏会在yahoo.c文件中用到:

高层应用

nDPI要求调用它的高层应用需要提前处理好网包的第二层,由nDPI处理第三层和以上的数据,如果用户使用nDPI作为底层,那么就有必要进行先协议切分,三层和以上的数据才能由nDPI处理。

性能

nDPI解析流的流程是:

1.高层应用把三、四层的数据交给nDPI

2.nDPI根据默认端口和承载协议尝试猜应用协议,并使用猜出来的协议解析器尝试解析,如果解析成功,返回结果;如果不成功,就下一步

3.根据承载协议使用该承载协议分类下的全部协议解析器按顺序尝试解析(比如流是基于TCP,就用和TCP有关的解析器解析,而不会考虑UDP的),如果成功,返回结果;不成功,就下一步

4.上一步不成功的原因可能是协议不被支持或者没有抓到关键的包,如果协议不被支持就会停止解析,如果是后面一种情况就继续等待高层应用提供新的数据(出现这种情况的主要原因是流开始了但没有抓到前面的关键的包,从而导致识别失败)

但是这里有个问题,怎样才知道哪些包重要,哪些不重要,不然让高层应用怎么抓?

流使用不同的承载协议还有某些软件在开始传输数据之前会进行协商或者其他的处理,这些都是可以作为参照的流量特征。参考libprotoident,它用来进行识别的包都是双向流的前面的包,一般而言,用双向流整个周期(流的开始到结束)前面的包来进行识别成功率会更高,笔者的机器学习流量分类研究也是采用了一个双向流的TCP三次握手后的第一个包来作为分类依据。

影响DPI引擎的性能的因素主要是支持的协议数量和流的元数据的抽取,因为在识别流程中,nDPI先根据端口或者url猜可能的协议种类并用对应的解析器尝试解析,如果猜不对就按照解析器的注册顺序解析直到有一个解析成功;另外对于某些流有很多特征元数据的话,抽取特征也是个很耗时的工作。

nDPI的设计中,每个解析器都会包含一个默认的协议和端口。

比如一个TCP流使用80端口的话,nDPI就会尝试猜应用层协议,并尝试使用HTTP解析器解析,如果正好猜对,就能让整个解析过程变得更快,如果猜错(比如HTTP流不用80端口),才会交给其他解析器处理。

加密流量

nDPI针对加密流量一般是两种方法:

1.通过url识别应用类别:不过默认情况下这个仅对互联网应用有效,常见的互联网应用都会连接到其官方服务器,提前注册了这些服务器的url,如果加密流量与这些url有关,就判断流属于与那些url相关的应用。

2.发现自签名的ssl证书:如果是VPN应用,通常使用了自签名的证书而且保持长时间连接,而非像HTTPS一样只发送少量数据。这部分的流识别只能靠行为识别。

扩展

前面提到过可以在运行时自定义协议解析器。使用的时候,需要按照一定的格式写配置文件,这些配置文件主要起到保存特征的作用,比如定义一个基于TCP,使用81端口的流为HTTP,那么nDPI就会从流中提取出某些有用的元数据,通过Aho-Corasick算法进行快速匹配,如果匹配成功,就可以认为这个流使用了HTTP。

简单安装试用

上面讲了技术细节那么久,接下来就简单地介绍下如何单独安装最新的nDPI。

由于官方的QuickStartGuide比较久(13年的),因此不应该参考那个文档。

先从github下载nDPI的源码:

git clone https://github.com/ntop/nDPI.git

安装配置、编译需要的软件。我的系统中已经安装了GCC和build-essential就只需安装下面的软件:

apt-get install autoconf libtool gawk libpcap-dev

进入目录执行autogen.sh:

root@desktop:~/nDPI# ./autogen.sh

上一步成功后,然后configure和make,用make install安装:

root@desktop:~/nDPI# ./configure && make && make install

编译完成后,键入ndpiReader就可以看到帮助信息:

root@desktop:~# ndpiReader
Welcome to nDPI 1.8.0-dev-466-72e3930
ndpiReader -i <file|device> [-f <filter>][-s <duration>]
          [-p <protos>][-l <loops> [-q][-d][-h][-t][-v <level>]
          [-n <threads>] [-w <file>] [-j <file>]
Usage:
  -i <file.pcap|device>     | Specify a pcap file/playlist to read packets from or a device for live capture (comma-separated list)
  -f <BPF filter>           | Specify a BPF filter for filtering selected traffic
  -s <duration>             | Maximum capture duration in seconds (live traffic capture only)
  -p <file>.protos          | Specify a protocol file (eg. protos.txt)
  -l <num loops>            | Number of detection loops (test only)
  -n <num threads>          | Number of threads. Default: number of interfaces in -i. Ignored with pcap files.
  -j <file.json>            | Specify a file to write the content of packets in .json format
  -g <id:id...>             | Thread affinity mask (one core id per thread)
  -d                        | Disable protocol guess and use only DPI
  -q                        | Quiet mode
  -t                        | Dissect GTP/TZSP tunnels
  -r                        | Print nDPI version and git revision
  -w <path>                 | Write test output on the specified file. This is useful for
                        | testing purposes in     order to compare results across runs
  -h                        | This help
  -v <1|2>                  | Verbose 'unknown protocol' packet print. 1=verbose, 2=very verbose

尝试使用ndpiReader抓eth0的包持续30秒,看看我们能抓到什么结果:

root@desktop:~# ndpiReader -i eth0 -s 30
-----------------------------------------------------------
* NOTE: This is demo app to show *some* nDPI features.
* In this demo we have implemented only some basic features
* just to show you what you can do with the library. Feel 
* free to extend it and send us the patches for inclusion
------------------------------------------------------------
Using nDPI (1.8.0-dev-466-72e3930) [1 thread(s)]
Capturing live traffic from device eth0...
Capturing traffic up to 30 seconds
Running thread 0...
nDPI Memory statistics:
    nDPI Memory (once):      92.13 KB     
    Flow Memory (per flow):  1.18 KB      
    Actual Memory:           1.10 MB      
    Peak Memory:             1.10 MB      
Traffic statistics:
    Ethernet bytes:        1028          (includes ethernet CRC/IFC/trailer)
    Discarded bytes:       0            
    IP packets:            8             of 8 packets total
    IP bytes:              836           (avg pkt size 104 bytes)
    Unique flows:          1            
    TCP Packets:           8            
    UDP Packets:           0            
    VLAN Packets:          0            
    MPLS Packets:          0            
    PPPoE Packets:         0            
    Fragmented Packets:    0            
    Max Packet size:       104          
    Packet Len < 64:       2            
    Packet Len 64-128:     6            
    Packet Len 128-256:    0            
    Packet Len 256-1024:   0            
    Packet Len 1024-1500:  0            
    Packet Len > 1500:     0            
    nDPI throughput:       0.27 pps / 273 b/sec
    Traffic throughput:    0.27 pps / 273 b/sec
    Traffic duration:      30.053 sec
    Guessed flow protos:   1            
Detected protocols:
    SSH                  packets: 8             bytes: 836           flows: 1            
Protocol statistics:
    Acceptable                     836 bytes

从这个ndpiReader的说明可以看到,这个工具只是用来展示nDPI的特性,所以读者如果想进一步使用nDPI,还需要更进一步的开发。

由于本人使用ssh连接这个系统,所以只能检测到ssh协议。

关于nDPI的简单入门,请读者参考官网(http://www.ntop.org/support/documentation/documentation/)的nDPI文档,不过文档较旧。

结语

笔者认为,nDPI是个相当不错的开源DPI工具,但是某些地方可能还需要改进,比如针对加密流量还有文档。

针对加密流量可能需要引入SSL卸载技术,以进行更全面的检测和控制。

关于文档,实际上能用来参考如何对nDPI进行二次开发或者基于nDPI开发应用的文档几乎很少或者没有,不知官方是出于何种原因不提供详细的开发者文档,一个开源项目连文档都不全的话不能说是一个好的开源项目,如果要进行二次开发,恐怕要阅读源码梳理过才能着手开发,门槛实在不低。

关于性能,有兴趣的读者可以去找找测试性能的方法,对nDPI的准确率和性能做一个调查。

本文介绍了nDPI的主要技术细节,最后展示了编译、试用的操作。希望本文能为那些有意使用nDPI进行DPI引擎开发的读者有所帮助。因为官方文档不多也有点旧,希望这篇文章能让更多的读者了解到这个工具。本人水平有限,如有缺点和漏洞,还请各位读者指出。

原文发布于微信公众号 - SDNLAB(SDNLAB)

原文发表时间:2016-07-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java社区

一招 | 利用脚本不限速下载百度网盘文件

2685

内存数据网格主要特性简介

将主存储器用作存储区域而不是使用磁盘是并不是一种全新的尝试。你可以在日常生活中发现许多使用主内存DBMS(数据库管理系统)(MMDB)执行比磁盘快得多的情况。一...

4274
来自专栏微服务生态

Log4j2的性能为什么这么好?

说明: 这个是logback日志的压测数据,在开发机(双核四线程),高配开发机(四核八线程)和服务器(32核)压测的效率都差不多,而且线程开多的时候,性能反而...

2243
来自专栏北京马哥教育

【Zabbix】中小型企业Zabbix监控实战之告警大全

转载声明:本文转载自「数睿技术」 前言 本篇文章介绍目前常用的告警方式,以及对功能实现做讲解。本篇文章中出现的代码片段可以回复"监控脚本"获取完整代码。 ...

49010
来自专栏阮一峰的网络日志

Unix目录结构的来历

Unix(包含Linux)的初学者,常常会很困惑,不明白目录结构的含义何在。 ? 举例来说,根目录下面有一个子目录/bin,用于存放二进制程序。但是,/usr子...

3143
来自专栏H2Cloud

C++中消息自动派发之四 使用IDL构建Chat Server

  前一篇blog 讲了如何实现IDL 解析器,本篇通过IDL解析器构建一个聊天服务器程序。本程序用来测试IDL解析器的功能,网络层使用前边blog中介绍的ff...

3164
来自专栏程序员宝库

Python库大全(涵盖了Python应用的方方面面),建议收藏留用!

学Python,想必大家都是从爬虫开始的吧。毕竟网上类似的资源很丰富,开源项目也非常多。

1654
来自专栏大数据和云计算技术

零基础入门Redis,这篇够了!

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。

1626
来自专栏大内老A

《我的WCF之旅》博文系列汇总

WCF是构建和运行互联系统的一系列技术的总称,它是建立在Web Service架构上的一个全新的通信平台。你可以把它看成是.NET平台上的新一代的Web Ser...

1888
来自专栏函数式编程语言及工具

ScalaPB(0): 找寻合适的内部系统微服务集成工具

1612

扫码关注云+社区

领取腾讯云代金券