1mod cli;
2mod config;
3mod result_handler;
4mod service;
5mod task_distributor;
6mod trace;
7mod worker;
8
9use std::collections::{HashMap, HashSet, VecDeque};
10use std::net::SocketAddr;
11use std::ops::AddAssign;
12use std::sync::{Arc, Mutex, RwLock};
13use std::time::{Duration, Instant};
14
15use crate::custom_module;
16use crate::custom_module::manycastr::{Address, MeasurementType, Start, WorkerStatus};
17use crate::orchestrator::config::{AllowedOrigin, load_allowed_origins, load_worker_config};
18use crate::orchestrator::mpsc::Sender;
19use crate::orchestrator::result_handler::SessionTracker;
20use crate::orchestrator::worker::WorkerSender;
21use crate::tls::server_identity;
22use clap::ArgMatches;
23use custom_module::manycastr::{
24 Instruction, ReplyBatch, Task, controller_server::ControllerServer,
25};
26use log::{info, warn};
27use tokio::sync::mpsc;
28use tonic::codec::CompressionEncoding;
29use tonic::transport::{Identity, ServerTlsConfig};
30use tonic::{Status, transport::Server};
31
32type ResultMessage = Result<ReplyBatch, Status>;
33type CliSender = Sender<ResultMessage>;
34pub(crate) type CliHandle = Arc<Mutex<Option<CliSender>>>;
35
36type TaskMessage = Result<Instruction, Status>;
37
38pub(crate) type WorkerRegistry = Arc<Mutex<Vec<WorkerSender<TaskMessage>>>>;
40
41pub type MeasurementHandle = Arc<RwLock<Option<MeasurementState>>>;
43
44#[derive(Debug)]
49pub struct Participant {
50 pub role: WorkerStatus,
52 pub is_counted: bool,
54}
55
56#[derive(Debug)]
58pub struct MeasurementState {
59 pub m_id: u32,
61 pub probing_workers: Vec<u32>,
63 pub participants: HashMap<u32, Participant>,
65 pub start_instructions: HashMap<u32, Start>,
67 pub is_finalizing: bool,
69 pub m_type: MeasurementType,
71 pub is_gated_broadcast: bool,
73 pub is_prefix_hitlist: bool,
75 pub nprobes: u32,
77 pub worker_stacks: HashMap<u32, VecDeque<Task>>,
79 pub trace_config: Option<TracerouteConfig>,
81 pub resolved_targets: HashSet<Address>,
83 pub live: Option<LiveState>,
85}
86
87impl MeasurementState {
88 pub fn active_workers(&self) -> usize {
91 self.participants.values().filter(|p| p.is_counted).count()
92 }
93}
94
95#[inline]
97pub fn wire_nprobes(nprobes: u32) -> u32 {
98 if nprobes > 1 { nprobes } else { 0 }
99}
100
101pub const LIVE_DISCOVERY_TIMEOUT_SECS: u64 = 3;
103
104#[derive(Debug)]
106pub enum WorkerSel {
107 Any,
109 All,
111 Set(Vec<u32>),
113}
114
115impl WorkerSel {
116 pub fn is_multi(&self) -> bool {
118 match self {
119 WorkerSel::Any => false,
120 WorkerSel::All => true,
121 WorkerSel::Set(ids) => ids.len() > 1,
122 }
123 }
124}
125
126#[derive(Debug)]
128pub struct LiveState {
129 pub pending: HashMap<(Address, u32), PendingTarget>,
131 pub set_stacks: HashMap<Vec<u32>, VecDeque<Task>>,
133 pub trace_targets: HashMap<Address, Instant>,
135}
136
137impl LiveState {
138 pub fn remove_pending(
141 &mut self,
142 addr: Address,
143 session_id: u32,
144 ) -> Option<(u32, PendingTarget)> {
145 if let Some(pending) = self.pending.remove(&(addr, session_id)) {
146 return Some((session_id, pending));
147 }
148 if session_id == 0 {
149 let key = self.pending.keys().find(|(a, _)| *a == addr).copied()?;
150 let pending = self.pending.remove(&key)?;
151 return Some((key.1, pending));
152 }
153 None
154 }
155}
156
157#[derive(Debug)]
159pub struct PendingTarget {
160 pub worker_sel: WorkerSel,
162 pub discovery_worker: u32,
164 pub nprobes: u32,
166 pub deadline: Instant,
168}
169
170#[derive(Debug)]
172pub struct TracerouteConfig {
173 pub session_tracker: SessionTracker,
175 pub origin_id: u32,
178 pub timeout: u64,
180 pub max_hops: u32,
182 pub initial_hop: u32,
184 pub max_failures: u32,
187 pub star_unresponsive: bool,
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub enum Access {
197 Both,
198 Workers,
199 Cli,
200}
201
202impl Access {
203 fn serves_workers(self) -> bool {
205 matches!(self, Access::Both | Access::Workers)
206 }
207
208 fn serves_cli(self) -> bool {
210 matches!(self, Access::Both | Access::Cli)
211 }
212}
213
214#[derive(Debug, Clone)]
216pub struct ControllerService {
217 saved_workers: WorkerRegistry,
219 cli_sender: CliHandle,
221 measurement: MeasurementHandle,
223 unique_id: Arc<Mutex<u32>>,
225 worker_config: Option<HashMap<String, u32>>,
227 max_rate: Option<u32>,
229 allowed_origins: Option<Vec<AllowedOrigin>>,
231 access: Access,
233}
234
235impl ControllerService {
236 fn check_worker_access(&self, call: &str) -> Result<(), Status> {
238 if self.access.serves_workers() {
239 return Ok(());
240 }
241
242 warn!("[Orchestrator] Refused worker call '{call}' on the CLI port");
243 Err(Status::permission_denied(
244 "this port only serves the CLI, connect workers to the worker port",
245 ))
246 }
247
248 fn check_cli_access(&self, call: &str) -> Result<(), Status> {
250 if self.access.serves_cli() {
251 return Ok(());
252 }
253
254 warn!("[Orchestrator] Refused CLI call '{call}' on the worker port");
255 Err(Status::permission_denied(
256 "this port only serves workers, connect the CLI to the CLI port",
257 ))
258 }
259
260 fn get_unique_id(&self) -> u32 {
263 let mut unique_id = self.unique_id.lock().unwrap();
264 let worker_id = *unique_id;
265 unique_id.add_assign(1);
266
267 worker_id
268 }
269
270 fn get_worker_id(&self, hostname: &str) -> Result<(u32, bool), Status> {
283 {
284 let workers = self.saved_workers.lock().unwrap();
285 if let Some(existing_worker) = workers.iter().find(|w| w.hostname == hostname) {
287 return if !existing_worker.is_closed() {
288 warn!("[Orchestrator] Refusing worker, hostname already exists: {hostname}");
289 Err(Status::already_exists("This hostname already exists"))
290 } else {
291 let id = existing_worker.worker_id;
293 Ok((id, true))
294 };
295 }
296 }
297
298 if let Some(worker_config) = &self.worker_config
300 && let Some(worker_id) = worker_config.get(hostname)
301 {
302 return Ok((*worker_id, false));
303 }
304
305 let new_id = self.get_unique_id();
307 Ok((new_id, false))
308 }
309
310 fn next_m_id(&self) -> u32 {
312 rand::random::<u16>() as u32
313 }
314}
315
316pub async fn start(args: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
327 let port = *args.get_one::<u16>("port").unwrap();
328 let cli_port = args.get_one::<u16>("cli_port").copied();
329 if cli_port == Some(port) {
330 return Err(
331 format!("--cli_port {port} must differ from --port, or be left out entirely").into(),
332 );
333 }
334
335 let (current_worker_id, worker_config) = args
337 .get_one::<String>("config")
338 .map(load_worker_config)
339 .unwrap_or_else(|| (Arc::new(Mutex::new(1)), None));
340
341 let controller = ControllerService {
342 saved_workers: Arc::new(Mutex::new(Vec::new())),
343 cli_sender: Arc::new(Mutex::new(None)),
344 measurement: Arc::new(RwLock::new(None)),
345 unique_id: current_worker_id,
346 worker_config,
347 max_rate: args.get_one::<u32>("max_rate").copied(),
348 allowed_origins: args.get_one::<String>("origins").map(load_allowed_origins),
349 access: Access::Both,
350 };
351
352 let identity = args.get_one::<String>("tls").map(|cert_path| {
354 info!("[Orchestrator] Starting orchestrator with TLS enabled");
355 server_identity(
356 cert_path,
357 args.get_one::<String>("tls_key").map(String::as_str),
358 )
359 });
360
361 let Some(cli_port) = cli_port else {
362 info!("[Orchestrator] Serving Workers and CLIs on port {port}");
363 return serve(port, controller, identity).await;
364 };
365
366 info!("[Orchestrator] Serving Workers on port {port}, CLIs on port {cli_port}");
368 let workers = serve(
369 port,
370 ControllerService {
371 access: Access::Workers,
372 ..controller.clone()
373 },
374 identity.clone(),
375 );
376 let clis = serve(
377 cli_port,
378 ControllerService {
379 access: Access::Cli,
380 ..controller
381 },
382 identity,
383 );
384
385 tokio::try_join!(workers, clis)?;
387
388 Ok(())
389}
390
391async fn serve(
401 port: u16,
402 controller: ControllerService,
403 identity: Option<Identity>,
404) -> Result<(), Box<dyn std::error::Error>> {
405 let addr: SocketAddr = format!("[::]:{port}").parse()?;
406
407 let svc = ControllerServer::new(controller)
408 .accept_compressed(CompressionEncoding::Zstd)
409 .max_decoding_message_size(10 * 1024 * 1024 * 1024) .max_encoding_message_size(10 * 1024 * 1024 * 1024);
411
412 let mut builder = Server::builder();
413 if let Some(identity) = identity {
414 builder = builder.tls_config(ServerTlsConfig::new().identity(identity))?;
415 }
416
417 builder
418 .http2_keepalive_interval(Some(Duration::from_secs(10)))
419 .http2_keepalive_timeout(Some(Duration::from_secs(20)))
420 .tcp_keepalive(Some(Duration::from_secs(30)))
421 .add_service(svc)
422 .serve(addr)
423 .await
424 .map_err(|e| format!("unable to serve on port {port}: {}", &e))?;
425
426 Ok(())
427}