Ping
是为了测试另外一台主机是否可达,如今已经成为一种经常使用的网络状态检查工具。linux
常见的ping命令:ios
/**** 往目的追击发送固定包数 ****/ ping -c 3 www.baidu.com // ping百度发送3个包 /**** 设置两次发包之间的等待时间 ****/ ping -i 5 www.baidu.com // 两包之间的时间间隔为5s ping -i 0.1 www.baidu.com // 两包之间的时间间隔为0.1s /**** 检查本地网络接口是否已经启动并正在运行 ****/ ping 127.0.0.1 (linux: ping 0) ping localhost /**** 超级用户能够利用 -f 几秒钟发送数十万个包给主服务形成压力 *****/ sudo ping -f www.baidu.com /**** 让电脑发出蜂鸣声: 响应包到达目时,会发出声音 ****/ ping -a www.baidu.com /**** 只打印ping的汇总结果 ****/ ping -c 5 -q www.baidu.com /**** 修改ping包(icmp包)的大小 ****/ ping -s 100 -c 5 www.baidu.com
示例:c++
macdeiMac:PhoneNetSDK ethan$ ping www.baidu.com PING www.a.shifen.com (61.135.169.121): 56 data bytes 64 bytes from 61.135.169.121: icmp_seq=0 ttl=49 time=32.559 ms 64 bytes from 61.135.169.121: icmp_seq=1 ttl=49 time=32.413 ms 64 bytes from 61.135.169.121: icmp_seq=2 ttl=49 time=32.489 ms ^C --- www.a.shifen.com ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 32.413/32.487/32.559/0.060 ms macdeiMac:PhoneNetSDK ethan$
分析以上结果:git
发送端信息github
主机响应的信息安全
统计总结信息服务器
TTL(Time to live): IP数据报的生存时间,单位是hop(跳)。好比64,每过一个路由器就把该值减1,若是减到0 就表示路由已经太长了仍然找不到目的主机的网络,就丢弃该包。网络
问题:在发包时,为何发送的是56字节的包,主机响应的倒是64字节的包? 在这里的56和64是同一个概念吗?并发
互联网控制消息协议(英语:Internet Control Message Protocol,缩写:ICMP)是互联网协议族的核心协议之一。它是TCP/IP协议族的一个子协议,它用于TCP/IP网络中发送控制消息,提供可能发生在通讯环境中的各类问题反馈,经过这些信息,使管理者能够对所发生的问题做出诊断,而后采起适当的措施解决。socket
控制消息有:目的不可达下次,超时信息,重定向消息,时间戳请求和时间戳响应消息,回显请求和回显应答消息。
ICMP [1]依靠IP來完成它的任务,它是IP的主要部分。它与传输协议(如TCP和UDP)显著不一样:它通常不用于在两点间传输数据。它一般不禁网络程序直接使用,除了ping和traceroute这两个特別的例子。 IPv4中的ICMP被称做ICMPv4,IPv6中的ICMP则被称做ICMPv6。
CMP是在RFC 792中定义的互联网协议族之一。一般用于返回的错误信息或是分析路由。ICMP错误消息老是包括了源数据并返回给发送者。 ICMP错误消息的例子之一是TTL值过时。每一个路由器在转发数据报的时候都会把IP包头中的TTL值减1。若是TTL值为0,“TTL在传输中过时”的消息将会回报给源地址。 每一个ICMP消息都是直接封裝在一个IP数据包中的,所以,和UDP同样,ICMP是不可靠的。
虽然ICMP是包含在IP数据包中的,可是对ICMP消息一般会特殊处理,会和通常IP数据包的处理不一样,而不是做为IP的一个子协议来处理。在不少时候,须要去查看ICMP消息的內容,而后发送过当的错误消息到那个原來产生IP数据包的程序,即那个致使ICMP信息被传送的IP数据包。
不少经常使用的工具是基于ICMP消息的。traceroute是经过发送包含有特殊的TTL的包,而后接收ICMP超超消息和目标不可达消息來实现的。ping则是用ICMP的”Echo request”(类别代码:8)和”Echo reply”(类别代码:0)消息來实现的。
ICMP报头从IP报头的第160位开始(ip首部20字节)
Echo Reply
类型的消息中要返回这个字段Echo Reply
类型的消息中要返回这个字段填充的数据紧接在ICMP报头的后面(以8位为一组):
Ping
是为了测试另外一台主机是否可达,如今已经成为一种经常使用的网络状态检查工具。该程序发送一份 ICMP回显请求报文给远程主机,并等待返回 ICMP回显应答。
ping 使用的是ICMP协议,它发送icmp回送请求消息给目的主机。ICMP协议规定:目的主机必须返回ICMP回送应答消息给源主机。若是源主机在必定时间内收到应答,则认为主机可达。大多数的 TCP/IP 实现都在内核中直接支持Ping服务器,ICMP回显请求和回显应答报文以下图所示。
ping的原理:
ping的原理是用类型码为8的ICMP发请求,收到请求的主机则用类型码为0的ICMP回应。经过计算ICMP应答报文数量和与接受与发送报文之间的时间差,判断当前的网络状态。这个往返时间的计算方法是:ping命令在发送ICMP报文时将当前的时间值存储在ICMP报文中发出,当应答报文返回时,使用当前时间值减去存放在ICMP报文数据中存放发送请求的时间值来计算往返时间。ping返回接收到的数据报文字节大小、TTL值以及往返时间。
我在命令行中ping www.baidu.com 如下是显示结果:
如上图所示,icmp包的type是8 , 是request请求; icmp的包type是0 ,是reply.
OSI七层模型以及TCP/IP模型:
两台计算机经过TCP/IP的通讯过程以下:
传输层及其如下的机制由内核提供,应用层由用户进程提供,应用程序对通信数据的含义进行解释,而传输层及其如下处理通信的细节,将数据从一台计算机经过必定的路径发送到另外一台计算机。应用层数据经过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装(Encapsulation)。
TCP/IP数据包的封装:
目的主机收到数据包后,通过各层协议栈最后到达应用程序。
以太网驱动程序首先根据以太网首部中的“上层协议”字段肯定该数据帧的有效载荷是IP、ARP仍是RARP协议的数据报,而后交给相应的协议处理。假如是IP数据报,IP协议再根据IP首部中的“上层协议”字段肯定该数据报的有效载荷是TCP、UDP、ICMP仍是IGMP,而后交给相应的协议处理。假如是TCP段或UDP段,TCP或UDP协议再根据TCP首部或UDP首部的“端口号”字段肯定应该将应用层数据交给哪一个用户进程。IP地址是标识网络中不一样主机的地址,而端口号就是同一台主机上标识不一样进程的地址,IP地址和端口号合起来标识网络中惟一的进程。
注意,虽然IP、ARP和RARP数据报都须要以太网驱动程序来封装成帧,可是从功能上划分,ARP和RARP属于链路层,IP属于网络层。虽然ICMP、IGMP、TCP、UDP的数据都须要IP协议来封装成数据报,可是从功能上划分,ICMP、IGMP与IP同属于网络层,TCP和UDP属于传输层。
IPv4数据包格式以下:
关于首部长度:
根据IP数据报,判断当前包是不是IPv4
version占4位,首部长度占4位,version = 4(IPv4), ipheader=20. 因为首部长度是以4字节为单位的-> version: 0100 ; 首部长度:0101 获取version: 0100 0101 & 0xFO(11110000) = 01000000 = 0x40 获取首部长度: 0100 0101 & 0x0F(00001111) = 0000 0101 = 5个4字节 = 20 Byte
根据ping的结果,咱们须要解决如下问题:
macdeiMac:PhoneNetSDK ethan$ ping www.baidu.com PING www.a.shifen.com (61.135.169.121): 56 data bytes 64 bytes from 61.135.169.121: icmp_seq=0 ttl=49 time=32.559 ms 64 bytes from 61.135.169.121: icmp_seq=1 ttl=49 time=32.413 ms 64 bytes from 61.135.169.121: icmp_seq=2 ttl=49 time=32.489 ms ^C --- www.a.shifen.com ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 32.413/32.487/32.559/0.060 ms macdeiMac:PhoneNetSDK ethan$
以上问题解决方案以下:
IP包定义:
typedef struct PNetIPHeader { uint8_t versionAndHeaderLength; uint8_t differentiatedServices; uint16_t totalLength; uint16_t identification; uint16_t flagsAndFragmentOffset; uint8_t timeToLive; uint8_t protocol; uint16_t headerChecksum; uint8_t sourceAddress[4]; uint8_t destinationAddress[4]; // options... // data... }PNetIPHeader;
ICMP包定义:
/* use linux style . totals 64B */ typedef struct UICMPPacket { uint8_t type; uint8_t code; uint16_t checksum; uint16_t identifier; uint16_t seq; char fills[56]; // data }UICMPPacket;
构造ICMP包:
+ (UICMPPacket *)constructPacketWithSeq:(uint16_t)seq andIdentifier:(uint16_t)identifier { UICMPPacket *packet = (UICMPPacket *)malloc(sizeof(UICMPPacket)); packet->type = ENU_U_ICMPType_EchoRequest; packet->code = 0; packet->checksum = 0; packet->identifier = OSSwapHostToBigInt16(identifier); packet->seq = OSSwapHostToBigInt16(seq); memset(packet->fills, 65, 56); packet->checksum = [self in_cksumWithBuffer:packet andSize:sizeof(UICMPPacket)]; return packet; }
发送icmp包:
UICMPPacket *packet = [PhoneNetDiagnosisHelper constructPacketWithSeq:index andIdentifier:identifier]; _sendDate = [NSDate date]; ssize_t sent = sendto(socket_client, packet, sizeof(UICMPPacket), 0, (struct sockaddr *)&remote_addr, (socklen_t)sizeof(struct sockaddr)); if (sent < 0) { log4cplus_warn("PhoneNetPing", "ping %s , send icmp packet error..\n",[self.host UTF8String]); }
接收icmp包:
size_t bytesRead = recvfrom(socket_client, buffer, 65535, 0, (struct sockaddr *)&ret_addr, &addrLen); if ((int)bytesRead < 0) { [self reporterPingResWithSorceIp:self.host ttl:0 timeMillSecond:0 seq:0 icmpId:0 dataSize:0 pingStatus:PhoneNetPingStatusDidTimeout]; res = YES; }else if(bytesRead == 0){ log4cplus_warn("PhoneNetPing", "ping %s , receive icmp packet error , bytesRead=0",[self.host UTF8String]); }else{ if ([PhoneNetDiagnosisHelper isValidPingResponseWithBuffer:(char *)buffer len:(int)bytesRead]) { UICMPPacket *icmpPtr = (UICMPPacket *)[PhoneNetDiagnosisHelper icmpInpacket:(char *)buffer andLen:(int)bytesRead]; int seq = OSSwapBigToHostInt16(icmpPtr->seq); NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:_sendDate]; int ttl = ((PNetIPHeader *)buffer)->timeToLive; int size = (int)(bytesRead-sizeof(PNetIPHeader)); NSString *sorceIp = self.host; // NSLog(@"PhoneNetPing, ping %@ , receive icmp packet..\n",self.host ); [self reporterPingResWithSorceIp:sorceIp ttl:ttl timeMillSecond:duration*1000 seq:seq icmpId:OSSwapBigToHostInt16(icmpPtr->identifier) dataSize:size pingStatus:PhoneNetPingStatusDidReceivePacket]; res = YES; }
从接收到的buffer中分离icmp包:
/* 从 ipv4 数据包中解析出icmp */ + (char *)icmpInpacket:(char *)packet andLen:(int)len { if (len < (sizeof(PNetIPHeader) + sizeof(UICMPPacket))) { return NULL; } const struct PNetIPHeader *ipPtr = (const PNetIPHeader *)packet; if ((ipPtr->versionAndHeaderLength & 0xF0) != 0x40 // IPv4 || ipPtr->protocol != 1) { //ICMP return NULL; } size_t ipHeaderLength = (ipPtr->versionAndHeaderLength & 0x0F) * sizeof(uint32_t); if (len < ipHeaderLength + sizeof(UICMPPacket)) { return NULL; } return (char *)packet + ipHeaderLength; }
校验接收到的icmp包:
+ (BOOL)isValidPingResponseWithBuffer:(char *)buffer len:(int)len { UICMPPacket *icmpPtr = (UICMPPacket *)[self icmpInpacket:buffer andLen:len]; if (icmpPtr == NULL) { return NO; } uint16_t receivedChecksum = icmpPtr->checksum; icmpPtr->checksum = 0; uint16_t calculatedChecksum = [self in_cksumWithBuffer:icmpPtr andSize:len-((char*)icmpPtr - buffer)]; return receivedChecksum == calculatedChecksum && icmpPtr->type == ENU_U_ICMPType_EchoReplay && icmpPtr->code == 0 && OSSwapBigToHostInt16(icmpPtr->identifier)>=KPingIcmpIdBeginNum;; }
当有些服务器禁ping时,能够选择TCP ping。
经过和目的主机及其端口创建TCP链接的方式计算其链接耗时。
/**** 设置每一个路由发送的包数 ****/ traceroute -q 5 baidu.com /**** 设置最大路由跳数 ****/ traceroute -m 5 baidu.com /**** 不作DNS解析 ****/ traceroute -n baidu.com /**** 绕过路由表直接发送到目的治具 ****/ traceroute -r baidu.com /**** 使用ICMP包取代UDP包 ****/ traceroute -I baidu.com
tacceroute是利用增长存活时间(TTL)值来实现功能的。每当一个icmp包通过一个路由器时,其存活时间值就会减1,当其存活时间为0时,路由器便会取消包发送,并发送一个ICMP TTL封包给原封包发出者。
主叫方首先发出TTL = 1 的数据包,第一个路由器将 TTL 减1得0后就再也不继续转发此数据包,而是返回一个ICMP超时报文,主叫方从超时报文中便可提取出数据包所通过的第一个路由器的地址。而后又发出一个TTL=2的ICMP数据包,可得到第二个路由器的地址,依次增长TTL便获取了沿途全部路由器位地址。
须要注意的是,并非全部路由器都会如实返回ICMP超时报文。出于安全性考虑,大多数防火墙以及启动了防火墙功能的路由器缺省配置为不返回各类ICMP报文,其路由器或交换机也可被管理员主动修改配置变为不返回ICMP报文。所以Traceroute程序不必定能拿全全部沿途路由器地址。因此当某个TTL值的数据包得不到响应是,并不能中止这一追踪过程,程序仍然会把TTL递增而发出下一个数据包。一直达到预设或用于参数制定的追踪限制时才结束追踪。
依据上述原理,利用了UDP数据包的Traceroute程序在数据包到达真正的目的主机时,就可能由于该主机没有提供UDP服务而简单将数据包丢弃,并不返回任何信息。为了解决这个问题,Traceroute故意使用了一个大于30000的端口号,因UDP协议规定端口号必须小于30000,因此目标主机收到数据包后惟一能作的事就是返回一个"端口不可达"的ICMP报文,因而主叫方就将端口不可达报文当作跟踪结束标志。
我在命令行中traceroute www.baidu.com 如下是显示结果:
如上图所示,UDP请求,第一个请求的端口是33435 , 接下来的UDP请求,端口会递增。
当到达目的地址时,目的地址会replay类型为3的包.
如上图所示,是路由器返回的ICMP包,type是11。
发送udp包,接收ip+icmp包,过滤route ip计算时间。
https://github.com/mediaios/n...
使用 UDP 的 traceroute,失败仍是比较常见的。这经常是因为,在运营商的路由器上,UDP 与 ICMP 的待遇大不相同。为了利于 troubleshooting,ICMP 的request 和 replay 是不会封的,而 UDP 则不一样。UDP 常被用来作网络攻击,由于 UDP 无需链接,于是没有任何状态约束它,比较方便攻击者伪造源 IP、伪造目的端口发送任意多的 UDP 包,长度自定义。因此运营商为安全考虑,对于 UDP 端口经常采用白名单 ACL,就是只有 ACL 容许的端口才能够经过,没有明确容许的则通通丢弃。好比容许 DNS/DHCP/SNMP 等。
发送icmp包,类型为8,每一个路由返回的icmp包类型是11的超时包,当到达目的地址时,目的地址会replay类型为0的包
https://github.com/mediaios/n...
net-diagnosis
是ios平台下的网络诊断SDK,提供的功能有:
项目地址: github
后续更多关于网络诊断的功能会不断开发完善,欢迎提交issue
另,欢迎fork和star !