0

The current GNURadio UDP Sink/Source blocks use UDP to send packets in/out of the NIC. It uses boost asio for the sending/receiving. I need to modify these UDP sink/source blocks to handle both UDP with IP for the existing functionality to go over a 1GB nic as well as add boost raw socket/protocol so that we can send layer2 packets without IP over a 10G nic that does NOT have IP addresses (receiver is an FPGA with custom error checking/recovery, etc).

Using just C, I have test programs where I can craft the packet as I want and send it out and receive it on the other side. They came from:

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

Since GNURadio is in C++, I need to modify the boost socket/protocol setup to fit into the udp_sink_impl.cc and udp_sink_impl.h files so I can send out the packets.

The Stack Overflow link seemed to be the most straight forward (I am not an experienced coder) and I was able to add it into my GNURadio block and have it transmit. It doesn't send the transmitbuffer2 buffer correctly (a packet is sent, but with wrong contents), but does send the Hello World! streambuf buffer correctly, so that is unknown to me. It doesn't handle 0x00 though, so I couldn't use that to test MAC address being sent.

Here is what I currently have:

udp_sink_impl.h snippet:

    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 snippet:

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

Ultimately, in the udp_sink_impl::work function I want to replace the existing UDP call:

udpsocket->send_to(transmitbuffer,d_endpoint); 

with something like:

d_socket10->send(transmitbuffer);

Currently, when I do that, no packet is sent and seems to crash or exit the program instead of continuously sending data, so I take that to be a crash of some sort.

Can someone explain to me how to implement the pointer for simple Stack Overflow example or the japanese tokenized raw socket example in C++ for the UDP Sink/Source blocks? Sink (sending) is currently my priority over Source (receiving).

Thanks very much in advance. Any help or suggestions are much appreciated.

kenba
  • 4,303
  • 1
  • 23
  • 40
J. Doe
  • 43
  • 6
  • Welcome to Stack Overflow! Please [edit] your code to reduce it to a [mcve] of your problem. Your current code includes much that is peripheral to your problem - a minimal sample normally looks similar to a good unit test: only performing one task, with input values specified for reproducibility. – Toby Speight Sep 19 '17 at 08:43
  • I'm sorry, but I reduced it as much as I could to only show relevant code. Every line of code shown is related to my issue. Reproducible requires a full GNURadio setup and test method, which I take to be way more verbose and effort than anyone is going to try. I'd need a specific example of a change to get an idea of what you're looking for. Thanks! – J. Doe Sep 20 '17 at 02:54

1 Answers1

0

From the boost doc on scatter-gather io, I think the syntax of your send should be:

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

I can't find any examples where they use a boost::asio::buffer to a vector of buffers so I'm not sure what is really happening (maybe there's some implicit conversion that I'm not seeing).


For your stream test, you have to manually specify the size when you insert in the stream using the write member function rather than operator<<. Something like:

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

That way, you can handle sending null characters.


Can you show how transmitbuffer is built? Is it the same as transmitbuffer2?

EDIT

segfaults indicate that you tried to access memory in an illegal way. It's kind of hard to debug only from your snippet, but I would start by checking that the buffer you're sending is still valid. boost::asio::buffer does NOT make a copy of the underlying memory and it needs to remain valid when you're ready to send.

You're also mixing pointers and objects in your snippet so make sure you don't have any dangling pointer and that everyone is in a valid state right before your call to send

EDIT 2

In your header file, you declare a member pointer named d_socket10 that you never assign or create in your constructor. This call on line 147:

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

creates an object with the same name as your member pointer that will be destroyed when the you exit the scope of the constructor. Later, you call send with your member pointer that was never assigned anything, hence your segfault.

To fix it, you need to create it and manage its lifetime the same way you did for udpsocket:

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

and calling delete when you no longer need it.

  • Your first suggestion to remove "boost::asio::buffer" was successful. I can now see the packet transmitting with the srcMAC, dstMAC and custom_type correctly in packet capture. Sweet! I swear I tried that before... I must have had other bugs that made me think it wasn't working. – J. Doe Sep 20 '17 at 03:15
  • The second edit for the stream write did not work without the Hello World for some reason. When executing the code, gnuradio printed "RuntimeError: send: Invalid argument" This works: unsigned char const deadbeef[5] = { 0x00, 0xde, 0xad, 0xbe, 0xef }; stream << "Hello, World!!!"; stream.write(reinterpret_cast(&deadbeef[0]), 5); d_socket10.send(buffer.data()); That's ok for now. I was using it for test and don't think I will need to do much with that. Thanks – J. Doe Sep 20 '17 at 03:40
  • The transmitbuffer is made the same way (copy and paste). At this point, I just need a way to use the d_socket10 from udp_sink_impl::work function (the test above is in the constructor). It's currently "udpsocket->send_to(transmitbuffer,d_endpoint);" I tried changing that to: "d_socket10.send(transmitbuffer);" and get: "udp_sink_impl.cc:388:24: error: request for member ‘send’ ... (maybe you meant to use ‘->’ ?) So I tried ->, and it compiles, but when it goes to send, I get "Segmentation fault (core dumped)" p.s. having troubles doing the formatting. Sorry. – J. Doe Sep 20 '17 at 04:01
  • added more info in my answer following your comments – user3910497 Sep 20 '17 at 13:07
  • Thanks @user3910497. I have committed my WIP to [link](https://github.com/BCITMike/GNU-Radio-Sink-Source-using-layer-2/blob/master/lib/udp_sink_impl.cc), specifically line 376 is the place where the change needs to happen. I believe the buffer contents to be valid as the existing udp socket call can transmit the buffer and I can see my srcMAC, dstMAC and custom_type in the payload. It is a pointer type issue where I very much have a lot to learn. I tried to follow what the existing udp socket was doing for init and usage, but there is obviously a difference in how to use them I'm missing. – J. Doe Sep 21 '17 at 01:00
  • Confirmed. Adding that and changing the calls to d_socket10->send successfully transmits the packet. I'm shaking my head as I thought I tried that early on, but did it incorrectly and had an extra d_socket10 before the brackets. It was right there in front of me... much appreciated! – J. Doe Sep 21 '17 at 23:30