草庐IT

c - 数据包套接字未接收自定义协议(protocol) ID 的数据

coder 2023-06-16 原文

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

发件人.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;
}

接收器.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),接收器工作正常。

为什么内核没有将带有我的自定义协议(protocol) ID 的数据包传送到我的自定义协议(protocol) ID 套接字?

当我使用 htons(ETH_P_ALL) 接收数据包时,我在 GDB 中验证了以太网 header 的格式是否正确

更新:如果我选择本地环回 lo00 的 MAC 地址,而不是接口(interface) eth0 及其对应的 MAC :00:00:00:00:00CUSTOM_PROTO 工作正常!

更新 2 如果发送方和接收方在不同的机器上,CUSTOM_PROTO 工作正常。这个发现和上一个更新让我怀疑在 eth0 上发送的数据包没有被同一台机器接收。但 ETH_P_ALL 在同一台机器上运行的事实驳斥了我的怀疑。

最佳答案

ETH_P_ALL 对比任何其他协议(protocol)

ETH_P_ALL 协议(protocol)具有捕获传出数据包的特殊作用。

具有任何不等于 ETH_P_ALL 的协议(protocol)的接收器套接字接收来自设备驱动程序的该协议(protocol)的数据包。

协议(protocol)为 ETH_P_ALL 的套接字在将传出数据包发送到设备驱动程序之前接收所有数据包,并接收从设备驱动程序接收的所有传入数据包。

环回设备与以太网设备

发送到环回设备的数据包从该设备发出,然后从设备接收到与传入相同的数据包。 因此,当 CUSTOM_PROTO 与环回一起使用时,套接字将使用自定义协议(protocol)捕获数据包作为传入。

请注意,如果 ETH_P_ALL 与环回设备一起使用,每个数据包将被接收两次。一次被捕获为传出,第二次被捕获为传入。

eth0 的情况下,数据包从设备传输。因此,此类数据包进入设备驱动程序,然后可以在物理以太网端口的另一侧看到它们。例如,使用 VirtualBox“Host-only”网络适配器,这些数据包可以被主机系统中的一些嗅探器捕获。

但是,传输到物理端口(或其仿真端口)的数据包不会重定向回该端口。因此,它们不会作为来自设备的传入接收。这就是为什么此类数据包只能在传出方向被 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 列表用于协议(protocol)为 ETH_P_ALL 的套接字处理程序。列表 ptype_base 适用于具有正常协议(protocol)的处理程序。

xmit_one() 中有一个用于传出数据包的钩子(Hook)从 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 且协议(protocol)为 ETH_P_ALL 的套接字捕获该传出数据包。


因此,观察到的行为与任何自定义协议(protocol)无关。使用 ETH_P_IP 可以观察到相同的行为。在这种情况下,接收方能够捕获所有传入的 IP 数据包,但是它无法捕获从 "eth0" 发送到 的 MAC 地址的 sender.c 的 IP 数据包>“eth0” 设备。

也可以通过tcpdump看到。如果调用 tcpdump 并带有仅捕获传入数据包的选项,则不会捕获发送方发送的数据包(不同版本的 tcpdump 使用不同的命令行参数来启用此类过滤) .


在同一台机器上需要通过协议(protocol) ID 区分数据包的初始任务可以使用 ETH_P_ALL 来解决。接收方应捕获所有数据包并检查协议(protocol),例如:

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;
    }
}

有用的引用 "kernel_flow"有一个漂亮的图表 http://www.linuxfoundation.org/images/1/1c/Network_data_flow_through_kernel.png

它基于 2.6.20 内核,但是在现代内核中 ETH_P_ALL 以相同的方式处理。

关于c - 数据包套接字未接收自定义协议(protocol) ID 的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33484762/

有关c - 数据包套接字未接收自定义协议(protocol) ID 的数据的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  4. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  9. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  10. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

随机推荐