Problems comparing two IPv6 addresses

5

I have recently been working on a number of "test-codes" to exercise the "head", specifically codes involving Unix sockets . The most recent code I wrote was a simple server that receives a new connection from one client every 2 seconds. The main idea behind the code was to simply compare the clients' addresses and see if they were not the same ones that successfully connected to a previous connection.

Here is the server code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <netdb.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT "9009"

short int check_addr(struct sockaddr aux,
                     const struct sockaddr *vet, size_t amount)
{
    short int rv=0;

    struct sockaddr_in *aux4=NULL, *addr4=NULL;
    struct sockaddr_in6 *aux6=NULL, *addr6=NULL;

    if(aux.sa_family==AF_INET){

        aux4=(struct sockaddr_in*)&aux;
        addr4=(struct sockaddr_in*)vet;

        for(size_t i=0; i<amount; i++){

            if(aux4->sin_addr.s_addr==addr4[i].sin_addr.s_addr){

                rv=-1;
                break;
            }
        }

    }else if(aux.sa_family==AF_INET6){

        aux6=(struct sockaddr_in6*)&aux;
        addr6=(struct sockaddr_in6*)vet;

        for(size_t i=0; i<amount; i++){

            if(aux6->sin6_addr.s6_addr==addr6[i].sin6_addr.s6_addr){

                rv=-1;
                break;
            }
        }

    }else{

        rv=-2;
    }

    return rv;
}

void show_addr(struct sockaddr addr){

    char address[1024];

    struct sockaddr_in *addr4=NULL;
    struct sockaddr_in6 *addr6=NULL;

    if(addr.sa_family==AF_INET){

        addr4=(struct sockaddr_in*)&addr;

        inet_ntop(AF_INET,
                  &addr4->sin_addr.s_addr, address, INET_ADDRSTRLEN);

    }else{

        addr6=(struct sockaddr_in6*)&addr;

        inet_ntop(AF_INET6,
                  &addr6->sin6_addr.s6_addr, address, INET6_ADDRSTRLEN);
    }

    printf("%s\n", address);
}

int make_server_socket(void){

    int sockfd;
    struct addrinfo *res=NULL, *ptr=NULL, hints;

    memset(&hints, 0, sizeof(hints));

    hints.ai_flags=AI_PASSIVE;
    hints.ai_family=AF_INET6;
    hints.ai_socktype=SOCK_STREAM;
    hints.ai_protocol=IPPROTO_TCP;

    if(getaddrinfo(NULL, PORT, &hints, &res)!=0){

        sockfd=-1;

    }else{

        for(ptr=res; ptr!=NULL; ptr=ptr->ai_next){

            if(ptr->ai_family==AF_INET || ptr->ai_family==AF_INET6){

                sockfd=socket(ptr->ai_family, SOCK_STREAM, IPPROTO_TCP);

                if(sockfd<0){

                    continue;
                }

                if(bind(sockfd, ptr->ai_addr, ptr->ai_addrlen)==0){

                    break;
                }

                close(sockfd);
            }
        }

        freeaddrinfo(res);
    }

    return sockfd;
}

int main(void){

    int sockfd=make_server_socket();

    if(sockfd<0){

        exit(1);
    }

    if(listen(sockfd, 10)<0){

        exit(1);
    }

    int csockfd;
    size_t count=0;
    struct sockaddr *vet=NULL, aux;
    socklen_t addrlen=sizeof(struct sockaddr);

    for(size_t i=0; i<1000; i++){

         csockfd=accept(sockfd, &aux, &addrlen);

         if(csockfd<0){

            continue;

         }else{

            if(check_addr(aux, vet, count)==0){

                count++;
                vet=realloc(vet, addrlen*count);

                vet[i]=aux;

                show_addr(vet[i]);

                close(csockfd);
            }
        }
    }

    close(sockfd);

    free(vet);
    vet=NULL;

    return 0;
}

