1 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4 // option. This file may not be copied, modified, or distributed
5 // except according to those terms.
6 
7 // https://datatracker.ietf.org/doc/html/draft-ietf-quic-datagram
8 
9 use crate::frame::{FRAME_TYPE_DATAGRAM, FRAME_TYPE_DATAGRAM_WITH_LEN};
10 use crate::packet::PacketBuilder;
11 use crate::recovery::RecoveryToken;
12 use crate::{events::OutgoingDatagramOutcome, ConnectionEvents, Error, Res, Stats};
13 use neqo_common::Encoder;
14 use std::cmp::min;
15 use std::collections::VecDeque;
16 use std::convert::TryFrom;
17 use std::ops::Deref;
18 
19 pub const MAX_QUIC_DATAGRAM: u64 = 65535;
20 
21 #[derive(Debug, Clone, Copy)]
22 pub enum DatagramTracking {
23     None,
24     Id(u64),
25 }
26 
27 impl From<Option<u64>> for DatagramTracking {
from(v: Option<u64>) -> Self28     fn from(v: Option<u64>) -> Self {
29         match v {
30             Some(id) => Self::Id(id),
31             None => Self::None,
32         }
33     }
34 }
35 
36 impl From<DatagramTracking> for Option<u64> {
from(v: DatagramTracking) -> Self37     fn from(v: DatagramTracking) -> Self {
38         match v {
39             DatagramTracking::Id(id) => Some(id),
40             DatagramTracking::None => None,
41         }
42     }
43 }
44 
45 struct QuicDatagram {
46     data: Vec<u8>,
47     tracking: DatagramTracking,
48 }
49 
50 impl QuicDatagram {
tracking(&self) -> &DatagramTracking51     fn tracking(&self) -> &DatagramTracking {
52         &self.tracking
53     }
54 }
55 
56 impl Deref for QuicDatagram {
57     type Target = [u8];
58     #[must_use]
deref(&self) -> &[u8]59     fn deref(&self) -> &[u8] {
60         &self.data[..]
61     }
62 }
63 
64 pub struct QuicDatagrams {
65     /// The max size of a datagram that would be acceptable.
66     local_datagram_size: u64,
67     /// The max size of a datagram that would be acceptable by the peer.
68     remote_datagram_size: u64,
69     max_queued_outgoing_datagrams: usize,
70     /// The max number of datagrams that will be queued in connection events.
71     /// If the number is exceeded, the oldest datagram will be dropped.
72     max_queued_incoming_datagrams: usize,
73     /// Datagram queued for sending.
74     datagrams: VecDeque<QuicDatagram>,
75     conn_events: ConnectionEvents,
76 }
77 
78 impl QuicDatagrams {
new( local_datagram_size: u64, max_queued_outgoing_datagrams: usize, max_queued_incoming_datagrams: usize, conn_events: ConnectionEvents, ) -> Self79     pub fn new(
80         local_datagram_size: u64,
81         max_queued_outgoing_datagrams: usize,
82         max_queued_incoming_datagrams: usize,
83         conn_events: ConnectionEvents,
84     ) -> Self {
85         Self {
86             local_datagram_size,
87             remote_datagram_size: 0,
88             max_queued_outgoing_datagrams,
89             max_queued_incoming_datagrams,
90             datagrams: VecDeque::with_capacity(max_queued_outgoing_datagrams),
91             conn_events,
92         }
93     }
94 
remote_datagram_size(&self) -> u6495     pub fn remote_datagram_size(&self) -> u64 {
96         self.remote_datagram_size
97     }
98 
set_remote_datagram_size(&mut self, v: u64)99     pub fn set_remote_datagram_size(&mut self, v: u64) {
100         self.remote_datagram_size = min(v, MAX_QUIC_DATAGRAM);
101     }
102 
103     /// This function tries to write a datagram frame into a packet.
104     /// If the frame does not fit into the packet, the datagram will
105     /// be dropped and a DatagramLost event will be posted.
write_frames( &mut self, builder: &mut PacketBuilder, tokens: &mut Vec<RecoveryToken>, stats: &mut Stats, )106     pub fn write_frames(
107         &mut self,
108         builder: &mut PacketBuilder,
109         tokens: &mut Vec<RecoveryToken>,
110         stats: &mut Stats,
111     ) {
112         while let Some(dgram) = self.datagrams.pop_front() {
113             let len = dgram.len();
114             if builder.remaining() > len {
115                 // We need 1 more than `len` for the Frame type.
116                 let length_len = Encoder::varint_len(u64::try_from(len).unwrap());
117                 // Include a length if there is space for another frame after this one.
118                 if builder.remaining() >= 1 + length_len + len + PacketBuilder::MINIMUM_FRAME_SIZE {
119                     builder.encode_varint(FRAME_TYPE_DATAGRAM_WITH_LEN);
120                     builder.encode_vvec(&dgram);
121                 } else {
122                     builder.encode_varint(FRAME_TYPE_DATAGRAM);
123                     builder.encode(&dgram);
124                     builder.mark_full();
125                 }
126                 debug_assert!(builder.len() <= builder.limit());
127                 stats.frame_tx.datagram += 1;
128                 tokens.push(RecoveryToken::Datagram(*dgram.tracking()));
129             } else if tokens.is_empty() {
130                 // If the packet is empty, except packet headers, and the
131                 // datagram cannot fit, drop it.
132                 // Also continue trying to write the next QuicDatagram.
133                 self.conn_events
134                     .datagram_outcome(dgram.tracking(), OutgoingDatagramOutcome::DroppedTooBig);
135                 stats.datagram_tx.dropped_too_big += 1;
136             } else {
137                 self.datagrams.push_front(dgram);
138                 // Try later on an empty packet.
139                 return;
140             }
141         }
142     }
143 
144     /// Returns true if there was an unsent datagram that has been dismissed.
145     /// # Error
146     /// The function returns `TooMuchData` if the supply buffer is bigger than
147     /// the allowed remote datagram size. The funcion does not check if the
148     /// datagram can fit into a packet (i.e. MTU limit). This is checked during
149     /// creation of an actual packet and the datagram will be dropped if it does
150     /// not fit into the packet.
add_datagram( &mut self, buf: &[u8], tracking: DatagramTracking, stats: &mut Stats, ) -> Res<()>151     pub fn add_datagram(
152         &mut self,
153         buf: &[u8],
154         tracking: DatagramTracking,
155         stats: &mut Stats,
156     ) -> Res<()> {
157         if u64::try_from(buf.len()).unwrap() > self.remote_datagram_size {
158             return Err(Error::TooMuchData);
159         }
160         if self.datagrams.len() == self.max_queued_outgoing_datagrams {
161             self.conn_events.datagram_outcome(
162                 self.datagrams.pop_front().unwrap().tracking(),
163                 OutgoingDatagramOutcome::DroppedQueueFull,
164             );
165             stats.datagram_tx.dropped_queue_full += 1;
166         }
167         self.datagrams.push_back(QuicDatagram {
168             data: buf.to_vec(),
169             tracking,
170         });
171         Ok(())
172     }
173 
handle_datagram(&self, data: &[u8], stats: &mut Stats) -> Res<()>174     pub fn handle_datagram(&self, data: &[u8], stats: &mut Stats) -> Res<()> {
175         if self.local_datagram_size < u64::try_from(data.len()).unwrap() {
176             return Err(Error::ProtocolViolation);
177         }
178         self.conn_events
179             .add_datagram(self.max_queued_incoming_datagrams, data, stats);
180         Ok(())
181     }
182 }
183