/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- acc_ip
- AC_acc_copy_l
- AC_persistence_get_leaves_hook_l
- ac_persistence_get_leaves
- AC_delete_timestamp_l
- AC_persistence_load
- AC_persistence_walk_l
- AC_persistence_save_l
- AC_persistence_save
- AC_persistence_init
- AC_persistence_daemon
1 /***************************************
2 $Revision: 1.7 $
3
4 Access control (AC). ac_persistence.c - functions to make the access
5 control tree persistent.
6
7 Status: NOT REVIEWED, NOT TESTED, COMPLETE
8
9 Implementation by: Tiago Antao
10
11 ******************/ /******************
12 Copyright (c) 2002 RIPE NCC
13
14 All Rights Reserved
15
16 Permission to use, copy, modify, and distribute this software and its
17 documentation for any purpose and without fee is hereby granted,
18 provided that the above copyright notice appear in all copies and that
19 both that copyright notice and this permission notice appear in
20 supporting documentation, and that the name of the author not be
21 used in advertising or publicity pertaining to distribution of the
22 software without specific, written prior permission.
23
24 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
26 AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
27 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
28 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 ***************************************/
31
32 /*
33 ac_persistence - Does access control persistence
34
35 External entry points are:
36
37 AC_persistence_daemon - Persistence thread
38 AC_persistence_save - Saves the current access control tree
39 */
40
41 #include "rip.h"
42 #include <math.h>
43
44 /*
45 Global variables.
46 */
47
48 int ac_auto_save; /* This should be encapsulated. */
49
50 /*
51 Local typedefs.
52 */
53
54
55 /* acc_ip
56 It is needed because we need to know the IP of a acc on a GList context.
57 */
58 typedef struct {
59 acc_st acc;
60 ip_prefix_internal_t ip; /* internal?????*/
61 } acc_ip;
/* [<][>][^][v][top][bottom][index][help] */
62
63
64 /*
65 Local variables.
66 */
67
68 static rw_lock_t save_lock; /* lock for save operation*/
69 static char on_save = 0; /* lack of trylock in TH requires this */
70
71 /*
72 From access_control.c
73 */
74 void AC_decay_leaf_l(acc_st *leaf);
75 char AC_prunable(acc_st *leaf);
76 extern ut_timer_t oldest_timestamp;
77
78 /*
79 AC_acc_copy_l:
80
81 Copies the contents of a acc_st variable.
82
83 maybe it should go to access_control.c
84 */
85 void AC_acc_copy_l(acc_st *dest, acc_st *src) {
/* [<][>][^][v][top][bottom][index][help] */
86 memcpy(dest, src, sizeof(acc_st)); /* This is OK for the current struct data */
87 }
88
89 /*
90 AC_persistence_get_leaves_hook_l:
91
92 Hook for the return of a Glist of leaves. Constructs an acc_ip.
93 */
94 /* static */
95 er_ret_t AC_persistence_get_leaves_hook_l(rx_node_t* node, int level,
/* [<][>][^][v][top][bottom][index][help] */
96 int node_counter, void *con) {
97 GList** list;
98 acc_ip* ip_acc;
99 acc_st* acc;
100
101
102
103 list = (GList**) con;
104 acc = node->leaves_ptr->data;
105 if (acc->changed != AC_ACC_NOT_CHANGED) {
106 ip_acc = UT_malloc(sizeof(acc_ip));
107 AC_acc_copy_l(&(ip_acc->acc), acc);
108
109 acc->changed = AC_ACC_NOT_CHANGED;
110
111 ip_acc->ip = node->prefix;
112
113 *list = g_list_prepend(*list, ip_acc);
114 }
115 return RX_OK;
116 } /* AC_persistence_get_leaves_hook_l */
117
118 /*
119 ac_persistence_get_leaves:
120
121 Returns a GList of leaves (clone, ie don't forget to deallocate elements)
122
123 Write locks act_runtime.
124 */
125 GList* ac_persistence_get_leaves() {
/* [<][>][^][v][top][bottom][index][help] */
126 GList *list;
127 er_ret_t ret_err;
128
129 list = NULL;
130
131 TH_acquire_write_lock(&(act_runtime->rwlock));
132 rx_walk_tree(act_runtime->top_ptr, AC_persistence_get_leaves_hook_l,
133 RX_WALK_SKPGLU, 255, 0, 0, &list, &ret_err);
134 /* dieif(ret_err != RX_OK); -- can be safely ignored as result is always OK*/
135 TH_release_write_lock(&(act_runtime->rwlock));
136
137 return list;
138 } /* ac_persistence_get_leaves */
139
140
141 /*
142 AC_delete_timestamp_l
143 */
144 void AC_delete_timestamp_l(SQ_connection_t* conn) {
/* [<][>][^][v][top][bottom][index][help] */
145 char delete_query[120];
146
147 sprintf(delete_query,
148 "DELETE FROM access "
149 " WHERE timestamp<%11.0f AND addr_passes=0 AND denials=0 ",
150 ceil(UT_time_getvalue(&oldest_timestamp)) - 10);
151 //printf("%s\n", delete_query);
152 SQ_execute_query(conn, delete_query, NULL); //ignoring ?!?
153 }
154
155 /*
156 AC_persistence_load:
157
158 Loads the SQL access control data into the act_runtime tree.
159 Also, clears old leaves from SQL.
160
161 This is different from discussed design!!! Explain:
162
163 This operation blocks the server. It is all in-memory so it should not take too
164 long.
165
166 Pseudo-code:
167
168 connect to admin DB
169 SELECT * FROM access
170 write lock act_runtime
171 for each row
172 creates acc_st and ip_prefix
173 calculates new decay
174 if node still active then
175 adds acc_st/ip_prefix to act_runtime
176 if timestamp<oldest_timestamp
177 oldest_timestamp = timestamp;
178 release lock
179 DELETE FROM access WHERE timestamp<oldest_timestamp
180 close DB
181
182 */
183 void AC_persistence_load(void) {
/* [<][>][^][v][top][bottom][index][help] */
184 SQ_connection_t* sql_conn;
185 SQ_result_set_t* rs;
186 SQ_row_t *row;
187 er_ret_t ret_err = RX_OK;
188 unsigned long timestamp;
189
190
191 sql_conn = AC_dbopen_admin();
192
193 if (SQ_execute_query(sql_conn, "SELECT prefix, prefix_length, connections, "
194 "addr_passes, denials, queries, "
195 "referrals, public_objects, "
196 "private_objects, public_bonus, "
197 "private_bonus, timestamp "
198 "FROM access", &rs) == 0) {
199 TH_acquire_write_lock(&(act_runtime->rwlock));
200
201 while ((row = SQ_row_next(rs)) != NULL && ret_err == RX_OK) {
202 acc_st *acc;
203 ip_prefix_t ip;
204
205 /* Repeat with me: With pre-historic languages you have to do basic
206 memory management. 8-) */
207 acc = UT_malloc(sizeof(acc_st));
208
209 IP_pref_f2b_v4(&ip,
210 SQ_get_column_string_nocopy(rs, row, 0),
211 SQ_get_column_string_nocopy(rs, row, 1));
212
213 /*
214 SQ_get_column_int errors are not detected.
215 In theory it should not be a problem
216 */
217 SQ_get_column_int(rs, row, 2, (long *)&acc->connections);
218 SQ_get_column_int(rs, row, 3, (long *)&acc->addrpasses);
219 SQ_get_column_int(rs, row, 4, (long *)&acc->denials);
220 SQ_get_column_int(rs, row, 5, (long *)&acc->queries);
221 SQ_get_column_int(rs, row, 6, (long *)&acc->referrals);
222 SQ_get_column_int(rs, row, 7, (long *)&acc->public_objects);
223 SQ_get_column_int(rs, row, 8, (long *)&acc->private_objects);
224 acc->public_bonus = atof(SQ_get_column_string_nocopy(rs, row, 9));
225 acc->private_bonus = atof(SQ_get_column_string_nocopy(rs, row, 10));
226 SQ_get_column_int(rs, row, 11, (long*)×tamp);
227
228 UT_time_set(&acc->timestamp, timestamp, 0);
229
230 /* Mark it as not changed */
231 acc->changed = AC_ACC_NOT_CHANGED;
232
233 AC_decay_leaf_l(acc);
234 /* lets be memory efficient and not create if it is decayed */
235 if (AC_prunable(acc)) {
236 UT_free(acc);
237 }
238 else {
239 if (UT_timediff(&acc->timestamp, &oldest_timestamp) > 0 &&
240 acc->denials == 0 && acc->addrpasses == 0) {
241 oldest_timestamp = acc->timestamp;
242 }
243 // what about if it already exists?
244 ret_err = rx_bin_node(RX_OPER_CRE, &ip, act_runtime, (rx_dataleaf_t*) acc);
245 }
246 }
247 // if ret_err...
248
249 SQ_free_result(rs);
250 TH_release_write_lock(&(act_runtime->rwlock));
251 }
252 else {
253 ER_perror(FAC_AC, AC_INTR_ERR, "Couldn't load access table");
254 }
255 AC_delete_timestamp_l(sql_conn);
256 SQ_close_connection(sql_conn);
257
258 } /* AC_persistence_load */
259
260 /*
261 AC_persistence_walk_l:
262
263 Writes each leaf (or not, depending on timestamp & changed).
264
265 Pseudo-code:
266
267 for each element
268 if acc.changed = not changed
269 continue
270 if acc.changed == new
271 insert
272 if insert ok
273 return
274 update (also on special acc.changed cases)
275 if fail log
276
277 */
278 static void AC_persistence_walk_l(GList *list) {
/* [<][>][^][v][top][bottom][index][help] */
279 acc_ip *element;
280 SQ_connection_t *sql_conn;
281 char sql[500];
282 GList *curr_list;
283
284 sql_conn = AC_dbopen_admin();
285 curr_list = list;
286
287 while (curr_list) {
288 element = curr_list->data;
289 curr_list = g_list_next(curr_list);
290 if (element->acc.changed == AC_ACC_NOT_CHANGED ||
291 IP_pref_b2v4_addr(&element->ip) == 0) continue;
292 if (element->acc.changed == AC_ACC_NEW) {
293 sprintf(sql, "INSERT INTO access(prefix, prefix_length, connections, addr_passes, denials, queries, referrals, public_objects, private_objects, public_bonus, private_bonus, timestamp) VALUES(%u, %d, %d, %d, %d, %d, %d, %d, %d, %f, %f, %10.0f)",
294 IP_pref_b2v4_addr(&element->ip),
295 IP_pref_b2_len(&element->ip),
296 element->acc.connections,
297 element->acc.addrpasses,
298 element->acc.denials,
299 element->acc.queries,
300 element->acc.referrals,
301 element->acc.public_objects,
302 element->acc.private_objects,
303 element->acc.public_bonus,
304 element->acc.private_bonus,
305 ceil(UT_time_getvalue(&element->acc.timestamp))
306 );
307 //printf("%s\n", sql);
308 if (SQ_execute_query(sql_conn, sql, NULL) == 0 ) {
309 continue;
310 }
311 /* It is OK to fail, let's wait for UPDATE */
312 }
313 sprintf(sql, "UPDATE access "
314 "SET connections = %d, addr_passes = %d, denials = %d, "
315 "queries = %d, referrals = %d, public_objects = %d, "
316 "private_objects = %d , public_bonus = %f, "
317 "private_bonus = %f, timestamp = %10.0f "
318 "WHERE prefix = %u AND prefix_length = %d",
319 element->acc.connections,
320 element->acc.addrpasses,
321 element->acc.denials,
322 element->acc.queries,
323 element->acc.referrals,
324 element->acc.public_objects,
325 element->acc.private_objects,
326 element->acc.public_bonus,
327 element->acc.private_bonus,
328 ceil(UT_time_getvalue(&element->acc.timestamp)),
329 IP_pref_b2v4_addr(&element->ip),
330 IP_pref_b2_len(&element->ip)
331 );
332 //printf("%s\n", sql);
333 if (SQ_execute_query(sql_conn, sql, NULL) != 0 ) {
334 ER_perror(FAC_AC, AC_INTR_ERR, "Problems with database updates on "
335 "admin database");
336 SQ_close_connection(sql_conn);
337 return;
338 }
339 }
340 AC_delete_timestamp_l(sql_conn);
341 SQ_close_connection(sql_conn);
342 }
343
344
345
346 /*
347 AC_persistence_save_l:
348
349 Saves the act_runtime tree into the SQL access control data table:
350 This is the function that realy does it (not the public
351 AC_persistence_save).
352
353 Pseudo-code:
354
355 gets a list of relevant leaves
356 writes the leaves to SQL
357 destroy the list of leaves
358
359 */
360 static void AC_persistence_save_l(void) {
/* [<][>][^][v][top][bottom][index][help] */
361 GList *leaves;
362 int length;
363 int cont;
364
365 leaves = ac_persistence_get_leaves();
366
367 if (leaves) {
368 AC_persistence_walk_l(leaves);
369
370 /* free GList (&contents!!) */
371 length = g_list_length(leaves);
372 for(cont=0; cont<length; cont++) {
373 UT_free(g_list_nth_data(leaves, cont));
374 }
375 g_list_free(leaves);
376 }
377 } /* AC_persistence_save_l */
378
379
380 /*
381 AC_persistence_save:
382
383 Saves the act_runtime tree into the SQL access control data table:
384 Checks if another thread is saving, if so doesn't save.
385 Saving is done by a call to ac_persistence_save which does the real job.
386
387 Returns AC_OK if done or AC_SAVING in the case of another save process
388 is running.
389
390 Pseudo-code:
391
392 if !(acquire saving rights)
393 return AC_SAVING
394 save
395 release saving rights
396 return AC_OK
397 */
398 er_ret_t AC_persistence_save(void) {
/* [<][>][^][v][top][bottom][index][help] */
399
400
401 /* Let's see if a save is already running... */
402 TH_acquire_write_lock(&save_lock);
403 /* Save already running*/
404 if (on_save) {
405 TH_release_write_lock(&save_lock);
406 return AC_SAVING;
407 }
408 /* else ...
409 We have a go!
410 */
411 on_save = 1;
412 TH_release_write_lock(&save_lock);
413
414 AC_persistence_save_l();
415
416 /* Signal end of save operation */
417 TH_acquire_write_lock(&save_lock);
418 on_save = 0;
419 TH_release_write_lock(&save_lock);
420
421 return AC_OK;
422
423 } /* AC_persistence save */
424
425
426 /*
427 AC_persistence_init:
428
429 Initializes the persistence system:
430
431 1. Initializes the dump control mutex.
432 2. Initializes the oldest_timestamp.
433 3. If required by user loads SQL accounting data.
434 4. Sets auto save.
435 */
436 er_ret_t AC_persistence_init(void) {
/* [<][>][^][v][top][bottom][index][help] */
437 TH_init_read_write_lock(&save_lock);
438
439 UT_timeget(&oldest_timestamp);
440
441 if (ca_get_ac_load) {
442 AC_persistence_load();
443 }
444
445 ac_auto_save = ca_get_ac_auto_save;
446 return AC_OK;
447 } /* AC_persistence_init */
448
449
450 /*
451 AC_persistence_daemon:
452
453 Implements the top-level thread code:
454
455 Pseudo-code:
456
457 while (CO_get_do_server)
458 Sleep.
459 If auto save
460 Dumps the current version of the act_runtime tree to the persistence
461 store.
462 */
463 er_ret_t AC_persistence_daemon(void) {
/* [<][>][^][v][top][bottom][index][help] */
464
465 TA_add(0, "persistence");
466
467 while(CO_get_do_server()) {
468 SV_sleep(ca_get_ac_save_interval);
469 if (ac_auto_save) {
470 AC_persistence_save();
471 }
472 }
473
474 TA_delete();
475
476 return AC_OK;
477 } /* AC_persistence_daemon */