Skip to content

Commit

Permalink
network path awareness
Browse files Browse the repository at this point in the history
Currently quiche does not hold any information about the network path of
a QUIC connection (such as the address of the peer), and the application
is responsible for maintaining such information.

This means that in case of a network migration, the application is
responsible for detecting the change and switching over to new path,
however there is currently no way for an application to actually
validate a new path, as required by the QUIC spec.

Adding an ad-hoc API to only expose path validation to applications
would likely be very cumbersome, due to the synchronization needed
between an application and quiche on the state of the current path, and
any other path being probed.

Instead, this change makes quiche be aware of the network path being
used.

The application needs to communicate the destination address of a
connection upon creationg (via `accept()` or `connect()`), as well as
the source address of received packets (via `recv()` and the new
`RecvInfo` structure).

In turn quiche will provide the application with the destination address
of generated packets (via `send()` and the new `SendInfo` structure).

Currently only the destination address of a connection is tracked, which
would allow quiche to handle responding to migrations transparently from
the application (but this will be added as a separate change).

Additional fields can later be added to `RecvInfo` and `SendInfo`, such
as the address of the local endpoint in order to be able to initiate
migrations, rather than just respond to them.
  • Loading branch information
ghedo committed May 20, 2021
1 parent 4aafa65 commit b426d0c
Show file tree
Hide file tree
Showing 19 changed files with 691 additions and 313 deletions.
31 changes: 25 additions & 6 deletions examples/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ static void debug_log(const char *line, void *argp) {
static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
static uint8_t out[MAX_DATAGRAM_SIZE];

quiche_send_info send_info;

while (1) {
ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out));
ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out),
&send_info);

if (written == QUICHE_ERR_DONE) {
fprintf(stderr, "done writing\n");
Expand All @@ -74,7 +77,10 @@ static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
return;
}

ssize_t sent = send(conn_io->sock, out, written, 0);
ssize_t sent = sendto(conn_io->sock, out, written, 0,
(struct sockaddr *) &send_info.to,
send_info.to_len);

