Chapter 78: IPv6 Implementation and Configuration

Chapter Objectives

Upon completing this chapter, you will be able to:

  • Understand the necessity and advantages of Internet Protocol version 6 (IPv6) over IPv4.
  • Comprehend the structure and types of IPv6 addresses.
  • Explain the concepts of Stateless Address Autoconfiguration (SLAAC) and DHCPv6 for IP address assignment.
  • Configure ESP32 devices to support IPv6 using the ESP-IDF v5.x framework.
  • Implement a basic IPv6 application to obtain an IPv6 address.
  • Troubleshoot common issues related to IPv6 deployment on ESP32.

Introduction

As the number of interconnected devices continues to explode, particularly in the realm of the Internet of Things (IoT), the limitations of IPv4’s 32-bit address space have become increasingly apparent. IPv4, with its approximately 4.3 billion unique addresses, is simply running out. Internet Protocol version 6 (IPv6) was designed to address this fundamental challenge, offering a massive 128-bit address space that can accommodate virtually every device imaginable. Beyond just addressing, IPv6 introduces several enhancements, including improved routing efficiency, enhanced security features, and simplified network configuration.

For embedded systems like the ESP32, embracing IPv6 is not just a future-proofing measure but a necessity for seamless integration into modern network infrastructures. This chapter will guide you through the theoretical underpinnings of IPv6, its various addressing mechanisms, and practical steps to implement and configure IPv6 on your ESP32 devices using the ESP-IDF v5.x framework.

Theory

1. Why IPv6? The Limitations of IPv4

IPv4 has served the Internet well for decades, but its design limitations are now critical bottlenecks. The primary issue is the exhaustion of its 32-bit address space, leading to widespread use of Network Address Translation (NAT) to conserve public IPv4 addresses. While NAT has extended IPv4’s lifespan, it complicates peer-to-peer communication, introduces latency, and adds complexity to network management.

IPv6, with its 128-bit address space, provides 2128 (approximately 3.4×1038) unique addresses, an astronomical number that ensures every grain of sand on Earth could have multiple IP addresses. This eliminates the need for NAT for address conservation, simplifying network design and enabling true end-to-end connectivity.

Feature IPv4 IPv6
Address Space 32-bit (approx. 4.3 billion addresses) 128-bit (approx. 3.4 x 1038 addresses)
Address Notation Dotted Decimal (e.g., 192.168.1.1) Hexadecimal with colons (e.g., 2001:db8::1)
Address Exhaustion Major issue, leading to NAT Effectively limitless, reduces need for NAT
Header Format More complex, variable length options Simplified, fixed-size base header, extension headers
Address Configuration Manual, DHCP Manual, SLAAC, DHCPv6 (Stateful/Stateless)
Broadcast Uses broadcast addresses No broadcast; uses multicast
Security (IPSec) Optional Mandatory support (though not always implemented/enforced)
NAT Requirement Often required for public access from private networks Generally not required for address conservation; enables true end-to-end
Mobility Mobile IP exists, but less integrated Improved support for mobile nodes (Mobile IPv6)
Packet Fragmentation Routers and sending host Sending host only

Beyond addressing, IPv6 offers:

  • Simplified Header: A more streamlined header format for faster processing by routers.
  • No Broadcasts: Replaced by multicast, reducing network traffic.
  • Built-in IPSec: Mandatory support for IPSec (IP Security) for enhanced security.
  • Stateless Address Autoconfiguration (SLAAC): Devices can automatically configure their own IP addresses without a DHCP server.
  • Improved Mobility: Better support for mobile devices.

2. IPv6 Address Format

An IPv6 address is 128 bits long, typically represented as eight groups of four hexadecimal digits, separated by colons.

Example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334

