1
0
mirror of https://github.com/coturn/coturn.git synced 2026-10-24 00:08:57 +03:00
Files
Pavel Punsky 1e3570dd1e Advertise IPv6 allocations under their real relayed transport address (#2058)
Fixes #1322.

The issue reports `clnet_allocate: !!!: relay addr cannot be received
(2)` when asking `turnutils_uclient` for an IPv6 relay. It turns out to
be two independent bugs that both end in that same line, one on each
side of the wire. Both are reproduced by the new test, which fails on
`master` and passes here.

## What the RFC requires

RFC 8656 Section 3 defines the relayed transport address as *"A
transport address on the TURN server that is used for relaying packets
between the client and a peer"*, and Section 7.2 leaves the server two
outcomes for an Allocate carrying `REQUESTED-ADDRESS-FAMILY`:

> If the server does not support the address family requested by the
client in REQUESTED-ADDRESS-FAMILY, or if the allocation of the
requested address family is disabled by local policy, it MUST generate
an Allocate error response, and it MUST include an ERROR-CODE attribute
with the 440 (Address Family not Supported) response code.

Section 7.3 puts one duty on the client:

> If the client receives an Allocate success response, then it MUST
check that the mapped address and the relayed transport address or
addresses are part of an address family or families that the client
understands and is prepared to handle.

## 1. `--external-ip` was applied across address families (server)

`external_ip` is a single `ioa_addr` and `--external-ip` may only be
given once, but it was substituted for the relayed address of *both*
families. On a server with an IPv4 `--external-ip`, an IPv6 allocation
was therefore advertised under an unrelated IPv4 address that no peer
can reach — neither an IPv6 relay nor a 440.

```
--relay-ip=127.0.0.1 --relay-ip=::1 --external-ip=127.0.0.1, client over ::1 with -x
INFO IPv4. Received relay addr: 127.0.0.1:30654
INFO clnet_allocate: !!!: relay addr cannot be received (2)
```

The relay socket really was IPv6; only the advertised address was wrong.
As a second effect, the substituted family made the response draw
`LIFETIME` from the IPv4 relay session that was never created for that
allocation.

`turn_set_xor_relayed_addr()` now applies the external IP only to a
relay of its own family, which is the same rule the
`ioa_addr_add_mapping()` setup path already follows in `mainrelay.c`.
The option's documentation is updated to match.

## 2. `turnutils_uclient` rejected an IPv6 relay it can handle (client)

The acceptance test took an IPv6 relayed address only when IPv6 had been
requested *explicitly*, and consulted the global
`default_address_family` rather than the `af` argument the caller passed
to `clnet_allocate()`. Against a server running
`--allocation-default-address-family=keep` (or the deprecated
`--keep-address-family`), which answers an IPv6 client with an IPv6
relay and no `REQUESTED-ADDRESS-FAMILY` in play, a perfectly usable
relay was refused — as reported in the issue thread. Section 7.3 asks
only that the client refuse a family it *cannot handle*, so the check
now rejects solely on a contradiction with the family this allocation
requested.

Note this does not make `keep` conformant — it is a deliberate
deviation, and `turnserver -h` says as much (*"It breaks RFC6156
section-4.2 (violates default IPv4)"*). The client simply stops
rejecting a relay it is able to use.

## Testing

`examples/run_tests_ipv6_relay.sh` covers both configurations end to
end, asserting a completed relayed round trip rather than just a
successful allocate. It uses ports off the defaults the other scripts
share, and SKIPs where no IPv6 loopback is bindable. Wired into the
clang, cmake, linux and macos workflows.

Verified red before green: reverting only `src/` and rebuilding turns
both cases into failures.

- macOS: build + `ctest` 18/18; Linux container: build + `ctest` 17/17
- Linux: `run_tests.sh`, `run_tests_conf.sh`, `run_tests_mobile.sh`,
`run_tests_multiplex_peer.sh`, `run_tests_rfc5780.sh`,
`run_tests_stateless_nonce.sh`, `run_tests_ipv6_relay.sh`,
`run_tests_dtls_default.sh` — all OK
- Fuzzing smoke tests (ASan targets 0 and 1) — OK
- Packaged Docker image (debian, arm64) + bats — 19 tests, 0 failures, 2
skipped

## Deliberately not in this PR

- The dual-allocation `508` guard when `external_ip_set` is left as is;
with family-aware rewriting it could now be relaxed to allow a
v4-rewritten + v6-as-is pair, but that is a separate behavioral change.
- `--external-ip` stays single-family. A per-family form would be the
complete answer for IPv6-NAT deployments.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:52:18 -07:00
..
2020-04-28 09:26:31 +00:00
2020-04-28 09:26:31 +00:00
2020-04-28 09:26:31 +00:00
2020-04-28 09:26:31 +00:00
2020-04-28 09:26:31 +00:00