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.