我正尝试在同一台机器上使用自己的自定义协议ID通过PF_SOCKET
发送和接收SOCK_RAW
类型的数据包。这是我的发送者和接收者示例代码-
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
和它对应的,如果我选择本地环回lo
和00:00:00:00:00:00
的MAC地址,CUSTOM_PROTO
就可以正常工作!
如果发送方和接收方位于不同的计算机上,则更新2 CUSTOM_PROTO
工作正常。这个发现和上一次更新让我怀疑在eth0
上发出的数据包不是由同一台机器接收的。但ETH_P_ALL
在同一台机器上运行的事实驳斥了我的怀疑。
发布于 2015-11-12 02:33:16
ETH_P_ALL与任何其他协议的比较
协议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
用于使用普通协议的处理程序。
在从dev_hard_start_xmit()
调用的xmit_one()
中有一个用于传出数据包的钩子
if (!list_empty(&ptype_all))
dev_queue_xmit_nit(skb, dev);
对于传出的数据包,将调用函数dev_queue_xmit_nit()
用于ETH_P_ALL
处理ptype_all
的每一项。最后,使用ETH_P_ALL
协议的AF_SOCKET
类型的套接字捕获该传出数据包。
因此,观察到的行为与任何自定义协议无关。使用ETH_P_IP
可以观察到相同的行为。在这种情况下,接收器能够捕获所有传入的IP分组,但是它不能捕获从"eth0"
发送到"eth0"
设备的sender.c
地址的IP分组。
它也可以被tcpdump
看到。如果使用仅捕获传入数据包的选项调用tcpdump
,则不会捕获发送方发送的数据包(不同版本的tcpdump
使用不同的命令行参数来启用此类过滤)。
在同一台机器上需要通过协议ID区分数据包的初始任务可以使用ETH_P_ALL
解决。接收方应捕获所有数据包并检查协议,例如:
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;
}
}
带有漂亮图表http://www.linuxfoundation.org/images/1/1c/Network_data_flow_through_kernel.png的有用参考"kernel_flow"
它是基于2.6.20内核的,但是在现代内核中,ETH_P_ALL
也是以同样的方式对待的。
发布于 2018-12-03 20:57:49
当具有相同源nad目的MAC地址的分组从真实网络设备ethX
发送并物理地环回时。
如果指定了协议ETH_P_ALL
,则会捕获两次数据包:
socket_address.sll_pkttype
的数据包是PACKET_OUTGOING
socket_address.sll_pkttype
的数据包是PACKET_HOST
如果指定特定协议为CUSTOM_PROTO
,则只捕获一次数据包:
正常包情况下的
socket_address.sll_pkttype
为PACKET_HOST
。VLAN包情况下的socket_address.sll_pkttype
为PACKET_OTHERHOST
.https://stackoverflow.com/questions/33484762
复制相似问题