轻量级TCP/IP实现包Lwip之ARP协议

1.首先看一下Lwip中函数调用关系图

轻量级TCP/IP实现包Lwip之ARP协议_第1张图片
函数调用关系图.jpg

ARP(Adrress ResolutionProtocol,地址解析协议),属于TCPIP协议族网络互联层协议,主要负责网络接口层与IP层之间地址映射和转换,因为协议是分层,各层都有自己的任务和算法。比如在以太网中,主要是解决的局域网通信问题,在网络中主机较少的情况,采用广播的方式就可以解决,但是当局域网主机较多的情况下,这种广播式通信就会因为共用介质的问题造成部分链路的闲置,如下图所示:


轻量级TCP/IP实现包Lwip之ARP协议_第2张图片
局域网.png

当节点A和B进行通信时,因为电路共享的原因,C和D无法就无法进行通信,除非在B和C之间有个节点能够有个“智能开关”,当检测到A和B通信时,自动与断开C节点, C和D就可正常进行通信。其实,这个“智能开关”就是以太网中的网桥和交换机,但是这种情况需要每台主机直接与交换机每个端口相连,交换机会自动建立起端口和主机MAC地址之间的关系。在共享同一条线路的情况下,就会出现介质争用的情况,以太网协议中采用“载波侦听“和”冲突检测”的方式,主机在每次发送数据前会检测线路是否是空闲,如果不是就等待一定的时间,再次进行检测,如果线路空闲就发送数据包,发送出去就不会管了。

2.网络接口层

网络接口层主要解决的是具体物理链路通信的问题,比如现在的采用RJ45接口以太网卡, 一根TX-线、一根TX+、一根RX-线、一根RX+线。TX-、TX+主要用于数据的发送,RX-、RX+主要用于数据的接口,这是接口的规定。基于这种连接方式的物理层数据通信协议有:RS232、RS485和IEEE 802.3等,都是异步通信协议(双方拥有自己独立的时钟,传输线路较长时,电流信号在传输过程中会因为阻抗的原因产生延迟和变形)。

3.IEEE802.3

IEEE802.3是以太网正式标准,对通信的数据格式进行如下图所示的规定:


以太网帧格式.JPG

①前同步码:主要用于同步时钟信号。在异步通信中,由于通信双方采用自己的时钟,同一时刻信号的相位就无法确定,为了确保双方信号的相位保持一致,就需要锁相环对电信号进行分析,使通信双方的接收的信号保持一致。因此前同步码就是用于调整时钟,确保发送和接收双方的时钟同步。前同步码值固定为:
二 进 制:10101010 10101010 10101010 10101010 10101010 10101010 10101010
十六进制:AA AA AA AA AA AA AA
②SFD:帧首界定符。本质上也是属于同步码,只不过用于指示其之后的数据将是真正的有效数据,其值为10101011(0x AB),前同步码完成时钟同步后,如果检测到此信号时,硬件就开始切换内部的工作状态机,开始启动有效数据接口的过程。
③目的地址:数据发送方的地址,也叫作MAC地址,6个字节共48位。因为共享介质的原因,在传输过程中需要地址来进行标识,否则,没有谁知道数据是要给谁的。在以前的Hub网络中,每一个设备发出的数据包都会送达到每一个连接设备,设备在接收到数据包之后发现MAC地址不是自己的就直接进行丢弃,显然,数据通信采用的是一种广播式的方式,如前面所说,大部分链路出现闲置的情况。此时MAC地址仅仅只是一个地址标识的作用,我们完全可以使用高层的协议来实现。但是随着交换机的普及,这种广播式通信带来的链路闲置情况就得到了有效地遏制,交换机通过学习的方式(就是ARP协议),获得了与其连接的每一台设备的MAC地址,在通信过程中交换机可以自动判断然后定向的发送出去,通过这种交换数据的方式,充分提高了各链路的利用率。
④源地址:数据接收方的地址,用于标识数据接收方的MAC地址。
⑤长度/类型:当这两个字节值小于1518时,其值代表其后“数据和填充”字段的长度;当这两个字节值大于1518时,则表示以太网帧所封装的上层协议类型,如0x0800表示IP协议,0x0806表示ARP。
⑥数据和填充:以太网帧所封装的上层协议数据,最小长度为46字节,最大长度为1500字节。
⑦CRC:以太网帧的差错校验信息。
Lwip对以太网帧头部进行了定义:

/** 
 * struct eth_ addr 
 * 以太网数据帧地址结构体定义 
 */  
