Skip to content

Commit 93f5ca5

Browse files
committed
Trace shared-socket QUIC routing
1 parent 38f86bb commit 93f5ca5

9 files changed

Lines changed: 107 additions & 89 deletions

File tree

generate_lsquic_ffi.nim

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,29 @@ from os import parentDir, `/`
77

88
import boringssl
99

10-
proc dropGeneratedCEnumsImpl(opirOutput: JsonNode): JsonNode =
10+
const preludeTypes = ["struct_lsquic_cid", "lsquic_cid_t"]
11+
12+
proc dropPreludeTypesAndGeneratedCEnumsImpl(opirOutput: JsonNode): JsonNode =
1113
var resp = newJArray()
1214
for node in opirOutput:
1315
# enums are generated manually to avoid issue described in
1416
# https://github.com/PMunch/futhark/issues/152
1517
if node{"kind"}.getStr("") == "enum":
1618
continue
19+
20+
# Futhark incorrectly maps the struct alignment on lsquic_cid_t to field alignment,
21+
# so the prelude supplies the correct native layout instead.
22+
if node{"name"}.getStr("") in preludeTypes:
23+
continue
24+
1725
resp.add node
1826
resp
1927

2028
importc:
2129
outputPath currentSourcePath.parentDir / "tmp_lsquic_ffi.nim"
2230
path currentSourcePath.parentDir / "libs/lsquic/include"
2331
addopircallback proc(opirOutput: JsonNode): JsonNode {.closure.} =
24-
dropGeneratedCEnumsImpl(opirOutput)
32+
dropPreludeTypesAndGeneratedCEnumsImpl(opirOutput)
2533
rename FILE, CFile # Rename `FILE` that STB uses to `CFile` which is the Nim equivalent
2634
rename struct_sockaddr, SockAddr # Rename `struct_sockaddr` for chronos SockAddr
2735
"lsquic.h"

