## modules/ac/ac_persistence.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following functions.

1. acc_ip
2. AC_acc_copy_l
3. AC_persistence_get_leaves_hook_l
4. ac_persistence_get_leaves
5. AC_delete_timestamp_l
6. AC_persistence_load
7. AC_persistence_walk_l
8. AC_persistence_save_l
9. AC_persistence_save
10. AC_persistence_init
11. 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*)&timestamp);
     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 */

/* [<][>][^][v][top][bottom][index][help] */