if (sent != written) {
perror("failed to send");
return;
Expand All @@ -96,7 +102,13 @@ static void recv_cb(EV_P_ ev_io *w, int revents) {
static uint8_t buf[65535];

while (1) {
ssize_t read = recv(conn_io->sock, buf, sizeof(buf), 0);
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len = sizeof(peer_addr);
memset(&peer_addr, 0, peer_addr_len);

ssize_t read = recvfrom(conn_io->sock, buf, sizeof(buf), 0,
(struct sockaddr *) &peer_addr,
&peer_addr_len);

if (read < 0) {
if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
Expand All @@ -108,7 +120,13 @@ static void recv_cb(EV_P_ ev_io *w, int revents) {
return;
}

ssize_t done = quiche_conn_recv(conn_io->conn, buf, read);
quiche_recv_info recv_info = {
(struct sockaddr *) &peer_addr,

peer_addr_len,
};

ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);

if (done < 0) {
fprintf(stderr, "failed to process packet\n");
Expand Down Expand Up @@ -269,8 +287,9 @@ int main(int argc, char *argv[]) {
return -1;
}

quiche_conn *conn = quiche_connect(host, (const uint8_t *) scid,
sizeof(scid), config);
quiche_conn *conn = quiche_connect(host, (const uint8_t*) scid, sizeof(scid),
peer->ai_addr, peer->ai_addrlen, config);

if (conn == NULL) {
fprintf(stderr, "failed to create connection\n");
return -1;
Expand Down
17 changes: 10 additions & 7 deletions examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ fn main() {
let scid = quiche::ConnectionId::from_ref(&scid);

// Create a QUIC connection and initiate handshake.
let mut conn = quiche::connect(url.domain(), &scid, &mut config).unwrap();
let mut conn =
quiche::connect(url.domain(), &scid, peer_addr, &mut config).unwrap();

info!(
"connecting to {:} from {:} with scid {}",
Expand All @@ -118,9 +119,9 @@ fn main() {
hex_dump(&scid)
);

let write = conn.send(&mut out).expect("initial send failed");
let (write, send_info) = conn.send(&mut out).expect("initial send failed");

while let Err(e) = socket.send(&out[..write]) {
while let Err(e) = socket.send_to(&out[..write], &send_info.to) {
if e.kind() == std::io::ErrorKind::WouldBlock {
debug!("send() would block");
continue;
Expand Down Expand Up @@ -151,7 +152,7 @@ fn main() {
break 'read;
}

let len = match socket.recv(&mut buf) {
let (len, from) = match socket.recv_from(&mut buf) {
Ok(v) => v,

Err(e) => {
Expand All @@ -168,8 +169,10 @@ fn main() {

debug!("got {} bytes", len);

let recv_info = quiche::RecvInfo { from };

// Process potentially coalesced packets.
let read = match conn.recv(&mut buf[..len]) {
let read = match conn.recv(&mut buf[..len], recv_info) {
Ok(v) => v,

Err(e) => {
Expand Down Expand Up @@ -233,7 +236,7 @@ fn main() {
// Generate outgoing QUIC packets and send them on the UDP socket, until
// quiche reports that there are no more packets to be sent.
loop {
let write = match conn.send(&mut out) {
let (write, send_info) = match conn.send(&mut out) {
Ok(v) => v,

Err(quiche::Error::Done) => {
Expand All @@ -249,7 +252,7 @@ fn main() {
},
};

if let Err(e) = socket.send(&out[..write]) {
if let Err(e) = socket.send_to(&out[..write], &send_info.to) {
if e.kind() == std::io::ErrorKind::WouldBlock {
debug!("send() would block");
break;
Expand Down
31 changes: 25 additions & 6 deletions examples/http3-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ static void debug_log(const char *line, void *argp) {
static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
static uint8_t out[MAX_DATAGRAM_SIZE];

quiche_send_info send_info;

while (1) {
ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out));
ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out),
&send_info);

if (written == QUICHE_ERR_DONE) {
fprintf(stderr, "done writing\n");
Expand All @@ -78,7 +81,10 @@ static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
return;
}

ssize_t sent = send(conn_io->sock, out, written, 0);
ssize_t sent = sendto(conn_io->sock, out, written, 0,
(struct sockaddr *) &send_info.to,
send_info.to_len);

if (sent != written) {
perror("failed to send");
return;
Expand Down Expand Up @@ -109,7 +115,13 @@ static void recv_cb(EV_P_ ev_io *w, int revents) {
static uint8_t buf[65535];

while (1) {
ssize_t read = recv(conn_io->sock, buf, sizeof(buf), 0);
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len = sizeof(peer_addr);
memset(&peer_addr, 0, peer_addr_len);

ssize_t read = recvfrom(conn_io->sock, buf, sizeof(buf), 0,
(struct sockaddr *) &peer_addr,
&peer_addr_len);

if (read < 0) {
if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
Expand All @@ -121,7 +133,13 @@ static void recv_cb(EV_P_ ev_io *w, int revents) {
return;
}

ssize_t done = quiche_conn_recv(conn_io->conn, buf, read);
quiche_recv_info recv_info = {
(struct sockaddr *) &peer_addr,

peer_addr_len,
};

ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);

if (done < 0) {
fprintf(stderr, "failed to process packet: %zd\n", done);
Expand Down Expand Up @@ -371,8 +389,9 @@ int main(int argc, char *argv[]) {
return -1;
}

quiche_conn *conn = quiche_connect(host, (const uint8_t *) scid,
sizeof(scid), config);
quiche_conn *conn = quiche_connect(host, (const uint8_t*) scid, sizeof(scid),
peer->ai_addr, peer->ai_addrlen, config);

if (conn == NULL) {
fprintf(stderr, "failed to create connection\n");
return -1;
Expand Down
17 changes: 10 additions & 7 deletions examples/http3-client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ fn main() {
let scid = quiche::ConnectionId::from_ref(&scid);

// Create a QUIC connection and initiate handshake.
let mut conn = quiche::connect(url.domain(), &scid, &mut config).unwrap();
let mut conn =
quiche::connect(url.domain(), &scid, peer_addr, &mut config).unwrap();

info!(
"connecting to {:} from {:} with scid {}",
Expand All @@ -117,9 +118,9 @@ fn main() {
hex_dump(&scid)
);

let write = conn.send(&mut out).expect("initial send failed");
let (write, send_info) = conn.send(&mut out).expect("initial send failed");

while let Err(e) = socket.send(&out[..write]) {
while let Err(e) = socket.send_to(&out[..write], &send_info.to) {
if e.kind() == std::io::ErrorKind::WouldBlock {
debug!("send() would block");
continue;
Expand Down Expand Up @@ -169,7 +170,7 @@ fn main() {
break 'read;
}

let len = match socket.recv(&mut buf) {
let (len, from) = match socket.recv_from(&mut buf) {
Ok(v) => v,

Err(e) => {
Expand All @@ -186,8 +187,10 @@ fn main() {

debug!("got {} bytes", len);

let recv_info = quiche::RecvInfo { from };

// Process potentially coalesced packets.
let read = match conn.recv(&mut buf[..len]) {
let read = match conn.recv(&mut buf[..len], recv_info) {
Ok(v) => v,

Err(e) => {
Expand Down Expand Up @@ -283,7 +286,7 @@ fn main() {
// Generate outgoing QUIC packets and send them on the UDP socket, until
// quiche reports that there are no more packets to be sent.
loop {
let write = match conn.send(&mut out) {
let (write, send_info) = match conn.send(&mut out) {
Ok(v) => v,

Err(quiche::Error::Done) => {
Expand All @@ -299,7 +302,7 @@ fn main() {
},
};

if let Err(e) = socket.send(&out[..write]) {
if let Err(e) = socket.send_to(&out[..write], &send_info.to) {
if e.kind() == std::io::ErrorKind::WouldBlock {
debug!("send() would block");
break;
Expand Down
33 changes: 25 additions & 8 deletions examples/http3-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,11 @@ static void debug_log(const char *line, void *argp) {
static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) {
static uint8_t out[MAX_DATAGRAM_SIZE];

quiche_send_info send_info;

while (1) {
ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out));
ssize_t written = quiche_conn_send(conn_io->conn, out, sizeof(out),
&send_info);

if (written == QUICHE_ERR_DONE) {
fprintf(stderr, "done writing\n");
Expand Down Expand Up @@ -173,7 +176,9 @@ static uint8_t *gen_cid(uint8_t *cid, size_t cid_len) {
}

static struct conn_io *create_conn(uint8_t *scid, size_t scid_len,
uint8_t *odcid, size_t odcid_len) {
uint8_t *odcid, size_t odcid_len,
struct sockaddr_storage *peer_addr,
socklen_t peer_addr_len) {
struct conn_io *conn_io = calloc(1, sizeof(*conn_io));
if (conn_io == NULL) {
fprintf(stderr, "failed to allocate connection IO\n");
Expand All @@ -187,7 +192,11 @@ static struct conn_io *create_conn(uint8_t *scid, size_t scid_len,
memcpy(conn_io->cid, scid, LOCAL_CONN_ID_LEN);

quiche_conn *conn = quiche_accept(conn_io->cid, LOCAL_CONN_ID_LEN,
odcid, odcid_len, config);
odcid, odcid_len,
(struct sockaddr *) peer_addr,
peer_addr_len,
config);

if (conn == NULL) {
fprintf(stderr, "failed to create connection\n");
return NULL;
Expand All @@ -196,6 +205,9 @@ static struct conn_io *create_conn(uint8_t *scid, size_t scid_len,
conn_io->sock = conns->sock;
conn_io->conn = conn;

memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len);
conn_io->peer_addr_len = peer_addr_len;

ev_init(&conn_io->timer, timeout_cb);
conn_io->timer.data = conn_io;

Expand Down Expand Up @@ -334,16 +346,21 @@ static void recv_cb(EV_P_ ev_io *w, int revents) {
continue;
}

conn_io = create_conn(dcid, dcid_len, odcid, odcid_len);
conn_io = create_conn(dcid, dcid_len, odcid, odcid_len,
&peer_addr, peer_addr_len);

if (conn_io == NULL) {
continue;
}

memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len);
conn_io->peer_addr_len = peer_addr_len;
}

ssize_t done = quiche_conn_recv(conn_io->conn, buf, read);
quiche_recv_info recv_info = {
(struct sockaddr *) &peer_addr,

peer_addr_len,
};

ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);

if (done < 0) {
fprintf(stderr, "failed to process packet: %zd\n", done);
Expand Down
Loading

0 comments on commit b426d0c

Please sign in to comment.