Skip to content

Bug: IPv6 BOB authentication fails on dual-stack nodes #155

Description

@heX16

Summary

On dual-stack nodes, IPv6 .p2p resolution can fail even when the IPv6 address is published in the DHT and is reachable.

The issue is related to dual-stack operation.

KadNode uses separate UDP sockets for IPv4 and IPv6 DHT traffic.
But BOB authentication appears to reuse a single remembered DHT socket (g_dht_socket).

If that remembered socket is the IPv4 socket, IPv6 BOB challenges be sent through the wrong socket family.

Expected behavior

On a dual-stack host, KadNode should be able to authenticate both IPv4 and IPv6 DHT results.

If an IPv6 address is found in the DHT and the peer can answer BOB authentication, resolving <pubkey>.p2p should return an AAAA record.

Actual behavior

IPv6 addresses are discovered from the DHT, but IPv6 BOB authentication does not complete reliably.

Environment

  • Two devices with dual-stack networking enabled
  • Both devices run KadNode
  • IPv4 and IPv6 are both available
  • IPv6 UDP ports are open (reachable from internet)
  • Peers successfully receive IP address lists from the DHT
  • IPv6 addresses are reachable on the local network and internet

Steps to reproduce

  1. Run KadNode with BOB enabled on two dual-stack devices.
  2. Make sure both devices can use IPv4 and IPv6.
  3. Make sure IPv6 UDP traffic is allowed between the devices.
  4. Try to resolve or ping the peer name over IPv6.

Suspected cause in code

In src/kad.c, KadNode creates two separate DHT UDP sockets:

g_dht_socket4 = net_bind("KAD", "0.0.0.0", gconf->dht_port, gconf->dht_ifname, IPPROTO_UDP);
g_dht_socket6 = net_bind("KAD", "::", gconf->dht_port, gconf->dht_ifname, IPPROTO_UDP);

However, in src/ext-bob.c, BOB keeps only one global DHT socket:

static int g_dht_socket = -1;

That socket is initialized from whichever DHT packet reaches bob_handler() first:

bool bob_handler(int fd, uint8_t buf[], uint32_t buflen, IP *from)
{
    // Hack to get the DHT socket..
    if (g_dht_socket == -1) {
        g_dht_socket = fd;
    }

Later, new BOB challenges are sent through that single remembered socket:

bob_send_challenge(g_dht_socket, resource);

Retries also use the socket passed into bob_send_challenges():

if (resource->challenges_send < MAX_AUTH_CHALLENGE_SEND) {
    bob_send_challenge(sock, resource);
}

On a dual-stack node, if g_dht_socket is set to the IPv4 socket, BOB may try to send an IPv6 challenge through an IPv4 UDP socket.

The challenge send path also does not check the return value of sendto():

sendto(sock, buf, sizeof(buf), 0, (struct sockaddr*) &resource->address, sizeof(IP));

This means a failed IPv6 send can still be counted as an authentication attempt:

resource->challenges_send += 1;

After enough failed attempts, the resource can end up in an authentication error state, so the IPv6 address is not returned as a valid result.

Impact

This breaks IPv6 .p2p resolution on dual-stack nodes.

The DHT can contain a valid IPv6 address, and that address can be reachable, but DNS/NSS may still not return an AAAA record because BOB authentication is attempted through the wrong UDP socket family.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions