Make the overlay dual stack
Every member now also derives an IPv4 address, from the same inputs as its IPv6 one, into 100.64.0.0/10 by default. The range is configurable and IPv4 can be turned off with --no-ipv4. IPv4 is honestly weaker than IPv6 here and the code says so. A 64 bit interface identifier makes an IPv6 collision impossible in practice; IPv4 has nothing like that room, and in a /10 with 50 members two will derive the same address about 0.03% of the time. A mesh with no coordinator cannot allocate around that, so a collision is detected and resolved instead: the member whose public key sorts lower keeps the address, a rule every member computes identically and therefore agrees on. The other keeps IPv6 and is flagged in the status. IPv6 always works; IPv4 almost always works and degrades predictably. Routing and address-ownership enforcement now cover both families: a packet goes to the peer that owns its destination, and a decrypted packet is dropped unless its source is an address derived for the peer that sent it, IPv4 included. Six new tests, among them a real IPv4 packet crossing a tunnel next to an IPv6 one, a spoofed IPv4 source being dropped, and an IPv6-only overlay. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+14
-2
@@ -136,6 +136,8 @@ pub struct OverlayReport {
|
||||
pub mtu: u32,
|
||||
/// This agent's overlay address.
|
||||
pub address: String,
|
||||
/// This agent's IPv4 overlay address, when the overlay is dual stack.
|
||||
pub address_v4: Option<String>,
|
||||
/// The subnet every member shares.
|
||||
pub prefix: String,
|
||||
/// Prefix length of that subnet.
|
||||
@@ -155,6 +157,8 @@ pub struct OverlayPeerReport {
|
||||
pub public_key: String,
|
||||
/// Its overlay address.
|
||||
pub address: String,
|
||||
/// Its IPv4 overlay address, when it has one.
|
||||
pub address_v4: Option<String>,
|
||||
/// Seconds since the last WireGuard handshake.
|
||||
///
|
||||
/// `None` means the tunnel has never handshaken and cannot carry traffic.
|
||||
@@ -229,10 +233,14 @@ impl StatusReport {
|
||||
let up = overlay.peers.iter().filter(|peer| peer.is_up()).count();
|
||||
let _ = writeln!(
|
||||
out,
|
||||
" overlay {} {}/{} mtu {} {}/{} tunnel(s) up",
|
||||
" overlay {} {}/{}{} mtu {} {}/{} tunnel(s) up",
|
||||
overlay.interface,
|
||||
overlay.address,
|
||||
overlay.prefix_len,
|
||||
match &overlay.address_v4 {
|
||||
Some(v4) => format!(" and {v4}"),
|
||||
None => String::new(),
|
||||
},
|
||||
overlay.mtu,
|
||||
up,
|
||||
overlay.peers.len()
|
||||
@@ -240,9 +248,13 @@ impl StatusReport {
|
||||
for peer in &overlay.peers {
|
||||
let _ = writeln!(
|
||||
out,
|
||||
" {} {} {} tx {} rx {}{} {}",
|
||||
" {} {}{} {} tx {} rx {}{} {}",
|
||||
&peer.public_key[..8.min(peer.public_key.len())],
|
||||
peer.address,
|
||||
match &peer.address_v4 {
|
||||
Some(v4) => format!(" / {v4}"),
|
||||
None => String::new(),
|
||||
},
|
||||
match peer.handshake_secs_ago {
|
||||
Some(secs) => format!("handshake {secs}s ago"),
|
||||
None => "NOT HANDSHAKEN".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user