学习TCP/IP的第1步:ARP数据包

学习TCP/IP,最好找ENC28J60这样的芯片,因为它没有硬件TCP/IP栈,有利于我们深入学习各种数据包的结构。我学习过W5500,它有硬件TCP/IP栈,学完之后,并不了解以太网数据的结构。在接触uip之后,我才明白以太网结构。

一、相关变量定义和初始化

1、相关变量

typedef struct NetworkInformation_t

{

uint8_t mac[6]; ///< Source Mac Address

uint8_t ip[4]; ///< Source IP Address

uint8_t sn[4]; ///< Subnet Mask

uint8_t gw[4]; ///< Gateway IP Address

}NetworkInformation;

NetworkInformation My_Network_Information;

u16 Local_Port; //本地端口

u8 Remote_ip[4]; //远程IP地址

u16 Remote_Port; //远程端口

u32 My_uipTimer;//uip 计时器,每10ms增加1.

struct strTimer

{

int start; //start为int型

int interval; //interval为int型

};

struct strTimer uIP_PeriodicTimer;

struct strTimer ARP_Timer;//ARP表刷新定时器

#define UIP_BUFSIZE 1514 //uIP缓存大小

#define MAX_UIP_BUFSIZE UIP_BUFSIZE+2

u8 TCP_TX_buf[MAX_UIP_BUFSIZE];//ENC28JI60发送数据的缓存

u16 TCP_TX_Length;//记录TCP_TX_buf[]的有效字节数

u8 TCP_RX_buf[MAX_UIP_BUFSIZE];//ENC28JI60接收数据的缓存

u16 TCP_RX_Length;//记录TCP_RX_buf[]的有效字节数

u16 My_IPID;// 每个带有"IP头部"的数据包均有一个唯一的id

//以太网帧的类型:

#define UIP_ETHTYPE_ARP 0x0806 //ARP请求包类型

#define UIP_ETHTYPE_IP4 0x0800 //IPV4数据包类型

#define UIP_ETHTYPE_IP6 0x86dd //IPv6包类型

#define UIP_TTL 64 // IP包的生存时间(TTL)

//以太网中的数据包协议:

#define UIP_PROTO_ICMP 1 //ICMP协议编号定义为1

#define UIP_PROTO_TCP 6 //TCP协议编号定义为6

#define UIP_PROTO_UDP 17 //TCP协议编号定义为17

#define UIP_PROTO_ICMP6 58 //ICMP6协议编号定义为58

#define UIP_LLH_LEN 14 //IPV4的以太网头部长度

#if UIP_CONF_IPV6

#define UIP_IPH_LEN 40 //IPV6的IP头的大小,IPV4头部(IPv4 header),占40个字节

#else

#define UIP_IPH_LEN 20

//IPV4的IP头的大小,IPV4头部(IPv4 header),占20个字节

#endif

#define UIP_LITTLE_ENDIAN 3412 //小端存储方式

#define UIP_BIG_ENDIAN 1234 //大端存储方式

#define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN

#if UIP_BYTE_ORDER == UIP_BIG_ENDIAN

#define HTONS(n) (n)

#else

#define HTONS(n) (u16)( ( ( (u16) (n)) << 8 ) | ( ( (u16) (n) ) >> 8 ) )

//小端存储方式将val的高8位值和低8位值交换

#endif

//函数功能:将ARP表中的IP地址设置为0

void Clear_ARP_Table(void)

{

u8 i;

for(i = 0; i < UIP_ARPTAB_SIZE; ++i)

{

memset( ARP_Table[i].RemoteIP_Address, 0, 4 );

//将首地址为arp_table[i].RemoteIP_Address[]的缓存的前4个字节设置为0

}

}

//配置网卡硬件,并设置MAC地址

//返回值:0,正常;1,失败;

u8 Network_Init(void)

{

u8 res=0;

//本地IP地址,如:"192.168.1.17"

My_Network_Information.ip[0] = 192;

My_Network_Information.ip[1] = 168;

My_Network_Information.ip[2] = 1;

My_Network_Information.ip[3] = 17;

//本地子网掩码: 255.255.255.0

My_Network_Information.sn[0] = 255;

My_Network_Information.sn[1] = 255;

My_Network_Information.sn[2] = 255;

My_Network_Information.sn[3] = 0;

//本地网关:192.168.1.1,其实就是你路由器的IP地址

My_Network_Information.gw[0] = 192;

My_Network_Information.gw[1] = 168;

My_Network_Information.gw[2] = 1;

My_Network_Information.gw[3] = 1;

//本地MAC地址:0x00 0x08 0xDC 0x11 0x11 0x02

My_Network_Information.mac[0] = 0x00;

My_Network_Information.mac[1] = 0x08;

My_Network_Information.mac[2] = 0xDC;

My_Network_Information.mac[3] = 0x11;

My_Network_Information.mac[4] = 0x11;

My_Network_Information.mac[5] = 0x02;

Local_Port=4096;//本地端口为4096

Remote_ip[0]=192;Remote_ip[1]=168;Remote_ip[2]=1;Remote_ip[3]=190;

//远程IP地址,如:"192.168.1.190"

Remote_Port=5000;//远程端口为5000

printf("IP:%d.%d.%d.%d\n",\

My_Network_Information.ip[0],\

My_Network_Information.ip[1],\

My_Network_Information.ip[2],\

My_Network_Information.ip[3]);

printf("Mask:%d.%d.%d.%d\n",\

My_Network_Information.sn[0],\

My_Network_Information.sn[1],\

My_Network_Information.sn[2],\

My_Network_Information.sn[3]);

printf("Gate:%d.%d.%d.%d\n",\

My_Network_Information.gw[0],\

My_Network_Information.gw[1],\

My_Network_Information.gw[2],\

My_Network_Information.gw[3]);

printf("Mac:%02x-%02x-%02x-%02x-%02x-%02x\n",\

My_Network_Information.mac[0],\

My_Network_Information.mac[1],\

My_Network_Information.mac[2],\

My_Network_Information.mac[3],\

My_Network_Information.mac[4],\

My_Network_Information.mac[5]);

res=ENC28J60_Init((u8*)My_Network_Information.mac);//初始化ENC28J60

ENC28J60_PHY_Write(PHLCON,0x0476);

//PHLCON:PHY模块LED控制寄存器

//指示灯状态:0x476 is PHLCON LEDA(绿)=links status, LEDB(红)=receive/transmit

TIM4_Interrupt_Initializtion(10000,72);//当arr=10000,psc=72时,则为10ms,误差为1us;

timer_set(&uIP_PeriodicTimer,50);//50*10=500毫秒

timer_set(&ARP_Timer,1000);//1000*10=10000毫秒=10秒

Clear_ARP_Table();

return res;

}

void uip_ipaddr( u16 *u16IP4_Address, u8 *u8IP4_Address)

{

((u16 *)(u16IP4_Address))[0] = HTONS(((u8IP4_Address[0]) << 8) | (u8IP4_Address[1]));

((u16 *)(u16IP4_Address))[1] = HTONS(((u8IP4_Address[2]) << 8) | (u8IP4_Address[3]));

}

//函数功能:将"大端存储方式"的data[]中的前len个字节按照"双字节进行累加和校验"

//data为"待校验数据缓存"的起始地址

//len为待校验数据的字节总数

u16 chksum(u16 sum, const u8 *data, u16 len)

{

u16 t;

const u8 *dataptr;

const u8 *last_byte;

dataptr = data; //指向"待校验数据缓存"的起始地址

last_byte = data + len - 1; //指向"待校验数据缓存"的结束地址

while(dataptr < last_byte)//至少有两个字节才能执行此while循环

{

t = (dataptr[0] << 8) + dataptr[1];//生成"大端存储方式"的16位整型数据

sum += t;//计算"累加和"

if(sum < t)//sum越过0x0000,发生进位

{

sum++;//若有进位,则加1

}

dataptr += 2;//修改指针,为下次计算"累加和"做准备

}

if(dataptr == last_byte)//剩余一个字节

{

t = (dataptr[0] << 8) + 0;//生成"大端存储方式"的16位整型数据

sum += t;//计算"累加和"

if(sum < t)//sum越过0x0000,发生进位

{

sum++;//若有进位,则加1

}

}

return sum;//按照"大端存储方式"返回sum

}

