如何在 C/C++ 中接收原始第 2 层数据包?
如何在 POSIXy C++ 中接收第 2 层数据包?数据包仅包含 src 和 dst MAC 地址、类型/长度和自定义格式的数据。它们不是 TCP、UDP、IP、IGMP、ARP 或其他什么——它们是硬件人员给我的自制格式。
我的 socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)
永远不会从其 recvfrom()
返回。
我可以很好地发送,但无论我在网络堆栈上抛出什么选项,我都无法接收。
(平台是VxWorks,但我可以翻译POSIX或Linux或其他...)
接收代码(当前版本):
int s;
if ((s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) < 0) {
printf("socket create error.");
return -1;
}
struct ifreq _ifr;
strncpy(_ifr.ifr_name, "lltemac0", strlen("lltemac0"));
ioctl(s, IP_SIOCGIFINDEX, &_ifr);
struct sockaddr_ll _sockAttrib;
memset(&_sockAttrib, 0, sizeof(_sockAttrib));
_sockAttrib.sll_len = sizeof(_sockAttrib);
_sockAttrib.sll_family = AF_PACKET;
_sockAttrib.sll_protocol = IFT_ETHER;
_sockAttrib.sll_ifindex = _ifr.ifr_ifindex;
_sockAttrib.sll_hatype = 0xFFFF;
_sockAttrib.sll_pkttype = PACKET_HOST;
_sockAttrib.sll_halen = 6;
_sockAttrib.sll_addr[0] = 0x00;
_sockAttrib.sll_addr[1] = 0x02;
_sockAttrib.sll_addr[2] = 0x03;
_sockAttrib.sll_addr[3] = 0x12;
_sockAttrib.sll_addr[4] = 0x34;
_sockAttrib.sll_addr[5] = 0x56;
int _sockAttribLen = sizeof(_sockAttrib);
char packet[64];
memset(packet, 0, sizeof(packet));
if (recvfrom(s, (char *)packet, sizeof(packet), 0,
(struct sockaddr *)&_sockAttrib, &_sockAttribLen) < 0)
{
printf("packet receive error.");
}
// code never reaches here
How do I receive layer 2 packets in POSIXy C++? The packets only have src and dst MAC address, type/length, and custom formatted data. They're not TCP or UDP or IP or IGMP or ARP or whatever - they're a home-brewed format given unto me by the Hardware guys.
My socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)
never returns from its recvfrom()
.
I can send fine, I just can't receive no matter what options I fling at the network stack.
(Platform is VxWorks, but I can translate POSIX or Linux or whatever...)
receive code (current incarnation):
int s;
if ((s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) < 0) {
printf("socket create error.");
return -1;
}
struct ifreq _ifr;
strncpy(_ifr.ifr_name, "lltemac0", strlen("lltemac0"));
ioctl(s, IP_SIOCGIFINDEX, &_ifr);
struct sockaddr_ll _sockAttrib;
memset(&_sockAttrib, 0, sizeof(_sockAttrib));
_sockAttrib.sll_len = sizeof(_sockAttrib);
_sockAttrib.sll_family = AF_PACKET;
_sockAttrib.sll_protocol = IFT_ETHER;
_sockAttrib.sll_ifindex = _ifr.ifr_ifindex;
_sockAttrib.sll_hatype = 0xFFFF;
_sockAttrib.sll_pkttype = PACKET_HOST;
_sockAttrib.sll_halen = 6;
_sockAttrib.sll_addr[0] = 0x00;
_sockAttrib.sll_addr[1] = 0x02;
_sockAttrib.sll_addr[2] = 0x03;
_sockAttrib.sll_addr[3] = 0x12;
_sockAttrib.sll_addr[4] = 0x34;
_sockAttrib.sll_addr[5] = 0x56;
int _sockAttribLen = sizeof(_sockAttrib);
char packet[64];
memset(packet, 0, sizeof(packet));
if (recvfrom(s, (char *)packet, sizeof(packet), 0,
(struct sockaddr *)&_sockAttrib, &_sockAttribLen) < 0)
{
printf("packet receive error.");
}
// code never reaches here
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我认为做到这一点的方法是编写自己的网络服务,绑定到VxWorks网络堆栈中的MUX层。这在《VxWorks 网络程序员指南》中有相当详细的记录,我也做过很多次。
可以将自定义网络服务配置为使用 MUX_PROTO_SNARF 服务类型(这就是 Wind River 自己的 WDB 协议的工作原理)查看网络接口上接收的所有第 2 层数据包,或具有特定协议类型的数据包。
还可以通过编写位于网络服务和套接字 API 之间的自定义套接字后端来向自定义网络服务添加套接字接口。如果您愿意在网络服务中进行应用程序处理,则不需要这样做。
您还没有说您使用的是哪个版本的 VxWorks,但我认为上述内容适用于 VxWorks 5.5.x 和 6.x
I think the way to do this is to write your own Network Service that binds to the MUX layer in the VxWorks network stack. This is reasonably well documented in the VxWorks Network Programmer's Guide and something I have done a number of times.
A custom Network Service can be configured to see all layer 2 packets received on a network interface using the MUX_PROTO_SNARF service type, which is how Wind River's own WDB protocol works, or packets with a specific protocol type.
It is also possible to add a socket interface to your custom Network Service by writing a custom socket back-end that sits between the Network Service and the socket API. This is not required if you are happy to do the application processing in the Network Service.
You haven't said which version of VxWorks you are using but I think the above holds for VxWorks 5.5.x and 6.x
您是否尝试按照
packet(7)
中的规定将套接字协议设置为htons(ETH_P_ALL)
?你所做的与IP没有太大关系(尽管IPPROTO_RAW
可能是一些通配符值,不知道)Have you tried setting the socket protocol to
htons(ETH_P_ALL)
as prescribed inpacket(7)
? What you're doing doesn't have much to do with IP (althoughIPPROTO_RAW
may be some wildcard value, dunno)我认为这个问题比你想象的更难解决。鉴于它根本不是IP(或者显然任何其他协议都可以识别),我认为您无法完全使用用户级代码解决您的问题。在 Linux 上,我认为您需要编写自己的 与设备无关的接口驱动程序(可能使用
NAPI
< /a>)。让它在 VxWorks 下工作几乎肯定是不简单的(更像是从头开始完全重写,而不是大多数人认为的移植)。I think this is going to be a bit tougher problem to solve than you expect. Given that it's not IP at all (or apparently any other protocol anything will recognize), I don't think you'll be able to solve your problem(s) entirely with user-level code. On Linux, I think you'd need to write your own device agnostic interface driver (probably using
NAPI
). Getting it to work under VxWorks will almost certainly be non-trivial (more like a complete rewrite from the ground-up than what most people would think of as a port).您是否尝试过通过 Wireshark 确认数据包实际上已从另一端发送?
另外,为了进行调试,请询问您的硬件人员是否有调试引脚(您可以连接到逻辑分析仪),他们可以在收到数据包时断言该引脚。只是为了确保硬件能够正常接收数据包。
Have you tried confirming via Wireshark that a packet has actually been sent from the other end?
Also, for debugging, ask your hardware guys if they have a debug pin (you can attach to a logic analyzer) that they can assert when it receives a packet. Just to make sure that the hardware is getting the packets fine.
首先,您需要将协议指定为 ETH_P_ALL,以便您的接口获取所有数据包。将套接字设置为混杂模式。然后在执行接收之前将 RAW 套接字绑定到接口。
First you need to specify the protocol as ETH_P_ALL so that your interface gets all the packet. Set your socket to be on promiscuous mode. Then bind your RAW socket to an interface before you perform a receive.