STM32NET學(xué)習(xí)筆記 IP ICMP部分
1.前言
嵌入式以太網(wǎng)開發(fā)是一個(gè)很有挑戰(zhàn)性的工作。通過幾個(gè)月的學(xué)習(xí),我個(gè)人覺得大致有兩條途徑。第一條途徑,先通過高級語言熟悉socket編程,例如C#或C++,對bind,listen,connect,accept等函數(shù)熟悉之后,應(yīng)用 lwIP。第二種途徑,通過分析嵌入式以太網(wǎng)代碼,結(jié)合TCPIP協(xié)議棧規(guī)范逐步實(shí)踐代碼。第一種途徑效率高,開發(fā)周期短,編寫出來的代碼性能穩(wěn)定,第二種途徑花的時(shí)間長,開發(fā)出來的代碼功能不完善,但是由于緊緊結(jié)合TCPIP規(guī)范,可以了解的內(nèi)容較多,適合學(xué)習(xí)。本文通過分析和修改AVRNET源碼,逐步實(shí)現(xiàn)TCPIP協(xié)議棧的各個(gè)子部分,包括ETHERNET部分,ARP部分,IP部分,ICMP部分,UDP部分,TCP部分和HTTP部分。
本文將實(shí)現(xiàn)IP部分和ICMP部分。
1.2 相關(guān)資料
【ENC28J60學(xué)習(xí)筆記】
【STM32NET學(xué)習(xí)筆記 ARP和Ethernet部分】
【AVRNET項(xiàng)目(國外)】
【AVR webserver項(xiàng)目(國外)】
1.3 代碼倉庫
【代碼倉庫】——CSDN Code代碼倉庫。
2.IP部分實(shí)現(xiàn)
IP層是TCP和UDP實(shí)現(xiàn)的基礎(chǔ)。IP首部緊跟以太網(wǎng)首部,長度為20字節(jié)。IP首部具有最基本的兩個(gè)任務(wù),
【第一】定義IP包的具體協(xié)議類型,例如ICMP,TCP或UDP等;
【第二】定義IP報(bào)文從哪個(gè)IP地址來和到哪個(gè)IP地址去。
需要強(qiáng)調(diào),在同一個(gè)子網(wǎng)中即同一個(gè)物理網(wǎng)絡(luò)中,IP報(bào)文中的目標(biāo)IP地址和以太網(wǎng)首部中的目標(biāo)MAC地址相對應(yīng),若不在同一個(gè)物理網(wǎng)路中,目標(biāo)IP地址和目標(biāo)MAC地址不同,目標(biāo)MAC地址被路由器的MAC地址替代,意味著通過路由器轉(zhuǎn)發(fā)報(bào)文。在IP首部中還包括很多其他內(nèi)容,需要注意的是IP標(biāo)識符,該標(biāo)識符主要用于區(qū)分IP報(bào)文,最簡單的算法即每發(fā)送一個(gè)IP報(bào)文后IP標(biāo)識符累加。具體通過以下代碼實(shí)現(xiàn)IP首部的填充。
2.1 IP首部填充
//IP首部總長度
#defineIP_HEADER_LEN20
//協(xié)議類型
//ICMP協(xié)議
#defineIP_PROTO_ICMP_V0x01
//TCP協(xié)議
#defineIP_PROTO_TCP_V0x06
//UDP協(xié)議
#defineIP_PROTO_UDP_V0x11
//IPV4版本
#defineIP_V4_V0x40
#defineIP_HEADER_LENGTH_V0x05
//IP版本號位置以太網(wǎng)首部2+6+6
#defineIP_P0x0E
//首部長度
#defineIP_HEADER_VER_LEN_P0x0E
//服務(wù)類型
#defineIP_TOS_P0x0F
//IP總長度
#defineIP_TOTLEN_H_P0x10
#defineIP_TOTLEN_L_P0x11
//IP標(biāo)識
#defineIP_ID_H_P0x12
#defineIP_ID_L_P0x13
//
#defineIP_FLAGS_H_P0x14
#defineIP_FLAGS_L_P0x15
//TTL生存時(shí)間
#defineIP_TTL_P0x16
//IP協(xié)議類型例如ICMPTCPUDP
#defineIP_PROTO_P0x17
//首部校驗(yàn)和
#defineIP_CHECKSUM_H_P0x18
#defineIP_CHECKSUM_L_P0x19
//源IP地址
#defineIP_SRC_IP_P0x1A
//目標(biāo)IP地址
#defineIP_DST_IP_P0x1E
voidip_generate_header(BYTE*rxtx_buffer,WORD_BYTEStotal_length,BYTEprotocol,BYTE*dest_ip)
{
BYTEi;
//校驗(yàn)結(jié)果
WORD_BYTESck;
//版本號和首都長度
rxtx_buffer[IP_P]=IP_V4_V|IP_HEADER_LENGTH_V;
//服務(wù)類型
rxtx_buffer[IP_TOS_P]=0x00;
//總長度
rxtx_buffer[IP_TOTLEN_H_P]=total_length.byte.high;
rxtx_buffer[IP_TOTLEN_L_P]=total_length.byte.low;
//IP標(biāo)識
rxtx_buffer[IP_ID_H_P]=ip_identfier>>8;
rxtx_buffer[IP_ID_H_P]=ip_identfier&0x00ff;
//累加
ip_identfier++;
//標(biāo)志和分片偏移
rxtx_buffer[IP_FLAGS_H_P]=0x00;
rxtx_buffer[IP_FLAGS_L_P]=0x00;
//生存時(shí)間
rxtx_buffer[IP_TTL_P]=128;
//setippackettypetotcp/udp/icmp...
rxtx_buffer[IP_PROTO_P]=protocol;
//設(shè)定目標(biāo)地址和源地址
for(i=0;i<4;i++)
{
rxtx_buffer[IP_DST_IP_P+i]=dest_ip[i];
rxtx_buffer[IP_SRC_IP_P+i]=avr_ip.byte[i];
}
//校驗(yàn)結(jié)果
rxtx_buffer[IP_CHECKSUM_H_P]=0;
rxtx_buffer[IP_CHECKSUM_L_P]=0;
ck.word=software_checksum(&rxtx_buffer[IP_P],sizeof(IP_HEADER),0);
rxtx_buffer[IP_CHECKSUM_H_P]=ck.byte.high;
rxtx_buffer[IP_CHECKSUM_L_P]=ck.byte.low;
}
2.2 IP報(bào)文查詢
IP報(bào)文查詢功能對應(yīng)于ARP報(bào)文查詢功能,通過以太網(wǎng)首部中的最后2個(gè)字節(jié)判斷該報(bào)文是否為IP報(bào)文;如果是IP報(bào)文則繼續(xù)和本機(jī)IP地址相比較。如果兩步檢查均通過則認(rèn)為是合法的IP報(bào)文,當(dāng)然這其中舍棄了IP版本號和首部校驗(yàn)和的檢查,雖然存在某些隱患但并不妨礙實(shí)現(xiàn)基本功能。
BYTEip_packet_is_ip(BYTE*rxtx_buffer)
{
unsignedchari;
//檢查該報(bào)文是否為IP報(bào)文
if(rxtx_buffer[ETH_TYPE_H_P]!=ETH_TYPE_IP_H_V||rxtx_buffer[ETH_TYPE_L_P]!=ETH_TYPE_IP_L_V)
return0;
//檢查該報(bào)文的IP地址是否為本機(jī)IP地址,逐個(gè)檢查
for(i=0;i
{
if(rxtx_buffer[IP_DST_IP_P+i]!=avr_ip.byte[i])
return0;
}
//若該報(bào)文為IP報(bào)文,且目標(biāo)IP地址為本機(jī)地址,返回1
return1;
}
3.ICMP部分實(shí)現(xiàn)
雖然ICMP具有很多的子協(xié)議,但是其中最著名的要數(shù)ping程序,即ICMP回顯請求和應(yīng)答報(bào)文。通過使用ping命令來判斷報(bào)文是否可以到達(dá)目標(biāo)地址。ICMP的實(shí)現(xiàn)是一個(gè)逐步遵守規(guī)則的過程,即向固定的字節(jié)填充數(shù)據(jù)。
//回顯應(yīng)答
#defineICMP_TYPE_ECHOREPLY_V0
//回顯請求
#defineICMP_TYPE_ECHOREQUEST_V8
//ICMP首部長度
#defineICMP_PACKET_LEN40
//ICMP類型
#defineICMP_TYPE_P0x22
//ICMP代碼
#defineICMP_CODE_P0x23
//ICMP首部校驗(yàn)和
#defineICMP_CHECKSUM_H_P0x24
#defineICMP_CHECKSUM_L_P0x25
//ICMP標(biāo)識符
#defineICMP_IDENTIFIER_H_P0x26
#defineICMP_IDENTIFIER_L_P0x27
//ICMP序號
#defineICMP_SEQUENCE_H_P0x28
#defineICMP_SEQUENCE_L_P0x29
#defineICMP_DATA_P0x2A
3.1 ICMP首部填充
ICMP首部填充需要根據(jù)協(xié)議類型填充不同的內(nèi)容,對于回顯請求而言只需在ICMP協(xié)議類型部分填充0即可,當(dāng)然ICMP部分也包括ICMP首部校驗(yàn)和。
voidicmp_generate_packet(BYTE*rxtx_buffer,BYTEtype)
{
BYTEi;
WORD_BYTESck;
//ICMP回顯請求
if(type==ICMP_TYPE_ECHOREQUEST_V)
{
rxtx_buffer[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V;
rxtx_buffer[ICMP_CODE_P]=0;
rxtx_buffer[ICMP_IDENTIFIER_H_P]=icmp_id;
rxtx_buffer[ ICMP_IDENTIFIER_L_P ] = 0;