Implement the WireGuard data plane plugin
The first IP plugin, built on the data plane boundary the core already had. Plugin: - one X25519 key per network in the plugin's own wireguard.sqlite, separate from the iroh identity and from the network secret; a damaged store is an error, never a silently regenerated identity - deterministic IPv6 ULA overlay: every member derives the same /64 from the network id and its own /128 from its WireGuard public key, so no coordinator allocates addresses - AllowedIPs are derived locally, never taken from a peer's announcement, so a member cannot claim another member's overlay address; a mismatched claim is rejected - bounded, versioned, validated announcement carried as the existing opaque capability payload, which the core still never parses - each agent builds its own full-mesh configuration (N-1 peers) and reconciles on every change and on a timer, repairing drift - WireguardBackend abstraction: RecordingBackend in memory, and WgToolBackend driving real wg/ip on Linux, split into a pure planner plus parsers and a thin executor so everything interesting is testable without root Core, three generic additions the plugin needed: - IpPlugin::on_network_activated, so per-network state is ready before peers - PluginContext for re-announcements and error reports from plugin tasks, with errors counted by the owning network runtime - IpPlugin::shutdown, awaited with a grace period, so system objects go away 94 tests pass offline with no privileges: 35 new WireGuard unit tests and 12 integration tests over real iroh connections. The real wg/ip backend needs root and is behind --ignored in tests/wireguard_system.rs; it was not run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7cea9afa37
commit
ea7aaa2b69
28 files changed
+4790
-46
No files matched your search
@@ -54,6 +54,15 @@ pub(crate) enum NetCommand {
|
||||
reply: oneshot::Sender<Box<NetworkStatus>>,
|
||||
},
|
||||
Recheck,
|
||||
/// Resend this agent's announcement to every peer of this network.
|
||||
Reannounce,
|
||||
/// An IP plugin reported an error from one of its own tasks.
|
||||
PluginError {
|
||||
/// Plugin protocol id.
|
||||
protocol: String,
|
||||
/// Human readable reason, free of secrets.
|
||||
reason: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for NetCommand {
|
||||
@@ -66,6 +75,8 @@ impl std::fmt::Debug for NetCommand {
|
||||
NetCommand::Broadcast { message, .. } => write!(f, "Broadcast({})", kind(message)),
|
||||
NetCommand::Status { .. } => f.write_str("Status"),
|
||||
NetCommand::Recheck => f.write_str("Recheck"),
|
||||
NetCommand::Reannounce => f.write_str("Reannounce"),
|
||||
NetCommand::PluginError { protocol, .. } => write!(f, "PluginError({protocol})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,6 +281,31 @@ impl Runtime {
|
||||
let _ = reply.send(Box::new(self.status()));
|
||||
}
|
||||
NetCommand::Recheck => self.discovery_round().await,
|
||||
NetCommand::Reannounce => self.reannounce(),
|
||||
NetCommand::PluginError { protocol, reason } => {
|
||||
// Counted here so that the per-network metric and the event
|
||||
// always agree, wherever the error came from.
|
||||
self.metrics.plugin_errors += 1;
|
||||
self.emit(Event::PluginError {
|
||||
network: self.network_id,
|
||||
protocol,
|
||||
reason,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rebuilds this agent's announcement and pushes it to every session.
|
||||
///
|
||||
/// Used when a plugin's capability changed, so peers do not have to wait
|
||||
/// for a reconnect to learn about it.
|
||||
fn reannounce(&mut self) {
|
||||
let announcement = ControlMessage::Announce(self.local_announcement());
|
||||
let peers: Vec<EndpointId> = self.sessions.keys().copied().collect();
|
||||
for peer in peers {
|
||||
if let Err(err) = self.send_to(peer, announcement.clone()) {
|
||||
tracing::debug!(%err, "could not queue re-announcement");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user