lwip之数据收发流程_1
生活随笔
收集整理的這篇文章主要介紹了
lwip之数据收发流程_1
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
lwip從邏輯上看也是分為4層:鏈路層、網(wǎng)絡(luò)層(IP、ARP、(ICMP、IGMP這兩個協(xié)議是網(wǎng)絡(luò)層的補充協(xié)議,并不嚴(yán)格屬于網(wǎng)絡(luò)層))、傳輸層(TCP、UDP)、應(yīng)用層,基本等同TCP/IP,只是各層之間可以進行交叉存取,沒有嚴(yán)格劃分。
協(xié)議匯總:1. ARP協(xié)議:根據(jù)IP地址獲取物理地址MAC的一個TCP/IP協(xié)議
一個典型的lwip系統(tǒng)包含3個進程:首先是上層應(yīng)用程序進程,然后是lwip協(xié)議棧進程,最后是底層硬件數(shù)據(jù)包接收進程動態(tài)內(nèi)存管理:采用ucos-ii內(nèi)存管理系統(tǒng),即申請一塊內(nèi)存,分割成整數(shù)個大小相同的內(nèi)存塊
一.?? ?鏈路層當(dāng)主機A要與主機B通信時,ARP協(xié)議可以將主機B的IP地址解析成主機B的MAC地址,工作流程如下:?? ?第一步:主機A先檢查自己的ARP緩沖,看是否存在主機B匹配的MAC地址,如果沒有,就會向外廣播一個ARP請求包第二步:其他主機收到后,發(fā)現(xiàn)請求的IP地址與自己的IP地址不匹配,則丟棄ARP請求第三步:主機B確定ARP請求中的IP地址與自己的IP地址匹配,則將主機A的IP地址和MAC地址映射到本地ARP緩存中第四步:主機B將包含其MAC地址的ARP回復(fù)發(fā)回給主機A第五步:主機A收到從主機B發(fā)來的ARP回復(fù)時,會將主機B的IP地址和MAC地址映射更新到本地ARP緩存中。主機B的MAC地址一旦確定,主機A就可以向主機B發(fā)送IP通信了連接鏈路層和網(wǎng)絡(luò)層的紐帶:以太網(wǎng)數(shù)據(jù)包接收進程tcpip_threadstatic void tcpip_thread(void *arg){struct tcpip_msg *msg;?? ??? ?// 消息來自于網(wǎng)卡中斷while(1){// 該任務(wù)阻塞在這里接收要處理的消息,當(dāng)有數(shù)據(jù)包到來時,網(wǎng)卡芯片中斷函數(shù)接收數(shù)據(jù),并post消息,中斷退出后,該任務(wù)獲取消息sys_timeouts_mbox_fetch(&mbox, (void **)&msg);?? ?// 判斷本條消息的類型,只關(guān)注數(shù)據(jù)包消息TCPIP_MSG_INPKTswitch (msg->type){case TCPIP_MSG_INPKT:?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?// 數(shù)據(jù)包消息if(msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET))ethernet_input(msg->msg.inp.p,msg->msg.inp.netif);?? ?// 如果支持ARP,先進行ARP處理,再判斷是否遞交IP層,對于IP數(shù)據(jù)包,這里2個選擇最終都要調(diào)用ip_input進入IP層elseip_input(msg->msg.inp.p, msg->msg.inp.netif);?? ??? ??? ?// 否則直接遞交IP層,ip_input為IP層主要函數(shù),解析見下文,這里直接調(diào)用ip_input存在問題,有誤,需要先以太網(wǎng)數(shù)據(jù)包指針,使掠過包頭,指向IP協(xié)議包頭memp_free(MEMP_TCPIP_MSG_INPKT, msg);?? ??? ??? ??? ??? ??? ?// 釋放消息內(nèi)存break;case TCPIP_MSG_TIMEOUT:?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?// 超時消息sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);memp_free(MEMP_TCPIP_MSG_API, msg);break;default:break;}}}err_t ethernet_input(struct pbuf *p,struct netif *netif){struct eth_hdr *ethhdr;?? ??? ??? ??? ??? ?// 以太網(wǎng)數(shù)據(jù)包頭結(jié)構(gòu)體u16_t type;s16_t ip_hdr_offset = SIZEOF_ETH_HDR;?? ?// 包頭固定值14字節(jié)ethhdr = (eth_hdr *)p->payload;type = htons(ethhdr->type);switch(type){case ETHTYPE_IP:?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?// IP數(shù)據(jù)包etharp_ip_input(netif,p);?? ??? ??? ??? ??? ??? ??? ??? ??? ?// 使用收到的IP包更新ARP緩存表,詳見《lwip之ARP協(xié)議》pbuf_header(p, -ip_hdr_offset);?? ??? ??? ??? ??? ??? ??? ??? ?// 調(diào)整以太網(wǎng)數(shù)據(jù)包指針,使掠過包頭,指向IP協(xié)議包頭ip_input(p,netif);?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?// 提交IP層,ip_input為IP層主要函數(shù),解析見下文case ETHTYPE_ARP:?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?// ARP數(shù)據(jù)包etharp_arp_input(netif,(struct eth_addr *)netif->hwaddr,p);?? ?// ARP數(shù)據(jù)包處理,第二個形參是本機MAC,詳見《lwip之ARP協(xié)議》break;default:break;}}注:消息結(jié)構(gòu)體struct tcpip_msg {enum tcpip_msg_type type;?? ??? ??? ??? ?// 本條消息的類型:TCPIP_MSG_INPKT - 數(shù)據(jù)包消息,TCPIP_MSG_TIMEOUT - 超時消息sys_sem_t *sem;?? ??? ??? ??? ??? ??? ??? ?// 事件控制塊ECBunion{struct api_msg *apimsg;struct netifapi_msg *netifapimsg;struct {struct pbuf *p;struct netif *netif;} inp;?? ??? ??? ??? ??? ??? ??? ??? ?// inp結(jié)構(gòu)體最重要,內(nèi)含數(shù)據(jù)包內(nèi)容結(jié)構(gòu)、網(wǎng)絡(luò)接口結(jié)構(gòu)struct {tcpip_callback_fn function;void *ctx;} cb;struct {u32_t msecs;sys_timeout_handler h;void *arg;} tmo;}msg;}----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------?? ?
二. 網(wǎng)絡(luò)層lwip使用一個ip_hdr的結(jié)構(gòu)體來描述IP協(xié)議包頭:struct ip_hdr{u16_t _v_hl_tos;?? ?// 包含4位版本號(IPv4 - 4,IPv6 - 6)、4位IP包頭長(通常為5*4,即本結(jié)構(gòu)體大小)、8位服務(wù)類型u16_t _len;?? ??? ??? ?// 整個IP數(shù)據(jù)包長度u16_t _id;?? ??? ??? ?// 16位標(biāo)識用于標(biāo)識IP層發(fā)出的每一份IP報文,自增u16_t _offset;?? ??? ?// 包含3位標(biāo)志和13位片偏移,IP數(shù)據(jù)包分片時使用u8_t _ttl;?? ??? ??? ?// TTL描述該IP數(shù)據(jù)包最多能被轉(zhuǎn)發(fā)的次數(shù),自減u8_t _proto;?? ??? ?// 協(xié)議字段用于描述該IP數(shù)據(jù)包的上層協(xié)議,0x01 - ICMP,0x02 - IGMP,0x06 - TCP,0x17 - UDPu16_t _chksum;?? ??? ?// 16位的IP首部校驗和ip_addr_p_t src;?? ?// 源IPip_addr_p_t dest;?? ?// 目的IP}ip_input為IP層主干函數(shù),完成了IP層數(shù)據(jù)包處理(核心工作就是IP地址匹配;得到完整數(shù)據(jù)包),然后將合適的數(shù)據(jù)包提交給上層,這里的p->payload已經(jīng)越過了14字節(jié)包頭,指向了IP頭err_t ip_input(struct pbuf *p,struct netif *inp){struct ?? ?ip_hdr *iphdr;?? ?// 指向IP包頭的指針struct ?? ?netif *netif;?? ?// 指向netif硬件網(wǎng)絡(luò)接口設(shè)備描述符的指針u16_t?? ?iphdr_hlen;?? ??? ?// IP包頭的長度,通常是固定20字節(jié)u16_t?? ?iphdr_len;?? ??? ?// 整個IP包長,包含IP包頭、上層協(xié)議頭、數(shù)據(jù)// 取出 IP數(shù)據(jù)包頭iphdr = (struct ip_hdr *)p->payload;// 檢查IP包頭中的版本號字段,IPv4 - 4,IPv6 - 6if(IPH_V(iphdr) != 4){pbuf_free(p);return ERR_OK;?? ?}// 提取IP包頭中的頭長度字段,通常固定值20字節(jié)iphdr_hlen = IPH_HL(iphdr);iphdr_hlen *= 4;// 提取IP包頭中的IP包總長度字段,確保小于遞交上來的pbuf包中的總長度iphdr_len = ntohs(IPH_LEN(iphdr));if(iphdr_len > p->len || iphdr_len > p->tot_len){pbuf_free(p);return ERR_OK;?? ?}// 校驗IP數(shù)據(jù)包頭if (inet_chksum(iphdr, iphdr_hlen) != 0){pbuf_free(p);return ERR_OK;?? ?}// 對IP數(shù)據(jù)報進行截斷,得到完整無冗余IP數(shù)據(jù)包pbuf_realloc(p, iphdr_len);// 遍歷netif_list鏈表(系統(tǒng)存在2個網(wǎng)卡設(shè)備,意味著有2個netif分別用于描述它們,也意味著本機有2個IP地址,所以此時就需要遍歷),檢測IP數(shù)據(jù)包中的目的IP是否與本機相符,不符則丟棄或轉(zhuǎn)發(fā)ip_addr_copy(current_iphdr_dest, iphdr->dest);ip_addr_copy(current_iphdr_src, iphdr->src);int first = 1;netif = inp;do{// 通過netif->flag標(biāo)志位判斷該網(wǎng)卡設(shè)備是否配置且使能,同時判斷本機IP是否有效if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))){// 如果目的IP地址與本機IP地址匹配或者目的IP地址是廣播類型,意味著成功匹配,退出遍歷if(ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) || ip_addr_isbroadcast(¤t_iphdr_dest, netif))?? ?{break;?? ?}}if (first){first = 0;netif = netif_list;}else{netif = netif->next;}if (netif == inp){netif = netif->next;}}while(netif != NULL);//? 如果該數(shù)據(jù)包中的源IP地址是廣播IP,則直接丟棄if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) || (ip_addr_ismulticast(¤t_iphdr_src))){pbuf_free(p);return ERR_OK;}// 遍歷完成以后,如果依舊沒有找到匹配的netif結(jié)構(gòu)體,說明該數(shù)據(jù)包不是給本機的,轉(zhuǎn)發(fā)或丟棄(這里直接丟棄)if (netif == NULL){pbuf_free(p);return ERR_OK;}// 判斷該IP包是否是分片數(shù)據(jù)包// 如果是分片數(shù)據(jù)包,則需要將該分片包暫存,等接收完所有分片包后,統(tǒng)一將整個數(shù)據(jù)包提交給上層if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0){// 在這里重組接收到的分片包,如果還沒接收完整,p=NULLp = ip_reass(p);// 如果分片包還沒接收完整,本函數(shù)結(jié)束if (p == NULL){return ERR_OK;}// 如果分片包接收完整,這時的p已經(jīng)是一個完整的數(shù)據(jù)包結(jié)構(gòu)體了// 再從p中獲取完整的IP包iphdr = (struct ip_hdr *)p->payload;?? ??? ??? ?}// 能到達(dá)這一步的數(shù)據(jù)包必然是未分片的或經(jīng)過分片重組完整后的數(shù)據(jù)包current_netif = inp;current_header = iphdr;if (raw_input(p, inp) == 0){// 根據(jù)IP數(shù)據(jù)包頭中的協(xié)議字段判斷該數(shù)據(jù)包應(yīng)該被遞交給上層哪個協(xié)議switch (IPH_PROTO(iphdr)){case IP_PROTO_UDP:?? ?// UDP協(xié)議udp_input(p, inp);?? ?// 從這里進入傳輸層,解析見下文break;?? ?case IP_PROTO_TCP:?? ?// TCP協(xié)議tcp_input(p, inp);?? ?// 從這里進入傳輸層,解析見下文break;case IP_PROTO_ICMP:?? ?// ICMP協(xié)議icmp_input(p, inp);break;case IP_PROTO_IGMP:?? ?// IGMP協(xié)議igmp_input(p, inp, ¤t_iphdr_dest);break;default:?? ??? ??? ?// 如果都不是// 如果不是廣播數(shù)據(jù)包,返回一個協(xié)議不可達(dá)ICMP數(shù)據(jù)包給源主機if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) && !ip_addr_ismulticast(¤t_iphdr_dest)){p->payload = iphdr;icmp_dest_unreach(p, ICMP_DUR_PROTO);}?? ?pbuf_free(p);?? ??? ?}?? ?}current_netif = NULL;current_header = NULL;ip_addr_set_any(¤t_iphdr_src);ip_addr_set_any(¤t_iphdr_dest);}IP層的補充協(xié)議:ICMP、IGMP這時候主機A學(xué)到了主機B的MAC地址,就把這個MAC封裝到ICMP協(xié)議中向主機B發(fā)送,報文格式如下:包頭14字節(jié)?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?:因為ICMP協(xié)議包屬于網(wǎng)絡(luò)層協(xié)議,所以幀類型是0x0800+?? ?ICMP協(xié)議頭(主要是二級協(xié)議類型、源IP、目的IP)?? ?:二級協(xié)議類型ICMP對應(yīng)值0x01+?? ?ICMP協(xié)議主體(主要是一個類別)?? ??? ??? ??? ??? ?:類別取值0x00 - 這是一條回應(yīng)信息?? ?0x03 - 目的不可達(dá)?? ?0x08 - 請求回應(yīng)信息---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
?
總結(jié)
以上是生活随笔為你收集整理的lwip之数据收发流程_1的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在Linux下写无线网卡的驱动
- 下一篇: lwip之数据收发流程_2