%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%%
graph TD
    A["<b>Full IPv6 Address (128 bits)</b><br>8 groups of 4 hexadecimal digits, separated by colons"] --> B("Example:<br><b>2001:0db8:85a3:0000:0000:8a2e:0370:7334</b>");
    
    B --> C["<b>Rule 1: Omit Leading Zeros</b><br>In any 4-digit group, leading zeros can be removed.<br><br>Example:<br><code style='color:#DC2626;'>0</code>db8 → db8<br><code style='color:#DC2626;'>0000</code> → 0<br><code style='color:#DC2626;'>0</code>370 → 370"];
    C --> D("Applied:<br><b>2001:db8:85a3:0:0:8a2e:370:7334</b>");

    D --> E["<b>Rule 2: Consecutive Zeros (Double Colon ::)</b><br>One sequence of consecutive zero groups can be replaced by '::'.<br><b>This can only be used ONCE per address.</b>"];
    E -- "Option 1 (zeros at 0:0)" --> F1("Applied to <b>2001:db8:85a3:<mark>0:0</mark>:8a2e:370:7334</b><br>→ <b>2001:db8:85a3::8a2e:370:7334</b>");
    
    E -- "Another Example" --> G["Full: fe80:0000:0000:0000:0202:b3ff:fe1e:8329"];
    G -- "Leading Zeros" --> H["fe80:0:0:0:202:b3ff:fe1e:8329"];
    H -- "Double Colon" --> I["Applied to <b>fe80:<mark>0:0:0</mark>:202:b3ff:fe1e:8329</b><br>→ <b>fe80::202:b3ff:fe1e:8329</b>"];

    classDef title fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6;
    classDef rule fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E;
    classDef example fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF;
    classDef result fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46;

    class A title;
    class C,E rule;
    class B,D,G,H example;
    class F1,I result;

Rules for Abbreviation:

  • Leading Zeros: Leading zeros in any 4-digit group can be omitted.
    • 0db8 becomes db8
    • 0000 becomes 0
  • Consecutive Zeros (Double Colon): A single :: (double colon) can be used to represent one or more consecutive groups of zeros. This can only be used once per address.
    • 2001:0db8:85a3:0000:0000:8a2e:0370:7334 can be 2001:db8:85a3::8a2e:370:7334
    • fe80:0000:0000:0000:0202:b3ff:fe1e:8329 can be fe80::202:b3ff:fe1e:8329

3. Types of IPv6 Addresses

IPv6 defines three primary types of addresses:

  • Unicast Address: Identifies a single network interface. Packets sent to a unicast address are delivered to that specific interface.
    • Global Unicast Address (GUA): Equivalent to public IPv4 addresses. Routable on the Internet. Typically start with 2 or 3.
    • Link-Local Address (LLA): Used for communication only within a single network segment (link). They are automatically configured on every interface and start with fe80::/10. They are not routable beyond the local link.
    • Unique Local Address (ULA): Similar to private IPv4 addresses. Used for communication within a set of private networks and are not routable on the Internet. They start with fc00::/7 or fd00::/8.
  • Multicast Address: Identifies a group of interfaces. Packets sent to a multicast address are delivered to all interfaces that are members of that group. Multicast addresses start with ff00::/8. There is no broadcast address in IPv6; its functionality is replaced by multicast.
  • Anycast Address: Identifies a group of interfaces, typically geographically dispersed, that provide the same service. Packets sent to an anycast address are delivered to the nearest interface in the group (according to routing protocols).