//函数功能:将"大端存储方式"的首地址为&uip_buf[14]的缓存中的前20个字节按照"双字节进行累加和校验";

//IP头部/IPV4头部(IPv4 header),占20个字节

//计算"IP头部/IPV4头部(IPv4 header)"20个字节的校验和

u16 ipV4chksum(u8 *tmpIPDataPointer)

{

u16 sum;

sum = chksum(0, tmpIPDataPointer, UIP_IPH_LEN);

//将tmpIPDataPointer[]中的前20个字节按照"双字节进行累加和校验"

//定义UIP_LLH_LEN=14,IP头的大小为UIP_IPH_LEN=20,IP头部/IPV4头部(IPv4 header),占20个字节

return (sum == 0) ? 0xffff : HTONS(sum);

// HTONS ()将累加和sum按照小端存储方式返回

}

//函数功能:打印一条报文;

void Print_Send_Package(unsigned char* buf,u16 len)

{

u16 i;

u8 temp;

printf("\r\nSend_Len=%u\r\n",len);

for(i=0;i<len;i++)

{

temp=0;

if( ( (buf[i]==0x0D)||(buf[i]==0x0A) ) )

{

printf("%c",buf[i]);

temp=1;

}

if(temp==0)

{

if( ( (buf[i]>0x20)&&(buf[i]<='~') ) ) printf("%c",buf[i]);

else

{

printf(" 0x%02X",buf[i]);

printf(" ");

}

}

}

printf("\r\n");//将"\r\n"发送到调试串口,由PC显示;

}

//函数功能:打印一条报文;

void Print_Receive_Package(unsigned char* buf,u16 len)

{

u16 i;

u8 temp;

if(len) printf("\r\nReceive_Len=%u\r\n",len);

for(i=0;i<len;i++)

{

temp=0;

if( ( (buf[i]==0x0D)||(buf[i]==0x0A) ) )

{

printf("%c",buf[i]);

temp=1;

}

if(temp==0)

{

if( ( (buf[i]>0x20)&&(buf[i]<='~') ) ) printf("%c",buf[i]);

else

{

printf(" 0x%02X",buf[i]);

printf(" ");

}

}

}

if(len) printf("\r\n");//将"\r\n"发送到调试串口,由PC显示;

}

//"IPV4头部校验和"正确,返回1

u8 Check_the_ipV4_header_checksume(u8 *tmpIPDataPointer)

{

u8 ret;

u16 sum;

ret=0;

/* Compute and check the IP header checksum. */

sum=ipV4chksum(tmpIPDataPointer);

//计算"IP头部/IPV4头部(IPv4 header)"20个字节的校验和

if(sum == 0xffff)//"IP头部校验和"正确

{

ret=1;//"IP头部校验和"正确,返回1

return(ret);

}

return(ret);

}

二、学习TCP/IP的第1步是学习ARP数据包

2.1、ARP驱动

//ARP结构

struct arp_hdr

{

u8 destMAC_Address[6]; //存放目的MAC地址

u8 srcMAC_Address[6]; //存放源MAC地址

u16 type;

//以太网帧的类型

//0x0800表示后面跟着的是IPV4数据包;

//0x0806表示后面跟着的是ARP数据包;

//0x86dd表示后面跟着的是IPV6数据包;

u16 hwtype; //硬件类型,若是以太网,值是0x0001

u16 protocol; //协议类型,若是ipv4,值是0x0800

u8 hwlen; //硬件长度,定义物理地址(MAC地址)的长度, mac地址就是6

u8 protolen; //协议长度,定义ip地址长度,是4

u16 opcode; //ARP操作码:ARP请求是1,ARP回复是2

u8 SendMAC_Address[6]; //发送方的6字节mac地址

u16 SendIP_Address[2]; //发送方的4字节ip地址

u8 ReceiveMAC_Address[6]; //接收方的6字节mac地址

u16 ReceiveIP_Address[2]; //接收方的4字节ip地址

};

//ARP操作码:

#define ARP_REQUEST 1 //ARP请求

#define ARP_REPLY 2 //ARP回复

//ARP表结构

struct arp_entry

{

u16 RemoteIP_Address[2]; //远程设备的IP地址

u8 RemoteMAC_Address[6]; //远程设备的MAC地址

u8 time; //"远程IP地址和MAC地址"建立的时间

};

#define UIP_ARPTable_Size 3 //ARP表的大小

struct arp_entry ARP_Table[UIP_ARPTable_Size];//ARP表

u8 ARPTimeCounter; //ARPTimeCounter每隔10秒加1

#define ARP_Table_UpdateTime 120

//timer_set(&ARP_Timer,1000);//1000*10=10000毫秒

//ARPTimeCounter每隔10秒加1,则120*10/60=20分钟

/*

ARP Ping: ARP(Address Resolution Protocol)用于将IP地址解析为MAC地址。

ARP请求会在局域网内广播,目标设备必须响应以确保通信正常。

*/

//不知道远程设备的MAC地址,发送ARP请求

/*

发送ARP数据格式:

0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x06

0x00 0x01 0x08 0x00 0x06 0x04 0x00 0x01 0x00 0x08 0xDC 0x11 0x11 0x02

0xC0 0xA8 0x01 0x11 0x00 0x00 0x00 0x00 0x00 0x00 0xC0 0xA8 0x01 0xBE

*/

void Send_ARP_REQUEST_In_Unknown_RemoteMACaddress(u8 *tmpRemoteIP_Address)

{

struct arp_hdr *pARPHeader;

u16 tmpIP4_Address[2];

pARPHeader=(struct arp_hdr *)&TCP_TX_buf[0];

TCP_TX_Length = 0;

//设置"目的MAC地址"

pARPHeader->destMAC_Address[0]=0xFF;

pARPHeader->destMAC_Address[1]=0xFF;

pARPHeader->destMAC_Address[2]=0xFF;

pARPHeader->destMAC_Address[3]=0xFF;

pARPHeader->destMAC_Address[4]=0xFF;

pARPHeader->destMAC_Address[5]=0xFF;

memcpy(pARPHeader->srcMAC_Address, My_Network_Information.mac, 6);

//设置"源MAC地址"为ENC28J60的MAC地址

pARPHeader->type = HTONS(UIP_ETHTYPE_ARP);

//设置太网帧的类型为0x0806,表示后面跟着的是ARP数据包;

pARPHeader->hwtype=HTONS(0x0001);

pARPHeader->protocol=HTONS(UIP_ETHTYPE_IP4);

pARPHeader->hwlen=6;

pARPHeader->protolen=4;

pARPHeader->opcode = HTONS(ARP_REQUEST);//设置操作码:ARP请求是1

memcpy(pARPHeader->SendMAC_Address, My_Network_Information.mac, 6);

//设置"发送方MAC地址"为ENC28J60的MAC地址

uip_ipaddr(tmpIP4_Address,My_Network_Information.ip); //设置远程IP为192.168.1.17

pARPHeader->SendIP_Address[0] = tmpIP4_Address[0];pARPHeader->SendIP_Address[1] = tmpIP4_Address[1];

//设置"发送方ip地址"

//设置"接收方MAC地址"

pARPHeader->ReceiveMAC_Address[0]=0;

pARPHeader->ReceiveMAC_Address[1]=0;

pARPHeader->ReceiveMAC_Address[2]=0;

pARPHeader->ReceiveMAC_Address[3]=0;

pARPHeader->ReceiveMAC_Address[4]=0;

pARPHeader->ReceiveMAC_Address[5]=0;

uip_ipaddr(tmpIP4_Address,tmpRemoteIP_Address); //准备"接收方ip地址"

pARPHeader->ReceiveIP_Address[0] = tmpIP4_Address[0];

pARPHeader->ReceiveIP_Address[1] = tmpIP4_Address[1];

//设置"接收方ip地址"

TCP_TX_Length = sizeof(struct arp_hdr);

ENC28J60_Packet_Receive(MAX_UIP_BUFSIZE,TCP_RX_buf);//清空ENC28J60的接收缓存

ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);

