Skip to content

Commit 71758d7

Browse files
committed
use ip from x-forwarded-for for searching a client
1 parent ef536b8 commit 71758d7

File tree

8 files changed

+76
-18
lines changed

8 files changed

+76
-18
lines changed

tempesta_fw/client.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ tfw_client_addr_eq(TdbRec *rec, void *data)
8383
}
8484

8585
/**
86-
* Find a client corresponding to the @sk by IP address.
86+
* Find a client corresponding to @addr and @cli_addr (e.g. from X-Forwarded-For).
8787
* More advanced identification is possible based on User-Agent,
8888
* Cookie and other HTTP headers.
8989
*
@@ -92,22 +92,24 @@ tfw_client_addr_eq(TdbRec *rec, void *data)
9292
* TODO #515 employ eviction strategy for the table
9393
*/
9494
TfwClient *
95-
tfw_client_obtain(struct sock *sk, void (*init)(TfwClient *))
95+
tfw_client_obtain(TfwAddr addr, TfwAddr *cli_addr, void (*init)(TfwClient *))
9696
{
9797
TfwClientEntry *ent;
9898
TfwClient *cli;
9999
unsigned long key;
100-
TfwAddr addr;
101100
size_t len;
102101
TdbRec *rec;
103102
bool is_new;
104103
int conn_users;
105104
time_t curr_time = tfw_current_timestamp();
106105

107-
ss_getpeername(sk, &addr);
108106
key = hash_calc((const char *)&addr.sin6_addr,
109107
sizeof(addr.sin6_addr));
110108

109+
if (cli_addr)
110+
key ^= hash_calc((const char *)&cli_addr->sin6_addr,
111+
sizeof(cli_addr->sin6_addr));
112+
111113
len = sizeof(*ent);
112114
rec = tdb_rec_get_alloc(client_db, key, &len, &tfw_client_addr_eq,
113115
&is_new, &addr.sin6_addr);

tempesta_fw/client.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ typedef struct {
4040
TfwClassifierPrvt class_prvt;
4141
} TfwClient;
4242

43-
TfwClient *tfw_client_obtain(struct sock *sk, void (*init)(TfwClient *));
43+
TfwClient *tfw_client_obtain(TfwAddr addr, TfwAddr *cli_addr, void (*init)(TfwClient *));
4444
void tfw_client_put(TfwClient *cli);
4545
int tfw_client_for_each(int (*fn)(void *));
4646
void tfw_cli_conn_release(TfwCliConn *cli_conn);

tempesta_fw/connection.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ tfw_connection_init(TfwConn *conn)
4141
void
4242
tfw_connection_link_peer(TfwConn *conn, TfwPeer *peer)
4343
{
44-
BUG_ON(conn->peer || !list_empty(&conn->list));
44+
BUG_ON(!list_empty(&conn->list));
4545
conn->peer = peer;
4646
tfw_peer_add_conn(peer, &conn->list);
4747
}

tempesta_fw/http.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,9 @@ tfw_http_req_destruct(void *msg)
16391639
tfw_vhost_put(req->vhost);
16401640
if (req->sess)
16411641
tfw_http_sess_put(req->sess);
1642+
1643+
if (req->peer)
1644+
tfw_client_put(req->peer);
16421645
}
16431646

