MacOS 实现ARP报文发送和接收

本文介绍了在MacOS上如何实现ARP报文的发送和接收。通过Berkeley Packet Filter(BPF)来操作原始链路层数据,详细阐述了ARP协议的作用、报文格式,并提供了实现ARP请求和响应的步骤,包括DNS解析、获取本机IP和MAC地址等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继上次 自己实现一个 Ping 命令 之后,尝试进入更底层的网络接口层实现局域网的 ARP 报文收发

ARP 协议概述

ARP(Address Resolution Protocol) 地址解析协议是用来通过网络层地址(IP地址)去寻找数据链路层地址(MAC地址)的网络传输协议.

在以太网(Ethernet)协议中规定,同一局域网中的一台主机要和另一台主机进行直接通信,必须要知道目标主机的 MAC 地址。而在 TCP/IP 协议中,网络层和传输层只关心目标主机的IP地址。这就导致在以太网中使用 IP 协议时,数据链路层的以太网协议接到上层IP协议提供的数据中,只包含目的主机的IP地址。于是需要一种方法,根据目的主机的IP地址,获得其MAC地址。这就是 ARP 协议要做的事情。所谓地址解析(address resolution)就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。另外,当发送主机和目的主机不在同一个局域网中时,即便知道对方的MAC地址,两者也不能直接通信,必须经过路由转发才可以。所以此时,发送主机通过ARP协议获得的将不是目的主机的真实MAC地址,而是一台可以通往局域网外的路由器的MAC地址。于是此后发送主机发往目的主机的所有帧,都将发往该路由器,通过它向外发送。这种情况称为委托ARP或ARP代理(ARP Proxy)。—— 地址解析协议

报文格式

ARP 报文格式

以太网首部: net/ethernet.h

typedef struct  ether_header {
   
	u_char  ether_dhost[ETHER_ADDR_LEN]; /* 目标以太网地址 */
	u_char  ether_shost[ETHER_ADDR_LEN]; /* 源以太网地址 */
	u_short ether_type;                  /* 帧类型 */
} ether_header_t;
// ETHER_ADDR_LEN 为 6

ARP 请求/应答: net/if_arp.h

struct  arphdr {
   
	u_short ar_hrd;         /* 硬件类型 format of hardware address */
#define ARPHRD_ETHER    1       /* ethernet hardware format */
#define ARPHRD_IEEE802  6       /* token-ring hardware format */
#define ARPHRD_FRELAY   15      /* frame relay hardware format */
#define ARPHRD_IEEE1394 24      /* IEEE1394 hardware address */
#define ARPHRD_IEEE1394_EUI64 27 /* IEEE1394 EUI-64 */
	u_short ar_pro;         /* 协议类型 format of protocol address */
	u_char  ar_hln;         /* 硬件地址长度 length of hardware address */
	u_char  ar_pln;         /* 协议地址长度 length of protocol address */
	u_short ar_op;          /* 操作码 one of: */
#define ARPOP_REQUEST   1       /* request to resolve address */
#define ARPOP_REPLY     2       /* response to previous request */
#define ARPOP_REVREQUEST 3      /* request protocol address given hardware */
#define ARPOP_REVREPLY  4       /* response giving protocol address */
#define ARPOP_INVREQUEST 8      /* request to identify peer */
#define ARPOP_INVREPLY  9       /* response identifying peer */
/*
 * The remaining fields are variable in size,
 * according to the sizes above.
 */
#ifdef COMMENT_ONLY
	u_char  ar_sha[];       /* 源硬件地址  sender hardware address */
	u_char  ar_spa[];       /* 源协议地址  sender protocol address */
	u_char  ar_tha[];       /* 目标硬件地址 target hardware address */
	u_char  ar_tpa[];       /* 目标协议地址 target protocol address */
#endif
};

实现

在 Linux 系统上, 可以通过 PF_PACKET 创建由用户态程序收发数据链接层数据的 Packet Socket, 从而发送完全自定义的 ARP 报文。但是在基于 BSD 的系统(比如 MacOS) 上, 是不支持 PF_PACKET 类型的 Socket 的,这时候就要利用 BPF(Berkeley Packet Filter)伯克利包过滤器来实现原始链路层数据的收发. —— BPF

Berkeley Packet Filter

数据包过滤器显示为字符特殊设备 /dev/bpfN(N为0~N, 一台机器上可能会提供多个 bpf 文件)。打开设备后,必须使用 ioctl 调用并结合 BIOCSETIF, 将文件描述符绑定到特定的网络接口。给定的接口可以由多个侦听器共享,并且每个描述符下面的过滤器将看到相同的数据包流。— man bpf

ls /dev/bpf

打开 BPF 设备

int openBpf()
{
   
    char _buf[32];
    int bfd = -1;
    int i = 0;
    // 查找一个可用的 BPF 设备
    for (i = 0; i < 255; i++)
    {
   
        snprintf(_buf, sizeof(_buf), "/dev/bpf%u", i);
        bfd = open(_buf, O_RDWR);
        if (bfd > 0)
        {
   
            break;
        }
    }
    return bfd;
}

设置 BPF 文件

int setupBpf(int fd, const char *ifname) {
   
    // ifname 为硬件接口名字, 比如 en0 就代表网卡一
    struct ifreq request;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值