Problems comparing two Ipv6 addresses

Asked

Viewed 112 times

5

Lately I’ve been working on several "test codes" to exercise the "head", specifically codes involving the API of sockets of Unix. The most recent code I wrote was a simple server that receives a new connection from a client every 2 seconds. The main idea behind the code was to simply compare customer addresses and see if they weren’t the same who successfully connected on a previous connection.

Here’s 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 are flowers my server ended up presenting the following problems:

first - The function check_addr() can only tell the 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 below the following demostration:

Accepting connections from a client 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 connections from a client using IPv4 (AF_INET):

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

192.168.1.123

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

2nd - The function inet_ntop() apparently is working only with Ipv4, that is, when it comes to Ipv6 the following situation occurs:

Client

 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 inaccuracy of the address is incorrect/incomplete.

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

OBS: I didn’t post the client code here because I felt it was unnecessary, since my client is nothing more than a phone call from netcat every 2 seconds.

1 answer

2


Ipv6 addresses are usually abbreviated to eliminate long sequences of zeros. 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 website

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

UPDATE:

you are making the comparison the wrong way

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

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

You must 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 stored 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 need to make 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 redid the program in a simplified way, contemplating only Ipv6. Even so, it would be better to save in the table the result of inet_ntop, and not "addr6.sin6_addr.6_addr", because it is very easy to make mistakes 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 telnet local the address that appears is :: (strange, I thought it would be ::1), connecting from my mobile:

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|
  • 1

    but in my case my external Ipv6 does not show these 0’s but other values not 0’s. And in the case of the failure when comparing two Ipv6 to each other? Because the comparison fails?

  • I used the function memcmp and did not work, continued accepting the same connections.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.