blob: bc75f67f8ace062b685bc577532d0e72ff5e7d21 [file] [log] [blame]
Emeric Brun3bd697e2010-01-04 15:23:48 +01001/*
2 * Stick tables management functions.
3 *
4 * Copyright 2009-2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <string.h>
14
15#include <common/config.h>
16#include <common/memory.h>
17#include <common/mini-clist.h>
18#include <common/standard.h>
19#include <common/time.h>
20
21#include <ebmbtree.h>
22#include <ebsttree.h>
23
24#include <types/stick_table.h>
25
26#include <proto/proxy.h>
27#include <proto/session.h>
28#include <proto/task.h>
29
30
31/*
Willy Tarreauaea940e2010-06-06 11:56:36 +020032 * Free an allocated sticky session <ts>, and decrease sticky sessions counter
33 * in table <t>.
Emeric Brun3bd697e2010-01-04 15:23:48 +010034 */
35void stksess_free(struct stktable *t, struct stksess *ts)
36{
37 t->current--;
Willy Tarreauaea940e2010-06-06 11:56:36 +020038 pool_free2(t->pool, ts);
Emeric Brun3bd697e2010-01-04 15:23:48 +010039}
40
41/*
Willy Tarreauaea940e2010-06-06 11:56:36 +020042 * Initialize or update the key in the sticky session <ts> present in table <t>
43 * from the value present in <key>.
Emeric Brun3bd697e2010-01-04 15:23:48 +010044 */
45void stksess_key(struct stktable *t, struct stksess *ts, struct stktable_key *key)
46{
47 if (t->type != STKTABLE_TYPE_STRING)
Willy Tarreauaea940e2010-06-06 11:56:36 +020048 memcpy(ts->keys.key, key->key, t->key_size);
Emeric Brun3bd697e2010-01-04 15:23:48 +010049 else {
50 memcpy(ts->keys.key, key->key, MIN(t->key_size - 1, key->key_len));
51 ts->keys.key[MIN(t->key_size - 1, key->key_len)] = 0;
52 }
53}
54
55
56/*
Willy Tarreauaea940e2010-06-06 11:56:36 +020057 * Init sticky session <ts> of table <t> using <key>.
Emeric Brun3bd697e2010-01-04 15:23:48 +010058 */
59struct stksess *stksess_init(struct stktable *t, struct stksess * ts, struct stktable_key *key)
60{
61 ts->keys.node.leaf_p = NULL;
62 ts->exps.node.leaf_p = NULL;
63 ts->sid = 0;
64 stksess_key(t, ts, key);
65
66 return ts;
67}
68
69/*
Willy Tarreauaea940e2010-06-06 11:56:36 +020070 * Trash oldest <to_batch> sticky sessions from table <t>
71 * Returns number of trashed sticky sessions.
Emeric Brun3bd697e2010-01-04 15:23:48 +010072 */
73static int stktable_trash_oldest(struct stktable *t, int to_batch)
74{
75 struct stksess *ts;
76 struct eb32_node *eb;
77 int batched = 0;
78
79 eb = eb32_lookup_ge(&t->exps, now_ms - TIMER_LOOK_BACK);
80
81 while (batched < to_batch) {
82
83 if (unlikely(!eb)) {
84 /* we might have reached the end of the tree, typically because
85 * <now_ms> is in the first half and we're first scanning the last
86 * half. Let's loop back to the beginning of the tree now.
87 */
88 eb = eb32_first(&t->exps);
89 if (likely(!eb))
90 break;
91 }
92
93 /* timer looks expired, detach it from the queue */
94 ts = eb32_entry(eb, struct stksess, exps);
95 eb = eb32_next(eb);
96
97 eb32_delete(&ts->exps);
98
99 if (ts->expire != ts->exps.key) {
100
101 if (!tick_isset(ts->expire))
102 continue;
103
104 ts->exps.key = ts->expire;
105
106 eb32_insert(&t->exps, &ts->exps);
107
108 if (!eb || eb->key > ts->exps.key)
109 eb = &ts->exps;
110
111 continue;
112 }
Emeric Brun3bd697e2010-01-04 15:23:48 +0100113
Willy Tarreauaea940e2010-06-06 11:56:36 +0200114 /* session expired, trash it */
Emeric Brun3bd697e2010-01-04 15:23:48 +0100115 ebmb_delete(&ts->keys);
116 stksess_free(t, ts);
117 batched++;
118 }
119
120 return batched;
121}
122
123/*
Willy Tarreauaea940e2010-06-06 11:56:36 +0200124 * Allocate and initialise a new sticky session.
125 * The new sticky session is returned or NULL in case of lack of memory.
126 * Sticky sessions should only be allocated this way, and must be freed using
127 * stksess_free(). Increase table <t> sticky session counter.
Emeric Brun3bd697e2010-01-04 15:23:48 +0100128 */
129struct stksess *stksess_new(struct stktable *t, struct stktable_key *key)
130{
131 struct stksess *ts;
132
133 if (unlikely(t->current == t->size)) {
134 if ( t->nopurge )
135 return NULL;
136
137 if (!stktable_trash_oldest(t, t->size >> 8))
138 return NULL;
139 }
140
141 ts = pool_alloc2(t->pool);
142 if (ts) {
143 t->current++;
144 stksess_init(t, ts, key);
145 }
146
147 return ts;
148}
149
150/*
Willy Tarreauaea940e2010-06-06 11:56:36 +0200151 * Looks in table <t> for a sticky session matching <key>.
152 * Returns pointer on requested sticky session or NULL if none was found.
Emeric Brun3bd697e2010-01-04 15:23:48 +0100153 */
154struct stksess *stktable_lookup(struct stktable *t, struct stktable_key *key)
155{
156 struct ebmb_node *eb;
157
Emeric Brun3bd697e2010-01-04 15:23:48 +0100158 if (t->type == STKTABLE_TYPE_STRING)
159 eb = ebst_lookup_len(&t->keys, key->key, key->key_len);
160 else
161 eb = ebmb_lookup(&t->keys, key->key, t->key_size);
162
163 if (unlikely(!eb)) {
164 /* no session found */
165 return NULL;
166 }
167
Emeric Brun3bd697e2010-01-04 15:23:48 +0100168 return ebmb_entry(eb, struct stksess, keys);
169}
170
Willy Tarreauaea940e2010-06-06 11:56:36 +0200171/* Try to store sticky session <ts> in the table. If another entry already
172 * exists with the same key, its server ID is updated with <sid> and a non
173 * zero value is returned so that the caller knows it can release its stksess.
174 * If no similar entry was present, <ts> is inserted into the tree and assigned
175 * server ID <sid>. Zero is returned in this case, and the caller must not
176 * release the stksess.
Emeric Brun3bd697e2010-01-04 15:23:48 +0100177 */
Willy Tarreauaea940e2010-06-06 11:56:36 +0200178int stktable_store(struct stktable *t, struct stksess *ts, int sid)
Emeric Brun3bd697e2010-01-04 15:23:48 +0100179{
Emeric Brun3bd697e2010-01-04 15:23:48 +0100180 struct ebmb_node *eb;
181
182 if (t->type == STKTABLE_TYPE_STRING)
Willy Tarreauaea940e2010-06-06 11:56:36 +0200183 eb = ebst_lookup(&(t->keys), (char *)ts->keys.key);
Emeric Brun3bd697e2010-01-04 15:23:48 +0100184 else
Willy Tarreauaea940e2010-06-06 11:56:36 +0200185 eb = ebmb_lookup(&(t->keys), ts->keys.key, t->key_size);
Emeric Brun3bd697e2010-01-04 15:23:48 +0100186
187 if (unlikely(!eb)) {
Willy Tarreauaea940e2010-06-06 11:56:36 +0200188 /* no existing session, insert ours */
189 ts->sid = sid;
190 ebmb_insert(&t->keys, &ts->keys, t->key_size);
Emeric Brun3bd697e2010-01-04 15:23:48 +0100191
Willy Tarreauaea940e2010-06-06 11:56:36 +0200192 ts->exps.key = ts->expire = tick_add(now_ms, MS_TO_TICKS(t->expire));
193 eb32_insert(&t->exps, &ts->exps);
Emeric Brun3bd697e2010-01-04 15:23:48 +0100194
195 if (t->expire) {
Willy Tarreauaea940e2010-06-06 11:56:36 +0200196 t->exp_task->expire = t->exp_next = tick_first(ts->expire, t->exp_next);
Emeric Brun3bd697e2010-01-04 15:23:48 +0100197 task_queue(t->exp_task);
198 }
199 return 0;
200 }
201
Emeric Brun3bd697e2010-01-04 15:23:48 +0100202 ts = ebmb_entry(eb, struct stksess, keys);
203
204 if ( ts->sid != sid )
205 ts->sid = sid;
206 return 1;
207}
208
209/*
Willy Tarreauaea940e2010-06-06 11:56:36 +0200210 * Trash expired sticky sessions from table <t>. The next expiration date is
211 * returned.
Emeric Brun3bd697e2010-01-04 15:23:48 +0100212 */
213static int stktable_trash_expired(struct stktable *t)
214{
215 struct stksess *ts;
216 struct eb32_node *eb;
217
218 eb = eb32_lookup_ge(&t->exps, now_ms - TIMER_LOOK_BACK);
219
220 while (1) {
221 if (unlikely(!eb)) {
222 /* we might have reached the end of the tree, typically because
223 * <now_ms> is in the first half and we're first scanning the last
224 * half. Let's loop back to the beginning of the tree now.
225 */
226 eb = eb32_first(&t->exps);
227 if (likely(!eb))
228 break;
229 }
230
231 if (likely(tick_is_lt(now_ms, eb->key))) {
232 /* timer not expired yet, revisit it later */
233 t->exp_next = eb->key;
234 return t->exp_next;
235 }
236
237 /* timer looks expired, detach it from the queue */
238 ts = eb32_entry(eb, struct stksess, exps);
239 eb = eb32_next(eb);
240
241 eb32_delete(&ts->exps);
242
243 if (!tick_is_expired(ts->expire, now_ms)) {
244 if (!tick_isset(ts->expire))
245 continue;
246
247 ts->exps.key = ts->expire;
248 eb32_insert(&t->exps, &ts->exps);
249
250 if (!eb || eb->key > ts->exps.key)
251 eb = &ts->exps;
252 continue;
253 }
254
255 /* session expired, trash it */
256 ebmb_delete(&ts->keys);
257 stksess_free(t, ts);
258 }
259
260 /* We have found no task to expire in any tree */
261 t->exp_next = TICK_ETERNITY;
262 return t->exp_next;
263}
264
265/*
Willy Tarreauaea940e2010-06-06 11:56:36 +0200266 * Task processing function to trash expired sticky sessions. A pointer to the
267 * task itself is returned since it never dies.
Emeric Brun3bd697e2010-01-04 15:23:48 +0100268 */
Willy Tarreauaea940e2010-06-06 11:56:36 +0200269static struct task *process_table_expire(struct task *task)
Emeric Brun3bd697e2010-01-04 15:23:48 +0100270{
271 struct stktable *t = (struct stktable *)task->context;
272
273 task->expire = stktable_trash_expired(t);
274 return task;
275}
276
Willy Tarreauaea940e2010-06-06 11:56:36 +0200277/* Perform minimal stick table intializations, report 0 in case of error, 1 if OK. */
Emeric Brun3bd697e2010-01-04 15:23:48 +0100278int stktable_init(struct stktable *t)
279{
280 if (t->size) {
281 memset(&t->keys, 0, sizeof(t->keys));
282 memset(&t->exps, 0, sizeof(t->exps));
283
284 t->pool = create_pool("sticktables", sizeof(struct stksess) + t->key_size, MEM_F_SHARED);
285
286 t->exp_next = TICK_ETERNITY;
287 if ( t->expire ) {
288 t->exp_task = task_new();
289 t->exp_task->process = process_table_expire;
290 t->exp_task->expire = TICK_ETERNITY;
291 t->exp_task->context = (void *)t;
292 }
293 return t->pool != NULL;
294 }
295 return 1;
296}
297
298/*
299 * Configuration keywords of known table types
300 */
301struct stktable_type stktable_types[STKTABLE_TYPES] = { { "ip", 0, 4 } ,
302 { "integer", 0, 4 },
Willy Tarreauaea940e2010-06-06 11:56:36 +0200303 { "string", STK_F_CUSTOM_KEYSIZE, 32 } };
Emeric Brun3bd697e2010-01-04 15:23:48 +0100304
305
306/*
307 * Parse table type configuration.
308 * Returns 0 on successful parsing, else 1.
309 * <myidx> is set at next configuration <args> index.
310 */
311int stktable_parse_type(char **args, int *myidx, unsigned long *type, size_t *key_size)
312{
313 for (*type = 0; *type < STKTABLE_TYPES; (*type)++) {
314 if (strcmp(args[*myidx], stktable_types[*type].kw) != 0)
315 continue;
316
317 *key_size = stktable_types[*type].default_size;
318 (*myidx)++;
319
Willy Tarreauaea940e2010-06-06 11:56:36 +0200320 if (stktable_types[*type].flags & STK_F_CUSTOM_KEYSIZE) {
Emeric Brun3bd697e2010-01-04 15:23:48 +0100321 if (strcmp("len", args[*myidx]) == 0) {
322 (*myidx)++;
323 *key_size = atol(args[*myidx]);
324 if ( !*key_size )
325 break;
326 /* null terminated string needs +1 for '\0'. */
327 (*key_size)++;
328 (*myidx)++;
329 }
330 }
331 return 0;
332 }
333 return 1;
334}
335
336