MacOS 实现ARP报文发送和接收

文章目录ARP 协议概述报文格式实现Berkeley Packet Filter打开 BPF 设备设置 BPF 文件DNS 解析获取本机 IP地址和 MAC地址发送 ARP 报文读取 ARP 报文结果完整源码继上次 自己实现一个 Ping 命令 之后,尝试进入更底层的网络接口层实现局域网的 ARP 报文收发ARP 协议概述ARP(Address Resolution Protocol) ...
摘要由CSDN通过智能技术生成

继上次 自己实现一个 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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值