首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OSX上的AF_PACKET

OSX上的AF_PACKET
EN

Stack Overflow用户
提问于 2013-06-18 20:48:59
回答 3查看 5.6K关注 0票数 4

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

代码语言:javascript
复制
       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上实现同样的功能?

EN

回答 3

Stack Overflow用户

发布于 2018-03-22 01:45:54

任何协议都不是POSIX标准。POSIX根本不要求系统支持任何特定的网络协议或任何网络协议。

AF_PACKET是一个纯粹的Linux发明,你在其他系统上找不到它。

BPF (Berkley Packet Filters)也不是POSIX,它是许多系统复制的BSD发明,因为它非常方便。但是,您不能使用它注入流量,只能使用它捕获传入和传出流量。

如果有人关心,这里是最新的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套接字是最便携的:

代码语言:javascript
复制
int soc = socket(PF_INET, SOCK_RAW, IPPROTO_IP);

然后,您需要告诉系统,您希望提供自己的IP报头:

代码语言:javascript
复制
int yes = 1;
setsockopt(soc, IPPROTO_IP, IP_HDRINCL, &yes, sizeof(yes));

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

引用该手册页中的内容:

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

ip->ip_v = IPVERSION;ip->ip_hl = hlen >> 2;ip->ip_id = 0;/* 0表示内核设置合适的值*/ ip->ip_off =偏移量;ip->ip_len = len;

请注意,ip_offip_len字段采用主机字节顺序

如果标头源地址设置为INADDR_ANY,内核将选择适当的地址。

请注意,根本没有提到ip_sum,所以显然您不必提供它,系统将始终为您计算它。

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

┌───────────────────────────────────────────────────┐├──────────────────────┬──────────────────────────在发送时修改的││IP标头字段──┤│IP Checksum│Always filled in│源地址│Filled zero│├──────────────────────┼─总长度│始终填写为零时填充的数据包ID│││└──────────────────────┴────────────────────────────┘

当从原始IP套接字接收时,您将获得到达主机的所有传入IP数据包或它们的子集(例如,Windows确实支持原始套接字,但不允许您发送或接收TCP数据包)。您将收到完整的数据包,包括所有报头,因此收到的每个数据包的第一个字节都是IP报头的第一个字节。

这里的一些人会问为什么我使用IPPROTO_IP而不是IPPROTO_RAW。在使用IPPROTO_RAW时,您不必设置IP_HDRINCL

IPPROTO_RAW的协议表示启用了IP_HDRINCL,并且能够发送在passed报头中指定的任何IP协议。

但您只能对传出流量使用IPPROTO_RAW

仅发送IPPROTO_RAW套接字

在macOS上,您可以使用IPPROTO_IP,您将接收所有IP数据包,但在Linux上这可能不起作用,因此创建了一个新的套接字PF_PACKET套接字类型。应该在两个系统上工作的是指定一个子协议:

代码语言:javascript
复制
int soc = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);

当然,现在您只能通过该套接字发送/接收UDP数据包。如果您再次设置IP_HDRINCL,则需要在发送时提供完整的IP报头,并且在接收时将收到完整的IP报头。如果您不设置它,您可以只在发送时提供UDP标头,系统将自己添加一个IP标头,也就是说,如果套接字已连接并可选地绑定,则系统知道在该标头中使用哪些地址。由于接收选项不起任何作用,因此您始终会获得在此类套接字上接收的每个UDP数据包的IP报头。

如果人们想知道为什么我使用PF_INET而不是AF_INET:PF表示协议族,AF表示地址族。通常这些是相同的(例如AF_INET == PF_INET),所以你使用什么都无关紧要,但严格地说,套接字应该用PF_创建,sockaddr结构中的家族应该用AF_设置,因为有一天可能会有一个协议支持两种不同的地址,然后就会有AF_XXX1AF_XXX2,它们可能都不会和PF_XXX相同。

票数 4
EN

Stack Overflow用户

发布于 2016-03-31 04:31:27

不幸的是,AF_PACKET在OS X上并不存在。相反,您应该使用/dev/bpfX (Berkeley Packet Filter),它将允许您捕获数据包。有关更多信息,请阅读:https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man4/bpf.4.html

票数 2
EN

Stack Overflow用户

发布于 2020-05-06 19:38:15

如果你想在Mac上发送原始以太网帧(例如,你自己的链路级协议,而不是IP),那么你可以使用类似于PF_RAW的PF_NDRV套接字:

代码语言:javascript
复制
#include <sys/socket.h>
#include <net/if.h>
#include <net/ndrv.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <net/ethernet.h>

int main (int argc, char **argv) {

   if (geteuid()) { fprintf(stderr,"No root, no service\n"); exit(1); }
   int s = socket(PF_NDRV,SOCK_RAW,0);
   if (s < 0) { perror ("socket"); exit(2); }

   uint16_t   etherType = ntohs(atoi(argv[1]));
   struct sockaddr_ndrv    sa_ndrv;

   strlcpy((char *)sa_ndrv.snd_name, "en0", sizeof (sa_ndrv.snd_name));
   sa_ndrv.snd_family = PF_NDRV;
   sa_ndrv.snd_len = sizeof (sa_ndrv);

   rc = bind(s, (struct sockaddr *) &sa_ndrv, sizeof(sa_ndrv));

   if (rc < 0) { perror ("bind"); exit (3);}

   char packetBuffer[2048];

#ifdef LISTENER
   struct ndrv_protocol_desc desc;
   struct ndrv_demux_desc demux_desc[1];
   memset(&desc, '\0', sizeof(desc));
   memset(&demux_desc, '\0', sizeof(demux_desc));

   /* Request kernel for demuxing of one chosen ethertype */
   desc.version = NDRV_PROTOCOL_DESC_VERS;
   desc.protocol_family = atoi(argv[1]);
   desc.demux_count = 1;
   desc.demux_list = (struct ndrv_demux_desc*)&demux_desc;
   demux_desc[0].type = NDRV_DEMUXTYPE_ETHERTYPE;
   demux_desc[0].length = sizeof(unsigned short);
   demux_desc[0].data.ether_type = ntohs(atoi(argv[1]));

   if (setsockopt(s, 
        SOL_NDRVPROTO, 
        NDRV_SETDMXSPEC, 
     (caddr_t)&desc, sizeof(desc))) {
      perror("setsockopt"); exit(4);
   }
   /* Socket will now receive chosen ethertype packets */
   while ((rc = recv (s, packetBuffer, 2048, 0) ) > 0 ) {
    printf("Got packet\n"); // remember, this is a PoC..
   }
#else
   memset(packetBuffer, '\xff', 12);
   memcpy(packetBuffer + 12, &etherType, 2);
   strcpy(packetBuffer,"NDRV is fun!");
   rc = sendto (s, packetBuffer, 20, 0, 
        (struct sockaddr *)&sa_ndrv, sizeof(sa_ndrv));
   if (rc < 0) { perror("sendto"); }
#endif
} 
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17169298

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档