delay_us(TCP_TX_Length*2);//延时1个来回的时间

}

//已知远程设备的MAC地址,发送ARP请求

/*

发送ARP数据格式:

0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x06

0x00 0x01 0x08 0x00 0x06 0x04 0x00 0x01 0x00 0x08 0xDC 0x11 0x11 0x02

0xC0 0xA8 0x01 0x11 0xB4 0x2E 0x99 0x59 0xEC 0x1E 0xC0 0xA8 0x01 0xBE

*/

void Send_ARP_REQUEST_In_Known_RemoteMACaddress(u8 *tmpRemoteIP_Address)

{

struct arp_hdr *pARPHeader;

u16 tmpIP4_Address[2];

u8 find,ch;

struct arp_entry *pARP_Table;

uip_ipaddr(tmpIP4_Address,tmpRemoteIP_Address); //准备"接收方ip地址"

find=0;

for(pARP_Table = &ARP_Table[0]; pARP_Table <= &ARP_Table[UIP_ARPTable_Size - 1];++pARP_Table)

{//在ARP_Table[]查找

ch=0;

if(tmpIP4_Address[0]==pARP_Table->RemoteIP_Address[0]) ch++;

if(tmpIP4_Address[1]==pARP_Table->RemoteIP_Address[1]) ch++;

if( ch==2 )

{

find=1;

break;

}

}

if(find)

{

pARPHeader=(struct arp_hdr *)&TCP_TX_buf[0];

TCP_TX_Length = 0;

//设置"目的MAC地址"

pARPHeader->destMAC_Address[0]=pARP_Table->RemoteMAC_Address[0];

pARPHeader->destMAC_Address[1]=pARP_Table->RemoteMAC_Address[1];

pARPHeader->destMAC_Address[2]=pARP_Table->RemoteMAC_Address[2];

pARPHeader->destMAC_Address[3]=pARP_Table->RemoteMAC_Address[3];

pARPHeader->destMAC_Address[4]=pARP_Table->RemoteMAC_Address[4];

pARPHeader->destMAC_Address[5]=pARP_Table->RemoteMAC_Address[5];

memcpy(pARPHeader->srcMAC_Address, My_Network_Information.mac, 6);

//设置"源MAC地址"为ENC28J60的MAC地址

pARPHeader->type = HTONS(UIP_ETHTYPE_ARP);

//设置太网帧的类型为0x0806,表示后面跟着的是ARP数据包;

pARPHeader->hwtype=HTONS(0x0001);

pARPHeader->protocol=HTONS(UIP_ETHTYPE_IP4);

pARPHeader->hwlen=6;

pARPHeader->protolen=4;

pARPHeader->opcode = HTONS(ARP_REQUEST);//设置操作码:ARP请求是1

memcpy(pARPHeader->SendMAC_Address, My_Network_Information.mac, 6);

//设置"发送方MAC地址"为ENC28J60的MAC地址

uip_ipaddr(tmpIP4_Address,My_Network_Information.ip); //准备"发送方ip地址"

pARPHeader->SendIP_Address[0] = tmpIP4_Address[0];pARPHeader->SendIP_Address[1] = tmpIP4_Address[1];

//设置"发送方ip地址"

//设置"接收方MAC地址"

pARPHeader->ReceiveMAC_Address[0]=pARP_Table->RemoteMAC_Address[0];

pARPHeader->ReceiveMAC_Address[1]=pARP_Table->RemoteMAC_Address[1];

pARPHeader->ReceiveMAC_Address[2]=pARP_Table->RemoteMAC_Address[2];

pARPHeader->ReceiveMAC_Address[3]=pARP_Table->RemoteMAC_Address[3];

pARPHeader->ReceiveMAC_Address[4]=pARP_Table->RemoteMAC_Address[4];

pARPHeader->ReceiveMAC_Address[5]=pARP_Table->RemoteMAC_Address[5];

pARPHeader->ReceiveIP_Address[0] = pARP_Table->RemoteIP_Address[0];

pARPHeader->ReceiveIP_Address[1] = pARP_Table->RemoteIP_Address[1];

//设置"接收方ip地址"

TCP_TX_Length = sizeof(struct arp_hdr);

ENC28J60_Packet_Receive(MAX_UIP_BUFSIZE,TCP_RX_buf);//清空ENC28J60的接收缓存

#if ARP_Debug == 1

Print_Send_Package(TCP_TX_buf,TCP_TX_Length);

#endif

ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);

delay_us(TCP_TX_Length*2);//延时1个来回的时间

TCP_TX_Length=0;

}

}

/*

接收"来自计算机的ARP请求"

0x00 0x08 0xDC 0x11 0x11 0x02 0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x08 0x06

0x00 0x01 0x08 0x00 0x06 0x04 0x00 0x01 0xB4 0x2E 0x99 0x59 0xEC 0x1E

0xC0 0xA8 0x01 0xBE 0x00 0x08 0xDC 0x11 0x11 0x02 0xC0 0xA8 0x01 0x11

0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

发送ARP应答

0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x06

0x00 0x01 0x08 0x00 0x06 0x04 0x00 0x02 0x00 0x08 0xDC 0x11 0x11 0x02

0xC0 0xA8 0x01 0x11 0xB4 0x2E 0x99 0x59 0xEC 0x1E 0xC0 0xA8 0x01 0xBE

*/

u8 Receive_ARP_REQUEST_And_Send_ARP_Reply(void)

