Milestone 5: metered awareness, DSCP, systemd, man pages, packaging
Metered detection polls NetworkManager's Metered property over D-Bus (feature "metered", default on; builds without D-Bus via no-default-features). While metered the daemon closes incoming blob connections and defers every fetch; VARDE_FORCE_METERED=true forces the state as a kill switch and test hook. Endpoint UDP sockets get DSCP CS1 best-effort by matching bound ports to /proc/net/udp inodes (iroh hides its fds). systemd socket activation adopts LISTEN_FDS fd 3, readiness is a hand-rolled sd_notify READY=1 (abstract + path sockets), and standalone binding still works unchanged. dist/ ships hardened system and user units (DynamicUser, ProtectSystem=strict, StateDirectory, RestrictAddressFamilies), a commented config example, scdoc man pages validated with scdoc, and an untested PKGBUILD skeleton. Tests: activation-socket round trip via a real fd-3 handoff, READY=1 received on a NOTIFY_SOCKET, metered daemons neither serve nor fetch. Dependencies: zbus (optional, feature-gated D-Bus client for the NetworkManager metered flag). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ use varde_proto::{Direction, Event};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::meta::Meta;
|
||||
use crate::metered::MeteredState;
|
||||
use crate::shaped::{ServeFilter, ShapedStore, TokenBucket};
|
||||
use crate::store::BlobStore;
|
||||
|
||||
@@ -39,6 +40,7 @@ pub struct Transfer {
|
||||
router: Router,
|
||||
downloader: Downloader,
|
||||
events: broadcast::Sender<Event>,
|
||||
metered: MeteredState,
|
||||
discovery_rx: std::sync::Mutex<Option<mpsc::Receiver<NodeId>>>,
|
||||
}
|
||||
|
||||
@@ -54,6 +56,7 @@ struct GatedProvider {
|
||||
events: EventSender,
|
||||
pool: LocalPoolHandle,
|
||||
serving_enabled: bool,
|
||||
metered: MeteredState,
|
||||
}
|
||||
|
||||
impl ProtocolHandler for GatedProvider {
|
||||
@@ -61,8 +64,8 @@ impl ProtocolHandler for GatedProvider {
|
||||
let this = self.clone();
|
||||
Box::pin(async move {
|
||||
let remote = connection.remote_node_id()?;
|
||||
if !this.serving_enabled {
|
||||
debug!(%remote, "serving disabled, closing connection");
|
||||
if !this.serving_enabled || this.metered.is_metered() {
|
||||
debug!(%remote, "serving disabled or metered, closing connection");
|
||||
connection.close(0u32.into(), b"serving disabled");
|
||||
return Ok(());
|
||||
}
|
||||
@@ -116,6 +119,7 @@ impl Transfer {
|
||||
open_filter: ServeFilter,
|
||||
pool: LocalPoolHandle,
|
||||
events: broadcast::Sender<Event>,
|
||||
metered: MeteredState,
|
||||
) -> Result<Transfer> {
|
||||
let secret = load_or_create_secret(&config.store_dir.join("secret.key"))?;
|
||||
let mut builder = Endpoint::builder().secret_key(secret.clone());
|
||||
@@ -146,6 +150,11 @@ impl Transfer {
|
||||
let endpoint = builder.bind().await.context("binding iroh endpoint")?;
|
||||
info!(node_id = %endpoint.node_id(), discovery = config.discovery, "endpoint up");
|
||||
|
||||
// Best-effort low-priority marking of our UDP traffic.
|
||||
let (v4, v6) = endpoint.bound_sockets();
|
||||
let bound: Vec<std::net::SocketAddr> = std::iter::once(v4).chain(v6).collect();
|
||||
crate::dscp::mark_endpoint_sockets(&bound);
|
||||
|
||||
let upload_bucket = TokenBucket::new(config.max_upload_bytes_per_sec);
|
||||
let download_bucket = TokenBucket::new(config.max_download_bytes_per_sec);
|
||||
let shaped = ShapedStore::new(store.inner().clone(), upload_bucket, download_bucket);
|
||||
@@ -158,6 +167,7 @@ impl Transfer {
|
||||
events: provider_events,
|
||||
pool: pool.clone(),
|
||||
serving_enabled: config.max_upload_bytes_per_sec > 0,
|
||||
metered: metered.clone(),
|
||||
};
|
||||
|
||||
let downloader = Downloader::new(shaped, endpoint.clone(), pool);
|
||||
@@ -170,6 +180,7 @@ impl Transfer {
|
||||
router,
|
||||
downloader,
|
||||
events,
|
||||
metered,
|
||||
discovery_rx: std::sync::Mutex::new(Some(discovery_rx)),
|
||||
})
|
||||
}
|
||||
@@ -200,6 +211,11 @@ impl Transfer {
|
||||
/// Queue a background fetch of `content` from `providers`. Emits
|
||||
/// progress events and [`Event::PinComplete`] when fully verified.
|
||||
pub fn spawn_fetch(&self, content: HashAndFormat, providers: Vec<NodeAddr>) {
|
||||
if self.metered.is_metered() {
|
||||
// The pin stays recorded; auto-sync retries once unmetered.
|
||||
info!(hash = %content.hash.to_hex(), "metered connection: fetch deferred");
|
||||
return;
|
||||
}
|
||||
let downloader = self.downloader.clone();
|
||||
let events = self.events.clone();
|
||||
let endpoint = self.endpoint.clone();
|
||||
|
||||
Reference in New Issue
Block a user