您好,欢迎来到爱站旅游。
搜索
您的当前位置:首页实验七:基于ICMP的简单程序实现

实验七:基于ICMP的简单程序实现

来源:爱站旅游


实验七 基于ICMP的简单程序实现

【实验目的】

⑴学习套接字编程的基本流程; ⑵理解ICMP的工作原理; 【实验环境】

电脑、VC++ 6.0编程环境 【实验重点及难点】

重点:使用原始套接字编写程序; 难点:使用原始套接字编写程序。 【实验内容】 1、基础知识

⑴ ICMP的作用:ICMP是(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。关于ICMP报文类型可参考维基百科。

⑵ IP的头部及ICMP头部、数据结构定义 IP头部:

typedef struct _iphdr {

//Suppose the BYTE_ORDER is LITTLE_ENDIAN unsigned int h_len:4; // 头部长度 unsigned int version:4; // IP的版本号 unsigned char tos; // 服务类型 unsigned short total_len; // 报长度 unsigned short id; // 标识符 unsigned short frag_offset; // 分片偏移 unsigned char ttl; // 生存时间

unsigned char protocol; // 上层协议 (TCP, UDP 等) unsigned short checksum; // IP 校验和 unsigned int sourceIP; // 源 IP unsigned int destIP; // 目标 IP } IpHeader;

//选项部分

typedef struct _ipoptionhdr {

unsigned char code; // 选项类型 unsigned char len; // 头部可选长度 unsigned char ptr; // 偏移量

unsigned long addr[9]; // 记录经过的IP用作路由 } IpOptionHeader;

ICMP头部:

typedef struct _icmphdr {

BYTE i_type; //ICMP类型

BYTE i_code; //ICMP code 字段 USHORT i_cksum; //ICMP 校验和 USHORT i_id; USHORT i_seq; ULONG timestamp; } IcmpHeader;

2、阅读程序,完成以下题目:

⑴ 画出程序的流程图,并解释每个模块的含义;

⑵ 运行程序,使用抓包工具捕获发送的数据包,并分析数据与程序对照。

⑶ 参照实验指导书实验12 ICMP协议应用中的设置ttl值的代码,将该程序修改为一个模拟tracert程序。

3、附录:

/*该程序使用原始套接字编写,默认ping本身IP地址,可以再VC6中设置参数 *格式为 ping www.sina.com.cn,也是可以使用-r选项,使用IP头部的选项字段记录 *经过的路由。 */

//#include \"stdafx.h\" #include #include #include #include #define WIN32_LEAN_AND_MEAN #pragma comment(lib,\"ws2_32.lib\") #define IP_RECORD_ROUTE 0x7

// IP头部的结构体 typedef struct _iphdr {

//Suppose the BYTE_ORDER is LITTLE_ENDIAN unsigned int h_len:4; // 头部长度 unsigned int version:4; // IP的版本号 unsigned char tos; // 服务类型 unsigned short total_len; // 报长度 unsigned short id; // 标识符 unsigned short frag_offset; // 分片偏移 unsigned char ttl; // 生存时间

unsigned char protocol; // 上层协议 (TCP, UDP 等) unsigned short checksum; // IP 校验和 unsigned int sourceIP; // 源 IP unsigned int destIP; // 目标 IP } IpHeader;

#define ICMP_ECHO 8 #define ICMP_ECHOREPLY 0

#define ICMP_MIN 8 // ICMP包头部最小长度

// ICMP 头部结构体

// This is not the standard header, but we reserve space for time typedef struct _icmphdr {

BYTE i_type; //ICMP类型

BYTE i_code; //ICMP code 字段 USHORT i_cksum; //ICMP 校验和 USHORT i_id; USHORT i_seq; ULONG timestamp; } IcmpHeader;

// IP 头部的选项部分 typedef struct _ipoptionhdr {

unsigned char code; // 选项类型 unsigned char len; // 头部可选长度 unsigned char ptr; // 偏移量

unsigned long addr[9]; // 记录经过的IP用作路由 } IpOptionHeader;

#define DEF_PACKET_SIZE 32 // 包的默认长度 #define MAX_PACKET 1024 // 最大长度

#define MAX_IP_HDR_SIZE 60 // IP头部的最大长度

BOOL bRecordRoute; //是否记录路由 int datasize; //数据长度 char *lpdest;

// 使用帮助函数

void usage(char *progname) {

printf(\"usage: ping -r [data size] \"); printf(\" -r record route \");

printf(\" host remote machine to ping \"); printf(\" datasize can be up to 1KB \"); ExitProcess(-1); }

// 用于填充ICMP的头部

void FillICMPData(char *icmp_data, int datasize) {

IcmpHeader *icmp_hdr = NULL; char *datapart = NULL;

icmp_hdr = (IcmpHeader*)icmp_data;

icmp_hdr->i_type = ICMP_ECHO; // ICMP 回送请求 类型为 8 icmp_hdr->i_code = 0; // code 为 0

icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); //填充当前进程号,用于返回响应的标示 icmp_hdr->i_cksum = 0; icmp_hdr->i_seq = 0;

datapart = icmp_data + sizeof(IcmpHeader); // 使用字符填充数据包

memset(datapart, 'E' , datasize - sizeof(IcmpHeader)); }

// Function: checksum // 计算校验和

USHORT checksum(USHORT *buffer, int size) {

unsigned long cksum=0; while(size > 1) {

cksum += *buffer++; size -= sizeof(USHORT); }

if(size) {

cksum += *(UCHAR*)buffer; }

cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); }

// Function: DecodeIPOptions

// 解析IP头部的选项部分,由于路径的路由记录 void DecodeIPOptions(char *buf, int bytes) {

IpOptionHeader *ipopt = NULL; IN_ADDR inaddr; int i;

HOSTENT *host = NULL;

ipopt = (IpOptionHeader *)(buf + 20); printf(\"Record Route: \");

for(i = 0; i < (ipopt->ptr / 4) - 1; i++) {

inaddr.S_un.S_addr = ipopt->addr[i]; if (i != 0) printf(\" \");

host = gethostbyaddr((char *)&inaddr.S_un.S_addr, sizeof(inaddr.S_un.S_addr), AF_INET); if (host)

printf(\"(%-15s) %s \ else

printf(\"(%-15s) \ }

printf(\"\\n\"); return; }

// Function: DecodeICMPHeader // 从返回的IP数据中解析出ICMP的头部数据

void DecodeICMPHeader(char *buf, int bytes, struct sockaddr_in *from) {

IpHeader *iphdr = NULL; IcmpHeader *icmphdr = NULL; unsigned short iphdrlen; DWORD tick;

static int icmpcount = 0; iphdr = (IpHeader *)buf; // 头部字段乘以4

iphdrlen = iphdr->h_len * 4; tick = GetTickCount();

if((iphdrlen == MAX_IP_HDR_SIZE) && (!icmpcount)) DecodeIPOptions(buf, bytes); if(bytes < iphdrlen + ICMP_MIN) {

printf(\"Too few bytes from %s \\n\ }

icmphdr = (IcmpHeader*)(buf + iphdrlen);

if(icmphdr->i_type != ICMP_ECHOREPLY) // echo type must be 0 {

printf(\"non-echo type %d recvd \\n\ return; }

// 检查该返回的数据是否为上述程序发送的数据的响应

// In FillICMPData function we assign the i_id is GetCurrentProcessId() if(icmphdr->i_id != (USHORT)GetCurrentProcessId()) {

printf(\"someone else's packet! \\n\"); return ; }

printf(\"%d bytes from %s \ printf(\"icmp_seq = %d. \ printf(\"time: %d ms\ printf(\"\\n\"); icmpcount++; return; }

// Function: ValidateArgs // 检查输入的参数是否合法

void ValidateArgs(int argc, char **argv) {

bRecordRoute = FALSE; lpdest = NULL;

datasize = DEF_PACKET_SIZE;

for(int i = 1; i < argc; i++) {

if((argv[i][0] == '-') || (argv[i][0] == '/')) {

switch (tolower(argv[i][1])) {

case 'r': // 记录经过的路由 ping -r dns.chzu.edu.cn bRecordRoute = TRUE; break; default:

usage(argv[0]); break; } }

else if(isdigit(argv[i][0])) datasize = atoi(argv[i]); else

lpdest = argv[i]; } } //

// Function: main

// 创建ICMP 原始套接字,创建ICMP头部,添加合适的IP头部选项,发送ICMP数据包 // 并等待响应,在等待响应式设置一个计时器,超时将放回。否则,对收到的数据包 // 进行解析。

int main(int argc, char **argv) {

WSADATA wsaData;

SOCKET sockRaw = INVALID_SOCKET; struct sockaddr_in dest, from; int bread,

fromlen = sizeof(from), timeout = 1000, ret;

char icmp_data[MAX_PACKET], recvbuf[MAX_PACKET]; unsigned int addr = 0; USHORT seq_no = 0; struct hostent *hp = NULL; IpOptionHeader ipopt;

if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {

printf(\"WSAStartup() failed: %d \\n\ return -1; }

// 检查用户输入的参数是否有效 ValidateArgs(argc, argv);

// 使用 IPPROTO_ICMP 创建一个原始套接字

sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); //sock创建失败

if(sockRaw == INVALID_SOCKET) {

printf(\"WSASocket() failed: %d \\n\ return -1; }

// bRecordRoute=true表示需要记录经过的路由 if(bRecordRoute) {

// 为每一个ICMP包设置IP头部的选项部分。 memset(&ipopt,0,sizeof(ipopt));

ipopt.code = IP_RECORD_ROUTE; // 设置记录路由选项

ipopt.ptr = 4; // Point to the first addr offset ipopt.len = 39; // 选项长度

ret = setsockopt(sockRaw, IPPROTO_IP, IP_OPTIONS, (char *)&ipopt, sizeof(ipopt)); if (ret == SOCKET_ERROR) {

printf(\"setsockopt(IP_OPTIONS) failed: %d \\n\ return -1; } }

// 设置发送或接收的超时时间 timeout = 1000;

bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); if(bread == SOCKET_ERROR) {

printf(\"setsockopt(SO_RCVTIMEO) failed: %d \\n\ return -1; }

timeout = 1000;

bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); if(bread == SOCKET_ERROR) {

printf(\"setsockopt(SO_SNDTIMEO) failed: %d \\n\ return -1; }

// 根据需要解析域名为IP memset(&dest, 0, sizeof(dest)); dest.sin_family = AF_INET;

if((dest.sin_addr.s_addr = inet_addr(lpdest)) == INADDR_NONE) {

if((hp = gethostbyname(lpdest)) != NULL) {

memcpy(&(dest.sin_addr), hp->h_addr, hp->h_length);

dest.sin_family = hp->h_addrtype;

printf(\"dest.sin_addr = %s \\n\ } else {

printf(\"gethostbyname() failed: %d \\n\ return -1; } }

// 创建ICMP包

datasize += sizeof(IcmpHeader); memset(icmp_data,0,MAX_PACKET); // 填充ICMP包

FillICMPData(icmp_data,datasize);

// 开始发送或者接收ICMP包 while(1) {

static int nCount = 0; int bwrote;

//设置发送ICMP包的数量,该程序设置为4个包 if(nCount++ == 4) break;

((IcmpHeader*)icmp_data)->i_cksum = 0;

((IcmpHeader*)icmp_data)->timestamp = GetTickCount(); ((IcmpHeader*)icmp_data)->i_seq = seq_no++;

((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);

bwrote = sendto(sockRaw, icmp_data, datasize, 0, (struct sockaddr*)&dest, sizeof(dest)); if(bwrote == SOCKET_ERROR) {

if(WSAGetLastError() == WSAETIMEDOUT) {

printf(\"timed out \\n\"); continue; }

printf(\"sendto() failed: %d \\n\ return -1; }

if(bwrote < datasize) {

printf(\"Wrote %d bytes \\n\ }

bread = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen); if(bread == SOCKET_ERROR) {

if(WSAGetLastError() == WSAETIMEDOUT) {

printf(\"timed out \\n\"); continue; }

printf(\"recvfrom() failed: %d \\n\ return -1; }

DecodeICMPHeader(recvbuf, bread, &from); Sleep(1000); }

// Socket清理

if (sockRaw != INVALID_SOCKET) closesocket(sockRaw); WSACleanup();

return 0; }

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- azee.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务