16441647
/**
@@ -2812,6 +2815,29 @@ tfw_http_chop_skb(TfwHttpMsg *hm, struct sk_buff *skb,
28122815
return ss_skb_chop_head_tail(hm->msg.skb_head, skb, off, trail);
28132816
}
28142817

2818+
static TfwStr
2819+
tfw_http_get_ip_from_xff(TfwHttpReq *req)
2820+
{
2821+
TfwStr s_xff, s_ip, *c, *end;
2822+
unsigned int nchunks;
2823+
2824+
/*
2825+
* If a client work through a forward proxy, then a proxy can pass it's
2826+
* IP address by the first value in X-Forwarded-For
2827+
*/
2828+
s_xff = req->h_tbl->tbl[TFW_HTTP_HDR_X_FORWARDED_FOR];
2829+
s_ip = tfw_str_next_str_val(&s_xff);
2830+
nchunks = 0;
2831+
TFW_STR_FOR_EACH_CHUNK(c, &s_ip, end) {
2832+
if (!(c->flags & TFW_STR_VALUE))
2833+
break;
2834+
nchunks++;
2835+
}
2836+
s_ip.nchunks = nchunks;
2837+
2838+
return s_ip;
2839+
}
2840+
28152841
/**
28162842
* @return zero on success and negative value otherwise.
28172843
* TODO enter the function depending on current GFSM state.
@@ -2828,6 +2854,8 @@ tfw_http_req_process(TfwConn *conn, const TfwFsmData *data)
28282854
TfwHttpMsg *hmsib;
28292855
TfwHttpParser *parser;
28302856
TfwFsmData data_up;
2857+
TfwAddr addr;
2858+
TfwStr s_ip;
28312859

28322860
BUG_ON(!conn->msg);
28332861
BUG_ON(off >= skb->len);
@@ -2931,6 +2959,21 @@ tfw_http_req_process(TfwConn *conn, const TfwFsmData *data)
29312959
skb = NULL;
29322960
}
29332961

2962+
s_ip = tfw_http_get_ip_from_xff(req);
2963+
if (!TFW_STR_EMPTY(&s_ip)) {
2964+
TfwClient *cli, *conn_cli;
2965+
2966+
if (tfw_addr_pton(&s_ip, &addr) != 0)
2967+
return TFW_BLOCK;
2968+
2969+
conn_cli = (TfwClient *)conn->peer;
2970+
cli = tfw_client_obtain(conn_cli->addr, &addr, NULL);
2971+
if (cli && cli != conn_cli)
2972+
req->peer = cli;
2973+
else
2974+
tfw_client_put(cli);
2975+
}
2976+
29342977
/*
29352978
* Sticky cookie module must be used before request can reach cache.
29362979
* Unauthorised clients mustn't be able to get any resource on

tempesta_fw/http.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "server.h"
3131
#include "str.h"
3232
#include "vhost.h"
33+
#include "client.h"
3334

3435
/**
3536
* HTTP Generic FSM states.
@@ -367,6 +368,7 @@ typedef struct {
367368
* @vhost - virtual host for the request;
368369
* @location - URI location;
369370
* @sess - HTTP session descriptor, required for scheduling;
371+
* @peer - end-to-end peer;
370372
* @userinfo - userinfo in URI, not mandatory.
371373
* @host - host in URI, may differ from Host header;
372374
* @uri_path - path + query + fragment from URI (RFC3986.3);
@@ -393,6 +395,7 @@ struct tfw_http_req_t {
393395
TfwVhost *vhost;
394396
TfwLocation *location;
395397
TfwHttpSess *sess;
398+
TfwClient *peer;
396399
TfwHttpCond cond;
397400
TfwStr userinfo;
398401
TfwStr host;

tempesta_fw/http_limits.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,10 @@ frang_conn_new(struct sock *sk)
335335
FrangAcc *ra;
336336
TfwClient *cli;
337337
FrangCfg *conf = tfw_vhost_global_frang_cfg();
338+
TfwAddr addr;
338339

339-
cli = tfw_client_obtain(sk, __frang_init_acc);
340+
ss_getpeername(sk, &addr);
341+
cli = tfw_client_obtain(addr, NULL, __frang_init_acc);
340342
if (unlikely(!cli)) {
341343
TFW_ERR("can't obtain a client for frang accounting\n");
342344
return TFW_BLOCK;
@@ -980,6 +982,10 @@ frang_http_req_handler(void *obj, TfwFsmData *data)
980982
TfwConn *conn = (TfwConn *)obj;
981983
FrangAcc *ra = conn->sk->sk_security;
982984
bool ip_block = tfw_vhost_global_frang_cfg()->ip_block;
985+
TfwHttpReq *req = (TfwHttpReq *)data->req;
986+
987+
if (req->peer)
988+
ra = FRANG_CLI2ACC(req->peer);
983989

984990
if (test_bit(TFW_HTTP_B_WHITELIST, ((TfwHttpReq *)data->req)->flags))
985991
return TFW_PASS;
@@ -1012,6 +1018,8 @@ frang_resp_handler(void *obj, TfwFsmData *data)
10121018

10131019
resp = (TfwHttpResp *)data->resp;
10141020
ra = (FrangAcc *)req->conn->sk->sk_security;
1021+
if (req->peer)
1022+
ra = FRANG_CLI2ACC(req->peer);
10151023
stat = ra->resp_code_stat;
10161024
frang_dbg("client %s check response %d, acc=%p\n",
10171025
&FRANG_ACC2CLI(ra)->addr, resp->status, ra);

tempesta_fw/http_parser.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3223,27 +3223,27 @@ __req_parse_x_forwarded_for(TfwHttpMsg *hm, unsigned char *data, size_t len)
32233223
__FSM_STATE(Req_I_XFF) {
32243224
/* Eat OWS before the node ID. */
32253225
if (unlikely(IS_WS(c)))
3226-
__FSM_I_MOVE(Req_I_XFF);
3226+
__FSM_I_MOVE_fixup(Req_I_XFF, 1, 0);
32273227
/*
32283228
* Eat IP address or host name.
32293229
*
32303230
* TODO: parse/validate IP addresses and textual IDs.
32313231
* Currently we just validate separate characters, but the
32323232
* whole value may be invalid (e.g. "---[_..[[").
32333233
*/
3234-
__FSM_I_MATCH_MOVE(xff, Req_I_XFF_Node_Id);
3234+
__FSM_I_MATCH_MOVE_fixup(xff, Req_I_XFF_Node_Id, TFW_STR_VALUE);
32353235
if (unlikely(!__fsm_sz))
32363236
return CSTR_NEQ;
3237-
__FSM_I_MOVE_n(Req_I_XFF_Sep, __fsm_sz);
3237+
__FSM_I_MOVE_fixup(Req_I_XFF_Sep, __fsm_sz, TFW_STR_VALUE);
32383238
}
32393239

