Skip to content

Commit

Permalink
Add TOS and TTL information to Datagrams
Browse files Browse the repository at this point in the history
This part of the refactor of mozilla#1495.

This doesn't include any ECN I/O or other logic, just adds the
required fields to the datagram header.
  • Loading branch information
larseggert committed Jan 18, 2024
1 parent 64fb41f commit 29e650b
Show file tree
Hide file tree
Showing 18 changed files with 215 additions and 60 deletions.
4 changes: 2 additions & 2 deletions neqo-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ fn process_loop(
break 'read;
}
if sz > 0 {
let d = Datagram::new(remote, *local_addr, &buf[..sz]);
let d = Datagram::new(remote, *local_addr, None, None, &buf[..sz]);
datagrams.push(d);
}
}
Expand Down Expand Up @@ -1365,7 +1365,7 @@ mod old {
break 'read;
}
if sz > 0 {
let d = Datagram::new(remote, *local_addr, &buf[..sz]);
let d = Datagram::new(remote, *local_addr, None, None, &buf[..sz]);
client.process_input(&d, Instant::now());
handler.maybe_key_update(client)?;
}
Expand Down
1 change: 1 addition & 0 deletions neqo-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ build = "build.rs"

[dependencies]
log = { version = "0.4.0", default-features = false }
enum-map = "~2.7.3"
env_logger = { version = "0.10", default-features = false }
lazy_static = "1.3.0"
qlog = "0.11.0"
Expand Down
94 changes: 91 additions & 3 deletions neqo-common/src/datagram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,97 @@
use std::net::SocketAddr;
use std::ops::Deref;

use enum_map::Enum;

use crate::hex_with_len;

// ECN (Explicit Congestion Notification) codepoints mapped to the
// lower 2 bits of the TOS field.
// https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml
#[derive(Copy, Clone, PartialEq, Eq, Enum)]
#[repr(u8)]
pub enum IpTosEcn {
NotEct = 0b00, // Not-ECT (Not ECN-Capable Transport) [RFC3168]
Ect1 = 0b01, // ECT(1) (ECN-Capable Transport(1))[1] [RFC8311][RFC Errata 5399][RFC9331]
Ect0 = 0b10, // ECT(0) (ECN-Capable Transport(0)) [RFC3168]
Ce = 0b11, // CE (Congestion Experienced) [RFC3168]
}

impl From<u8> for IpTosEcn {
fn from(v: u8) -> Self {
match v & 0b11 {
0b00 => IpTosEcn::NotEct,
0b01 => IpTosEcn::Ect1,
0b10 => IpTosEcn::Ect0,
0b11 => IpTosEcn::Ce,
_ => unreachable!(),
}
}
}

impl From<IpTosEcn> for u8 {
fn from(val: IpTosEcn) -> Self {
val as u8
}
}

impl std::fmt::Debug for IpTosEcn {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
IpTosEcn::NotEct => f.write_str("Not-ECT"),
IpTosEcn::Ect1 => f.write_str("ECT(1)"),
IpTosEcn::Ect0 => f.write_str("ECT(0)"),
IpTosEcn::Ce => f.write_str("CE"),
}
}
}

// DiffServ Codepoints, mapped to the upper six bits of the TOS field.
// https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum IpTosDscp {
Cs0 = 0b0000_0000, // [RFC2474]
Cs1 = 0b0010_0000, // [RFC2474]
Cs2 = 0b0100_0000, // [RFC2474]
Cs3 = 0b0110_0000, // [RFC2474]
Cs4 = 0b1000_0000, // [RFC2474]
Cs5 = 0b1010_0000, // [RFC2474]
Cs6 = 0b1100_0000, // [RFC2474]
Cs7 = 0b1110_0000, // [RFC2474]
Af11 = 0b0010_1000, // [RFC2597]
Af12 = 0b0011_0000, // [RFC2597]
Af13 = 0b0011_1000, // [RFC2597]
Af21 = 0b0100_1000, // [RFC2597]
Af22 = 0b0101_0000, // [RFC2597]
Af23 = 0b0101_1000, // [RFC2597]
Af31 = 0b0110_1000, // [RFC2597]
Af32 = 0b0111_0000, // [RFC2597]
Af33 = 0b0111_1000, // [RFC2597]
Af41 = 0b1000_1000, // [RFC2597]
Af42 = 0b1001_0000, // [RFC2597]
Af43 = 0b1001_1000, // [RFC2597]
Ef = 0b1011_1000, // [RFC3246]
VoiceAdmit = 0b1011_0000, // [RFC5865]
Le = 0b0000_0100, // [RFC8622]
}

