* we note the error in the session flags but don't set any state.
* Since the error will be noted there, it will not be counted by
* process_session() as a frontend error.
+ * Last, we may increase some tracked counters' http request errors on
+ * the cases that are deliberately the client's fault. For instance,
+ * a timeout or connection reset is not counted as an error. However
+ * a bad request is.
*/
if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
* First, let's catch bad requests.
*/
if (unlikely(msg->msg_state == HTTP_MSG_ERROR)) {
+ session_inc_http_req_ctr(s);
+ session_inc_http_err_ctr(s);
proxy_inc_fe_req_ctr(s->fe);
goto return_bad_req;
}
/* FIXME: check if URI is set and return Status
* 414 Request URI too long instead.
*/
+ session_inc_http_req_ctr(s);
+ session_inc_http_err_ctr(s);
proxy_inc_fe_req_ctr(s->fe);
goto return_bad_req;
}
goto failed_keep_alive;
/* we cannot return any message on error */
- if (msg->err_pos >= 0)
+ if (msg->err_pos >= 0) {
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
+ session_inc_http_err_ctr(s);
+ }
+
msg->msg_state = HTTP_MSG_ERROR;
req->analysers = 0;
+ session_inc_http_req_ctr(s);
proxy_inc_fe_req_ctr(s->fe);
s->fe->counters.failed_req++;
if (s->listener->counters)
goto failed_keep_alive;
/* read timeout : give up with an error message. */
- if (msg->err_pos >= 0)
+ if (msg->err_pos >= 0) {
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
+ session_inc_http_err_ctr(s);
+ }
txn->status = 408;
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_408));
msg->msg_state = HTTP_MSG_ERROR;
req->analysers = 0;
+ session_inc_http_req_ctr(s);
proxy_inc_fe_req_ctr(s->fe);
s->fe->counters.failed_req++;
if (s->listener->counters)
msg->msg_state = HTTP_MSG_ERROR;
req->analysers = 0;
+ session_inc_http_err_ctr(s);
+ session_inc_http_req_ctr(s);
proxy_inc_fe_req_ctr(s->fe);
s->fe->counters.failed_req++;
if (s->listener->counters)
* left uninitialized (for instance in the absence of headers).
*/
+ session_inc_http_req_ctr(s);
proxy_inc_fe_req_ctr(s->fe); /* one more valid request for this FE */
if (txn->flags & TX_WAIT_NEXT_RQ) {
/* let's log the request time */
s->logs.tv_request = now;
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
+ session_inc_http_err_ctr(s);
goto return_prx_cond;
}
}
txn->status = 403;
s->logs.tv_request = now;
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
+ session_inc_http_err_ctr(s);
goto return_prx_cond;
}
/* let's log the request time */
s->logs.tv_request = now;
stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
+ session_inc_http_err_ctr(s);
goto return_prx_cond;
}
req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.tarpit);
if (!req->analyse_exp)
req->analyse_exp = tick_add(now_ms, 0);
+ session_inc_http_err_ctr(s);
return 1;
}
}
chunk_initlen(&msg, trash, sizeof(trash), strlen(trash));
txn->status = 401;
stream_int_retnclose(req->prod, &msg);
+ /* on 401 we still count one error, because normal browsing
+ * won't significantly increase the counter but brute force
+ * attempts will.
+ */
+ session_inc_http_err_ctr(s);
goto return_prx_cond;
}
if (!ret)
goto missing_data;
- else if (ret < 0)
+ else if (ret < 0) {
+ session_inc_http_err_ctr(s);
goto return_bad_req;
+ }
}
/* Now we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state.
missing_data:
/* we get here if we need to wait for more data */
- if (req->flags & BF_FULL)
+ if (req->flags & BF_FULL) {
+ session_inc_http_err_ctr(s);
goto return_bad_req;
+ }
if ((req->flags & BF_READ_TIMEOUT) || tick_is_expired(req->analyse_exp, now_ms)) {
txn->status = 408;
if (!ret)
goto missing_data;
- else if (ret < 0)
+ else if (ret < 0) {
+ session_inc_http_err_ctr(s);
goto return_bad_req;
+ }
/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
}
else if (msg->msg_state == HTTP_MSG_DATA_CRLF) {
if (ret == 0)
goto missing_data;
- else if (ret < 0)
+ else if (ret < 0) {
+ session_inc_http_err_ctr(s);
goto return_bad_req;
+ }
/* we're in MSG_CHUNK_SIZE now */
}
else if (msg->msg_state == HTTP_MSG_TRAILERS) {
if (ret == 0)
goto missing_data;
- else if (ret < 0)
+ else if (ret < 0) {
+ session_inc_http_err_ctr(s);
goto return_bad_req;
+ }
/* we're in HTTP_MSG_DONE now */
}
else {
n = msg->sol[msg->sl.st.c] - '0';
if (n < 1 || n > 5)
n = 0;
+ /* when the client triggers a 4xx from the server, it's most often due
+ * to a missing object or permission. These events should be tracked
+ * because if they happen often, it may indicate a brute force or a
+ * vulnerability scan.
+ */
+ if (n == 4)
+ session_inc_http_err_ctr(s);
+
if (s->srv)
s->srv->counters.p.http.rsp[n]++;
return acl_fetch_sess_rate(&px->table, test, stktable_lookup_key(&px->table, key));
}
+/* set test->i to the cumulated number of sessions in the stksess entry <ts> */
+static int
+acl_fetch_http_req_cnt(struct stktable *table, struct acl_test *test, struct stksess *ts)
+{
+ test->flags = ACL_TEST_F_VOL_TEST;
+ test->i = 0;
+ if (ts != NULL) {
+ void *ptr = stktable_data_ptr(table, ts, STKTABLE_DT_HTTP_REQ_CNT);
+ if (!ptr)
+ return 0; /* parameter not stored */
+ test->i = stktable_data_cast(ptr, http_req_cnt);
+ }
+ return 1;
+}
+
+/* set test->i to the cumulated number of sessions from the session's tracked counters */
+static int
+acl_fetch_trk_http_req_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
+ struct acl_expr *expr, struct acl_test *test)
+{
+ if (!l4->tracked_counters)
+ return 0;
+
+ return acl_fetch_http_req_cnt(l4->tracked_table, test, l4->tracked_counters);
+}
+
+/* set test->i to the cumulated number of session from the session's source
+ * address in the table pointed to by expr.
+ */
+static int
+acl_fetch_src_http_req_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
+ struct acl_expr *expr, struct acl_test *test)
+{
+ struct stktable_key *key;
+
+ key = tcpv4_src_to_stktable_key(l4);
+ if (!key)
+ return 0; /* only TCPv4 is supported right now */
+
+ if (expr->arg_len)
+ px = find_stktable(expr->arg.str);
+
+ if (!px)
+ return 0; /* table not found */
+
+ return acl_fetch_http_req_cnt(&px->table, test, stktable_lookup_key(&px->table, key));
+}
+
+/* set test->i to the session rate in the stksess entry <ts> over the configured period */
+static int
+acl_fetch_http_req_rate(struct stktable *table, struct acl_test *test, struct stksess *ts)
+{
+ test->flags = ACL_TEST_F_VOL_TEST;
+ test->i = 0;
+ if (ts != NULL) {
+ void *ptr = stktable_data_ptr(table, ts, STKTABLE_DT_HTTP_REQ_RATE);
+ if (!ptr)
+ return 0; /* parameter not stored */
+ test->i = read_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
+ table->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u);
+ }
+ return 1;
+}
+
+/* set test->i to the session rate from the session's tracked counters over
+ * the configured period.
+ */
+static int
+acl_fetch_trk_http_req_rate(struct proxy *px, struct session *l4, void *l7, int dir,
+ struct acl_expr *expr, struct acl_test *test)
+{
+ if (!l4->tracked_counters)
+ return 0;
+
+ return acl_fetch_http_req_rate(l4->tracked_table, test, l4->tracked_counters);
+}
+
+/* set test->i to the session rate from the session's source address in the
+ * table pointed to by expr, over the configured period.
+ */
+static int
+acl_fetch_src_http_req_rate(struct proxy *px, struct session *l4, void *l7, int dir,
+ struct acl_expr *expr, struct acl_test *test)
+{
+ struct stktable_key *key;
+
+ key = tcpv4_src_to_stktable_key(l4);
+ if (!key)
+ return 0; /* only TCPv4 is supported right now */
+
+ if (expr->arg_len)
+ px = find_stktable(expr->arg.str);
+
+ if (!px)
+ return 0; /* table not found */
+
+ return acl_fetch_http_req_rate(&px->table, test, stktable_lookup_key(&px->table, key));
+}
+
+/* set test->i to the cumulated number of sessions in the stksess entry <ts> */
+static int
+acl_fetch_http_err_cnt(struct stktable *table, struct acl_test *test, struct stksess *ts)
+{
+ test->flags = ACL_TEST_F_VOL_TEST;
+ test->i = 0;
+ if (ts != NULL) {
+ void *ptr = stktable_data_ptr(table, ts, STKTABLE_DT_HTTP_ERR_CNT);
+ if (!ptr)
+ return 0; /* parameter not stored */
+ test->i = stktable_data_cast(ptr, http_err_cnt);
+ }
+ return 1;
+}
+
+/* set test->i to the cumulated number of sessions from the session's tracked counters */
+static int
+acl_fetch_trk_http_err_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
+ struct acl_expr *expr, struct acl_test *test)
+{
+ if (!l4->tracked_counters)
+ return 0;
+
+ return acl_fetch_http_err_cnt(l4->tracked_table, test, l4->tracked_counters);
+}
+
+/* set test->i to the cumulated number of session from the session's source
+ * address in the table pointed to by expr.
+ */
+static int
+acl_fetch_src_http_err_cnt(struct proxy *px, struct session *l4, void *l7, int dir,
+ struct acl_expr *expr, struct acl_test *test)
+{
+ struct stktable_key *key;
+
+ key = tcpv4_src_to_stktable_key(l4);
+ if (!key)
+ return 0; /* only TCPv4 is supported right now */
+
+ if (expr->arg_len)
+ px = find_stktable(expr->arg.str);
+
+ if (!px)
+ return 0; /* table not found */
+
+ return acl_fetch_http_err_cnt(&px->table, test, stktable_lookup_key(&px->table, key));
+}
+
+/* set test->i to the session rate in the stksess entry <ts> over the configured period */
+static int
+acl_fetch_http_err_rate(struct stktable *table, struct acl_test *test, struct stksess *ts)
+{
+ test->flags = ACL_TEST_F_VOL_TEST;
+ test->i = 0;
+ if (ts != NULL) {
+ void *ptr = stktable_data_ptr(table, ts, STKTABLE_DT_HTTP_ERR_RATE);
+ if (!ptr)
+ return 0; /* parameter not stored */
+ test->i = read_freq_ctr_period(&stktable_data_cast(ptr, http_err_rate),
+ table->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u);
+ }
+ return 1;
+}
+
+/* set test->i to the session rate from the session's tracked counters over
+ * the configured period.
+ */
+static int
+acl_fetch_trk_http_err_rate(struct proxy *px, struct session *l4, void *l7, int dir,
+ struct acl_expr *expr, struct acl_test *test)
+{
+ if (!l4->tracked_counters)
+ return 0;
+
+ return acl_fetch_http_err_rate(l4->tracked_table, test, l4->tracked_counters);
+}
+
+/* set test->i to the session rate from the session's source address in the
+ * table pointed to by expr, over the configured period.
+ */
+static int
+acl_fetch_src_http_err_rate(struct proxy *px, struct session *l4, void *l7, int dir,
+ struct acl_expr *expr, struct acl_test *test)
+{
+ struct stktable_key *key;
+
+ key = tcpv4_src_to_stktable_key(l4);
+ if (!key)
+ return 0; /* only TCPv4 is supported right now */
+
+ if (expr->arg_len)
+ px = find_stktable(expr->arg.str);
+
+ if (!px)
+ return 0; /* table not found */
+
+ return acl_fetch_http_err_rate(&px->table, test, stktable_lookup_key(&px->table, key));
+}
+
/* set test->i to the number of kbytes received from clients matching the stksess entry <ts> */
static int
acl_fetch_kbytes_in(struct stktable *table, struct acl_test *test, struct stksess *ts)
{ "src_sess_cnt", acl_parse_int, acl_fetch_src_sess_cnt, acl_match_int, ACL_USE_TCP4_VOLATILE },
{ "trk_sess_rate", acl_parse_int, acl_fetch_trk_sess_rate, acl_match_int, ACL_USE_NOTHING },
{ "src_sess_rate", acl_parse_int, acl_fetch_src_sess_rate, acl_match_int, ACL_USE_TCP4_VOLATILE },
+ { "trk_http_req_cnt", acl_parse_int, acl_fetch_trk_http_req_cnt, acl_match_int, ACL_USE_NOTHING },
+ { "src_http_req_cnt", acl_parse_int, acl_fetch_src_http_req_cnt, acl_match_int, ACL_USE_TCP4_VOLATILE },
+ { "trk_http_req_rate", acl_parse_int, acl_fetch_trk_http_req_rate, acl_match_int, ACL_USE_NOTHING },
+ { "src_http_req_rate", acl_parse_int, acl_fetch_src_http_req_rate, acl_match_int, ACL_USE_TCP4_VOLATILE },
+ { "trk_http_err_cnt", acl_parse_int, acl_fetch_trk_http_err_cnt, acl_match_int, ACL_USE_NOTHING },
+ { "src_http_err_cnt", acl_parse_int, acl_fetch_src_http_err_cnt, acl_match_int, ACL_USE_TCP4_VOLATILE },
+ { "trk_http_err_rate", acl_parse_int, acl_fetch_trk_http_err_rate, acl_match_int, ACL_USE_NOTHING },
+ { "src_http_err_rate", acl_parse_int, acl_fetch_src_http_err_rate, acl_match_int, ACL_USE_TCP4_VOLATILE },
{ "trk_kbytes_in", acl_parse_int, acl_fetch_trk_kbytes_in, acl_match_int, ACL_USE_TCP4_VOLATILE },
{ "src_kbytes_in", acl_parse_int, acl_fetch_src_kbytes_in, acl_match_int, ACL_USE_TCP4_VOLATILE },
{ "trk_bytes_in_rate", acl_parse_int, acl_fetch_trk_bytes_in_rate, acl_match_int, ACL_USE_NOTHING },