Address Type Sub-Type / Prefix Typical Starting Bits / Range Scope / Reachability Purpose
Unicast
(Identifies a single interface)
Global Unicast (GUA) 2000::/3 (commonly 2xxx: or 3xxx:) Global (Internet routable) Publicly accessible devices on the Internet.
Link-Local (LLA) fe80::/10 Link-Local (only on the same physical/logical link, not routable) Communication between devices on the same local segment; NDP. Automatically configured.
Unique Local (ULA) fc00::/7 (commonly fdxx:) Site-Local (routable within a cooperating set of sites, not on global Internet) Private addressing within an organization, similar to IPv4 private ranges.
Multicast
(Identifies a group of interfaces)
Various specific types ff00::/8 Varies by specific multicast address (e.g., link-local, site-local, global) One-to-many communication; replaces broadcast. Used by NDP, routing protocols, streaming.
Anycast
(Identifies one of a group of interfaces, typically the “nearest”)
Allocated from Unicast space Same as Unicast (no distinct prefix) Global or Local (depends on advertisement) Service discovery, load balancing (e.g., nearest DNS server).
Loopback ::1/128 ::1 Host-Local (only on the device itself) Testing network stack on the local host.
%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%%
graph TD
    subgraph "IPv6 Address Scopes & Reachability"
        Global["<b>Global Unicast Addresses (GUA)</b><br>e.g., 2001:db8::/32<br><b>Scope: Entire Internet</b><br>Publicly routable"]
        
        subgraph "Site / Organization Scope"
            ULA["<b>Unique Local Addresses (ULA)</b><br>fd00::/8<br><b>Scope: Within an organization or site(s)</b><br>Not globally routable, like private IPv4"]
        end

        subgraph "Local Link Scope"
            LLA["<b>Link-Local Addresses (LLA)</b><br>fe80::/10<br><b>Scope: Single network segment/link</b><br>Not routable beyond the local link"]
        end
        
        Multicast["<b>Multicast Addresses</b><br>ff00::/8<br><b>Scope: Varies (e.g., ff02:: is Link-Local)</b><br>One-to-many communication"]
        
        Loopback["<b>Loopback Address</b><br>::1<br><b>Scope: Host itself</b><br>For local testing"]
    end

    Global --> ULA;
    ULA --> LLA;
    Global -.-> Multicast;
    ULA -.-> Multicast;
    LLA -.-> Multicast;
    
    classDef global fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46;
    classDef site fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E;
    classDef linklocal fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF;
    classDef multi fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B;
    classDef loop fill:#EDE9FE,stroke:#5B21B6,stroke-width:1px,color:#5B21B6;

    class Global global;
    class ULA site;
    class LLA linklocal;
    class Multicast multi;
    class Loopback loop;

4. Interface Identifiers and EUI-64

The host portion of an IPv6 address (the last 64 bits for most address types) is called the Interface Identifier (IID). It uniquely identifies the interface within a subnet.

A common method to generate the IID is Modified EUI-64 (Extended Unique Identifier). This method derives the 64-bit IID from the interface’s 48-bit MAC address.

EUI-64 Conversion:

%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%%
graph TD
    Start["Start: Generate Interface ID (IID) using EUI-64"] --> MAC["1- Take 48-bit MAC Address<br>Example: <b>00:1A:2B:3C:4D:5E</b>"];
    
    MAC --> InsertFFFE["2- Insert 'FF:FE' in the middle<br>(Splitting MAC into two 24-bit halves)<br><br>00:1A:2B : 3C:4D:5E<br>    ↓<br>00:1A:2B:<code style='color:#059669;'>FF:FE</code>:3C:4D:5E"];
    
    InsertFFFE --> FlipBit["3- Flip the 7th bit (U/L bit) of the first octet<br>Original first octet: 00 (binary <code style='color:#DC2626;'>00</code>000000)<br>7th bit is 0. Flip to 1.<br>New first octet: 02 (binary <code style='color:#059669;'>00</code>000010)"];
    
    FlipBit --> Result["4 Resulting 64-bit EUI-64 Interface ID<br><b>02:1A:2B:FF:FE:3C:4D:5E</b><br><br>This forms the last 64 bits of many IPv6 addresses."];
    Result --> End["End: IID Generated"];

    classDef startEnd fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6;
    classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF;
    classDef importantStep fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E;
    classDef finalResult fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46;

    class Start startEnd;
    class End startEnd;
    class MAC process;
    class InsertFFFE process;
    class FlipBit process;
    class Result finalResult;
  1. Take the 48-bit MAC address (e.g., 00:1A:2B:3C:4D:5E).
  2. Insert FF:FE in the middle: 00:1A:2B:FF:FE:3C:4D:5E.
  3. Flip the 7th bit (Universal/Local bit) of the first octet. If the 7th bit is 0, change to 1; if 1, change to 0. (For 00, it becomes 02).
  4. Resulting EUI-64: 02:1A:2B:FF:FE:3C:4D:5E. This forms the 64-bit IID.

