gpt4 book ai didi

macos - OSX 上的 AF_PACKET

转载 作者:行者123 更新时间:2023-12-01 06:54:30 24 4
gpt4 key购买 nike

在 Linux 上,可以使用 AF_PACKET 创建一个套接字,以从套接字接收原始数据并在应用程序中进行 IP 过滤。但是 OSX 的手册页没有这个:

       PF_LOCAL        Host-internal protocols, formerly called PF_UNIX,
PF_UNIX Host-internal protocols, deprecated, use PF_LOCAL,
PF_INET Internet version 4 protocols,
PF_ROUTE Internal Routing protocol,
PF_KEY Internal key-management function,
PF_INET6 Internet version 6 protocols,
PF_SYSTEM System domain,
PF_NDRV Raw access to network device

这不是 POSIX 标准接口(interface)吗?如何在 OSX 上实现相同的目标?

最佳答案

没有任何协议(protocol)是 POSIX 标准。 POSIX 根本不需要系统支持任何特定的网络协议(protocol)或任何网络协议(protocol)。AF_PACKET是一个纯 Linux 发明 AFAIK,你不会在其他系统上找到它。
BPF(伯克利包过滤器)也不是 POSIX,它是许多系统复制的 BSD 发明,因为它非常方便。但是,您不能使用它注入(inject)流量,您只能使用它捕获传入和传出流量。
如果有人关心,这里是最新的 POSIX 标准:
The Open Group Base Specifications Issue 7, 2018 edition
IEEE Std 1003.1™-2017 (Revision of IEEE Std 1003.1-2008)

如果你真的想发送原始 IP 数据包(无论是 IPv4 还是 IPv6),使用原始 IP 套接字是最便携的:

int soc = socket(PF_INET, SOCK_RAW, IPPROTO_IP);
然后你需要告诉系统你想提供你自己的IP头:
int yes = 1;
setsockopt(soc, IPPROTO_IP, IP_HDRINCL, &yes, sizeof(yes));
现在您可以将原始 IP 数据包(例如 IP header + UDP header + 有效负载数据)发送到套接字进行发送,但是,根据您的系统,系统将执行一些健全性检查,并且可能会覆盖 header 中的某些字段。例如。它可能不允许您创建格式错误的 IP 数据包或阻止您执行 IP 地址欺骗。因此,它可能会为您计算 IPv4 header 校验和,或者如果您的 IP header 使用 0.0.0.0,则自动填写正确的源地址。或 ::作为源地址。查看 ip(4) 的手册页或 raw(7)在您的目标系统上。 Apple 不再提供 macOS 的程序员手册页,但 you can find them online.
引用该手册页:

Unlike previous BSD releases, the program must set all the fields ofthe IP header, including the following:

 ip->ip_v = IPVERSION;
ip->ip_hl = hlen >> 2;
ip->ip_id = 0; /* 0 means kernel set appropriate value */
ip->ip_off = offset;
ip->ip_len = len;

Note that the ip_off and ip_len fields are in host byte order.

If the header source address is set to INADDR_ANY, the kernel willchoose an appropriate address.


请注意 ip_sum根本没有提到,所以显然你不必提供那个,系统总是会为你计算它。
如果将其与 Linux raw(7) 进行比较:
┌───────────────────────────────────────────────────┐
│IP Header fields modified on sending by IP_HDRINCL │
├──────────────────────┬────────────────────────────┤
│IP Checksum │ Always filled in │
├──────────────────────┼────────────────────────────┤
│Source Address │ Filled in when zero │
├──────────────────────┼────────────────────────────┤
│Packet ID │ Filled in when zero │
├──────────────────────┼────────────────────────────┤
│Total Length │ Always filled in │
└──────────────────────┴────────────────────────────┘

从原始 IP 套接字接收时,您将获得到达主机的所有传入 IP 数据包或仅获取其中的一部分(例如,Windows 确实支持原始套接字,但永远不会让您发送或接收 TCP 数据包)。您将收到完整的数据包,包括所有 header ,因此收到的每个数据包的第一个字节是 IP header 的第一个字节。
这里有人会问我为什么用 IPPROTO_IP而不是 IPPROTO_RAW .使用 IPPROTO_RAW 时您不必设置 IP_HDRINCL :

A protocol of IPPROTO_RAW implies enabled IP_HDRINCL and is able tosend any IP protocol that is specified in the passed header.


但是你只能使用 IPPROTO_RAW对于传出流量:

An IPPROTO_RAW socket is send only.


在 macOS 上,您可以使用 IPPROTO_IP您将收到所有 IP 数据包,但在 Linux 上这可能不起作用,因此创建了一个新套接字 PF_PACKET socket 类型。应该在两个系统上工作的是指定一个子协议(protocol):
int soc = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
当然,现在您只能通过该套接字发送/接收 UDP 数据包。如果设置 IP_HDRINCL同样,您需要在发送时提供完整的 IP header ,并且您将在接收时收到完整的 IP header 。如果你不设置它,你可以只在发送时提供UDP头,系统会自己添加一个IP头,也就是说,如果套接字是连接的并且可以选择绑定(bind),那么系统就知道在那个头中使用哪些地址.对于接收该选项不起作用,您总是会为您在此类套接字上收到的每个 UDP 数据包获取 IP header 。
如果人们想知道我为什么使用 PF_INET而不是 AF_INET : PF 表示协议(protocol)族,AF 表示地址族。通常这些是相同的(例如 AF_INET == PF_INET )所以你使用什么并不重要,但严格来说应该使用 PF_ 创建套接字和家人在 sockaddr结构应设置为 AF_有一天可能会有一种协议(protocol)支持两种不同的地址,然后会有 AF_XXX1AF_XXX2并且两者都不能与 PF_XXX 相同.

关于macos - OSX 上的 AF_PACKET,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17169298/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com