#[derive(PartialEq, Eq, Clone)]
pub struct Datagram {
src: SocketAddr,
dst: SocketAddr,
tos: Option<u8>,
ttl: Option<u8>,
d: Vec<u8>,
}

impl Datagram {
pub fn new<V: Into<Vec<u8>>>(src: SocketAddr, dst: SocketAddr, d: V) -> Self {
pub fn new<V: Into<Vec<u8>>>(src: SocketAddr, dst: SocketAddr, tos: Option<u8>, ttl: Option<u8>, d: V) -> Self {
Self {
src,
dst,
tos,
ttl,
d: d.into(),
}
}
Expand All @@ -34,7 +111,16 @@ impl Datagram {
pub fn destination(&self) -> SocketAddr {
self.dst
}
}

#[must_use]
pub fn tos(&self) -> Option<u8> {
self.tos
}

#[must_use]
pub fn ttl(&self) -> Option<u8> {
self.ttl
}}

impl Deref for Datagram {
type Target = Vec<u8>;
Expand All @@ -48,7 +134,9 @@ impl std::fmt::Debug for Datagram {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"Datagram {:?}->{:?}: {}",
"Datagram TOS {:?} TTL {:?} {:?}->{:?}: {}",
self.tos,
self.ttl,
self.src,
self.dst,
hex_with_len(&self.d)
Expand Down
2 changes: 1 addition & 1 deletion neqo-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub mod qlog;
pub mod timer;

pub use self::codec::{Decoder, Encoder};
pub use self::datagram::Datagram;
pub use self::datagram::{IpTosDscp, IpTosEcn, Datagram};
pub use self::header::Header;
pub use self::incrdecoder::{
IncrementalDecoderBuffer, IncrementalDecoderIgnore, IncrementalDecoderUint,
Expand Down
12 changes: 9 additions & 3 deletions neqo-interop/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ fn process_loop(
continue;
}
if sz > 0 {
let received = Datagram::new(nctx.remote_addr, nctx.local_addr, &buf[..sz]);
let received = Datagram::new(nctx.remote_addr, nctx.local_addr, None, None, &buf[..sz]);
client.process_input(&received, Instant::now());
}
}
Expand Down Expand Up @@ -309,7 +309,7 @@ fn process_loop_h3(
continue;
}
if sz > 0 {
let received = Datagram::new(nctx.remote_addr, nctx.local_addr, &buf[..sz]);
let received = Datagram::new(nctx.remote_addr, nctx.local_addr, None, None, &buf[..sz]);
handler.h3.process_input(&received, Instant::now());
}
}
Expand Down Expand Up @@ -682,7 +682,13 @@ impl Handler for VnHandler {
fn rewrite_out(&mut self, d: &Datagram) -> Option<Datagram> {
let mut payload = d[..].to_vec();
payload[1] = 0x1a;
Some(Datagram::new(d.source(), d.destination(), payload))
Some(Datagram::new(
d.source(),
d.destination(),
d.tos(),
d.ttl(),
payload,
))
}
}

Expand Down
2 changes: 1 addition & 1 deletion neqo-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ fn read_dgram(
eprintln!("zero length datagram received?");
Ok(None)
} else {
Ok(Some(Datagram::new(remote_addr, *local_address, &buf[..sz])))
Ok(Some(Datagram::new(remote_addr, *local_address, None, None, &buf[..sz])))
}
}

Expand Down
2 changes: 1 addition & 1 deletion neqo-transport/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ impl Connection {
/// part that we don't have keys for.
fn save_datagram(&mut self, cspace: CryptoSpace, d: &Datagram, remaining: usize, now: Instant) {
let d = if remaining < d.len() {
Datagram::new(d.source(), d.destination(), &d[d.len() - remaining..])
Datagram::new(d.source(), d.destination(), d.tos(), d.ttl(), &d[d.len() - remaining..])
} else {
d.clone()
};
Expand Down
5 changes: 2 additions & 3 deletions neqo-transport/src/connection/tests/close.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ use super::{connect, connect_force_idle, default_client, default_server, send_so
use crate::tparams::{self, TransportParameter};
use crate::{AppError, ConnectionError, Error, ERROR_APPLICATION_CLOSE};

use neqo_common::Datagram;
use std::time::Duration;
use test_fixture::{self, addr, now};
use test_fixture::{self, datagram, now};

fn assert_draining(c: &Connection, expected: &Error) {
assert!(c.state().closed());
Expand Down Expand Up @@ -201,6 +200,6 @@ fn stateless_reset_client() {
.unwrap();
connect_force_idle(&mut client, &mut server);

client.process_input(&Datagram::new(addr(), addr(), vec![77; 21]), now());
client.process_input(&datagram(vec![77; 21]), now());
assert_draining(&client, &Error::StatelessReset);
}
12 changes: 8 additions & 4 deletions neqo-transport/src/connection/tests/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use std::mem;
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::rc::Rc;
use std::time::Duration;
use test_fixture::{self, addr, assertions, fixture_init, now, split_datagram};
use test_fixture::{self, addr, assertions, datagram, fixture_init, now, split_datagram};

const ECH_CONFIG_ID: u8 = 7;
const ECH_PUBLIC_NAME: &str = "public.example";
Expand Down Expand Up @@ -615,7 +615,7 @@ fn corrupted_initial() {
.find(|(_, &v)| v != 0)
.unwrap();
corrupted[idx] ^= 0x76;
let dgram = Datagram::new(d.source(), d.destination(), corrupted);
let dgram = Datagram::new(d.source(), d.destination(), d.tos(), d.ttl(), corrupted);
server.process_input(&dgram, now());
// The server should have received two packets,
// the first should be dropped, the second saved.
Expand Down Expand Up @@ -711,7 +711,7 @@ fn extra_initial_invalid_cid() {
let mut copy = hs.to_vec();
assert_ne!(copy[5], 0); // The DCID should be non-zero length.
copy[6] ^= 0xc4;
let dgram_copy = Datagram::new(hs.destination(), hs.source(), copy);
let dgram_copy = Datagram::new(hs.destination(), hs.source(), hs.tos(), hs.ttl(), copy);
let nothing = client.process(Some(&dgram_copy), now).dgram();
assert!(nothing.is_none());
}
Expand Down Expand Up @@ -814,7 +814,7 @@ fn garbage_initial() {
let mut corrupted = Vec::from(&initial[..initial.len() - 1]);
corrupted.push(initial[initial.len() - 1] ^ 0xb7);
corrupted.extend_from_slice(rest.as_ref().map_or(&[], |r| &r[..]));
let garbage = Datagram::new(addr(), addr(), corrupted);
let garbage = datagram(corrupted);
assert_eq!(Output::None, server.process(Some(&garbage), now()));
}

Expand All @@ -832,6 +832,8 @@ fn drop_initial_packet_from_wrong_address() {
let dgram = Datagram::new(
SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)), 443),
p.destination(),
p.tos(),
p.ttl(),
&p[..],
);

Expand All @@ -858,6 +860,8 @@ fn drop_handshake_packet_from_wrong_address() {
let dgram = Datagram::new(
SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2)), 443),
p.destination(),
p.tos(),
p.ttl(),
&p[..],
);

Expand Down
4 changes: 2 additions & 2 deletions neqo-transport/src/connection/tests/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fn loopback() -> SocketAddr {
}

fn change_path(d: &Datagram, a: SocketAddr) -> Datagram {
Datagram::new(a, a, &d[..])
Datagram::new(a, a, d.tos(), d.ttl(), &d[..])
}

fn new_port(a: SocketAddr) -> SocketAddr {
Expand All @@ -61,7 +61,7 @@ fn new_port(a: SocketAddr) -> SocketAddr {
}

fn change_source_port(d: &Datagram) -> Datagram {
Datagram::new(new_port(d.source()), d.destination(), &d[..])
Datagram::new(new_port(d.source()), d.destination(), d.tos(), d.ttl(), &d[..])
}

/// As these tests use a new path, that path often has a non-zero RTT.
Expand Down
Loading

0 comments on commit 29e650b

Please sign in to comment.