【lwip】10-ICMP协议&源码分析( 四 )

10.6.2 icmp_dest_unreach()目的不可达差错报告icmp_dest_unreach()

  • struct pbuf *p:目的不可达的pbuf包 。
  • enum icmp_dur_type t:目的不可达的原因 。
/** * Send an icmp 'destination unreachable' packet, called from ip_input() if * the transport layer protocol is unknown and from udp_input() if the local * port is not bound. * * @param p the input packet for which the 'unreachable' should be sent, *p->payload pointing to the IP header * @param t type of the 'unreachable' packet */voidicmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t){MIB2_STATS_INC(mib2.icmpoutdestunreachs);icmp_send_response(p, ICMP_DUR, t);}10.6.3 icmp_time_exceeded()超时差错报告icmp_time_exceeded()
  • struct pbuf *p:目的不可达的pbuf包 。
  • enum icmp_te_type t:超时原因 。
/** * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. * * @param p the input packet for which the 'time exceeded' should be sent, *p->payload pointing to the IP header * @param t type of the 'time exceeded' packet */voidicmp_time_exceeded(struct pbuf *p, enum icmp_te_type t){MIB2_STATS_INC(mib2.icmpouttimeexcds);icmp_send_response(p, ICMP_TE, t);}10.7 接收ICMP报文处理IP层接收到ICMP报文,会调用icmp_input函数处理 。
目前LWIP只支持ICMP回显请求的报文处理,其它ICMP报文直接丢弃 。
而对于ICMP回显请求,LWIP组装回显回答报文即可 。
icmp_input()
  • 获取IP报文首部 。判断是否有效:首部长度
  • 判断pbuf是否有效:ICMP报文长度不能少于ICMP首部长度4Byte 。
  • 提取ICMP报文类型字段:
    • 只处理回显请求报文,组装回显回答ICMP差错控制报文,用于ping 。
      • 开启了PING功能才支持多播和广播地址的回显请求 。
    • 其它ICMP报文,只记录,不处理 。
  • LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN表示是否检查IP报文对应的pbuf包是否有足够的空间扩充首部 。
    • 如果够空间,那就利用这个pbuf的空间资源作为回显回答ICMP报文 。
    • 如果不够空间,那就重新申请一个pbuf 。
  • 组装回显回答ICMP报文 。
  • 调用ip4_output_if()通过IP层发送出去 。
