c++broadcast

broadcast addresses for all local netowk interfaces AND their prefix lengths


I know that question about broadcast address was many times discussed. But I did not find any, when the answer considered netmask prefix length in the calculation of broadcast address. All answers give the code that ends up with something like x.x.x.255 and all happy, but, as we know from here https://www.wikihow.com/Calculate-Network-and-Broadcast-Address, 255 is not always correct. F.E. the broadcast must be calculated using the netmask prefix length

So what I do if use boost asio:

  1. get list of all local interfaces using tcp::resolver::query

  2. loop over the tcp::resolver::iterator

    2.1 - extract IP from endpoint 2.2 - need to do something, to determine REAL broadcast address of the network the interface connected to!

    std::set<std::string> interfaces;

    using boost::asio::ip::tcp;
    boost::asio::io_service service;
    boost::system::error_code ec;
    tcp::resolver::query resolver_query(tcp::v4(), hostname(), "", tcp::resolver::query::numeric_service);
    tcp::resolver resolver(service);
    tcp::resolver::iterator it = resolver.resolve(resolver_query, ec);

    if (ec)
    {
        //std::cout << "Failed to resolve a DNS name." << "Error code = " << ec.value() << ". Message = " << ec.message();
        return interfaces;
    }

    tcp::resolver::iterator it_end;

    typedef boost::asio::ip::address_v4 A;
    // loop over iterator 
    for (; it != it_end; ++it)
    {       
        auto addr = it->endpoint().address();

        // case A: returns something like x.x.x.255
        auto broadcast = A::broadcast(addr, A::netmask(addr))

        //case B: returns always 255.255.255.255
        //auto broadcast = A::netmask(add).broadcast().to_str();
         
        interfaces.emplace(broadcast.to_string());
    }

The code above works only in case A, and only if the IP address of the interface (endpoint in the loop) is x.x.x.x/24. In cases when the prefix length is f.e. /25, the calculated broadcast address is wrong (as it still ends with 255), and broadcast messages sent to it do not reach the nodes.

I know there is a class network_v4 in boost asio, that has prefix_length() getter, but to create an instance of network_v4 I need to pass the known prefix length into the constructor, so this seems like egg-chicken dilemma.

Is there any way to get the correct broadcast address for every local interface using the prefix length of the network it connected to? The resolver does not seem to be able to give me something more other than just endpoints with their addresses.

UPD. After some research I found that boost::asio does not provide proper address for subnets different from /24.


Solution

  • The solution below was build after some research on the subject. Boost asio not able to provide proper broadcast address for networks different from /24. So the code below reaches the goal for windows using GetIpAddrTable() and linux using getifaddrs(). It was checked in local network with different prefixes.

    #ifdef WIN32
    #include <winsock2.h>
    #include <iphlpapi.h>
    #include <stdio.h>
    #include <stdlib.h>
    #pragma comment(lib, "IPHLPAPI.lib")
    #endif
    
    #ifdef __unix__
    #include <sys/socket.h>
    #include <netdb.h>
    #include <ifaddrs.h>
    #endif
    
    std::string bipaddress(const std::string& interface)
    {
        std::string result{ "" };
    #ifdef WIN32
    
        auto inet_n2a = [](uint32_t addr, char* ipbuf)
        {
            sprintf(ipbuf, "%li.%li.%li.%li", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, (addr >> 0) & 0xFF);
        };
    
        PMIB_IPADDRTABLE p_ip_table;
        DWORD dw_size = 0;
        p_ip_table = (MIB_IPADDRTABLE*)malloc(sizeof(MIB_IPADDRTABLE));
        if (p_ip_table)
        {
            if (GetIpAddrTable(p_ip_table, &dw_size, 0) == ERROR_INSUFFICIENT_BUFFER)
            {
                free(p_ip_table);
                p_ip_table = (MIB_IPADDRTABLE*)malloc(dw_size);
                if (p_ip_table == NULL)
                {
                    return result;
                }
            }
        }
        else
            return result;
    
        DWORD dw_ret_val = 0;
        LPVOID lp_msg_buf = nullptr;
        if ((dw_ret_val = GetIpAddrTable(p_ip_table, &dw_size, 0)) != NO_ERROR)
        {
            if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw_ret_val,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR)&lp_msg_buf, 0, NULL))
            {
                LocalFree(lp_msg_buf);
            }
            return result;
        }
    
        IN_ADDR ip_addr{};
        IN_ADDR mask_addr{};
        for (int i = 0; i < (int)p_ip_table->dwNumEntries; i++)
        {
            ip_addr.S_un.S_addr = ntohl((u_long)p_ip_table->table[i].dwAddr);
            char ifa_addr_str[INET_ADDRSTRLEN]{ 0 };
            inet_n2a(ip_addr.S_un.S_addr, ifa_addr_str);
    
            mask_addr.S_un.S_addr = ntohl((u_long)p_ip_table->table[i].dwMask);
    
            uint32_t baddr = ip_addr.S_un.S_addr & mask_addr.S_un.S_addr;
            if (p_ip_table->table[i].dwBCastAddr)
                baddr |= ~mask_addr.S_un.S_addr;
            char dst_addr_str[INET_ADDRSTRLEN]{ 0 };
            inet_n2a(baddr, dst_addr_str);
    
            if (ifa_addr_str == interface)
            {
                result = dst_addr_str;
                break;
            }
        }
    
        if (p_ip_table)
        {
            free(p_ip_table);
            p_ip_table = nullptr;
        }
    
    #else
        struct ifaddrs* ifas;
        if (getifaddrs(&ifas) == -1)
        {
            return "";
        }
    
        for (struct ifaddrs *ifa = ifas; ifa != nullptr; ifa = ifa->ifa_next)
        {
            if (!ifa->ifa_addr)
                continue;
    
            int family = ifa->ifa_addr->sa_family;
            if (family == AF_INET)
            {
                char host[NI_MAXHOST]{ 0 };
                getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, 0, 0, NI_NUMERICHOST);
                if (interface == host)
                {
                    if (ifa->ifa_flags & IFF_BROADCAST)
                    {
                        char dst_buffer[NI_MAXHOST]{0};
                        unsigned int dst = ((struct sockaddr_in *)(ifa->ifa_ifu.ifu_broadaddr))->sin_addr.s_addr;
                        inet_ntop(family, &dst, dst_buffer, NI_MAXHOST);
                        result = dst_buffer;
                    }
                    break;
                }
            }
        }
        freeifaddrs(ifas);
    #endif
        return result;
    }