首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >数据包套接字未接收自定义协议ID的数据

数据包套接字未接收自定义协议ID的数据
EN

Stack Overflow用户
提问于 2015-11-03 02:50:44
回答 2查看 1.9K关注 0票数 18

我正尝试在同一台机器上使用自己的自定义协议ID通过PF_SOCKET发送和接收SOCK_RAW类型的数据包。这是我的发送者和接收者示例代码-

sender.c

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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和它对应的,如果我选择本地环回lo00:00:00:00:00:00的MAC地址,CUSTOM_PROTO就可以正常工作!

如果发送方和接收方位于不同的计算机上,则更新2 CUSTOM_PROTO工作正常。这个发现和上一次更新让我怀疑在eth0上发出的数据包不是由同一台机器接收的。但ETH_P_ALL在同一台机器上运行的事实驳斥了我的怀疑。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 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。有两个不同的列表:

代码语言:javascript
复制
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()中有一个用于传出数据包的钩子

代码语言:javascript
复制
    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解决。接收方应捕获所有数据包并检查协议,例如:

代码语言:javascript
复制
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也是以同样的方式对待的。

票数 8
EN

Stack Overflow用户

发布于 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_pkttypePACKET_HOST。VLAN包情况下的
  • socket_address.sll_pkttypePACKET_OTHERHOST.
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33484762

复制
相关文章

相似问题

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