{

u8 ch1,ret;

struct arp_hdr *pARPHeaderTX;

struct arp_hdr *pARPHeaderRX;

u16 tmpIP4_Address[2];

ret=0;

ch1=0;

pARPHeaderRX=(struct arp_hdr *)&TCP_RX_buf[0];

if( (u8)pARPHeaderRX->type==(u8)(UIP_ETHTYPE_ARP>>8) ) ch1++;

if( (u8)(pARPHeaderRX->type>>8)==(u8)UIP_ETHTYPE_ARP ) ch1++;

if( TCP_RX_Length>=sizeof(struct arp_hdr) ) ch1++;

if( pARPHeaderRX->opcode==HTONS(ARP_REQUEST) ) ch1++;

if( (u8)pARPHeaderRX->ReceiveIP_Address[0]==My_Network_Information.ip[0] ) ch1++;

if( (u8)(pARPHeaderRX->ReceiveIP_Address[0]>>8)==My_Network_Information.ip[1]) ch1++;

if( (u8)pARPHeaderRX->ReceiveIP_Address[1]==My_Network_Information.ip[2]) ch1++;

if( (u8)(pARPHeaderRX->ReceiveIP_Address[1]>>8)==My_Network_Information.ip[3] ) ch1++;

if(ch1==8)

{

pARPHeaderTX=(struct arp_hdr *)&TCP_TX_buf[0];

TCP_TX_Length = 0;

//设置"目的MAC地址"

pARPHeaderTX->destMAC_Address[0]=pARPHeaderRX->SendMAC_Address[0];

pARPHeaderTX->destMAC_Address[1]=pARPHeaderRX->SendMAC_Address[1];

pARPHeaderTX->destMAC_Address[2]=pARPHeaderRX->SendMAC_Address[2];

pARPHeaderTX->destMAC_Address[3]=pARPHeaderRX->SendMAC_Address[3];

pARPHeaderTX->destMAC_Address[4]=pARPHeaderRX->SendMAC_Address[4];

pARPHeaderTX->destMAC_Address[5]=pARPHeaderRX->SendMAC_Address[5];

memcpy(pARPHeaderTX->srcMAC_Address, My_Network_Information.mac, 6);

//设置"源MAC地址"为ENC28J60的MAC地址

pARPHeaderTX->type = HTONS(UIP_ETHTYPE_ARP);

//设置太网帧的类型为0x0806,表示后面跟着的是ARP数据包;

pARPHeaderTX->hwtype=HTONS(0x0001);

pARPHeaderTX->protocol=HTONS(UIP_ETHTYPE_IP4);

pARPHeaderTX->hwlen=6;

pARPHeaderTX->protolen=4;

pARPHeaderTX->opcode = HTONS(ARP_REPLY);//设置操作码:ARP应答是2

memcpy(pARPHeaderTX->SendMAC_Address, My_Network_Information.mac, 6);

//设置"发送方MAC地址"为ENC28J60的MAC地址

uip_ipaddr(tmpIP4_Address,My_Network_Information.ip); //准备"发送方ip地址"

pARPHeaderTX->SendIP_Address[0] = tmpIP4_Address[0];pARPHeaderTX->SendIP_Address[1] = tmpIP4_Address[1];

//设置"发送方ip地址"

//设置"接收方MAC地址"

pARPHeaderTX->ReceiveMAC_Address[0]=pARPHeaderRX->SendMAC_Address[0];

pARPHeaderTX->ReceiveMAC_Address[1]=pARPHeaderRX->SendMAC_Address[1];

pARPHeaderTX->ReceiveMAC_Address[2]=pARPHeaderRX->SendMAC_Address[2];

pARPHeaderTX->ReceiveMAC_Address[3]=pARPHeaderRX->SendMAC_Address[3];

pARPHeaderTX->ReceiveMAC_Address[4]=pARPHeaderRX->SendMAC_Address[4];

pARPHeaderTX->ReceiveMAC_Address[5]=pARPHeaderRX->SendMAC_Address[5];

pARPHeaderTX->ReceiveIP_Address[0] = pARPHeaderRX->SendIP_Address[0];

pARPHeaderTX->ReceiveIP_Address[1] = pARPHeaderRX->SendIP_Address[1];

//设置"接收方ip地址"

TCP_TX_Length = sizeof(struct arp_hdr);

ENC28J60_Packet_Send(TCP_TX_Length,TCP_TX_buf);

update_ARP_Table(pARPHeaderRX->SendIP_Address,pARPHeaderRX->SendMAC_Address);

//更新ARP表

#if ARP_Debug == 1

Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);

Print_Send_Package(TCP_TX_buf,TCP_TX_Length);

#endif

delay_us(TCP_TX_Length);

TCP_TX_Length=0;

ret=1;//保存MAC地址和IP地址

}

return(ret);

}

//接收ARP应答

/*

发送ARP数据格式:

0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x08 0xDC 0x11 0x11 0x02 0x08 0x06

0x00 0x01 0x08 0x00 0x06 0x04 0x00 0x01 0x00 0x08 0xDC 0x11 0x11 0x02

0xC0 0xA8 0x01 0x11 0x00 0x00 0x00 0x00 0x00 0x00 0xC0 0xA8 0x01 0xBE

接收ARP数据格式:

0x00 0x08 0xDC 0x11 0x11 0x02 0xB4 0x2E 0x99 0x59 0xEC 0x1E 0x08 0x06

0x00 0x01 0x08 0x00 0x06 0x04 0x00 0x02 0xB4 0x2E 0x99 0x59 0xEC 0x1E

0xC0 0xA8 0x01 0xBE 0x00 0x08 0xDC 0x11 0x11 0x02 0xC0 0xA8 0x01 0x11

0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

*/

u8 Receive_ARP_REPLY(u8 *tmpRemoteIP_Address)

{

u8 ch1,ret;

struct arp_hdr *pARPHeader;

ret=0;

TCP_RX_Length=ENC28J60_Packet_Receive(MAX_UIP_BUFSIZE,TCP_RX_buf);

ch1=0;

pARPHeader=(struct arp_hdr *)&TCP_RX_buf[0];

if( (u8)pARPHeader->type==(u8)(UIP_ETHTYPE_ARP>>8) ) ch1++;

if( (u8)(pARPHeader->type>>8)==(u8)UIP_ETHTYPE_ARP ) ch1++;

if( TCP_RX_Length>=sizeof(struct arp_hdr) ) ch1++;

if( pARPHeader->opcode==HTONS(ARP_REPLY) ) ch1++;

if( (u8)pARPHeader->SendIP_Address[0]==tmpRemoteIP_Address[0] ) ch1++;

if( (u8)(pARPHeader->SendIP_Address[0]>>8)==tmpRemoteIP_Address[1]) ch1++;

if( (u8)pARPHeader->SendIP_Address[1]==tmpRemoteIP_Address[2]) ch1++;

if( (u8)(pARPHeader->SendIP_Address[1]>>8)==tmpRemoteIP_Address[3] ) ch1++;

if( (u8)pARPHeader->ReceiveIP_Address[0]==My_Network_Information.ip[0] ) ch1++;

if( (u8)(pARPHeader->ReceiveIP_Address[0]>>8)==My_Network_Information.ip[1]) ch1++;

if( (u8)pARPHeader->ReceiveIP_Address[1]==My_Network_Information.ip[2]) ch1++;

if( (u8)(pARPHeader->ReceiveIP_Address[1]>>8)==My_Network_Information.ip[3] ) ch1++;

if(ch1==12)

{

#if ARP_Debug == 1

Print_Send_Package(TCP_TX_buf,TCP_TX_Length);

Print_Receive_Package(TCP_RX_buf,TCP_RX_Length);

#endif

ret=1;//保存MAC地址和IP地址

TCP_TX_Length=0;

}

return(ret);

}

//函数功能:更新ARP表

//先在ARP表里发现这个IP地址,则更新MAC地址;如果没有发现这个IP地址,则在ARP表里查找IP地址为0的条目;

//若发现IP地址为0,则保存远程IP地址和MAC地址。如果ARP表里没有IP地址为0的条目,则根据通讯时间去查找条目,

//然后保存远程IP地址和MAC地址。

//pIP_address是接收到的"发送方的4字节ip地址"

//pMAC是接收到的"发送方的6字节mac地址"

void update_ARP_Table(u16 *pIP_address, u8 *pMAC)

{

u8 i;

u8 tmpage;

register struct arp_entry *pARP_Table;

//register请求编译器将局部变量pARP_Table存储在寄存器中,以提高访问速度。

u8 find;

u8 deltValue;

//在ARP表里发现有"pIP_address所指向的IP地址",则更新MAC地址后,返回

for(i = 0; i < UIP_ARPTable_Size; ++i)

{

pARP_Table = &ARP_Table[i];

if(pARP_Table->RemoteIP_Address[0] != 0 && pARP_Table->RemoteIP_Address[1] != 0)

{

//检查传入数据包的源IP地址是否与ARP表项中的IP地址匹配。

if(pIP_address[0] == pARP_Table->RemoteIP_Address[0] && pIP_address[1] == pARP_Table->RemoteIP_Address[1])

{//在ARP表里发现这个IP地址

/* An old entry found, update this and return. */

memcpy(pARP_Table->RemoteMAC_Address, pMAC, 6);//更新MAC地址

pARP_Table->time = ARPTimeCounter;

return;

}

}

}

//在ARP表里没有"pIP_address所指向的IP地址",则查找"IP地址为0"的存储位置

for(i = 0; i < UIP_ARPTable_Size; ++i)

{

pARP_Table = &ARP_Table[i];

if(pARP_Table->RemoteIP_Address[0] == 0 && pARP_Table->RemoteIP_Address[1] == 0)

{//在ARP_Table[]中,发现IP地址为0,则将在这个位置保存新的"IP地址和MAC地址"

memcpy(pARP_Table->RemoteIP_Address, pIP_address, 4);//保存新的IP地址

memcpy(pARP_Table->RemoteMAC_Address, pMAC, 6); //保存新的MAC地址

pARP_Table->time = ARPTimeCounter;

break;

}

}

//在ARP表里没有"pIP_address所指向的IP地址",也没有找到"IP地址为0"的存储位置

//则查找"IP地址和MAC地址"建立最久的条目,用来保存新的"IP地址和MAC地址"

if(i == UIP_ARPTable_Size)//没有发现IP地址为0

{

tmpage = 0;find = 0;

for(i = 0; i < UIP_ARPTable_Size; ++i)

{//查找建立时间最长的"IP地址和MAC地址"

pARP_Table = &ARP_Table[i];

if(ARPTimeCounter >= pARP_Table->time)

deltValue=ARPTimeCounter - pARP_Table->time;

//"IP地址和MAC地址"建立多长时间

else

{

deltValue=256 - pARP_Table->time;

deltValue=deltValue + ARPTimeCounter;

}

//如果ARPTimeCounter<pARP_Table->time,deltValue<0;

if( deltValue > tmpage)

{

tmpage = deltValue;

find = i;//记录,然后再循环查找

}

}

i = find;//记录修改位置

pARP_Table = &ARP_Table[find];

memcpy(pARP_Table->RemoteIP_Address, pIP_address, 4); //保存新的IP地址

memcpy(pARP_Table->RemoteMAC_Address, pMAC, 6); //保存新的MAC地址

pARP_Table->time = ARPTimeCounter;

}

}

