Skip to content

Commit 31af5f7

Browse files
authored
Merge branch 'main' into fix-send-buffer
2 parents 0234c4a + 7a3f33e commit 31af5f7

File tree

13 files changed

+263
-113
lines changed

13 files changed

+263
-113
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
merge_group:
77
push:
88
branches:
9-
- main-iroh
9+
- main
1010

1111
concurrency:
1212
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}

.github/workflows/codecov.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Coverage
33
on:
44
pull_request:
55
push:
6-
branches: ['main-iroh']
6+
branches: ['main']
77

88
jobs:
99
coverage:
@@ -37,4 +37,4 @@ jobs:
3737
with:
3838
token: ${{ secrets.CODECOV_TOKEN }}
3939
files: lcov.info
40-
fail_ci_if_error: false
40+
fail_ci_if_error: false

.github/workflows/tests.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,26 @@ jobs:
254254
- name: Install cmake
255255
uses: lukka/get-cmake@latest
256256

257+
# Install LLVM for libclang (required by bindgen for aws-lc-fips-sys)
258+
- name: Cache LLVM
259+
id: cache-llvm
260+
uses: actions/cache@v4
261+
with:
262+
path: C:\Program Files\LLVM
263+
key: llvm-17.0.6-windows
264+
265+
- name: Install LLVM
266+
if: steps.cache-llvm.outputs.cache-hit != 'true'
267+
run: |
268+
$llvmUrl = "https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.6/LLVM-17.0.6-win64.exe"
269+
Invoke-WebRequest -Uri $llvmUrl -OutFile llvm-installer.exe
270+
Start-Process -FilePath .\llvm-installer.exe -ArgumentList "/S" -Wait
271+
272+
- name: Add LLVM to PATH
273+
run: |
274+
echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
275+
echo "LIBCLANG_PATH=C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
276+
257277
- name: build tests
258278
run: |
259279
cargo nextest run --workspace --exclude fuzz ${{ env.FEATURES }} --lib --bins --tests --target ${{ matrix.target }} --no-run

CONTRIBUTING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Contributing
2+
3+
4+
## Git Branches
5+
6+
- `main`: development
7+
- `main-quinn`: tracks upstream quinn

LICENSE-APACHE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright [yyyy] [name of copyright owner]
189+
Copyright [2025] [The quinn developers]
190+
Copyright [2025] [N0, Inc]
190191

191192
Licensed under the Apache License, Version 2.0 (the "License");
192193
you may not use this file except in compliance with the License.

LICENSE-MIT

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
Copyright (c) 2018 The quinn Developers
1+
Copyright (c) 2018-2025 The quinn Developers
2+
Copyright (c) 2025 N0, Inc.
23

34
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
45

README.md

