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