ping与ping6

最近在做一个ping的功能,用python实现,要分别实现ipv4和ipv6两种栈。虽然也是用开源的ping包,并且只有不到300行,但ipv6改的时候确实感觉无从下手;所以老大改完之后,就仔细研究了一下这个包,发现囊括的知识点确实不少。为了避免自己以后忘记,这里记录下来。

ICMP协议

实现ping主要通过ICMP协议,因为IP协议不是一个可靠的协议,不保证数据被成功送达。所以需要使用ICMP网络控制报文协议。因为它传递差错报文和其他重要信息,所以通常供IP层或TCP/UP层使用,被人为是IP层一个重要组成部分。

ping与ping6_第1张图片

ICMPv4和ICMPv6消息的前4个字节(也就是前32位)是相同的。数据链路层所能发送的最大数据帧MTU为1500字节,ICMP协议在实际传输中数据包为20字节IP首部+8字节ICMP首部+1472字节(数据大小)。

ping与ping6_第2张图片

ping原理

ping程序的目的是测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给远程主机,并等待ICMP回显应答。如果源主机在一定时间内收到应答,则认为主机可达。ping的原理是用类型码为0的ICMP发请求,收到请求的主机则用类型码为8的ICMP回应。通过计算ICMP应答报文数量和接收与发送报文之间的时间差,判断当前网络状态。ping命令在发送报文的时候,将当前时间值存储在ICMP报文中发出,当应答报文返回时,使用当前值减去存放在ICMP报文数据中存放发送请求的时间值来计算往返时间。Unix系统在实现ping程序时是把ICMP报文中的标识符字段置成发送进程的 ID号。这样 即使在同一台主机上同时运行了多个 ping程序实例,ping程序也可以识别出返回的信息。


ping与ping6_第3张图片

ping包分析

my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, 1)

//ipv6:my_socket = socket.socket(socket.AF_INET6, socket.SOCK_RAW, 58)

my_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, src_nic)

my_id = os.getpid() & 0xFFFF

send_one_ping(my_socket, dest_addr, my_id, psize)

delay = receive_one_ping(my_socket, my_id, timeout)

1. socket(family,type[,protocal]) 使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。

socket类型

socket.AF_INET:服务器之间网络通信

socket.SOCK_RAW:原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文

1这里是协议编号,ICMP协议编号为1,ICMPv6的协议编号为58

这是winsock2.h里的定义。

define IPPROTO_ICMP    1

define IPPROTO_ICMPV6    58   /* ICMPv6 */

2. 默认的socket选项不够时,用setsocket来调整。socket.setsockopt(level, optname, value)

level:选项定义的层次--SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP、IPPROTO_IPV6

optname:需要设置的选项

value:选项值

SOL_SOCKET:正在使用socket选项

当level设定为SOL_SOCKET,会有一些常见选项。SO_BINDTODEVICE:可以使socket只在某个特殊的网络接口有效。这时,value就是设备名称,或者为空字符串返回默认值。

3.send_one_ping(my_socket, dest_addr, my_id, psize)

my_id:Unix系统在实现ping程序时是把ICMP报文中的标识符字段置成发送进程的 ID号。这里和0xFFFF做了&运算,表示取字符低16位

在send_one_ping中要完成构造包的工作。

这一步用到了struct模块。当我们需要用python处理二进制数据,如存储文件,socket操作时,可以使用struct模块,来处理C语言中的结构体。

pack(fmt,v1,v2,v3,...)//按照给定格式fmt,把数据封装成字符串(实际上是类似于C结构体字节流)

format          C type            python type        standard size        notes

b                signed char          integer                    1                    (3)

B            unsigned char          integer                    1                    (3)

h                    short                 integer                    1                    (3)

H             unsigned short        integer                    1                    (3)

对齐方式:!表示我们要使用网络字节顺序解析,因为数据是从网络中接收到的。(字节对齐,通常是以4个字节为单位的32位系统。故struct根据本地机器字节顺序转换,可以用格式中第一个字符来改变对齐方式。)

这里要提到signed char和unsigned char的区别,作为字符使用,都是存字符的ASCII码,作为整数使用,两种类型取值范围不通,unsigned char取0~255,signed char可取值为-128至127。在ipv4中ICMP_ECHO_REQUEST赋值为8,所以struct.pack时用b转换,在ipv6中,ICMP_ECHO_REQUEST赋值为128,所以用B来转换。

    # Remove header size from packet size

    psize = psize - 8

    # Header is type (8), code (8), checksum (16), id (16), sequence (16)

    my_checksum = 0

    # Make a dummy heder with a 0 checksum.

    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, id, 1)

    //ipv6:header = struct.pack("!BbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, id, 1)

    bytes = struct.calcsize("d")

    data = (psize - bytes) * "Q"

    data = struct.pack("d", time.time()) + data

    # Calculate the checksum on the data and the dummy header.

    my_checksum = checksum(header + data)

    # Now that we have the right checksum, we put that in. It's just easier

    # to make up a new header than to stuff it into the dummy.

    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), id, 1)

   //ipv6:header = struct.pack( "!BbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), id, 1 )

    packet = header + data

    my_socket.sendto(packet, (dest_addr, 1))  # Don't know about the 1

    //发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。

    //ipv6:my_socket.sendto(packet, (dest_addr, 58, 0, 0))

4.校验和算法

IP包发送端,首先将校验和字段设置为0,然后将IP数据包头按16比特划分单元,若包头长度非16倍数,则用0补齐,然后对各个单元采用反码加法运算(即高位溢出加到低位),将得到的和的反码填入校验和字段发送。

比如:

一个数据包:45 00 00 29 44 F1 40 00 80 06 61 8D C0 A6 01 AE 4A 7D 47 7D


ping与ping6_第4张图片


参考

https://www.cnblogs.com/JetpropelledSnake/p/9177770.html

https://www.xuebuyuan.com/3254723.html

你可能感兴趣的:(ping与ping6)