1use std::collections::HashMap;
10use std::io;
11use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV6, ToSocketAddrs};
12use std::time::{Duration, Instant};
13
14use crate::addr::normalize;
15use crate::error::{ConfigReason, LimitKind, NetError, TimeoutKind};
16use crate::sys::{self, AsSock};
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct UdpBudget {
27 pub datagrams_per_sec: u32,
29 pub bytes_per_sec: u32,
31 pub burst: u32,
33}
34
35impl UdpBudget {
36 pub const SOURCE_DEFAULT: UdpBudget = UdpBudget {
39 datagrams_per_sec: 1_000,
40 bytes_per_sec: 1_000_000,
41 burst: 100,
42 };
43}
44
45#[must_use]
55#[derive(Clone, Debug, PartialEq, Eq)]
56pub struct UdpOptions {
57 factor: u8,
58 window: Duration,
59 source: UdpBudget,
60 global: Option<UdpBudget>,
61 table: u32,
62 ipv6_only: bool,
63 packet_info: bool,
64}
65
66impl Default for UdpOptions {
67 fn default() -> UdpOptions {
68 UdpOptions::new()
69 }
70}
71
72impl UdpOptions {
73 pub fn new() -> UdpOptions {
76 UdpOptions {
77 factor: 1,
78 window: Duration::from_secs(10),
79 source: UdpBudget::SOURCE_DEFAULT,
80 global: None,
81 table: 65_536,
82 ipv6_only: true,
83 packet_info: false,
84 }
85 }
86
87 pub fn amplification_factor(mut self, factor: u8) -> UdpOptions {
90 self.factor = factor;
91 self
92 }
93
94 pub fn amplification_window(mut self, window: Duration) -> UdpOptions {
98 self.window = window;
99 self
100 }
101
102 pub fn source_budget(mut self, budget: UdpBudget) -> UdpOptions {
104 self.source = budget;
105 self
106 }
107
108 pub fn global_budget(mut self, budget: UdpBudget) -> UdpOptions {
110 self.global = Some(budget);
111 self
112 }
113
114 pub fn source_table(mut self, entries: u32) -> UdpOptions {
116 self.table = entries;
117 self
118 }
119
120 pub fn packet_info(mut self, on: bool) -> UdpOptions {
127 self.packet_info = on;
128 self
129 }
130
131 pub fn ipv6_only(mut self, on: bool) -> UdpOptions {
133 self.ipv6_only = on;
134 self
135 }
136
137 fn validate(&self) -> Result<(), NetError> {
138 let invalid = |field, reason| Err(NetError::InvalidConfig { field, reason });
139 if self.factor == 0 {
140 return invalid("amplification_factor", ConfigReason::Zero);
141 }
142 if self.factor > 3 {
143 return invalid("amplification_factor", ConfigReason::TooLarge { max: 3 });
144 }
145 if self.window < Duration::from_secs(1) {
146 return invalid("amplification_window", ConfigReason::TooSmall { min: 1 });
147 }
148 if self.table == 0 {
149 return invalid("source_table", ConfigReason::Zero);
150 }
151 for (field, b) in [
152 ("source_budget", Some(self.source)),
153 ("global_budget", self.global),
154 ] {
155 if let Some(b) = b {
156 if b.datagrams_per_sec == 0 || b.bytes_per_sec == 0 || b.burst == 0 {
157 return invalid(field, ConfigReason::Zero);
158 }
159 }
160 }
161 Ok(())
162 }
163}
164
165#[derive(Clone, Copy, Debug, PartialEq, Eq)]
172pub struct Datagram {
173 pub len: usize,
176 pub from: SocketAddr,
178 pub truncated: bool,
180 pub destination: Option<IpAddr>,
182}
183
184#[non_exhaustive]
192#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
193pub struct UdpStats {
194 pub received: u64,
196 pub sent: u64,
198 pub dropped: u64,
200 pub refused: u64,
202 pub truncated: u64,
204 pub icmp_errors: u64,
206 pub overflow: u64,
208 pub sources: u32,
210}
211
212#[derive(Clone, Copy, Debug)]
214struct Bucket {
215 datagrams: u64,
216 bytes: u64,
217 at: Instant,
218}
219
220const MICRO: u64 = 1_000_000;
221
222impl Bucket {
223 fn full(budget: &UdpBudget, now: Instant) -> Bucket {
224 Bucket {
225 datagrams: u64::from(budget.burst) * MICRO,
226 bytes: u64::from(budget.bytes_per_sec) * MICRO,
227 at: now,
228 }
229 }
230
231 fn take(&mut self, budget: &UdpBudget, len: usize, now: Instant) -> bool {
233 let us = now
236 .saturating_duration_since(self.at)
237 .as_micros()
238 .min(1_000_000_000) as u64;
239 self.at = now;
240 let cap_d = u64::from(budget.burst) * MICRO;
241 let cap_b = u64::from(budget.bytes_per_sec) * MICRO;
242 self.datagrams = (self.datagrams + us * u64::from(budget.datagrams_per_sec)).min(cap_d);
243 self.bytes = (self.bytes + us * u64::from(budget.bytes_per_sec)).min(cap_b);
244 let need_b = (len as u64).saturating_mul(MICRO);
245 if self.datagrams >= MICRO && self.bytes >= need_b {
246 self.datagrams -= MICRO;
247 self.bytes -= need_b;
248 true
249 } else {
250 false
251 }
252 }
253}
254
255#[derive(Clone, Copy, Debug)]
257struct Entry {
258 ip: IpAddr,
259 bucket: Bucket,
260 received: [u64; 2],
261 sent: [u64; 2],
262 window_at: Instant,
263 verified: bool,
264 referenced: bool,
265}
266
267impl Entry {
268 fn new(ip: IpAddr, budget: &UdpBudget, now: Instant) -> Entry {
269 Entry {
270 ip,
271 bucket: Bucket::full(budget, now),
272 received: [0; 2],
273 sent: [0; 2],
274 window_at: now,
275 verified: false,
276 referenced: true,
277 }
278 }
279
280 fn roll(&mut self, window: Duration, now: Instant) {
282 let gone = now.saturating_duration_since(self.window_at);
283 if gone >= window * 2 {
284 self.received = [0; 2];
285 self.sent = [0; 2];
286 self.window_at = now;
287 } else if gone >= window {
288 self.received = [0, self.received[0]];
289 self.sent = [0, self.sent[0]];
290 self.window_at += window;
291 }
292 }
293}
294
295const SWEEP: usize = 64;
298
299#[derive(Debug)]
301struct Guard {
302 index: HashMap<IpAddr, u32>,
303 slots: Vec<Entry>,
304 hand: usize,
305 overflow: Bucket,
306 global: Option<Bucket>,
307 options: UdpOptions,
308}
309
310impl Guard {
311 fn new(options: UdpOptions, now: Instant) -> Guard {
312 Guard {
313 index: HashMap::new(),
314 slots: Vec::new(),
315 hand: 0,
316 overflow: Bucket::full(&options.source, now),
317 global: options.global.map(|g| Bucket::full(&g, now)),
318 options,
319 }
320 }
321
322 fn find(&mut self, ip: IpAddr) -> Option<usize> {
323 let i = *self.index.get(&ip)? as usize;
324 self.slots[i].referenced = true;
325 Some(i)
326 }
327
328 fn slot(&mut self, ip: IpAddr, now: Instant, force: bool) -> Option<usize> {
331 if let Some(i) = self.find(ip) {
332 return Some(i);
333 }
334 let entry = Entry::new(ip, &self.options.source, now);
335 if self.slots.len() < self.options.table as usize {
336 self.slots.push(entry);
337 let i = self.slots.len() - 1;
338 self.index.insert(ip, i as u32);
339 return Some(i);
340 }
341 let n = self.slots.len();
342 let looks = if force { 2 * n } else { SWEEP.min(2 * n) };
343 for _ in 0..looks {
344 let i = self.hand;
345 self.hand = (self.hand + 1) % n;
346 let e = &mut self.slots[i];
347 if e.verified {
348 continue;
349 }
350 if e.referenced {
351 e.referenced = false;
352 continue;
353 }
354 self.index.remove(&e.ip);
355 *e = entry;
356 self.index.insert(ip, i as u32);
357 return Some(i);
358 }
359 None
360 }
361
362 fn admit(&mut self, ip: IpAddr, len: usize, now: Instant, overflow: &mut bool) -> bool {
365 let source = self.options.source;
366 let ok = match self.slot(ip, now, false) {
367 Some(i) => {
368 let window = self.options.window;
369 let e = &mut self.slots[i];
370 if e.bucket.take(&source, len, now) {
371 e.roll(window, now);
372 e.received[0] += len as u64;
373 true
374 } else {
375 false
376 }
377 }
378 None => {
379 *overflow = true;
380 self.overflow.take(&source, len, now)
381 }
382 };
383 if !ok {
384 return false;
385 }
386 match (&mut self.global, &self.options.global) {
387 (Some(bucket), Some(budget)) => bucket.take(budget, len, now),
388 _ => true,
389 }
390 }
391
392 fn may_send(&mut self, ip: IpAddr, len: usize, now: Instant) -> bool {
394 let Some(i) = self.find(ip) else {
395 return len == 0;
396 };
397 let window = self.options.window;
398 let factor = u64::from(self.options.factor);
399 let e = &mut self.slots[i];
400 if e.verified {
401 return true;
402 }
403 e.roll(window, now);
404 let allowed = factor * (e.received[0] + e.received[1]);
405 e.sent[0] + e.sent[1] + len as u64 <= allowed
406 }
407
408 fn sent(&mut self, ip: IpAddr, len: usize) {
409 if let Some(&i) = self.index.get(&ip) {
410 self.slots[i as usize].sent[0] += len as u64;
411 }
412 }
413
414 fn verify(&mut self, ip: IpAddr, now: Instant) {
415 if let Some(i) = self.slot(ip, now, true) {
416 self.slots[i].verified = true;
417 }
418 }
419}
420
421#[derive(Debug)]
437pub struct UdpSocket {
438 inner: std::net::UdpSocket,
439 guard: Guard,
440 read_timeout: Option<Duration>,
441 peer: Option<SocketAddr>,
442 v6: bool,
443 #[cfg_attr(windows, allow(dead_code))]
444 packet_info: bool,
445 stats: UdpStats,
446}
447
448#[derive(Debug)]
456pub struct DatagramBuffer<'a> {
457 pub buf: &'a mut [u8],
459 pub datagram: Option<Datagram>,
461}
462
463impl<'a> DatagramBuffer<'a> {
464 #[must_use]
466 pub fn new(buf: &'a mut [u8]) -> DatagramBuffer<'a> {
467 DatagramBuffer {
468 buf,
469 datagram: None,
470 }
471 }
472}
473
474#[derive(Clone, Copy, Debug)]
481pub struct OutgoingDatagram<'a> {
482 pub bytes: &'a [u8],
484 pub to: SocketAddr,
486}
487
488#[non_exhaustive]
495#[derive(Clone, Copy, Debug, PartialEq, Eq)]
496pub enum MulticastInterface {
497 Any,
499 V4(Ipv4Addr),
501 V6Index(u32),
503}
504
505impl UdpSocket {
506 pub fn bind<A: ToSocketAddrs>(addr: A) -> Result<UdpSocket, NetError> {
512 UdpSocket::bind_with(addr, UdpOptions::new())
513 }
514
515 pub fn bind_with<A: ToSocketAddrs>(
521 addr: A,
522 options: UdpOptions,
523 ) -> Result<UdpSocket, NetError> {
524 options.validate()?;
525 let addrs: Vec<SocketAddr> = addr
526 .to_socket_addrs()
527 .map_err(|e| NetError::Os {
528 op: "resolve",
529 source: e,
530 })?
531 .collect();
532 let mut last = NetError::NoAddresses;
533 if options.packet_info && !cfg!(unix) {
534 return Err(NetError::Unsupported {
535 feature: "UDP packet info",
536 platform: std::env::consts::OS,
537 });
538 }
539 let packet_info = options.packet_info;
540 for a in addrs {
541 match sys::udp_bind(a, options.ipv6_only) {
542 Ok(inner) => {
543 #[cfg(unix)]
544 if packet_info {
545 sys::udp_packet_info(inner.sock(), a.is_ipv6())
546 .map_err(|e| NetError::from_os("setsockopt", e))?;
547 }
548 return Ok(UdpSocket {
549 inner,
550 guard: Guard::new(options, Instant::now()),
551 read_timeout: None,
552 peer: None,
553 v6: a.is_ipv6(),
554 packet_info,
555 stats: UdpStats::default(),
556 });
557 }
558 Err(e) => last = crate::tcp::bind_error(a, e),
559 }
560 }
561 Err(last)
562 }
563
564 pub fn recv_from(&mut self, buf: &mut [u8]) -> Result<Datagram, NetError> {
572 let deadline = self.read_timeout.map(|t| Instant::now() + t);
573 loop {
574 let (len, from, truncated, destination) = match self.recv_one(buf) {
575 Ok(got) => got,
576 Err(e) => match e.kind() {
577 io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut => {
578 return Err(NetError::TimedOut {
579 kind: TimeoutKind::Read,
580 });
581 }
582 io::ErrorKind::Interrupted => continue,
583 io::ErrorKind::ConnectionRefused | io::ErrorKind::ConnectionReset => {
584 self.stats.icmp_errors += 1;
586 if past(deadline) {
587 return Err(NetError::TimedOut {
588 kind: TimeoutKind::Read,
589 });
590 }
591 continue;
592 }
593 _ => return Err(NetError::from_os("recvfrom", e)),
594 },
595 };
596 let from = crate::addr::normalize_socket(from);
597 if !self.admit(from, len, truncated) {
598 if past(deadline) {
599 return Err(NetError::TimedOut {
600 kind: TimeoutKind::Read,
601 });
602 }
603 continue;
604 }
605 return Ok(Datagram {
606 len,
607 from,
608 truncated,
609 destination,
610 });
611 }
612 }
613
614 fn recv_one(
615 &mut self,
616 buf: &mut [u8],
617 ) -> io::Result<(usize, SocketAddr, bool, Option<IpAddr>)> {
618 #[cfg(unix)]
619 if self.packet_info {
620 return sys::udp_recv_with_destination(self.inner.sock(), buf);
621 }
622 let (len, from, truncated) = sys::udp_recv_from(self.inner.sock(), buf)?;
623 Ok((len, from, truncated, None))
624 }
625
626 fn admit(&mut self, from: SocketAddr, len: usize, truncated: bool) -> bool {
628 let mut overflow = false;
629 let admitted = self
630 .guard
631 .admit(from.ip(), len, Instant::now(), &mut overflow);
632 if overflow {
633 self.stats.overflow += 1;
634 }
635 if !admitted {
636 self.stats.dropped += 1;
637 return false;
638 }
639 self.stats.received += 1;
640 if truncated {
641 self.stats.truncated += 1;
642 }
643 true
644 }
645
646 pub fn recv_batch(&mut self, slots: &mut [DatagramBuffer<'_>]) -> Result<usize, NetError> {
656 for slot in slots.iter_mut() {
657 slot.datagram = None;
658 }
659 if slots.is_empty() {
660 return Ok(0);
661 }
662 self.recv_many(slots)
663 }
664
665 #[cfg(target_os = "linux")]
666 fn recv_many(&mut self, slots: &mut [DatagramBuffer<'_>]) -> Result<usize, NetError> {
667 let count = slots.len().min(sys::BATCH_MAX);
668 let mut got = [None; sys::BATCH_MAX];
669 let n = loop {
670 let received = {
671 let mut bufs: [&mut [u8]; sys::BATCH_MAX] = std::array::from_fn(|_| &mut [][..]);
672 for (b, slot) in bufs.iter_mut().zip(slots.iter_mut()).take(count) {
673 *b = &mut *slot.buf;
674 }
675 sys::udp_recv_many(self.inner.sock(), &mut bufs[..count], &mut got)
676 };
677 match received {
678 Ok(n) => break n,
679 Err(e) => match e.kind() {
680 io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut => {
681 return Err(NetError::TimedOut {
682 kind: TimeoutKind::Read,
683 });
684 }
685 io::ErrorKind::Interrupted => {}
686 io::ErrorKind::ConnectionRefused | io::ErrorKind::ConnectionReset => {
687 self.stats.icmp_errors += 1;
688 }
689 _ => return Err(NetError::from_os("recvmmsg", e)),
690 },
691 }
692 };
693 let mut filled = 0;
694 for (slot, r) in slots.iter_mut().zip(got.iter()).take(n) {
695 let Some(r) = r else { continue };
696 let from = crate::addr::normalize_socket(r.from);
697 if self.admit(from, r.len, r.truncated) {
698 slot.datagram = Some(Datagram {
699 len: r.len,
700 from,
701 truncated: r.truncated,
702 destination: None,
703 });
704 filled += 1;
705 }
706 }
707 Ok(filled)
708 }
709
710 #[cfg(not(target_os = "linux"))]
711 fn recv_many(&mut self, slots: &mut [DatagramBuffer<'_>]) -> Result<usize, NetError> {
712 let (first, rest) = match slots.split_first_mut() {
713 Some(parts) => parts,
714 None => return Ok(0),
715 };
716 first.datagram = Some(self.recv_from(first.buf)?);
717 let mut filled = 1;
718 self.inner
719 .set_nonblocking(true)
720 .map_err(|e| NetError::from_os("ioctl", e))?;
721 for slot in rest.iter_mut().take(63) {
722 match self.recv_from(slot.buf) {
723 Ok(d) => {
724 slot.datagram = Some(d);
725 filled += 1;
726 }
727 Err(_) => break,
728 }
729 }
730 self.inner
731 .set_nonblocking(false)
732 .map_err(|e| NetError::from_os("ioctl", e))?;
733 Ok(filled)
734 }
735
736 pub fn send_batch(&mut self, datagrams: &[OutgoingDatagram<'_>]) -> Result<usize, NetError> {
744 let mut sent = 0;
745 while sent < datagrams.len() {
746 match self.send_some(&datagrams[sent..]) {
747 Ok(0) => break,
748 Ok(n) => sent += n,
749 Err(e) if sent == 0 => return Err(e),
750 Err(_) => break,
751 }
752 }
753 Ok(sent)
754 }
755
756 fn charge(&mut self, datagrams: &[OutgoingDatagram<'_>]) -> Result<usize, NetError> {
759 let now = Instant::now();
760 let mut allowed = 0;
761 for d in datagrams.iter().take(64) {
762 let ip = normalize(d.to.ip());
763 if !self.guard.may_send(ip, d.bytes.len(), now) {
764 self.stats.refused += 1;
765 break;
766 }
767 self.guard.sent(ip, d.bytes.len());
768 allowed += 1;
769 }
770 if allowed == 0 && !datagrams.is_empty() {
771 return Err(NetError::Limit {
772 which: LimitKind::Amplification,
773 });
774 }
775 Ok(allowed)
776 }
777
778 #[cfg(target_os = "linux")]
779 fn send_some(&mut self, datagrams: &[OutgoingDatagram<'_>]) -> Result<usize, NetError> {
780 let allowed = self.charge(datagrams)?;
781 let mut wire = [(&[][..], SocketAddr::from(([0, 0, 0, 0], 0))); sys::BATCH_MAX];
782 for (w, d) in wire.iter_mut().zip(datagrams.iter().take(allowed)) {
783 *w = (d.bytes, self.wire(d.to));
784 }
785 let n = sys::udp_send_many(self.inner.sock(), &wire[..allowed])
786 .map_err(|e| NetError::from_os("sendmmsg", e))?;
787 self.stats.sent += n as u64;
788 Ok(n)
789 }
790
791 #[cfg(not(target_os = "linux"))]
792 fn send_some(&mut self, datagrams: &[OutgoingDatagram<'_>]) -> Result<usize, NetError> {
793 let allowed = self.charge(datagrams)?;
794 let mut n = 0;
795 for d in &datagrams[..allowed] {
796 let wire = self.wire(d.to);
797 if let Err(e) = self.inner.send_to(d.bytes, wire) {
798 if n == 0 {
799 return Err(NetError::from_os("sendto", e));
800 }
801 break;
802 }
803 self.stats.sent += 1;
804 n += 1;
805 }
806 Ok(n)
807 }
808
809 pub fn join_multicast(
816 &self,
817 group: IpAddr,
818 interface: MulticastInterface,
819 ) -> Result<(), NetError> {
820 let result = match (group, interface) {
821 (IpAddr::V4(g), MulticastInterface::Any) => {
822 self.inner.join_multicast_v4(&g, &Ipv4Addr::UNSPECIFIED)
823 }
824 (IpAddr::V4(g), MulticastInterface::V4(i)) => self.inner.join_multicast_v4(&g, &i),
825 (IpAddr::V6(g), MulticastInterface::Any) => self.inner.join_multicast_v6(&g, 0),
826 (IpAddr::V6(g), MulticastInterface::V6Index(i)) => self.inner.join_multicast_v6(&g, i),
827 _ => {
828 return Err(NetError::InvalidConfig {
829 field: "interface",
830 reason: ConfigReason::Inconsistent,
831 });
832 }
833 };
834 result.map_err(|e| NetError::from_os("setsockopt", e))
835 }
836
837 pub fn recv(&mut self, buf: &mut [u8]) -> Result<Datagram, NetError> {
843 self.recv_from(buf)
844 }
845
846 pub fn send_to(&mut self, buf: &[u8], to: SocketAddr) -> Result<usize, NetError> {
854 let ip = normalize(to.ip());
855 if !self.guard.may_send(ip, buf.len(), Instant::now()) {
856 self.stats.refused += 1;
857 return Err(NetError::Limit {
858 which: LimitKind::Amplification,
859 });
860 }
861 let wire = self.wire(to);
862 let n = self
863 .inner
864 .send_to(buf, wire)
865 .map_err(|e| NetError::from_os("sendto", e))?;
866 self.guard.sent(ip, n);
867 self.stats.sent += 1;
868 Ok(n)
869 }
870
871 pub fn send(&mut self, buf: &[u8]) -> Result<usize, NetError> {
877 let peer = self.peer.ok_or(NetError::Closed)?;
878 let ip = normalize(peer.ip());
879 if !self.guard.may_send(ip, buf.len(), Instant::now()) {
880 self.stats.refused += 1;
881 return Err(NetError::Limit {
882 which: LimitKind::Amplification,
883 });
884 }
885 let n = self
886 .inner
887 .send(buf)
888 .map_err(|e| NetError::from_os("send", e))?;
889 self.guard.sent(ip, n);
890 self.stats.sent += 1;
891 Ok(n)
892 }
893
894 pub fn connect(&mut self, to: SocketAddr) -> Result<(), NetError> {
901 self.inner
902 .connect(self.wire(to))
903 .map_err(|e| NetError::from_os("connect", e))?;
904 self.peer = Some(to);
905 Ok(())
906 }
907
908 pub fn mark_verified(&mut self, peer: SocketAddr) {
911 self.guard.verify(normalize(peer.ip()), Instant::now());
912 }
913
914 pub fn set_read_timeout(&mut self, timeout: Option<Duration>) -> Result<(), NetError> {
920 if timeout == Some(Duration::ZERO) {
921 return Err(NetError::InvalidConfig {
922 field: "read_timeout",
923 reason: ConfigReason::Zero,
924 });
925 }
926 self.inner
927 .set_read_timeout(timeout)
928 .map_err(|e| NetError::from_os("setsockopt", e))?;
929 self.read_timeout = timeout;
930 Ok(())
931 }
932
933 pub(crate) fn set_nonblocking(&self) -> Result<(), NetError> {
935 self.inner
936 .set_nonblocking(true)
937 .map_err(|e| NetError::from_os("ioctl", e))
938 }
939
940 pub(crate) fn raw_sock(&self) -> sys::Sock<'_> {
941 self.inner.sock()
942 }
943
944 pub fn local_addr(&self) -> Result<SocketAddr, NetError> {
950 self.inner
951 .local_addr()
952 .map_err(|e| NetError::from_os("getsockname", e))
953 }
954
955 #[must_use]
957 pub fn stats(&self) -> UdpStats {
958 UdpStats {
959 sources: self.guard.slots.len() as u32,
960 ..self.stats
961 }
962 }
963
964 fn wire(&self, to: SocketAddr) -> SocketAddr {
966 match to {
967 SocketAddr::V4(a) if self.v6 => {
968 SocketAddr::V6(SocketAddrV6::new(a.ip().to_ipv6_mapped(), a.port(), 0, 0))
969 }
970 other => other,
971 }
972 }
973}
974
975pub trait UdpSession: Send + 'static {
990 fn on_datagram(&mut self, cx: &mut UdpContext<'_>, from: SocketAddr, bytes: &[u8]);
992}
993
994#[derive(Debug)]
996pub struct UdpContext<'a> {
997 pub(crate) socket: &'a mut UdpSocket,
998}
999
1000impl UdpContext<'_> {
1001 pub fn send_to(&mut self, bytes: &[u8], to: SocketAddr) -> Result<usize, NetError> {
1007 self.socket.send_to(bytes, to)
1008 }
1009
1010 pub fn mark_verified(&mut self, peer: SocketAddr) {
1012 self.socket.mark_verified(peer);
1013 }
1014
1015 #[must_use]
1017 pub fn now(&self) -> Instant {
1018 Instant::now()
1019 }
1020}
1021
1022pub trait UdpSessionFactory: Send + Sync + 'static {
1025 fn open(&self, listener: &crate::tcp::ListenerInfo) -> Box<dyn UdpSession>;
1027}
1028
1029impl<F, S> UdpSessionFactory for F
1030where
1031 F: Fn(&crate::tcp::ListenerInfo) -> S + Send + Sync + 'static,
1032 S: UdpSession,
1033{
1034 fn open(&self, listener: &crate::tcp::ListenerInfo) -> Box<dyn UdpSession> {
1035 Box::new(self(listener))
1036 }
1037}
1038
1039fn past(deadline: Option<Instant>) -> bool {
1040 deadline.is_some_and(|d| Instant::now() >= d)
1041}
1042
1043#[cfg(unix)]
1044impl std::os::fd::AsFd for UdpSocket {
1045 fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
1046 self.inner.as_fd()
1047 }
1048}
1049
1050#[cfg(windows)]
1051impl std::os::windows::io::AsSocket for UdpSocket {
1052 fn as_socket(&self) -> std::os::windows::io::BorrowedSocket<'_> {
1053 self.inner.as_socket()
1054 }
1055}
1056
1057#[cfg(test)]
1058mod tests {
1059 use super::*;
1060
1061 fn ip(n: u8) -> IpAddr {
1062 IpAddr::from([10, 0, 0, n])
1063 }
1064
1065 #[test]
1066 fn test_bucket_refills_at_its_rate() {
1067 let b = UdpBudget {
1068 datagrams_per_sec: 10,
1069 bytes_per_sec: 1_000,
1070 burst: 2,
1071 };
1072 let t = Instant::now();
1073 let mut bucket = Bucket::full(&b, t);
1074 assert!(bucket.take(&b, 100, t));
1075 assert!(bucket.take(&b, 100, t));
1076 assert!(!bucket.take(&b, 100, t), "burst used up");
1077 assert!(bucket.take(&b, 100, t + Duration::from_millis(100)));
1079 assert!(!bucket.take(&b, 1_001, t + Duration::from_secs(5)));
1081 }
1082
1083 #[test]
1084 fn test_amplification_factor_one_until_verified() {
1085 let t = Instant::now();
1087 let mut g = Guard::new(UdpOptions::new(), t);
1088 let mut o = false;
1089 assert!(!g.may_send(ip(1), 1, t), "never heard from");
1090 assert!(g.may_send(ip(1), 0, t));
1091 assert!(g.admit(ip(1), 1, t, &mut o));
1092 assert!(!g.may_send(ip(1), 2, t));
1093 assert!(g.may_send(ip(1), 1, t));
1094 g.sent(ip(1), 1);
1095 assert!(!g.may_send(ip(1), 1, t), "the allowance is spent");
1096 g.verify(ip(1), t);
1097 assert!(g.may_send(ip(1), 10_000, t));
1098 }
1099
1100 #[test]
1101 fn test_amplification_factor_three_and_the_window() {
1102 let t = Instant::now();
1103 let w = Duration::from_secs(10);
1104 let mut g = Guard::new(UdpOptions::new().amplification_factor(3), t);
1105 let mut o = false;
1106 assert!(g.admit(ip(1), 100, t, &mut o));
1107 assert!(g.may_send(ip(1), 300, t));
1108 assert!(!g.may_send(ip(1), 301, t));
1109 assert!(g.may_send(ip(1), 300, t + w + Duration::from_millis(1)));
1111 assert!(!g.may_send(ip(1), 1, t + 3 * w));
1113 }
1114
1115 #[test]
1116 fn test_source_budget_drops_and_others_pass() {
1117 let t = Instant::now();
1118 let budget = UdpBudget {
1119 datagrams_per_sec: 1,
1120 bytes_per_sec: 10_000,
1121 burst: 3,
1122 };
1123 let mut g = Guard::new(UdpOptions::new().source_budget(budget), t);
1124 let mut o = false;
1125 let passed = (0..10).filter(|_| g.admit(ip(1), 10, t, &mut o)).count();
1126 assert_eq!(passed, 3);
1127 assert!(
1128 g.admit(ip(2), 10, t, &mut o),
1129 "another source has its own budget"
1130 );
1131 assert!(!o);
1132 }
1133
1134 #[test]
1135 fn test_global_budget_caps_every_source() {
1136 let t = Instant::now();
1137 let global = UdpBudget {
1138 datagrams_per_sec: 1,
1139 bytes_per_sec: 10_000,
1140 burst: 4,
1141 };
1142 let mut g = Guard::new(UdpOptions::new().global_budget(global), t);
1143 let mut o = false;
1144 let passed = (1..=10).filter(|&n| g.admit(ip(n), 10, t, &mut o)).count();
1145 assert_eq!(passed, 4);
1146 }
1147
1148 #[test]
1149 fn test_full_table_evicts_strangers_and_keeps_verified() {
1150 let t = Instant::now();
1152 let mut g = Guard::new(UdpOptions::new().source_table(4), t);
1153 let mut o = false;
1154 for n in 1..=4 {
1155 assert!(g.admit(ip(n), 10, t, &mut o));
1156 }
1157 g.verify(ip(1), t);
1158 g.verify(ip(2), t);
1159 assert!(g.admit(ip(5), 10, t, &mut o));
1162 assert!(!o, "an unverified entry was evicted");
1163 assert!(g.index.contains_key(&ip(1)) && g.index.contains_key(&ip(2)));
1164 assert_eq!(g.slots.len(), 4);
1165 for n in [3, 4, 5] {
1167 if g.index.contains_key(&ip(n)) {
1168 g.verify(ip(n), t);
1169 }
1170 }
1171 for n in 6..=7 {
1172 g.verify(ip(n), t);
1173 }
1174 let mut o = false;
1175 assert!(g.admit(ip(9), 10, t, &mut o));
1176 assert!(o, "no room: the overflow budget");
1177 assert!(
1178 !g.may_send(ip(9), 1, t),
1179 "an overflow stranger gets no allowance"
1180 );
1181 assert!(
1182 g.index.contains_key(&ip(1)),
1183 "verified peers keep their entries"
1184 );
1185 }
1186
1187 #[test]
1188 fn test_options_validate() {
1189 let bad = |o: UdpOptions| matches!(o.validate(), Err(NetError::InvalidConfig { .. }));
1190 assert!(bad(UdpOptions::new().amplification_factor(0)));
1191 assert!(bad(UdpOptions::new().amplification_factor(4)));
1192 assert!(bad(
1193 UdpOptions::new().amplification_window(Duration::from_millis(500))
1194 ));
1195 assert!(bad(UdpOptions::new().source_table(0)));
1196 assert!(bad(UdpOptions::new().source_budget(UdpBudget {
1197 datagrams_per_sec: 0,
1198 bytes_per_sec: 1,
1199 burst: 1
1200 })));
1201 assert!(UdpOptions::new().validate().is_ok());
1202 assert_eq!(UdpOptions::default(), UdpOptions::new());
1203 }
1204
1205 #[test]
1206 fn test_echo_truncation_and_zero_length() {
1207 let mut s = UdpSocket::bind("127.0.0.1:0").unwrap();
1208 s.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
1209 let to = s.local_addr().unwrap();
1210 let c = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
1211 c.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
1212 c.send_to(b"hello", to).unwrap();
1213 let mut buf = [0u8; 64];
1214 let d = s.recv_from(&mut buf).unwrap();
1215 assert_eq!(
1216 (d.len, d.truncated, d.from),
1217 (5, false, c.local_addr().unwrap())
1218 );
1219 assert_eq!(s.send_to(&buf[..5], d.from).unwrap(), 5);
1220 let mut back = [0u8; 8];
1221 assert_eq!(c.recv(&mut back).unwrap(), 5);
1222 assert!(matches!(
1223 s.send_to(b"x", d.from),
1224 Err(NetError::Limit {
1225 which: LimitKind::Amplification
1226 })
1227 ));
1228 c.send_to(&[7u8; 100], to).unwrap();
1230 let mut small = [0u8; 10];
1231 let d = s.recv_from(&mut small).unwrap();
1232 assert!(d.truncated);
1233 c.send_to(&[], to).unwrap();
1234 let d = s.recv_from(&mut buf).unwrap();
1235 assert_eq!((d.len, d.truncated), (0, false));
1236 let st = s.stats();
1237 assert_eq!(
1238 (st.received, st.sent, st.refused, st.truncated),
1239 (3, 1, 1, 1)
1240 );
1241 assert_eq!(st.sources, 1);
1242 }
1243
1244 #[test]
1245 fn test_icmp_reports_never_end_the_socket() {
1246 use crate::sys::shim::{Fault, Op, clear_here, program_here};
1248 let mut s = UdpSocket::bind("127.0.0.1:0").unwrap();
1249 s.set_read_timeout(Some(Duration::from_millis(300)))
1250 .unwrap();
1251 let closed = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
1252 let gone = closed.local_addr().unwrap();
1253 drop(closed);
1254 s.connect(gone).unwrap();
1255 s.mark_verified(gone);
1256 s.send(b"anyone?").unwrap();
1257 program_here(Op::Recv, 0, 1, Fault::Kind(io::ErrorKind::ConnectionReset));
1258 let mut buf = [0u8; 16];
1259 assert!(matches!(
1260 s.recv(&mut buf),
1261 Err(NetError::TimedOut {
1262 kind: TimeoutKind::Read
1263 })
1264 ));
1265 clear_here();
1266 assert!(s.stats().icmp_errors >= 1);
1267 let mut t = UdpSocket::bind("127.0.0.1:0").unwrap();
1269 t.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
1270 let c = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
1271 c.send_to(b"ok", t.local_addr().unwrap()).unwrap();
1272 assert_eq!(t.recv_from(&mut buf).unwrap().len, 2);
1273 }
1274
1275 #[test]
1276 fn test_dual_stack_replies_to_ipv4_sources() {
1277 let Ok(mut s) = UdpSocket::bind_with("[::]:0", UdpOptions::new().ipv6_only(false)) else {
1278 return; };
1280 s.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
1281 let port = s.local_addr().unwrap().port();
1282 let c = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
1283 c.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
1284 c.send_to(b"v4", ("127.0.0.1", port)).unwrap();
1285 let mut buf = [0u8; 8];
1286 let d = s.recv_from(&mut buf).unwrap();
1287 assert!(d.from.is_ipv4(), "normalized: {}", d.from);
1288 s.send_to(b"ok", d.from).unwrap();
1289 assert_eq!(c.recv(&mut buf).unwrap(), 2);
1290 }
1291
1292 fn verified_pair() -> (UdpSocket, UdpSocket) {
1293 let mut a = UdpSocket::bind("127.0.0.1:0").unwrap();
1294 let mut b = UdpSocket::bind("127.0.0.1:0").unwrap();
1295 let (aa, ba) = (a.local_addr().unwrap(), b.local_addr().unwrap());
1296 a.mark_verified(ba);
1297 b.mark_verified(aa);
1298 a.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
1299 b.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
1300 (a, b)
1301 }
1302
1303 #[test]
1304 fn test_batches_cross_in_one_call_each() {
1305 let (mut a, mut b) = verified_pair();
1306 let to = b.local_addr().unwrap();
1307 let bodies: Vec<Vec<u8>> = (0..10u8).map(|i| vec![i; 1 + usize::from(i)]).collect();
1308 let out: Vec<_> = bodies
1309 .iter()
1310 .map(|b| OutgoingDatagram { bytes: b, to })
1311 .collect();
1312 assert_eq!(a.send_batch(&out).unwrap(), 10);
1313 std::thread::sleep(Duration::from_millis(50));
1314 let mut store = vec![[0u8; 64]; 16];
1315 let mut seen = 0;
1316 while seen < 10 {
1317 let mut slots: Vec<_> = store.iter_mut().map(|b| DatagramBuffer::new(b)).collect();
1318 let n = b.recv_batch(&mut slots).unwrap();
1319 assert!(n > 0);
1320 for slot in &slots {
1321 if let Some(d) = slot.datagram {
1322 assert_eq!(d.from, a.local_addr().unwrap());
1323 assert_eq!(&slot.buf[..d.len], &bodies[seen][..]);
1324 seen += 1;
1325 }
1326 }
1327 }
1328 assert_eq!(a.stats().sent, 10);
1329 assert_eq!(b.stats().received, 10);
1330 }
1331
1332 #[test]
1335 fn test_batch_send_stays_under_the_guard() {
1336 let mut server = UdpSocket::bind("127.0.0.1:0").unwrap();
1337 let client = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
1338 let to = client.local_addr().unwrap();
1339 client
1340 .send_to(&[0u8; 100], server.local_addr().unwrap())
1341 .unwrap();
1342 let mut buf = [0u8; 200];
1343 server.recv_from(&mut buf).unwrap();
1344 let big = [1u8; 40];
1345 let out = [OutgoingDatagram { bytes: &big, to }; 4];
1346 assert_eq!(
1347 server.send_batch(&out).unwrap(),
1348 2,
1349 "100 bytes of allowance"
1350 );
1351 assert!(matches!(
1352 server.send_batch(&out),
1353 Err(NetError::Limit {
1354 which: LimitKind::Amplification
1355 })
1356 ));
1357 }
1358
1359 #[cfg(unix)]
1360 #[test]
1361 fn test_packet_info_reports_the_destination() {
1362 let mut server =
1363 UdpSocket::bind_with("0.0.0.0:0", UdpOptions::new().packet_info(true)).unwrap();
1364 server
1365 .set_read_timeout(Some(Duration::from_secs(5)))
1366 .unwrap();
1367 let port = server.local_addr().unwrap().port();
1368 let client = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
1369 client.send_to(b"hi", ("127.0.0.1", port)).unwrap();
1370 let mut buf = [0u8; 16];
1371 let d = server.recv_from(&mut buf).unwrap();
1372 assert_eq!(d.len, 2);
1373 assert_eq!(d.destination, Some("127.0.0.1".parse().unwrap()));
1374 }
1375
1376 #[cfg(windows)]
1377 #[test]
1378 fn test_packet_info_is_refused_on_windows() {
1379 assert!(matches!(
1380 UdpSocket::bind_with("127.0.0.1:0", UdpOptions::new().packet_info(true)),
1381 Err(NetError::Unsupported { .. })
1382 ));
1383 }
1384
1385 #[test]
1386 fn test_multicast_join_checks_the_family() {
1387 let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
1388 let group: IpAddr = "239.1.2.3".parse().unwrap();
1389 assert!(matches!(
1390 socket.join_multicast(group, MulticastInterface::V6Index(1)),
1391 Err(NetError::InvalidConfig { .. })
1392 ));
1393 let _ = socket.join_multicast(group, MulticastInterface::Any);
1395 }
1396}