1use crate::{
10 bbframe::{BBFrameDefrag, BBFrameReceiver, BBFrameRecv, BBFrameStream},
11 gseheader::Label,
12 gsepacket::{GSEPacketDefrag, PDU},
13 metrics::Metrics as MetricsTrait,
14};
15use anyhow::{Context, Result};
16use bytes::Bytes;
17use clap::Parser;
18use std::{
19 net::{SocketAddr, TcpListener, UdpSocket},
20 os::unix::io::AsRawFd,
21 sync::{Arc, Mutex, mpsc},
22 thread,
23 time::Duration,
24};
25
26pub struct App<AppArgs = Args, Metrics = ()> {
40 args: AppArgs,
41 metrics: AppMetrics<Metrics>,
42}
43
44#[derive(Parser, Debug)]
46#[command(author, version, about, long_about = None)]
47pub struct Args {
48 #[arg(long)]
50 pub listen: SocketAddr,
51 #[arg(long)]
53 pub tun: String,
54 #[arg(long, default_value_t)]
56 pub input: InputFormat,
57 #[arg(long, default_value_t = 0)]
59 pub header_length: usize,
60 #[arg(long)]
62 pub isi: Option<u8>,
63 #[arg(long, default_value_t = 100.0)]
65 pub stats_interval: f64,
66 #[arg(long)]
68 pub skip_total_length: bool,
69 #[arg(long)]
74 pub allow_broadcast: bool,
75 #[arg(long, value_parser = Label::from_hex)]
81 pub allow_label: Vec<Label>,
82}
83
84impl AsRef<Args> for Args {
85 fn as_ref(&self) -> &Args {
86 self
87 }
88}
89
90#[derive(Debug, Clone)]
91struct AppMetrics<Metrics> {
92 stats: Arc<Mutex<Stats>>,
93 metrics: Metrics,
94}
95
96impl<Metrics: MetricsTrait> MetricsTrait for AppMetrics<Metrics> {
97 fn bbframe_received(&mut self, bbframe: &Bytes) {
98 self.stats.lock().unwrap().bbframes += 1;
99 self.metrics.bbframe_received(bbframe);
100 }
101
102 fn bbframe_error(&mut self, error: &std::io::Error) {
103 self.stats.lock().unwrap().bbframe_errors += 1;
104 self.metrics.bbframe_error(error);
105 }
106
107 fn gse_pdu_received(&mut self, pdu: &PDU) {
108 self.stats.lock().unwrap().gse_pdus += 1;
109 self.metrics.gse_pdu_received(pdu);
110 }
111
112 fn gse_pdu_dropped_label_filtering(&mut self, pdu: &PDU) {
113 self.stats.lock().unwrap().gse_pdus_dropped_by_label += 1;
114 self.metrics.gse_pdu_dropped_label_filtering(pdu);
115 }
116
117 fn tcp_client_connected(&mut self, stream: &std::net::TcpStream) {
118 self.metrics.tcp_client_connected(stream);
119 }
120
121 fn tcp_client_finished(&mut self) {
122 self.metrics.tcp_client_finished();
123 }
124
125 fn tun_error(&mut self, error: &std::io::Error, pdu: &PDU) {
126 self.stats.lock().unwrap().tun_errors += 1;
127 self.metrics.tun_error(error, pdu);
128 }
129}
130
131impl<AppArgs: Parser, Metrics> App<AppArgs, Metrics> {
132 pub fn new<F: FnOnce(&AppArgs) -> Result<Metrics>>(build_metrics: F) -> Result<Self> {
140 env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
141 let args = AppArgs::parse();
142 let metrics = build_metrics(&args)?;
143 let stats = Arc::new(Mutex::new(Stats::default()));
144 let metrics = AppMetrics { stats, metrics };
145 Ok(App { args, metrics })
146 }
147}
148
149impl<AppArgs: Parser, Metrics: Default> App<AppArgs, Metrics> {
150 pub fn with_default_metrics() -> Result<Self> {
158 Self::new(|_| Ok(Metrics::default()))
159 }
160}
161
162impl<AppArgs: AsRef<Args>, Metrics: MetricsTrait + Clone + Send + 'static> App<AppArgs, Metrics> {
163 pub fn run(self) -> Result<()> {
167 let tun = tun_tap::Iface::without_packet_info(&self.args.as_ref().tun, tun_tap::Mode::Tun)
168 .context("failed to open TUN device")?;
169 log::info!("dvb-gse v{} started", env!("CARGO_PKG_VERSION"));
170 let stats_interval = self.args.as_ref().stats_interval;
171 if stats_interval != 0.0 {
172 std::thread::spawn({
173 let stats = Arc::clone(&self.metrics.stats);
174 move || {
175 report_stats(&stats, Duration::from_secs_f64(stats_interval));
176 }
177 });
178 }
179 match self.args.as_ref().input {
180 InputFormat::UdpFragments | InputFormat::UdpComplete => self.run_udp_input(tun),
181 InputFormat::Tcp => self.run_tcp_input(tun),
182 }
183 }
184
185 fn run_udp_input(self, tun: tun_tap::Iface) -> Result<()> {
186 let gsepacket_defrag = gsepacket_defragmenter(self.args.as_ref());
187 let socket =
188 UdpSocket::bind(self.args.as_ref().listen).context("failed to bind to UDP socket")?;
189 setup_multicast(&socket, &self.args.as_ref().listen)?;
190 let allow_settings = AllowSettings::from(self.args.as_ref());
191 match self.args.as_ref().input {
192 InputFormat::UdpFragments => {
193 let mut bbframe_recv = BBFrameDefrag::new(socket);
194 bbframe_recv.set_isi(self.args.as_ref().isi);
195 bbframe_recv.set_header_bytes(self.args.as_ref().header_length)?;
196 let mut app = AppLoop {
197 bbframe_recv,
198 gsepacket_defrag,
199 tun,
200 bbframe_recv_errors_fatal: true,
201 metrics: self.metrics,
202 allow_settings,
203 };
204 app.app_loop()?;
205 }
206 InputFormat::UdpComplete => {
207 let mut bbframe_recv = BBFrameRecv::new(socket);
208 bbframe_recv.set_isi(self.args.as_ref().isi);
209 bbframe_recv.set_header_bytes(self.args.as_ref().header_length)?;
210 let mut app = AppLoop {
211 bbframe_recv,
212 gsepacket_defrag,
213 tun,
214 bbframe_recv_errors_fatal: false,
215 metrics: self.metrics,
216 allow_settings,
217 };
218 app.app_loop()?;
219 }
220 InputFormat::Tcp => unreachable!(),
222 }
223 Ok(())
224 }
225
226 fn run_tcp_input(mut self, mut tun: tun_tap::Iface) -> Result<()> {
227 let listener =
228 TcpListener::bind(self.args.as_ref().listen).context("failed to bind to TCP socket")?;
229 let channel_capacity = 64;
234 let (tun_tx, tun_rx) = mpsc::sync_channel(channel_capacity);
235 let allow_settings = AllowSettings::from(self.args.as_ref());
236 thread::spawn({
237 let mut metrics = self.metrics.clone();
238 move || {
239 for pdu in tun_rx.iter() {
240 write_pdu_tun(&pdu, &mut tun, &mut metrics, &allow_settings);
241 }
242 }
243 });
244 thread::scope(|s| {
246 for stream in listener.incoming() {
247 let stream = match stream {
248 Ok(s) => s,
249 Err(e) => {
250 log::error!("connection error {e}");
251 continue;
252 }
253 };
254 match stream.peer_addr() {
255 Ok(addr) => log::info!("TCP client connected from {addr}"),
256 Err(err) => log::error!(
257 "TCP client connected (but could not retrieve peer address): {err}"
258 ),
259 }
260 self.metrics.tcp_client_connected(&stream);
261 s.spawn({
262 let args = self.args.as_ref();
263 let tun_tx = tun_tx.clone();
264 let mut metrics = self.metrics.clone();
265 move || {
266 let mut gsepacket_defrag = gsepacket_defragmenter(args);
267 let mut bbframe_recv = BBFrameStream::new(stream);
268 bbframe_recv.set_isi(args.isi);
269 if let Err(err) = bbframe_recv.set_header_bytes(args.header_length) {
270 eprintln!("could not set header length: {err}");
271 std::process::exit(1);
272 }
273 loop {
274 let bbframe = bbframe_recv.get_bbframe();
275 let bbframe = {
276 match bbframe {
277 Ok(b) => {
278 metrics.bbframe_received(&b);
279 b
280 }
281 Err(err) => {
282 log::error!("failed to receive BBFRAME; terminating connection: {err}");
283 metrics.bbframe_error(&err);
284 metrics.tcp_client_finished();
285 return;
286 }
287 }
288 };
289 for pdu in gsepacket_defrag.defragment(&bbframe).unwrap() {
291 tun_tx.send(pdu).unwrap();
292 }
293 }
294 }
295 });
296 }
297 });
298 Ok(())
299 }
300}
301
302#[derive(Debug, Clone)]
303struct AllowSettings {
304 allow_broadcast: bool,
305 allow_label: Vec<Label>,
306}
307
308impl AllowSettings {
309 fn is_label_allowed(&self, label: &Label) -> bool {
310 if !self.allow_broadcast && self.allow_label.is_empty() {
311 return true;
313 }
314 if label.is_broadcast() {
315 return self.allow_broadcast;
316 }
317 self.allow_label.iter().any(|allowed| label == allowed)
318 }
319}
320
321impl From<&Args> for AllowSettings {
322 fn from(args: &Args) -> AllowSettings {
323 AllowSettings {
324 allow_broadcast: args.allow_broadcast,
325 allow_label: args.allow_label.clone(),
326 }
327 }
328}
329
330#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
332pub enum InputFormat {
333 #[default]
335 UdpFragments,
336 UdpComplete,
338 Tcp,
340}
341
342impl std::str::FromStr for InputFormat {
343 type Err = String;
344
345 fn from_str(s: &str) -> Result<Self, Self::Err> {
346 Ok(match s {
347 "UDP" | "UDP fragments" => InputFormat::UdpFragments,
348 "UDP complete" => InputFormat::UdpComplete,
349 "TCP" => InputFormat::Tcp,
350 _ => return Err(format!("invalid input format {s}")),
351 })
352 }
353}
354
355impl std::fmt::Display for InputFormat {
356 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
357 write!(
358 f,
359 "{}",
360 match self {
361 InputFormat::UdpFragments => "UDP fragments",
362 InputFormat::UdpComplete => "UDP complete",
363 InputFormat::Tcp => "TCP",
364 }
365 )
366 }
367}
368
369fn setup_multicast(socket: &UdpSocket, addr: &SocketAddr) -> Result<()> {
370 match addr.ip() {
371 std::net::IpAddr::V4(addr) if addr.is_multicast() => {
372 set_reuseaddr(socket)?;
373 log::info!("joining multicast address {}", addr);
374 socket.join_multicast_v4(&addr, &std::net::Ipv4Addr::UNSPECIFIED)?;
375 }
376 std::net::IpAddr::V6(addr) if addr.is_multicast() => {
377 set_reuseaddr(socket)?;
378 log::info!("joining multicast address {}", addr);
379 socket.join_multicast_v6(&addr, 0)?;
380 }
381 _ => (),
382 }
383 Ok(())
384}
385
386fn set_reuseaddr(socket: &UdpSocket) -> Result<()> {
387 let optval: libc::c_int = 1;
388 if unsafe {
389 libc::setsockopt(
390 socket.as_raw_fd(),
391 libc::SOL_SOCKET,
392 libc::SO_REUSEADDR,
393 &optval as *const _ as *const libc::c_void,
394 libc::socklen_t::try_from(std::mem::size_of::<libc::c_int>()).unwrap(),
395 )
396 } != 0
397 {
398 let err = std::io::Error::last_os_error();
399 anyhow::bail!("could not set SO_REUSEADDR: {err}")
400 }
401 Ok(())
402}
403
404#[derive(Debug)]
405struct AppLoop<D, Metrics> {
406 bbframe_recv: D,
407 gsepacket_defrag: GSEPacketDefrag,
408 tun: tun_tap::Iface,
409 bbframe_recv_errors_fatal: bool,
410 metrics: AppMetrics<Metrics>,
411 allow_settings: AllowSettings,
412}
413
414fn write_pdu_tun<Metrics: MetricsTrait>(
415 pdu: &PDU,
416 tun: &mut tun_tap::Iface,
417 metrics: &mut AppMetrics<Metrics>,
418 allow_settings: &AllowSettings,
419) {
420 metrics.gse_pdu_received(pdu);
421 if !allow_settings.is_label_allowed(pdu.label()) {
422 metrics.gse_pdu_dropped_label_filtering(pdu);
423 return;
424 }
425 if let Err(err) = tun.send(pdu.data()) {
426 log::error!("could not write packet to TUN device: {err}");
427 metrics.tun_error(&err, pdu);
428 }
429}
430
431impl<D: BBFrameReceiver, Metrics: MetricsTrait> AppLoop<D, Metrics> {
432 fn app_loop(&mut self) -> Result<()> {
433 loop {
434 let bbframe = self.bbframe_recv.get_bbframe();
435 let bbframe = match bbframe {
436 Ok(b) => {
437 self.metrics.bbframe_received(&b);
438 b
439 }
440 Err(err) => {
441 self.metrics.bbframe_error(&err);
442 if self.bbframe_recv_errors_fatal {
443 return Err(err).context("failed to receive BBFRAME");
444 } else {
445 continue;
446 }
447 }
448 };
449 for pdu in self.gsepacket_defrag.defragment(&bbframe).unwrap() {
451 write_pdu_tun(&pdu, &mut self.tun, &mut self.metrics, &self.allow_settings);
452 }
453 }
454 }
455}
456
457fn gsepacket_defragmenter(args: &Args) -> GSEPacketDefrag {
458 let mut defrag = GSEPacketDefrag::new();
459 defrag.set_skip_total_length_check(args.skip_total_length);
460 defrag
461}
462
463#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
464struct Stats {
465 bbframes: u64,
466 bbframe_errors: u64,
467 gse_pdus: u64,
468 gse_pdus_dropped_by_label: u64,
469 tun_errors: u64,
470}
471
472fn report_stats(stats: &Mutex<Stats>, interval: Duration) {
473 loop {
474 {
475 let stats = stats.lock().unwrap();
476 log::info!(
477 "BBFRAMES: {}, BBFRAME errors: {}, GSE PDUs: {}, GSE PDUs dropped by label: {}, TUN errors: {}",
478 stats.bbframes,
479 stats.bbframe_errors,
480 stats.gse_pdus,
481 stats.gse_pdus_dropped_by_label,
482 stats.tun_errors
483 );
484 }
485 std::thread::sleep(interval);
486 }
487}