//打印ARP表

void Print_ARP_Table(void)

{

#if ARP_Debug == 1

u8 i;

struct arp_entry *pARP_Table;

printf("\r\n");

for(i=0;i<UIP_ARPTable_Size;i++)

{

pARP_Table = &ARP_Table[i];

printf("arp_table[%u].ip=%u:%u:%u:%u ",

i,(u8)pARP_Table->RemoteIP_Address[0],(u8)(pARP_Table->RemoteIP_Address[0]>>8),

(u8)pARP_Table->RemoteIP_Address[1],(u8)(pARP_Table->RemoteIP_Address[1]>>8)

);

printf("arp_table[%u].mac=%02X:%02X:%02X:%02X:%02X:%02X ",

i,pARP_Table->RemoteMAC_Address[0],pARP_Table->RemoteMAC_Address[1],

pARP_Table->RemoteMAC_Address[2],pARP_Table->RemoteMAC_Address[3],

pARP_Table->RemoteMAC_Address[4],pARP_Table->RemoteMAC_Address[5]

);

printf("arp_table[%u].time=%u\r\n",i,pARP_Table->time);

}

#endif

}

//根据IP地址过去远程设备的MAC地址

void Get_Remote_MAC_Address(u8 *tmpRemoteIP_Address)

{

u8 loop,ret;

u16 cnt;

struct arp_hdr *pARPHeader;

ret=0;cnt=3000;loop=10;

while(loop)

{

if(cnt>=3000)

{

Send_ARP_REQUEST_In_Unknown_RemoteMACaddress(tmpRemoteIP_Address);

//发送ARP请求

cnt=0;

loop--;

}

ret=Receive_ARP_REPLY(tmpRemoteIP_Address);//接收ARP应答

if(ret==1)//读到远程设备的MAC地址

break;

cnt++;

}

if(ret==1)//读到远程设备的MAC地址

{

pARPHeader=(struct arp_hdr *)&TCP_RX_buf[0];

update_ARP_Table(pARPHeader->SendIP_Address,pARPHeader->SendMAC_Address);

//更新ARP表

Print_ARP_Table();//打印ARP表

ret=0;cnt=3000;loop=10;

while(loop)

{

if(cnt>=3000)

{

Send_ARP_REQUEST_In_Known_RemoteMACaddress(tmpRemoteIP_Address);//发送ARP请求

cnt=0;

loop--;

}

ret=Receive_ARP_REPLY(tmpRemoteIP_Address);//接收ARP应答

if(ret==1)//读到远程设备的MAC地址

break;

cnt++;

}

}

}

//函数功能:

//如果ARP表中的某个"IP地址和MAC地址"的建立时间在20分钟内,没有更新,则将这个IP地址设置为0

//注意:uip_arp_timer()每10秒执行一次

void ARP_Table_Update_Timer(void)

{

u8 i;

struct arp_entry *pARP_Table;

u8 deltValue;

++ARPTimeCounter;//10秒计数器加1

for(i = 0; i < UIP_ARPTable_Size; ++i)

{

pARP_Table = &ARP_Table[i];//获取首地址

if( ARPTimeCounter >= pARP_Table->time )

deltValue=ARPTimeCounter - pARP_Table->time;

//"IP地址和MAC地址"建立多长时间

else

{

deltValue=256 - pARP_Table->time;

deltValue=deltValue + ARPTimeCounter;

}

//发现ARP表中有一个"IP地址和MAC地址"建立时间,长期没有更新,因此将这个IP地址设置为0

if( (pARP_Table->RemoteIP_Address[0] | pARP_Table->RemoteIP_Address[1]) != 0 && deltValue >= ARP_Table_UpdateTime)

{//远程设备IP地址不为0,且建立时间超过120*10=1200秒=20分钟

memset(pARP_Table->RemoteIP_Address, 0, 4);

//将首地址为pARP_Table->ipaddr的缓存的前4个字节设置为0

}

}

}

2.2、测试ARP的main.c

#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t

#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()

#include "delay.h"

#include "USART1.h"

#include "IWDG.h"

#include "ENC28J60_Variable.h"

#include "Timer4.h"

#include "enc28j60.h"

#include "ENC28J60_Interface.h"

#include "ARP.h"

//PLL配置: PLLCLK = HSE * 6 = 72 MHz,2025年11月8日修改

u8 Main_cnt;

const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";

int main(void)

{

HSE_SetSysClock(RCC_PLLMul_6); // 设置系统时钟为12MHz * 6 = 72MHz

// SystemInit();//在使用IAP时,该语句需要屏蔽

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4

USART1_Serial_Interface_Enable(115200);

SysRstSrcRecord();//系统复位记录

delay_ms(1000);

while( Network_Init() )

{

}

Remote_ip[0]=192;Remote_ip[1]=168;Remote_ip[2]=1;Remote_ip[3]=190;

//远程IP地址,如:"192.168.1.190"

Get_Remote_MAC_Address(Remote_ip);

while(1)

{

}

}

三、附uip自带的uip_arp.c之源文件

复制代码
#include "uip_arp.h"
#include "string.h"

//uip_eth_addr结构成员为字节型数组addr[6]
static const struct uip_eth_addr broadcast_ethaddr ={
 { 0xff,0xff,0xff,0xff,0xff,0xff}
};

static const u16_t broadcast_ipaddr[2] = {0xffff,0xffff};


struct arp_entry arp_table[UIP_ARPTAB_SIZE];//源文件是静态结构数组

static u16_t TmpIpAddress[2];
static u8_t Index;

static u8_t arptime; //声明字节静态变量arptime
static u8_t tmpage;  //声明字节静态变量tmpage

#define ARP_Header_Buffer   ((struct arp_hdr *)&uip_buf[0])
#define IPBUF ((struct ethip_hdr *)&uip_buf[0])
/*-----------------------------------------------------------------------------------*/
/**
 * Initialize the ARP module.
 *
 */
/*-----------------------------------------------------------------------------------*/
void uip_arp_init(void)
{
  for(Index = 0; Index < UIP_ARPTAB_SIZE; ++Index) 
  {
    memset(arp_table[Index].ipaddr, 0, 4);
  }
}



/*-----------------------------------------------------------------------------------*/
/**
 * Periodic ARP processing function.
 *
 * This function performs periodic timer processing in the ARP module and should be called at regular intervals. 
The recommended interval is 10 seconds between the calls.
 *
 */
