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

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240719094420.png

  • 在20240717-2中,就是因为Windows的arp表中没有虚拟机的记录,所以无法发送消息。

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240719095439.png

PC 1 只知道PC3 的IP地址是10.1.1.3, 但是不知道PC3的MAC, 现在想获取PC3 的MAC地址:

  1. 发送者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, 请回复一下我。

  2. 非目的主机PC2:由于这个广播帧网络内主机都可以收到,PC2 收到这个帧, 提取IP地址, 发现, 它要找的IP地址是10.1.1.3, 我的IP地址是10.1.1.2, 这个数据包不是发给我的, 丢弃, 不回复。

  3. 目的主机PC3:PC 3 收到这个广播数据帧后,拆开数据帧, 提取IP地址, 发现和我本地IP地址是一样的, 这个数据包就是发给我的, 然后再拆, 提取报文后, 了解到对方要请求我自己的MAC地址,然后就打包一个ARP的应答报文,源IP和源MAC都是自己的,目的IP和目的MAC是对方的,单播的发送给接收者,这样PC1 就有了PC3的MAC地址。

arp header

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240719100639.png

![[Pasted image 20240719100953.png]]

  • 抓包如下

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240719101356.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包,如果是,发送一个回复包,让对方知道自己的IPMAC

获取网卡MAC

rte_eth_macaddr_get(nDevPortId, (struct rte_ether_addr*)nSrcMac);
  • 在昨天的代码中,使用udp带来的的dst_mac来给nSrcMac赋值。但是先有ARP的广播,才会收到数据包,所以使用这个函数直接get到网卡的MAC地址。

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240719145203.png

实验结果

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240719141735.png

主要更新部分

#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);

项目地址