d2d_rendezvous.rs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. //! Example for usage of the Connection Rendezvous Protocol state machine, using MPSC channels to
  2. //! simulate paths.
  3. #![expect(unused_crate_dependencies, reason = "Example triggered false positive")]
  4. use core::time::Duration;
  5. use std::{
  6. sync::mpsc::{self, RecvTimeoutError},
  7. thread,
  8. time::Instant,
  9. };
  10. use anyhow::Context as _;
  11. use data_encoding::HEXLOWER_PERMISSIVE;
  12. use libthreema::{
  13. d2d_rendezvous::{
  14. RendezvousAuthenticationKey, RendezvousOutgoingFrame, RendezvousPathProcessResult,
  15. RendezvousPathStateUpdate, RendezvousProtocol,
  16. },
  17. utils::logging::init_stderr_logging,
  18. };
  19. use tracing::{Level, info, trace, trace_span, warn};
  20. struct Keys;
  21. impl Keys {
  22. const AK: [u8; 32] = [
  23. 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
  24. 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
  25. ];
  26. }
  27. fn process_incoming_frame(
  28. protocol: &mut RendezvousProtocol,
  29. pid: u32,
  30. incoming_frame: &RendezvousOutgoingFrame,
  31. ) -> anyhow::Result<Option<RendezvousPathProcessResult>> {
  32. let (header, payload) = incoming_frame.encode();
  33. if let Some(nominated_pid) = protocol.nominated_path()
  34. && pid != nominated_pid
  35. {
  36. warn!(
  37. pid,
  38. ?incoming_frame,
  39. "Discarding chunk for unknown or dropped path"
  40. );
  41. return Ok(None);
  42. }
  43. // Process incoming frame
  44. protocol
  45. .add_chunks(pid, &[header.as_slice(), payload])
  46. .context("Failed to add chunk")?;
  47. let result = protocol.process_frame(pid).context("Failed to process frame")?;
  48. Ok(result)
  49. }
  50. #[expect(clippy::needless_pass_by_value, reason = "Prevent re-use")]
  51. fn run_protocol(
  52. mut protocol: RendezvousProtocol,
  53. initial_outgoing_frames: Vec<(u32, RendezvousOutgoingFrame)>,
  54. tx: mpsc::Sender<(u32, RendezvousOutgoingFrame)>,
  55. rx: mpsc::Receiver<(u32, RendezvousOutgoingFrame)>,
  56. ) -> anyhow::Result<()> {
  57. // Send initial frames
  58. for outgoing_frame in initial_outgoing_frames {
  59. tx.send(outgoing_frame)?;
  60. }
  61. // Nomination loop where we run the handshakes simultaneously over all available paths until we
  62. // have nominated one path.
  63. info!("Entering nomination loop");
  64. let (nominated_pid, rph) = 'nomination: loop {
  65. // Receive and process incoming frame
  66. let (pid, incoming_frame) = rx.recv().context("Failed to receive incoming frame")?;
  67. let mut maybe_result = process_incoming_frame(&mut protocol, pid, &incoming_frame)?;
  68. // Handle results
  69. while let Some(result) = maybe_result {
  70. // We're not expecting to receive any incoming ULP data.
  71. assert!(result.incoming_ulp_data.is_none(), "Unexpected incoming ULP data");
  72. // Send any outgoing frame
  73. if let Some(outgoing_frame) = result.outgoing_frame {
  74. tx.send((pid, outgoing_frame))
  75. .context("Failed to send outgoing frame")?;
  76. }
  77. // Handle any state update
  78. maybe_result = match result.state_update {
  79. Some(RendezvousPathStateUpdate::AwaitingNominate { measured_rtt }) => {
  80. // Check if we should nominate the path
  81. //
  82. // Note: A real implementation should wait a bit and then choose the _best_ path
  83. // based on the measured RTT.
  84. trace!(?measured_rtt, "Path ready to nominate");
  85. if protocol.is_nominator() {
  86. Some(protocol.nominate_path(pid).context("Failed to nominate")?)
  87. } else {
  88. None
  89. }
  90. },
  91. Some(RendezvousPathStateUpdate::Nominated { rph }) => {
  92. // The path was nominated
  93. break 'nomination (pid, rph);
  94. },
  95. None => None,
  96. }
  97. }
  98. };
  99. // ULP loop where we can use the nominated path to exchange arbitrary data. For this example, we
  100. // will send a string every 3s and print out whatever remote sent us.
  101. info!(
  102. rph = HEXLOWER_PERMISSIVE.encode(&rph.0),
  103. "Path nominated, entering ULP loop"
  104. );
  105. let (initial_timeout, outgoing_ulp_data) = if protocol.is_nominator() {
  106. (1000, "Tick")
  107. } else {
  108. (2000, "Tock")
  109. };
  110. let mut timeout = Duration::from_millis(initial_timeout);
  111. loop {
  112. let started_at = Instant::now();
  113. match rx.recv_timeout(timeout) {
  114. Ok((pid, incoming_frame)) => {
  115. // Calculate remaining time for the next iteration
  116. timeout = timeout.saturating_sub(Instant::elapsed(&started_at));
  117. // Receive and process incoming frame
  118. let maybe_result = process_incoming_frame(&mut protocol, pid, &incoming_frame)?;
  119. // Handle result
  120. if let Some(result) = maybe_result {
  121. // We're not expecting any state updates.
  122. assert!(result.state_update.is_none(), "Unexpected state update");
  123. // We're not expecting to send any outgoing frames since the handshake state
  124. // machine has completed.
  125. assert!(
  126. result.outgoing_frame.is_none(),
  127. "Unexpected outgoing frame in nominated state"
  128. );
  129. // We do expect incoming ULP data.
  130. let incoming_ulp_data =
  131. String::from_utf8(result.incoming_ulp_data.expect("Expecting incoming ULP data"))
  132. .context("Failed to decode ULP data string")?;
  133. info!(data = incoming_ulp_data, ?incoming_frame, "Received ULP data");
  134. }
  135. },
  136. Err(RecvTimeoutError::Timeout) => {
  137. // Create outgoing frame
  138. let result = protocol
  139. .create_ulp_frame(outgoing_ulp_data.as_bytes().to_vec())
  140. .context("Failed to create ULP frame")?;
  141. info!(
  142. data = outgoing_ulp_data,
  143. outgoing_frame = ?result.outgoing_frame,
  144. "Sending ULP data"
  145. );
  146. // We're not expecting any state updates.
  147. assert!(result.state_update.is_none(), "Unexpected state update");
  148. // Send any outgoing frame
  149. if let Some(outgoing_frame) = result.outgoing_frame {
  150. tx.send((nominated_pid, outgoing_frame))
  151. .context("Failed to send outgoing frame")?;
  152. }
  153. // We're not expecting to receive any incoming ULP data.
  154. assert!(result.incoming_ulp_data.is_none(), "Unexpected incoming ULP data");
  155. // Reset timeout
  156. timeout = Duration::from_secs(2);
  157. },
  158. Err(RecvTimeoutError::Disconnected) => {
  159. return Err(RecvTimeoutError::Disconnected).context("Failed to receive incoming frame")?;
  160. },
  161. }
  162. }
  163. }
  164. fn main() {
  165. // Configure logging
  166. init_stderr_logging(Level::TRACE);
  167. // Communication channels for RID and RRD
  168. let (to_rrd, from_rid) = mpsc::channel::<(u32, RendezvousOutgoingFrame)>();
  169. let (to_rid, from_rrd) = mpsc::channel::<(u32, RendezvousOutgoingFrame)>();
  170. // Start RID
  171. let rid_thread = thread::spawn(move || {
  172. trace_span!("initiator").in_scope(|| {
  173. // Create and run protocol for RID
  174. let protocol =
  175. RendezvousProtocol::new_as_rid(true, RendezvousAuthenticationKey(Keys::AK), &[0x1, 0x2]);
  176. let result = run_protocol(protocol, vec![], to_rrd, from_rrd);
  177. info!("Initiator stopped: {result:?}");
  178. });
  179. });
  180. // Start RRD
  181. let rrd_thread = thread::spawn(move || {
  182. trace_span!("responder").in_scope(|| {
  183. // Create and run protocol for RRD
  184. let (protocol, initial_outgoing_frames) =
  185. RendezvousProtocol::new_as_rrd(false, RendezvousAuthenticationKey(Keys::AK), &[0x1, 0x2]);
  186. let result = run_protocol(protocol, initial_outgoing_frames, to_rid, from_rid);
  187. info!("Responder stopped: {result:?}");
  188. });
  189. });
  190. // Join threads
  191. let _ = [rid_thread, rrd_thread].map(|handle| handle.join().expect("Joining threads failed"));
  192. }