5. IPv6 Address Assignment Methods

Devices can obtain IPv6 addresses through various mechanisms:

  • Stateless Address Autoconfiguration (SLAAC): This is the most common method. Devices automatically configure their own IPv6 addresses based on information received from routers.
    1. The device generates a Link-Local address (LLA) using its MAC address.
    2. It sends a Router Solicitation (RS) message (multicast) to discover routers.
    3. Routers respond with Router Advertisement (RA) messages, containing network prefixes and other configuration information.
    4. The device combines the received network prefix with its self-generated Interface Identifier (often EUI-64 derived) to form a Global Unicast Address.
    5. The device performs Duplicate Address Detection (DAD) to ensure the address is unique on the link. SLAAC provides addresses but typically doesn’t provide DNS server information.
  • Stateful DHCPv6: Similar to DHCP for IPv4, a DHCPv6 server explicitly assigns full IPv6 addresses and other configuration parameters (like DNS servers) to clients. This is used when more centralized control over addressing is required.
  • Stateless DHCPv6: A hybrid approach where SLAAC is used for address assignment, but DHCPv6 is used to provide other configuration information (e.g., DNS servers) that SLAAC doesn’t provide. Routers indicate this option in their RA messages.
  • Manual/Static Configuration: IPv6 addresses can also be manually assigned, similar to static IPv4.
Method Address Source Other Config (e.g., DNS) Server Required Key Characteristics
SLAAC
(Stateless Address Autoconfiguration)
Device generates IID (e.g., EUI-64) + Router Advertisement (RA) prefix Typically not provided by SLAAC itself (may use RDNSS option in RA) Router (for RA messages) Default, simple, device-driven, automatic. Duplicate Address Detection (DAD) performed.
Stateful DHCPv6 DHCPv6 Server assigns full address Provided by DHCPv6 Server DHCPv6 Server Centralized control, explicit address assignment, similar to IPv4 DHCP.
Stateless DHCPv6 Address from SLAAC Provided by DHCPv6 Server (e.g., DNS, NTP) DHCPv6 Server (for “other config”) + Router (for RA) Hybrid: SLAAC for address, DHCPv6 for additional parameters. Router’s RA indicates this mode.
Manual/Static Configuration Administrator assigns full address Manually configured by administrator None Fixed, predictable address. Requires careful management to avoid conflicts.

6. Neighbor Discovery Protocol (NDP)

NDP is a crucial protocol in IPv6 that replaces several IPv4 protocols (ARP, ICMP Router Discovery, ICMP Redirect). NDP operates on the local link and uses ICMPv6 messages for various functions:

