arp.h在这里定义了以太网帧头,arp协议头与ip帧头。
先是一些类型的定义:
typedef unsigned short u16;
typedef unsigned char u8;
以太网头的结构体
typedef struct eth_hdr
{
/*目的MAC地址*/
u8 d_mac[6];
/*源MAC地址*/
u8 s_mac[6];
/*上层协议的类型*/
u16 type;
}ETH_HDR;
arp头的结构体定义
typedef struct arp_hdr
{
/*以太网头*/
ETH_HDR ethhdr;
/*硬件地址的类型,它的值为1表示以太网地址*/
u16 hwtype;
/*上层协议的类型*/
u16 protocol;
/*硬件地址的长度,通常为6*/
u8 hwlen;
/*协议地址的长度,通常为4*/
u8 protolen;
/*操作类型,1表示ARP请求,2表示ARP应答*/
u16 opcode;
/*发送方的MAC地址*/
u8 smac[6];
/*发送方的ip地址*/
u8 sipaddr[4];
/*接收方的MAC地址,在arp请求包里通常设为0*/
u8 dmac[6];
/*接受方的ip地址*/
u8 dipaddr[4];
}IP_HDR
同时arp.h中还用extern声明了一些全局变量,这些全局变量实际在dm9000的网卡驱动中定义,具体参见(在文章末尾处定义)http://blog.csdn.net/u011412588/article/details/53549522
/*pc机的MAC地址,初始值全部为全1*/
extern u8 host_mac_addr[6];
/*开发板的MAC地址*/
extern u8 mac_addr[6];
/*开发板的ip地址,主机号可以随意,但是要与pc机的ip处于同一个网段*/
extern u8 ip_addr[4];
/*pc机的ip地址*/
extern u8 host_ip_addr[4];
/*接受到数据包的长度*/
extern u16 packet_len;
/*用来存储arp请求包*/
ARP_HDR arpbuf;
/*arp协议的type编号*/
#define PROTO_ARP 0x086
/*ip协议的type编号*/
#define PROTO_IP 0x0800
/*udp协议的type编号*/
#define PROTO_UDP 0x11
在上一篇博文mini2440_dm9000网卡驱动中,实现了网卡的发送与接受两个API,这篇文章将会使用其中的api进行数据的发送与接收。
arp_request函数,用于构建arp请求包,需要注意的是,因为数据需要在网络上传输,凡是两个字节的字段都需要装换成网络字节序,这里提供了一个转换的宏
#define HON(n) ((((u16)((n)&0xff))<<8)|(((n)&0xff00)>>8))
void arp_request()
{
/*装填以太网帧头的目的MAC地址,这里被设为全1*/
memcpy(arpbuf.ethhdr.d_mac,host_mac_addr,6);
/*装填以太网帧头的源MAC地址*/
memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);
/*设置上层协议的类型*/
arpbuf.ethhdr.type = HON(PROTO_ARP);
/*设置硬件地址类型,需要转网络字节序*/
arpbuf.hwtype = HON(1);
/*设置上层协议的类型*/
arpbuf.protocol = HON(PROTO_IP);
/*设置硬件地址的长度*/
arpbuf.hwlen = 6;
/*设置协议地址的长度*/
arpbuf.protolen = 4;
/*设置arp包的操作类型*/
arpbuf.opcode = HON(1);
/*设置源MAC地址*/
memcpy(arpbuf.smac,mac_addr,6);
/*设置源ip地址*/
memcpy(arpbuf.sipaddr,ip_addr,4);
/*设置目的MAC地址*/
memcpy(arpbuf.dmac,host_mac_addr,6);
/*设置目的ip地址*/
memcpy(arpbuf.dipaddr,host_ip_addr,4);
/*以太网帧头长度为14(byte),arp头长度为28(byte)*/
packet_len = 14+28;
/*调用网卡的发送api*/
dm9000_tx(&arpbuf,packet_len);
}
下面介绍的是arp_process函数,用于处理其他机器发来的arp请求或者接受arp回应
u8 arp_process(u8 * buf,u32 len)
{
u32 i;
APR_HDR *arp_p=(ARP_HDR *)buf;
/*如果数据包小于28则不可能是arp包*/
if(len < 28)
return 0;
/*此处需要把原来的包内的网络字节序转换成主机字节序*/
switch(HON(arp_p->opcode))
{
/*若为1,arp请求包,回以应答包*/
case 1:
memcpy(arpbuf.ethhdr.d_mac,arp_p->ethhdr.s_mac,6);
memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);
arpbuf.ethhdr.type = HON(PROTO_ARP);
arpbuf.hwtype = HON(1);
arpbuf.protocol = HON(PROTO_IP);
arpbuf.hwlen = 6;
arpbuf.protolen = 4;
/*op设为2,回应arp请求*/
arpbuf.opcode = HON(2);
memcpy(arpbuf.smac,mac_addr,6);
memcpy(arpbuf.sipaddr,ip_addr,4);
memcpy(arpbuf.dmac,arp_p->smac,6);
memcpy(arpbuf.dipaddr,arp_p->sipaddr,4);
packet_len = 14+28;
dm9000_tx(&arpbuf,packet_len);
break;
/*接收到arp回应包*/
case 2:
memcpy(host_mac_addr,arp_p->smac,6);
printf("host_mac_addr is:");
for(i=0;i<6;i++)
printf("%02x ",host_mac_addr[i]);
printf("\n");
memcpy(host_ip_addr,arp_p->sipaddr,4);
printf("host_ip_addr is:");
for(i=0;i<3;i++)
printf("%03d ",host_ip_addr[i]);
printf("\n");
break;
default:
break;
}
}
void net_handle(u8 * buf,_u32 len)函数在上篇博文中已经出现过,用途是根据以太网帧头(ETH_HDR)中的type确定数据包的协议,并调用相应的处理函数
void net_handle(u8 *buf,u32 len)
{
ETH_HDR *p = (ETH_HDR*)buf;
/*需要网络字节序转主机字节序*/
switch(HON(p->type))
{
case PROTO_IP:
ip_process(buf,len);
break;
case PROTP_ARP:
arp_process(buf,len);
break;
default:
break;
}
}
其中的ip_process()函数声明为:
void ip_process(u8 *buf,u32 len);
作用是解析基于ip协议的数据包,并调用相应的处理函数,将会在下篇博文mini2440_TFTP客户端移植中给出实现。