ICMP功能简介
当IP数据报由于网络状况、链路不通等问题无法到达目标主机时,ICMP会返回一个差错报文。
ICMP查询报文(ping)
标识符用于标识在同一台主机上同时运行了多个ping程序,序列号从0开始,每发送一次新的回显请求就进行加1。

源码分析
PACK_STRUCT_BEGIN
struct icmp_echo_hdr {
PACK_STRUCT_FLD_8(u8_t type);
PACK_STRUCT_FLD_8(u8_t code);
PACK_STRUCT_FIELD(u16_t chksum);
PACK_STRUCT_FIELD(u16_t id);
PACK_STRUCT_FIELD(u16_t seqno);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
对应着ICMP报文格式。
echo:回声,表示验证链路可达性。
hdr:header,头部。
seqno中no表示number,No.1
ICMP目标不可达代码枚举为:
enum icmp_dur_type {
/** net unreachable */
ICMP_DUR_NET = 0,
/** host unreachable */
ICMP_DUR_HOST = 1,
/** protocol unreachable */
ICMP_DUR_PROTO = 2,
/** port unreachable */
ICMP_DUR_PORT = 3,
/** fragmentation needed and DF set */
ICMP_DUR_FRAG = 4,
/** source route failed */
ICMP_DUR_SR = 5
};
dur :destination unreachable ,目标不可达
ICMP超时代码枚举为:
enum icmp_te_type {
/** time to live exceeded in transit */
ICMP_TE_TTL = 0,
/** fragment reassembly time exceeded */
ICMP_TE_FRAG = 1
};
te:time exceeded,超时
当ip数据报无法到达传输层或者应用层时,调用icmp_dest_unreach()函数返回一个ICMP协议报文不可达。
void
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
{
MIB2_STATS_INC(mib2.icmpoutdestunreachs);
icmp_send_response(p, ICMP_DUR, t);
}
dest_unreach:目标不可达
根据
#define MIB2_STATS_INC(x) STATS_INC(x)
#define STATS_INC(x) ++lwip_stats.x
两个宏可知,
MIB2_STATS_INC(mib2.icmpoutdestunreachs);相当于
++lwip_stats.mib2.icmpoutdestunreachs;
lwip_stats是全局统计数据容器,在stats.c文件中定义如下:
struct stats_ lwip_stats;
结构体struct stats_在stats.h文件中定义:
/** lwIP stats container */
struct stats_ {
#if LINK_STATS
/** Link level */
struct stats_proto link;
#endif
。。。
#if MIB2_STATS
/** SNMP MIB2 */
struct stats_mib2 mib2;
#endif
};
mib2 : management information base,2表示版本号是2,base有库和集合的意思,所以含义是“管理数据集合”
stats.h中有如下两个宏
#define STATS_INC(x) ++lwip_stats.x
#define STATS_DEC(x) --lwip_stats.x
inc:increment,递增
dec:decrement,递减
如果数据报超时,lwip会调用icmp_time_exceeded()函数发送一个ICMP超时报文
void
icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
{
MIB2_STATS_INC(mib2.icmpouttimeexcds);
icmp_send_response(p, ICMP_TE, t);
}
time_exceeded:超时
icmp_send_response分析
源码为:
static void
icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
{
struct pbuf *q;
struct ip_hdr *iphdr;
/* we can use the echo header here */
struct icmp_echo_hdr *icmphdr;
ip4_addr_t iphdr_src;
struct netif *netif;
/* increase number of messages attempted to send */
MIB2_STATS_INC(mib2.icmpoutmsgs);
/* ICMP header + IP header + 8 bytes of data */
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
PBUF_RAM);
if (q == NULL) {
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
MIB2_STATS_INC(mib2.icmpouterrors);
return;
}
LWIP_ASSERT("check that first pbuf can hold icmp message",
(q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
iphdr = (struct ip_hdr *)p->payload;
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
LWIP_DEBUGF(ICMP_DEBUG, (" to "));
ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
icmphdr = (struct icmp_echo_hdr *)q->payload;
icmphdr->type = type;
icmphdr->code = code;
icmphdr->id = 0;
icmphdr->seqno = 0;
/* copy fields from original packet */
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
ip4_addr_copy(iphdr_src, iphdr->src);
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
{
ip4_addr_t iphdr_dst;
ip4_addr_copy(iphdr_dst, iphdr->dest);
netif = ip4_route_src(&iphdr_dst, &iphdr_src);
}
#else
netif = ip4_route(&iphdr_src);
#endif
if (netif != NULL) {
/* calculate checksum */
icmphdr->chksum = 0;
#if CHECKSUM_GEN_ICMP
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {
icmphdr->chksum = inet_chksum(icmphdr, q->len);
}
#endif
ICMP_STATS_INC(icmp.xmit);
ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
}
pbuf_free(q);
}
输入的三个变量中,
struct pbuf *p用于查找输出接口对象netif,和目标ip地址iphdr_src
u8_t type, u8_t code用于填充输出内容。
接下来是函数体,可以看到声明的几个变量:
struct pbuf *q;
struct ip_hdr *iphdr;
/* we can use the echo header here */
struct icmp_echo_hdr *icmphdr;
ip4_addr_t iphdr_src;
struct netif *netif;
与他们第一次被使用的顺序是相同的。
struct netif *
ip4_route(const ip4_addr_t *dest)
route:路由
该函数用于根据ip地址查找对应接口的netif,所有的netif都挂在netif_list链表中。
err_t
ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif *netif)
用IPv4协议发送数据。其上层还有一个函数:
err_t
ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
u8_t ttl, u8_t tos, u8_t proto)
接下来是icmp_input(struct pbuf *p, struct netif *inp)函数,用于处理输入的ICMP数据,其中只对回显请求报文做了处理,所以ping运行lwip的设备可以ping通。
其中有一段:
const ip4_addr_t *src;
src = ip4_current_dest_addr();
其中:
#define ip4_current_dest_addr() (&ip_data.current_iphdr_dest)
struct ip_globals ip_data;
struct ip_globals
{
/** The interface that accepted the packet for the current callback invocation. */
struct netif *current_netif;
/** The interface that received the packet for the current callback invocation. */
struct netif *current_input_netif;
#if LWIP_IPV4
/** Header of the input packet currently being processed. */
const struct ip_hdr *current_ip4_header;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
/** Header of the input IPv6 packet currently being processed. */
struct ip6_hdr *current_ip6_header;
#endif /* LWIP_IPV6 */
/** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */
u16_t current_ip_header_tot_len;
/** Source IP address of current_header */
ip_addr_t current_iphdr_src;
/** Destination IP address of current_header */
ip_addr_t current_iphdr_dest;
};
ip_globals:表示ip层相关的一些全局变量。声明的变量为ip_data。
其中“当前的”用current来表示。
1420

被折叠的 条评论
为什么被折叠?