NDP Message Type (ICMPv6 Type) Sent By Sent To (Typically) Purpose
Router Solicitation (RS)
(Type 133)
Host/Device All-Routers Multicast Address (ff02::2) To request routers on the link to send Router Advertisements immediately.
Router Advertisement (RA)
(Type 134)
Router All-Nodes Multicast Address (ff02::1) or Source of RS (Unicast) To advertise router presence, network prefixes, link parameters (MTU, hop limit), and other configuration flags (e.g., for SLAAC, DHCPv6).
Neighbor Solicitation (NS)
(Type 135)
Host/Device or Router Solicited-Node Multicast Address (derived from target IPv6) or Target IPv6 (Unicast for DAD) To resolve a target IPv6 address to its link-layer (MAC) address. Also used for Duplicate Address Detection (DAD) to check if an address is already in use.
Neighbor Advertisement (NA)
(Type 136)
Host/Device or Router Source of NS (Unicast) or All-Nodes Multicast Address (ff02::1 for unsolicited updates) To respond to an NS message, providing link-layer address. Also sent unsolicited to announce link-layer address changes.
Redirect
(Type 137)
Router Originating Host (Unicast) To inform a host of a better first-hop router for a specific destination on the same link.
%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%%
sequenceDiagram;
    participant Client as ESP32 Device (Host);
    participant Router as IPv6 Router;

    Client->>Client: 1. Generate Link-Local Address (LLA)<br>(e.g., fe80:: + EUI-64 IID);
    Client->>Router: 2. Router Solicitation (RS)<br>(ICMPv6 Type 133, to ff02::2)<br>"Any routers out there?";
    
    Router-->>Client: 3. Router Advertisement (RA)<br>(ICMPv6 Type 134, from LLA to ff02::1 or Client's LLA)<br>"I'm a router! Here's prefix: 2001:db8:cafe::/64";
    
    Client->>Client: 4. Form Global Unicast Address (GUA)<br>(Prefix from RA + IID)<br>e.g., 2001:db8:cafe::[EUI-64 IID];
    
    Client->>Router: 5. Duplicate Address Detection (DAD)<br>Sends Neighbor Solicitation (NS) for its new GUA<br>(ICMPv6 Type 135, to solicited-node multicast);
    Note right of Client: If no Neighbor Advertisement (NA) received,<br>address is unique and usable.;
    Note left of Router: Router may also perform DAD or have other mechanisms.

7. IPv6 on ESP-IDF

The ESP-IDF’s esp_netif component fully supports IPv6. When a network interface (Wi-Fi STA, Ethernet) is brought up, it automatically starts the IPv6 address autoconfiguration process (SLAAC by default). You can register event handlers to be notified when IPv6 addresses are obtained.

Key concepts in ESP-IDF for IPv6:

  • esp_netif_create_default_wifi_sta() or esp_netif_create_default_eth(): These functions set up the network interface, including default IPv6 capabilities.
  • IP_EVENT_STA_GOT_IP6 or IP_EVENT_ETH_GOT_IP6: Events triggered when an IPv6 address is successfully configured on the interface.
  • esp_netif_get_ip6_info(): Function to retrieve the assigned IPv6 address information.

Practical Examples

This example demonstrates how to enable IPv6 on an ESP32 Wi-Fi station and observe the acquisition of a Link-Local and Global Unicast address via SLAAC.

1. Project Setup

Create a new ESP-IDF project using VS Code. You can use the “ESP-IDF: New Project” command. Name it ipv6_example.

2. main/ipv6_example_main.c

C
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "sdkconfig.h"

static const char *TAG = "IPV6_EXAMPLE";

// Event handler for Wi-Fi and IP events
static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        ESP_LOGI(TAG, "Wi-Fi STA started, connecting...");
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        ESP_LOGI(TAG, "Wi-Fi STA disconnected, retrying connection...");
        esp_wifi_connect(); // Retry connection
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        // This event is for IPv4. We still log it for completeness.
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "Got IPv4 address: " IPSTR, IP2STR(&event->ip_info.ip));
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP6) {
        // This event is for IPv6.
        ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
        char ip6_addr[64];
        esp_ip6addr_ntoa(&event->ip_info.ip, ip6_addr, sizeof(ip6_addr));
        ESP_LOGI(TAG, "Got IPv6 address: %s", ip6_addr);
        // Check the type of IPv6 address (Link-Local, Global Unicast)
        if (esp_netif_is_link_local_ip6(event->esp_netif, &event->ip_info.ip)) {
            ESP_LOGI(TAG, "  (Link-Local Address)");
        } else {
            ESP_LOGI(TAG, "  (Global Unicast Address)");
        }
    }
}

