arp
What
ARP(Address Resolution Protocol ) ,即地址解析协议,负责把目的主机的IP 地址解析成目的MAC地址。当发送者 知道目的主机的IP地址之后, 就可以使用这个IP地址去解析对方的MAC地址。
Why
在发送者给其他的网络设备发送数据的时, 是以数据帧的形式发送的,数据帧被网卡解析成电信号或者光信号传递到对端。
数据帧是由源MAC地址和目的MAC地址组成的,如果发送者只知道目的主机的IP地址, 不知道目的主机的MAC地址, 就不能把这个数据包转化成数据帧发走,而ARP 协议就是负责地址解析的, 使用目的主机IP地址来解析对方的MAC地址。
如果发送者和接收者在同一个网络内,arp解析的就是接收者的MAC地址。如果发送者和接收者不在同一个网络内, arp解析的就是这个网络内网关的接口MAC地址。
How
在网络设备中会存在一个ARP缓存表,这个缓存表记录着这个IP 地址和MAC地址的映射关系,可以在设备终端使用arp -a 来查看本地的arp 缓存表的缓存信息。
arp -a
- 在20240717-2中,就是因为Windows的arp表中没有虚拟机的记录,所以无法发送消息。
PC 1 只知道PC3 的IP地址是10.1.1.3, 但是不知道PC3的MAC, 现在想获取PC3 的MAC地址:
发送者PC1:PC 1 会发送一个广播帧, 源IP和源MAC是PC1, 目的IP是PC3,目的MAC为FF-FF-FF-FF-FF-FF, 这个帧是广播发送的, 该网络内所有主机都会接收到,这个报文的载荷内容是一个ARP请求报文,意思是我的MAC地址是MAC1,我想给10.1.1.3发送数据, 谁是10.1.1.3, 请回复一下我。
非目的主机PC2:由于这个广播帧网络内主机都可以收到,PC2 收到这个帧, 提取IP地址, 发现, 它要找的IP地址是10.1.1.3, 我的IP地址是10.1.1.2, 这个数据包不是发给我的, 丢弃, 不回复。
目的主机PC3:PC 3 收到这个广播数据帧后,拆开数据帧, 提取IP地址, 发现和我本地IP地址是一样的, 这个数据包就是发给我的, 然后再拆, 提取报文后, 了解到对方要请求我自己的MAC地址,然后就打包一个ARP的应答报文,源IP和源MAC都是自己的,目的IP和目的MAC是对方的,单播的发送给接收者,这样PC1 就有了PC3的MAC地址。
arp header
![[Pasted image 20240719100953.png]]
- 抓包如下
硬件类型:值为1表示以太网
协议类型:映射的地址地址类型
硬件地址长度:MAC的长度
协议地址长度:IP地址长度
OP:数据包类型,req为1,rep为2
备注:虽然ARP和IP协议都属于网络层的协议,但是从分层的结构来看,ARP处于网络层的最底层
代码解读
代码越来越多了,放在一个文件里显然不合适,分文件写,后面也只把当天相关的放在文档里。
修改IP
#define MAKE_IPV4_ADDR(a, b, c, d) (a + (b << 8) + (c << 16) + (d << 24))
uint32_t nLocalIp = MAKE_IPV4_ADDR(192, 168, 0, 200);
这里可以自定义一个IP(要在网段内)然后获取他的网络字节序。
为什么IP可以自定义?本来就是抽象出来的一个东西,为什么不可以自定义。
ARP包组织
uint32_t nLocalIp = MAKE_IPV4_ADDR(192, 168, 0, 200);
/*
* 函数名称: ln_arp_encode
* 作 者: 黄彦杰 Lenn
* 设计日期: 2024-07-19
* 功能描述: 组织arp数据包
* 返 回 值: 0
*/
static int ln_arp_encode(uint8_t* pktbuf, uint8_t* dst_mac, uint32_t src_ip, uint32_t dst_ip) {
struct rte_ether_hdr* ehdr = (struct rte_ether_hdr*)pktbuf;
rte_memcpy(ehdr->s_addr.addr_bytes, nSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy(ehdr->d_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
ehdr->ether_type = htons(RTE_ETHER_TYPE_ARP);
struct rte_arp_hdr* arphdr = (struct rte_arp_hdr*)(ehdr + 1);
arphdr->arp_hardware = htons(1);
arphdr->arp_protocol = htons(RTE_ETHER_TYPE_IPV4);
arphdr->arp_hlen = RTE_ETHER_ADDR_LEN;
arphdr->arp_plen = sizeof(uint32_t);
arphdr->arp_opcode = htons(2);
rte_memcpy(arphdr->arp_data.arp_sha.addr_bytes, nSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy(arphdr->arp_data.arp_tha.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
// rte_memcpy(&arphdr->arp_data.arp_sip, &src_ip, sizeof(uint32_t));
// rte_memcpy(&arphdr->arp_data.arp_tip, &dst_ip, sizeof(uint32_t));
arphdr->arp_data.arp_sip = src_ip;
arphdr->arp_data.arp_tip = dst_ip;
return 0;
}
struct rte_mbuf* ln_arp_send(struct rte_mempool* mbuf_pool, uint8_t* dst_mac, uint32_t src_ip, uint32_t dst_ip) {
int total_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_arp_hdr);
struct rte_mbuf* mbuf = rte_pktmbuf_alloc(mbuf_pool);
if (!mbuf) {
rte_exit(EXIT_FAILURE, "Alloc arp mbuf failed");
}
mbuf->pkt_len = total_len;
mbuf->data_len = total_len;
uint8_t* pktdata = rte_pktmbuf_mtod(mbuf, uint8_t*);
ln_arp_encode(pktdata, dst_mac, src_ip, dst_ip);
return mbuf;
}
- 参考上面的数据头知识和前面的udp知识。
数据包接收
if (ehdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP)) {
struct rte_arp_hdr* arphdr = (struct rte_arp_hdr*)(ehdr + 1);
struct in_addr addr;
addr.s_addr = arphdr->arp_data.arp_sip;
printf("---> arp pkt src %s, ", inet_ntoa(addr));
addr.s_addr = arphdr->arp_data.arp_tip;
printf("dst %s\n", inet_ntoa(addr));
if (arphdr->arp_data.arp_tip == nLocalIp) {
struct rte_mbuf* arpbuf = ln_arp_send(mbuf_pool, arphdr->arp_data.arp_sha.addr_bytes,
arphdr->arp_data.arp_tip, arphdr->arp_data.arp_sip);
rte_eth_tx_burst(nDevPortId, 0, &arpbuf, 1);
rte_pktmbuf_free(arpbuf);
rte_pktmbuf_free(mbufs[i]);
}
continue;
}
- 判断是不是发给本机的
arp
包,如果是,发送一个回复包,让对方知道自己的IP
和MAC
。
获取网卡MAC
rte_eth_macaddr_get(nDevPortId, (struct rte_ether_addr*)nSrcMac);
- 在昨天的代码中,使用udp带来的的
dst_mac
来给nSrcMac
赋值。但是先有ARP的广播,才会收到数据包,所以使用这个函数直接get到网卡的MAC地址。
实验结果
主要更新部分
#if ARP_ENABLE
#define MAKE_IPV4_ADDR(a, b, c, d) (a + (b << 8) + (c << 16) + (d << 24))
extern uint32_t nLocalIp;
/*
* 函数名称: ln_arp_send
* 作 者: 黄彦杰 Lenn
* 设计日期: 2024-07-19
* 功能描述: arp数据包发送
* 返 回 值: 存储arp数据包的一段内存
*/
struct rte_mbuf* ln_arp_send(struct rte_mempool* mbuf_pool, uint8_t* dst_mac, uint32_t src_ip, uint32_t dst_ip);
#endif
#if ARP_ENABLE
uint32_t nLocalIp = MAKE_IPV4_ADDR(192, 168, 0, 200);
/*
* 函数名称: ln_arp_encode
* 作 者: 黄彦杰 Lenn
* 设计日期: 2024-07-19
* 功能描述: 组织arp数据包
* 返 回 值: 0
*/
static int ln_arp_encode(uint8_t* pktbuf, uint8_t* dst_mac, uint32_t src_ip, uint32_t dst_ip) {
struct rte_ether_hdr* ehdr = (struct rte_ether_hdr*)pktbuf;
rte_memcpy(ehdr->s_addr.addr_bytes, nSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy(ehdr->d_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
ehdr->ether_type = htons(RTE_ETHER_TYPE_ARP);
struct rte_arp_hdr* arphdr = (struct rte_arp_hdr*)(ehdr + 1);
arphdr->arp_hardware = htons(1);
arphdr->arp_protocol = htons(RTE_ETHER_TYPE_IPV4);
arphdr->arp_hlen = RTE_ETHER_ADDR_LEN;
arphdr->arp_plen = sizeof(uint32_t);
arphdr->arp_opcode = htons(2);
rte_memcpy(arphdr->arp_data.arp_sha.addr_bytes, nSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy(arphdr->arp_data.arp_tha.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
// rte_memcpy(&arphdr->arp_data.arp_sip, &src_ip, sizeof(uint32_t));
// rte_memcpy(&arphdr->arp_data.arp_tip, &dst_ip, sizeof(uint32_t));
arphdr->arp_data.arp_sip = src_ip;
arphdr->arp_data.arp_tip = dst_ip;
return 0;
}
struct rte_mbuf* ln_arp_send(struct rte_mempool* mbuf_pool, uint8_t* dst_mac, uint32_t src_ip, uint32_t dst_ip) {
int total_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_arp_hdr);
struct rte_mbuf* mbuf = rte_pktmbuf_alloc(mbuf_pool);
if (!mbuf) {
rte_exit(EXIT_FAILURE, "Alloc arp mbuf failed");
}
mbuf->pkt_len = total_len;
mbuf->data_len = total_len;
uint8_t* pktdata = rte_pktmbuf_mtod(mbuf, uint8_t*);
ln_arp_encode(pktdata, dst_mac, src_ip, dst_ip);
return mbuf;
}
#endif
#if ARP_ENABLE
if (ehdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP)) {
struct rte_arp_hdr* arphdr = (struct rte_arp_hdr*)(ehdr + 1);
struct in_addr addr;
addr.s_addr = arphdr->arp_data.arp_sip;
printf("---> arp pkt src %s, ", inet_ntoa(addr));
addr.s_addr = arphdr->arp_data.arp_tip;
printf("dst %s\n", inet_ntoa(addr));
if (arphdr->arp_data.arp_tip == nLocalIp) {
struct rte_mbuf* arpbuf = ln_arp_send(mbuf_pool, arphdr->arp_data.arp_sha.addr_bytes,
arphdr->arp_data.arp_tip, arphdr->arp_data.arp_sip);
rte_eth_tx_burst(nDevPortId, 0, &arpbuf, 1);
rte_pktmbuf_free(arpbuf);
rte_pktmbuf_free(mbufs[i]);
}
continue;
}
#endif
rte_eth_macaddr_get(nDevPortId, (struct rte_ether_addr*)nSrcMac);
评论