/*-----------------------------------------------------------------------------------*/
//函数功能:周期性ARP处理功能,10秒执行一次
void uip_arp_timer(void)
{
  struct arp_entry *tabptr;
  
  ++arptime;
  for(Index = 0; Index < UIP_ARPTAB_SIZE; ++Index)
	{//宏定义UIP_ARPTAB_SIZE=8
    tabptr = &arp_table[Index];
    if( (tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 &&
       arptime - tabptr->time >= UIP_ARP_MAXAGE)
		{
      memset(tabptr->ipaddr, 0, 4);
    }
  }

}
/*-----------------------------------------------------------------------------------*/
//函数功能:
//先在ARP表里发现这个IP地址,则更新MAC地址;如果没有发现这个IP地址,则在ARP表里查找IP地址为0的条目;
//若发现IP地址为0,则保存远程IP地址和MAC地址。如果ARP表里没有IP地址为0的条目,则根据通讯时间去查找条目,
//然后保存远程IP地址和MAC地址。
//ipaddr是接收到的"发送方的4字节ip地址",ethaddr是接收到的"发送方的6字节mac地址"
static void uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr)
{
  register struct arp_entry *tabptr;
  /* Walk through the ARP mapping table and try to find an entry to
     update. If none is found, the IP -> MAC address mapping is
     inserted in the ARP table. */
	u8_t find;

/////在ARP表里发现这个IP地址,则更新MAC地址
  for(Index = 0; Index < UIP_ARPTAB_SIZE; ++Index)
	{
		tabptr = &arp_table[Index];
    /* Only check those entries that are actually in use. */
    if(tabptr->ipaddr[0] != 0 && tabptr->ipaddr[1] != 0)
		{
			//检查传入数据包的源IP地址是否与ARP表项中的IP地址匹配。
      /* Check if the source IP address of the incoming packet matches the IP address in this ARP table entry. */
      if(ipaddr[0] == tabptr->ipaddr[0] && ipaddr[1] == tabptr->ipaddr[1])
			{//在ARP表里发现这个IP地址 
	       /* An old entry found, update this and return. */
	      memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);//更新MAC地址
	      tabptr->time = arptime;
	      return;
      }
    }
  }

/////在ARP表里没有发现这个IP地址,则查看ARP表里是否有IP地址为0的条目
  /* If we get here, no existing ARP table entry was found, so we
     create one. */

  /* First, we try to find an unused entry in the ARP table. */
  for(Index = 0; Index < UIP_ARPTAB_SIZE; ++Index)
	{
    tabptr = &arp_table[Index];
    if(tabptr->ipaddr[0] == 0 && tabptr->ipaddr[1] == 0)
		{//在arp_table[]中,发现IP地址为0,则将这个条目这个下标值
      break;
    }
  }

	//若未发现未使用的条目,则尝试找出最旧的条目并将其删除。
  /* If no unused entry is found, we try to find the oldest entry and throw it away. */
  if(Index == UIP_ARPTAB_SIZE)//没有发现IP地址为0
	{
    tmpage = 0;
    find = 0;
    for(Index = 0; Index < UIP_ARPTAB_SIZE; ++Index)
		{
      tabptr = &arp_table[Index];
      if(arptime - tabptr->time > tmpage)
			{//发现最旧的条目
	      tmpage = arptime - tabptr->time;
	      find = Index;
      }
    }
    Index = find;
    tabptr = &arp_table[find];
  }

  /* Now, i is the ARP table entry which we will fill with the new information. */
//arp_table[0].ip=192:168:1:190  arp_table[0].mac=B4:2E:99:59:EC:1E   arp_table[0].time=188
//arp_table[1].ip=192:168:1:1  arp_table[1].mac=60:DA:83:44:76:21   arp_table[1].time=203
  memcpy(tabptr->ipaddr, ipaddr, 4);//更新远程IP地址
  memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);//更新MAC地址
  tabptr->time = arptime;
}
/*-----------------------------------------------------------------------------------*/
/**
 * ARP processing for incoming IP packets
 *
 * This function should be called by the device driver when an IP
 * packet has been received. The function will check if the address is
 * in the ARP cache, and if so the ARP cache entry will be
 * refreshed. If no ARP cache entry was found, a new one is created.
 *
 * This function expects an IP packet with a prepended Ethernet header
 * in the uip_buf[] buffer, and the length of the packet in the global
 * variable uip_len.
 */
/*-----------------------------------------------------------------------------------*/
#if 0
void
uip_arp_ipin(void)
{
  uip_len -= sizeof(struct uip_eth_hdr);
	
  /* Only insert/update an entry if the source IP address of the
     incoming IP packet comes from a host on the local network. */
  if((IPBUF->srcipaddr[0] & uip_netmask[0]) !=
     (uip_hostaddr[0] & uip_netmask[0])) {
    return;
  }
  if((IPBUF->srcipaddr[1] & uip_netmask[1]) !=
     (uip_hostaddr[1] & uip_netmask[1])) {
    return;
  }
  uip_arp_update(IPBUF->srcipaddr, &(IPBUF->ethhdr.src));
  
  return;
}
#endif /* 0 */

/*-----------------------------------------------------------------------------------*/
/**
 * ARP processing for incoming ARP packets.
 *
 * This function should be called by the device driver when an ARP
 * packet has been received. The function will act differently
 * depending on the ARP packet type: if it is a reply for a request
 * that we previously sent out, the ARP cache will be filled in with
 * the values from the ARP reply. If the incoming ARP packet is an ARP
 * request for our IP address, an ARP reply packet is created and put
 * into the uip_buf[] buffer.
 *
 * When the function returns, the value of the global variable uip_len
 * indicates whether the device driver should send out a packet or
 * not. If uip_len is zero, no packet should be sent. If uip_len is
 * non-zero, it contains the length of the outbound packet that is
 * present in the uip_buf[] buffer.
 *
 * This function expects an ARP packet with a prepended Ethernet
 * header in the uip_buf[] buffer, and the length of the packet in the
 * global variable uip_len.
 */
