The lane byte was described in prose wedged between the first and second
rows of the table, which both broke up the diagram and left the wire layout
still claiming a single Reserved (uint16). Make it a field: widen the table
to fit Reserved (uint8) and Lane (uint8) with their types, and move the
explanation below the whole diagram.
The prose now covers what the picture can't: lane 0 is what a lanes-unaware
sender and every non-lane packet carry, so the field is compatible in both
directions; the remaining 8 bits stay reserved and zero; the H struct still
stores the pair as one Reserved field, so callers reach the low byte through
H.Lane and EncodeLane; and lane is part of the AEAD's associated data, so it
cannot be altered in flight.
Lanes used to be separate HostInfos, each with its own handshake, its own
half-established states, its own lifetime and its own slot bookkeeping. That
bought nothing: a lane is the same tunnel over a different underlay 5-tuple.
Derive lane sessions instead. Noise leaves us with A.eKey == B.dKey, so both
sides HKDF-expand the same two keys with the same per-lane label and land on a
matched pair without exchanging anything. Lanes now cost no handshake, have no
half-established state, and die exactly when their base tunnel does. Which lane
a packet belongs to rides the low byte of the nebula header's Reserved field,
inside the AEAD's associated data.
Receiving on a lane needs no permission, since the session exists the moment the
base handshake completes. Sending on one needs proof the new 5-tuple works, so a
lane stays down until a probe on it is acked and falls back to the base tunnel
the moment it stops being acked. Probing is demand-driven off the connection
manager's per-tunnel traffic tick: a peer we exchange a trickle with never costs
more than its base tunnel, however many lanes are configured. The ack rides the
base session on purpose, so a broken reverse lane can't fail a working one.
Because the data now rides lane counters, the rehandshake, exhaustion and
swap-primary checks take the max counter across the base session and its lanes;
otherwise the base counter would sit near zero while a lane ran its keys past
the nonce ceiling.
Removes OutboundLaneTimer, EnsureLanes, startLaneHandshake, completeLane,
completeLaneResponder, makeLaneTrafficDecision and the lane fields on HostInfo.
Handshake payload field 3 (the per-lane handshake index) is permanently
reserved; peers advertise a TxLanes count instead.
* refactor readOutsidePackets
They layout of this method is confusing and relys on certain parts to
return early for things to work correctly.
Change the ordering of the logic so that we do this:
- Handle unencrypted packets
- Decrypt packet
- Handle encrypted packets
This way, nothing can sneak through unencrypted to where it shouldn't
be.
* fix comment
* code review comments
* check for expected type/subtype
* check header version
* log header
* need to handle TestReply
* clean roaming / connectionManager
* dont need to roam here now, we do it earlier
* cleanup metrics and errors
* rxInvalid
* debug logger checks
* ErrOutOfWindow
* upgrade to yaml.v3
The main nice fix here is that maps unmarshal into `map[string]any`
instead of `map[any]any`, so it cleans things up a bit.
* add config.AsBool
Since yaml.v3 doesn't automatically convert yes to bool now, for
backwards compat
* use type aliases for m
* more cleanup
* more cleanup
* more cleanup
* go mod cleanup