/** * Processes ICMP input packets, called from ip_input(). * * Currently only processes icmp echo requests and sends * out the echo response. * * @param p the icmp echo request packet, p->payload pointing to the icmp header * @param inp the netif on which this packet was received */voidicmp_input(struct pbuf *p, struct netif *inp){u8_t type;#ifdef LWIP_DEBUGu8_t code;#endif /* LWIP_DEBUG */struct icmp_echo_hdr *iecho;const struct ip_hdr *iphdr_in;u16_t hlen;const ip4_addr_t *src;ICMP_STATS_INC(icmp.recv);MIB2_STATS_INC(mib2.icmpinmsgs);iphdr_in = ip4_current_header(); /* 获取IP报文首部 */hlen = IPH_HL_BYTES(iphdr_in); /* 获取IP报文首部长度 */if (hlen < IP_HLEN) { /* IP报文首部长度异常,丢弃 */LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));goto lenerr;}if (p->len < sizeof(u16_t) * 2) { /* ICMP报文长度异常,丢弃 */LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));goto lenerr;}type = *((u8_t *)p->payload); /* 获取ICMP类型字段 */#ifdef LWIP_DEBUGcode = *(((u8_t *)p->payload) + 1); /* 获取ICMP代码字段 *//* if debug is enabled but debug statement below is somehow disabled: */LWIP_UNUSED_ARG(code);#endif /* LWIP_DEBUG */switch (type) {case ICMP_ER:/* This is OK, echo reply might have been parsed by a raw PCB(as obviously, an echo request has been sent, too). */MIB2_STATS_INC(mib2.icmpinechoreps);break;case ICMP_ECHO: /* 只处理ICMP差错控制中的回显请求 */MIB2_STATS_INC(mib2.icmpinechos);src = https://www.huyubaike.com/biancheng/ip4_current_dest_addr(); /* 获取IP报文的目的地址作为ICMP回显回答的原IP地址 */if (ip4_addr_ismulticast(ip4_current_dest_addr())) { /* 回显请求的买的地址为多播地址 */#if LWIP_MULTICAST_PING/* 对于组播,使用接收接口的地址作为源地址 */src = netif_ip4_addr(inp);#else /* LWIP_MULTICAST_PING */LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast pings\n"));goto icmperr;#endif /* LWIP_MULTICAST_PING */}if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) { /* 回显请求的买的地址为多播地址 */#if LWIP_BROADCAST_PING/* 对于广播,使用接收接口的地址作为源地址 */src = https://www.huyubaike.com/biancheng/netif_ip4_addr(inp);#else /* LWIP_BROADCAST_PING */LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n"));goto icmperr;#endif /* LWIP_BROADCAST_PING */}LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));if (p->tot_len < sizeof(struct icmp_echo_hdr)) { /* 长度校验 */LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));goto lenerr;}#if CHECKSUM_CHECK_ICMPIF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP) {if (inet_chksum_pbuf(p) != 0) { /* ICMP校验和校验失败 */LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));pbuf_free(p);ICMP_STATS_INC(icmp.chkerr);MIB2_STATS_INC(mib2.icmpinerrors);return;}}#endif#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN /* 支持检查pbuf复用 */if (pbuf_add_header(p, hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN)) { /* IP报文的pbuf长度不够,不能复用 *//* 长度不够就重新申请pbuf */struct pbuf *r;u16_t alloc_len = (u16_t)(p->tot_len + hlen); /* 新pbuf的长度:IP数据区长度+IP首部长度 */if (alloc_len < p->tot_len) { /* 溢出,丢弃 */LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed (tot_len overflow)\n"));goto icmperr;}/* 为链路头分配空间的新数据包缓冲区 */r = pbuf_alloc(PBUF_LINK, alloc_len, PBUF_RAM);if (r == NULL) {LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));goto icmperr;}if (r->len < hlen + sizeof(struct icmp_echo_hdr)) { /* 检查空间是否足够 */LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header"));pbuf_free(r);goto icmperr;}/* 复制IP头 */MEMCPY(r->payload, iphdr_in, hlen);/* 有效负载移到IP数据区 */if (pbuf_remove_header(r, hlen)) {LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0);pbuf_free(r);goto icmperr;}/* 拷贝原IP报文数据区到新的pbuf */if (pbuf_copy(r, p) != ERR_OK) {LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("icmp_input: copying to new pbuf failed"));pbuf_free(r);goto icmperr;}/* 释放就pbuf */pbuf_free(p);/* 现在,我们有了一个相同的p副本,其中有链路头的空间 */p = r;} else { /* 如果空间足够 , 就把有效负载恢复到IP数据区 。因为这里只需要检查并报文pbuf有足够空间即可 *//* restore p->payload to point to icmp header (cannot fail) */if (pbuf_remove_header(p, hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN)) {LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);goto icmperr;}}#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN *//* 到这里,所有的检查都是OK的 *//* 通过切换dest和src的ip地址,将icmp类型设置为ECHO_RESPONSE并更新校验和来生成回显回答的ICMP差错控制报文 */iecho = (struct icmp_echo_hdr *)p->payload; /* 获取ICMP报文地址 *//* 扩展报文首部 , 成为IP报文 */if (pbuf_add_header(p, hlen)) {LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));} else {err_t ret;struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;ip4_addr_copy(iphdr->src, *src); /* 设置IP报文的原IP地址 */ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());/* 设置IP报文的目的IP地址 */ICMPH_TYPE_SET(iecho, ICMP_ER); /* 设置ICMP报文为回显回答类型 */#if CHECKSUM_GEN_ICMPIF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) { /* 设置ICMP校验和 *//* adjust the checksum */if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS((u16_t)(ICMP_ECHO << 8)) + 1);} else {iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS(ICMP_ECHO << 8));}}#if LWIP_CHECKSUM_CTRL_PER_NETIFelse {iecho->chksum = 0;}#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */#else /* CHECKSUM_GEN_ICMP */iecho->chksum = 0;#endif /* CHECKSUM_GEN_ICMP *//* S设置正确的TTL并重新计算头部校验和 */IPH_TTL_SET(iphdr, ICMP_TTL);IPH_CHKSUM_SET(iphdr, 0);#if CHECKSUM_GEN_IPIF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen)); /* 设置IP首部校验和 */}#endif /* CHECKSUM_GEN_IP */ICMP_STATS_INC(icmp.xmit);/* increase number of messages attempted to send */MIB2_STATS_INC(mib2.icmpoutmsgs);/* increase number of echo replies attempted to send */MIB2_STATS_INC(mib2.icmpoutechoreps);/* 发送ICMP报文 */ret = ip4_output_if(p, src, LWIP_IP_HDRINCL,ICMP_TTL, 0, IP_PROTO_ICMP, inp);if (ret != ERR_OK) {LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));}}break;default:if (type == ICMP_DUR) {MIB2_STATS_INC(mib2.icmpindestunreachs);} else if (type == ICMP_TE) {MIB2_STATS_INC(mib2.icmpintimeexcds);} else if (type == ICMP_PP) {MIB2_STATS_INC(mib2.icmpinparmprobs);} else if (type == ICMP_SQ) {MIB2_STATS_INC(mib2.icmpinsrcquenchs);} else if (type == ICMP_RD) {MIB2_STATS_INC(mib2.icmpinredirects);} else if (type == ICMP_TS) {MIB2_STATS_INC(mib2.icmpintimestamps);} else if (type == ICMP_TSR) {MIB2_STATS_INC(mib2.icmpintimestampreps);} else if (type == ICMP_AM) {MIB2_STATS_INC(mib2.icmpinaddrmasks);} else if (type == ICMP_AMR) {MIB2_STATS_INC(mib2.icmpinaddrmaskreps);}LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",(s16_t)type, (s16_t)code));ICMP_STATS_INC(icmp.proterr);ICMP_STATS_INC(icmp.drop);}pbuf_free(p);return;lenerr:pbuf_free(p);ICMP_STATS_INC(icmp.lenerr);MIB2_STATS_INC(mib2.icmpinerrors);return;#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PINGicmperr:pbuf_free(p);ICMP_STATS_INC(icmp.err);MIB2_STATS_INC(mib2.icmpinerrors);return;#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */}

推荐阅读