comparison src/http/v3/ngx_http_v3_filter_module.c @ 8643:72b304f6207c quic

HTTP/3: traffic-based flood detection. With this patch, all traffic over HTTP/3 bidi and uni streams is counted in the h3c->total_bytes field, and payload traffic is counted in the h3c->payload_bytes field. As long as total traffic is many times larger than payload traffic, we consider this to be a flood. Request header traffic is counted as if all fields are literal. Response header traffic is counted as is.
author Roman Arutyunyan <arut@nginx.com>
date Thu, 07 Oct 2021 13:22:42 +0300
parents 5a2080d48da8
children 8d0753760546
comparison
equal deleted inserted replaced
8642:a09bcc304eef 8643:72b304f6207c
99 ngx_uint_t i, port; 99 ngx_uint_t i, port;
100 ngx_chain_t *out, *hl, *cl, **ll; 100 ngx_chain_t *out, *hl, *cl, **ll;
101 ngx_list_part_t *part; 101 ngx_list_part_t *part;
102 ngx_table_elt_t *header; 102 ngx_table_elt_t *header;
103 ngx_connection_t *c; 103 ngx_connection_t *c;
104 ngx_http_v3_session_t *h3c;
104 ngx_http_v3_filter_ctx_t *ctx; 105 ngx_http_v3_filter_ctx_t *ctx;
105 ngx_http_core_loc_conf_t *clcf; 106 ngx_http_core_loc_conf_t *clcf;
106 ngx_http_core_srv_conf_t *cscf; 107 ngx_http_core_srv_conf_t *cscf;
107 u_char addr[NGX_SOCKADDR_STRLEN]; 108 u_char addr[NGX_SOCKADDR_STRLEN];
108 109
117 r->header_sent = 1; 118 r->header_sent = 1;
118 119
119 if (r != r->main) { 120 if (r != r->main) {
120 return NGX_OK; 121 return NGX_OK;
121 } 122 }
123
124 h3c = ngx_http_v3_get_session(r->connection);
122 125
123 if (r->method == NGX_HTTP_HEAD) { 126 if (r->method == NGX_HTTP_HEAD) {
124 r->header_only = 1; 127 r->header_only = 1;
125 } 128 }
126 129
529 cl->buf = b; 532 cl->buf = b;
530 cl->next = NULL; 533 cl->next = NULL;
531 534
532 n = b->last - b->pos; 535 n = b->last - b->pos;
533 536
537 h3c->payload_bytes += n;
538
534 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) 539 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS)
535 + ngx_http_v3_encode_varlen_int(NULL, n); 540 + ngx_http_v3_encode_varlen_int(NULL, n);
536 541
537 b = ngx_create_temp_buf(r->pool, len); 542 b = ngx_create_temp_buf(r->pool, len);
538 if (b == NULL) { 543 if (b == NULL) {
569 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, 574 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last,
570 NGX_HTTP_V3_FRAME_DATA); 575 NGX_HTTP_V3_FRAME_DATA);
571 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, 576 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last,
572 r->headers_out.content_length_n); 577 r->headers_out.content_length_n);
573 578
579 h3c->payload_bytes += r->headers_out.content_length_n;
580 h3c->total_bytes += r->headers_out.content_length_n;
581
574 cl = ngx_alloc_chain_link(r->pool); 582 cl = ngx_alloc_chain_link(r->pool);
575 if (cl == NULL) { 583 if (cl == NULL) {
576 return NGX_ERROR; 584 return NGX_ERROR;
577 } 585 }
578 586
586 if (ctx == NULL) { 594 if (ctx == NULL) {
587 return NGX_ERROR; 595 return NGX_ERROR;
588 } 596 }
589 597
590 ngx_http_set_ctx(r, ctx, ngx_http_v3_filter_module); 598 ngx_http_set_ctx(r, ctx, ngx_http_v3_filter_module);
599 }
600
601 for (cl = out; cl; cl = cl->next) {
602 h3c->total_bytes += cl->buf->last - cl->buf->pos;
591 } 603 }
592 604
593 return ngx_http_write_filter(r, out); 605 return ngx_http_write_filter(r, out);
594 } 606 }
595 607
1094 1106
1095 static ngx_chain_t * 1107 static ngx_chain_t *
1096 ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path, 1108 ngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path,
1097 uint64_t push_id) 1109 uint64_t push_id)
1098 { 1110 {
1099 size_t n, len; 1111 size_t n, len;
1100 ngx_buf_t *b; 1112 ngx_buf_t *b;
1101 ngx_chain_t *hl, *cl; 1113 ngx_chain_t *hl, *cl;
1114 ngx_http_v3_session_t *h3c;
1115
1116 h3c = ngx_http_v3_get_session(r->connection);
1102 1117
1103 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1118 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1104 "http3 create push promise id:%uL", push_id); 1119 "http3 create push promise id:%uL", push_id);
1105 1120
1106 len = ngx_http_v3_encode_varlen_int(NULL, push_id); 1121 len = ngx_http_v3_encode_varlen_int(NULL, push_id);
1230 1245
1231 cl->buf = b; 1246 cl->buf = b;
1232 cl->next = NULL; 1247 cl->next = NULL;
1233 1248
1234 n = b->last - b->pos; 1249 n = b->last - b->pos;
1250
1251 h3c->payload_bytes += n;
1235 1252
1236 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE) 1253 len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE)
1237 + ngx_http_v3_encode_varlen_int(NULL, n); 1254 + ngx_http_v3_encode_varlen_int(NULL, n);
1238 1255
1239 b = ngx_create_temp_buf(r->pool, len); 1256 b = ngx_create_temp_buf(r->pool, len);
1263 u_char *chunk; 1280 u_char *chunk;
1264 off_t size; 1281 off_t size;
1265 ngx_int_t rc; 1282 ngx_int_t rc;
1266 ngx_buf_t *b; 1283 ngx_buf_t *b;
1267 ngx_chain_t *out, *cl, *tl, **ll; 1284 ngx_chain_t *out, *cl, *tl, **ll;
1285 ngx_http_v3_session_t *h3c;
1268 ngx_http_v3_filter_ctx_t *ctx; 1286 ngx_http_v3_filter_ctx_t *ctx;
1269 1287
1270 if (in == NULL) { 1288 if (in == NULL) {
1271 return ngx_http_next_body_filter(r, in); 1289 return ngx_http_next_body_filter(r, in);
1272 } 1290 }
1273 1291
1274 ctx = ngx_http_get_module_ctx(r, ngx_http_v3_filter_module); 1292 ctx = ngx_http_get_module_ctx(r, ngx_http_v3_filter_module);
1275 if (ctx == NULL) { 1293 if (ctx == NULL) {
1276 return ngx_http_next_body_filter(r, in); 1294 return ngx_http_next_body_filter(r, in);
1277 } 1295 }
1296
1297 h3c = ngx_http_v3_get_session(r->connection);
1278 1298
1279 out = NULL; 1299 out = NULL;
1280 ll = &out; 1300 ll = &out;
1281 1301
1282 size = 0; 1302 size = 0;
1338 NGX_HTTP_V3_FRAME_DATA); 1358 NGX_HTTP_V3_FRAME_DATA);
1339 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size); 1359 b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size);
1340 1360
1341 tl->next = out; 1361 tl->next = out;
1342 out = tl; 1362 out = tl;
1363
1364 h3c->payload_bytes += size;
1343 } 1365 }
1344 1366
1345 if (cl->buf->last_buf) { 1367 if (cl->buf->last_buf) {
1346 tl = ngx_http_v3_create_trailers(r, ctx); 1368 tl = ngx_http_v3_create_trailers(r, ctx);
1347 if (tl == NULL) { 1369 if (tl == NULL) {
1354 1376
1355 } else { 1377 } else {
1356 *ll = NULL; 1378 *ll = NULL;
1357 } 1379 }
1358 1380
1381 for (cl = out; cl; cl = cl->next) {
1382 h3c->total_bytes += cl->buf->last - cl->buf->pos;
1383 }
1384
1359 rc = ngx_http_next_body_filter(r, out); 1385 rc = ngx_http_next_body_filter(r, out);
1360 1386
1361 ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, 1387 ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
1362 (ngx_buf_tag_t) &ngx_http_v3_filter_module); 1388 (ngx_buf_tag_t) &ngx_http_v3_filter_module);
1363 1389
1367 1393
1368 static ngx_chain_t * 1394 static ngx_chain_t *
1369 ngx_http_v3_create_trailers(ngx_http_request_t *r, 1395 ngx_http_v3_create_trailers(ngx_http_request_t *r,
1370 ngx_http_v3_filter_ctx_t *ctx) 1396 ngx_http_v3_filter_ctx_t *ctx)
1371 { 1397 {
1372 size_t len, n; 1398 size_t len, n;
1373 u_char *p; 1399 u_char *p;
1374 ngx_buf_t *b; 1400 ngx_buf_t *b;
1375 ngx_uint_t i; 1401 ngx_uint_t i;
1376 ngx_chain_t *cl, *hl; 1402 ngx_chain_t *cl, *hl;
1377 ngx_list_part_t *part; 1403 ngx_list_part_t *part;
1378 ngx_table_elt_t *header; 1404 ngx_table_elt_t *header;
1405 ngx_http_v3_session_t *h3c;
1406
1407 h3c = ngx_http_v3_get_session(r->connection);
1379 1408
1380 len = 0; 1409 len = 0;
1381 1410
1382 part = &r->headers_out.trailers.part; 1411 part = &r->headers_out.trailers.part;
1383 header = part->elts; 1412 header = part->elts;
1458 &header[i].key, 1487 &header[i].key,
1459 &header[i].value); 1488 &header[i].value);
1460 } 1489 }
1461 1490
1462 n = b->last - b->pos; 1491 n = b->last - b->pos;
1492
1493 h3c->payload_bytes += n;
1463 1494
1464 hl = ngx_chain_get_free_buf(r->pool, &ctx->free); 1495 hl = ngx_chain_get_free_buf(r->pool, &ctx->free);
1465 if (hl == NULL) { 1496 if (hl == NULL) {
1466 return NULL; 1497 return NULL;
1467 } 1498 }