Problem with converting IPv6 addresses to strings using ipv6_addr_to_str

Hello everyone :slight_smile:

I am trying to retrieve an IPv6 address with the “highest” avaible scope and convert it into a string. While retrieving the IP addresses and determining their scope works fine I have, however, problems converting an ipv6_addr_t * pointer into a sting using ipv6_addr_to_str. Despite the fact that I used the same code as in the definition of ipv6_addr_print (see here), ipv6_addr_to_str changes the ip address that is represented by the string. Using ipv6_addr_print itself, however, prints out the correct result. I encountered this behavior both on native as well as on an ESP32.

Is there some way for me to fix this or could this be a bug within the ipv6_addr_to_str function? Thanks in advance!

You should be able to reproduce the problem using the following code and make all term:

main.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "xtimer.h"
#include "shell.h"
#include "fmt.h"
#include "net/netif.h"
#include "net/ipv6/addr.h"

#define MAIN_QUEUE_SIZE (4)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];

static ipv6_addr_t *get_base_ip_address(void){
    const int MAX_ADRESSES_TO_CHECK = 5;
    netif_t* interface = NULL;
    ipv6_addr_t* local_address = NULL;
    ipv6_addr_t* ula_address = NULL;

    while ((interface = netif_iter(interface)) != NULL) {
        ipv6_addr_t adresses[MAX_ADRESSES_TO_CHECK];
        netif_get_opt(interface, NETOPT_IPV6_ADDR, 0, adresses, sizeof(adresses));
        for (int i = 0; i < MAX_ADRESSES_TO_CHECK; i++)
        {
            ipv6_addr_t* current_address = &adresses[i];

            if (current_address == NULL) {
                break;
            }
            printf("Loop begin\n");
            if (ipv6_addr_is_global(current_address)) {
                printf("Global address: ");
                ipv6_addr_print(current_address);
                printf("\n");
                printf("Loop end\n");
                return current_address;
            }
            else if (ipv6_addr_is_unique_local_unicast(current_address)) {
                printf("Unique local unicast: ");
                ula_address = current_address;
            }
            else if (ipv6_addr_is_link_local(current_address)) {
                printf("Link local address: ");
                local_address = current_address;
            }
            ipv6_addr_print(current_address);
            printf("\n");
            printf("Loop end\n");
        }
    }

    if (ula_address != NULL) {
        return ula_address;
        return 0;
    }
    else if (local_address != NULL) {
        return local_address;
    }
    return NULL;
}

int ip_test_cmd(int argc, char **argv)
{
    if (argc != 2)
    {
        (void)puts("Usage: iptest test");
        return 1;
    }
    if (strcmp(argv[1], "test") == 0) 
    {
        char address_as_string[IPV6_ADDR_MAX_STR_LEN];
        ipv6_addr_t *base_ip_adress = get_base_ip_address();
        assert(base_ip_adress);
        printf("ipv6_addr_print:\n");
        ipv6_addr_print(base_ip_adress);
        printf("\nipv6_addr_to_str:\n");
        ipv6_addr_to_str(address_as_string, base_ip_adress, sizeof(address_as_string));
        print_str(address_as_string);
        printf("\n");
        return 0;
    }

    return 1;
}

static const shell_command_t shell_commands[] = {
    {"iptest", "Get an IP address of the highest order possible.", ip_test_cmd},
    {NULL, NULL, NULL}};

int main(void)
{
    xtimer_sleep(3);
    /* for the thread running the shell */
    msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);

    /* start shell */
    puts("All up, running the shell now");
    char line_buf[SHELL_DEFAULT_BUFSIZE];
    shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);

    return 0;
}

Makefile:

# name of your application
APPLICATION = ipaddr_test

# If no BOARD is found in the environment, use this default:
BOARD ?= native

# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..

# Include packages that pull up and auto-init the link layer.
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
USEMODULE += gnrc_netdev_default
USEMODULE += auto_init_gnrc_netif
# Activate ICMPv6 error messages
USEMODULE += gnrc_icmpv6_error
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_router_default
USEMODULE += gnrc_udp
# Add a routing protocol
USEMODULE += gnrc_rpl
USEMODULE += auto_init_gnrc_rpl
# This application dumps received packets to STDIO using the pktdump module
USEMODULE += gnrc_pktdump
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
# Add also the shell, some shell commands
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
USEMODULE += netstats_l2
USEMODULE += netstats_ipv6
USEMODULE += netstats_rpl
USEMODULE += fmt

# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1

# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

include $(RIOTBASE)/Makefile.include

Hi @JKRhb , I’m not sure of your problem but might help knowing that ipvN_addr_from_str (being N, 4 or 6) modifies the input buffer. I had a similar issue some time ago, and I had to check how it was coded to solve it. Maybe is happening similar with your issue.

Have a nice one :slight_smile:

1 Like

Hi, you’re returning the pointer to an ipv6_addr_t struct from addresses, which is defined in get_base_ip_address. I’m think the pointer will be undefined after returning to the function.

The better way to return the address after calling get_base_ip_address would be to accept a ipv6_addr_t struct pointer as parameter and in get_base_ip_address, use memcpy to copy the found addres to the parameter back.

Something like this:

int get_base_ip_address(ipv6_addr_t *res) {
    [....]
    if (ipv6_addr_is_global(current_address) {
        memcpy(res, current_address, sizeof(ipv6_addr_t));
        return 0;
    }
}


ipv6_addr_t base_ip_address = {0};
if (get_base_ip_address(&base_ip_address) < 0) {
    return -1;
}
2 Likes

Works like a charm now :slight_smile: Thank you so much!