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