Source File ac/ac_persistence.c

    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;
    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) {
    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,
    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() {
    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) {
    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) {
    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) {
    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) {
    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) {
    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) {
    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) {
    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 */