我正在尝试使用我自己的自定义协议ID在同一台计算机上发送和接收SOCK_RAW
类型的数据包PF_SOCKET
。这是我的发件人和收件人示例代码 -
sender.c
#include<sys/socket.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<linux/if_arp.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define CUSTOM_PROTO 0xB588
int main ()
{
int sockfd = -1;
struct sockaddr_ll dest_addr = {0}, src_addr={0};
char *buffer = NULL;
struct ethhdr *eh;
sockfd = socket(PF_PACKET, SOCK_RAW, htons(CUSTOM_PROTO) );
if ( sockfd == -1 )
{
perror("socket");
return -1;
}
buffer = malloc(1518);
eh = (struct ethhdr *)buffer;
dest_addr.sll_ifindex = if_nametoindex("eth0");
dest_addr.sll_addr[0] = 0x0;
dest_addr.sll_addr[1] = 0xc;
dest_addr.sll_addr[2] = 0x29;
dest_addr.sll_addr[3] = 0x49;
dest_addr.sll_addr[4] = 0x3f;
dest_addr.sll_addr[5] = 0x5b;
dest_addr.sll_addr[6] = 0x0;
dest_addr.sll_addr[7] = 0x0;
//other host MAC address
unsigned char dest_mac[6] = {0x0, 0xc, 0x29, 0x49, 0x3f, 0x5b};
/*set the frame header*/
memcpy((void*)buffer, (void*)dest_mac, ETH_ALEN);
memcpy((void*)(buffer+ETH_ALEN), (void*)dest_mac, ETH_ALEN);
eh->h_proto = htons(PAVAN_PROTO);
memcpy((void*)(buffer+ETH_ALEN+ETH_ALEN + 2), "Pavan", 6 );
int send = sendto(sockfd, buffer, 1514, 0, (struct sockaddr*)&dest_addr,
sizeof(dest_addr) );
if ( send == -1 )
{
perror("sendto");
return -1;
}
return 0;
}
receiver.c
#include<sys/socket.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<linux/if_arp.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define CUSTOM_PROTO 0xB588
int main ()
{
int sockfd = -1;
struct sockaddr_ll dest_addr = {0}, src_addr={0};
char *recvbuf = malloc(1514);
sockfd = socket(PF_PACKET, SOCK_RAW, htons(CUSTOM_PROTO) );
if ( sockfd == -1 )
{
perror("socket");
return -1;
}
int len = recvfrom(sockfd, recvbuf, 1514, 0, NULL, NULL);
printf("I received: \n");
return 0;
}
发件人和收件人都在Ubuntu Virtualbox上运行。问题是接收器在recvfrom
中挂起。但在receiver.c
中,如果我将htons(CUSTOM_PROTO)
更改为htons(ETH_P_ALL)
,则接收器工作正常。
为什么内核没有将带有自定义协议ID的数据包传送到我的自定义协议ID套接字?
我在GDB中验证了当我收到htons(ETH_P_ALL)
更新:而不是接口eth0
及其对应的MAC,如果我选择本地环回lo
且MAC地址为00:00:00:00:00:00
,{{1}工作得很好!
更新2 CUSTOM_PROTO
可以正常工作。此发现和prev更新使我怀疑在CUSTOM_PROTO
发送的数据包未被同一台机器接收。但是eth0
在同一台机器上工作的事实,驳斥了我的怀疑。
答案 0 :(得分:7)
协议ETH_P_ALL
具有捕获传出数据包的特殊作用。
任何协议不等于ETH_P_ALL
的接收器套接字都会收到来自设备驱动程序的协议数据包。
协议ETH_P_ALL
的套接字在将传出数据包发送到设备驱动程序之前接收所有数据包,并从设备驱动程序接收所有传入数据包。
发送到环回设备的数据包从该设备发出,然后从设备接收相同的数据包作为传入。
因此,当CUSTOM_PROTO
与loopback一起使用时,套接字会使用自定义协议捕获数据包作为传入。
请注意,如果ETH_P_ALL
与环回设备一起使用,则每个数据包都会被接收两次。一旦它被捕获为传出,第二次被传入。
在eth0
的情况下,数据包从设备传输。因此,这些数据包会转到设备驱动程序,然后可以在物理以太网端口的另一端看到它们。例如,使用VirtualBox“仅主机”网络适配器,主机系统中的某些嗅探器可以捕获这些数据包。
但是,传输到物理端口(或其仿真)的数据包不会重定向回该端口。因此,它们不会从设备接收到。这就是为什么这些数据包只能在传出方向上被ETH_P_ALL
捕获,而CUSTOM_PROTO
在传入方向上无法看到它们。
从技术上讲,应该可以准备特殊设置来进行外部数据包环回(来自设备端口的数据包应该被发送回该端口)。在这种情况下,行为应该类似于环回设备。
查看内核文件net/core/dev.c
。有两个不同的列表:
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
struct list_head ptype_all __read_mostly; /* Taps */
列表ptype_all
适用于协议ETH_P_ALL
的套接字处理程序。列表ptype_base
适用于具有正常协议的处理程序。
来自xmit_one()
的dev_hard_start_xmit()
传出数据包的挂钩:
if (!list_empty(&ptype_all))
dev_queue_xmit_nit(skb, dev);
对于传出数据包,调用函数dev_queue_xmit_nit()
以ETH_P_ALL
处理ptype_all
的每个项目。最后,具有协议AF_SOCKET
的{{1}}类型的套接字捕获该传出数据包。
因此,观察到的行为与任何自定义协议无关。使用ETH_P_ALL
可以观察到相同的行为。在这种情况下,接收器能够捕获所有传入的IP数据包,但它无法捕获ETH_P_IP
从sender.c
发送到"eth0"
设备的MAC地址的IP数据包。
"eth0"
也可以看到它。如果调用tcpdump
并且选项仅捕获传入的数据包(不同版本的tcpdump
使用不同的命令行参数来启用此类过滤),则不会捕获发送方发送的数据包。
可以使用tcpdump
解决在同一台机器上按协议ID区分数据包所需的初始任务。接收方应捕获所有数据包并检查协议,例如:
ETH_P_ALL
有用的参考"kernel_flow",附图很好http://www.linuxfoundation.org/images/1/1c/Network_data_flow_through_kernel.png
它基于2.6.20内核,但在现代内核中while (1) {
int len = recvfrom(sockfd, recvbuf, 1514, 0, NULL, NULL);
if (ntohs(*(uint16_t*)(recvbuf + ETH_ALEN + ETH_ALEN)) == CUSTOM_PROTO) {
printf("I received: \n");
break;
}
}
的处理方式相同。
答案 1 :(得分:0)
When packets with same source nad destination MAC address are transmitted from real network device ethX
and physically looped back.
If protocol ETH_P_ALL
is specified, packet is captured twice:
socket_address.sll_pkttype
is PACKET_OUTGOING
socket_address.sll_pkttype
is PACKET_HOST
If specific protocol is specified CUSTOM_PROTO
, packet is captured once:
socket_address.sll_pkttype
is
PACKET_HOST
. socket_address.sll_pkttype
is PACKET_OTHERHOST
.