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