002.原始套接字,构建ICMP包,实现Ping程序,简化版

大致流程:

将ICMP头和时间数据设置好后,通过创建好的原始套接字socket发出去。目的主机计算效验和后会将数据原样返回,用当前时间和返回的数据结算时间差,计算出rtt。

其中:

1.我们以ping www.baidu.com为例,但我们并没有计算平均rtt

2.我们并没有手动创建IP头,而是交给了内核去帮我们处理

3.中间设计到的函数需要查相关资料

代码实现:

  1 /*
  2  ============================================================================
  3  Name        : myping.c
  4  Author      : huh
  5  Version     : 0.01
  6  Copyright   : Your copyright notice
  7  Description : Hello World in C, Ansi-style
  8  ============================================================================
  9  */
 10
 11 #include <sys/types.h>
 12 #include <sys/select.h>
 13 #include <stdio.h>
 14 #include <time.h>
 15 #include <string.h>
 16 #include <netdb.h>
 17 #include <arpa/inet.h>
 18 #include <unistd.h>
 19 #include <signal.h>
 20 #include <sys/time.h>
 21 #include <sys/socket.h>
 22 #include <netinet/ip_icmp.h>
 23
 24 #define PACKET_SIZE 1024*4
 25
 26 int pid;
 27 int sockfd;
 28 int datalen = 56;
 29 int nsent = 1, nrecv = 1;
 30 char sendbuf[PACKET_SIZE];
 31 char recvbuf[PACKET_SIZE];
 32 struct sockaddr_in dest_addr; //socket目的地址
 33 struct sockaddr_in src_addr;
 34 struct timeval tvrecv;
 35
 36 void send_packet();
 37 void un_packet(int);
 38 void tv_sub(struct timeval *out, struct timeval *in);
 39 unsigned short cal_chksum(unsigned short *addr, int len);
 40
 41 int main()
 42 {
 43     pid = getpid();
 44     char str[20];
 45     unsigned int inaddr;
 46     struct hostent *host;
 47     int size = 1024 * 25;
 48     strcpy(str,"www.baidu.com");
 49     //IP地址(域名)到无符号整数的转换
 50     inaddr = inet_addr(str);
 51     if (inaddr == INADDR_NONE)
 52     {
 53         host = gethostbyname(str);
 54         if (host == NULL)
 55         {
 56             printf("参数格式不正确,请重新输入!\n");
 57             return 0;
 58         }
 59         memcpy((char*) &inaddr, host->h_addr, sizeof(dest_addr.sin_addr));
 60     }
 61     //设置套接字地址
 62     dest_addr.sin_family = AF_INET;
 63     memcpy((char *)&dest_addr.sin_addr, (char *)&inaddr,sizeof(inaddr));
 64     //创建套接字
 65     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 66     //改变socket缓冲区大小
 67     setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
 68
 69     printf("PING %s (%s) %d(84) bytes of data.\n",str, inet_ntoa(dest_addr.sin_addr), datalen);
 70     //不停的发送和接受ICMP数据包
 71     while (1)
 72     {
 73         send_packet();
 74         int src_addr_len = sizeof(struct sockaddr_in);
 75         //接收数据包,一直阻塞到有数据包到达为止
 76         int len = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *) &src_addr, (socklen_t *) &src_addr_len);
 77         if(len < 0)
 78             printf("recvfrom error!\n");
 79         un_packet(len);
 80         sleep(1); //间隔一秒,后面会用信号实现每秒发送一个ICMP包。
 81     }
 82     return 0;
 83 }
 84
 85 //解包
 86 void un_packet(int len)
 87 {
 88     int hlen1;
 89     double rtt;
 90     struct ip *ip;
 91     struct icmp *icmp;
 92     struct timeval *tvsend;
 93
 94     ip = (struct ip *) recvbuf;
 95     hlen1 = ip->ip_hl << 2;
 96     if (ip->ip_p != IPPROTO_ICMP)
 97         return ;
 98
 99     icmp = (struct icmp *) (recvbuf + hlen1);
100     len -= hlen1;
101
102     if ((icmp->icmp_type == ICMP_ECHOREPLY))
103     {
104         if (icmp->icmp_id != pid)
105             return;
106         tvsend = (struct timeval *) icmp->icmp_data;  //发送时间
107         gettimeofday(&tvrecv, NULL);  //得到当前时间
108
109         tv_sub(&tvrecv, tvsend); //计算接收和发送的时间差
110         rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec / 1000.0; //以毫秒单位计算rtt
111         printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3fms\n", len, inet_ntoa(src_addr.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
112         nrecv++;
113     }
114 }
115
116 //手动构建数据包,并通过原始套接字发送
117 void send_packet()
118 {
119     int len;
120     struct icmp *icmp;
121     icmp = (struct icmp *) (sendbuf);
122     icmp->icmp_type = ICMP_ECHO;  //拼接icmp
123     icmp->icmp_code = 0;
124     icmp->icmp_id = pid;   //2字节
125     icmp->icmp_seq = nsent++; //2字节
126     memset(icmp->icmp_data, 0xa5, datalen);
127     gettimeofday((struct timeval *) icmp->icmp_data, NULL);    //将发送时间作为数据传递过去
128
129     len = datalen + 8;
130     icmp->icmp_cksum = 0;  //校验和需要先置0
131     icmp->icmp_cksum = cal_chksum((unsigned short *) icmp, len);  //计算效验和
132
133     sendto(sockfd, sendbuf, len, 0, (struct sockaddr *) &dest_addr,
134             sizeof(dest_addr));  //将包发出去
135     //printf("package have sent!\n");
136 }
137
138 //计算效验和
139 unsigned short cal_chksum(unsigned short *addr, int len)
140 {
141     int nleft = len;
142     int sum = 0;
143     unsigned short *w = addr;
144     unsigned short answer = 0;
145     //把ICMP报头二进制数据以2字节为单位累加起来
146     while (nleft > 1)
147     {
148         sum += *w++;
149         nleft -= 2;
150     }
151     if (nleft == 1)
152     {
153         *(unsigned char *) (&answer) = *(unsigned char *) w;
154         sum += answer;
155     }
156     sum = (sum >> 16) + (sum & 0xffff);
157     sum += (sum >> 16);
158     answer = ~sum;
159     return answer;
160 }
161
162 //计算时间差
163 void tv_sub(struct timeval *out, struct timeval *in)
164 {
165     if ((out->tv_usec -= in->tv_usec) < 0)
166     {
167         --out->tv_sec;
168         out->tv_usec += 1000000;
169     }
170     out->tv_sec -= in->tv_sec;
171 }
时间: 2024-10-18 17:53:08