Lines changed: 25 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
# Quinn fork of iroh
1+
# Iroh Quinn
2+
3+
> This is a fork based on [quinn](https://github.com/quinn-rs/quinn), maintained
4+
> by [n0](https://github.com/n0-computer). Currently published to crates.io under
5+
> `iroh-quinn`.
6+
7+
## Main differences to upstream quinn
8+
9+
- Small API improvements
10+
- Implements additional QUIC extensions
11+
- Multipath
12+
- QAD
13+
- QNT
14+
- Expanded support for QLOG
215

316
Quinn is a pure-rust, async-compatible implementation of the IETF
417
[QUIC][quic] transport protocol.
@@ -12,55 +25,21 @@ Quinn is a pure-rust, async-compatible implementation of the IETF
1225
- Future-based async API
1326
- Minimum supported Rust version of 1.83.0
1427

15-
This is a fork incorporating some changes for use in iroh. The aim is
16-
to contribute back any generally useful changes into upstream Quinn,
17-
so it is strongly discouraged to use this fork directly.
18-
19-
20-
## Git branches
21-
22-
The upstream branches are kept unmodified and get occasionally synced
23-
(e.g. our `main` branch tracks `upstream/main` with a small delay).
24-
The iroh-specific branches are:
25-
26-
- `iroh-0.10.x` is the branch for [email protected] series.
27-
- `iroh-0.11.x` is the branch for [email protected] series.
28-
29-
The default branch should be set the currently actively used branch by
30-
iroh.
31-
32-
### Updating a branch
33-
34-
To update a branch to include the upstream changes, merge the upstream
35-
branch. E.g. when upstream is `main` and the current iroh branch is
36-
`iroh-0.11.x`:
37-
38-
- Check which commits are new in main.
39-
40-
Using *magit*: `magit-cherry` (Y), from `main` to `iroh-0.11.x`
41-
42-
- Find the commit to merge.
43-
44-
You probably want to find the last released commit on the `main`
45-
branch, which might not be the last commit on main. So you need to
46-
find the commit hash as you can't use "main" in this case.
4728

48-
- Merge this commit: `git merge abc123`
29+
## License
4930

50-
- You can check the log and cherries again to see if the right commits
51-
are left in main.
31+
Copyright 2025 The quinn developers
32+
Copyright 2025 N0, INC.
5233

53-
### Upstream versions
34+
This project is licensed under either of
5435

55-
Usually we only try to merge tagged upstream versions. Currently (as
56-
of the 0.13 iroh-quinn release) we've released work that hasn't been
57-
released upstream yet.
36+
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
37+
http://www.apache.org/licenses/LICENSE-2.0)
38+
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
39+
http://opensource.org/licenses/MIT)
5840

59-
In the normal case, you'd be able to check the current matching
60-
upstream version by running:
41+
at your option.
6142

62-
`git tag --merged`
43+
## Contribution
6344

64-
This shows all the tags which are in the ancestors of HEAD. Look for
65-
the highest `quinn`, `quinn-proto` and `quinn-udp` tags which are
66-
found in all the ancestor commits.
45+
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

quinn-proto/src/connection/mod.rs

Lines changed: 43 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -821,31 +821,23 @@ impl Connection {
821821
&mut self.paths.get_mut(&path_id).expect("known path").data
822822
}
823823

824-
/// Check if the remote has been validated in any active path
825-
fn is_remote_validated(&self, remote: SocketAddr) -> bool {
826-
self.paths
827-
.values()
828-
.any(|path_state| path_state.data.remote == remote && path_state.data.validated)
829-
// TODO(@divma): we might want to ensure the path has been recently active to consider the
830-
// address validated
831-
}
832-
833824
fn ensure_path(
834825
&mut self,
835826
path_id: PathId,
836827
remote: SocketAddr,
837828
now: Instant,
838829
pn: Option<u64>,
839830
) -> &mut PathData {
840-
let validated = self.is_remote_validated(remote);
841831
let vacant_entry = match self.paths.entry(path_id) {
842832
btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
843833
btree_map::Entry::Occupied(occupied_entry) => {
844834
return &mut occupied_entry.into_mut().data;
845835
}
846836
};
847837

848-
debug!(%validated, %path_id, ?remote, "path added");
838+
// TODO(matheus23): Add back short-circuiting path.validated = true, if we know that the
839+
// path's four-tuple was already validated.
840+
debug!(%path_id, ?remote, "path added");
849841
let peer_max_udp_payload_size =
850842
u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
851843
self.path_counter = self.path_counter.wrapping_add(1);
@@ -858,8 +850,6 @@ impl Connection {
858850
&self.config,
859851
);
860852

861-
data.validated = validated;
862-
863853
let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base();
864854
self.timers.set(
865855
Timer::PerPath(path_id, PathTimer::PathOpen),
@@ -4288,44 +4278,30 @@ impl Connection {
42884278
.get_mut(&path_id)
42894279
.expect("payload is processed only after the path becomes known");
42904280

4291-
match path.data.challenges_sent.get(&response.0) {
4292-
// Response to an on-path PathChallenge
4293-
Some(info) if info.remote == remote && path.data.remote == remote => {
4294-
let sent_instant = info.sent_instant;
4295-
// TODO(@divma): reset timers using the remaining off-path challenges
4296-
self.timers.stop(
4297-
Timer::PerPath(path_id, PathTimer::PathValidation),
4298-
self.qlog.with_time(now),
4299-
);
4300-
self.timers.stop(
4301-
Timer::PerPath(path_id, PathTimer::PathChallengeLost),
4302-
self.qlog.with_time(now),
4281+
use PathTimer::*;
4282+
use paths::OnPathResponseReceived::*;
4283+
match path.data.on_path_response_received(now, response.0, remote) {
4284+
OnPath { was_open } => {
4285+
let qlog = self.qlog.with_time(now);
4286+
4287+
self.timers
4288+
.stop(Timer::PerPath(path_id, PathValidation), qlog.clone());
4289+
self.timers
4290+
.stop(Timer::PerPath(path_id, PathOpen), qlog.clone());
4291+
4292+
let next_challenge = path
4293+
.data
4294+
.earliest_expiring_challenge()
4295+
.map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
4296+
self.timers.set_or_stop(
4297+
Timer::PerPath(path_id, PathChallengeLost),
4298+
next_challenge,
4299+
qlog,
43034300
);
4304-
if !path.data.validated {
4305-
trace!("new path validated");
4306-
}
4307-
self.timers.stop(
4308-
Timer::PerPath(path_id, PathTimer::PathOpen),
4309-
self.qlog.with_time(now),
4310-
);
4311-
// Clear any other on-path sent challenge.
4312-
path.data
4313-
.challenges_sent
4314-
.retain(|_token, info| info.remote != remote);
4315-
path.data.send_new_challenge = false;
4316-
path.data.validated = true;
4317-
4318-
// This RTT can only be used for the initial RTT, not as a normal
4319-
// sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2.
4320-
let rtt = now.saturating_duration_since(sent_instant);
4321-
path.data.rtt.reset_initial_rtt(rtt);
4322-
4323-
self.events
4324-
.push_back(Event::Path(PathEvent::Opened { id: path_id }));
4325-
// mark the path as open from the application perspective now that Opened
4326-
// event has been queued
4327-
if !std::mem::replace(&mut path.data.open, true) {
4328-
trace!("path opened");
4301+
4302+
if !was_open {
4303+
self.events
4304+
.push_back(Event::Path(PathEvent::Opened { id: path_id }));
43294305
if let Some(observed) = path.data.last_observed_addr_report.as_ref()
43304306
{
43314307
self.events.push_back(Event::Path(PathEvent::ObservedAddr {
@@ -4339,19 +4315,22 @@ impl Connection {
43394315
prev.send_new_challenge = false;
43404316
}
43414317
}
4342-
// Response to an off-path PathChallenge
4343-
Some(info) if info.remote == remote => {
4318+
OffPath => {
43444319
debug!("Response to off-path PathChallenge!");
4345-
path.data
4346-
.challenges_sent
4347-
.retain(|_token, info| info.remote != remote);
4320+
let next_challenge = path
4321+
.data
4322+
.earliest_expiring_challenge()
4323+
.map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
4324+
self.timers.set_or_stop(
4325+
Timer::PerPath(path_id, PathChallengeLost),
4326+
next_challenge,
4327+
self.qlog.with_time(now),
4328+
);
43484329
}
4349-
// Response to a PathChallenge we recognize, but from an invalid remote
4350-
Some(info) => {
4351-
debug!(%response, from=%remote, expected=%info.remote, "ignoring invalid PATH_RESPONSE")
4330+
Invalid { expected } => {
4331+
debug!(%response, from=%remote, %expected, "ignoring invalid PATH_RESPONSE")
43524332
}
4353-
// Response to an unknown PathChallenge
4354-
None => debug!(%response, "ignoring invalid PATH_RESPONSE"),
4333+
Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"),
43554334
}
43564335
}
43574336
Frame::MaxData(bytes) => {
@@ -6273,6 +6252,10 @@ impl Connection {
62736252
&mut self,
62746253
now: Instant,
62756254
) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
6255+
if self.state.is_closed() {
6256+
return Err(iroh_hp::Error::Closed);
6257+
}
6258+
62766259
let client_state = self.iroh_hp.client_side_mut()?;
62776260
let iroh_hp::NatTraversalRound {
62786261
new_round,

0 commit comments

Comments
 (0)