32403240
/*
32413241
* At this state we know that we saw at least one character as
32423242
* a host address and now we can pass zero length token.
32433243
*/
32443244
__FSM_STATE(Req_I_XFF_Node_Id) {
3245-
__FSM_I_MATCH_MOVE(xff, Req_I_XFF_Node_Id);
3246-
__FSM_I_MOVE_n(Req_I_XFF_Sep, __fsm_sz);
3245+
__FSM_I_MATCH_MOVE_fixup(xff, Req_I_XFF_Node_Id, TFW_STR_VALUE);
3246+
__FSM_I_MOVE_fixup(Req_I_XFF_Sep, __fsm_sz, TFW_STR_VALUE);
32473247
}
32483248

32493249
__FSM_STATE(Req_I_XFF_Sep) {
@@ -3256,14 +3256,14 @@ __req_parse_x_forwarded_for(TfwHttpMsg *hm, unsigned char *data, size_t len)
32563256

32573257
/* OWS before comma or before EOL (is unusual). */
32583258
if (unlikely(IS_WS(c)))
3259-
__FSM_I_MOVE(Req_I_XFF_Sep);
3259+
__FSM_I_MOVE_fixup(Req_I_XFF_Sep, 1, 0);
32603260

32613261
/*
32623262
* Multiple subsequent commas look suspicious, so we don't
32633263
* stay in this state after the first comma is met.
32643264
*/
32653265
if (likely(c == ','))
3266-
__FSM_I_MOVE(Req_I_XFF);
3266+
__FSM_I_MOVE_fixup(Req_I_XFF, 1, 0);
32673267

32683268
return CSTR_NEQ;
32693269
}
@@ -4075,9 +4075,9 @@ tfw_http_parse_req(void *req_data, unsigned char *data, size_t len,
40754075
TFW_HTTP_HDR_TRANSFER_ENCODING);
40764076

40774077
/* 'X-Forwarded-For:*OWS' is read, process field-value. */
4078-
TFW_HTTP_PARSE_SPECHDR_VAL(Req_HdrX_Forwarded_ForV, Req_I_XFF,
4079-
msg, __req_parse_x_forwarded_for,
4080-
TFW_HTTP_HDR_X_FORWARDED_FOR);
4078+
__TFW_HTTP_PARSE_SPECHDR_VAL(Req_HdrX_Forwarded_ForV, Req_I_XFF,
4079+
msg, __req_parse_x_forwarded_for,
4080+
TFW_HTTP_HDR_X_FORWARDED_FOR, 0);
40814081

40824082
/* 'User-Agent:*OWS' is read, process field-value. */
40834083
TFW_HTTP_PARSE_SPECHDR_VAL(Req_HdrUser_AgentV, Req_I_UserAgent,

tempesta_fw/sock_clnt.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ tfw_sock_clnt_new(struct sock *sk)
145145
TfwClient *cli;
146146
TfwConn *conn;
147147
SsProto *listen_sock_proto;
148+
TfwAddr addr;
148149

149150
TFW_DBG3("new client socket: sk=%p, state=%u\n", sk, sk->sk_state);
150151
TFW_INC_STAT_BH(clnt.conn_attempts);
@@ -158,7 +159,8 @@ tfw_sock_clnt_new(struct sock *sk)
158159
listen_sock_proto = sk->sk_user_data;
159160
tfw_connection_unlink_from_sk(sk);
160161

161-
cli = tfw_client_obtain(sk, NULL);
162+
ss_getpeername(sk, &addr);
163+
cli = tfw_client_obtain(addr, NULL, NULL);
162164
if (!cli) {
163165
TFW_ERR("can't obtain a client for the new socket\n");
164166
return -ENOENT;

0 commit comments

Comments
 (0)