void app_main(void)
{
    // Initialize NVS (Non-Volatile Storage) for Wi-Fi configuration
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // Initialize TCP/IP stack
    ESP_ERROR_CHECK(esp_netif_init());

    // Create default event loop
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // Create Wi-Fi station interface
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    // Register event handlers
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP6, &event_handler, NULL));

    // Initialize Wi-Fi
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // Configure Wi-Fi station mode
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = CONFIG_WIFI_SSID,
            .password = CONFIG_WIFI_PASSWORD,
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
            .sae_pwe_h2e = WIFI_SAE_MODE_OWE, // For WPA3, if supported by AP
            .sae_h2e_identifier = "",
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    
    // Enable IPv6 support
    // This is typically enabled by default with esp_netif_create_default_wifi_sta(),
    // but explicitly enabling it ensures it's active.
    ESP_ERROR_CHECK(esp_netif_set_ip6_addr_type(sta_netif, ESP_NETIF_IP6_ADDR_TYPE_AUTOCONF));

    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "Waiting for IP addresses (IPv4 and IPv6)...");
}

3. main/CMakeLists.txt

The default CMakeLists.txt should be sufficient. Ensure idf_component_register is present.

Plaintext
# In the main/CMakeLists.txt file
idf_component_register(SRCS "ipv6_example_main.c"
                       INCLUDE_DIRS ".")

4. main/Kconfig.projbuild

This file allows you to configure project-specific settings via idf.py menuconfig. Create main/Kconfig.projbuild and add the following:

Plaintext
menu "Network Configuration"
    config WIFI_SSID
        string "Wi-Fi SSID"
        default "YOUR_SSID"
        help
            SSID of your Wi-Fi network.

    config WIFI_PASSWORD
        string "Wi-Fi Password"
        default "YOUR_PASSWORD"
        help
            Password of your Wi-Fi network.
endmenu

5. Build Instructions

  1. Open VS Code: Open your ipv6_example project folder in VS Code.
  2. Configure with menuconfig:
    • Press Ctrl+Shift+P (or Cmd+Shift+P on macOS) to open the command palette.
    • Type and select “ESP-IDF: SDK Configuration Editor (menuconfig)”.
    • Navigate to “Network Configuration”.
    • Enter your Wi-Fi SSID and Password.
    • Crucially, ensure IPv6 is enabled in the ESP-IDF configuration:
      • Navigate to Component config -> LWIP.
      • Check Enable IPv6 support.
      • Under IPv6, ensure Enable IPv6 Stateless Address Autoconfiguration (SLAAC) is checked.
      • You can also explore options for DHCPv6 if your network supports it.
    • Save the configuration and exit menuconfig.
  3. Build the Project:
    • Open the VS Code Terminal (Terminal > New Terminal).
    • Run idf.py build. This will compile the project.

6. Run/Flash/Observe Steps

  1. Connect Hardware:
    • Connect your ESP32 development board to your computer via USB.
  2. Flash the Firmware:
    • In the VS Code Terminal, run idf.py flash. This will erase the chip and flash your compiled firmware.
  3. Monitor Serial Output:
    • After flashing, run idf.py monitor. This will open the serial monitor and display the logs from your ESP32.

Expected Output:

You should see log messages similar to this:

Plaintext
I (XXX) IPV6_EXAMPLE: Waiting for IP addresses (IPv4 and IPv6)...
I (XXX) IPV6_EXAMPLE: Wi-Fi STA started, connecting...
I (XXX) IPV6_EXAMPLE: Got IPv4 address: 192.168.1.105
I (XXX) IPV6_EXAMPLE: Got IPv6 address: fe80::XXXX:XXXX:XXXX:XXXX
I (XXX) IPV6_EXAMPLE:   (Link-Local Address)
I (XXX) IPV6_EXAMPLE: Got IPv6 address: 2001:db8:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
I (XXX) IPV6_EXAMPLE:   (Global Unicast Address)