lsquic/context/client.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ method dial*(
107107
if conn.isNil:
108108
GC_unref(quicClientConn)
109109
return err("could not dial: " & $remote)
110-
ctx.trackConnectionCid(conn)
111110

112111
quicClientConn.lsquicConn = conn
112+
ctx.trackConnectionCid(conn)
113113
ctx.processWhenReady()
114114

115115
ok(quicClientConn)
@@ -120,9 +120,9 @@ const Adaptive = 3
120120
proc new*(T: typedesc[ClientContext], tlsConfig: TLSConfig): Result[T, string] =
121121
var ctx = ClientContext()
122122
ctx.tlsConfig = tlsConfig
123-
ctx.initCidTracking()
124123
ctx.running = true
125124
ctx.setupSSLContext()
125+
ctx.initCidTracking()
126126

127127
lsquic_engine_init_settings(addr ctx.settings, 0)
128128
ctx.settings.es_versions = 1.cuint shl LSQVER_I001.cuint #IETF QUIC v1

lsquic/context/context.nim

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: Apache-2.0 OR MIT
22
# Copyright (c) Status Research & Development GmbH
33

4-
import std/[deques, hashes, sets]
4+
import std/[deques, hashes, sets, strutils]
55
import boringssl
66
import chronos
77
import chronos/osdefs
@@ -17,14 +17,7 @@ type
1717
len*: uint8
1818
bytes*: array[MAX_CID_LEN, uint8]
1919

20-
# The generated FFI type currently aligns fields, not just the C struct.
21-
# Read CIDs through the native wire layout until the binding is regenerated.
22-
RawLsquicCid = object
23-
buf: array[MAX_CID_LEN, uint8]
24-
len: uint8
25-
padding: array[3, uint8]
26-
27-
RawLsquicCidArray = UncheckedArray[RawLsquicCid]
20+
LsquicCidArray = UncheckedArray[lsquic_cid_t]
2821

2922
QuicContext* = ref object of RootObj
3023
settings*: struct_lsquic_engine_settings
@@ -39,17 +32,22 @@ type
3932
running*: bool
4033
ownedCids*: HashSet[CidKey]
4134

42-
static:
43-
doAssert sizeof(RawLsquicCid) == 24
44-
doAssert offsetOf(RawLsquicCid, len) == 20
45-
4635
func hash*(cid: CidKey): Hash =
4736
var h = hash(cid.len)
4837
for i in 0 ..< cid.len.int:
4938
h = h !& hash(cid.bytes[i])
5039
!$h
5140

52-
func toCidKey(cid: RawLsquicCid, key: var CidKey): bool =
41+
func shortLog*(cid: CidKey): string =
42+
var ret = $cid.len & ":"
43+
for i in 0 ..< min(cid.len.int, 8):
44+
ret.add(toHex(cid.bytes[i], 2))
45+
ret
46+
47+
chronicles.formatIt(CidKey):
48+
shortLog(it)
49+
50+
func toCidKey(cid: lsquic_cid_t, key: var CidKey): bool =
5351
if cid.len == 0 or cid.len.int > MAX_CID_LEN:
5452
return false
5553

@@ -68,11 +66,12 @@ proc addCids*(
6866
if quicCtx.isNil or cids.isNil:
6967
return
7068

71-
let cidsArr = cast[ptr RawLsquicCidArray](cids)
69+
let cidsArr = cast[ptr LsquicCidArray](cids)
7270
for i in 0 ..< nCids.int:
7371
var key: CidKey
7472
if toCidKey(cidsArr[i], key):
7573
quicCtx.ownedCids.incl(key)
74+
trace "Registered CID", cid = key, cidCount = quicCtx.ownedCids.len
7675

7776
proc removeCids*(
7877
ctx: pointer, peerCtxs: ptr pointer, cids: ptr lsquic_cid_t, nCids: cuint
@@ -81,15 +80,14 @@ proc removeCids*(
8180
if quicCtx.isNil or cids.isNil:
8281
return
8382

84-
let cidsArr = cast[ptr RawLsquicCidArray](cids)
83+
let cidsArr = cast[ptr LsquicCidArray](cids)
8584
for i in 0 ..< nCids.int:
8685
var key: CidKey
8786
if toCidKey(cidsArr[i], key):
8887
quicCtx.ownedCids.excl(key)
88+
trace "Removed CID", cid = key, cidCount = quicCtx.ownedCids.len
8989

90-
proc trackConnectionCid*(
91-
ctx: QuicContext, conn: ptr lsquic_conn_t
92-
) {.raises: [].} =
90+
proc trackConnectionCid*(ctx: QuicContext, conn: ptr lsquic_conn_t) {.raises: [].} =
9391
if ctx.isNil or conn.isNil:
9492
return
9593

@@ -98,8 +96,9 @@ proc trackConnectionCid*(
9896
return
9997

10098
var key: CidKey
101-
if toCidKey(cast[ptr RawLsquicCid](cid)[], key):
99+
if toCidKey(cid[], key):
102100
ctx.ownedCids.incl(key)
101+
trace "Tracked connection CID", cid = key, cidCount = ctx.ownedCids.len
103102

104103
proc ownsCid*(ctx: QuicContext, cid: CidKey): bool {.raises: [].} =
105104
not ctx.isNil and cid in ctx.ownedCids

lsquic/context/io.nim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import chronos
55
import chronos/osdefs
6+
import chronicles
67
import ./context
78
import ../[lsquic_ffi, datagram]
89
import ../helpers/[openarray, sequninit, transportaddr]
@@ -144,6 +145,7 @@ proc sendPacketsOut*(
144145

145146
let res = sendmsg(SocketHandle(quicCtx.fd), msg.addr, 0)
146147
if res < 0:
148+
trace "sendmsg failed", sent, nspecs
147149
break
148150

149151
sent.inc

lsquic/context/server.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ proc new*(T: typedesc[ServerContext], tlsConfig: TLSConfig): Result[T, string] =
5757
var ctx = ServerContext()
5858
ctx.tlsConfig = tlsConfig
5959
ctx.running = true
60-
ctx.initCidTracking()
6160
ctx.incoming = newAsyncQueue[QuicConnection]()
6261
ctx.setupSSLContext()
62+
ctx.initCidTracking()
6363

6464
lsquic_engine_init_settings(addr ctx.settings, LSENG_SERVER)
6565
ctx.settings.es_versions = 1.cuint shl LSQVER_I001.cuint #IETF QUIC v1

lsquic/endpoint.nim

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ proc createClientContext(
3939
context.fd = fd
4040
context
4141

42-
proc serverCidLen(endpoint: QuicEndpoint): cuint {.raises: [].} =
43-
if not endpoint.serverContext.isNil and endpoint.serverContext.settings.es_scid_len != 0:
42+
proc scidLen(endpoint: QuicEndpoint): cuint {.raises: [].} =
43+
if not endpoint.serverContext.isNil and
44+
endpoint.serverContext.settings.es_scid_len != 0:
4445
return endpoint.serverContext.settings.es_scid_len
45-
if not endpoint.clientContext.isNil and endpoint.clientContext.settings.es_scid_len != 0:
46+
if not endpoint.clientContext.isNil and
47+
endpoint.clientContext.settings.es_scid_len != 0:
4648
return endpoint.clientContext.settings.es_scid_len
4749
LSQUIC_DF_SCID_LEN.cuint
4850

@@ -54,10 +56,7 @@ proc packetDcid(
5456

5557
var cidLen: uint8
5658
let offset = lsquic_dcid_from_packet(
57-
unsafeAddr packet[0],
58-
packet.len.csize_t,
59-
endpoint.serverCidLen(),
60-
addr cidLen,
59+
unsafeAddr packet[0], packet.len.csize_t, endpoint.scidLen(), addr cidLen
6160
)
6261
if offset < 0:
6362
return false
@@ -71,28 +70,48 @@ proc packetDcid(
7170
cid.bytes[i] = packet[start + i]
7271
true
7372

73+
func isIetfInitial(packet: seq[byte]): bool {.raises: [].} =
74+
if packet.len == 0:
75+
return false
76+
(packet[0] and 0xC0'u8) == 0xC0'u8 and (packet[0] and 0x30'u8) == 0
77+
7478
proc receiveDatagram(
7579
endpoint: QuicEndpoint, data: seq[byte], local, remote: TransportAddress
7680
) {.raises: [].} =
7781
if endpoint.isNil or endpoint.stopped:
7882
return
7983

84+
let
85+
hasClientContext = not endpoint.clientContext.isNil
86+
hasServerContext = not endpoint.serverContext.isNil
87+
8088
var cid: CidKey
8189
if endpoint.packetDcid(data, cid):
82-
if not endpoint.clientContext.isNil and endpoint.clientContext.ownsCid(cid):
90+
if hasClientContext and endpoint.clientContext.ownsCid(cid):
91+
trace "Routing datagram to client context", cid
8392
endpoint.clientContext.receive(Datagram(data: data), local, remote)
8493
return
8594

86-
if not endpoint.serverContext.isNil and endpoint.serverContext.ownsCid(cid):
95+
if hasServerContext and endpoint.serverContext.ownsCid(cid):
96+
trace "Routing datagram to server context", cid
8797
endpoint.serverContext.receive(Datagram(data: data), local, remote)
8898
return
8999

90-
# Unknown CIDs can be new handshakes; each engine gets a chance to claim them.
91-
# Endpoints can have both contexts; each engine needs to see the packet.
92-
if not endpoint.clientContext.isNil:
100+
if hasClientContext and not hasServerContext:
93101
endpoint.clientContext.receive(Datagram(data: data), local, remote)
94-
if not endpoint.serverContext.isNil:
102+
return
103+
104+
if hasServerContext and not hasClientContext:
105+
endpoint.serverContext.receive(Datagram(data: data), local, remote)
106+
return
107+
108+
if hasServerContext and data.isIetfInitial():
109+
trace "Routing initial datagram with unknown CID to server context",
110+
bytes = data.len, local, remote
95111
endpoint.serverContext.receive(Datagram(data: data), local, remote)
112+
return
113+
114+
trace "Dropping datagram with unknown CID", bytes = data.len, local, remote
96115

97116
proc receiveFromUdp(
98117
endpoint: QuicEndpoint, udp: DatagramTransport, remote: TransportAddress

0 commit comments

Comments
 (0)