使用ping命令定位网络延迟问题

使用ping命令定位网络延迟问题

一、背景

使用ping命令发现局域网内延迟大,且变化较大。需要分析耗时在那一层。

二、分析

上图可以看出,本机ping延时非常低。在本机网络 IO 的过程中,流程会有一些差别。有差异的地方总共有两个,分别是路由和驱动程序。

路由:

对于本机网络 IO 来说,特殊之处在于在 local 路由表中就能找到路由项,对应的设备都将使用 loopback 网卡,也就是我们常见的 lO。

驱动程序:

网络设备子系统的入口函数是 dev_queue_xmit。对于真的有队列的物理设备,在该函数中进行了一系列复杂的排队等处理以后,才调用 dev_hard_start_xmit,从这个函数再进入驱动程序来发送。

但是对于启动状态的回环设备来说(q->enqueue 判断为 false),就简单多了:没有队列的问题,直接进入 dev_hard_start_xmit。接着进入回环设备的“驱动”里的发送回调函数 loopback_xmit,将 skb “发送”出去。

因此,可以从上面两层入手,排查问题!

驱动:

监控icmp包发送和接受的时刻,看时间是否过大。

但有一个问题,链路上运行的不一定只有ICMP数据包。因此,我们需要在每一层对我们发送和接收的ICMP包进行过滤。

路由:

三、ICMP

ICMP报文包含在IP数据报中,IP报头在ICMP报文的最前面。一个ICMP报文包括IP报头(至少20字节)、ICMP报头(至少八字节)和ICMP报文(属于ICMP报文的数据部分)。当IP报头中的协议字段值为1时,就说明这是一个ICMP报文。ICMP报头如下图所示。

这里只讲解与ping有关的ICMP消息类型,主机发送回送消息(Type = 8),被请求主机回送响应消息(Type = 0),基本格式如下: 回送消息[ECHO]

回送响应消息[ECHO REPLY]

•CheckSum为校验和,重点注意从ICMP的头部(即Type开始),到data结束(即到整个数据包结束) •Identifier为标识符,由主机设定,一般设置为进程号,回送响应消息与回送消息中identifier保持一致 •Sequence Number为序列号,由主机设定,一般设为由0递增的序列,回送响应消息与回送消息中Sequence Number保持一致 •data为数据,由主机设定,回送响应消息与回送消息中data保持一致

///include/uapi/linux/icmp.h

struct icmphdr {

__u8 type;

__u8 code;

__sum16 checksum;

union {

struct {

__be16 id;

__be16 sequence;

} echo;

__be32 gateway;

struct {

__be16 __unused;

__be16 mtu;

} frag;

__u8 reserved[4];

} un;

};

四、实现

#ifndef _LINUX_IP_H

#define _LINUX_IP_H

#include

#include

static inline struct iphdr *ip_hdr(const struct sk_buff *skb)

{

return (struct iphdr *)skb_network_header(skb);

}

static inline struct iphdr *inner_ip_hdr(const struct sk_buff *skb)

{

return (struct iphdr *)skb_inner_network_header(skb);

}

static inline struct iphdr *ipip_hdr(const struct sk_buff *skb)

{

return (struct iphdr *)skb_transport_header(skb);

}

#endif /* _LINUX_IP_H */

struct iphdr {

#if defined(__LITTLE_ENDIAN_BITFIELD)

__u8 ihl:4,

version:4;

#elif defined (__BIG_ENDIAN_BITFIELD)

__u8 version:4,

ihl:4;

#else

#error "Please fix "

#endif

__u8 tos;

__be16 tot_len;

__be16 id;

__be16 frag_off;

__u8 ttl;

__u8 protocol;

__sum16 check;

__be32 saddr;

__be32 daddr;

/*The options start here. */

};

///include/uapi/linux/in.h

