OSX上的AF_PACKET [英] AF_PACKET on OSX

查看:143
本文介绍了OSX上的AF_PACKET的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

On Linux, it's possible to create a socket with AF_PACKET to receive raw data from socket and do IP filtering in the application. But the man page in OSX doesn't have this:

       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标准接口吗?如何在OSX上实现相同的目标?

Is this not a POSIX standard interface? How to achieve same thing on OSX?

推荐答案

没有协议是POSIX标准. POSIX不需要系统完全支持任何特定的网络协议或任何网络协议.

No protocol whatsoever is POSIX standard. POSIX does not require a system to support any specific network protocol or any network protocol at all.

AF_PACKET是纯Linux发明的AFAIK,在其他系统上找不到.

AF_PACKET is a pure Linux invention AFAIK, you won't find it on other systems.

BPF(伯克利分组过滤器)也不是POSIX,它是许多系统已经复制的BSD发明,因为它非常方便.但是,您不能使用它注入流量,只能捕获它的传入和传出流量.

BPF (Berkley Packet Filters) is also not POSIX, it's a BSD invention that many systems have copied, as it's pretty handy. However, you cannot inject traffic with it, you can only capture incoming and outgoing traffic with it.

万一有人在意,这是最新的POSIX标准:
Open Group基本规范,2018年第7版,
IEEE Std 1003.1™-2017(IEEE Std 1003.1-2008修订版)

In case anyone cares, here's the latest POSIX Standard:
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套接字是最可移植的:

If you actually want to send raw IP packets (no matter if IPv4 or IPv6), using a raw IP socket is most portable:

int soc = socket(PF_INET, SOCK_RAW, IPPROTO_IP);

然后您需要告诉系统您要提供自己的IP标头:

And then you need to tell the system, that you want to provide your own IP header:

int yes = 1;
setsockopt(soc, IPPROTO_IP, IP_HDRINCL, &yes, sizeof(yes));

现在,您可以将原始IP数据包(例如IP标头+ UDP标头+有效负载数据)发送到套接字以进行发送,但是,根据您的系统,系统将执行一些健全性检查,并且可能会覆盖标头中的某些字段.例如.它可能不允许您创建格式错误的IP数据包或阻止您执行IP地址欺骗.因此,例如,如果您的IP标头使用0.0.0.0::作为源地址,它可能会为您计算IPv4标头校验和或自动填写正确的源地址.在手册页上查看目标系统上的ip(4)raw(7). Apple不再为macOS发行程序员手册页,但是您可以找到他们在线.

Now you can send raw IP packets (e.g. IP header + UDP header + payload data) to the socket for sending, however, depending on your system, the system will perform some sanity checks and maybe override some fields in the header. E.g. it may not allow you to create malformed IP packets or prevent you from performing IP address spoofing. Therefor it may for example calculate the IPv4 header checksum for you or automatically fill in the correct source address if your IP header uses 0.0.0.0 or :: as source address. Check the man page for ip(4) or for raw(7) on your target system. Apple doesn't ship programmer man pages for macOS any longer, but you can find them online.

要引用该手册页:

与以前的BSD版本不同,该程序必须设置以下字段的所有字段: IP标头,包括以下内容:

Unlike previous BSD releases, the program must set all the fields of the 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;

请注意,ip_offip_len字段按主机字节顺序.

如果标头源地址设置为INADDR_ANY,则内核将 选择一个合适的地址.

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

请注意,根本没有提到ip_sum,因此显然您不必提供该名称,系统将始终为您计算出它.

Note that ip_sum is not mentioned at all, so apparently you don't have to provide that one and the system will always calculate it for you.

如果将其与Linux进行比较 raw(7):

If you compare that to 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数据包) .您将收到包括所有标头的完整数据包,因此收到的每个数据包的第一个字节就是IP标头的第一个字节.

When receiving from a raw IP socket, you will either get all incoming IP packets that arrive at the host or just a subset of them (e.g. Windows does support raw sockets but won't ever let you send or receive TCP packets). You will receive the full packet, including all headers, so the first byte of every packet received is the first byte of the IP header.

这里有些人会问为什么我使用IPPROTO_IP而不是IPPROTO_RAW.使用IPPROTO_RAW时,不必设置IP_HDRINCL:

Some people here will ask why I use IPPROTO_IP and not IPPROTO_RAW. When using IPPROTO_RAW you don't have to set IP_HDRINCL:

IPPROTO_RAW的协议表示已启用IP_HDRINCL,并且能够 发送通过的标头中指定的任何IP协议.

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

但是您只能将IPPROTO_RAW用于传出流量:

But you can only use IPPROTO_RAW for outgoing traffic:

仅发送IPPROTO_RAW套接字.

在macOS上,您可以使用IPPROTO_IP,并且您将收到所有IP数据包,但在Linux上可能无法正常工作,因此创建了新的套接字PF_PACKET套接字类型.在两个系统上都应该工作的是指定一个子协议:

On macOS you can use IPPROTO_IP and you will receive all IP packets but on Linux this may not work, hence the created a new socket PF_PACKET socket type. What should work on both systems is specifying a sub-protocol:

int soc = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);

当然,现在您只能通过该套接字发送/接收UDP数据包.如果再次设置IP_HDRINCL,则需要在发送时提供完整的IP标头,并且在接收时将收到完整的IP标头.如果未设置,则只需在send上提供UDP报头,系统便会添加IP报头本身,即套接字是否已连接并可选绑定,因此系统知道该报头中要使用的地址.对于接收该选项不起作用,您总是会收到在这样的套接字上接收到的每个UDP数据包的IP标头.

Of course, now you can only send/receive UDP packets over that socket. If you set IP_HDRINCL again, you need to provide a full IP header on send and you will receive a full IP header on receive. If you don't set it, you can just provide the UDP header on send and the system will add an IP header itself, that is, if the socket is connected and optionally bound, so the system knows which addresses to use in that header. For receiving that option plays no role, you always get the IP header for every UDP packet you receive on such a socket.

以防人们想知道为什么我使用PF_INET而不使用AF_INET:PF表示协议族,AF表示地址族.通常它们是相同的(例如AF_INET == PF_INET),所以使用什么都无所谓,但严格来说,套接字应使用PF_创建,并且sockaddr结构中的族应设置为AF_作为一天.可能有一种协议支持两种不同的地址,然后会有AF_XXX1AF_XXX2,而这两个都不可能与PF_XXX相同.

In case people wonder why I use PF_INET and not AF_INET: PF means Protocol Family and AF means Address Family. Usually these are the same (e.g. AF_INET == PF_INET) so it won't matter what you use, but strictly speaking sockets should be creates with PF_ and the family in sockaddr structures should be set with AF_ as one day there might be a protocol that supports two kind of different addresses and then there will be AF_XXX1 and AF_XXX2 and neither one may be the same as PF_XXX.

这篇关于OSX上的AF_PACKET的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