1 /*
2 * include/proto/stream.h
3 * This file defines everything related to streams.
4 *
5 * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation, version 2.1
10 * exclusively.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #ifndef _PROTO_STREAM_H
23 #define _PROTO_STREAM_H
24
25 #include <common/config.h>
26 #include <common/memory.h>
27 #include <types/action.h>
28 #include <types/stream.h>
29 #include <proto/fd.h>
30 #include <proto/freq_ctr.h>
31 #include <proto/stick_table.h>
32 #include <proto/task.h>
33
34 extern struct pool_head *pool_head_stream;
35 extern struct list streams;
36
37 extern struct data_cb sess_conn_cb;
38
39 struct stream *stream_new(struct session *sess, enum obj_type *origin);
40 int stream_create_from_cs(struct conn_stream *cs);
41
42 /* kill a stream and set the termination flags to <why> (one of SF_ERR_*) */
43 void stream_shutdown(struct stream *stream, int why);
44
45 void stream_process_counters(struct stream *s);
46 void sess_change_server(struct stream *sess, struct server *newsrv);
47 struct task *process_stream(struct task *t, void *context, unsigned short state);
48 void default_srv_error(struct stream *s, struct stream_interface *si);
49 int parse_track_counters(char **args, int *arg,
50 int section_type, struct proxy *curpx,
51 struct track_ctr_prm *prm,
52 struct proxy *defpx, char **err);
53
54 /* Update the stream's backend and server time stats */
55 void stream_update_time_stats(struct stream *s);
56 void stream_release_buffers(struct stream *s);
57 int stream_buf_available(void *arg);
58
59 /* returns the session this stream belongs to */
strm_sess(const struct stream * strm)60 static inline struct session *strm_sess(const struct stream *strm)
61 {
62 return strm->sess;
63 }
64
65 /* returns the frontend this stream was initiated from */
strm_fe(const struct stream * strm)66 static inline struct proxy *strm_fe(const struct stream *strm)
67 {
68 return strm->sess->fe;
69 }
70
71 /* returns the listener this stream was initiated from */
strm_li(const struct stream * strm)72 static inline struct listener *strm_li(const struct stream *strm)
73 {
74 return strm->sess->listener;
75 }
76
77 /* returns a pointer to the origin of the session which created this stream */
strm_orig(const struct stream * strm)78 static inline enum obj_type *strm_orig(const struct stream *strm)
79 {
80 return strm->sess->origin;
81 }
82
83 /* Remove the refcount from the stream to the tracked counters, and clear the
84 * pointer to ensure this is only performed once. The caller is responsible for
85 * ensuring that the pointer is valid first. We must be extremely careful not
86 * to touch the entries we inherited from the session.
87 */
stream_store_counters(struct stream * s)88 static inline void stream_store_counters(struct stream *s)
89 {
90 void *ptr;
91 int i;
92 struct stksess *ts;
93
94 for (i = 0; i < MAX_SESS_STKCTR; i++) {
95 ts = stkctr_entry(&s->stkctr[i]);
96 if (!ts)
97 continue;
98
99 if (stkctr_entry(&s->sess->stkctr[i]))
100 continue;
101
102 ptr = stktable_data_ptr(s->stkctr[i].table, ts, STKTABLE_DT_CONN_CUR);
103 if (ptr) {
104 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
105
106 if (stktable_data_cast(ptr, conn_cur) > 0)
107 stktable_data_cast(ptr, conn_cur)--;
108
109 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
110
111 /* If data was modified, we need to touch to re-schedule sync */
112 stktable_touch_local(s->stkctr[i].table, ts, 0);
113 }
114 stkctr_set_entry(&s->stkctr[i], NULL);
115 stksess_kill_if_expired(s->stkctr[i].table, ts, 1);
116 }
117 }
118
119 /* Remove the refcount from the stream counters tracked at the content level if
120 * any, and clear the pointer to ensure this is only performed once. The caller
121 * is responsible for ensuring that the pointer is valid first. We must be
122 * extremely careful not to touch the entries we inherited from the session.
123 */
stream_stop_content_counters(struct stream * s)124 static inline void stream_stop_content_counters(struct stream *s)
125 {
126 struct stksess *ts;
127 void *ptr;
128 int i;
129
130 for (i = 0; i < MAX_SESS_STKCTR; i++) {
131 ts = stkctr_entry(&s->stkctr[i]);
132 if (!ts)
133 continue;
134
135 if (stkctr_entry(&s->sess->stkctr[i]))
136 continue;
137
138 if (!(stkctr_flags(&s->stkctr[i]) & STKCTR_TRACK_CONTENT))
139 continue;
140
141 ptr = stktable_data_ptr(s->stkctr[i].table, ts, STKTABLE_DT_CONN_CUR);
142 if (ptr) {
143 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
144
145 if (stktable_data_cast(ptr, conn_cur) > 0)
146 stktable_data_cast(ptr, conn_cur)--;
147
148 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
149
150 /* If data was modified, we need to touch to re-schedule sync */
151 stktable_touch_local(s->stkctr[i].table, ts, 0);
152 }
153 stkctr_set_entry(&s->stkctr[i], NULL);
154 stksess_kill_if_expired(s->stkctr[i].table, ts, 1);
155 }
156 }
157
158 /* Increase total and concurrent connection count for stick entry <ts> of table
159 * <t>. The caller is responsible for ensuring that <t> and <ts> are valid
160 * pointers, and for calling this only once per connection.
161 */
stream_start_counters(struct stktable * t,struct stksess * ts)162 static inline void stream_start_counters(struct stktable *t, struct stksess *ts)
163 {
164 void *ptr;
165
166 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
167
168 ptr = stktable_data_ptr(t, ts, STKTABLE_DT_CONN_CUR);
169 if (ptr)
170 stktable_data_cast(ptr, conn_cur)++;
171
172 ptr = stktable_data_ptr(t, ts, STKTABLE_DT_CONN_CNT);
173 if (ptr)
174 stktable_data_cast(ptr, conn_cnt)++;
175
176 ptr = stktable_data_ptr(t, ts, STKTABLE_DT_CONN_RATE);
177 if (ptr)
178 update_freq_ctr_period(&stktable_data_cast(ptr, conn_rate),
179 t->data_arg[STKTABLE_DT_CONN_RATE].u, 1);
180 if (tick_isset(t->expire))
181 ts->expire = tick_add(now_ms, MS_TO_TICKS(t->expire));
182
183 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
184
185 /* If data was modified, we need to touch to re-schedule sync */
186 stktable_touch_local(t, ts, 0);
187 }
188
189 /* Enable tracking of stream counters as <stkctr> on stksess <ts>. The caller is
190 * responsible for ensuring that <t> and <ts> are valid pointers. Some controls
191 * are performed to ensure the state can still change.
192 */
stream_track_stkctr(struct stkctr * ctr,struct stktable * t,struct stksess * ts)193 static inline void stream_track_stkctr(struct stkctr *ctr, struct stktable *t, struct stksess *ts)
194 {
195 /* Why this test ???? */
196 if (stkctr_entry(ctr))
197 return;
198
199 ctr->table = t;
200 stkctr_set_entry(ctr, ts);
201 stream_start_counters(t, ts);
202 }
203
204 /* Increase the number of cumulated HTTP requests in the tracked counters */
stream_inc_http_req_ctr(struct stream * s)205 static void inline stream_inc_http_req_ctr(struct stream *s)
206 {
207 struct stksess *ts;
208 void *ptr;
209 int i;
210
211 for (i = 0; i < MAX_SESS_STKCTR; i++) {
212 struct stkctr *stkctr = &s->stkctr[i];
213
214 ts = stkctr_entry(stkctr);
215 if (!ts) {
216 stkctr = &s->sess->stkctr[i];
217 ts = stkctr_entry(stkctr);
218 if (!ts)
219 continue;
220 }
221
222 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
223
224 ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_REQ_CNT);
225 if (ptr)
226 stktable_data_cast(ptr, http_req_cnt)++;
227
228 ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_REQ_RATE);
229 if (ptr)
230 update_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
231 stkctr->table->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
232
233 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
234
235 /* If data was modified, we need to touch to re-schedule sync */
236 stktable_touch_local(stkctr->table, ts, 0);
237 }
238 }
239
240 /* Increase the number of cumulated HTTP requests in the backend's tracked
241 * counters. We don't look up the session since it cannot happen in the bakcend.
242 */
stream_inc_be_http_req_ctr(struct stream * s)243 static void inline stream_inc_be_http_req_ctr(struct stream *s)
244 {
245 struct stksess *ts;
246 void *ptr;
247 int i;
248
249 for (i = 0; i < MAX_SESS_STKCTR; i++) {
250 struct stkctr *stkctr = &s->stkctr[i];
251
252 ts = stkctr_entry(stkctr);
253 if (!ts)
254 continue;
255
256 if (!(stkctr_flags(&s->stkctr[i]) & STKCTR_TRACK_BACKEND))
257 continue;
258
259 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
260
261 ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_REQ_CNT);
262 if (ptr)
263 stktable_data_cast(ptr, http_req_cnt)++;
264
265 ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_REQ_RATE);
266 if (ptr)
267 update_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
268 stkctr->table->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
269
270 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
271
272 /* If data was modified, we need to touch to re-schedule sync */
273 stktable_touch_local(stkctr->table, ts, 0);
274 }
275 }
276
277 /* Increase the number of cumulated failed HTTP requests in the tracked
278 * counters. Only 4xx requests should be counted here so that we can
279 * distinguish between errors caused by client behaviour and other ones.
280 * Note that even 404 are interesting because they're generally caused by
281 * vulnerability scans.
282 */
stream_inc_http_err_ctr(struct stream * s)283 static void inline stream_inc_http_err_ctr(struct stream *s)
284 {
285 struct stksess *ts;
286 void *ptr;
287 int i;
288
289 for (i = 0; i < MAX_SESS_STKCTR; i++) {
290 struct stkctr *stkctr = &s->stkctr[i];
291
292 ts = stkctr_entry(stkctr);
293 if (!ts) {
294 stkctr = &s->sess->stkctr[i];
295 ts = stkctr_entry(stkctr);
296 if (!ts)
297 continue;
298 }
299
300 HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
301
302 ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_ERR_CNT);
303 if (ptr)
304 stktable_data_cast(ptr, http_err_cnt)++;
305
306 ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_ERR_RATE);
307 if (ptr)
308 update_freq_ctr_period(&stktable_data_cast(ptr, http_err_rate),
309 stkctr->table->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
310
311 HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
312
313 /* If data was modified, we need to touch to re-schedule sync */
314 stktable_touch_local(stkctr->table, ts, 0);
315 }
316 }
317
__stream_add_srv_conn(struct stream * sess,struct server * srv)318 static void inline __stream_add_srv_conn(struct stream *sess, struct server *srv)
319 {
320 sess->srv_conn = srv;
321 LIST_ADD(&srv->actconns, &sess->by_srv);
322 }
323
stream_add_srv_conn(struct stream * sess,struct server * srv)324 static void inline stream_add_srv_conn(struct stream *sess, struct server *srv)
325 {
326 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
327 __stream_add_srv_conn(sess, srv);
328 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
329 }
330
stream_del_srv_conn(struct stream * sess)331 static void inline stream_del_srv_conn(struct stream *sess)
332 {
333 struct server *srv = sess->srv_conn;
334
335 if (!srv)
336 return;
337
338 HA_SPIN_LOCK(SERVER_LOCK, &srv->lock);
339 sess->srv_conn = NULL;
340 LIST_DEL(&sess->by_srv);
341 HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
342 }
343
stream_init_srv_conn(struct stream * sess)344 static void inline stream_init_srv_conn(struct stream *sess)
345 {
346 sess->srv_conn = NULL;
347 LIST_INIT(&sess->by_srv);
348 }
349
350 void service_keywords_register(struct action_kw_list *kw_list);
351
352 #endif /* _PROTO_STREAM_H */
353
354 /*
355 * Local variables:
356 * c-indent-level: 8
357 * c-basic-offset: 8
358 * End:
359 */
360