The XXX represents timestamps and parts of the IP addresses. The specific IPv6 addresses will vary based on your network and MAC address. You will typically see a Link-Local address first, followed by a Global Unicast address if your router supports IPv6 and is sending Router Advertisements.

Variant Notes

The implementation and configuration of IPv6 within the ESP-IDF framework are largely independent of the specific ESP32 variant (ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2).

The esp_netif component provides a unified abstraction layer for network interfaces, meaning the API calls and event handling for IPv6 address acquisition (via SLAAC or DHCPv6) remain consistent across all supported chips and network interfaces (Wi-Fi, Ethernet). The underlying hardware differences, such as the specific Wi-Fi module or the presence/absence of an internal Ethernet MAC, are handled by their respective low-level drivers. Your application code for IPv6 configuration and event processing will remain virtually identical, regardless of the ESP32 variant being used. Therefore, no specific variant notes are required for this chapter on IPv6 implementation.

Common Mistakes & Troubleshooting Tips

Mistake / Issue Symptom(s) Troubleshooting / Solution
IPv6 Not Enabled on Router/Network ESP32 only gets a Link-Local Address (LLA, fe80::), no Global Unicast Address (GUA). Cannot reach IPv6 internet. IP_EVENT_STA_GOT_IP6 fires for LLA only.
  • Check router’s admin interface for IPv6 settings (enable it, e.g., Native, Passthrough, 6to4, etc.).
  • Verify ISP provides IPv6. Test with test-ipv6.com on a PC on the same network.
  • Router must send Router Advertisements (RAs) with a valid prefix.
IPv6 Support Not Enabled in ESP-IDF No IPv6 addresses obtained at all, or errors related to IPv6 functions.
  • Run idf.py menuconfig.
  • Navigate to Component config -> LWIP.
  • Ensure Enable IPv6 support is checked.
  • Ensure Enable IPv6 Stateless Address Autoconfiguration (SLAAC) is checked for SLAAC.
Firewall Blocking ICMPv6 (NDP) SLAAC fails, no GUA. Device may not see Router Advertisements. Pinging link-local addresses might fail.
  • ICMPv6 is essential for IPv6 (NDP uses it).
  • Temporarily disable router/PC firewall for testing.
  • If resolved, configure firewall to allow ICMPv6 types: Router Solicitation (133), Router Advertisement (134), Neighbor Solicitation (135), Neighbor Advertisement (136).
Incorrect Network Interface Setup IPv6 issues stemming from underlying Wi-Fi/Ethernet problems.
  • Ensure basic IPv4 connectivity works (if dual-stack).
  • Verify Wi-Fi SSID/password or Ethernet physical link.
  • Check for WIFI_EVENT_STA_CONNECTED or ETHERNET_EVENT_CONNECTED before expecting IP events.
Lack of DHCPv6 Server (if expecting DHCPv6 for addresses or DNS) SLAAC might provide an address, but DNS or other parameters expected from DHCPv6 are missing. Or, no address if only Stateful DHCPv6 is configured on device and no server exists.
  • Confirm if your network uses SLAAC, Stateless DHCPv6, or Stateful DHCPv6.
  • Most home networks use SLAAC. Routers might provide DNS via RA RDNSS option.
  • If Stateful DHCPv6 is needed, ensure a server is active and configured.
  • Check esp_netif_set_ip6_addr_type() configuration on ESP32.
Duplicate Address Detection (DAD) Failure Rare, but an address (especially manually configured or sometimes SLAAC) might conflict. Device might drop the address.
  • Usually handled automatically by NDP.
  • If using static IPv6, ensure uniqueness.
  • Check logs for DAD-related messages.

Exercises

Exercise 1: Ping an IPv6 Address

