Day02 手写ARP协议(上)

一、arp的初始化

1、arp的作用是什么?

》》因为我们机器的网卡是工作在数据链路层的二层设备他是不能够识别来自三层网络层的ip地址,他只能识别二层的MAC地址,因此我们需要一种机制来实现ip地址到mac地址的转换,由此引出了arp(address resolution protocal)

2、那么为什么要定义两个层次呢?只使用一个不是更加简单吗?

》》因为在网络层的IP地址是分层的,他按照子网号聚集,目的是为了便于路由和计算,减少路由表的表项,而MAC地址则是平面的,由网卡的生产商在生产网卡的时候写死在网卡的内部的,起作用仅仅是为了标识一个网络内部的不通设备。

3、arp协议的数据包的数据结构如下,在网络上一般存在多个表项,但是为了简单起见,我们只使用一个表项,如果需要多个表项的话,就直接使用数组来扩充就好了。

1)首先我们在xnet_tiny.h中添加IP地址的结构定义(因为后面会使用到):

#define XNET_IPV4_ADDR_SIZE 4	//IP地址长度
//IP地址
typedef union _xipaddr_t {
	uint8_t array[XNET_IPV4_ADDR_SIZE];	 //使用数组的形式来存储ip
	uint32_t addr;						 //32位的IP地址
}xipaddr_t;

2)在xnet_tiny.h中定义arp数据包的数据结构:

//定义arp表项
typedef struct _xarp_entry_t {
	xipaddr_t ipaddr;						//ip地址
	uint8_t macaddr[XNET_MAC_ADDR_SIZE];	//mac地址
	uint8_t state;							//状态
	uint16_t tmo;							//超时时间
	uint8_t retry_cnt;						//重试次数
}xarp_entry_t;

3)初始化arp

在xnet_tiny.c中定义:

static xarp_entry_t arp_entry;    // 节省内存,只使用一个ARP表项

然后在添加xarp_init()函数,目前就添加一个功能:将arp表项的状态设置为XARP_ENTRY_FREE

其中:#define  XARP_ENTRY_FREE 0    用于标识arp空闲状态

然后在xnet_init()函数中调用xarp_init()即可

二、无回报ARP的生成

1、为什么需要无回报的ARP协议?

》》为了减少网络中的请求查询的包的数量,每个节点上线时,网卡会被启动,此时操作系统的协议栈会自动地向网络当中发送一个广播包(目的MAC地址为全1的特殊预留地址),这样一个子网内的其他设备收到广播包之后就会缓存在自己的ARP表中,到时候需要通信时就不需要向网络中广播了,直接就可以通过交换机瞬间和目的地址连通(switch),当然考虑到节点以及ip的动态性,所以arp表项并不会永久缓存在主机当中,每个表项都有一个生存时间(tmo)

2、如何来实现这种功能呢?

》》当我们所在的节点上线的时候,就向网络当中发送一个广播的arp包,使用sender_mac和sender_ip来标识自己,同时将target_ip也设置为自己的ip,而target_mac全部填0,这意味着接收方收到这个包的时候不需要发送ACK之类的确认消息,只需要拿到arp包之后解封装,得到相关节点的mac,ip信息缓存在自己的arp表当中就可以了

我们定义一个函数xnet_err_t xarp_make_request(const xipaddr_t * ipaddr):

通过将参数ipaddr设置为自己的IP地址来实现无回报的ARP

3、我们首先通过抓包工具分析一下无回报的arp包:

在虚拟机上将网卡先停用然后再启用,此时在wireshark当中就可以查看到一个无回报的arp包,这个包就是虚拟机的Windows网卡启用时会告诉网络上的计算机我的ip与mac地址

1)在xnet_tiny.h中添加arp包的数据结构的定义:

