Skip to content

Commit

Permalink
Add ffi / c functions to be able to send packets to a specific path
Browse files Browse the repository at this point in the history
Motivation:

Quiche provides mutliple methods that allows to send packets on a specific path. These were not exposed in the C api.

Modifications:

- Add missing functions to the c API

Result:

Be able to send packets on a specific path

Co-authored-by: Alessandro Ghedini <alessandro@ghedini.me>
  • Loading branch information
normanmaurer and ghedo committed Nov 27, 2023
1 parent 5796adc commit bdee221
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
30 changes: 30 additions & 0 deletions quiche/include/quiche.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,19 @@ ssize_t quiche_conn_send(quiche_conn *conn, uint8_t *out, size_t out_len,
// Returns the size of the send quantum, in bytes.
size_t quiche_conn_send_quantum(const quiche_conn *conn);

// Writes a single QUIC packet to be sent to the peer from the specified
// local address "from" to the destination address "to".
ssize_t quiche_conn_send_on_path(quiche_conn *conn, uint8_t *out, size_t out_len,
const struct sockaddr *from, socklen_t from_len,
const struct sockaddr *to, socklen_t to_len,
quiche_send_info *out_info);

// Returns the size of the send quantum over the given 4-tuple, in bytes.
size_t quiche_conn_send_quantum_on_path(const quiche_conn *conn,
const struct sockaddr *local_addr, socklen_t local_len,
const struct sockaddr *peer_addr, socklen_t peer_len);


// Reads contiguous data from a stream.
ssize_t quiche_conn_stream_recv(quiche_conn *conn, uint64_t stream_id,
uint8_t *out, size_t buf_len, bool *fin);
Expand Down Expand Up @@ -766,6 +779,23 @@ void quiche_path_event_free(quiche_path_event *ev);
// host to reach its peer.
int quiche_conn_retire_dcid(quiche_conn *conn, uint64_t dcid_seq);

typedef struct quiche_socket_addr_iter quiche_socket_addr_iter;

// Returns an iterator over destination `SockAddr`s whose association
// with "from" forms a known QUIC path on which packets can be sent to.
quiche_socket_addr_iter *quiche_conn_paths_iter(quiche_conn *conn, const struct sockaddr *from, size_t from_len);

// Fetches the next peer from the given iterator. Returns false if there are
// no more elements in the iterator.
bool quiche_socket_addr_iter_next(quiche_socket_addr_iter *iter, struct sockaddr_storage *peer, size_t *peer_len);

// Frees the given path iterator object.
void quiche_socket_addr_iter_free(quiche_socket_addr_iter *iter);

// Returns whether the network path with local address "from and remote address "to" has been validated.
// If the 4-tuple does not exist over the connection, returns an InvalidState.
int quiche_conn_is_path_validated(const quiche_conn *conn, const struct sockaddr *from, size_t from_len, const struct sockaddr *to, size_t to_len);

// Frees the connection object.
void quiche_conn_free(quiche_conn *conn);

Expand Down
92 changes: 92 additions & 0 deletions quiche/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,34 @@ pub extern fn quiche_conn_send(
}
}

#[no_mangle]
pub extern fn quiche_conn_send_on_path(
conn: &mut Connection, out: *mut u8, out_len: size_t, from: *const sockaddr,
from_len: socklen_t, to: *const sockaddr, to_len: socklen_t,
out_info: &mut SendInfo,
) -> ssize_t {
if out_len > <ssize_t>::max_value() as usize {
panic!("The provided buffer is too large");
}

let from = optional_std_addr_from_c(from, from_len);
let to = optional_std_addr_from_c(to, to_len);
let out = unsafe { slice::from_raw_parts_mut(out, out_len) };

match conn.send_on_path(out, from, to) {
Ok((v, info)) => {
out_info.from_len = std_addr_to_c(&info.from, &mut out_info.from);
out_info.to_len = std_addr_to_c(&info.to, &mut out_info.to);

std_time_to_c(&info.at, &mut out_info.at);

v as ssize_t
},

Err(e) => e.to_c(),
}
}

#[no_mangle]
pub extern fn quiche_conn_stream_recv(
conn: &mut Connection, stream_id: u64, out: *mut u8, out_len: size_t,
Expand Down Expand Up @@ -1465,6 +1493,57 @@ pub extern fn quiche_conn_retired_scid_next(
}
}

#[no_mangle]
pub extern fn quiche_conn_send_quantum_on_path(
conn: &Connection, local: &sockaddr, local_len: socklen_t, peer: &sockaddr,
peer_len: socklen_t,
) -> size_t {
let local = std_addr_from_c(local, local_len);
let peer = std_addr_from_c(peer, peer_len);

conn.send_quantum_on_path(local, peer) as size_t
}

#[no_mangle]
pub extern fn quiche_conn_paths_iter(
conn: &Connection, from: &sockaddr, from_len: socklen_t,
) -> *mut SocketAddrIter {
let addr = std_addr_from_c(from, from_len);

Box::into_raw(Box::new(conn.paths_iter(addr)))
}

#[no_mangle]
pub extern fn quiche_socket_addr_iter_next(
iter: &mut SocketAddrIter, peer: &mut sockaddr_storage,
peer_len: *mut socklen_t,
) -> bool {
if let Some(v) = iter.next() {
unsafe { *peer_len = std_addr_to_c(&v, peer) }
return true;
}

false
}

#[no_mangle]
pub extern fn quiche_socket_addr_iter_free(iter: *mut SocketAddrIter) {
drop(unsafe { Box::from_raw(iter) });
}

#[no_mangle]
pub extern fn quiche_conn_is_path_validated(
conn: &Connection, from: &sockaddr, from_len: socklen_t, to: &sockaddr,
to_len: socklen_t,
) -> c_int {
let from = std_addr_from_c(from, from_len);
let to = std_addr_from_c(to, to_len);
match conn.is_path_validated(from, to) {
Ok(v) => v as c_int,
Err(e) => e.to_c() as c_int,
}
}

#[no_mangle]
pub extern fn quiche_conn_path_event_next(
conn: &mut Connection,
Expand Down Expand Up @@ -1635,6 +1714,19 @@ pub extern fn quiche_get_varint(
b.off() as ssize_t
}

fn optional_std_addr_from_c(
addr: *const sockaddr, addr_len: socklen_t,
) -> Option<SocketAddr> {
if addr.is_null() || addr_len <= 0 {
return None;
}

Some({
let addr = unsafe { slice::from_raw_parts(addr, addr_len as usize) };
std_addr_from_c(addr.first().unwrap(), addr_len)
})
}

fn std_addr_from_c(addr: &sockaddr, addr_len: socklen_t) -> SocketAddr {
match addr.sa_family as i32 {
AF_INET => {
Expand Down

0 comments on commit bdee221

Please sign in to comment.