As not everything in the programming is flowers my server ended up presenting the following problems:

1st - The function check_addr() can only notice difference between IPv4 , thus making the acceptance of several connections coming from the same client / ip that uses IPv6 . For a better understanding of the problem described here, see the following demostration below:

Accepting client connections using IPv6 ( AF_INET6 ):

zherkezhi@zherkezhi :~/Documents/C$ ./server

2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::
2804:d47:1b17:2100::

Accepting client connections using IPv4 ( AF_INET ):

zherkezhi@zherkezhi :~/Documents/C$ ./server

192.168.1.123

In the case above, I had my client try to connect to the 12x server using the first time IPv6 and the second IPv4 . With IPv6 presented problems (the one described above) and with IPv4 the same did not occur.

2nd - The function inet_ntop() seems to be working only with IPv4 , that is, when it comes to IPv6 :

Customer

 zherkezhi@zherkezhi :~/Documents/C$ nc 

 2804:d47:1b17:2100:XXXX:XXXX:XXXX:XXXX 9009

Server

 zherkezhi@zherkezhi :~/Documents/C$ ./server

 2804:d47:1b17:2100::

It was to print 2804:d47:1b17:2100:XXXX:XXXX:XXXX:XXXX and not 2804:d47:1b17:2100:: , that is, the address printing is incorrect / incomplete.

So what would be a possible solution to the problems presented above?

NOTE: I did not post client code here because I found it unnecessary, since my client is only a netcat call every 2 seconds.     

asked by anonymous 31.10.2018 / 05:53

1 answer

2

IPv6 addresses are usually abbreviated to eliminate long zeros sequences. For example:

2804:d47:1b17:2100:000:0000:0000:0000

is represented by

2804:d47:1b17:2100::

You can do IPv6 address normalization tests on this site

There is no automatic way to do this normalization via program, you have to write code. See this question from the English OS to get an idea of what to do.

UPDATE:

You're doing the comparison the wrong way

if (aux6->sin6_addr.s6_addr == addr6[i].sin6_addr.s6_addr)

Because s6_addr is an array of 16 characters, it is not a scalar value. The result will always be false because the command is comparing two different memory addresses.

You should use memcmp function:

if (memcmp(aux6->sin6_addr.s6_addr, addr6[i].sin6_addr.s6_addr, 16) == 0)

UPDATE

A copy of the addresses received by the server must be saved in the table. These lines

vet = realloc(vet, addrlen*count);
vet[i] = aux; //<----erro no caso de IPv6

are wrong (in the case of IPv6), you must do a malloc of a sockaddr_in structure, copy "aux" to the allocated memory, and save the address of the allocated memory in the table.

As it is difficult to explain in words, I refined the program in a simplified way, contemplating only IPv6. Even so, it would be better to save the result of inet_ntop in the table, not "addr6.sin6_addr.6_addr" because it is very easy to make mistakes in doing so.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <netdb.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT "9009"

static void print_hex(struct sockaddr_in6* addr6)
{
  size_t i;
  unsigned char* buf = (unsigned char*)(addr6->sin6_addr.s6_addr);
  for (i = 0; i < 16; i++)
  {
    printf("%02X|", buf[i]);
  }
}

// retorno
//   0: nao encontrou na tabela
//  -1: encontrou na tabela
//  -2: familia invalida
short int check_addr(const struct sockaddr* addr, struct sockaddr *addrTable[], size_t n)
{
  if (n == 0)
  {
    printf("* primeiro endereco, nao vai verificar\n");
    return 0;
  }

  size_t i;
  struct sockaddr_in6 *addr1 = (struct sockaddr_in6*)addr;

  for (i = 0; i < n; i++)
  {
    struct sockaddr_in6 *addr2 = (struct sockaddr_in6*)addrTable[i];
    printf("* comparando enderecos do indice %d\n", i);
    printf("* primeiro endereco: ");
    print_hex(addr1);
    printf("\n");
    printf("* segundo endereco: ");
    print_hex(addr2);
    printf("\n");
    if (memcmp(addr1->sin6_addr.s6_addr, addr2[i].sin6_addr.s6_addr, 16) == 0)
      return -1;
  }

  return 0;
}

