ant_quic/
unified_config.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8//! Configuration for ant-quic P2P endpoints
9//!
10//! This module provides `P2pConfig` with builder pattern support for
11//! configuring endpoints, NAT traversal, MTU, PQC, and other settings.
12//!
13//! # v0.13.0 Symmetric P2P API
14//!
15//! ```rust,ignore
16//! use ant_quic::P2pConfig;
17//!
18//! // All nodes are symmetric - no client/server roles
19//! let config = P2pConfig::builder()
20//!     .bind_addr("0.0.0.0:9000".parse()?)
21//!     .known_peer("peer1.example.com:9000".parse()?)
22//!     .known_peer("peer2.example.com:9000".parse()?)
23//!     .build()?;
24//! ```
25
26use std::net::SocketAddr;
27use std::time::Duration;
28
29// v0.2: AuthConfig removed - TLS handles peer authentication via ML-DSA-65
30use crate::config::nat_timeouts::TimeoutConfig;
31use crate::crypto::pqc::PqcConfig;
32use crate::crypto::pqc::types::{MlDsaPublicKey, MlDsaSecretKey};
33
34/// Configuration for ant-quic P2P endpoints
35///
36/// This struct provides all configuration options for P2P networking including
37/// NAT traversal, authentication, MTU settings, and post-quantum cryptography.
38///
39/// Named `P2pConfig` to avoid collision with the low-level `config::EndpointConfig`
40/// which is used for QUIC protocol settings.
41///
42/// # Pure P2P Design (v0.13.0+)
43/// All nodes are symmetric - they can connect, accept connections, and coordinate
44/// NAT traversal for peers. There is no role distinction.
45#[derive(Debug, Clone)]
46pub struct P2pConfig {
47    /// Local address to bind to. If `None`, an ephemeral port is auto-assigned
48    /// with enhanced security through port randomization.
49    pub bind_addr: Option<SocketAddr>,
50
51    /// Known peers for initial discovery and NAT traversal coordination
52    /// These can be any nodes in the network - all nodes are symmetric.
53    pub known_peers: Vec<SocketAddr>,
54
55    /// Maximum number of concurrent connections
56    pub max_connections: usize,
57
58    // v0.2: auth field removed - TLS handles peer authentication via ML-DSA-65
59    /// NAT traversal configuration
60    pub nat: NatConfig,
61
62    /// Timeout configuration for all operations
63    pub timeouts: TimeoutConfig,
64
65    /// Post-quantum cryptography configuration
66    pub pqc: PqcConfig,
67
68    /// MTU configuration for network packet sizing
69    pub mtu: MtuConfig,
70
71    /// Interval for collecting and reporting statistics
72    pub stats_interval: Duration,
73
74    /// Identity keypair for persistent peer identity (ML-DSA-65).
75    /// If `None`, a fresh keypair is generated on startup.
76    /// Provide this for persistent identity across restarts.
77    pub keypair: Option<(MlDsaPublicKey, MlDsaSecretKey)>,
78}
79// v0.13.0: enable_coordinator removed - all nodes are coordinators
80
81/// NAT traversal specific configuration
82///
83/// These options control how the endpoint discovers external addresses,
84/// coordinates hole punching, and handles NAT traversal failures.
85#[derive(Debug, Clone)]
86pub struct NatConfig {
87    /// Maximum number of address candidates to track
88    pub max_candidates: usize,
89
90    /// Enable symmetric NAT prediction algorithms
91    pub enable_symmetric_nat: bool,
92
93    /// Enable automatic relay fallback when direct connection fails
94    pub enable_relay_fallback: bool,
95
96    /// Known relay nodes for MASQUE CONNECT-UDP Bind fallback
97    pub relay_nodes: Vec<std::net::SocketAddr>,
98
99    /// Maximum concurrent NAT traversal attempts
100    pub max_concurrent_attempts: usize,
101
102    /// Prefer RFC-compliant NAT traversal frame format
103    pub prefer_rfc_nat_traversal: bool,
104}
105
106impl Default for NatConfig {
107    fn default() -> Self {
108        Self {
109            max_candidates: 10,
110            enable_symmetric_nat: true,
111            enable_relay_fallback: true,
112            relay_nodes: Vec::new(),
113            max_concurrent_attempts: 3,
114            prefer_rfc_nat_traversal: true,
115        }
116    }
117}
118
119/// MTU (Maximum Transmission Unit) configuration
120///
121/// Controls packet sizing for optimal network performance. Post-quantum
122/// cryptography requires larger packets due to bigger key sizes:
123/// - ML-KEM-768: 1,184 byte public key + 1,088 byte ciphertext
124/// - ML-DSA-65: 1,952 byte public key + 3,309 byte signature
125///
126/// The default configuration enables MTU discovery which automatically
127/// finds the optimal packet size for the network path.
128#[derive(Debug, Clone)]
129pub struct MtuConfig {
130    /// Initial MTU to use before discovery (default: 1200)
131    ///
132    /// Must be at least 1200 bytes per QUIC specification.
133    /// For PQC-enabled connections, consider using 1500+ if network allows.
134    pub initial_mtu: u16,
135
136    /// Minimum MTU that must always work (default: 1200)
137    ///
138    /// The connection will fall back to this if larger packets are lost.
139    /// Must not exceed `initial_mtu`.
140    pub min_mtu: u16,
141
142    /// Enable path MTU discovery (default: true)
143    ///
144    /// When enabled, the connection probes for larger packet sizes
145    /// to optimize throughput. Disable for constrained networks.
146    pub discovery_enabled: bool,
147
148    /// Upper bound for MTU discovery probing (default: 1452)
149    ///
150    /// For PQC connections, consider higher values (up to 4096) if the
151    /// network path supports jumbo frames.
152    pub max_mtu: u16,
153
154    /// Automatically adjust MTU for PQC handshakes (default: true)
155    ///
156    /// When enabled, the connection will use larger MTU settings
157    /// during PQC handshakes to accommodate large key exchanges.
158    pub auto_pqc_adjustment: bool,
159}
160
161impl Default for MtuConfig {
162    fn default() -> Self {
163        Self {
164            initial_mtu: 1200,
165            min_mtu: 1200,
166            discovery_enabled: true,
167            max_mtu: 1452, // Ethernet MTU minus IP/UDP headers
168            auto_pqc_adjustment: true,
169        }
170    }
171}
172
173impl MtuConfig {
174    /// Configuration optimized for PQC (larger MTUs)
175    pub fn pqc_optimized() -> Self {
176        Self {
177            initial_mtu: 1500,
178            min_mtu: 1200,
179            discovery_enabled: true,
180            max_mtu: 4096, // Higher bound for PQC key exchange
181            auto_pqc_adjustment: true,
182        }
183    }
184
185    /// Configuration for constrained networks (no discovery)
186    pub fn constrained() -> Self {
187        Self {
188            initial_mtu: 1200,
189            min_mtu: 1200,
190            discovery_enabled: false,
191            max_mtu: 1200,
192            auto_pqc_adjustment: false,
193        }
194    }
195
196    /// Configuration for high-bandwidth networks with jumbo frames
197    pub fn jumbo_frames() -> Self {
198        Self {
199            initial_mtu: 1500,
200            min_mtu: 1200,
201            discovery_enabled: true,
202            max_mtu: 9000, // Jumbo frame MTU
203            auto_pqc_adjustment: true,
204        }
205    }
206}
207
208impl Default for P2pConfig {
209    fn default() -> Self {
210        Self {
211            bind_addr: None,
212            known_peers: Vec::new(),
213            max_connections: 256,
214            // v0.2: auth removed
215            nat: NatConfig::default(),
216            timeouts: TimeoutConfig::default(),
217            pqc: PqcConfig::default(),
218            mtu: MtuConfig::default(),
219            stats_interval: Duration::from_secs(30),
220            keypair: None,
221        }
222    }
223}
224
225impl P2pConfig {
226    /// Create a new configuration builder
227    pub fn builder() -> P2pConfigBuilder {
228        P2pConfigBuilder::default()
229    }
230
231    /// Convert to `NatTraversalConfig` for internal use
232    pub fn to_nat_config(&self) -> crate::nat_traversal_api::NatTraversalConfig {
233        crate::nat_traversal_api::NatTraversalConfig {
234            known_peers: self.known_peers.clone(),
235            max_candidates: self.nat.max_candidates,
236            coordination_timeout: self.timeouts.nat_traversal.coordination_timeout,
237            enable_symmetric_nat: self.nat.enable_symmetric_nat,
238            enable_relay_fallback: self.nat.enable_relay_fallback,
239            relay_nodes: self.nat.relay_nodes.clone(),
240            max_concurrent_attempts: self.nat.max_concurrent_attempts,
241            bind_addr: self.bind_addr,
242            prefer_rfc_nat_traversal: self.nat.prefer_rfc_nat_traversal,
243            pqc: Some(self.pqc.clone()),
244            timeouts: self.timeouts.clone(),
245            identity_key: None,
246        }
247    }
248
249    /// Convert to `NatTraversalConfig` with a specific identity key
250    ///
251    /// This ensures the same ML-DSA-65 keypair is used for both P2pEndpoint
252    /// authentication and TLS/RPK identity in NatTraversalEndpoint.
253    pub fn to_nat_config_with_key(
254        &self,
255        public_key: MlDsaPublicKey,
256        secret_key: MlDsaSecretKey,
257    ) -> crate::nat_traversal_api::NatTraversalConfig {
258        let mut config = self.to_nat_config();
259        config.identity_key = Some((public_key, secret_key));
260        config
261    }
262}
263
264/// Builder for `P2pConfig`
265#[derive(Debug, Clone, Default)]
266pub struct P2pConfigBuilder {
267    bind_addr: Option<SocketAddr>,
268    known_peers: Vec<SocketAddr>,
269    max_connections: Option<usize>,
270    // v0.2: auth removed
271    nat: Option<NatConfig>,
272    timeouts: Option<TimeoutConfig>,
273    pqc: Option<PqcConfig>,
274    mtu: Option<MtuConfig>,
275    stats_interval: Option<Duration>,
276    keypair: Option<(MlDsaPublicKey, MlDsaSecretKey)>,
277}
278
279/// Error type for configuration validation
280#[derive(Debug, Clone, thiserror::Error)]
281pub enum ConfigError {
282    /// Invalid max connections value
283    #[error("max_connections must be at least 1")]
284    InvalidMaxConnections,
285
286    /// Invalid timeout value
287    #[error("Invalid timeout: {0}")]
288    InvalidTimeout(String),
289
290    /// PQC configuration error
291    #[error("PQC configuration error: {0}")]
292    PqcError(String),
293
294    /// Invalid MTU configuration
295    #[error("Invalid MTU configuration: {0}")]
296    InvalidMtu(String),
297}
298
299impl P2pConfigBuilder {
300    /// Set the bind address
301    pub fn bind_addr(mut self, addr: SocketAddr) -> Self {
302        self.bind_addr = Some(addr);
303        self
304    }
305
306    /// Add a known peer for initial discovery
307    /// In v0.13.0+ all nodes are symmetric - these are just starting points
308    pub fn known_peer(mut self, addr: SocketAddr) -> Self {
309        self.known_peers.push(addr);
310        self
311    }
312
313    /// Add multiple known peers
314    pub fn known_peers(mut self, addrs: impl IntoIterator<Item = SocketAddr>) -> Self {
315        self.known_peers.extend(addrs);
316        self
317    }
318
319    /// Add a bootstrap node (alias for known_peer for backwards compatibility)
320    #[doc(hidden)]
321    pub fn bootstrap(mut self, addr: SocketAddr) -> Self {
322        self.known_peers.push(addr);
323        self
324    }
325
326    /// Set maximum connections
327    pub fn max_connections(mut self, max: usize) -> Self {
328        self.max_connections = Some(max);
329        self
330    }
331
332    // v0.2: auth() method removed - TLS handles peer authentication via ML-DSA-65
333
334    /// Set NAT traversal configuration
335    pub fn nat(mut self, nat: NatConfig) -> Self {
336        self.nat = Some(nat);
337        self
338    }
339
340    /// Set timeout configuration
341    pub fn timeouts(mut self, timeouts: TimeoutConfig) -> Self {
342        self.timeouts = Some(timeouts);
343        self
344    }
345
346    /// Use fast timeouts (for local networks)
347    pub fn fast_timeouts(mut self) -> Self {
348        self.timeouts = Some(TimeoutConfig::fast());
349        self
350    }
351
352    /// Use conservative timeouts (for unreliable networks)
353    pub fn conservative_timeouts(mut self) -> Self {
354        self.timeouts = Some(TimeoutConfig::conservative());
355        self
356    }
357
358    /// Set PQC configuration
359    pub fn pqc(mut self, pqc: PqcConfig) -> Self {
360        self.pqc = Some(pqc);
361        self
362    }
363
364    /// Set MTU configuration
365    pub fn mtu(mut self, mtu: MtuConfig) -> Self {
366        self.mtu = Some(mtu);
367        self
368    }
369
370    /// Use PQC-optimized MTU settings
371    ///
372    /// Enables larger MTU bounds (up to 4096) for efficient PQC handshakes.
373    pub fn pqc_optimized_mtu(mut self) -> Self {
374        self.mtu = Some(MtuConfig::pqc_optimized());
375        self
376    }
377
378    /// Use constrained network MTU settings
379    ///
380    /// Disables MTU discovery and uses minimum MTU (1200).
381    pub fn constrained_mtu(mut self) -> Self {
382        self.mtu = Some(MtuConfig::constrained());
383        self
384    }
385
386    /// Use jumbo frame MTU settings
387    ///
388    /// For high-bandwidth networks supporting larger frames (up to 9000).
389    pub fn jumbo_mtu(mut self) -> Self {
390        self.mtu = Some(MtuConfig::jumbo_frames());
391        self
392    }
393
394    /// Set statistics collection interval
395    pub fn stats_interval(mut self, interval: Duration) -> Self {
396        self.stats_interval = Some(interval);
397        self
398    }
399
400    /// Set identity keypair for persistent peer ID (ML-DSA-65)
401    ///
402    /// If not set, a fresh keypair is generated on startup.
403    /// Provide this for stable identity across restarts.
404    pub fn keypair(mut self, public_key: MlDsaPublicKey, secret_key: MlDsaSecretKey) -> Self {
405        self.keypair = Some((public_key, secret_key));
406        self
407    }
408
409    /// Build the configuration with validation
410    pub fn build(self) -> Result<P2pConfig, ConfigError> {
411        // Validate max_connections
412        let max_connections = self.max_connections.unwrap_or(256);
413        if max_connections == 0 {
414            return Err(ConfigError::InvalidMaxConnections);
415        }
416
417        // v0.13.0+: No role validation - all nodes are symmetric
418        // Nodes can operate without known peers (they can be connected to by others)
419
420        Ok(P2pConfig {
421            bind_addr: self.bind_addr,
422            known_peers: self.known_peers,
423            max_connections,
424            // v0.2: auth removed
425            nat: self.nat.unwrap_or_default(),
426            timeouts: self.timeouts.unwrap_or_default(),
427            pqc: self.pqc.unwrap_or_default(),
428            mtu: self.mtu.unwrap_or_default(),
429            stats_interval: self.stats_interval.unwrap_or(Duration::from_secs(30)),
430            keypair: self.keypair,
431        })
432    }
433}
434
435#[cfg(test)]
436mod tests {
437    use super::*;
438
439    #[test]
440    fn test_default_config() {
441        let config = P2pConfig::default();
442        // v0.13.0+: No role field - all nodes are symmetric
443        assert!(config.bind_addr.is_none());
444        assert!(config.known_peers.is_empty());
445        assert_eq!(config.max_connections, 256);
446    }
447
448    #[test]
449    fn test_builder_basic() {
450        let config = P2pConfig::builder()
451            .max_connections(100)
452            .build()
453            .expect("Failed to build config");
454
455        // v0.13.0+: No role field - all nodes are symmetric
456        assert_eq!(config.max_connections, 100);
457    }
458
459    #[test]
460    fn test_builder_with_known_peers() {
461        let addr1: SocketAddr = "127.0.0.1:9000".parse().expect("valid addr");
462        let addr2: SocketAddr = "127.0.0.1:9001".parse().expect("valid addr");
463
464        let config = P2pConfig::builder()
465            .known_peer(addr1)
466            .known_peer(addr2)
467            .build()
468            .expect("Failed to build config");
469
470        assert_eq!(config.known_peers.len(), 2);
471    }
472
473    #[test]
474    fn test_invalid_max_connections() {
475        let result = P2pConfig::builder().max_connections(0).build();
476
477        assert!(matches!(result, Err(ConfigError::InvalidMaxConnections)));
478    }
479
480    #[test]
481    fn test_to_nat_config() {
482        let config = P2pConfig::builder()
483            .known_peer("127.0.0.1:9000".parse().expect("valid addr"))
484            .nat(NatConfig {
485                max_candidates: 20,
486                enable_symmetric_nat: false,
487                ..Default::default()
488            })
489            .build()
490            .expect("Failed to build config");
491
492        let nat_config = config.to_nat_config();
493        assert_eq!(nat_config.max_candidates, 20);
494        assert!(!nat_config.enable_symmetric_nat);
495    }
496
497    #[test]
498    fn test_nat_config_default() {
499        let nat = NatConfig::default();
500        assert_eq!(nat.max_candidates, 10);
501        assert!(nat.enable_symmetric_nat);
502        assert!(nat.enable_relay_fallback);
503        assert_eq!(nat.max_concurrent_attempts, 3);
504        assert!(nat.prefer_rfc_nat_traversal);
505    }
506
507    #[test]
508    fn test_mtu_config_default() {
509        let mtu = MtuConfig::default();
510        assert_eq!(mtu.initial_mtu, 1200);
511        assert_eq!(mtu.min_mtu, 1200);
512        assert!(mtu.discovery_enabled);
513        assert_eq!(mtu.max_mtu, 1452);
514        assert!(mtu.auto_pqc_adjustment);
515    }
516
517    #[test]
518    fn test_mtu_config_pqc_optimized() {
519        let mtu = MtuConfig::pqc_optimized();
520        assert_eq!(mtu.initial_mtu, 1500);
521        assert_eq!(mtu.min_mtu, 1200);
522        assert!(mtu.discovery_enabled);
523        assert_eq!(mtu.max_mtu, 4096);
524        assert!(mtu.auto_pqc_adjustment);
525    }
526
527    #[test]
528    fn test_mtu_config_constrained() {
529        let mtu = MtuConfig::constrained();
530        assert_eq!(mtu.initial_mtu, 1200);
531        assert_eq!(mtu.min_mtu, 1200);
532        assert!(!mtu.discovery_enabled);
533        assert_eq!(mtu.max_mtu, 1200);
534        assert!(!mtu.auto_pqc_adjustment);
535    }
536
537    #[test]
538    fn test_mtu_config_jumbo_frames() {
539        let mtu = MtuConfig::jumbo_frames();
540        assert_eq!(mtu.initial_mtu, 1500);
541        assert_eq!(mtu.min_mtu, 1200);
542        assert!(mtu.discovery_enabled);
543        assert_eq!(mtu.max_mtu, 9000);
544        assert!(mtu.auto_pqc_adjustment);
545    }
546
547    #[test]
548    fn test_builder_with_mtu_config() {
549        // v0.13.0+: No role - all nodes are symmetric P2P nodes
550        let config = P2pConfig::builder()
551            .mtu(MtuConfig::pqc_optimized())
552            .build()
553            .expect("Failed to build config");
554
555        assert_eq!(config.mtu.initial_mtu, 1500);
556        assert_eq!(config.mtu.max_mtu, 4096);
557    }
558
559    #[test]
560    fn test_builder_pqc_optimized_mtu() {
561        // v0.13.0+: No role - all nodes are symmetric P2P nodes
562        let config = P2pConfig::builder()
563            .pqc_optimized_mtu()
564            .build()
565            .expect("Failed to build config");
566
567        assert_eq!(config.mtu.initial_mtu, 1500);
568        assert_eq!(config.mtu.max_mtu, 4096);
569    }
570
571    #[test]
572    fn test_builder_constrained_mtu() {
573        // v0.13.0+: No role - all nodes are symmetric P2P nodes
574        let config = P2pConfig::builder()
575            .constrained_mtu()
576            .build()
577            .expect("Failed to build config");
578
579        assert!(!config.mtu.discovery_enabled);
580        assert_eq!(config.mtu.max_mtu, 1200);
581    }
582
583    #[test]
584    fn test_builder_jumbo_mtu() {
585        // v0.13.0+: No role - all nodes are symmetric P2P nodes
586        let config = P2pConfig::builder()
587            .jumbo_mtu()
588            .build()
589            .expect("Failed to build config");
590
591        assert_eq!(config.mtu.max_mtu, 9000);
592    }
593
594    #[test]
595    fn test_default_config_has_mtu() {
596        let config = P2pConfig::default();
597        assert_eq!(config.mtu.initial_mtu, 1200);
598        assert!(config.mtu.discovery_enabled);
599    }
600}