当扫描任务的数量膨胀到一定程度时,单机扫描模式下微小的性能下降将会在庞大的基数作用下变得愈发明显,进而导致时间成本飙升至无法接受的程度。本文将从操作系统的内核层面深入研究性能损耗问题,并探讨可行的优化策略。
基础
端口扫描是网络安全领域中的一种重要技术,主要用于发现目标系统中开放的端口。这项技术的主要目的是识别网络中可访问的服务,从而为安全评估和弱点挖掘提供基础。端口扫描的结果可以帮助安全专家更好地了解目标系统的潜在弱点,以便采取相应的安全措施。
端口扫描方法主要包括SYN扫描和ACK扫描。SYN扫描通过发送SYN数据包检测开放端口,目标系统回应SYN-ACK数据包表示端口开放,无回应表示端口关闭。ACK扫描通过发送ACK数据包检测开放端口,目标系统回应RST数据包表示端口开放,无回应表示端口关闭。SYN扫描速度快,但可能被检测到,ACK扫描隐蔽性高,但速度较慢。安全专家需根据实际情况选择合适的端口扫描方法。
基础的扫描过程相对简单,但是当我们需要进行大范围扫描时,情况就会变得复杂许多。例如,假设我们正在组织一场规模庞大的红蓝对抗,涉及200个组织,每个组织拥有100个域名,每个域名对应100个IP地址,而每个IP地址又有65535个端口。总计是 200 x 100 x 100 x 65536 = 131072000000,千亿级别的放大倍数。在这种情况下,即使我们只进行TCP SYN扫描,也会产生大约6.4TB的数据流量(假设每个TCP SYN包的大小约为54字节)。而如果我们发送HTTP报文,那么数据量更是天文数字。面对如此庞大的数据量,扫描性能的优化就变得至关重要。
性能瓶颈分析
要评估扫描性能的限制因素,我们不妨重新考虑数据报文的接收和发送过程。当网络上的数据包到达网卡时,会经过以下步骤,最终到达socket的接收缓冲区(Recieve buffer):
- 网络驱动的加载和初始化
- 数据包抵达网络接口卡(NIC)
- 通过直接内存访问(DMA)将数据包拷贝到内存中的环形缓冲区(Ring Buffer)
- 产生硬中断,通知CPU数据包已存入内存
- 驱动程序调用新API轮询(NAPI poll)处理数据包
- 每个CPU核心运行的ksoftirqd进程通过NAPI poll函数处理Ring Buffer中的数据包
- 释放环形缓冲区中已处理的数据区域
- 将Ring Buffer中的数据传输到网络层,并封装成套接字缓冲区(skb)
- 如果启用了接收包处理软件(RPS)或NIC支持多队列,则数据包会被分配到多个CPU处理
网络层处理积压队列中的数据包 - 协议层处理数据
- 数据被追加到关联socket的接收缓存
在小规模扫描过程中,这些环节通常不会对系统性能产生明显影响。但是,当我们回顾千亿级别的放大倍数时,问题变得不太一样:数据报文一旦达到这个数量级,过多的中断可能会引发中断风暴;大量的上下文切换会导致数据竞争,最终可能导致处理时间呈指数级增长。
优化策略
该问题的产生原因显而易见。Linux作为一款广泛应用的操作系统,其内核设计的核心理念是灵活性和可配置性,而非单纯的性能优化。基于这个原因,为了实现最高的端口扫描性能,我们需要实施的优化策略也是trivial的,即将通用操作系统转换为专用系统,通过定制处理逻辑来达成限定条件下的性能优化。
针对上述问题,我们可以逐项进行优化。大量的数据包导致内存碎片化,那么通过采用大页内存(HugePages)技术来减少页表项,从而提高内存访问速度。大页内存的应用可以减少页表项的数量,从而提升内存访问效率。这种方法可以有效地减少内存碎片,提高内存利用率,进而提升端口扫描性能。
对于海量的中断处理,可采用用户态轮询(User-space polling)技术以减少内核态与用户态之间的切换,从而降低中断处理开销。用户态轮询是一种允许用户态程序直接处理中断的技术,从而减少了内核态与用户态之间的切换次数,降低了中断处理开销,并提升了端口扫描性能。
此外,对于驱动到内核、内核到用户态空间的多级数据通路,可以通过内存映射(Memory mapping)技术来减少不必要的内存拷贝,提高数据处理效率。内存映射是一种将用户态程序的虚拟内存地址直接映射到物理内存地址的技术,从而减少了不必要的内存拷贝,提高了数据处理效率,进而提升了端口扫描性能。
具体实现
在确定优化方向之后,容易找到对应的网络优化方案,如DPDK、PF_RING、libpcap和eBPF等。以下是这些方案的简单说明和对应的实现方法。
DPDK
DPDK(Data Plane Development Kit)是一个用于快速数据包处理的开发工具包。它提供了一系列库和驱动程序,可以使应用程序直接在数据平面上运行,从而避免了操作系统的协议栈,提高了网络性能。使用DPDK进行优化,需要将应用程序与DPDK库集成,并使用DPDK提供的API来编写高性能的网络应用程序。
PF_RING
PF_RING是一个高性能的网络框架,它提供了一种方法来加速数据包捕获和传输。PF_RING通过绕过操作系统的协议栈,直接在网络接口卡(NIC)上处理数据包,从而实现了高性能的数据包处理。要在PF_RING上实现优化,我们需要将我们的应用程序与PF_RING库集成,并使用PF_RING提供的API来编写高性能的网络应用程序。
libpcap
libpcap是一个用于捕获网络数据包的库。它可以用于网络监控、安全和网络分析等应用。要在libpcap上实现优化,我们需要将我们的应用程序与libpcap库集成,并使用libpcap提供的API来捕获和分析网络数据包。
eBPF
eBPF(Extended Berkeley Packet Filter)是一个允许用户在操作系统内核中运行程序的技术。它可以用于网络优化、安全监控和跟踪等应用。要在eBPF上实现优化,我们需要使用eBPF提供的API来编写内核模块,并在内核中运行我们的程序。
总结
在端口扫描过程中,随着数据量的增加,可能会引发中断风暴和巨大的放大系数,从而导致质变。在这种情况下,传统的工具如nmap可能不再适用。为了应对这种情况,可以考虑使用DPDK、PF_RING等技术对内核网络处理进行定向优化。