Boost Asio accessing asio::ip::address underlying data and byte ordering

637 views Asked by At

My goal is to create a unique ID for all IP address - port pair. The UID must be same across systems (no conflict for different endian systems). Size of IPV4 UID is 6 bytes and for ipv6 is 18 bytes.

uint8_t sourcePair[18];     /*ipv4=(4+2) bytes or ipv6=(16+2) bytes*/

I have two functions that will take the remote endpoint of a socket and get the desired UID. The design is as follows.

void CmdInterpreter::makeSourcePairV4(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, unsigned char(&binSourcePair)[18])
{
    auto addressClass = remoteEp.address().to_v4();
    auto ipBin = addressClass.to_uint();
    memcpy(&binSourcePair[0], &ipBin, 4);
    memcpy(&binSourcePair[4], &portNum, 2);
}

void CmdInterpreter::makeSourcePairV6(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, unsigned char(&binSourcePair)[18])
{
    auto addressClass = remoteEp.address().to_v6();
    auto ipBin = addressClass.to_bytes();
    memcpy(&binSourcePair[0], &ipBin[0], 16);
    memcpy(&binSourcePair[16], &portNum, 2);
}

This is how these functions are called

remoteEp = socketPtr->remote_endpoint();
if (remoteEp.address().is_v4())
    CmdInterpreter::makeSourcePairV4(remoteEp, remoteEp.port(), sourcePair);
else
    CmdInterpreter::makeSourcePairV6(remoteEp, remoteEp.port(), sourcePair);

Here the problem is the only way to access the IPv6 underlying data is using to_byte() which will give the data in network byte order. Also, I am doing a memcopy in unsigned short which is multibyte in length. Does this work? Is it a safe way? Is their any workarounds?

1

There are 1 answers

0
Thinkal VB On
void CmdInterpreter::makeSourcePairV4(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, uint8_t(&sourcePair)[18])
{
    auto addressClass = remoteEp.address().to_v4();
    auto ipBin = addressClass.to_bytes();
    memcpy(&sourcePair[0], &ipBin[0], 4);

    #ifdef BOOST_ENDIAN_LITTLE_BYTE
    byteSwap(portNum);
    #endif
    memcpy(&sourcePair[4], &portNum, 2);
}

void CmdInterpreter::makeSourcePairV6(asio::ip::tcp::endpoint& remoteEp, unsigned short portNum, uint8_t(&sourcePair)[18])
{
    auto addressClass = remoteEp.address().to_v6();
    auto ipBin = addressClass.to_bytes();
    memcpy(&sourcePair[0], &ipBin[0], 16);
    #ifdef BOOST_ENDIAN_LITTLE_BYTE
    byteSwap(portNum);
    #endif
    memcpy(&sourcePair[16], &portNum, 2);
}

For both IPv4 and IPv6 address, use to_byte() function to get the remote endpoint address in big-endian format. For little-endian host, the port number will make endianness problem which can be fixed by swapping the bytes. To encode it to base 64 I used cppcodec library.

UID = cppcodec::base64_rfc4648::encode(sourcePair, 6);
UID = cppcodec::base64_rfc4648::encode(sourcePair, 18);

The template function used to swap the port number is:

template <typename T>
void byteSwap(T& portNumber)
{
    char* startIndex = static_cast<char*>((void*)&portNumber);
    char* endIndex = startIndex + sizeof(T);
    std::reverse(startIndex, endIndex);
}