【问题标题】:Using boost asio to send layer2 in C++在 C++ 中使用 boost asio 发送 layer2
【发布时间】:2017-09-19 02:31:19
【问题描述】:

当前的 GNURadio UDP Sink/Source 块使用 UDP 将数据包发送进/出 NIC。它使用 boost asio 进行发送/接收。我需要修改这些 UDP 接收器/源块以处理带有 IP 的 UDP,以便现有功能通过 1GB 网卡以及添加提升原始套接字/协议,以便我们可以通过 10G 网卡发送没有 IP 的第 2 层数据包没有 IP 地址(接收器是具有自定义错误检查/恢复等功能的 FPGA)。

仅使用 C,我有测试程序,我可以根据需要制作数据包并将其发送出去并在另一端接收。他们来自:

Create a layer 2 / ethernet socket with boost asio raw socket (in C++) http://qiita.com/kadopoly/items/acafa01945e5cfe39270

由于 GNURadio 使用 C++,我需要修改 boost 套接字/协议设置以适应 udp_sink_impl.cc 和 udp_sink_impl.h 文件,以便发送数据包。

Stack Overflow 链接似乎是最直接的(我不是经验丰富的编码人员),我能够将它添加到我的 GNURadio 块中并让它传输。它没有正确发送transmitbuffer2缓冲区(发送了一个数据包,但内容错误),但确实发送了Hello World! streambuf 缓冲区正确,所以我不知道。但是它不处理 0x00,所以我不能用它来测试正在发送的 MAC 地址。

这是我目前拥有的:

udp_sink_impl.h sn-p:

    char tmpHeaderBuff[12];  // 32-bit sync word (0xFFFFFFFF), 32-bit sequence num and 32-bit data size

    unsigned char custom_type[2] = {0x82,0x00};
    unsigned char ethHeader[12] = {0x00,0x0c,0x29,0x05,0x0d,0x15,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
    unsigned char mac_str[6];

    unsigned char dstMAC[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};    
    unsigned char srcMAC[6];

/* 1G management stuff */
        boost::asio::io_service d_io_service;
        boost::asio::ip::udp::endpoint d_endpoint;
        boost::asio::ip::udp::socket *udpsocket;
/* 1G management stuff */

/* 10G raw socket stuff */    
      typedef boost::asio::generic::raw_protocol raw_protocol_t;
      typedef boost::asio::generic::basic_endpoint<raw_protocol_t> raw_endpoint_t;      
      boost::asio::io_service d_io_service10;

      raw_protocol_t::socket *d_socket10;
/* 10G raw socket stuff */

udp_sink_impl.cc sn-p:

  getMACAddress(d_send_iface,srcMAC);

/* Begin 1G management connection */
      std::string s__port = (boost::format("%d") % port).str();
      std::string s__host = host.empty() ? std::string("localhost") : host;
      boost::asio::ip::udp::resolver resolver(d_io_service);
      boost::asio::ip::udp::resolver::query query(s__host, s__port,
          boost::asio::ip::resolver_query_base::passive);
      d_endpoint = *resolver.resolve(query);

          udpsocket = new boost::asio::ip::udp::socket(d_io_service);
        udpsocket->connect(d_endpoint);
/* End 1G management connection */

/* Begin 10G management connection */      
      sockaddr_ll sockaddr;
      memset(&sockaddr, 0, sizeof(sockaddr));
      sockaddr.sll_family = PF_PACKET;
      sockaddr.sll_protocol = htons(ETH_P_ALL);
      sockaddr.sll_ifindex = if_nametoindex(send_iface.c_str());
      sockaddr.sll_hatype = 1;

      raw_protocol_t::socket d_socket10(d_io_service10, raw_protocol_t(PF_PACKET, SOCK_RAW));   /* Needs root or cap_net_raw permissions */   

      d_socket10.bind(raw_endpoint_t(&sockaddr, sizeof(sockaddr)));
/* End 10G management connection */

/* This currently does not work for unknown reason. Transmitted packet does NOT match buffer contents expected. But the streambuf seems to work.      */

      std::vector<boost::asio::const_buffer> transmitbuffer2;        
      transmitbuffer2.clear();
      transmitbuffer2.push_back(boost::asio::buffer((const void *)srcMAC, sizeof(srcMAC)));
      transmitbuffer2.push_back(boost::asio::buffer((const void *)dstMAC, sizeof(dstMAC)));
      transmitbuffer2.push_back(boost::asio::buffer((const void *)custom_type, sizeof(custom_type)));

      d_socket10.send(boost::asio::buffer(transmitbuffer2));

/* for testing */
    boost::asio::streambuf buffer;
    std::ostream stream( &buffer );    
    unsigned char const deadbeef[] = { 0xde, 0xad, 0xbe, 0xef }; // can't handle 0x00

    stream << "Hello, World!!!"
           << reinterpret_cast< char const * >( deadbeef ); 
    d_socket10.send(buffer.data());    

    } // Close constructor

