comparison src/http/v3/ngx_http_v3_request.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 a09bcc304eef
children 925572184d4a
comparison
equal deleted inserted replaced
8642:a09bcc304eef 8643:72b304f6207c
216 ssize_t n; 216 ssize_t n;
217 ngx_buf_t *b; 217 ngx_buf_t *b;
218 ngx_int_t rc; 218 ngx_int_t rc;
219 ngx_connection_t *c; 219 ngx_connection_t *c;
220 ngx_http_request_t *r; 220 ngx_http_request_t *r;
221 ngx_http_v3_session_t *h3c;
221 ngx_http_core_srv_conf_t *cscf; 222 ngx_http_core_srv_conf_t *cscf;
222 ngx_http_v3_parse_headers_t *st; 223 ngx_http_v3_parse_headers_t *st;
223 224
224 c = rev->data; 225 c = rev->data;
225 r = c->data; 226 r = c->data;
230 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); 231 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
231 c->timedout = 1; 232 c->timedout = 1;
232 ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); 233 ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
233 return; 234 return;
234 } 235 }
236
237 h3c = ngx_http_v3_get_session(c);
235 238
236 st = &r->v3_parse->headers; 239 st = &r->v3_parse->headers;
237 240
238 b = r->header_in; 241 b = r->header_in;
239 242
296 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); 299 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
297 break; 300 break;
298 } 301 }
299 302
300 r->request_length += b->pos - p; 303 r->request_length += b->pos - p;
304 h3c->total_bytes += b->pos - p;
305
306 if (ngx_http_v3_check_flood(c) != NGX_OK) {
307 ngx_http_close_request(r, NGX_HTTP_CLOSE);
308 break;
309 }
301 310
302 if (rc == NGX_BUSY) { 311 if (rc == NGX_BUSY) {
303 if (rev->error) { 312 if (rev->error) {
304 ngx_http_close_request(r, NGX_HTTP_CLOSE); 313 ngx_http_close_request(r, NGX_HTTP_CLOSE);
305 break; 314 break;
315 if (rc == NGX_AGAIN) { 324 if (rc == NGX_AGAIN) {
316 continue; 325 continue;
317 } 326 }
318 327
319 /* rc == NGX_OK || rc == NGX_DONE */ 328 /* rc == NGX_OK || rc == NGX_DONE */
329
330 h3c->payload_bytes += ngx_http_v3_encode_field_l(NULL,
331 &st->field_rep.field.name,
332 &st->field_rep.field.value);
320 333
321 if (ngx_http_v3_process_header(r, &st->field_rep.field.name, 334 if (ngx_http_v3_process_header(r, &st->field_rep.field.name,
322 &st->field_rep.field.value) 335 &st->field_rep.field.value)
323 != NGX_OK) 336 != NGX_OK)
324 { 337 {
1078 u_char *p; 1091 u_char *p;
1079 ngx_int_t rc; 1092 ngx_int_t rc;
1080 ngx_buf_t *b; 1093 ngx_buf_t *b;
1081 ngx_uint_t last; 1094 ngx_uint_t last;
1082 ngx_chain_t *cl, *out, *tl, **ll; 1095 ngx_chain_t *cl, *out, *tl, **ll;
1096 ngx_http_v3_session_t *h3c;
1083 ngx_http_request_body_t *rb; 1097 ngx_http_request_body_t *rb;
1084 ngx_http_core_loc_conf_t *clcf; 1098 ngx_http_core_loc_conf_t *clcf;
1085 ngx_http_core_srv_conf_t *cscf; 1099 ngx_http_core_srv_conf_t *cscf;
1086 ngx_http_v3_parse_data_t *st; 1100 ngx_http_v3_parse_data_t *st;
1087 1101
1088 rb = r->request_body; 1102 rb = r->request_body;
1089 st = &r->v3_parse->body; 1103 st = &r->v3_parse->body;
1104
1105 h3c = ngx_http_v3_get_session(r->connection);
1090 1106
1091 if (rb->rest == -1) { 1107 if (rb->rest == -1) {
1092 1108
1093 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1109 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1094 "http3 request body filter"); 1110 "http3 request body filter");
1133 p = cl->buf->pos; 1149 p = cl->buf->pos;
1134 1150
1135 rc = ngx_http_v3_parse_data(r->connection, st, cl->buf); 1151 rc = ngx_http_v3_parse_data(r->connection, st, cl->buf);
1136 1152
1137 r->request_length += cl->buf->pos - p; 1153 r->request_length += cl->buf->pos - p;
1154 h3c->total_bytes += cl->buf->pos - p;
1155
1156 if (ngx_http_v3_check_flood(r->connection) != NGX_OK) {
1157 return NGX_HTTP_CLOSE;
1158 }
1138 1159
1139 if (rc == NGX_AGAIN) { 1160 if (rc == NGX_AGAIN) {
1140 continue; 1161 continue;
1141 } 1162 }
1142 1163
1176 && st->length <= 128 1197 && st->length <= 128
1177 && (uint64_t) (cl->buf->last - cl->buf->pos) >= st->length) 1198 && (uint64_t) (cl->buf->last - cl->buf->pos) >= st->length)
1178 { 1199 {
1179 rb->received += st->length; 1200 rb->received += st->length;
1180 r->request_length += st->length; 1201 r->request_length += st->length;
1202 h3c->total_bytes += st->length;
1203 h3c->payload_bytes += st->length;
1181 1204
1182 if (st->length < 8) { 1205 if (st->length < 8) {
1183 1206
1184 while (st->length) { 1207 while (st->length) {
1185 *b->last++ = *cl->buf->pos++; 1208 *b->last++ = *cl->buf->pos++;
1220 1243
1221 if (size > st->length) { 1244 if (size > st->length) {
1222 cl->buf->pos += (size_t) st->length; 1245 cl->buf->pos += (size_t) st->length;
1223 rb->received += st->length; 1246 rb->received += st->length;
1224 r->request_length += st->length; 1247 r->request_length += st->length;
1248 h3c->total_bytes += st->length;
1249 h3c->payload_bytes += st->length;
1225 st->length = 0; 1250 st->length = 0;
1226 1251
1227 } else { 1252 } else {
1228 st->length -= size; 1253 st->length -= size;
1229 rb->received += size; 1254 rb->received += size;
1230 r->request_length += size; 1255 r->request_length += size;
1256 h3c->total_bytes += size;
1257 h3c->payload_bytes += size;
1231 cl->buf->pos = cl->buf->last; 1258 cl->buf->pos = cl->buf->last;
1232 } 1259 }
1233 1260
1234 b->last = cl->buf->pos; 1261 b->last = cl->buf->pos;
1235 } 1262 }