enum {

IPPROTO_IP = 0, /* Dummy protocol for TCP */

#define IPPROTO_IP IPPROTO_IP

IPPROTO_ICMP = 1, /* Internet Control Message Protocol */

#define IPPROTO_ICMP IPPROTO_ICMP

IPPROTO_IGMP = 2, /* Internet Group Management Protocol */

#define IPPROTO_IGMP IPPROTO_IGMP

IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */

#define IPPROTO_IPIP IPPROTO_IPIP

IPPROTO_TCP = 6, /* Transmission Control Protocol */

#define IPPROTO_TCP IPPROTO_TCP

IPPROTO_EGP = 8, /* Exterior Gateway Protocol */

#define IPPROTO_EGP IPPROTO_EGP

IPPROTO_PUP = 12, /* PUP protocol */

#define IPPROTO_PUP IPPROTO_PUP

IPPROTO_UDP = 17, /* User Datagram Protocol */

#define IPPROTO_UDP IPPROTO_UDP

IPPROTO_IDP = 22, /* XNS IDP protocol */

#define IPPROTO_IDP IPPROTO_IDP

IPPROTO_TP = 29, /* SO Transport Protocol Class 4 */

#define IPPROTO_TP IPPROTO_TP

IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */

#define IPPROTO_DCCP IPPROTO_DCCP

IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */

#define IPPROTO_IPV6 IPPROTO_IPV6

IPPROTO_RSVP = 46, /* RSVP Protocol */

#define IPPROTO_RSVP IPPROTO_RSVP

IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */

#define IPPROTO_GRE IPPROTO_GRE

IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */

#define IPPROTO_ESP IPPROTO_ESP

IPPROTO_AH = 51, /* Authentication Header protocol */

#define IPPROTO_AH IPPROTO_AH

IPPROTO_MTP = 92, /* Multicast Transport Protocol */

#define IPPROTO_MTP IPPROTO_MTP

IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */

#define IPPROTO_BEETPH IPPROTO_BEETPH

IPPROTO_ENCAP = 98, /* Encapsulation Header */

#define IPPROTO_ENCAP IPPROTO_ENCAP

IPPROTO_PIM = 103, /* Protocol Independent Multicast */

#define IPPROTO_PIM IPPROTO_PIM

IPPROTO_COMP = 108, /* Compression Header Protocol */

#define IPPROTO_COMP IPPROTO_COMP

IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */

#define IPPROTO_SCTP IPPROTO_SCTP

IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828) */

#define IPPROTO_UDPLITE IPPROTO_UDPLITE

IPPROTO_MPLS = 137, /* MPLS in IP (RFC 4023) */

#define IPPROTO_MPLS IPPROTO_MPLS

IPPROTO_RAW = 255, /* Raw IP packets */

#define IPPROTO_RAW IPPROTO_RAW

IPPROTO_MAX

};

驱动层如何识别ICMP报文?

在.ndo_start_xmit = esp_hard_start_xmit,函数中,将邻居子系统传递下来的skb使用ip_hdr函数即可转换!

#include

#include

#include

struct iphdr *iph;

struct icmphdr *icmpph = NULL;

iph = ip_hdr(skb);

​if(iph->protocol==IPPROTO_ICMP ) {

//如果是ICMP报文

icmpph = (struct icmphdr *) ((u8 *) iph + (iph->ihl << 2));

printk("ESP type:%d, code:%d\n", icmpph->type, icmpph->code);

if(icmpph->type==8 && icmpph->code==0) {//ping 请求

printk(KERN_ERR "ping RQ:%d\n",icmpph->un.echo.sequence);

}

}

ref:

不为人知的网络编程(十三):深入操作系统,彻底搞懂127.0.0.1本机网络通信 - 知乎

linux内核协议栈 icmp 报文收发流程_老王不让用的博客-CSDN博客

ping命令全链路分析(4) - 数据包内核态处理 - 知乎

Linux内核:从skb获取udp头,udp_hdr()获取到是错误的udp头_kanguolaikanguolaik的博客-CSDN博客

网络-ICMP协议、Ping命令实现与ICMP攻击_51CTO博客_icmp ping

ICMP协议之ping实现_yo-yo的博客-CSDN博客

相关推荐

excel 表格怎么换行距
365BET体育下载

excel 表格怎么换行距

📅 07-18 👁️ 6795
汽车电瓶满电为多少v?
365BET体育下载

汽车电瓶满电为多少v?

📅 08-03 👁️ 3296
三个幽默说话方法,助你成为高情商会说话的人! 三个幽默说话方法,助你成为高情商会说话的人!无论是在工作还是在生活中,你会发现这样一种现象:那些幽默说话的人,总是比那些...