//arp包数据结构
typedef struct _xarp_packet_t {
	uint16_t hw_type, pro_type;	//硬件类型,协议类型
	uint8_t hw_len, pro_len;//硬件地址长度,协议长度
	uint16_t opcode;	//操作码(请求/响应)
	uint8_t sender_mac[XNET_MAC_ADDR_SIZE];//源mac
	uint8_t sender_ip[XNET_IPV4_ADDR_SIZE];//源IP
	uint8_t target_mac[XNET_MAC_ADDR_SIZE];//目标mac
	uint8_t target_ip[XNET_IPV4_ADDR_SIZE];// 目标ip
}xarp_packet_t;

《警告》:一定要把这个数据结构定义在#pragma pack(1)与#pragma pack()之间

2)接下来我们来定义arp协议中的字段:

#define XARP_HW_ETHER               0x1         // 以太网
#define XARP_REQUEST                0x1         // ARP请求包
#define XARP_REPLY                  0x2         // ARP响应包

 同时添加,visual studio本机网卡IP:

#define XNET_CFG_NETIF_IP	{192, 168, 254, 2}	//visual studio本机网卡IP

这里需要明确的是我们之前使用的192.168.254.1不是给程序的协议栈使用的,而是我们的本地物理机器的网卡IP,我们使用192.168.254.1这个网卡来实现网络的发包收包

然后在xnet_tiny.c中,定义visual studio网卡的IP地址以及广播地址:

static const xipaddr_t netif_ipaddr = XNET_CFG_NETIF_IP;
static const uint8_t ether_broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

此处的IP和MAC地址我们使用一个常量来修饰,即没有把IP地址写死,又不会被用户随意修改

3)实现arp请求函数xarp_make_request():

//主要就是去填写arp请求的一些字段,然后丢给ethernet_out_to去处理(通过以太网发包)
xnet_err_t xarp_make_request(const xipaddr_t* ipaddr) {
	xarp_packet_t* arp_packet;
	xnet_packet_t* packet = xnet_alloc_for_send(sizeof(xarp_packet_t));
	arp_packet = (xarp_packet_t*)packet->data;

	//发包的时候注意要大小端的转换
	arp_packet->hw_type = swap_order16(XARP_HW_ETHER);
	arp_packet->pro_type = swap_order16(XNET_PROTOCOL_IP);
	arp_packet->hw_len = XNET_MAC_ADDR_SIZE;
	arp_packet->pro_len = XNET_IPV4_ADDR_SIZE;
	arp_packet->opcode = swap_order16(XARP_REQUEST);
	//几个数组使用mencpy,targetmac使用memset全部置零(#include<string.h>)
	//数组名就是指针(地址)
	memcpy(arp_packet->sender_mac, netif_mac, XNET_MAC_ADDR_SIZE);
	memcpy(arp_packet->sender_ip, netif_ipaddr.array, XNET_IPV4_ADDR_SIZE);
	memset(arp_packet->target_mac, 0, XNET_MAC_ADDR_SIZE);
	memcpy(arp_packet->target_ip, ipaddr->array, XNET_IPV4_ADDR_SIZE);

	return ethernet_out_to(XNET_PROTOCOL_ARP, ether_broadcast, packet);	//发的是一个完整以太网的包

}

该函数的主要功能:填写arp请求的一些字段,然后丢给ethernet_out_to去处理(通过以太网发包)

参数含义:const xipaddr_t* ipaddr,代表要发送的主机的ip地址

具体的步骤:

①通过alloc_for_send()来分配一个包

②定义一个arp包的指针指向起始的数据区域

③填充arp的内容

注意:填充字段的时候一定要注意大小端的转换,因为网络中机器一般都采用大端存储的方式,而我们本地的操作系统采用的是小端存储。

④通过return ethernet_out_to()来将我们生成的arp包通过以太网的接口发送出去

最后在ethernet_init()中加入xarp_make_request()即可:

return xarp_make_request(&netif_ipaddr);

使用Wireshark查看运行结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值