1

After going through many articles and not finding anything useful I am writing here.
I am getting an incorrect behavior for getaddrinfo function on two different RHEL variants. This is specifically for Ipv6 related functionality.

By default on both machines Ipv6 is enabled and both have only one network interface configured. I tried disabling it.

To do so I have added following entries to /etc/sysctl.conf

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

I have referred https://www.thegeekdiary.com/how-to-disable-ipv6-in-centos-rhel-8/ for this change. Strange thing is this worked perfectly fine for RHEL 7 and getaddrinfo returns only IPv4 addresses but for RHEL 8 it is still returning IPv6 addresses.

I have gone through page https://jameshfisher.com/2018/02/03/what-does-getaddrinfo-do/ to understand what getaddrinfo do (It may not be exact and implementation dependent on OS).

Following are the hosts, resolv.conf file for both OS sequentially.

RHEL 7:

/etc/hosts:

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
hostip hostname #cannot show actual ip and name here in stackoverflow

/etc/resolv.conf:

# Generated by NetworkManager
search some_microsoft_azure_address
nameserver nm_ip1
nameserver nm_ip2

RHEL 8:

/etc/hosts:

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
hostip hostname #cannot show actual ip and name here in stackoverflow

/etc/resolv.conf:

# Generated by NetworkManager
search some_microsoft_azure_address
nameserver nm_ip1
nameserver nm_ip2

The C++ code snippet looks something like this:

#include <fcntl.h>
#include <iostream>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

typedef struct sock_internal
{       /* communication model */
    int fd;              /* Descriptor or handle for O/S socket */
    int sktno;                /* socket number for indexing arrays */
    unsigned int flags;
    short dom;                    /* Socket domain */
    char *fn;
    unsigned int read_tout;   /* read timeout value */
    unsigned int write_tout;  /* write timeout value */
    unsigned short priority[2];
    unsigned long  user_val;  /* A user-defined value                   */
    unsigned int ip_version;  /* ipv4 = 4 & ipv6 = 6 */
} SOCKET_INTERNAL;

static void socket_reinit (SOCKET_INTERNAL* skt)
{
    int optval;
    struct linger linger;
    int pid = 0;

    fcntl(skt->fd, F_SETOWN, pid); /* Set OWNER id */

    optval = 0;
    setsockopt(skt->fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(int));

    linger.l_onoff = 0;
    linger.l_linger = 0;
    optval = 1;  /* Set REUSEADDR */
    setsockopt(skt->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(int));

    /* Set TCP_NODELAY so tcp/ip doesn't use the "Nagle algorithm" */
    /* to delay sending the second of 2 small (< 1460 byte) sends. */
    optval = 1;
    setsockopt(skt->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &optval, sizeof(int));
}

SOCKET_INTERNAL* bindsocket()
{
    int nBindRet = 0;
    struct addrinfo hints, *res, *results;
    struct sockaddr_un sad_un;   // Socket addres-> UNIX
    unsigned short srvc = 5746;   //Just a random port number
    bool isBound = false;
    SOCKET_INTERNAL s;

    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_INET6;

    char port[10];
    int portno = ntohs(srvc);
    if (!portno && srvc == 0)
    {
            return NULL;
    }
    sprintf(port,"%d", portno);

    int rc = getaddrinfo(NULL, port, &hints, &res);
    if (rc != 0)
    {
        std::cout << "getaddrinfo error -" << gai_strerror(rc) << std::endl;
        return NULL;
    }
    results=res;
    while(res != NULL)
    {
        if(res->ai_family != AF_INET6)
        {
            res = res->ai_next;
            continue;
        }
        s.fd = socket(AF_INET6, SOCK_STREAM, 0);
        if(s.fd < 0)
        {
            res = res->ai_next;
            continue; //fail, try next interface
        }
        socket_reinit(&s);
        int off = 0,on = 1;

        setsockopt(s.fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off));
        if( (nBindRet=::bind(s.fd,res->ai_addr, res->ai_addrlen)) == 0)
        {
            isBound = true;
            break; //Success
        }

        close(s.fd);/*fail, try next one*/
        res = res->ai_next;
    }
    res = results;
    while((res!= NULL) && !isBound)
    {
        if(res->ai_family != AF_INET)
        {
            res  = res->ai_next;
            continue;
        }

        s.fd = socket(AF_INET, SOCK_STREAM, 0);
        if(s.fd < 0)
        {
            res = res->ai_next;
            continue; //fail, try next interface
        }
        socket_reinit(&s);
        if( (nBindRet=::bind(s.fd,res->ai_addr, res->ai_addrlen)) == 0)
        {
            isBound = true;
            s.ip_version=4;
            break; //Success
        }
        close(s.fd);/*fail, try next one*/
        res = res->ai_next;
    }
    freeaddrinfo(results);
    if (nBindRet < 0 || !isBound)
    {
         std::cout << "Failed to bind" << std::endl;
    }
}
       

Question is: With above configuration and code I should get only IPv4 address as output of getaddrinfo function but why am I getting IPv6 address? This behavior is for RHEL 8 system only, for RHEL 7 it is returning only IPv4 address.

  • 1
    What is your question? – eerorika Feb 18 '22 at 11:34
  • Thank you for pointing out. I have already mentioned the question in between but did not highlighted. So again added at the end. – sourabh kesharwani Feb 18 '22 at 15:24
  • 1
    Disabling IPv6 networking has no relation to resolver. It will still query for both types of addresses. Please see this [long discussion](https://bugzilla.redhat.com/show_bug.cgi?id=1027452) and this small [fix](https://github.com/ossobv/nss-dns4only). – gudok Feb 18 '22 at 20:00
  • Do you have a literal `net.ipv6.conf..disable_ipv6 = 1` there? the `..`? Strikes me odd, and quick look at docs doesn't mention it as a feature. – domen Feb 21 '22 at 16:00
  • @gudok: Thank for your comment. I think I have already gone through that long discussion. About the fix so I will try that and let you know. – sourabh kesharwani Feb 22 '22 at 13:10
  • @domen: I have mentioned the link from where I got that line. I agree it might be incorrect but as I mentioned these options are working as expected on an RHEL 7. – sourabh kesharwani Feb 22 '22 at 13:11
  • What do you expect from this website? I work with the assumption that what you wrote is true, so I'm not in any way disputing that what you said works or not. I did notice something odd in configuration, and it makes sense to maybe have a look into it (I assume you're out of ideas, hence asking here). – domen Feb 22 '22 at 15:20
  • I know that line is as you wrote on the website, I had checked yesterday. It also contains quite illuminating comment, which you deleted. – domen Feb 22 '22 at 15:21
  • I have raised a support ticket for this with RHEL. After communicating multiple times they suggested one work around to resolve this issue. The workaround is to put one change in nsswitch.conf file. There will be a line in the nsswitch.conf file like “hosts: files dns myhostname”. They suggested to remove myhostname and reboot the system. It worked for me. So the line will look like like “hosts: files dns”. – sourabh kesharwani Mar 09 '22 at 11:40
  • One more way that I found out to resolve this issue is changing the address family in the code. So instead of passing AF_INET6 one can pass AF_UNSPEC and it will result in both family addresses. so in above code need to update line as hints.ai_family = AF_UNSPEC. This change worked as expected irrespective of platform. – sourabh kesharwani Mar 09 '22 at 11:50

0 Answers0