3.实现ETHERNET TCPIP是一系列协议的组合,其中最有名的为TCP协议和IP协议。但是千万不要忽视最底层的协议结构——ETHERNET。ETHERNET包括14个字节,称之为以太网首部,其中前六个字节为目标MAC地址,紧着的6个字节为源MAC地址,最后的两个字节为协议类型。以太网的实现通信时必须要知道双方的MAC地址,发送方不明确接收方的地址便通过ARP协议寻找目标MAC地址,如果依然没有结果则可只能把该报文转发给路由器,让路由器处理该报文。协议类型只需关心两种,0800的IP协议和0806的ARP协议。
ethernet.h中相关宏定义
[cpp] view plaincopy
- // 协议类型 ARP报文
- #define ETH_TYPE_ARP_V 0x0806
- #define ETH_TYPE_ARP_H_V 0x08
- #define ETH_TYPE_ARP_L_V 0x06
- // 协议类型 以太网报文
- #define ETH_TYPE_IP_V 0x0800
- #define ETH_TYPE_IP_H_V 0x08
- #define ETH_TYPE_IP_L_V 0x00
- // 以太网报文头部长度 14
- #define ETH_HEADER_LEN 14
- // 目标MAC地址
- #define ETH_DST_MAC_P 0
- // 源MAC地址
- #define ETH_SRC_MAC_P 6
- // 协议类型
- #define ETH_TYPE_H_P 12
- #define ETH_TYPE_L_P 13
ethernet.c中相关函数
[cpp] view plaincopy
- void eth_generate_header ( BYTE *rxtx_buffer, WORD_BYTES type, BYTE *dest_mac )
- {
- BYTE i;
- // 配置以太网报文 目标MAC地址和源MAC地址
- for ( i=0; i<sizeof(MAC_ADDR); i++)
- {
- rxtx_buffer[ ETH_DST_MAC_P + i ] = dest_mac;
- // avr_mac为全局变量
- rxtx_buffer[ ETH_SRC_MAC_P + i ] = stm32_mac.byte;
- }
- // 配置协议类型 IP报文或ARP报文
- rxtx_buffer[ ETH_TYPE_H_P ] = type.byte.high;
- rxtx_buffer[ ETH_TYPE_L_P ] = type.byte.low;
- }
eth_generate_header函数实现了填充以太网首部的功能,第一个输入参数为发送接收缓冲区。第二个参数为IP类型,在AVRNET项目中传入的参数不是0800的IP协议类型就是0806的ARP协议类型。第三个参数为目标MAC地址,由于本机MAC地址作为了全局变量,可以在函数内部填充到缓冲区中。
4.实现ARP 为了使用最少的代码实现TCPIP功能,假设通过IP发送报文时已经确认了目标的IP地址,设备总是先被动的通过ARP先让PC机知道其MAC地址,这样当PC机发送UDP或者TCP报文时,在报文中已经包含了PC机的IP地址,设备仅需从rxtx_buffer中取出PC机IP地址。ARP协议是一个找邻居的过程,是一个广播找MAC的过程。发出者通过广播报文确认某个IP的MAC地址。ARP首部包括,2字节硬件类型,2字节协议类型,1字节硬件长度,1字节协议长度,2字节操作码,6字节发送者硬件地址,4字节发送者IP地址,6字节目标硬件地址和4字节目标IP地址。
在使用ARP协议时需要注意三点:
第一,操作码分为两种——ARP请求和ARP响应,ARP请求的编码为1,ARP响应的编码为2,先有请求后有响应。第二,发送ARP协议请求时请求方明确对方IP地址,但是不明确对方MAC地址,所以在请求报文中MAC地址全部以0替代。第三,由于不知道对方的MAC地址,所以只能通过广播帧发送以太网数据,所以以太网首部的前6个字节被FF填充。
为了便于ARP功能的实现,在arp.h文件中定义了以下宏定义 |