.sk_buff的結(jié)構(gòu)和操作:
struct sk_buff { unsigned char pad[2]; unsigned char *data; /* Data head pointer */這個指針總是指向當前層協(xié)議頭在buf中的位置或者當前層協(xié)議數(shù)據(jù)部分在buf中的位置。 buf[ETH_FRAME_LEN] 就是一幀實體,也是一幀協(xié)議棧的棧的實體。*data 是棧的指針,len則相當棧的底部,但是它是變化的,意義是*data到*data+len部分是當前協(xié)議層的內(nèi)容(接收),或者這部分是已經(jīng)填好的上層協(xié)議內(nèi)容(發(fā)送)。 對應(yīng)的操作有skb_push,skb_pull。 skb_push 用于從上層協(xié)議向下封裝數(shù)據(jù)包,相當于壓棧。char *skb_pull(struct sk_buff *skb, unsigned int ln) 就是要向棧中寫入len字節(jié)前,先把棧指針*data-=ln, 而棧長len+=ln,返回當前*data指針,數(shù)據(jù)或者協(xié)議頭(長度一定是ln)就可以往*data處填充了。很明顯這是個向下生長的滿棧 (FD) 。 skb_pull 用于從幀開始向上逐次解析協(xié)議。相當于彈棧的過程, char *skb_pull(struct sk_buff *skb, unsigned int ln)就是從棧中彈出ln個字節(jié),*data+=ln, 棧長len-=ln,返回當前*data。彈出的ln字節(jié)就是下層協(xié)議已經(jīng)處理過的協(xié)議頭,返回值指向本層協(xié)議頭,用它就可以開始解析本層協(xié)議了。 2、接收包的流程 1) 申請一個緩沖區(qū)sk_buf *skb, 以skb為參數(shù)調(diào)用以太網(wǎng)層的接收數(shù)據(jù)幀函數(shù)eth_rcv(skb)。eth_rcv(skb)調(diào)用board_eth_rcv(skb->data, &skb->len); board_eth_rcv調(diào)用CS8900 查詢函數(shù)CS8900DBG_IsReceivedPacket()檢查當前是否收到數(shù)據(jù)幀,如果收到調(diào)用RcvPkt((BYTE *)data, 1532);接收幀。 這樣一幀數(shù)據(jù)就緩存到skb->buff中了。此時棧指針*data=buff,len=幀長。 2)處理以太網(wǎng)幀頭,彈棧skb_pull(skb, ETH_HLEN)。以太網(wǎng)的幀頭protocol解析上層協(xié)議是IP還是ARP,分別調(diào)用ip_rcv_packet(skb);(見4)或者arp_rcv_packet(skb);(見3) 3)如果是ARP報文,arp_rcv_packet(skb)處理ARP協(xié)議。判斷ARP頭的目標IP是否為本地IP,不是丟棄。是則判斷ARP操作碼是否為ARP請求,是發(fā)送ARP reply。緩存對方MAC IP 至ARP cache.當前幀的處理結(jié)束。 4)如果IP報文 ,ip_rcv_packet(skb); 處理IP層協(xié)議頭,檢查目的IP是否是本地IP,不是直接丟棄,是則根據(jù)IP頭的上層協(xié)議是UDP還是ICMP,分別調(diào)用udp_rcv_packet(skb);或者icmp_rcv_packet(skb); 5)如果是ICMP數(shù)據(jù)包,icmp_rcv_packet(skb); 處理ICMP報文,如果ICMP頭的類型是8,即請求回顯,則發(fā)送一個回顯ICMP報文。當前幀的處理結(jié)束。 6)如果是UDP數(shù)據(jù)包,udp_rcv_packet(skb)處理UDP協(xié)議頭,檢查目的端口是否為TFTP端口。由于程序只有TFTP作為UDP的上層協(xié)議。 如果是則彈棧后調(diào)用tftp_rcv_packet(skb); 7)如果是ftfp包,tftp_rcv_packet(skb);處理TFTP協(xié)議,限于功能,只處理WRQ和DATA兩種請求,如果是WRQ請求,保存源IP和端口,發(fā)送一個ftfp應(yīng)答block=0,開始block++計數(shù)并進入下載狀態(tài)。 如果是DATA包,判斷源IP和端口與上面保存的是否一致,當前tftp包的block號與block計數(shù)是否相等。相等則拷貝數(shù)據(jù), 發(fā)送應(yīng)答,block++,判斷數(shù)據(jù)長是否小于512,是則表明block接收結(jié)束。當前tftp包的block號 3、發(fā)送包過程 1)sk_buff結(jié)構(gòu)的另外兩個操作,skb_put操作和skb_reserve(預(yù)留)操作:申請一個緩沖示進行任何操作的時候,*data=buff, len=0. 每層協(xié)議都有一個相對于下層協(xié)議頭的偏移,這個偏移是一定的。skb_reserve(skb,len)操作是把skb->data+=len。每層協(xié)議都實現(xiàn)對應(yīng)的reserve操作,它調(diào)用下層reserve,再把自己協(xié)議頭的長度len添加到預(yù)留空間skb_reserve(skb,len)。 skb_put(skb,len)操作則是實現(xiàn)內(nèi)容填充之后,skb->len+=len. 這兩個操作和skb_push在發(fā)送數(shù)據(jù)包中起重要作用。如發(fā)送TFTP包,申請緩沖區(qū),調(diào)用udp_skb_reserve(skb);把UDP頭到以太網(wǎng)幀頭的所有協(xié)議頭的位置預(yù)留出來。再向*data處添加TFTP頭和數(shù)據(jù)。 2)TFTP包發(fā)送過程 發(fā)送TFTP應(yīng)答,tftp_send_ack :申請緩沖,預(yù)留所有下層協(xié)議(UDP,IP,ETH)協(xié)議頭空間,填寫TFTP頭和數(shù)據(jù)。調(diào)用udp_send(skb, client_ip, TFTP, client_port); 參數(shù)告訴下層相對的協(xié)議頭怎么填。如TFTP,client_port 是給UDP協(xié)議層的,指定了本地端口和目標端口;client_ip則是給IP層的,給定目標IP。 udp_send(skb, client_ip, TFTP, client_port); UDP層壓棧后填寫自己UDP協(xié)議頭。調(diào)用ip_send(skb, ip, UDP); 指定目標IP和上層協(xié)議是UDP協(xié)議。 IP層ip_send(skb, ip, UDP); 填寫協(xié)議頭,在這里注意校驗和的計算。在ARP緩存中查找目標IP對應(yīng)的MAC地址,查找成功然后調(diào)用下層eth_send(skb, dest_eth_addr, ETH_P_IP);查找失敗發(fā)送ARP查詢請求。 ETH層 填充以太網(wǎng)幀頭,調(diào)用底層驅(qū)動函數(shù):board_eth_send(skb->data, skb->len);由CS8900芯片完成幀發(fā)送。 |
|