linux原始套接字(2)-icmp请求与接收
一.概述
上一篇arp请求使用的是链路层的原始套接字。icmp封装在ip数据报里面,所以icmp请求可以直接使用网络层的原始套接字,即socket()第一个参数是PF_INET。如下:
sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
icmp报文不同的类型有不同的格式,我们以icmp回显请求和会显应答报文格式(即ping程序使用的报文类型)为例:
类型为8表示请求,为0表示应答。校验和要自己计算,标识符一般为程序的进程ID号。序号自定义,一般从1开始。选项数据里面可以放时间戳,用作计算ping一次的花费时间!
icmp报文结构定义在netinet/ip_icmp.h
struct icmp { u_int8_t icmp_type; /* type of message, see below */ u_int8_t icmp_code; /* type sub code */ u_int16_t icmp_cksum; /* ones complement checksum of struct */ union { u_char ih_pptr; /* ICMP_PARAMPROB */ struct in_addr ih_gwaddr; /* gateway address */ struct ih_idseq /* echo datagram */ { u_int16_t icd_id; u_int16_t icd_seq; } ih_idseq; u_int32_t ih_void; /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ struct ih_pmtu { u_int16_t ipm_void; u_int16_t ipm_nextmtu; } ih_pmtu; struct ih_rtradv { u_int8_t irt_num_addrs; u_int8_t irt_wpa; u_int16_t irt_lifetime; } ih_rtradv; } icmp_hun; #define icmp_pptr icmp_hun.ih_pptr #define icmp_gwaddr icmp_hun.ih_gwaddr #define icmp_id icmp_hun.ih_idseq.icd_id #define icmp_seq icmp_hun.ih_idseq.icd_seq #define icmp_void icmp_hun.ih_void #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime union { struct { u_int32_t its_otime; u_int32_t its_rtime; u_int32_t its_ttime; } id_ts; struct { struct ip idi_ip; /* options and then 64 bits of data */ } id_ip; struct icmp_ra_addr id_radv; u_int32_t id_mask; u_int8_t id_data[]; } icmp_dun; #define icmp_otime icmp_dun.id_ts.its_otime #define icmp_rtime icmp_dun.id_ts.its_rtime #define icmp_ttime icmp_dun.id_ts.its_ttime #define icmp_ip icmp_dun.id_ip.idi_ip #define icmp_radv icmp_dun.id_radv #define icmp_mask icmp_dun.id_mask #define icmp_data icmp_dun.id_data };
二.icmp请求代码
/** * @file icmp_request.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip_icmp.h> #include <sys/time.h> /* icmp报文长度 */ #define ICMP_PACKET_LEN sizeof(struct icmp) void err_exit(const char *err_msg) { perror(err_msg); exit(); } /* 校验和 */ unsigned short check_sum(unsigned short *addr, int len) { int nleft = len; ; unsigned short *w = addr; unsigned ; ) { sum += *w++; nleft -= ; } ) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum += answer; } sum = (sum >> ) + (sum & 0xffff); sum += (sum >> ); answer = ~sum; return answer; } /* 填充icmp报文 */ struct icmp *fill_icmp_packet(int icmp_type, int icmp_sequ) { struct icmp *icmp_packet; icmp_packet = (struct icmp *)malloc(ICMP_PACKET_LEN); icmp_packet->icmp_type = icmp_type; icmp_packet->icmp_code = ; icmp_packet->icmp_cksum = ; icmp_packet->icmp_id = htons(getpid()); icmp_packet->icmp_seq = htons(icmp_sequ); /* 发送时间 */ gettimeofday((struct timeval *)icmp_packet->icmp_data, NULL); /* 校验和 */ icmp_packet->icmp_cksum = check_sum((unsigned short *)icmp_packet, ICMP_PACKET_LEN); return icmp_packet; } /* 发送icmp请求 */ void icmp_request(const char *dst_ip, int icmp_type, int icmp_sequ) { struct sockaddr_in dst_addr; struct icmp *icmp_packet; int sockfd, ret_len; char buf[ICMP_PACKET_LEN]; /* 请求的地址 */ bzero(&dst_addr, sizeof(struct sockaddr_in)); dst_addr.sin_family = AF_INET; dst_addr.sin_addr.s_addr = inet_addr(dst_ip); ) err_exit("sockfd()"); /* icmp包 */ icmp_packet = fill_icmp_packet(icmp_type, icmp_sequ); memcpy(buf, icmp_packet, ICMP_PACKET_LEN); /* 发送请求 */ ret_len = sendto(sockfd, buf, ICMP_PACKET_LEN, , (struct sockaddr *)&dst_addr, sizeof(struct sockaddr_in)); ) printf("sendto() ok!!!\n"); close(sockfd); } int main(int argc, const char *argv[]) { ) { printf(]); exit(); } /* 发送icmp请求 */ icmp_request(argv[], , ); ; }
流程:命令行接收icmp请求的目标IP,106行发送请求,指定icmp类型是8,序列号是1。然后通过目标IP地址创建网络地址结构,接着创建ICMP类型的原始套接字,填充icmp报文,并把发送时间填到icmp的数据结构。
三.icmp接收代码
/** * @file icmp_recv.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/time.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> /* IP首部长度 */ #define IP_HEADER_LEN sizeof(struct ip) /* icmp报文长度 */ #define ICMP_PACKET_LEN sizeof(struct icmp) /* IP + ICMP长度 */ #define IP_ICMP_PACKET_LEN IP_HEADER_LEN + ICMP_PACKET_LEN void err_exit(const char *err_msg) { perror(err_msg); exit(); } /* 计算发送时间与接收时间的毫秒差 */ float time_interval(struct timeval *recv_time, struct timeval *send_time) { ; /* 如果接收的时间微妙小于发送的微妙 */ if (recv_time->tv_usec < send_time->tv_usec) { recv_time->tv_sec -= ; recv_time->tv_usec += ; } msec = (recv_time->tv_sec - send_time->tv_sec) * 1000.0 + (recv_time->tv_usec - send_time->tv_usec) / 1000.0; return msec; } int main(void) { struct ip *ip_header; struct icmp *icmp_packet; char buf[IP_ICMP_PACKET_LEN]; struct timeval *recv_timeval, *send_timeval; int sockfd, ret_len; ) err_exit("sockfd()"); recv_timeval = malloc(sizeof(struct timeval)); ) { ret_len = recv(sockfd, buf, IP_ICMP_PACKET_LEN, ); ) { /* 接收时间 */ gettimeofday(recv_timeval, NULL); /* 取出ip首部 */ /* 取出icmp报文 */ ip_header = (struct ip *)buf; icmp_packet = (struct icmp *)(buf + IP_HEADER_LEN); /* 取出发送时间 */ send_timeval = (struct timeval *)icmp_packet->icmp_data; printf("===============================\n"); printf("from ip:%s\n", inet_ntoa(ip_header->ip_src)); printf("icmp_type:%d\n", icmp_packet->icmp_type); printf("icmp_code:%d\n", icmp_packet->icmp_code); printf("time interval:%.3fms\n", time_interval(recv_timeval, send_timeval)); } } free(recv_timeval); close(sockfd); ; }
流程:创建ICMP类型的原始套接字后直接接收。首先获取接收时间,然后依次取出ip首部,icmp报文,再取出icmp的请求时间。从ip首部获取源ip地址,从icmp报文获取该报文的类型,代码号,通过发送时间和接收时间计算毫秒差!
四.实验
.打开wireshark一起观察。以root运行icmp_recv,再运行icmp_request
可以看到icmp的类型是0,代码也是0。响应时间跟我们的程序差不多。
.现在我们请求一个不可达的ip地址
主机不可达时,返回的icmp报文类型是3,代码是1。报文结构不同,取出的发送时间是不正常的,所以这里计算的时间间隔也不正常。wireshark里面的结果是,本机自动广播了一个arp请求,但没有机器回答本机。
部分icmp类型:
linux原始套接字(2)-icmp请求与接收的更多相关文章
- linux原始套接字(1)-arp请求与接收
一.概述 以太网的arp数据包结构: arp结构op操作参数:1为请求,2为应答. 常用的数据结构如 ...
- Linux原始套接字实现分析---转
http://blog.chinaunix.net/uid-27074062-id-3388166.html 本文从IPV4协议栈原始套接字的分类入手,详细介绍了链路层和网络层原始套接字的特点及其内核 ...
- 关于linux 原始套接字编程
关于linux 网络编程最权威的书是<<unix网络编程>>,但是看这本书时有些内容你可能理解的不是很深刻,或者说只知其然而不知其所以然,那么如果你想搞懂的话那么我建议你可以看 ...
- Linux原始套接字抓取底层报文
1.原始套接字使用场景 我们平常所用到的网络编程都是在应用层收发数据,每个程序只能收到发给自己的数据,即每个程序只能收到来自该程序绑定的端口的数据.收到的数据往往只包括应用层数据,原有的头部信息在传递 ...
- linux原始套接字(4)-构造IP_UDP
一.概述 同上一篇tcp一样,udp也是封装在ip报文里面.创建UDP的原始套接字如下: (soc ...
- linux原始套接字(3)-构造IP_TCP发送与接收
一.概述 tcp报文封装在ip报文中,创建tcp的原始套接字如下: sockfd = socket ...
- Linux网络编程——原始套接字编程
原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据.区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有 ...
- UNIX网络编程——原始套接字的魔力【续】
如何从链路层直接发送数据帧 上一篇里面提到的是从链路层"收发"数据,该篇是从链路层发送数据帧. 上一节我们主要研究了如何从链路层直接接收数据帧,可以通过bind函数来将原始套接字绑 ...
- Linux基础(11)原始套接字
一边接收函数返回一边判断返回值时一定要把接收的优先级加()提高再去判断 例 if((sockfd = socket()) < 0) 问题: 如何实现SYN扫描器扫描端口 , 比如AB两个设备要进 ...
随机推荐
- jQuery中的事件与动画 (你的明天Via Via)
众所周知,页面在加载时,会触发load事件:当用户单击某个按钮时,会触发该按钮的click事件. 这些事件就像日常生活中,人们按下开关,灯就亮了(或者灭了),往游戏机里投入游戏币就可以启动游戏一样, ...
- (原) tmux安装与使用
本文为原创文章,转载请注明出处,谢谢 tmux安装 1.yum search tmux 2.yum install package (package为选择要安装的包名,如上图“tmux.x86_64” ...
- 从启动日志看Spring IOC的初始化和Bean生命周期
一.Tomcat中启动IoC容器的日志 启动Tomcat等容器时,控制台每次都打印出一些日志. 最近刚好在研究Spring源码,所以换个角度,从启动日志来简单的看看Spring的初始化过程! 以下是T ...
- 【问题】js 改变鼠标样式,chrome浏览器不能立即更新,暂没有解决办法
元素的css,cursor可以改变鼠标样式.也就是鼠标放到元素上去时,改变为相应状态. 通过JS改变cursor时,我发现chrome浏览器不能立即更新,需要动一下鼠标才行,试了几个其它浏览器都是立即 ...
- Eclipse开发STM32出现找不到函数的情况的解决方法
问题表现: 在明明引用了头文件的情况下,出现“undefined reference to `…'”的情况,例如下图: 解决方法: 在左边的数据目录定位到“system\src\stm32f0-st ...
- sqlmap笔记
sqlmap -u "注入链接" --其他参数或命令 (-v 1表示回显出注入过程) [判断指定字段是否存在注入点]当链接包含两个参数时,可用-p开关选择要注入的参数,例检测id是 ...
- Sharepoint学习笔记—习题系列--70-576习题解析 -(Q92-Q94)
Question 92 You are designing a SharePoint 2010 application. You need to make sure the application ...
- 关于android的一些基础知识
怕自己以后忘了,所以在这里先写写! equal和==的区别是,一个用于判断字符串,一个用于判断int是否相等 equal比较的是对象,==比较的是值
- 网络初见&网络监测
这里需要下载一个第三方 Reachability-master 大家可以百度一下 下载之后把 Reachability拖进来 具体代码如下 #import "ViewController.h ...
- Blocks的实现
相关结构体与函数 __block_impl 存储block对应类型信息(属于哪个Block大类,以及对应的回调函数指针FuncPtr struct __block_impl { void *isa; ...