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