/*-----------------------------------------------------------------------------------*/
//发送数据前,准备远程的IP地址和远程的MAC地址,以及本地的IP地址和MAC地址
void uip_arp_arpin(void)
{
	if( uip_len < sizeof(struct arp_hdr) )
	{
    uip_len = 0;
    return;
  }

  uip_len = 0;

//ARP_Header_Buffer->opcode是ARP消息的类型,ARP请求是1,ARP回复是2,RARP请求是3,RARP回复是4  
  switch(ARP_Header_Buffer->opcode)
	{
    case HTONS(ARP_REQUEST)://ARP请求
			//ARP请求。如果它询问我们的地址,我们会发送回复。
    /* ARP request. If it asked for our address, we send out a reply. */
    if(uip_ipaddr_cmp(ARP_Header_Buffer->dipaddr, uip_hostaddr))
		{//接收到的目的IP地址和ENC28J60的IP地址相同,表示这个数据是我的
      /* First, we register the one who made the request in our ARP
	 table, since it is likely that we will do more communication
	 with this host in the future. */
      uip_arp_update(ARP_Header_Buffer->sipaddr, &ARP_Header_Buffer->shwaddr);
			//sipaddr是接收到的"发送方的4字节ip地址",shwaddr是接收到的"发送方的6字节mac地址"
//先在ARP表里发现这个IP地址,则更新MAC地址;如果没有发现这个IP地址,则在ARP表里查找IP地址为0的条目;
//若发现IP地址为0,则保存远程IP地址和MAC地址。如果ARP表里没有IP地址为0的条目,则根据通讯时间去查找条目,
//然后保存远程IP地址和MAC地址。
      
      /* The reply opcode is 2. */
      ARP_Header_Buffer->opcode = HTONS(2);

      memcpy(ARP_Header_Buffer->dhwaddr.addr, ARP_Header_Buffer->shwaddr.addr, 6);
			//将"发送方的6字节mac地址"拷贝到"接收方的6字节mac地址位置"
      memcpy(ARP_Header_Buffer->shwaddr.addr, uip_ethaddr.addr, 6);
			//将ENC28J60的MAC地址拷贝到"发送方的6字节mac地址"

      memcpy(ARP_Header_Buffer->ethhdr.src.addr, uip_ethaddr.addr, 6);
			//将ENC28J60的MAC地址拷贝到"源MAC地址位置"
      memcpy(ARP_Header_Buffer->ethhdr.dest.addr, ARP_Header_Buffer->dhwaddr.addr, 6);
			//将远程MAC地址拷贝到"接收方的6字节mac地址位置"
      
      ARP_Header_Buffer->dipaddr[0] = ARP_Header_Buffer->sipaddr[0];ARP_Header_Buffer->dipaddr[1] = ARP_Header_Buffer->sipaddr[1];
			//设置接收方的4字节ip地址

      ARP_Header_Buffer->sipaddr[0] = uip_hostaddr[0];ARP_Header_Buffer->sipaddr[1] = uip_hostaddr[1];
			//设置"发送方的4字节ip地址"

      ARP_Header_Buffer->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);
	    //设置报文类型为0x0806,表示后面跟着的是ARP数据包;
      uip_len = sizeof(struct arp_hdr);
    }
    break;

  case HTONS(ARP_REPLY)://ARP回复
		//ARP回复。如果ARP表是为我们准备的,我们会插入或更新该表。
    /* ARP reply. We insert or update the ARP table if it was meant for us. */
    if(uip_ipaddr_cmp(ARP_Header_Buffer->dipaddr, uip_hostaddr))
		{//接收到的目的IP地址和ENC28J60的IP地址相同,表示这个数据是我的
      uip_arp_update(ARP_Header_Buffer->sipaddr, &ARP_Header_Buffer->shwaddr);
			//ARP_Header_Buffer->sipaddr是发送方的4字节ip地址,ARP_Header_Buffer->shwaddr为"发送方的6字节mac地址"
//先在ARP表里发现这个IP地址,则更新MAC地址;如果没有发现这个IP地址,则在ARP表里查找IP地址为0的条目;
//若发现IP地址为0,则保存远程IP地址和MAC地址。如果ARP表里没有IP地址为0的条目,则根据通讯时间去查找条目,
//然后保存远程IP地址和MAC地址。
    }
    break;
  }

  return;
}

/*-----------------------------------------------------------------------------------*/
/**
 * Prepend Ethernet header to an outbound IP packet and see if we need
 * to send out an ARP request.
 *
 * This function should be called before sending out an IP packet. The
 * function checks the destination IP address of the IP packet to see
 * what Ethernet MAC address that should be used as a destination MAC
 * address on the Ethernet.
 *
 * If the destination IP address is in the local network (determined
 * by logical ANDing of netmask and our IP address), the function
 * checks the ARP cache to see if an entry for the destination IP
 * address is found. If so, an Ethernet header is prepended and the
 * function returns. If no ARP cache entry is found for the
 * destination IP address, the packet in the uip_buf[] is replaced by
 * an ARP request packet for the IP address. The IP packet is dropped
 * and it is assumed that they higher level protocols (e.g., TCP)
 * eventually will retransmit the dropped packet.
 *
 * If the destination IP address is not on the local network, the IP
 * address of the default router is used instead.
 *
 * When the function returns, a packet is present in the uip_buf[]
 * buffer, and the length of the packet is in the global variable
 * uip_len.
 */
/*-----------------------------------------------------------------------------------*/
void uip_arp_out(void)
{
  struct arp_entry *tabptr;
  
  /* Find the destination IP address in the ARP table and construct
     the Ethernet header. If the destination IP addres isn't on the
     local network, we use the default router's IP address instead.

     If not ARP table entry is found, we overwrite the original IP
     packet with an ARP request for the IP address. */

  /* First check if destination is a local broadcast. */
  if(uip_ipaddr_cmp(IPBUF->destipaddr, broadcast_ipaddr))
	{
    memcpy(IPBUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6);
  }
	else
	{
    /* Check if the destination address is on the local network. */
    if(!uip_ipaddr_maskcmp(IPBUF->destipaddr, uip_hostaddr, uip_netmask))
		{
      /* Destination address was not on the local network, so we need to
	 use the default router's IP address instead of the destination
	 address when determining the MAC address. */
      uip_ipaddr_copy(TmpIpAddress, uip_draddr);
			//将网关地址拷贝到TmpIpAddress[]中
			//uip_draddr[2]保存的是网关:192.168.1.1
    }
		else
		{
      /* Else, we use the destination IP address. */
      uip_ipaddr_copy(TmpIpAddress, IPBUF->destipaddr);
    }
      
    for(Index = 0; Index < UIP_ARPTAB_SIZE; ++Index)
		{
      tabptr = &arp_table[Index];
      if( uip_ipaddr_cmp(TmpIpAddress, tabptr->ipaddr) )
			{
	      break;
      }
    }

    if(Index == UIP_ARPTAB_SIZE)//发现ARP表中没有这个IP
		{
      /* The destination address was not in our ARP table, so we
	 overwrite the IP packet with an ARP request. */

      memset(ARP_Header_Buffer->ethhdr.dest.addr, 0xff, 6);
      memset(ARP_Header_Buffer->dhwaddr.addr, 0x00, 6);
			//设置"接收方的6字节mac地址"为0
      memcpy(ARP_Header_Buffer->ethhdr.src.addr, uip_ethaddr.addr, 6);
      memcpy(ARP_Header_Buffer->shwaddr.addr, uip_ethaddr.addr, 6);
    
      uip_ipaddr_copy(ARP_Header_Buffer->dipaddr, TmpIpAddress);
      uip_ipaddr_copy(ARP_Header_Buffer->sipaddr, uip_hostaddr);

      ARP_Header_Buffer->opcode = HTONS(ARP_REQUEST); //ARP请求
      ARP_Header_Buffer->hwtype = HTONS(ARP_HWTYPE_ETH);//数据链路层的类型,若是以太网,值是0x0001
      ARP_Header_Buffer->protocol = HTONS(UIP_ETHTYPE_IP);//IPV4数据包类型
      ARP_Header_Buffer->hwlen = 6;//硬件字段大小(单位字节),若是mac地址就是6
      ARP_Header_Buffer->protolen = 4;//协议字段大小(单位字节),若是ip地址就是4
      ARP_Header_Buffer->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);//ARP请求包类型

      uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN];
    
      uip_len = sizeof(struct arp_hdr);
      return;
    }

    /* Build an ethernet header. */
    memcpy(IPBUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6);
  }

  memcpy(IPBUF->ethhdr.src.addr, uip_ethaddr.addr, 6);
	//uip_ethaddr.addr[]保存的是ENC28J60的MAC地址
  
  IPBUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP);

  uip_len += sizeof(struct uip_eth_hdr);
}
/*-----------------------------------------------------------------------------------*/

/** @} */
/** @} */

附uip自带的uip_arp.h之源文件

复制代码
#include "uip.h"
#include "uipopt.h"

extern struct uip_eth_addr uip_ethaddr;

//uip_eth_hdr型结构:目的地址占6个字节,源地址占6个字节,报文类型占1个字节
struct uip_eth_hdr {
  struct uip_eth_addr dest; //uip_eth_addr结构成员是字节型数组addr[6],存放目的MAC地址
  struct uip_eth_addr src;  //uip_eth_addr结构成员是字节型数组addr[6],存放源MAC地址
  u16_t type;
	//报文类型
	//0x0800表示后面跟着的是IPV4数据包;
	//0x0806表示后面跟着的是ARP数据包;
	//0x86dd表示后面跟着的是IPV6数据包;
};

#define UIP_ETHTYPE_ARP 0x0806  //ARP请求包类型
#define UIP_ETHTYPE_IP  0x0800  //IPV4数据包类型
#define UIP_ETHTYPE_IP6 0x86dd  //IPv6包类型