.
.
.
    udp_sink_impl::work(int noutput_items,
    gr_vector_const_void_star &input_items,
    gr_vector_void_star &output_items)
{
.
.
.
    udpsocket->send_to(transmitbuffer,d_endpoint);

最终,在 udp_sink_impl::work 函数中我想替换现有的 UDP 调用:

udpsocket->send_to(transmitbuffer,d_endpoint); 

类似:

d_socket10->send(transmitbuffer);

目前,当我这样做时,没有发送任何数据包,并且似乎崩溃或退出程序而不是连续发送数据,所以我认为这是某种崩溃。

有人可以向我解释如何在 C++ 中为 UDP Sink/Source 块实现简单 Stack Overflow 示例或日语标记化原始套接字示例的指针吗? Sink(发送)目前是我优先于 Source(接收)的优先级。

提前非常感谢。非常感谢任何帮助或建议。

【问题讨论】:

  • 欢迎来到 Stack Overflow!请edit您的代码将其减少为您的问题的minimal reproducible example。您当前的代码包含许多与您的问题无关的内容 - 一个最小样本通常看起来类似于一个好的单元测试:只执行一项任务,并为可重复性指定输入值。
  • 对不起,我尽可能地减少它,只显示相关代码。显示的每一行代码都与我的问题有关。 Reproducible 需要一个完整的 GNURadio 设置和测试方法,我认为它比任何人都想尝试的更冗长和努力。我需要一个具体的更改示例来了解您要查找的内容。谢谢!

标签: c++ sockets networking boost boost-asio


【解决方案1】:

从 scatter-gather io 上的boost doc,我认为您发送的语法应该是:

d_socket10.send(transmitbuffer2); // don't add the extra boost::asio::buffer()

我找不到任何使用boost::asio::buffer 到缓冲区向量的示例,所以我不确定到底发生了什么(也许有一些我没有看到的隐式转换)。


对于您的流测试,您必须在使用write 成员函数而不是operator&lt;&lt; 插入流时手动指定大小。比如:

unsigned char const deadbeef[5] = { 0x00, 0xde, 0xad, 0xbe, 0xef };
stream.write(reinterpret_cast<const char*>(&deadbeef[0]), 5);

这样,您可以处理发送空字符。


你能说明transmitbuffer 是如何构建的吗?和transmitbuffer2一样吗?

编辑

段错误表明您试图以非法方式访问内存。仅从您的 sn-p 进行调试有点困难,但我会首先检查您发送的缓冲区是否仍然有效。 boost::asio::buffer 不会复制底层内存,它需要在您准备发送时保持有效。

您还在 sn-p 中混合了指针和对象,因此请确保您没有任何悬空指针,并且在您调用 send 之前每个人都处于有效状态

编辑 2

在您的头文件中,您声明了一个名为d_socket10 的成员指针,您从未在构造函数中分配或创建该指针。在第 147 行调用:

raw_protocol_t::socket d_socket10(d_io_service10, raw_protocol_t(PF_PACKET, SOCK_RAW));

创建一个与您的成员指针同名的对象,当您退出构造函数的范围时,该对象将被销毁。稍后,您使用从未分配过任何内容的成员指针调用send,因此您的段错误。

要修复它,您需要像为udpsocket 所做的那样创建它并管理它的生命周期:

d_socket10 = new raw_protocol_t::socket(d_io_service10, raw_protocol_t(PF_PACKET, SOCK_RAW));

并在您不再需要时致电delete

【讨论】:

  • 您删除“boost::asio::buffer”的第一个建议是成功的。我现在可以在数据包捕获中看到使用 srcMAC、dstMAC 和 custom_type 正确传输的数据包。甜的!我发誓我之前尝试过...我一定有其他错误让我认为它不起作用。
  • 由于某种原因,在没有 Hello World 的情况下,流写入的第二次编辑无法工作。执行代码时,gnuradio 打印“RuntimeError: send: Invalid argument” 这有效: unsigned char const deadbeef[5] = { 0x00, 0xde, 0xad, 0xbe, 0xef };流 (&deadbeef[0]), 5); d_socket10.send(buffer.data());现在没关系。我正在使用它进行测试,并且认为我不需要做太多事情。谢谢
  • 传输缓冲区的制作方式相同(复制和粘贴)。此时,我只需要一种方法来使用 udp_sink_impl::work 函数中的 d_socket10(上面的测试在构造函数中)。目前是“udpsocket->send_to(transmitbuffer,d_endpoint);”我尝试将其更改为:“d_socket10.send(transmitbuffer);”并得到:“udp_sink_impl.cc:388:24: error: request for member 'send' ...(也许你打算使用'->'?)所以我尝试了->,它编译了,但是当它去发送,我收到“分段错误(核心转储)”ps 在格式化时遇到问题。抱歉。
  • 在您的 cmets 之后在我的回答中添加了更多信息
  • 谢谢@user3910497。我已将我的 WIP 提交给link,特别是第 376 行是需要进行更改的地方。我相信缓冲区内容是有效的,因为现有的 udp 套接字调用可以传输缓冲区,并且我可以在有效负载中看到我的 srcMAC、dstMAC 和 custom_type。这是一个指针类型的问题,我有很多东西要学。我试图遵循现有的 udp 套接字对 init 和使用所做的事情,但显然在如何使用它们方面存在差异。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-02
相关资源
最近更新 更多