002.原始套接字,构建ICMP包,实现Ping程序,简化版的相关文章

linux原始套接字(2)-icmp请求与接收

一.概述                                                    上一篇arp请求使用的是链路层的原始套接字.icmp封装在ip数据报里面,所以icmp请求可以直接使用网络层的原始套接字,即socket()第一个参数是PF_INET.如下: 1 sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); icmp报文不同的类型有不同的格式,我们以icmp回显请求和会显应答报文格式(即ping程序使用的报文类型)

004.原始套接字,拼接UDP数据包,通信

大致流程: 建立一个client端,一个server端,自己构建IP头和UDP头,写入数据(hello,world!)后通过原始套接字(SOCK_RAW)将包发出去. server端收到数据后,打印UDP数据并发送确认消息(yes),client收到yes后将其打印. 其中: client端IP:192.168.11.104 端口:8600 server端IP:192.168.11.105 端口:8686 注意事项: 1.运行原始套接字socket需要有root权限. 2.注意主机字节序和网络字

原始套接字学习笔记(1)

一般来说,我们会用到如下三种套接字: TCP:SOCK_STREAM套接字 UDP:SOCK_DGRAM套接字 原始套接字:SOCK_RAW套接字 对于TCP和UDP两种套接字,相对来说只要配置好IP地址和端口号就可以了,比较简单,这里我们主要介绍原始套接字的使用. 1.原始套接字简介 原始套接字的强大之处在于,不同与UDP和TCP套接字只能访问传输层和传输层以上的数据包,原始套接字可以访问传输层以下的数据包,实现上至应用层下至链路层的数据操作,尤其适合用来进行抓包等工作. 2.原始套接字的建立

ICMP拒绝服务攻击(原始套接字系列四)

拒绝服务攻击(DoS)企图通过使被攻击的计算机资源消耗殆尽从而不能再提供服务,拒绝服务攻击是最容易实施的攻击行为.中美黑客大战中的中国黑客一般对美进行的就是拒绝服务攻击,其技术手段大多不够高明. ICMP实现拒绝服务攻击的途径有二:一者"单刀直入",一者"借刀杀人".具体过程分析如下:   ICMPFLOOD攻击 大量的 ICMP消息发送给目标系统,使得它不能够对合法的服务请求做出响应.中美黑客大战中的多数中国黑客采用的正是此项技术.ICMP FLOOD攻击实际上是

包分析(原始套接字七)

紧接上节,DecodeIpPack()函数完成包的解析: //IP包解析int DecodeIpPack(char *buf, int iBufSize){ IP_HEADER *pIpheader; int iProtocol, iTTL; char szProtocol[MAX_PROTO_TEXT_LEN]; char szSourceIP[MAX_ADDR_LEN], szDestIP[MAX_ADDR_LEN]; SOCKADDR_IN saSource, saDest; pIphea

利用原始套接字实现一个简单的采集网络数据包

//利用原始套接字实现一个简单的采集网络数据包,并进行反向解析IP,MAC地址#include <stdio.h>#include <sys/socket.h>#include <unistd.h>#include <sys/types.h>#include <linux/if_ether.h>#include <linux/in.h> #define BUFFER_MAX 2048 int main(int argc, char *

Windows下利用原始套接字实现的一个抓包程序Demo

早就学过了套接字编程,但是原始套接字还没用过.最近听了网络安全老师的课,心血来潮,写了个抓包程序Demo,把代码分享给大家,感兴趣的可以看看.引用一句网络安全老师的话:"你们要本着技术的心态去实践,哎,一部分人,写着写着就成黑客了". #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <WinSock2.h> #include <WS2tcpip.h> #include <s

Linux 网络编程——原始套接字实例:发送 UDP 数据包

以太网报文格式: 详细的说明,请看<MAC 头部报文分析>. IP 报文格式: 详细的说明,请看<IP 数据报格式详解>. UDP 报文格式: 详细的说明,请看<UDP 数据报格式详解>. 校验和函数: /******************************************************* 功能: 校验和函数 参数: buf: 需要校验数据的首地址 nword: 需要校验数据长度的一半 返回值: 校验和 ********************

Linux Socket 原始套接字编程

对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发之自己构建协议头.对于原始套接字编程有些细节性的东西还是需要注意的. 1. 原始套接字创建 原始套接字的编程和udp网络编程的流程有点类似,但是原始套接字编程中不需要bind操作,因为在数据接收和发送过程中使用sendto和recvfrom函数实现数据的接收和发送.不过不是说原始套接字不能使用bin