Extend the provided example to perform a basic ping to a well-known public IPv6 address (e.g., Google’s IPv6 DNS 2001:4860:4860::8888) once a Global Unicast Address is obtained.

Steps:

  1. Include Ping Headers: Add #include "lwip/sockets.h" and #include "lwip/inet.h" for socket operations, and #include "lwip/netdb.h" for getaddrinfo.
  2. Create Ping Task: Implement a FreeRTOS task (ping6_task) that uses socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), sendto(), and recvfrom() to send ICMPv6 echo requests and receive replies. You’ll need to construct simple ICMPv6 packets.
  3. Trigger Ping: In the event_handler for IP_EVENT_STA_GOT_IP6, if the address is a Global Unicast Address, create and start the ping6_task.
  4. Log Results: Print whether the ping was successful and any round-trip time.

Exercise 2: Display All IPv6 Addresses

Modify the application to retrieve and display all IPv6 addresses (Link-Local, Global Unicast, etc.) assigned to the Wi-Fi interface. An interface can have multiple IPv6 addresses.

Steps:

  1. Iterate Addresses: After IP_EVENT_STA_GOT_IP6 (or in a separate task), use esp_netif_get_all_ip6_info() or iterate through the esp_netif‘s internal list of IP addresses (more advanced, usually not necessary). A simpler approach is to just log each IP_EVENT_STA_GOT_IP6 event as it occurs, as the example already does.
  2. Distinguish Address Types: Enhance the logging to clearly identify each IPv6 address as Link-Local, Global Unicast, or Unique Local (if applicable). Use esp_netif_is_link_local_ip6() and check the prefix for GUAs/ULAs.
  3. Test: Flash the code and observe the serial monitor output showing all assigned IPv6 addresses and their types.

Exercise 3: Simple IPv6 HTTP Client

Extend the IPv6 example to perform a basic HTTP GET request to an IPv6-only website (e.g., http://[2606:2800:220:1:248:1893:25c8:1946]/ which is ipv6.google.com or similar) once a Global Unicast IPv6 address is obtained.

Steps:

  1. Integrate HTTP Client: Include the esp_http_client.h header.
  2. Create HTTP GET Function: Write a function (e.g., http_get_ipv6_task) that uses esp_http_client_init(), esp_http_client_set_url(), esp_http_client_perform(), and esp_http_client_cleanup() to fetch content. Ensure the URL uses square brackets for the IPv6 address if it’s a literal address.
  3. Trigger on IPv6 Event: In the event_handler, after IP_EVENT_STA_GOT_IP6 is received and the address is a Global Unicast Address, create a new FreeRTOS task to run your http_get_ipv6_task.
  4. Parse Response (Optional): Print the HTTP response status code and a portion of the received content to the serial monitor.
  5. Test: Flash the code and ensure your network has IPv6 connectivity. Observe the HTTP request and response in the serial monitor.

Summary

  • IPv6 addresses the limitations of IPv4 by providing a 128-bit address space, enabling a massive number of unique device identifiers.
  • IPv6 addresses are represented in eight groups of four hexadecimal digits separated by colons, with abbreviation rules for leading zeros and consecutive zero groups (::).
  • Key IPv6 address types include Unicast (Global, Link-Local, Unique Local), Multicast, and Anycast.
  • SLAAC (Stateless Address Autoconfiguration) allows devices to automatically configure their IPv6 addresses using Router Advertisements and their MAC address (EUI-64).
  • DHCPv6 provides stateful address assignment and configuration, while Stateless DHCPv6 provides only configuration information alongside SLAAC.
  • NDP (Neighbor Discovery Protocol) is crucial for IPv6 local link operations, replacing ARP and router discovery.
  • The ESP-IDF esp_netif component offers consistent IPv6 support across all ESP32 variants, simplifying implementation.
  • Troubleshooting often involves verifying router IPv6 support, ESP-IDF menuconfig settings, and firewall configurations.

Further Reading

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top