2

https://github.com/ThinkalVB/RTDS-Server I am making a simple UDP IPv6 server that prints version of the UDP packet sent by the remote endpoint. But this code is acting strangely. When sending IPv6 and IPv4 packets it is printing IPv6. What is that I am doing wrong? [Testing in Win10 with Packet Sender Portable 6.2.3 (127.0.0.1 and ::1)]

#include <asio.hpp>
#include <iostream>
#include "udp_peer.h"
#include <thread>


asio::io_context ioContext;
asio::io_context::work worker(ioContext);

void runServer()
{
    ioContext.run();
}

int main()
{
    asio::ip::udp::endpoint mUDPep(asio::ip::udp::v6(), 321);
    asio::ip::udp::socket mUDPsock(ioContext);
    std::thread thread(runServer);
    thread.detach();

    asio::error_code ec;
    UDPpeer udpPeer(&mUDPsock);  // Ignore this, it contains the character array
    asio::ip::udp::endpoint ep;


    mUDPsock.open(mUDPep.protocol(), ec);
    mUDPsock.bind(mUDPep, ec);
    while (true)
    {
        auto dataSize = mUDPsock.receive_from(udpPeer.getReadBuffer(), ep);
        if (ep.address().is_v4())
            std::cout << "IPv4";
        else
            std::cout << "IPv6";
    }
}
Thinkal VB
  • 189
  • 3
  • 12

1 Answers1

2

You're only listening on v6.

The ep do not dictate how you receive.

Your v6 endpoint is able to receive both. Print the actual endpoint to see:

#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::udp;

int main() {
    asio::thread_pool context(1);
    udp::socket sock(context, {udp::v6(), 8888});

    udp::endpoint ep;
    char arr[4096];

    while (true) {
        /*auto n =*/ sock.receive_from(asio::buffer(arr), ep);
        std::cout 
            << std::boolalpha << ep.address().is_v4() << " "
            << ep << "\n";
    }

    context.join();
}

Now sending two packets:

echo -n "hello world $RANDOM" | nc -6 -w 0 -u ::1 8888
echo -n "hello world $RANDOM" | nc -4 -w 0 -u 127.0.0.1 8888

Prints:

false [::1]:49972
false [::ffff:127.0.0.1]:34368

For comparison, saying udp::socket sock(context, {udp::v4(), 8888}); instead simply doesn't receive the v6 packet:

true 127.0.0.1:39805

In other words, because your socket is bound to v6, the address you get is mapped as if with:

if (a.is_v4())
    return asio::ip::address_v6::v4_mapped(a.to_v4());

What to do?

Check whether the v6 is v4 mapped or compatible:

asio::ip::address_v4 a4;
if (a6.is_v4_compatible() || a6.is_v4_mapped())
    a4 = a6.to_v4();

Looks like the more modern interface for this is going to be something like

 a4 = make_address_v4(asio::ip::v4_mapped, a6);

Demo: Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::udp;

int main() {
    asio::thread_pool context(1);
    udp::socket sock(context, {udp::v6(), 8888});

    udp::endpoint ep;
    char arr[4096];

    for (auto n=2; n--;) {
        /*auto n =*/ sock.receive_from(asio::buffer(arr), ep);
        asio::ip::address_v4 a4;

        {
            auto a6 = ep.address().to_v6();
            if (a6.is_v4_compatible() || a6.is_v4_mapped())
                a4 = a6.to_v4();
        }

        std::cout
            << (a4.is_unspecified()? "not-mapped" : a4.to_string()) << " "
            << ep << "\n";
    }

    context.join();
}

Prints

127.0.0.1 [::ffff:127.0.0.1]:54859
not-mapped [::1]:36231
sehe
  • 374,641
  • 47
  • 450
  • 633