comparison src/event/quic/ngx_event_quic_socket.c @ 8423:4117aa7fa38e quic

QUIC: connection migration. The patch adds proper transitions between multiple networking addresses that can be used by a single quic connection. New networking paths are validated using PATH_CHALLENGE/PATH_RESPONSE frames.
author Vladimir Homutov <vl@nginx.com>
date Thu, 29 Apr 2021 15:35:02 +0300
parents
children 6d1488b62dc5
comparison
equal deleted inserted replaced
8422:12f18e0bca09 8423:4117aa7fa38e
1
2 /*
3 * Copyright (C) Nginx, Inc.
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_event.h>
10 #include <ngx_event_quic_connection.h>
11
12
13 static ngx_int_t ngx_quic_create_temp_socket(ngx_connection_t *c,
14 ngx_quic_connection_t *qc, ngx_str_t *dcid, ngx_quic_path_t *path,
15 ngx_quic_client_id_t *cid);
16
17 static void ngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path);
18
19
20 ngx_int_t
21 ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
22 ngx_quic_header_t *pkt)
23 {
24 ngx_quic_path_t *path;
25 ngx_quic_socket_t *qsock;
26 ngx_quic_client_id_t *cid;
27
28 /*
29 * qc->nclient_ids = 0
30 * qc->nsockets = 0
31 * qc->max_retired_seqnum = 0
32 * qc->client_seqnum = 0
33 */
34
35 ngx_queue_init(&qc->sockets);
36 ngx_queue_init(&qc->free_sockets);
37
38 ngx_queue_init(&qc->paths);
39 ngx_queue_init(&qc->free_paths);
40
41 ngx_queue_init(&qc->client_ids);
42 ngx_queue_init(&qc->free_client_ids);
43
44 qc->tp.original_dcid.len = pkt->odcid.len;
45 qc->tp.original_dcid.data = ngx_pstrdup(c->pool, &pkt->odcid);
46 if (qc->tp.original_dcid.data == NULL) {
47 return NGX_ERROR;
48 }
49
50 /* socket to use for further processing */
51 qsock = ngx_quic_alloc_socket(c, qc);
52 if (qsock == NULL) {
53 return NGX_ERROR;
54 }
55
56 /* socket is listening at new server id (autogenerated) */
57 if (ngx_quic_listen(c, qc, qsock) != NGX_OK) {
58 return NGX_ERROR;
59 }
60
61 qc->tp.initial_scid.len = qsock->sid.len;
62 qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len);
63 if (qc->tp.initial_scid.data == NULL) {
64 goto failed;
65 }
66 ngx_memcpy(qc->tp.initial_scid.data, qsock->sid.id, qsock->sid.len);
67
68 /* for all packets except first, this is set at udp layer */
69 c->udp = &qsock->udp;
70
71 /* ngx_quic_get_connection(c) macro is now usable */
72
73 /* we have a client identified by scid */
74 cid = ngx_quic_create_client_id(c, &pkt->scid, 0, NULL);
75 if (cid == NULL) {
76 goto failed;
77 }
78
79 /* the client arrived from this path */
80 path = ngx_quic_add_path(c, c->sockaddr, c->socklen);
81 if (path == NULL) {
82 goto failed;
83 }
84
85 if (pkt->validated) {
86 path->state = NGX_QUIC_PATH_VALIDATED;
87 path->validated_at = ngx_time();
88 }
89
90 /* now bind socket to client and path */
91 ngx_quic_connect(c, qsock, path, cid);
92
93 if (ngx_quic_create_temp_socket(c, qc, &pkt->odcid, path, cid) != NGX_OK) {
94 goto failed;
95 }
96
97 /* use this socket as default destination */
98 qc->socket = qsock;
99
100 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
101 "quic active socket is #%uL:%uL:%uL (%s)",
102 qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum,
103 ngx_quic_path_state_str(qsock->path));
104
105 return NGX_OK;
106
107 failed:
108
109 ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);
110 c->udp = NULL;
111
112 return NGX_ERROR;
113 }
114
115
116 static ngx_int_t
117 ngx_quic_create_temp_socket(ngx_connection_t *c, ngx_quic_connection_t *qc,
118 ngx_str_t *dcid, ngx_quic_path_t *path, ngx_quic_client_id_t *cid)
119 {
120 ngx_str_t id;
121 ngx_quic_socket_t *qsock;
122 ngx_quic_server_id_t *sid;
123
124 qsock = ngx_quic_alloc_socket(c, qc);
125 if (qsock == NULL) {
126 return NGX_ERROR;
127 }
128
129 sid = &qsock->sid;
130
131 sid->seqnum = NGX_QUIC_UNSET_PN; /* mark socket as temporary */
132
133 sid->len = dcid->len;
134 ngx_memcpy(sid->id, dcid->data, dcid->len);
135
136 id.len = sid->len;
137 id.data = sid->id;
138
139 ngx_insert_udp_connection(c, &qsock->udp, &id);
140
141 ngx_queue_insert_tail(&qc->sockets, &qsock->queue);
142
143 qc->nsockets++;
144 qsock->quic = qc;
145
146 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
147 "quic socket #%L listening at sid:%xV nsock:%ui",
148 (int64_t) sid->seqnum, &id, qc->nsockets);
149
150 ngx_quic_connect(c, qsock, path, cid);
151
152 return NGX_OK;
153 }
154
155
156 ngx_quic_socket_t *
157 ngx_quic_alloc_socket(ngx_connection_t *c, ngx_quic_connection_t *qc)
158 {
159 ngx_queue_t *q;
160 ngx_quic_socket_t *sock;
161
162 if (!ngx_queue_empty(&qc->free_sockets)) {
163
164 q = ngx_queue_head(&qc->free_sockets);
165 sock = ngx_queue_data(q, ngx_quic_socket_t, queue);
166
167 ngx_queue_remove(&sock->queue);
168
169 ngx_memzero(sock, sizeof(ngx_quic_socket_t));
170
171 } else {
172
173 sock = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t));
174 if (sock == NULL) {
175 return NULL;
176 }
177 }
178
179 return sock;
180 }
181
182
183 void
184 ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock)
185 {
186 ngx_quic_connection_t *qc;
187
188 qc = ngx_quic_get_connection(c);
189
190 ngx_queue_remove(&qsock->queue);
191 ngx_queue_insert_head(&qc->free_sockets, &qsock->queue);
192
193 ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);
194 qc->nsockets--;
195
196 if (qsock->path) {
197 ngx_quic_unref_path(c, qsock->path);
198 }
199
200 if (qsock->cid) {
201 ngx_quic_unref_client_id(c, qsock->cid);
202 }
203
204 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
205 "quic socket #%L closed nsock:%ui",
206 (int64_t) qsock->sid.seqnum, qc->nsockets);
207 }
208
209
210 static void
211 ngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path)
212 {
213 ngx_quic_connection_t *qc;
214
215 path->refcnt--;
216
217 if (path->refcnt) {
218 return;
219 }
220
221 qc = ngx_quic_get_connection(c);
222
223 ngx_queue_remove(&path->queue);
224 ngx_queue_insert_head(&qc->free_paths, &path->queue);
225
226 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
227 "quic path #%uL addr:%V removed",
228 path->seqnum, &path->addr_text);
229 }
230
231
232 ngx_int_t
233 ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,
234 ngx_quic_socket_t *qsock)
235 {
236 ngx_str_t id;
237 ngx_quic_server_id_t *sid;
238
239 sid = &qsock->sid;
240
241 sid->len = NGX_QUIC_SERVER_CID_LEN;
242
243 if (ngx_quic_create_server_id(c, sid->id) != NGX_OK) {
244 return NGX_ERROR;
245 }
246
247 sid->seqnum = qc->server_seqnum++;
248
249 id.data = sid->id;
250 id.len = sid->len;
251
252 ngx_insert_udp_connection(c, &qsock->udp, &id);
253
254 ngx_queue_insert_tail(&qc->sockets, &qsock->queue);
255
256 qc->nsockets++;
257 qsock->quic = qc;
258
259 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
260 "quic socket #%uL listening at sid:%xV nsock:%ui",
261 sid->seqnum, &id, qc->nsockets);
262
263 return NGX_OK;
264 }
265
266
267 void
268 ngx_quic_connect(ngx_connection_t *c, ngx_quic_socket_t *sock,
269 ngx_quic_path_t *path, ngx_quic_client_id_t *cid)
270 {
271 sock->path = path;
272 path->refcnt++;
273
274 sock->cid = cid;
275 cid->refcnt++;
276
277 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
278 "quic socket #%L connected to cid #%uL path:%uL",
279 (int64_t) sock->sid.seqnum,
280 sock->cid->seqnum, path->seqnum);
281 }
282
283
284 void
285 ngx_quic_close_sockets(ngx_connection_t *c)
286 {
287 ngx_queue_t *q;
288 ngx_quic_socket_t *qsock;
289 ngx_quic_connection_t *qc;
290
291 qc = ngx_quic_get_connection(c);
292
293 ngx_quic_close_socket(c, qc->socket);
294
295 if (qc->backup) {
296 ngx_quic_close_socket(c, qc->backup);
297 }
298
299 while (!ngx_queue_empty(&qc->sockets)) {
300 q = ngx_queue_head(&qc->sockets);
301 qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);
302
303 ngx_quic_close_socket(c, qsock);
304 }
305 }
306
307
308 ngx_quic_socket_t *
309 ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum)
310 {
311 ngx_queue_t *q;
312 ngx_quic_socket_t *qsock;
313 ngx_quic_connection_t *qc;
314
315 qc = ngx_quic_get_connection(c);
316
317 for (q = ngx_queue_head(&qc->sockets);
318 q != ngx_queue_sentinel(&qc->sockets);
319 q = ngx_queue_next(q))
320 {
321 qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);
322
323 if (qsock->sid.seqnum == seqnum) {
324 return qsock;
325 }
326 }
327
328 return NULL;
329 }
330
331
332 ngx_quic_socket_t *
333 ngx_quic_get_unconnected_socket(ngx_connection_t *c)
334 {
335 ngx_queue_t *q;
336 ngx_quic_socket_t *sock;
337 ngx_quic_connection_t *qc;
338
339 qc = ngx_quic_get_connection(c);
340
341 for (q = ngx_queue_head(&qc->sockets);
342 q != ngx_queue_sentinel(&qc->sockets);
343 q = ngx_queue_next(q))
344 {
345 sock = ngx_queue_data(q, ngx_quic_socket_t, queue);
346
347 if (sock->cid == NULL) {
348 return sock;
349 }
350 }
351
352 return NULL;
353 }
354
355