一、原理
给一个端口发送UDP报文,如果端口是开放的,则没有响应,如果端口是关闭的,对方会回复一个ICMP端口不可达报文(对应ICMP首部前两个字段:类型3 代码3)。这种方式扫描速度慢;不可靠(udp和icmp都是不可靠协议)。
二、实现方式及遇到的问题
每个扫描线程(udpIcmpScanPort)会建立一个线程池,对每个要扫描的端口都创建一个线程udpIcmpScanEach(线程配置成detach属性)。udpIcmpScanEach负责发送UDP包到各端口,另有一个线程udpIcmpScanRecv负责处理所有收到的数据包。发送的数据包要自己组装,协议可以使用原始套接字,协议选择UDP,接收时另建立一个socket,也是使用原始套接字,协议选择ICMP.
1.发送频率太快会大量丢包
按照SYN或者FIN的频率发送UDP包的话,会出现大量ICMP或者UDP丢包现象。频率的大小和扫描端口的数量有关系,需要扫描的数量越大,用来记录目前存在线程数的全局变量udpCnt越容易超过上限,程序就卡死在这里了
我想到两种解决方法,一个是把频率调低,频率降为原来的1/2时扫描1000个包还行。但是总觉得这种方法有侥幸的感觉。我尝试使用第二种方法,还是原来的频率,加了一个定时器(信号ALARM实现),在规定的时间内(我设置的是30秒)如果程序卡死在这里,则重新发送UDP包,速度可以做到跟FIN差不多,程序也没有卡死。
2.选择icmp有关数据结构的问题
linux提供的有关icmp的数据结构有两种,icmp与icmphdr,我用sizeof测了一下,前者是20字节,后者是8字节。因为抓包来看,接收到的ICMP报文是IP首部(20字节)+ICMP首部(8字节)+发送的IP层的UDP报文,所以其实是IP首部(20字节,目的端口方)+ICMP首部(8字节)+IP首部(20字节,端口扫描方)+UDP首部+UDP数据,所以我用的是icmphdr。
三、实现代码
#ifndef UDPICMPSCAN_H_H
#define UDPICMPSCAN_H_H
#include "mysock.h"
int udpCnt;
static pthread_mutex_t udp_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t udp_num_mutex = PTHREAD_MUTEX_INITIALIZER;
//extern pthread_mutex_t udp_printf_mutex;
//extern pthread_mutex_t udp_num_mutex;
void* udpIcmpScanPort(void *arg);
void* udpIcmpScanEach(void *arg);
void* udpIcmpScanRecv(void *arg);
void alarm_u