当前位置:首页 > 技术与方案 > 网络知识相关

透析ICMP协议(四)

(2012-09-08 20:55:24)

用RAW Socket实现的ping可能比上一节的应用ICMP.DLL的程序庞大些, 但是这才是我们需要关注的东西, 我的观点真正想做网络开发的程序员应该静下心来读读这篇文章, 相信你会从中获益颇多. 中间我也会讲解一些东西为后一章的路由追踪做一些铺垫.

如何少作变动来使的这个程序实现追踪路由的功能, 这里只是抛砖引玉. 将ICMP包中IP包的包头该为特定的值就能得到那个路由器的IP(要求到达目的地的跳数大于你设的特定值).
这个程序需要windows2k/WindowsXP/WindowsNT平台和系统管理员的权限.


这段源代码大部分来自:
http://tangentsoft.net/wskfaq/examples/rawping.html
[bugfree]只做了少量修改,给出了大量的注释, 最后结合经验给出了自己的建议.

 


* 程序名: rawping_driver.cpp
* 说明:
* 驱动程序,也是主函数


 

 

#define DEFAULT_PACKET_SIZE 32 // 默认ICMP包字节数
#define DEFAULT_TTL 30 // 默认TTL值
#define MAX_PING_DATA_SIZE 1024 // 最大数据块
#define MAX_PING_PACKET_SIZE (MAX_PING_DATA_SIZE + sizeof(IPHeader)) //最大ICMP包长度

