LwIP之ICMP协议
ICMP(網際控制報文協議),ICMP數據包是封裝在IP數據包中的,由于IP不是為可靠傳輸服務設計的,ICMP的目的主要是用于在TCP/IP網絡中發送和控制消息。主要應用有Ping、Traceroute和MTU測試。
? ? ? ? ? ? ? ? ? ?
ICMP報文的種類有三大種類,即ICMP差錯報文、控制報文、請求/應答報文,各大類型報文又分為多種類型報文。
差錯報文:
???(1) 特點:
??????????? 1.ICMP差錯報文都是有路由器發送到源主機的。
??????????? 2.ICMP報文只提供IP數據報的差錯報告,并不采取處理措施,差錯處理由應用程序處理。
??????????? 3.傳輸過程中可能丟失、損壞,甚至被拋棄。
??????????? 4.ICMP差錯報文是伴隨著拋棄出錯的IP數據報而產生的。
??????????? 5.為了防止廣播風暴,以下情況不會產生ICMP差錯報文。
??????????????????? 1)ICMP差錯報文??? 2)目的地址是廣播或多播??? 3)鏈路層廣播的數據報??? 4)不是IP分片的第一片??? 5)源地址是零地址、回送地址、廣播地址或多播地址
??? (2)信息不可達報文:
??????????? 1.目的機硬件故障或關機
????????????2.目標地址不存在
??????????? 3.網關不知道去往目的機的路徑
??? (3)超時報文:
??????????? 1.為了避免無限制的在網中循環,IP協議采用???
?????????????????? ?1)在數據報頭設置TTL域??? 2)對分片數據報采用定時器技術
??????????? 2.當報文超時出現時,路由器或目的機立即丟棄該數據報,并向信源機發送超時報文
?
控制報文:
???? (1)擁塞控制與源站控制報文:
???????????? 1.當路由器接收比處理快或者傳入比傳出快時就會產生擁塞
???????????? 2.路由器通過發送源站抑制報文來抑制主機發送速率
???????????? 3.源主機一段時間內沒有收到抑制報文,便認為抑制解除,逐漸恢復原來的數據流量
???? (2)路由控制與重定向報文:
???????????? 1.當源主機要向目標主機發送IP數據報時,則把IP數據報發送給默認路器1,再由路由器1經過選路發送給路由器2,路由器2發送給目標主機
???????????? 2.如果路由器1和路由器2在同一網絡,路由器1發現源主機可直接發送IP數據報給路由器2,就會向源主機發送重定向報文,以后源主機將直接發送IP報文給路由器2
?
請求/應答報文:
??? (1)回送請求與應答報文:
??????????? 1.測試目標主機和路由器是否可以到達
??? (2)時戳請求與應答報文:
??????????? 1.同步互聯網中各個主機的時鐘
??? (3)地址掩碼請求和應答報文
??????????? 1.用于無盤系統在引導過程中獲取子網掩碼。啟動時廣播地址掩碼請求,路由器收到請求后回送一個包含32位地址掩碼應答報文
?
報文格式,ICMP報文由8字節首部和可變長數據部分組成。不同類型的ICMP報文,首部格式有一定差異,但是首部前4字節的字段對所有類型通用。
類型:標識了ICMP具體類型
代碼:進一步指出產生該類型ICMP的原因
校驗和:整個ICMP的校驗和
?
LWIP實現了ICMP的部分功能:目的不可達報文、超時報文、回送查詢報文。
報文格式分別如下所示:
ICMP頭部基本類似,信息不可達和超時報文的頭部,相當于回送查詢報文頭部中標識符和序號設置為0的情況。因此,LwIP只以回送查詢報文為基礎提供了一種ICMP頭部結構體
/* ICMP類型 */ #define ICMP_ER 0 /* 回送查詢應答 */ #define ICMP_DUR 3 /* 目的不可達 */ #define ICMP_SQ 4 /* 源端被關閉 */ #define ICMP_RD 5 /* 重定向 */ #define ICMP_ECHO 8 /* 回送查詢請求 */ #define ICMP_TE 11 /* 超時 */ #define ICMP_PP 12 /* 參數問題 */ #define ICMP_TS 13 /* 時間戳請求 */ #define ICMP_TSR 14 /* 時間戳應答 */ #define ICMP_IRQ 15 /* 信息請求 */ #define ICMP_IR 16 /* 信息應答 *//* ICMP目的不可達代碼 */ enum icmp_dur_type {ICMP_DUR_NET = 0, /* 網絡不可達 */ICMP_DUR_HOST = 1, /* 主機不可達 */ICMP_DUR_PROTO = 2, /* 協議不可達 */ICMP_DUR_PORT = 3, /* 端口不可達 */ICMP_DUR_FRAG = 4, /* 需要分片但不分片位被置位 */ICMP_DUR_SR = 5 /* 源路由失敗 */ };/* ICMP超時代碼 */ enum icmp_te_type {ICMP_TE_TTL = 0, /* 生存時間計時器超時 */ICMP_TE_FRAG = 1 /* 分片重裝超時 */ };/* ICMP回送查詢報文頭部 */ struct icmp_echo_hdr {PACK_STRUCT_FIELD(u8_t type); //類型PACK_STRUCT_FIELD(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?
在前面分析ip_input函數時,當報文類型為ICMP報文時,調用icmp_input函數處理ICMP報文。當遇到不支持的協議時調用icmp_dest_unreach函數,返回目的不可達報文。
/* IP數據包輸入處理 */ err_t ip_input(struct pbuf *p, struct netif *inp) {struct ip_hdr *iphdr;....../* IP頭部指針 */iphdr = p->payload;....../* 判斷協議類型 */switch (IPH_PROTO(iphdr)) {/* UDP協議 */case IP_PROTO_UDP:udp_input(p, inp);break;/* TCP協議 */case IP_PROTO_TCP:tcp_input(p, inp);break;/* ICMP協議 */case IP_PROTO_ICMP:icmp_input(p, inp);break;/* 不支持的協議 */default:/* 目的IP不是廣播地址且不是組播地址 */if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && !ip_addr_ismulticast(&(iphdr->dest))) {p->payload = iphdr;/* 發送ICMP目的不可達報文(協議不可達) */icmp_dest_unreach(p, ICMP_DUR_PROTO);}/* 釋放數據包 */pbuf_free(p);}}......return ERR_OK; }下面先來看目的不可達報文的實現方式
/* 發送ICMP目的不可達報文 */ void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) {icmp_send_response(p, ICMP_DUR, t); }/* 發送ICMP響應報文 */ static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code) {struct pbuf *q;struct ip_hdr *iphdr;struct icmp_echo_hdr *icmphdr;/* 為ICMP報文申請內存空間 */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"));return;}LWIP_ASSERT("check that first pbuf can hold icmp message", (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));/* IP頭部指針 */iphdr = p->payload;LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));LWIP_DEBUGF(ICMP_DEBUG, (" to "));ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));LWIP_DEBUGF(ICMP_DEBUG, ("\n"));/* ICMP頭部指針 */icmphdr = q->payload;/* ICMP頭部類型 */icmphdr->type = type;/* ICMP頭部代碼 */icmphdr->code = code;/* ICMP頭部剩余字節(標識符) */icmphdr->id = 0;/* ICMP頭部剩余字節(序號) */icmphdr->seqno = 0;/* 將源數據報中的8字節 */SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);/* ICMP頭部檢驗和 */icmphdr->chksum = 0;icmphdr->chksum = inet_chksum(icmphdr, q->len);ICMP_STATS_INC(icmp.xmit);snmp_inc_icmpoutmsgs();snmp_inc_icmpouttimeexcds();/* 調用IP輸出函數,發送ICMP數據包 */ip_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP);/* 釋放數據包 */pbuf_free(q); }接下來再看?icmp_input函數
/* ICMP報文接收處理 */ void icmp_input(struct pbuf *p, struct netif *inp) {u8_t type;struct icmp_echo_hdr *iecho;struct ip_hdr *iphdr;struct ip_addr tmpaddr;s16_t hlen;/* IP頭部指針 */iphdr = p->payload;/* IP頭部長度 */hlen = IPH_HL(iphdr) * 4;/* 向后調整剝離IP頭部,保證ICMP頭部不小于4字節 */if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t) * 2)) {goto lenerr;}/* ICMP類型 */type = *((u8_t *)p->payload);/* 判斷ICMP類型 */switch (type) {/* 回送查詢報文 */case ICMP_ECHO:{int accepted = 1;/* ICMP目的IP為組播地址 */if (ip_addr_ismulticast(&iphdr->dest)) {accepted = 0; //不做處理}/* ICMP目的IP為廣播地址 */if (ip_addr_isbroadcast(&iphdr->dest, inp)) {accepted = 0; //不做處理}/* 不做處理,直接釋放ICMP數據包 */if (!accepted) {pbuf_free(p);return;}}/* 檢查ICMP數據包長度合不合理 */if (p->tot_len < sizeof(struct icmp_echo_hdr)) {goto lenerr;}/* 檢查檢驗和是否正確 */if (inet_chksum_pbuf(p) != 0) {pbuf_free(p);return;}/* ICMP頭部指針 */iecho = p->payload;/* 將IP頭部中源地址和目的地址交換 */tmpaddr.addr = iphdr->src.addr;iphdr->src.addr = iphdr->dest.addr;iphdr->dest.addr = tmpaddr.addr;/* 設置ICMP報文類型為回送查詢應答 */ICMPH_TYPE_SET(iecho, ICMP_ER);/* 重新設置ICMP頭部校驗和 */if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) {iecho->chksum += htons(ICMP_ECHO << 8) + 1;} else {iecho->chksum += htons(ICMP_ECHO << 8);}/* 設置IP頭部TTL字段 */IPH_TTL_SET(iphdr, ICMP_TTL);/* 計算IP頭部校驗和字段 */IPH_CHKSUM_SET(iphdr, 0);IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));/* 向前調整出IP頭部空間 */if(pbuf_header(p, hlen)) {} else {/* 通過IP發送函數,發送回送查詢應答報文 */ip_output_if(p, &(iphdr->src), IP_HDRINCL, ICMP_TTL, 0, IP_PROTO_ICMP, inp);}break;/* 不處理其它類型的ICMP報文 */default:break;}pbuf_free(p);return;lenerr:pbuf_free(p);return; }?
在前面分析IP數據報重組的時候,?一旦重組超時協議棧就會刪除重組并向源主機返回ICMP分片重組超時報文
/* 重組IP數據報定時器回調函數(周期1秒) */ void ip_reass_tmr(void) {struct ip_reassdata *r, *prev = NULL;/* 遍歷所有重組IP數據報 */r = reassdatagrams;while (r != NULL) {/* 沒有超時,將生存周期減一 */if (r->timer > 0) {r->timer--;prev = r;r = r->next;} /* 超時 */else {struct ip_reassdata *tmp;tmp = r;r = r->next;/* 釋放重組IP數據報的所有分片,并將重組IP數據報從鏈表中移除 */ip_reass_free_complete_datagram(tmp, prev);}} }/* 釋放重組IP數據報的所有分片,并將重組IP數據報從鏈表中移除 */ static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) {......struct ip_reass_helper *iprh;/* 向源主機發送ICMP分片重組超時報文 */iprh = (struct ip_reass_helper *)ipr->p->payload;if (iprh->start == 0) {p = ipr->p;ipr->p = iprh->next_pbuf;SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);icmp_time_exceeded(p, ICMP_TE_FRAG);pbufs_freed += pbuf_clen(p);pbuf_free(p);}...... }下面就來看一下ICMP超時報文發送函數
/* 發送ICMP超時報文 */ void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) {icmp_send_response(p, ICMP_TE, t); }總結
以上是生活随笔為你收集整理的LwIP之ICMP协议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32之ADC单通道单次例程
- 下一篇: Altium Designer之原理图