Skip to content

Commit 460957d

Browse files
committed
use ip from x-forwarded-for for searching a client
1 parent 92ceda6 commit 460957d

File tree

8 files changed

+124
-27
lines changed

8 files changed

+124
-27
lines changed

tempesta_fw/client.c

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,18 @@ static struct {
4141
/**
4242
* Client tdb entry.
4343
*
44-
* @cli - client descriptor
45-
* @expires - expiration time for the client descriptor after all
46-
* connections are closed
44+
* @cli - client descriptor;
45+
* @xff_addr - peer IPv6 address from X-Forwarded-For;
46+
* @expires - expiration time for the client descriptor after all;
47+
* connections are closed;
4748
* @lock - lock for atomic change @expires and @users;
4849
* @users - reference counter.
4950
* Expiration state will begind, when the counter reaches
50-
* zero
51+
* zero;
5152
*/
5253
typedef struct {
5354
TfwClient cli;
55+
TfwAddr xff_addr;
5456
time_t expires;
5557
spinlock_t lock;
5658
atomic_t users;
@@ -88,21 +90,34 @@ tfw_client_put(TfwClient *cli)
8890
TFW_DEC_STAT_BH(clnt.online);
8991
}
9092

93+
typedef struct {
94+
TfwAddr addr;
95+
TfwAddr xff_addr;
96+
} TfwClientEqCtx;
97+
98+
static struct in6_addr any_addr = IN6ADDR_ANY_INIT;
99+
91100
static bool
92101
tfw_client_addr_eq(TdbRec *rec, void (*init)(void *), void *data)
93102
{
94103
TfwClientEntry *ent = (TfwClientEntry *)rec->data;
95104
TfwClient *cli = &ent->cli;
96-
TfwAddr *addr = (TfwAddr *)data;
105+
TfwClientEqCtx *ctx = (TfwClientEqCtx *)data;
97106
time_t curr_time = tfw_current_timestamp();
98107
int users;
99108

100-
if (memcmp_fast(&cli->addr.sin6_addr, &addr->sin6_addr,
109+
if (memcmp_fast(&cli->addr.sin6_addr, &ctx->addr.sin6_addr,
101110
sizeof(cli->addr.sin6_addr)))
102111
{
103112
return false;
104113
}
105114

115+
if (memcmp_fast(&ent->xff_addr.sin6_addr, &ctx->xff_addr.sin6_addr,
116+
sizeof(ent->xff_addr.sin6_addr)))
117+
{
118+
return false;
119+
}
120+
106121
spin_lock(&ent->lock);
107122

108123
if (curr_time > ent->expires) {
@@ -130,7 +145,7 @@ tfw_client_ent_init(TdbRec *rec, void (*init)(void *), void *data)
130145
{
131146
TfwClientEntry *ent = (TfwClientEntry *)rec->data;
132147
TfwClient *cli = &ent->cli;
133-
TfwAddr *addr = (TfwAddr *)data;
148+
TfwClientEqCtx *ctx = (TfwClientEqCtx *)data;
134149

135150
spin_lock_init(&ent->lock);
136151

@@ -142,15 +157,16 @@ tfw_client_ent_init(TdbRec *rec, void (*init)(void *), void *data)
142157
atomic_set(&ent->users, 1);
143158
TFW_INC_STAT_BH(clnt.online);
144159

145-
tfw_peer_init((TfwPeer *)cli, addr);
160+
tfw_peer_init((TfwPeer *)cli, &ctx->addr);
161+
ent->xff_addr = ctx->xff_addr;
146162

147163
TFW_DBG("new client: cli=%p\n", cli);
148164
TFW_DBG_ADDR("client address", &cli->addr, TFW_NO_PORT);
149165
TFW_DBG2("client %p, users=%d\n", cli, 1);
150166
}
151167

152168
/**
153-
* Find a client corresponding to the @sk by IP address.
169+
* Find a client corresponding to @addr and @xff_addr (e.g. from X-Forwarded-For).
154170
* More advanced identification is possible based on User-Agent,
155171
* Cookie and other HTTP headers.
156172
*
@@ -159,23 +175,32 @@ tfw_client_ent_init(TdbRec *rec, void (*init)(void *), void *data)
159175
* TODO #515 employ eviction strategy for the table.
160176
*/
161177
TfwClient *
162-
tfw_client_obtain(struct sock *sk, void (*init)(void *))
178+
tfw_client_obtain(TfwAddr addr, TfwAddr *xff_addr, void (*init)(void *))
163179
{
164180
TfwClientEntry *ent;
165181
TfwClient *cli;
166182
unsigned long key;
167-
TfwAddr addr;
183+
TfwClientEqCtx ctx;
168184
size_t len;
169185
TdbRec *rec;
170186
bool is_new;
171187

172-
ss_getpeername(sk, &addr);
188+
ctx.addr = addr;
189+
173190
key = hash_calc((const char *)&addr.sin6_addr,
174191
sizeof(addr.sin6_addr));
175192

193+
if (xff_addr) {
194+
key ^= hash_calc((const char *)&xff_addr->sin6_addr,
195+
sizeof(xff_addr->sin6_addr));
196+
ctx.xff_addr = *xff_addr;
197+
} else {
198+
ctx.xff_addr.sin6_addr = any_addr;
199+
}
200+
176201
len = sizeof(TfwClientEntry);
177202
rec = tdb_rec_get_alloc(client_db, key, &len, &tfw_client_addr_eq,
178-
&tfw_client_ent_init, init, &addr, &is_new);
203+
&tfw_client_ent_init, init, &ctx, &is_new);
179204
BUG_ON(len < sizeof(TfwClientEntry));
180205
if (!rec) {
181206
TFW_WARN("cannot allocate TDB space for client\n");

tempesta_fw/client.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ typedef struct {
3636
TfwClassifierPrvt class_prvt;
3737
} TfwClient;
3838

39-
TfwClient *tfw_client_obtain(struct sock *sk, void (*init)(void *));
39+
TfwClient *tfw_client_obtain(TfwAddr addr, TfwAddr *cli_addr,
40+
void (*init)(void *));
4041
void tfw_client_put(TfwClient *cli);
4142
int tfw_client_for_each(int (*fn)(void *));
4243
void tfw_client_set_expires_time(unsigned int expires_time);

tempesta_fw/connection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ enum {
8888
* @refcnt - number of users of the connection structure instance;
8989
* @timer - The keep-alive/retry timer for the connection;
9090
* @msg - message that is currently being processed;
91-
* @peer - TfwClient or TfwServer handler;
91+
* @peer - TfwClient or TfwServer handler. Hop-by-hop peer;
9292
* @sk - an appropriate sock handler;
9393
* @destructor - called when a connection is destroyed;
9494
*/

tempesta_fw/http.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,9 @@ tfw_http_req_destruct(void *msg)
18101810
tfw_vhost_put(req->vhost);
18111811
if (req->sess)
18121812
tfw_http_sess_put(req->sess);
1813+
1814+
if (req->peer)
1815+
tfw_client_put(req->peer);
18131816
}
18141817

18151818
/**
@@ -2982,6 +2985,54 @@ tfw_http_chop_skb(TfwHttpMsg *hm, struct sk_buff *skb,
29822985
return ss_skb_chop_head_tail(hm->msg.skb_head, skb, off, trail);
29832986
}
29842987

2988+
static TfwStr
2989+
tfw_http_get_ip_from_xff(TfwHttpReq *req)
2990+
{
2991+
TfwStr s_xff, s_ip, *c, *end;
2992+
unsigned int nchunks;
2993+
2994+
/*
2995+
* If a client works through a forward proxy, then a proxy can pass it's
2996+
* IP address by the first value in X-Forwarded-For
2997+
*/
2998+
s_xff = req->h_tbl->tbl[TFW_HTTP_HDR_X_FORWARDED_FOR];
2999+
s_ip = tfw_str_next_str_val(&s_xff);
3000+
nchunks = 0;
3001+
TFW_STR_FOR_EACH_CHUNK(c, &s_ip, end) {
3002+
if (!(c->flags & TFW_STR_VALUE))
3003+
break;
3004+
nchunks++;
3005+
}
3006+
s_ip.nchunks = nchunks;
3007+
3008+
return s_ip;
3009+
}
3010+
3011+
static int
3012+
tfw_http_req_client_link(TfwConn *conn, TfwHttpReq *req)
3013+
{
3014+
TfwStr s_ip;
3015+
TfwAddr addr;
3016+
TfwClient *cli, *conn_cli;
3017+
3018+
s_ip = tfw_http_get_ip_from_xff(req);
3019+
if (!TFW_STR_EMPTY(&s_ip)) {
3020+
if (tfw_addr_pton(&s_ip, &addr) != 0)
3021+
return TFW_BLOCK;
3022+
3023+
conn_cli = (TfwClient *)conn->peer;
3024+
cli = tfw_client_obtain(conn_cli->addr, &addr, NULL);
3025+
if (cli) {
3026+
if (cli != conn_cli)
3027+
req->peer = cli;
3028+
else
3029+
tfw_client_put(cli);
3030+
}
3031+
}
3032+
3033+
return 0;
3034+
}
3035+
29853036
/**
29863037
* @return zero on success and negative value otherwise.
29873038
* TODO enter the function depending on current GFSM state.
@@ -3101,6 +3152,9 @@ tfw_http_req_process(TfwConn *conn, const TfwFsmData *data)
31013152
skb = NULL;
31023153
}
31033154

3155+
if ((r = tfw_http_req_client_link(conn, req)))
3156+
return r;
3157+
31043158
/*
31053159
* Sticky cookie module must be used before request can reach cache.
31063160
* Unauthorised clients mustn't be able to get any resource on

tempesta_fw/http.h

Lines changed: 5 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,9 @@ 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. The peer is not set if
372+
* hop-by-hop peer (TfwConnection->peer) and end-to-end peer are
373+
* the same;
370374
* @userinfo - userinfo in URI, not mandatory.
371375
* @host - host in URI, may differ from Host header;
372376
* @uri_path - path + query + fragment from URI (RFC3986.3);
@@ -393,6 +397,7 @@ struct tfw_http_req_t {
393397
TfwVhost *vhost;
394398
TfwLocation *location;
395399
TfwHttpSess *sess;
400+
TfwClient *peer;
396401
TfwHttpCond cond;
397402
TfwStr userinfo;
398403
TfwStr host;

tempesta_fw/http_limits.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,10 @@ frang_conn_new(struct sock *sk)
336336
FrangAcc *ra;
337337
TfwClient *cli;
338338
FrangCfg *conf = tfw_vhost_global_frang_cfg();
339+
TfwAddr addr;
339340

340-
cli = tfw_client_obtain(sk, __frang_init_acc);
341+
ss_getpeername(sk, &addr);
342+
cli = tfw_client_obtain(addr, NULL, __frang_init_acc);
341343
if (unlikely(!cli)) {
342344
TFW_ERR("can't obtain a client for frang accounting\n");
343345
return TFW_BLOCK;
@@ -986,6 +988,10 @@ frang_http_req_handler(void *obj, TfwFsmData *data)
986988
TfwConn *conn = (TfwConn *)obj;
987989
FrangAcc *ra = conn->sk->sk_security;
988990
bool ip_block = tfw_vhost_global_frang_cfg()->ip_block;
991+
TfwHttpReq *req = (TfwHttpReq *)data->req;
992+
993+
if (req->peer)
994+
ra = FRANG_CLI2ACC(req->peer);
989995

990996
if (test_bit(TFW_HTTP_B_WHITELIST, ((TfwHttpReq *)data->req)->flags))
991997
return TFW_PASS;
@@ -1013,6 +1019,8 @@ frang_resp_process(TfwHttpResp *resp)
10131019
return TFW_PASS;
10141020

10151021
ra = (FrangAcc *)req->conn->sk->sk_security;
1022+
if (req->peer)
1023+
ra = FRANG_CLI2ACC(req->peer);
10161024

10171025
/* Ensure message body size doesn't overcome acceptable limits. */
10181026
if ((resp->content_length > body_len) || (resp->body.len > body_len)) {
@@ -1044,6 +1052,8 @@ frang_resp_fwd_process(TfwHttpResp *resp)
10441052
return TFW_PASS;
10451053

10461054
ra = (FrangAcc *)req->conn->sk->sk_security;
1055+
if (req->peer)
1056+
ra = FRANG_CLI2ACC(req->peer);
10471057
stat = ra->resp_code_stat;
10481058
frang_dbg("client %s check response %d, acc=%p\n",
10491059
&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)