struct eth_addr {  
    u8_t addr[ETHARP_HWADDR_LEN];  
}  
/** 
* struct eth_hdr 
* 以太网数据帧首部结构体定义 
*/  
struct eth_hdr {  
    struct eth_addr dest; // 以太网目的地址 6B  
    struct eth_addr src; // 以太网源地址 6B  
    u16_t type; // 帧类型 2B  
} 

4.IP层

IP层解决的主要是广域网通信问题,在主机数量多的情况下,为了解决线路闲置的情况,就需要对整个大的网络进行划分,规定好哪些主机的数据包在指定范围内流动,各个划分的网络由专门的设备(如路由器)连接起来,跨范围通信需要由衔接点设备进行转发。网络协议采用的分层设计思想,因此在IP层就有专门的通信地址(或者说设备唯一标识),这就是我们经常所说的IP地址,IPv4采用的是长度为32位的地址,分为A、B、C、D、E共五类网络。里面还规定单播、广播、组播地址,私网和公网地址。
IP层通信中,数据发送方和数据接收方采用的是32位的IP地址,数据流动在逻辑网络层面。当数据包在物理链路传输过程中,需要采用物理MAC地址。一般地,一个IP地址对应一个MAC地址,但是多个IP地址也可以对应一个MAC地址(绑定多个IP),或者一个IP地址对应多个MAC地址(多路径链路聚合)。这种IP与MAC映射关系的维护就是由ARP和RARP协议负责完成的。

5. ARP报文结构

arp报文格式.JPG
/** 
 * struct etharp_hdr 
 * ARP数据包结构体定义 
 */  
struct etharp _hdr {  
    u16_t hwtype;    // 硬件类型 2B  
    u16_t proto;     // 协议类型 1B  
    u8_t  hwlen;     // 硬件地址长度 1B  
    u8_t  protolen;  // 协议地址长度 1B  
    u16_t opcode;    // 操作字段 2B  
    struct eth_addr shwaddr; // 发送方MAC  
    struct ip_addr2 sipaddr; // 发送方IP  
    struct eth_addr dhwaddr; // 接收方MAC  
    struct ip_addr2 dipaddr; // 接收方IP  
}  

6..ARP工作流程介绍

如下图所示,为ARP工作流程的简要示意图:


轻量级TCP/IP实现包Lwip之ARP协议_第3张图片
arp工作流程图.jpg

下图为Lwip对ARP协议的实现,简要的整理了各函数之间的调用关系:


轻量级TCP/IP实现包Lwip之ARP协议_第4张图片
arp函数调用关系.png

网卡收到数据帧后,通过中断方式通知主程序,或者由主程序主动进行查询。此时会调用到ethernetif_input函数,具体代码如下:
/**
* ethernet_input
* @param struct pbuf  *p 指向接收到的数据包
* @param struct netif *netif 全局变量 当前网络接口指针 不能为空
*/
err_t ethernet_input (struct pbuf *p, struct netif *netif) 
{
    struct eth_hdr *ethhdr;
    u16_t type;
    
    if (p->len <= SIZEOF_ETH_HDR) {
        goto free_and_return;
    }
    
    ethhdr = p->payload;
    type   = htons(ethhdr->type);
    
    switch (type) {
        case ETHTYPE_IP : 
            #if ETHARP_TRUST_IP_MAC
            etharp_ip_input(netif, ip);
            #endif
            if (pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) {
                goto free_and_return;
            } else {
                ip_input(p, netif);
            }
            break;
        case ETHTYPE_ARP :
            etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
            break;
        default:
            goto free_and_return;
    }
    return ERR_OK;
    
    free_and_return:
        pbuf_free(p);
        return ERR_OK;
}

ethernet_input函数与ethernetif_input类似,只不过在处理的更细一些。如果以太网帧头部协议类型为IP,则调用etharp_ip_input函数更新ARP缓存,而后调用ip_input函数提交IP数据包到上层处理,主要是;如果协议类型为ARP,则调用etharp_arp_input函数进行处理。

/** 
* etharp_arp_input 
* @param struct netif *netif 全局变量 当前网络接口指针 不能为空 
* @param struct eth_addr *ethaddr全局变量 当前网络接口MAC地址 
* @param struct pbuf  *p 指向接收到的数据包 
*/  
err_t ethernet_input (struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)  
{  
    struct etharp_hdr *hdr;      // 指向ARP数据包头部  
    struct eth_hdr *ethhdr;      // 指向以太网帧头  
    ip_addr_t sipaddr, dipaddr; // 暂存源IP地址和目的IP地址  
      
      
}  

你可能感兴趣的:(轻量级TCP/IP实现包Lwip之ARP协议)