/* 为 send_buf 和 recv_buf 分配内存
* send_buf大小为 packet_size
* recv_buf大小为 MAX_PING_PACKET_SIZE, 保证大于send_buf

int allocate_buffers(ICMPHeader* send_buf, IPHeader* recv_buf,


int seq_no = 0; //用在发送和接受的ICMP包头中
ICMPHeader* send_buf = 0;
IPHeader* recv_buf = 0;

// 判断命令行是否合法

cerr "usage: " argv[0] " host [data_size] [ttl]"

cerr "tdata_size can be up to " MAX_PING_DATA_SIZE
" bytes. Default is " DEFAULT_PACKET_SIZE "."


DEFAULT_TTL "." endl;

 

// 处理命令行参数
int packet_size = DEFAULT_PACKET_SIZE;
int ttl = DEFAULT_TTL;


acket_size = max(sizeof(ICMPHeader),
min(MAX_PING_DATA_SIZE, (unsigned int)packet_size));

// 启动 Winsock


cerr "Failed to find Winsock 2.1 or better." endl;

 

SOCKET sd; // RAW Socket句柄


// 三个任务(创建sd, 设置ttl, 初试dest的值)

goto cleanup; //释放资源并退出

// 为send_buf和recv_buf分配内存
if (allocate_buffers(send_buf, recv_buf, packet_size) 0) {


// 初试化IMCP数据包(type=8,code=0)
 

// 发送ICMP数据包


// 接受回应包
if (recv_ping(sd, source, recv_buf, MAX_PING_PACKET_SIZE)

// Pull the sequence number out of the ICMP header. If
// it's bad, we just complain, but otherwise we take
// off, because the read failed for some reason.

ICMPHeader* icmphdr = (ICMPHeader*)

// Success or fatal error (as opposed to a minor error)

delete[]send_buf; //释放分配的内存

WSACleanup(); // 清理winsock

// 为send_buf 和 recv_buf的内存分配. 太简单, 我略过
int allocate_buffers(ICMPHeader* send_buf, IPHeader* recv_buf,

end_buf = (ICMPHeader*)new char[packet_size];

cerr "Failed to allocate output buffer." endl;

recv_buf = (IPHeader*)new char[MAX_PING_PACKET_SIZE];

* 程序名: rawping.h
* 说明:
* 主要函数库头文件
 

#define WIN32_LEAN_AND_MEAN
 

// ICMP 包类型, 具体参见本文的第一节
#define ICMP_ECHO_REPLY 0
#define ICMP_DEST_UNREACH 3
#define ICMP_TTL_EXPIRE 11
#define ICMP_ECHO_REQUEST 8

// 最小的ICMP包大小
#define ICMP_MIN 8


// IP 包头
truct IPHeader {
BYTE h_len:4; // Length of the header in dwords

BYTE tos; // Type of service
USHORT total_len; // Length of the packet in dwords
USHORT ident; // unique identifier
USHORT flags; // Flags
BYTE ttl; // Time to live, 这个字段我在下一节中用来实现Tracert功能
BYTE proto; // Protocol number (TCP, UDP etc)
USHORT checksum; // IP checksum
ULONG source_ip;
ULONG dest_ip;
 

// ICMP 包头(实际的包不包括timestamp字段,
// 作者用来计算包的回应时间,其实完全没有必要这样做)
truct ICMPHeader {
BYTE type; // ICMP packet type
BYTE code; // Type sub code
USHORT checksum;
USHORT id;
USHORT seq;
ULONG timestamp; // not part of ICMP, but we need it
 


extern USHORT ip_checksum(USHORT* buffer, int size);
extern int setup_for_ping(char* host, int ttl, SOCKET sd, sockaddr_in dest);
extern int send_ping(SOCKET sd, const sockaddr_in dest, ICMPHeader* send_buf, int packet_size);
extern int recv_ping(SOCKET sd, sockaddr_in source, IPHeader* recv_buf,

extern int decode_reply(IPHeader* reply, int bytes, sockaddr_in* from);
extern void init_ping_packet(ICMPHeader* icmp_hdr, int packet_size, int seq_no);


* 程序名: rawping.cpp
* 说明:
* 主要函数库实现部分

// 计算ICMP包的校验和的简单算法, 很多地方都有说明, 这里没有必要详细将
// 只是一点要提, 做校验之前, 务必将ICMP包头的checksum字段置为0
// Sum all the words together, adding the final byte if size is odd
 

//初试化RAW Socket, 设置ttl, 初试化dest
// 返回值 0 表失败

int setup_for_ping(char* host, int ttl, SOCKET sd, sockaddr_in dest)


d = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, 0);
if (sd == INVALID_SOCKET) {
cerr "Failed to create raw socket: " WSAGetLastError()


 

if (setsockopt(sd, IPPROTO_IP, IP_TTL, (const char*) ttl,
izeof(ttl)) == SOCKET_ERROR) {
cerr "TTL setsockopt failed: " WSAGetLastError() endl;

 

// Initialize the destination host info block
 

// Turn first passed parameter into an IP address to ping

if (addr != INADDR_NONE) {
// It was a dotted quad number, so save result

dest.sin_family = AF_INET;


// Not in dotted quad form, so try and look it up


// Found an address for that host, so save it


// Not a recognized hostname either!

 

//初试化ICMP的包头, 给data部分填充数据, 最后计算整个包的校验和

void init_ping_packet(ICMPHeader* icmp_hdr, int packet_size, int seq_no)


icmp_hdr- type = ICMP_ECHO_REQUEST;


icmp_hdr- id = (USHORT)GetCurrentProcessId();

icmp_hdr- timestamp = GetTickCount();

// "You're dead meat now, packet!"
const unsigned long int deadmeat = 0xDEADBEEF;
char* datapart = (char*)icmp_hdr + sizeof(ICMPHeader);
int bytes_left = packet_size - sizeof(ICMPHeader);

memcpy(datapart, deadmeat, min(int(sizeof(deadmeat)),


datapart += sizeof(deadmeat);
 

// Calculate a checksum on the result
icmp_hdr- checksum = ip_checksum((USHORT*)icmp_hdr, packet_size);
 

// 发送生成的ICMP包
 

int send_ping(SOCKET sd, const sockaddr_in dest, ICMPHeader* send_buf,


// Send the ping packet in send_buf as-is

if (bwrote == SOCKET_ERROR) {
cerr "send failed: " WSAGetLastError() endl;


// 接受ICMP包

int recv_ping(SOCKET sd, sockaddr_in source, IPHeader* recv_buf,

acket_size + sizeof(IPHeader), 0,

if (bread == SOCKET_ERROR) {

if (WSAGetLastError() == WSAEMSGSIZE) {

cerr "error #" WSAGetLastError() endl;

// 对收到的ICMP解码
// 返回值 -2表忽略, -1 表失败, 0 成功
int decode_reply(IPHeader* reply, int bytes, sockaddr_in* from)

// 跳过IP包头, 找到ICMP的包头

// 包的长度合法, header_len + ICMP_MIN为最小ICMP包的长度
if (bytes header_len + ICMP_MIN) {

// 下面的包类型详细参见我的第一部分 "透析ICMP协议(一): 协议原理"
else if (icmphdr- type != ICMP_ECHO_REPLY) { //非正常回复
if (icmphdr- type != ICMP_TTL_EXPIRE) { //ttl减为零
if (icmphdr- type == ICMP_DEST_UNREACH) { //主机不可达
cerr "Destination unreachable" endl;

else { //非法的ICMP包类型
cerr "Unknown ICMP packet type " int(icmphdr- type)

else if (icmphdr- id != (USHORT)GetCurrentProcessId()) {
//不是本进程发的包, 可能是同机的其它ping进程发的

// 指出包传递了多远
// [bugfree]我认为作者这里有问题, 因为有些系统的ttl初值为128如winXP,
// 有些为256如我的DNS服务器211.97.168.129, 作者假设为256有点武断,

更多
关闭窗口 打印 
网站首页    -    联系我们    -   收藏本站    -    网站地图                                                               客户服务热线:0571-85023000
本网站所有网页信息已申请知识产权和著作权保护,版权归四海光纤公司所有,未经授权禁止任何人复制或镜像,违者必究。
公司主营:杭州光纤光缆视频会议系统,是专业的通信网络工程、视频会议系统建设专家

中华人民共和国备案号:浙ICP备10018243号