Source File ac/access_control.c

    1    | /***************************************
    2    |   $Revision: 1.47 $
    3    |
    4    |   Access control module (ac) - access control for the query part
    5    |
    6    |   Status: NOT REVIEWED, TESTED
    7    |
    8    |   Design and implementation by: Marek Bukowy
    9    |
    10   |   ******************/ /******************
    11   |   Copyright (c) 1999,2000,2001,2002                         RIPE NCC
    12   |
    13   |   All Rights Reserved
    14   |
    15   |   Permission to use, copy, modify, and distribute this software and its
    16   |   documentation for any purpose and without fee is hereby granted,
    17   |   provided that the above copyright notice appear in all copies and that
    18   |   both that copyright notice and this permission notice appear in
    19   |   supporting documentation, and that the name of the author not be
    20   |   used in advertising or publicity pertaining to distribution of the
    21   |   software without specific, written prior permission.
    22   |
    23   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    24   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    25   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    26   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    27   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    28   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    29   |   ***************************************/
    30   |
    31   | /*
    32   |    test excercises:
    33   |
    34   |    1. add a function to delete an entry from the acl table,
    35   |       it should be called from the pc module.
    36   |
    37   | */
    38   |
    39   | #define AC_IMPL
    40   | #include "rip.h"
    41   |
    42   | #include <stdio.h>
    43   | #include <glib.h>
    44   | #include <string.h>
    45   | #include <math.h>
    46   |
    47   | #include <unistd.h>
    48   | #include <stdlib.h>
    49   |
    50   | extern char *suboptarg;
    51   | extern int getsubopt(char **optionp, char * const *tokens, char **valuep);
    52   |
    53   | /* formats for printing the access control list entries */
    54   | #define ACL_FORMAT        "%10d %10d %10d %10d %10d"
    55   | #define ACL_HEADER  "%-20s %10s %10s %10s %10s %10s\n"
    56   |
    57   | /* formats for printing the accounting entries */
    58   | #define ACC_FORMAT       "%4d %4d %4d %4d %7d %7d %7d %7.2f %7.1f %10.0f"
    59   | #define ACC_HEADER "%-20s %4s %4s %4s %4s %7s %7s %7s %7s %7s %s\n"
    60   |
    61   |
    62   | typedef struct {
    63   | /*  double decay_factor;*/
    64   |   unsigned newtotal;
    65   |   GList *prunelist;
    66   | } ac_decay_data_t;
    67   |
    68   | ut_timer_t oldest_timestamp;
    69   |
    70   | /*++++++++++++++++++++++++++++++++++++++
    71   |   ac_to_string_header:
    72   |
    73   |   produce a header for the access stats printout
    74   |
    75   |   returns an allocated string
    76   |   ++++++++++++++++++++++++++++++++++++++*/
    77   | static
    78   | char *ac_to_string_header(void)
    79   | {
    80   |   char *result_buf;
    81   |
    82   |   result_buf = UT_malloc(256);
    83   |
    84   |   sprintf(result_buf, ACC_HEADER,
    85   | 	  "ip", "conn", "pass", "deny", "qry", "refs", "priv_o", "pub_o", "priv_b","pub_b", "ts");
    86   |
    87   |   return result_buf;
    88   | }
    89   |
    90   | /*++++++++++++++++++++++++++++++++++++++
    91   |   ac_to_string:
    92   |
    93   |   Show an access structure
    94   |
    95   |   returns an allocated string
    96   |   ++++++++++++++++++++++++++++++++++++++*/
    97   | static
    98   | char *ac_to_string(GList *leafptr)
    99   | {
    100  |   char *result_buf;
    101  |   acc_st *a = leafptr->data;
    102  |
    103  |   result_buf = UT_malloc(256);
    104  |
    105  |   if( a == NULL ) {
    106  |     strcpy(result_buf, "DATA MISSING!");
    107  |   }
    108  |   else {
    109  |     sprintf(result_buf,  ACC_FORMAT,
    110  |             a->connections,
    111  | 	    a->addrpasses,
    112  |             a->denials,
    113  |             a->queries,
    114  | 	    a->referrals,
    115  |             a->private_objects,
    116  |             a->public_objects,
    117  |             a->private_bonus,
    118  | 	    a->public_bonus,
    119  |             UT_time_getvalue(&a->timestamp)
    120  |             );
    121  |   }
    122  |
    123  |   return result_buf;
    124  | } /* ac_to_string() */
    125  |
    126  |
    127  | /*++++++++++++++++++++++++++++++++++++++
    128  |   AC_credit_to_string:
    129  |
    130  |  Show credit used (for logging of queries)
    131  |
    132  |  acc_st *a     - the credit structure
    133  |
    134  |  returns an allocated string
    135  |  ++++++++++++++++++++++++++++++++++++++*/
    136  | char *AC_credit_to_string(acc_st *a)
    137  | {
    138  |   char *result_buf;
    139  |
    140  |   result_buf = UT_malloc(64);
    141  |
    142  |   dieif( a == NULL );
    143  |
    144  |   sprintf(result_buf,"%d+%d+%d%s",
    145  | 	  a->private_objects,
    146  | 	  a->public_objects,
    147  | 	  a->referrals,
    148  | 	  a->denials ? " **DENIED**" : ""
    149  | 	  );
    150  |
    151  |   return result_buf;
    152  | } /* AC_credit_to_string */
    153  |
    154  |
    155  | /*+++++++++++++++++++++++++++++++++++++++
    156  |   ac_acl_to_string_header:
    157  |
    158  |   produce a header for the acl printout
    159  |
    160  |   returns an allocated string
    161  |   ++++++++++++++++++++++++++++++++++++++*/
    162  | static char *
    163  | ac_acl_to_string_header(void)
    164  | {
    165  |   char *result_buf;
    166  |
    167  |   result_buf = UT_malloc(256);
    168  |
    169  |   sprintf(result_buf, ACL_HEADER, "ip",
    170  | 	  /* the names must match those in AC_ar_acl, so just take
    171  | 	   them from there */
    172  | 	  AC_ar_acl[AC_AR_MAXPRIVATE],
    173  | 	  AC_ar_acl[AC_AR_MAXPUBLIC],
    174  | 	  AC_ar_acl[AC_AR_MAXDENIALS],
    175  | 	  AC_ar_acl[AC_AR_DENY],
    176  | 	  AC_ar_acl[AC_AR_TRUSTPASS]
    177  | 	  );
    178  |
    179  |
    180  |   return result_buf;
    181  | }
    182  |
    183  |
    184  |
    185  | /*++++++++++++++++++++++++++++++++++++++
    186  |   ac_acl_to_string:
    187  |
    188  |   Show an access control list structure
    189  |
    190  |   returns an allocated string
    191  |   ++++++++++++++++++++++++++++++++++++++*/
    192  | static
    193  | char *ac_acl_to_string(GList *leafptr)
    194  | {
    195  |   char *result_buf;
    196  |   acl_st *a = leafptr->data;
    197  |
    198  |   result_buf = UT_malloc(256);
    199  |
    200  |   if( a != NULL ) {
    201  |     sprintf(result_buf, ACL_FORMAT,
    202  |             a->maxprivate,
    203  | 	    a->maxpublic,
    204  | 	    a->maxdenials,
    205  |             a->deny,
    206  |             a->trustpass
    207  |             );
    208  |   }
    209  |   else {
    210  |     strcpy(result_buf, "DATA MISSING\n");
    211  |   }
    212  |
    213  |   return result_buf;
    214  | } /* ac_acl_to_string() */
    215  |
    216  |
    217  | /*+++++++++++++++++++++++++++++++++++++++
    218  |   ac_find_acl_l:
    219  |
    220  |   find the exact or exact/less specific match for the given prefix in the acl tree.
    221  |
    222  |   ip_prefix_t *prefix - prefix to look for
    223  |
    224  |   acl_st *store_acl   - pointer to store the output
    225  |
    226  |   returns error code from RX or OK
    227  |
    228  |   MT-Note: assumes locked acl tree
    229  |   ++++++++++++++++++++++++++++++++++++++*/
    230  | static er_ret_t
    231  | ac_find_acl_l(rx_srch_mt searchmode, ip_prefix_t *prefix, acl_st *store_acl)
    232  | {
    233  |   GList       *datlist=NULL;
    234  |   er_ret_t    ret_err;
    235  |   rx_datref_t *datref;
    236  |
    237  |   /* accept only RX_SRCH_EXLESS | RX_SRCH_EXACT modes */
    238  |   dieif( searchmode != RX_SRCH_EXLESS && searchmode != RX_SRCH_EXACT);
    239  |
    240  |   /* it must work */
    241  |   dieif( (ret_err = RX_bin_search(searchmode, 0, 0, act_acl,
    242  |                                prefix, &datlist, RX_ANS_ALL)
    243  |        ) != RX_OK );
    244  |   /* In exless mode, something must be found or the acl tree is not
    245  |      configured at all !
    246  |      There always must be a catch-all record with defaults */
    247  |   dieif( searchmode == RX_SRCH_EXLESS && g_list_length(datlist) == 0 );
    248  |
    249  |
    250  |   datref = (rx_datref_t *)g_list_nth_data(datlist,0);
    251  |
    252  |   *store_acl = * ((acl_st *)  datref->leafptr);
    253  |
    254  |   wr_clear_list( &datlist );
    255  |
    256  | #if 0
    257  |   /* XXX dbg checking tree consistency */
    258  |   {
    259  |     rx_treecheck_t errorfound;
    260  |     er_ret_t err;
    261  |     if( (err=RX_treecheck(act_acl, 1, &errorfound)) != RX_OK ) {
    262  |       fprintf(stderr, "Nope! %d returned \n", err);
    263  |       die;
    264  |     }
    265  |   }
    266  | #endif
    267  |
    268  |   return ret_err;
    269  | }
    270  | /* ac_find_acl_l */
    271  |
    272  |
    273  | /*+++++++++++++++++++++++++++++++++++++++
    274  |   AC_findcreate_acl_l:
    275  |
    276  |   find or create an entry for the given prefix in the acl tree.
    277  |
    278  |   ip_prefix_t *prefix - prefix to look for
    279  |
    280  |   acl_st **store_acl  - pointer to store the ptr to the acl struct
    281  |                         (initialised to the values of the parent entry
    282  | 			if just created)
    283  |
    284  |   returns error code from RX or OK
    285  |
    286  |   MT-Note: assumes locked acl tree
    287  |   ++++++++++++++++++++++++++++++++++++++*/
    288  | er_ret_t
    289  | AC_findcreate_acl_l(ip_prefix_t *prefix, acl_st **store_acl)
    290  | {
    291  |   GList       *datlist=NULL;
    292  |   er_ret_t    ret_err;
    293  |   acl_st      *newacl;
    294  |   acl_st acl_copy;
    295  |
    296  |   if( NOERR(ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, act_acl,
    297  | 				    prefix, &datlist, RX_ANS_ALL)
    298  | 	    )) {
    299  |
    300  |     switch( g_list_length(datlist)) {
    301  |     case 0:
    302  |       newacl = UT_calloc(sizeof(acl_st), 1);
    303  |
    304  |       /* make the new one inherit all parameters after the old one */
    305  |
    306  |       ac_find_acl_l(RX_SRCH_EXLESS, prefix, &acl_copy);
    307  |
    308  |       *newacl = acl_copy;
    309  |
    310  |       /* link in */
    311  |       rx_bin_node(RX_OPER_CRE, prefix, act_acl, (rx_dataleaf_t *)newacl);
    312  |       break;
    313  |     case 1:
    314  |       {
    315  | 	/* Uh-oh, the guy is already known ! (or special, in any case) */
    316  | 	rx_datref_t *datref = (rx_datref_t *)g_list_nth_data(datlist,0);
    317  | 	newacl = (acl_st *) datref->leafptr;
    318  |       }
    319  |       break;
    320  |     default:
    321  |       die;
    322  |     }
    323  |   }
    324  |
    325  |   /* free search results */
    326  |   wr_clear_list( &datlist );
    327  |
    328  |   /* store */
    329  |   *store_acl = newacl;
    330  |   return ret_err;
    331  | }
    332  | /* AC_findcreate_acl_l */
    333  |
    334  |
    335  | /*+++++++++++++++++++++++++++++++++++++++
    336  |   AC_findcreate_account_l:
    337  |
    338  |   finds exact prefix in the accounting tree
    339  |   or creates area initialised to zeros + sets ptr to it.
    340  |
    341  |   rx_tree_t *tree     - the tree
    342  |
    343  |   ip_prefix_t *prefix - prefix to look for
    344  |
    345  |   acc_st **store_acl  - pointer to store the ptr to the account struct
    346  |
    347  |   returns error code from RX or OK
    348  |
    349  |   MT-Note: assumes locked accounting tree
    350  |   ++++++++++++++++++++++++++++++++++++++*/
    351  | er_ret_t
    352  | AC_findcreate_account_l(rx_tree_t *tree, ip_prefix_t *prefix,
    353  | 			acc_st **acc_store)
    354  | {
    355  |   GList       *datlist=NULL;
    356  |   er_ret_t    ret_err;
    357  |   acc_st      *recacc;
    358  |
    359  |   if( (ret_err = RX_bin_search(RX_SRCH_EXACT, 0, 0, tree,
    360  |                                prefix, &datlist, RX_ANS_ALL)) == RX_OK ) {
    361  |     switch( g_list_length(datlist) ) {
    362  |     case 0:
    363  |       /* need to create a new accounting record */
    364  |       recacc = UT_malloc(sizeof(acc_st));
    365  |
    366  |       /*  counters = init to zeros */
    367  |       memset( recacc, 0, sizeof(acc_st));
    368  |
    369  |       recacc->changed = AC_ACC_NEW;
    370  |
    371  |       /* attach. The recacc is to be treated as a dataleaf
    372  |         (must use lower levels than RX_asc_*)
    373  |       */
    374  |       ret_err = rx_bin_node( RX_OPER_CRE, prefix,
    375  |                              act_runtime, (rx_dataleaf_t *)recacc );
    376  |       if (ret_err != RX_OK) {
    377  |         ER_perror(FAC_AC, AC_INTR_ERR, "rx_bin_node() returned %d", ret_err);
    378  |       }
    379  |       break;
    380  |     case 1:
    381  |       {
    382  |         rx_datref_t *datref = (rx_datref_t *) g_list_nth_data( datlist,0 );
    383  |         /* OK, there is a record already */
    384  |         recacc = (acc_st *) datref->leafptr;
    385  |
    386  |       }
    387  |       break;
    388  |     default: die; /* there shouldn't be more than 1 entry per IP */
    389  |     }
    390  |   } else {
    391  |     ER_perror(FAC_AC, AC_INTR_ERR, "RX_bin_search() returned %d", ret_err);
    392  |   }
    393  |
    394  |   wr_clear_list( &datlist );
    395  |
    396  |   *acc_store = recacc;
    397  |
    398  | #if 0
    399  |   /* XXX dbg checking tree consistency */
    400  |     if( act_runtime->top_ptr != NULL ) {
    401  |       rx_treecheck_t errorfound;
    402  |       er_ret_t err;
    403  |       if( (err=RX_treecheck(act_runtime, 1, &errorfound)) != RX_OK ) {
    404  | 	fprintf(stderr, "Nope! %d returned \n", errorfound);
    405  | 	ER_dbg_va( FAC_AC, ASP_AC_DECAY,
    406  | 		   "AC: checking access tree consistency: error %d",
    407  | 		   errorfound);
    408  | 	die; /* access tree not consistent */
    409  |       }
    410  |     }
    411  | #endif
    412  |
    413  |   return ret_err;
    414  | }
    415  |
    416  |
    417  | /*++++++++++++++++++++++++++++++++++++++
    418  |   AC_fetch_acc:
    419  |
    420  |   Finds the runtime accounting record for this IP,
    421  |   stores a copy of it in acc_store.
    422  |   If not found, then it is created and initialised to zeros in findcreate()
    423  |
    424  |   ip_addr_t *addr  - address
    425  |
    426  |   acc_st *acc_store - pointer to store the account struct
    427  |
    428  |   MT-Note: locks/unlocks the accounting tree
    429  |   ++++++++++++++++++++++++++++++++++++++*/
    430  | er_ret_t AC_fetch_acc( ip_addr_t *addr, acc_st *acc_store)
    431  | {
    432  |   er_ret_t ret_err;
    433  |   ip_prefix_t prefix;
    434  |   acc_st *ac_ptr;
    435  |
    436  |   prefix.ip = *addr;
    437  |   prefix.bits = IP_sizebits(addr->space);
    438  |
    439  |   TH_acquire_write_lock( &(act_runtime->rwlock) );
    440  |
    441  |   ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
    442  |   *acc_store = *ac_ptr;
    443  |
    444  |   TH_release_write_lock( &(act_runtime->rwlock) );
    445  |
    446  |   return ret_err;
    447  | }/* AC_fetch_acc() */
    448  |
    449  |
    450  | /*++++++++++++++++++++++++++++++++++++++
    451  |   AC_check_acl:
    452  |
    453  |   search for this ip or less specific record in the access control tree
    454  |
    455  |   if( bonus in combined runtime+connection accountings > max_bonus in acl)
    456  |             set denial in the acl for this ip (create if needed)
    457  |   if( combined denialcounter > max_denials in acl)
    458  |             set the permanent ban in acl; save in SQL too
    459  |   calculate credit if pointer provided
    460  |   save the access record (ip if created or found/prefix otherwise)
    461  |             at *acl_store if provided
    462  |
    463  |   ip_addr_t *addr  - address
    464  |
    465  |   acc_st *acc_store - pointer to store the *credit* account struct
    466  |
    467  |   acl_st *acl_store - pointer to store the acl struct
    468  |
    469  |   any of the args except address can be NULL
    470  |
    471  |   returns error code from RX or OK
    472  |
    473  |   MT-Note: locks/unlocks the accounting tree
    474  |   ++++++++++++++++++++++++++++++++++++++*/
    475  | er_ret_t AC_check_acl( ip_addr_t *addr,
    476  |                        acc_st *credit_acc,
    477  |                        acl_st *acl_store
    478  |                        )
    479  | {
    480  |   ip_prefix_t prefix;
    481  |   er_ret_t    ret_err = AC_OK;
    482  |   acl_st      acl_record;
    483  |   acc_st      run_acc;
    484  |
    485  |   AC_fetch_acc( addr, &run_acc );
    486  |
    487  |   prefix.ip = *addr;
    488  |   prefix.bits = IP_sizebits(addr->space);
    489  |
    490  |   /* lock the tree accordingly */
    491  |   TH_acquire_read_lock( &(act_acl->rwlock) );
    492  |
    493  |   /* find an applicable record */
    494  |   ac_find_acl_l(RX_SRCH_EXLESS, &prefix, &acl_record);
    495  |
    496  |   /* calculate the credit if pointer given */
    497  |   if( credit_acc ) {
    498  |     memset( credit_acc, 0, sizeof(acc_st));
    499  |
    500  |     /* credit = -1 if unlimited, otherwise credit = limit - bonus */
    501  |     credit_acc->public_objects =
    502  |       ( acl_record.maxpublic == -1 )
    503  |       ? -1 /* -1 == unlimited */
    504  |       : (acl_record.maxpublic - run_acc.public_bonus);
    505  |
    506  |     credit_acc->private_objects =
    507  |       ( acl_record.maxprivate == -1 )
    508  |       ? -1 /* -1 == unlimited */
    509  |       : (acl_record.maxprivate - run_acc.private_bonus);
    510  |   }
    511  |
    512  |   /* copy the acl record if asked for it*/
    513  |   if( acl_store ) {
    514  |     *acl_store =  acl_record;
    515  |   }
    516  |
    517  |   /* release lock */
    518  |   TH_release_read_lock( &(act_acl->rwlock) );
    519  |
    520  |
    521  |   return ret_err;
    522  | }
    523  |
    524  |
    525  | void AC_decay_leaf_l(acc_st *leaf) {
    526  |   double factor;
    527  |   ut_timer_t current;
    528  |   float time_diff;
    529  |
    530  |   UT_timeget(&current);
    531  |   time_diff = UT_timediff(&leaf->timestamp, &current);
    532  |   if (UT_time_getvalue(&leaf->timestamp) > 1 && abs(time_diff) > 0.2) {
    533  |     factor = exp (-0.693147180559945 * time_diff / ca_get_ac_decay_halflife);
    534  |     leaf->private_bonus *= factor;
    535  |     leaf->public_bonus  *= factor;
    536  |   }
    537  | }
    538  |
    539  | /*++++++++++++++++++++++++++++++++++++++
    540  |   AC_acc_addup:
    541  |
    542  |   Add/subtract the values from one accounting structure to another
    543  |
    544  |   acc_st *a   this one gets changed
    545  |
    546  |   acc_st *b   this one provides the values to change a
    547  |
    548  |   int minus   triggers subtraction if non-zero
    549  |
    550  | +++++++++++++++++++++++++++++++++++++++*/
    551  | void AC_acc_addup(acc_st *a, acc_st *b, int minus)
    552  | {
    553  |   int mul = minus ? -1 : 1;
    554  |   time_t current_time;
    555  |
    556  |   current_time = time(NULL);
    557  |
    558  |   AC_decay_leaf_l(a);
    559  |
    560  |
    561  |   UT_timeget(&a->timestamp);
    562  |
    563  |   /* add all counters from b to those in a */
    564  |   a->connections     +=  mul * b->connections;
    565  |   a->addrpasses      +=  mul * b->addrpasses;
    566  |
    567  |   a->denials         +=  mul * b->denials;
    568  |   a->queries         +=  mul * b->queries;
    569  |   a->referrals       +=  mul * b->referrals;
    570  |   a->public_objects  +=  mul * b->public_objects;
    571  |   a->private_objects +=  mul * b->private_objects;
    572  |   a->private_bonus   +=  mul * b->private_bonus;
    573  |   a->public_bonus    +=  mul * b->public_bonus;
    574  |
    575  |
    576  |   if (a->changed == AC_ACC_NOT_CHANGED) {
    577  |     a->changed = AC_ACC_CHANGED;
    578  |   }
    579  |
    580  |
    581  | }/* AC_acc_addup */
    582  |
    583  | /*++++++++++++++++++++++++++++++++++++++
    584  |   commit_credit_l:
    585  |
    586  |   performs the commit on an accounting tree (locks them first)
    587  |   stores a copy of the accounting record at rec_store
    588  |
    589  |   Assumes locked tree.
    590  |
    591  |   rx_tree_t *tree      - the tree
    592  |
    593  |   ip_prefix_t *prefix  - prefix (usually a /32)
    594  |
    595  |   acc_st *acc_conn     - credit used
    596  |
    597  |   acc_st *rec_store    - pointer to store the account struct or NULL
    598  |
    599  |   returns error code from AC_findcreate_account_l or OK
    600  |
    601  |   MT-Note: locks/unlocks the accounting tree
    602  | +++++++++++++++++++++++++++++++++++++++*/
    603  | static
    604  | er_ret_t
    605  | AC_commit_credit_l(rx_tree_t *tree, ip_prefix_t *prefix,
    606  | 		 acc_st *acc_conn, acc_st *rec_store )
    607  | {
    608  |   acc_st      *accountrec;
    609  |   er_ret_t    ret_err;
    610  |   char dbg_pref[50];
    611  |
    612  |
    613  |   acc_conn->private_bonus = acc_conn->private_objects;
    614  |   acc_conn->public_bonus  = acc_conn->public_objects;
    615  |
    616  |
    617  |   ret_err = AC_findcreate_account_l(act_runtime, prefix, &accountrec);
    618  |
    619  |   if( NOERR(ret_err)) {
    620  | // XXX remove debug    IP_pref_b2a(prefix, dbg_pref, 50);
    621  |     AC_acc_addup(accountrec, acc_conn, ACC_PLUS);
    622  |   }
    623  |
    624  |   if( rec_store ) {
    625  |     *rec_store = *accountrec;
    626  |   }
    627  |
    628  |   return ret_err;
    629  | }/* AC_commit_credit */
    630  |
    631  | /*++++++++++++++++++++++++++++++++++++++
    632  |   AC_dbopen_admin:
    633  |
    634  |   opens the ADMIN database and returns a pointer to the connection structure
    635  |   (rationale: the opening process became a bit bloated and is done twice,
    636  |   so I put it into a separate function)
    637  | ++++++++++++++++++++++++++++++++++++++*/
    638  | SQ_connection_t *
    639  | AC_dbopen_admin(void)
    640  | {
    641  |   SQ_connection_t *con=NULL;
    642  |   char *dbhost = ca_get_ripadminhost;
    643  |   char *dbname = ca_get_ripadmintable;
    644  |   char *dbuser = ca_get_ripadminuser;
    645  |   char *dbpass = ca_get_ripadminpassword;
    646  |   unsigned dbport = ca_get_ripadminport;
    647  |
    648  |   if( (con = SQ_get_connection(dbhost, dbport, dbname, dbuser, dbpass)
    649  |        ) == NULL ) {
    650  |     fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
    651  |     die;
    652  |   }
    653  |
    654  |   UT_free(dbhost);
    655  |   UT_free(dbname);
    656  |   UT_free(dbuser);
    657  |   UT_free(dbpass);
    658  |
    659  |   return con;
    660  | }
    661  |
    662  | /*++++++++++++++++++++++++++++++++++++++
    663  |   AC_acl_sql:
    664  |
    665  |   updates/creates a record for the given prefix in the acl table of
    666  |   the RIPADMIN database. Adds a comment.
    667  |
    668  |   ip_prefix_t *prefix  - prefix
    669  |
    670  |   acl_st *newacl       - new values to store in the database
    671  |
    672  |   char *newcomment     - comment to be added (must not be NULL)
    673  |
    674  |   placeholder: it may return an error code from SQ - as soon as sq
    675  |   implements common error scheme
    676  |
    677  |  ++++++++++++++++++++++++++++++++++++++*/
    678  | er_ret_t
    679  | AC_acl_sql(ip_prefix_t *prefix, acl_st *newacl, char *newcomment )
    680  | {
    681  |   SQ_connection_t *sql_connection = NULL;
    682  |   SQ_result_set_t *result;
    683  |   SQ_row_t *row;
    684  |   char *oldcomment;
    685  |   char *query;
    686  |   char querybuf[256];
    687  |
    688  |   sql_connection = AC_dbopen_admin();
    689  |
    690  |   /* get the old entry, extend it */
    691  |   sprintf(querybuf, "SELECT comment FROM acl WHERE "
    692  | 	  "prefix = %u AND prefix_length = %d",
    693  | 	  prefix->ip.words[0],
    694  | 	  prefix->bits);
    695  |   dieif( SQ_execute_query(sql_connection, querybuf, &result) == -1 );
    696  |
    697  |   if( SQ_num_rows(result) == 1 ) {
    698  |     dieif( (row = SQ_row_next(result)) == NULL);
    699  |     oldcomment = SQ_get_column_string(result, row, 0);
    700  |   }
    701  |   else {
    702  |     oldcomment = "";
    703  |   }
    704  |
    705  |   SQ_free_result(result);
    706  |
    707  |   /* must hold the thing below (REPLACE..blah blah blah) + text */
    708  |   query = UT_malloc(strlen(oldcomment) + strlen(newcomment) + 256);
    709  |
    710  |   /* compose new entry and insert it */
    711  |   sprintf(query, "REPLACE INTO acl VALUES(%u, %d, %d, %d, %d, %d, %d,"
    712  | 	  "\"%s%s%s\")",
    713  | 	  prefix->ip.words[0],
    714  | 	  prefix->bits,
    715  | 	  newacl->maxprivate,
    716  | 	  newacl->maxpublic,
    717  | 	  newacl->maxdenials,
    718  | 	  newacl->deny,
    719  | 	  newacl->trustpass,
    720  | 	  oldcomment,
    721  | 	  strlen(oldcomment) > 0 ? "\n" : "",
    722  | 	  newcomment
    723  | 	  );
    724  |
    725  |   SQ_execute_query(sql_connection, query, NULL);
    726  |   SQ_close_connection(sql_connection);
    727  |
    728  |   UT_free(query);
    729  |
    730  |   return AC_OK;
    731  |
    732  | }/* AC_acl_sql */
    733  |
    734  | /*++++++++++++++++++++++++++++++++++++++
    735  |   AC_ban_set:
    736  |
    737  |   re/sets the permanent ban flag both in the acl tree in memory
    738  |   and the sql table. The "text" is appended to the comment
    739  |   in the sql record (the expected cases are
    740  |   - "automatic" in case the limit is exceeded and ban is set by s/w
    741  |   - "manual"    in case it is (un)set from the config iface
    742  |
    743  |   ip_prefix_t *prefix   - prefix
    744  |
    745  |   char *text            - usually "automatic" or "manual"
    746  |
    747  |   int denyflag          - new value of the denyflag (ban)
    748  |
    749  |   returns error code from AC_acl_sql or OK
    750  |   +++++++++++++++++++++++++++++++++++++++*/
    751  | er_ret_t
    752  | AC_ban_set(ip_prefix_t *prefix, char *text, int denyflag)
    753  | {
    754  |   acl_st *treeacl;
    755  |   char newcomment[256];
    756  |   er_ret_t ret_err;
    757  |   time_t  clock;
    758  |   char timebuf[26];
    759  |   char prefstr[IP_PREFSTR_MAX];
    760  |
    761  |   time(&clock);
    762  |   ctime_r(&clock, timebuf);
    763  |
    764  |   sprintf(newcomment,"%s permanent ban set to %d at %s", text,
    765  | 	  denyflag, timebuf);
    766  |
    767  |   if( IP_pref_b2a(prefix, prefstr, IP_PREFSTR_MAX) != IP_OK ) {
    768  |     die; /* program error - this is already converted so must be OK */
    769  |   }
    770  |
    771  |   ER_inf_va( FAC_AC, ASP_AC_I_PERMBAN,
    772  | 	     "%s permanent ban set to %d for %s", text, denyflag, prefstr );
    773  |
    774  |   TH_acquire_write_lock( &(act_acl->rwlock) );
    775  |
    776  |   /* find a record in the tree */
    777  |   if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
    778  |     treeacl->deny = denyflag;
    779  |     ret_err = AC_acl_sql( prefix, treeacl, newcomment );
    780  |   }
    781  |   TH_release_write_lock( &(act_acl->rwlock) );
    782  |
    783  |   return ret_err;
    784  | }/* AC_ban_set */
    785  |
    786  |
    787  | /*++++++++++++++++++++++++++++++++++++++
    788  |   AC_asc_ban_set:
    789  |
    790  |   This is not used, should be removed?
    791  |
    792  |   sets ban on text address/range. Parses the text address/range/prefix
    793  |   and then calls AC_ban_set on that prefix.
    794  |
    795  |   Precondition: if the key is a range, it must decompose into one prefix
    796  |
    797  |   returns error code from IP_smart_conv, AC_ban_set or
    798  |   AC_INVARG if range composed
    799  |   +++++++++++++++++++++++++++++++++++++++*/
    800  | er_ret_t
    801  | AC_asc_ban_set(char *addrstr, char *text, int denyflag)
    802  | {
    803  |   er_ret_t ret_err;
    804  |   GList *preflist = NULL;
    805  |   ip_keytype_t key_type;
    806  |
    807  |   if( (ret_err = IP_smart_conv(addrstr, 0, 0,
    808  | 			       &preflist, IP_PLAIN, &key_type)) != IP_OK ) {
    809  |     return ret_err;
    810  |   }
    811  |
    812  |   /* allow only one prefix */
    813  |   /* The argument can be even a range, but must decompose into one prefix */
    814  |   if(  NOERR(ret_err) && g_list_length( preflist ) != 1 ) {
    815  |     ret_err = AC_INVARG;
    816  |   }
    817  |
    818  |   if( NOERR(ret_err) ) {
    819  |     ret_err = AC_ban_set( (g_list_first(preflist)->data), text, denyflag);
    820  |   }
    821  |
    822  |   wr_clear_list( &preflist );
    823  |
    824  |   return ret_err;
    825  | }/* AC_asc_ban_set */
    826  |
    827  | /*++++++++++++++++++++++++++++++++++++++
    828  |   AC_asc_all_set:
    829  |
    830  |   take ascii prefix and find/create a new entry, inheriting all parameters
    831  |   and then set them according to the array of args.
    832  |
    833  | +*/
    834  | er_ret_t
    835  | AC_asc_all_set(ip_prefix_t *prefix, char *comment, char * array[])
    836  | {
    837  |   er_ret_t ret_err;
    838  |   acl_st *treeacl;
    839  |   int i;
    840  |
    841  |   TH_acquire_write_lock( &(act_acl->rwlock) );
    842  |
    843  |   /* find/create a record in the tree */
    844  |   if( NOERR(ret_err = AC_findcreate_acl_l( prefix, &treeacl )) ) {
    845  |
    846  |     /* update it from the array */
    847  |     for(i=0; i<AC_AR_SIZE; i++) {
    848  |       if(array[i] != NULL) { /* set only those that have been specified */
    849  | 	int val,k;
    850  |
    851  | 	if( (k=sscanf( array[i], "%d", &val)) < 1 ) {
    852  | 	  ret_err = AC_INVARG;
    853  | 	  break; /* quit the for */
    854  | 	}
    855  |
    856  | 	/* otherwise, the value makes sense. Put it in the structure. */
    857  | 	switch(i) {
    858  | 	case AC_AR_MAXPRIVATE: treeacl->maxprivate = val; break;
    859  | 	case AC_AR_MAXPUBLIC:  treeacl->maxpublic  = val; break;
    860  | 	case AC_AR_MAXDENIALS: treeacl->maxdenials = val; break;
    861  | 	case AC_AR_DENY:       treeacl->deny       = val; break;
    862  | 	case AC_AR_TRUSTPASS:  treeacl->trustpass  = val; break;
    863  | 	} /* switch */
    864  |       } /* if array[i] not null */
    865  |     } /* for each array element */
    866  |
    867  |     if( NOERR(ret_err) ) { /* protect against AC_INVARG */
    868  |       ret_err = AC_acl_sql( prefix, treeacl, comment );
    869  |     }
    870  |   } /* if find/create OK */
    871  |
    872  |   TH_release_write_lock( &(act_acl->rwlock) );
    873  |
    874  |   return ret_err;
    875  | }
    876  |
    877  |
    878  | /*++++++++++++++++++++++++++++++++++++++
    879  | AC_asc_acl_command_set:
    880  |
    881  |   parse a command and set acl options for an entry.
    882  |   command syntax:
    883  |
    884  |   <prefix> option=value,option=value,option=value...
    885  |
    886  |   where <option> is defined in AC_ar_acl[] array, value is an integer
    887  |
    888  |   char *command  text of the command.
    889  |                  Syntax: ip[/prefixlength] column=value,column=value...
    890  |                  Column names as in acl display. Unset columns are inherited.
    891  |
    892  |   char *comment  text to be added to the acl record's comment column.
    893  |
    894  |   ++++++++++++++++++++++++++++++++++++++*/
    895  |
    896  | er_ret_t
    897  | AC_asc_acl_command_set( char *command, char *comment )
    898  | {
    899  |   ip_prefix_t *prefix;
    900  |   char *eop, *eoc, *value;
    901  |   char *array[AC_AR_SIZE];
    902  |   er_ret_t ret_err = AC_OK;
    903  |   GList *preflist = NULL;
    904  |   ip_keytype_t key_type;
    905  |
    906  |   char *copy = UT_strdup(command);
    907  |   char *addrstr = copy;
    908  |   eoc = strchr(copy, '\0'); /* points to the end of it */
    909  |
    910  |   memset(array, 0 ,sizeof(array));
    911  |
    912  |   /* first comes the prefix. Find the space after it
    913  |      and break the string there.
    914  |   */
    915  |   if( (eop = strchr(copy,' ')) == NULL) {
    916  |     ret_err = AC_INVARG;
    917  |   }
    918  |
    919  |   if( NOERR(ret_err) ) {
    920  |     *eop++ = 0;
    921  |
    922  |     /* now eop points to the rest of the string (if any). Take options.
    923  |      */
    924  |     while( eop != eoc && ret_err == AC_OK) {
    925  |       char *sp;
    926  |
    927  |       /* give getsubopt chunks with no spaces */
    928  |       if( (sp = strchr(eop, ' ')) != NULL ) {
    929  | 	*sp=0;
    930  |       }
    931  |
    932  |       while( *eop != '\0' ) {
    933  | 	int k = getsubopt(&eop, AC_ar_acl, &value);
    934  | 	if( k < 0 ) {
    935  | 	  ret_err = AC_INVARG;
    936  | 	  break;
    937  | 	}
    938  |
    939  | 	array[k] = value;
    940  |       }
    941  |
    942  |       if( eop != eoc ) { /*getsubopt finished but did not consume all string*/
    943  | 	eop ++;            /* must have been a space. advance one */
    944  |       }
    945  |     }
    946  |   }
    947  |
    948  |   /* convert the prefix */
    949  |   if(  NOERR(ret_err) ) {
    950  |     ret_err = IP_smart_conv(addrstr, 0, 0, &preflist, IP_PLAIN, &key_type);
    951  |
    952  |     /* allow only one prefix */
    953  |     /* The argument can be even a range, but must decompose into one prefix */
    954  |     if(  NOERR(ret_err) && g_list_length( preflist ) == 1 ) {
    955  |       prefix = (g_list_first(preflist)->data);
    956  |     }
    957  |     else {
    958  |       ret_err = AC_INVARG;
    959  |     }
    960  |   }
    961  |
    962  |   /* perform changes */
    963  |   if(  NOERR(ret_err) ) {
    964  |     ret_err = AC_asc_all_set(prefix, comment, array);
    965  |   }
    966  |
    967  |   wr_clear_list( &preflist );
    968  |   UT_free(copy);
    969  |
    970  |   return ret_err;
    971  | }/* AC_asc_acl_command_set */
    972  |
    973  |
    974  | /*++++++++++++++++++++++++++++++++++++++
    975  |   AC_asc_set_nodeny:
    976  |
    977  |   reset the deny counter in the access tree to 0 (after reenabling).
    978  |
    979  |   Operates on the runtime access tree.
    980  |
    981  |   char *ip      text IP (ip only, not prefix or range).
    982  |   +++++++++++++++++++++++++++++++++++++++*/
    983  | er_ret_t AC_asc_set_nodeny(char *ip)
    984  | {
    985  |   ip_prefix_t  prefix;
    986  |   er_ret_t     ret_err;
    987  |   acc_st *ac_ptr;
    988  |
    989  |   ret_err = IP_addr_e2b( &(prefix.ip), ip );
    990  |
    991  |   if( NOERR(ret_err)) {
    992  |     prefix.bits = IP_sizebits(prefix.ip.space);
    993  |
    994  |     TH_acquire_write_lock( &(act_runtime->rwlock) );
    995  |
    996  |     ret_err = AC_findcreate_account_l(act_runtime, &prefix, &ac_ptr);
    997  |     if( NOERR(ret_err)) {
    998  |       ac_ptr->denials = 0;
    999  |     }
    1000 |
    1001 |     TH_release_write_lock( &(act_runtime->rwlock) );
    1002 |   }
    1003 |
    1004 |   return ret_err;
    1005 | }
    1006 |
    1007 |
    1008 |
    1009 | /*++++++++++++++++++++++++++++++++++++++
    1010 |   AC_commit:
    1011 |
    1012 |   commits the credit into all accounting trees, (XXX: only one at the moment)
    1013 |   checks the limits and sets automatic ban if limit exceeded.
    1014 |
    1015 |   ip_addr_t *addr  - user's address
    1016 |
    1017 |   acc_st *acc_conn - credit used
    1018 |
    1019 |   acl_st *acl_copy - pointer to store a copy of the acl
    1020 |
    1021 |   returns error code from AC_commit_credit or AC_ban_set or OK.
    1022 |
    1023 |   outline:
    1024 |         lock runtime + minute accounting trees
    1025 | 	-----------------------  XXX runtime only for the moment
    1026 |            find or create entries,
    1027 |            increase accounting values by the values from passed acc
    1028 |            check values against acl, see if permanent ban applies
    1029 |
    1030 |            reset the connection acc
    1031 |         unlock accounting trees
    1032 |
    1033 |         if permanent ban - set it! :
    1034 |             lock acl
    1035 |             find/create IP in memory
    1036 |             set ban
    1037 |             find/create IP in SQL
    1038 |             copy old values (if any), set ban, append comment
    1039 |             unlock acl
    1040 |
    1041 |  +++++++++++++++++++++++++++++++++++++++*/
    1042 | er_ret_t AC_commit(ip_addr_t *addr, acc_st *acc_conn, acl_st *acl_copy) {
    1043 |   acc_st   account;
    1044 |   er_ret_t ret_err;
    1045 |   ip_prefix_t prefix;
    1046 |   sk_conn_st condat;
    1047 |
    1048 |   prefix.ip = *addr;
    1049 |   prefix.bits = IP_sizebits(addr->space);
    1050 |
    1051 |   TH_acquire_write_lock( &(act_runtime->rwlock) );
    1052 |   ret_err = AC_commit_credit_l(act_runtime, &prefix, acc_conn, &account);
    1053 |   TH_release_write_lock( &(act_runtime->rwlock) );
    1054 |
    1055 |   memset(acc_conn,0, sizeof(acc_st));
    1056 |
    1057 |   /* set permanent ban if deserved  and if not set yet */
    1058 |   if( account.denials > acl_copy->maxdenials
    1059 |       && acl_copy->deny == 0
    1060 |       && NOERR(ret_err) ) {
    1061 |
    1062 |     ret_err = AC_ban_set(&prefix, "Automatic", 1);
    1063 |   }
    1064 |
    1065 |   /*
    1066 |   SK_cd_make(&condat, 1, 0);
    1067 |   rx_tree_print(&condat, act_runtime);
    1068 |   SK_cd_free(&condat);
    1069 |   */
    1070 |
    1071 |   return ret_err;
    1072 | } /* AC_commit */
    1073 |
    1074 |
    1075 |
    1076 | /*++++++++++++++++++++++++++++++++++++++
    1077 |
    1078 |
    1079 | unsigned AC_prune     deletes the entries listed in the prunelist
    1080 |                     (this cannot be done from within the rx_walk_tree,
    1081 | 		    because the walk would be confused).
    1082 | 		    Returns number of nodes deleted.
    1083 |
    1084 | GList *prunelist  list of pointers to nodes that should be deleted.
    1085 |                   the prefixes actually are allocated in the node
    1086 | 		  structures, so they must not be dereferenced after
    1087 | 		  they are freed here.
    1088 |
    1089 |   ++++++++++++++++++++++++++++++++++++++*/
    1090 | unsigned AC_prune(GList *prunelist)
    1091 | {
    1092 |   GList *pitem;
    1093 |   char prstr[IP_PREFSTR_MAX];
    1094 |   unsigned count = 0;
    1095 |   acc_st accu; /* to accumulate the accounting of deleted nodes */
    1096 |   ip_prefix_t globalpref;
    1097 |
    1098 |   memset( &accu, 0, sizeof(accu));
    1099 |
    1100 |   for( pitem = g_list_first(prunelist);
    1101 |        pitem != NULL;
    1102 |        pitem = g_list_next(pitem)) {
    1103 |
    1104 |     rx_node_t   *nodeptr = (rx_node_t *) pitem->data;
    1105 |     ip_prefix_t *prefptr = &(nodeptr->prefix);
    1106 |     acc_st      *nodeacc = nodeptr->leaves_ptr->data;
    1107 |
    1108 |     AC_acc_addup(&accu, nodeacc, ACC_PLUS); /* transfer the account */
    1109 |     dieif( IP_pref_b2a( prefptr, prstr, IP_PREFSTR_MAX ) != IP_OK );
    1110 |     ER_dbg_va( FAC_AC, ASP_AC_PRUNE_DET, "AC_prune: entry %s", prstr );
    1111 |     /* delete the node. Assume there's one and only one dataleaf */
    1112 |     rx_bin_node( RX_OPER_DEL, prefptr, act_runtime, (void *)nodeacc );
    1113 |     count ++;
    1114 |   }
    1115 |   /* store the accumulated account at 0/0 */
    1116 |   dieif( !NOERR (IP_pref_a2b( &globalpref, "0/0" )));
    1117 |   AC_commit_credit_l(act_runtime, &globalpref, &accu, NULL);
    1118 |
    1119 |   return count;
    1120 | }
    1121 |
    1122 |
    1123 | char AC_prunable(acc_st *leaf) {
    1124 |   if(    leaf->private_bonus < 0.5
    1125 |       && leaf->public_bonus  < 0.5
    1126 |       && leaf->denials == 0
    1127 |       && leaf->addrpasses == 0 ) {
    1128 |     return 1;
    1129 |   }
    1130 |   return 0;
    1131 | }
    1132 |
    1133 | /*++++++++++++++++++++++++++++++++++++++
    1134 |   AC_decay_hook:
    1135 |
    1136 |   action performed on a single account node during decay (diminishing the
    1137 |   bonus). Conforms to rx_walk_tree interface, therefore some of the
    1138 |   arguments do not apply and are not used.
    1139 |
    1140 |   rx_node_t *node  - pointer to the node of the radix tree
    1141 |
    1142 |   int level        - not used
    1143 |
    1144 |   int nodecounter  - not used
    1145 |
    1146 |   void *con        - in real life: (double *) - points to the decay factor.
    1147 |
    1148 |   returns always OK
    1149 | +++++++++++++++++++++++++++++++++++++++*/
    1150 | er_ret_t AC_decay_hook(rx_node_t *node, int level,
    1151 | 		       int nodecounter, void *con)
    1152 | {
    1153 |   acc_st *a = node->leaves_ptr->data;
    1154 |   acc_st clone;
    1155 |   ac_decay_data_t *dec_dat_p = (ac_decay_data_t *)con;
    1156 |
    1157 |   clone = *a;
    1158 |
    1159 |   AC_decay_leaf_l(&clone);
    1160 |
    1161 |   /* XXX pending: if bonus is close to zero and the node did not hit
    1162 |      its limit, and it's not an address-passing node
    1163 |      then add it to the list of nodes for deletion */
    1164 |
    1165 | /*
    1166 |   ER_dbg_va( FAC_AC, ASP_AC_PRUNE_DET,
    1167 | 	     "%5.2f / %5.2f   * %5.2f  -> %5.2f / %5.2f ",
    1168 | 	     bpr, bpu, factor, a->private_bonus, a->public_bonus);
    1169 | */
    1170 |
    1171 |   if (AC_prunable(&clone)) {
    1172 |     dec_dat_p->prunelist = g_list_append(dec_dat_p->prunelist, node);
    1173 |   }
    1174 |
    1175 |   /* process accounting - add all queries to the total counter */
    1176 |   dec_dat_p->newtotal += a->queries;
    1177 |
    1178 |   /* change oldest timestamp*/
    1179 |   if (UT_timediff(&clone.timestamp, &oldest_timestamp) > 0 &&
    1180 |       clone.addrpasses==0 && clone.denials==0 ) {
    1181 |     oldest_timestamp = clone.timestamp;
    1182 |   }
    1183 |
    1184 |   return RX_OK;
    1185 | } /* AC_decay_hook() */
    1186 |
    1187 |
    1188 |
    1189 | /*++++++++++++++++++++++++++++++++++++++
    1190 |   AC_decay:
    1191 |
    1192 |   Every AC_DECAY_TIME goes through the accounting tree(s) and decays the
    1193 |   bonus values.
    1194 |
    1195 |   returns always OK
    1196 |
    1197 |   MT-Note  This should be run as a detached thread.
    1198 |   +++++++++++++++++++++++++++++++++++++++*/
    1199 | er_ret_t AC_decay(void) {
    1200 |   er_ret_t ret_err = AC_OK;
    1201 |   ac_decay_data_t dec_dat;
    1202 |   ut_timer_t begintime, endtime;
    1203 |   unsigned pruned;
    1204 |   float elapsed, rate, exactinterval;
    1205 |   unsigned oldtotal = 0;
    1206 |   unsigned increase;
    1207 |   unsigned count;
    1208 |
    1209 |   TA_add(0, "decay");
    1210 |
    1211 |   UT_timeget( &endtime );
    1212 |
    1213 |   /* XXX uses CO_get_do_server() to see when to exit the program.
    1214 |      this will change */
    1215 |   while(CO_get_do_server()) {
    1216 |     GString *gs;
    1217 |
    1218 |
    1219 |     UT_timeget( &begintime );
    1220 |     exactinterval =  UT_timediff( &endtime, &begintime ); /* old endtime */
    1221 |
    1222 |
    1223 |     /* those values can be changed in runtime - so recalculate
    1224 |        the decay factor vefore each pass */
    1225 |     dieif( ca_get_ac_decay_halflife == 0 );
    1226 |
    1227 |
    1228 |     dec_dat.prunelist = NULL;
    1229 |     /* the decay factor of
    1230 |        f(t) = exp(-a*t)
    1231 |        a = -ln(0.5) / t
    1232 |        so for T being the half-life period and v being the sampling interval
    1233 |        used as the unit of time
    1234 |        a = -ln(0.5) / T;
    1235 |        f(t+x) = exp(-a(t+x)) = f(t)*f(x) = f(t)*exp(-ax) =
    1236 |        = f(t)*exp(ln(0.5)*v/T)
    1237 |        so you multiply the previous value by exp(ln(0.5)*v/T)
    1238 |     */
    1239 | /*
    1240 |     dec_dat.decay_factor =
    1241 |       exp ( -0.693147180559945 * exactinterval / ca_get_ac_decay_halflife) ;
    1242 | */
    1243 |     dec_dat.newtotal = 0;
    1244 |     TH_acquire_write_lock( &(act_runtime->rwlock) );
    1245 |
    1246 |     UT_timeget(&oldest_timestamp);
    1247 |     if( act_runtime->top_ptr != NULL ) {
    1248 |       count = rx_walk_tree(act_runtime->top_ptr, AC_decay_hook,
    1249 | 			   RX_WALK_SKPGLU,  /* skip glue nodes */
    1250 | 			   255, 0, 0, &dec_dat, &ret_err);
    1251 |     }
    1252 |     else {
    1253 |       count = 0;
    1254 |     }
    1255 |
    1256 |     /* it should also be as smart as to delete nodes that have reached
    1257 |        zero, otherwise the whole of memory will be filled.
    1258 |        Next release :-)
    1259 |     */
    1260 |
    1261 |     pruned = AC_prune( dec_dat.prunelist );
    1262 |
    1263 |     /*
    1264 |     gs = g_string_new("");
    1265 |     AC_print_access(gs);
    1266 |     printf(gs->str);
    1267 |     g_list_free( dec_dat.prunelist );
    1268 |     */
    1269 |
    1270 | #if 0
    1271 |     /* XXX dbg checking tree consistency */
    1272 |     if( act_runtime->top_ptr != NULL ) {
    1273 |       rx_treecheck_t errorfound;
    1274 |       er_ret_t err;
    1275 |       if( (err=RX_treecheck(act_runtime, 1, &errorfound)) != RX_OK ) {
    1276 | 	fprintf(stderr, "Nope! %d returned \n", err);
    1277 | 	ER_dbg_va( FAC_AC, ASP_AC_DECAY,
    1278 | 		   "AC: checking access tree consistency: error %d", err);
    1279 | 	die; /* access tree not consistent */
    1280 |       }
    1281 |     }
    1282 | #endif
    1283 |
    1284 |     TH_release_write_lock( &(act_runtime->rwlock) );
    1285 |
    1286 |     UT_timeget(&endtime);
    1287 |
    1288 |     elapsed = UT_timediff( &begintime, &endtime);
    1289 |
    1290 |     ER_dbg_va( FAC_AC, ASP_AC_DECAY,
    1291 | 	      "AC_decay: Pruned %d of %d nodes. Took %5.3fs. Runs every %ds.",
    1292 | 	       pruned, count, elapsed, ca_get_ac_decay_interval);
    1293 |
    1294 |     /* number/rate of queries within the last <interval> */
    1295 |     {
    1296 |       char actbuf[32];
    1297 |
    1298 |       increase = dec_dat.newtotal - oldtotal;
    1299 |       rate = increase / exactinterval;
    1300 |
    1301 |       sprintf(actbuf, "%.2f q/s in %.1fs", rate, exactinterval);
    1302 |       TA_setactivity(actbuf);
    1303 |
    1304 |       oldtotal = dec_dat.newtotal;
    1305 |     }
    1306 |
    1307 |     SV_sleep(ca_get_ac_decay_interval);
    1308 |   } /* while */
    1309 |
    1310 |   TA_delete();
    1311 |
    1312 |   return ret_err;
    1313 | } /* AC_decay() */
    1314 |
    1315 |
    1316 | /*++++++++++++++++++++++++++++++++++++++
    1317 |   AC_acc_load:
    1318 |
    1319 |   loads the acl access tree from the acl table of the RIPADMIN database.
    1320 |   (takes port/host/user/password from the config module).
    1321 |
    1322 |   bails out if encounters problems with the database (logs to stderr).
    1323 |
    1324 |   returns error code from RX_bin_node or wr_malloc.
    1325 |   ++++++++++++++++++++++++++++++++++++++*/
    1326 | er_ret_t AC_acc_load(void)
    1327 | {
    1328 |   SQ_connection_t *con=NULL;
    1329 |   SQ_result_set_t *result;
    1330 |   SQ_row_t *row;
    1331 |   er_ret_t ret_err = RX_OK;
    1332 |
    1333 |   con = AC_dbopen_admin();
    1334 |
    1335 |   if( SQ_execute_query(con, "SELECT * FROM acl", &result) == -1 ) {
    1336 |       fprintf(stderr, "ERROR %d: %s\n", SQ_errno(con), SQ_error(con));
    1337 |       die;
    1338 |   }
    1339 |
    1340 |   TH_acquire_write_lock( &(act_acl->rwlock) );
    1341 |
    1342 |   while ( (row = SQ_row_next(result)) != NULL && ret_err == RX_OK) {
    1343 |     ip_prefix_t mypref;
    1344 |     acl_st *newacl;
    1345 |  #define NUMELEM (7)
    1346 |     char *col[NUMELEM];
    1347 |     unsigned myint, i;
    1348 |
    1349 |     memset(&mypref, 0, sizeof(ip_prefix_t));
    1350 |     mypref.ip.space = IP_V4;
    1351 |
    1352 |     newacl = UT_malloc(sizeof(acl_st));
    1353 |
    1354 |     for(i=0; i<NUMELEM; i++) {
    1355 |       if ( (col[i] = SQ_get_column_string(result, row, i)) == NULL) {
    1356 |         die;
    1357 |       }
    1358 |     }
    1359 |
    1360 |     /* prefix ip */
    1361 |     if( sscanf(col[0], "%u", &mypref.ip.words[0] ) < 1 ) { die; }
    1362 |
    1363 |     /* prefix length */
    1364 |     if( sscanf(col[1], "%u", &mypref.bits ) < 1 ) { die; }
    1365 |
    1366 |     /* acl contents */
    1367 |     if( sscanf(col[2], "%u",  & (newacl->maxprivate)  ) < 1 ) { die; }
    1368 |     if( sscanf(col[3], "%u",  & (newacl->maxpublic)   ) < 1 ) { die; }
    1369 |     if( sscanf(col[4], "%hd", & (newacl->maxdenials)  ) < 1 ) { die; }
    1370 |
    1371 |     /* these are chars therefore cannot read directly */
    1372 |     if( sscanf(col[5], "%u", &myint              ) < 1 ) { die; }
    1373 |     else {
    1374 |       newacl->deny = myint;
    1375 |     }
    1376 |     if( sscanf(col[6], "%u", &myint  ) < 1 ) { die; }
    1377 |     else {
    1378 |       newacl->trustpass = myint;
    1379 |     }
    1380 |
    1381 |     /* free space */
    1382 |     for(i=0; i<NUMELEM; i++) {
    1383 | 	UT_free(col[i]);
    1384 |     }
    1385 |
    1386 |     /* now add to the tree */
    1387 |     ret_err = rx_bin_node( RX_OPER_CRE, &mypref,
    1388 |                            act_acl, (rx_dataleaf_t *) newacl );
    1389 |   } /* while row */
    1390 |
    1391 |   TH_release_write_lock( &(act_acl->rwlock) );
    1392 |
    1393 |   SQ_free_result(result);
    1394 |   /* Close connection */
    1395 |   SQ_close_connection(con);
    1396 |
    1397 |   return ret_err;
    1398 | } /* AC_acc_load */
    1399 |
    1400 |
    1401 |
    1402 | /*++++++++++++++++++++++++++++++++++++++
    1403 |   AC_build:
    1404 |
    1405 |   creates empty trees for accounting/acl.
    1406 |
    1407 |   returns error code from RX_tree_cre or OK.
    1408 |   (XXX): just now only bails out when encounters problems.
    1409 |   ++++++++++++++++++++++++++++++++++++++*/
    1410 | er_ret_t AC_build(void)
    1411 | {
    1412 |   /* create trees */
    1413 |   if (      RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
    1414 | 			RX_SUB_NONE, &act_runtime) != RX_OK
    1415 | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
    1416 | 			RX_SUB_NONE, &act_hour) != RX_OK
    1417 | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
    1418 | 			RX_SUB_NONE, &act_minute) != RX_OK
    1419 | 	 || RX_tree_cre("0.0.0.0/0", RX_FAM_IP, RX_MEM_RAMONLY,
    1420 | 			RX_SUB_NONE, &act_acl) != RX_OK
    1421 |          )
    1422 |     die; /*can be changed to an error and handled ... some day */
    1423 |
    1424 |   return RX_OK;
    1425 | }
    1426 |
    1427 | /*++++++++++++++++++++++++++++++++++++++
    1428 |   ac_rxwalkhook_print:
    1429 |
    1430 |   action performed on a single account node
    1431 |   when listing the contents of the access tree: format and print the
    1432 |   data from this node.
    1433 |
    1434 |   Conforms to rx_walk_tree interface, therefore some of the
    1435 |   arguments do not apply and are not used.
    1436 |
    1437 |   rx_node_t *node  - pointer to the node of the radix tree
    1438 |
    1439 |   int level        - not used
    1440 |
    1441 |   int nodecounter  - not used
    1442 |
    1443 |   void *con        - pointer to the target string (prints to it)
    1444 |
    1445 |   returns always OK
    1446 | +++++++++++++++++++++++++++++++++++++++*/
    1447 | static
    1448 | er_ret_t ac_rxwalkhook_print(rx_node_t *node,
    1449 |                              int level, int nodecounter,
    1450 |                              void *outvoid)
    1451 | {
    1452 |   char adstr[IP_ADDRSTR_MAX];
    1453 |   char *dat;
    1454 |   GString *output = outvoid;
    1455 |
    1456 |   dieif( IP_addr_b2a(&(node->prefix.ip), adstr, IP_ADDRSTR_MAX) != IP_OK );
    1457 |   /* program error. */
    1458 |
    1459 |   dat = ac_to_string( node->leaves_ptr );
    1460 |   g_string_sprintfa(output, "%-20s %s\n", adstr, dat );
    1461 |   UT_free(dat);
    1462 |
    1463 |   return RX_OK;
    1464 | } /* ac_rxwalkhook_print */
    1465 |
    1466 |
    1467 | /*++++++++++++++++++++++++++++++++++++++
    1468 |   This function displays the access table to the given connection.
    1469 |
    1470 |   unsigned AC_print_access    Returns the number of nodes traversed
    1471 |
    1472 |   GString *output             target string
    1473 |   ++++++++++++++++++++++++++++++++++++++*/
    1474 | unsigned AC_print_access(GString *output)
    1475 | {
    1476 |   int cnt = 0;
    1477 |   er_ret_t err;
    1478 |
    1479 |   if( act_runtime->top_ptr != NULL ) {
    1480 |     char *header = ac_to_string_header();
    1481 |
    1482 |     /* print header */
    1483 |     g_string_append(output, header);
    1484 |     UT_free(header);
    1485 |
    1486 |     cnt = rx_walk_tree(act_runtime->top_ptr, ac_rxwalkhook_print,
    1487 | 		       RX_WALK_SKPGLU,  /* print no glue nodes */
    1488 | 		       255, 0, 0, output, &err);
    1489 |   }
    1490 |
    1491 |   return cnt;
    1492 | } /* show_access() */
    1493 |
    1494 |
    1495 |
    1496 | /*++++++++++++++++++++++++++++++++++++++
    1497 |   ac_rxwalkhook_print_acl:
    1498 |
    1499 |   action performed on a single account node
    1500 |   when listing the contents of the acl tree: format and print the
    1501 |   data from this node.
    1502 |
    1503 |   Conforms to rx_walk_tree interface, therefore some of the
    1504 |   arguments do not apply and are not used.
    1505 |
    1506 |   rx_node_t *node  - pointer to the node of the radix tree
    1507 |
    1508 |   int level        - not used
    1509 |
    1510 |   int nodecounter  - not used
    1511 |
    1512 |   void *con        - pointer to the target string (prints to it)
    1513 |
    1514 |   returns always OK
    1515 |   +++++++++++++++++++++++++++++++++++++++*/
    1516 | static
    1517 | er_ret_t ac_rxwalkhook_print_acl(rx_node_t *node,
    1518 |                              int level, int nodecounter,
    1519 |                              void *outvoid)
    1520 | {
    1521 |   char prefstr[IP_PREFSTR_MAX];
    1522 |   char *dat;
    1523 |   GString *output = outvoid;
    1524 |
    1525 |   dieif( IP_pref_b2a(&(node->prefix), prefstr, IP_PREFSTR_MAX) != IP_OK );
    1526 |
    1527 |   dat = ac_acl_to_string( node->leaves_ptr );
    1528 |   g_string_sprintfa(output, "%-20s %s\n", prefstr, dat );
    1529 |   UT_free(dat);
    1530 |
    1531 |   return RX_OK;
    1532 | }/* ac_rxwalkhook_print_acl */
    1533 |
    1534 |
    1535 | /*++++++++++++++++++++++++++++++++++++++
    1536 |   This function writes the acl (access control) table to the given
    1537 |   Gstring (auto-expandable)
    1538 |
    1539 |   unsigned AC_print_acl     Returns the number of nodes traversed
    1540 |
    1541 |   GString *output           target string
    1542 |   ++++++++++++++++++++++++++++++++++++++*/
    1543 | unsigned AC_print_acl(GString *output)
    1544 | {
    1545 |   /* Administrator wishes to show access control list. */
    1546 |   int cnt = 0;
    1547 |   er_ret_t err;
    1548 |
    1549 |   if( act_acl->top_ptr != NULL ) {
    1550 |     char *header = ac_acl_to_string_header();
    1551 |
    1552 |     /* print header */
    1553 |     g_string_append(output, header);
    1554 |     UT_free(header);
    1555 |
    1556 |     cnt = rx_walk_tree(act_acl->top_ptr, ac_rxwalkhook_print_acl,
    1557 | 		       RX_WALK_SKPGLU,  /* print no glue nodes */
    1558 | 		       255, 0, 0, output, &err);
    1559 |   }
    1560 |
    1561 |   return cnt;
    1562 | }
    1563 |
    1564 |
    1565 | /*++++++++++++++++++++++++++++++++++++++
    1566 |   AC_count_object:
    1567 |
    1568 |   accounts an objects in the credit accordingly to its type,
    1569 |   or sets denial if the limit is defined and the credit is exceeded.
    1570 |
    1571 |   acc_st    *acc_credit     pointer to the credit structure (gets modified)
    1572 |
    1573 |   acl_st    *acl            acl, contains the limits for private/public objects
    1574 |
    1575 |   int private               indicates if the object type is private
    1576 |   ++++++++++++++++++++++++++++++++++++++*/
    1577 | void
    1578 | AC_count_object( acc_st    *acc_credit,
    1579 | 		 acl_st    *acl,
    1580 | 		 int private )
    1581 | {
    1582 |   if( private ) {
    1583 |     if( acc_credit->private_objects <= 0 && acl->maxprivate != -1 ) {
    1584 |       /* must be negative - will be subtracted */
    1585 |       acc_credit->denials = -1;
    1586 |     } else {
    1587 |       acc_credit->private_objects --;
    1588 |     }
    1589 |   }
    1590 |   else {
    1591 |     if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) {
    1592 |       acc_credit->denials = -1;
    1593 |     } else {
    1594 |       acc_credit->public_objects --;
    1595 |     }
    1596 |   }
    1597 | } /* AC_count_object */
    1598 |
    1599 |
    1600 | /* AC_credit_isdenied */
    1601 | /*++++++++++++++++++++++++++++++++++++++
    1602 |
    1603 |   checks the denied flag in credit (-1 or 1 means denied)
    1604 |
    1605 |   int
    1606 |   AC_credit_isdenied     returns 1 if denied, 0 otherwise
    1607 |
    1608 |   acc_st    *acc_credit    pointer to the credit structure
    1609 |   ++++++++++++++++++++++++++++++++++++++*/
    1610 | int
    1611 | AC_credit_isdenied(acc_st    *acc_credit)
    1612 | {
    1613 |   return (acc_credit->denials != 0);
    1614 | } /* AC_credit_isdenied */
    1615 |
    1616 |
    1617 | /* AC_get_higher_limit */
    1618 | /*++++++++++++++++++++++++++++++++++++++
    1619 |
    1620 |   returns the higher number of the two acl limits: maxprivate & maxpublic
    1621 |   corrected w.r.t the current credit left,
    1622 |   or unlimited if any of them is 'unlimited'.
    1623 |
    1624 |   int AC_get_higher_limit       returns the higher limit
    1625 |
    1626 |   acc_st    *acc_credit         current credit left
    1627 |
    1628 |   acl_st    *acl                acl for that user
    1629 | ++++++++++++++++++++++++++++++++++++++*/
    1630 | int
    1631 | AC_get_higher_limit(acc_st    *acc_credit,
    1632 | 		    acl_st    *acl)
    1633 | {
    1634 |   if( acl->maxprivate == -1 || acl->maxpublic == -1 ) {
    1635 |     return -1;
    1636 |   }
    1637 |   else {
    1638 |     int a = acc_credit->private_objects;
    1639 |     int b = acc_credit->public_objects;
    1640 |
    1641 |     return (a > b ? a : b);
    1642 |   }
    1643 | }/* AC_get_higher_limit */