//ARP结构
struct arp_hdr 
{
  struct uip_eth_hdr ethhdr;//uip_eth_hdr型结构:目的地址占6个字节,源地址占6个字节,报文类型占1个字节
  u16_t hwtype; //数据链路层的类型,若是以太网,值是0x0001
  u16_t protocol;//通信协议类型,若是ipv4,值是0x0800
  u8_t hwlen;    //硬件字段大小(单位字节),若是mac地址就是6
  u8_t protolen; //协议字段大小(单位字节),若是ip地址就是4
  u16_t opcode;  //ARP消息的类型,ARP请求是1,ARP回复是2,RARP请求是3,RARP回复是4
  struct uip_eth_addr shwaddr;  //发送方的6字节mac地址
  u16_t sipaddr[2];             //发送方的4字节ip地址
  struct uip_eth_addr dhwaddr;  //接收方的6字节mac地址
  u16_t dipaddr[2];             //接收方的4字节ip地址
};
//发送数据:0xFF  0xFF  0xFF  0xFF  0xFF  0xFF  0x00  0x08  0xDC  0x11  0x11  0x02  0x08  0x06  0x00  0x01  0x08  0x00  0x06  0x04  0x00  0x01  0x00  0x08  0xDC  0x11  0x11  0x02  0xC0  0xA8  0x01  0x11  0x00  0x00  0x00  0x00  0x00  0x00  0xC0  0xA8  0x01  0xBE
//接收方MAC地址:0xFF  0xFF  0xFF  0xFF  0xFF  0xFF
//发送方MAC地址:0x00  0x08  0xDC  0x11  0x11  0x02
//ARP请求包类型:0x08  0x06
//数据链路层的类型hwtype是以太网:0x00  0x01
//通信协议类型protocol是ipv4:0x08  0x00
//mac地址长度hwlen是:0x06
//ip地址长度protolen:0x04
//ARP消息的类型opcode:0x00  0x01
//发送方的6字节mac地址:0x00  0x08  0xDC  0x11  0x11  0x02
//发送方的4字节ip地址:0xC0  0xA8 0x01  0x11
//接收方的6字节mac地址:0x00  0x00  0x00  0x00  0x00  0x00
//接收方的4字节ip地址:0xC0  0xA8  0x01  0xBE

//以太网结构
struct ethip_hdr 
{
  struct uip_eth_hdr ethhdr;
	//目标MAC地址6个字节,源MAC地址6个字节,以太网类型2字节
  //P报文头部 IP header
  u8_t  vhl;//版本与首部长度
  u8_t  tos;//服务类型
  u8_t  len[2];//IP报文总长度2个字节
  u8_t  ipid[2];//标识,2字节,用于分片重组
  u8_t  ipoffset[2];//标志与片偏移,2字节,包含DF/MF标志和偏移量
  u8_t  ttl;//生存时间
  u8_t  proto;//协议:1字节,如6表示TCP,17表示UDP
  u16_t ipchksum;//检验和
  u16_t srcipaddr[2];//发送方IP地址,4字节
  u16_t destipaddr[2];//接收方IP地址,4字节
};
//发送数据:0xB4 . 0x99 Y 0xEC  0x1E  0x00  0x08  0xDC  0x11  0x11  0x02  0x08  0x00 E 0x00  0x00 ( 0x00  0x02  0x00  0x00 @ 0x06  0xF6  0xAE  0xC0  0xA8  0x01  0x11  0xC0  0xA8  0x01  0xBE  0x00 P:} 0x00  0x00  0x02  0x81  0xEA  0x8B  0xE0 0P 0x14  0xFA  0xF0 ( 0xB5  0x00  0x00
//接收方MAC地址:0xB4 0x2E 0x99 0x59 0xEC  0x1E
//发送方MAC地址:0x00  0x08  0xDC  0x11  0x11  0x02
//IPV4数据包类型:0x08  0x00
//版本与首部长度vhl:0x45
//服务类型tos:0x00
//IP报文总长度len[2]:0x00 0x28
//标识ipid[2]:0x00  0x02
//标志与片偏移ipoffset[2]:0x00  0x00
//生存时间ttl:0x40
//TCP协议:0x06
//检验和ipchksum:0xF6  0xAE
//发送方IP地址:0xC0  0xA8  0x01  0x11
//接收方IP地址:0xC0  0xA8  0x01  0xBE
//0x00 0x50 0x3A 0x7D 0x00  0x00  0x02  0x81  0xEA  0x8B  0xE0 0P 0x14  0xFA  0xF0 0x28 0xB5  0x00  0x00

//接收方MAC地址:0xB4 0x2E 0x99 0x59 0xEC  0x1E
//发送方MAC地址:0x00  0x08  0xDC  0x11  0x11  0x02
//IPV4数据包类型:0x08  0x00
//版本与首部长度vhl:0x45
//服务类型tos:0x00
//IP报文总长度len[2]:0x00 0x2C
//标识ipid[2]:0x00  0x03
//标志与片偏移ipoffset[2]:0x00  0x00
//生存时间ttl:0x40
//TCP协议:0x06
//检验和ipchksum:0xF6  0xA9
//发送方IP地址:0xC0  0xA8  0x01  0x11
//接收方IP地址:0xC0  0xA8  0x01  0xBE
//0x00 0x50 0x3A 0xE0  0x00  0x00  0x05 0x78 0x49 0xFC  0xEC 0x36 0x60 0x12  0x0F  0xCA  0x83 ; 0x00  0x00  0x02  0x04  0x0F  0xCA

#define ARP_REQUEST   1  //ARP请求
#define ARP_REPLY     2  //ARP回复

#define ARP_HWTYPE_ETH 1  //数据链路层的类型,若是以太网,值是0x0001

struct arp_entry 
{
  u16_t ipaddr[2];
  struct uip_eth_addr ethaddr; //uip_eth_addr结构成员为字节型数组addr[6]
  u8_t time;
};
extern struct arp_entry arp_table[UIP_ARPTAB_SIZE];

void uip_arp_init(void);


#define uip_arp_ipin()

void uip_arp_arpin(void);
void uip_arp_out(void);
void uip_arp_timer(void);


#define uip_setethaddr(eaddr) do {uip_ethaddr.addr[0] = eaddr.addr[0]; \
                              uip_ethaddr.addr[1] = eaddr.addr[1];\
                              uip_ethaddr.addr[2] = eaddr.addr[2];\
                              uip_ethaddr.addr[3] = eaddr.addr[3];\
                              uip_ethaddr.addr[4] = eaddr.addr[4];\
                              uip_ethaddr.addr[5] = eaddr.addr[5];} while(0)
相关推荐
深蓝海拓18 分钟前
PySide6从0开始学习的笔记(二十六) 重写Qt窗口对象的事件(QEvent)处理方法
笔记·python·qt·学习·pyqt
qqssss121dfd21 分钟前
STM32H750XBH6的ETH模块移植LWIP
网络·stm32·嵌入式硬件
酣大智1 小时前
参考模型--物理层
网络
星火开发设计2 小时前
C++ 预处理指令:#include、#define 与条件编译
java·开发语言·c++·学习·算法·知识
B2_Proxy2 小时前
IP 来源合规性,正在成为全球业务的隐性门槛
网络·爬虫·网络协议·安全
想放学的刺客2 小时前
单片机嵌入式试题(第27期)设计可移植、可配置的外设驱动框架的关键要点
c语言·stm32·单片机·嵌入式硬件·物联网
BackCatK Chen2 小时前
第 1 篇:软件视角扫盲|TMC2240 软件核心特性 + 学习路径(附工具清单)
c语言·stm32·单片机·学习·电机驱动·保姆级教程·tmc2240
兆龙电子单片机设计2 小时前
【STM32项目开源】STM32单片机多功能电子秤
stm32·单片机·开源·毕业设计·智能家居
深蓝海拓3 小时前
PySide6从0开始学习的笔记(二十五) Qt窗口对象的生命周期和及时销毁
笔记·python·qt·学习·pyqt
理人综艺好会3 小时前
Web学习之用户认证
前端·学习