void show_addr6(struct sockaddr_in6* addr6)
{
  char address[INET6_ADDRSTRLEN];
  printf("* show_addr: addr6: ");
  print_hex(addr6);
  printf("\n");
  inet_ntop(AF_INET6, &addr6->sin6_addr.s6_addr, address, INET6_ADDRSTRLEN);
  printf("* from ntop: %s\n", address);
}

int make_server_socket(void)
{
  int sockfd;
  struct addrinfo *res = NULL, *ptr, hints;

  memset(&hints, 0, sizeof(hints));

  hints.ai_flags = AI_PASSIVE;
  hints.ai_family = AF_INET6;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;

  if (getaddrinfo(NULL, PORT, &hints, &res) != 0)
  {
    printf("* erro em getaddrinfo\n");
    exit(1);
  }

  for (ptr = res; ptr != NULL; ptr = ptr->ai_next)
  {
    if (ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6)
    {
      sockfd = socket(ptr->ai_family, SOCK_STREAM, IPPROTO_TCP);
      if (sockfd < 0)
        continue;

      if (bind(sockfd, ptr->ai_addr, ptr->ai_addrlen) == 0)
        break;

      close(sockfd);
      sockfd = -1;
    }
  }

  if (sockfd < 0)
  {
    printf("* endereco ip invalido\n");
    exit(1);
  }

  freeaddrinfo(res);

  return sockfd;
}

int main(void)
{
  size_t i;
  int csockfd;
  socklen_t addrlen;

  struct sockaddr addr;
  struct sockaddr *addrTable[1000];
  struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&addr;

  int sockfd = make_server_socket();

  if (listen(sockfd, 10) < 0)
    exit(1);

  for (i = 0; i < 1000; i++)
  {
    printf("*\n");
    printf("* aguardando conexao\n");
    addrlen = sizeof(struct sockaddr);
    csockfd = accept(sockfd, &addr, &addrlen);

    if (csockfd < 0)
    {
      printf("* erro no accept\");
      exit(1);
    }

    if (addr.sa_family != AF_INET6)
    {
      printf("* conexao nao e' ipv6\");
      exit(1);
    }

    printf("* endereco IPv6 do client: ");
    print_hex(addr6);
    printf("\n");

    if (check_addr(&addr, addrTable, i) == 0)
    {
      struct sockaddr_in6* saveAddr6 = malloc(sizeof(struct sockaddr));
      void* from = &addr;
      void* to = saveAddr6;
      memcpy(to, from, sizeof(struct sockaddr));

      printf("* saveAddr6: ");
      print_hex(saveAddr6);
      printf("\n");
      addrTable[i] = (struct sockaddr*)saveAddr6;
      show_addr6(saveAddr6);
    }

    close(csockfd);
  }

  return 0;
}

Running local telnet the address that appears is: (strange, thought it would be: 1), connecting from my cell:

zv@localhost so]$  ./test-ip6
*
* aguardando conexao
* endereco IPv6 do client: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
* primeiro endereco, nao vai verificar
* saveAddr6: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
* show_addr: addr6: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
* from ntop: 2804:14d:3280:45a1:: <-------------------
*
* aguardando conexao
* endereco IPv6 do client: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
* comparando enderecos do indice 0
* primeiro endereco: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
* segundo endereco: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
*
* aguardando conexao
* endereco IPv6 do client: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
* comparando enderecos do indice 0
* primeiro endereco: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
* segundo endereco: 28|04|01|4D|32|80|45|A1|00|00|00|00|00|00|00|00|
    
31.10.2018 / 13:26