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