Navigation


Changeset 285:0daf6fc2b751 in freeDiameter


Ignore:
Timestamp:
Apr 30, 2010, 5:55:16 PM (14 years ago)
Author:
Sebastien Decugis <sdecugis@nict.go.jp>
Branch:
default
Phase:
public
Message:

Added a test case for the app_acct extension

Files:
1 added
10 edited

Legend:

Unmodified
Added
Removed
  • extensions/app_acct/CMakeLists.txt

    r284 r285  
    1717        app_acct.h
    1818        app_acct.c
     19        acct_db.c
     20        acct_records.c
     21)
     22SET( APP_ACCT_SRC_GEN
    1923        lex.acct_conf.c
    2024        acct_conf.tab.c
    2125        acct_conf.tab.h
    22         acct_db.c
    23         acct_records.c
    2426)
    2527
  • extensions/app_acct/acct_db.c

    r284 r285  
    5151
    5252static const char * stmt = "acct_db_stmt";
    53 static PGconn *conn = NULL;
    54 
     53#ifndef TEST_DEBUG
     54static
     55#endif /* TEST_DEBUG */
     56PGconn *conn = NULL;
     57
     58/* Initialize the database context: connection to the DB, prepared statement to insert new records */
    5559int acct_db_init(void)
    5660{
     
    6367        int idx = 0;
    6468        PGresult * res;
    65         #define REALLOC_SIZE    1024
     69        #define REALLOC_SIZE    1024    /* We extend the buffer by this amount */
    6670       
    6771        TRACE_ENTRY();
     
    7680                acct_db_free();
    7781                return EINVAL;
    78         } else {
    79                 TRACE_DEBUG(INFO, "Connection to database successfull: user:%s, db:%s, host:%s.", PQuser(conn), PQdb(conn), PQhost(conn));
    80         }
     82        }
     83        if (PQprotocolVersion(conn) < 3) {
     84                fd_log_debug("Database protocol version is too old, version 3 is required for prepared statements.\n");
     85                acct_db_free();
     86                return EINVAL;
     87        }
     88       
     89        TRACE_DEBUG(FULL, "Connection to database successful, server version %d.", PQserverVersion(conn));
    8190       
    8291        /* Now, prepare the request object */
     
    174183        PQclear(res);
    175184       
    176        
    177 
     185        free(sql);
     186        acct_rec_empty(&emptyrecords);
     187       
     188        /* Ok, ready */
    178189        return 0;
    179190}
    180191
     192/* Terminate the connection to the DB */
    181193void acct_db_free(void)
    182 {
    183         if (conn)
     194{       
     195        if (conn) {
     196                /* Note: the prepared statement is automatically freed when the session terminates */
    184197                PQfinish(conn);
    185         conn = NULL;
     198                conn = NULL;
     199        }
    186200}
    187201
     202/* When a new message has been received, insert the content of the parsed mapping into the DB (using prepared statement) */
    188203int acct_db_insert(struct acct_record_list * records)
    189204{
    190         return ENOTSUP;
     205        char    **val;
     206        int      *val_len;
     207        int      *val_isbin;
     208        int       idx = 0;
     209        PGresult *res;
     210        struct fd_list *li;
     211       
     212        TRACE_ENTRY("%p", records);
     213        CHECK_PARAMS( conn && records );
     214       
     215        /* First, check if the connection with the DB has not staled, and eventually try to fix it */
     216        if (PQstatus(conn) != CONNECTION_OK) {
     217                /* Attempt a reset */
     218                PQreset(conn);
     219                if (PQstatus(conn) != CONNECTION_OK) {
     220                        TRACE_DEBUG(INFO, "Lost connection to the database server, and attempt to reestablish it failed");
     221                        TODO("Terminate the freeDiameter instance completly?");
     222                        return ENOTCONN;
     223                }
     224        }
     225       
     226        /* Alloc the arrays of parameters */
     227        CHECK_MALLOC( val       = calloc(records->nball, sizeof(const char *)) );
     228        CHECK_MALLOC( val_len   = calloc(records->nball, sizeof(const int)) );
     229        CHECK_MALLOC( val_isbin = calloc(records->nball, sizeof(const int)) );
     230       
     231        /* Now write all the map'd records in these arrays */
     232        for (li = records->all.next; li != &records->all; li = li->next) {
     233                struct acct_record_item * r = (struct acct_record_item *)(li->o);
     234                if (r->value) {
     235                        val_isbin[idx] = 1; /* We always pass binary parameters */
     236                        switch (r->param->avptype) {
     237                                case AVP_TYPE_OCTETSTRING:
     238                                        val[idx] = (void *)(r->value->os.data);
     239                                        val_len[idx] = r->value->os.len;
     240                                        break;
     241                                       
     242                                case AVP_TYPE_INTEGER32:
     243                                case AVP_TYPE_UNSIGNED32:
     244                                case AVP_TYPE_FLOAT32:
     245                                        r->scalar.v32 = htonl(r->value->u32);
     246                                        val[idx] = &r->scalar.c;
     247                                        val_len[idx] = sizeof(uint32_t);
     248                                        break;
     249                                       
     250                                case AVP_TYPE_INTEGER64:
     251                                case AVP_TYPE_UNSIGNED64:
     252                                case AVP_TYPE_FLOAT64:
     253                                        r->scalar.v64 = htonll(r->value->u64);
     254                                        val[idx] = &r->scalar.c;
     255                                        val_len[idx] = sizeof(uint64_t);
     256                                        break;
     257                               
     258                                default:
     259                                        ASSERT(0); /* detect bugs */
     260                        }
     261                }
     262               
     263                idx++;
     264        }
     265       
     266        /* OK, now execute the SQL statement */
     267        res = PQexecPrepared(conn, stmt, records->nball, (const char * const *)val, val_len, val_isbin, 1 /* We actually don't care here */);
     268       
     269        /* Done with the parameters */
     270        free(val);
     271        free(val_len);
     272        free(val_isbin);
     273       
     274        /* Now check the result code */
     275        if (PQresultStatus(res) != PGRES_COMMAND_OK) {
     276                TRACE_DEBUG(INFO, "An error occurred while INSERTing in the database: %s", PQerrorMessage(conn));
     277                PQclear(res);
     278                return EINVAL; /* It was probably a mistake in configuration file... */
     279        }
     280        PQclear(res);
     281       
     282        /* Ok, we are done */
     283        return 0;
    191284}
    192285
  • extensions/app_acct/acct_records.c

    r284 r285  
    7474}
    7575
     76/* Find the AVPs from configuration inside a received message */
    7677int acct_rec_map(struct acct_record_list * records, struct msg * msg)
    7778{
     79        struct avp * avp;
     80       
    7881        TRACE_ENTRY("%p %p", records, msg);
    7982       
    8083        /* For each AVP in the message, search if we have a corresponding unmap'd record */
     84        CHECK_FCT(  fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL)  );
     85        while (avp) {
     86                struct fd_list * li;
     87                struct dict_object * model;
     88               
     89                CHECK_FCT( fd_msg_model(avp, &model) );
     90                if (model != NULL) {    /* we ignore the AVPs we don't recognize */
     91               
     92                        /* Search this model in the list */
     93                        for (li = records->unmaped.next; li != &records->unmaped; li = li->next) {
     94                                struct acct_record_item * r = (struct acct_record_item *)(li->o);
     95                                if (r->param->avpobj == model) {
     96                                        /* It matches: save the AVP value and unlink this record from the unmap'd list */
     97                                        struct avp_hdr * h;
     98                                        CHECK_FCT( fd_msg_avp_hdr( avp, &h ) );
     99                                        r->value = h->avp_value;
     100                                        fd_list_unlink(&r->unmapd);
     101                                        records->nbunmap -= 1;
     102                                        break;
     103                                }
     104                        }
     105
     106                        /* Continue only while there are some AVPs to map */
     107                        if (FD_IS_LIST_EMPTY(&records->unmaped))
     108                                break;
     109                }
     110               
     111                /* Go to next AVP in the message */
     112                CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
     113        }
    81114       
    82         return ENOTSUP;
     115        /* Done */
     116        return 0;
    83117}
     118
     119/* Check that a mapped list is not empty and no required AVP is missing. Free the record list in case of error */
     120int acct_rec_validate(struct acct_record_list * records)
     121{
     122        struct fd_list * li;
     123        TRACE_ENTRY("%p", records);
     124        CHECK_PARAMS( records );
     125       
     126        /* Check at least one AVP was mapped */
     127        if (records->nball == records->nbunmap) {
     128                fd_log_debug("The received ACR does not contain any AVP from the configuration file.\n"
     129                                "This is an invalid situation. Please fix your configuration file.\n"
     130                                "One way to ensure this does not happen is to include Session-Id in the database.\n");
     131                acct_rec_empty(records);
     132                return EINVAL;
     133        }
     134       
     135        /* Now, check there is no required AVP unmap'd */
     136        for (li = records->unmaped.next; li != &records->unmaped; li = li->next) {
     137                struct acct_record_item * r = (struct acct_record_item *)(li->o);
     138                if (r->param->required && (r->index <= 1)) {
     139                        fd_log_debug("The received ACR does not contain the required AVP '%s'.\n", r->param->avpname);
     140                        acct_rec_empty(records);
     141                        return EINVAL;
     142                }
     143        }
     144       
     145        /* The record list is OK */
     146        return 0;
     147}
     148
     149/* Free all the items in an acct_record_list returned by acct_rec_prepare */
     150void acct_rec_empty(struct acct_record_list * records)
     151{
     152        TRACE_ENTRY("%p", records);
     153        CHECK_PARAMS_DO( records, return );
     154       
     155        while (!FD_IS_LIST_EMPTY(&records->all)) {
     156                struct acct_record_item * r = (struct acct_record_item *)(records->all.next);
     157                fd_list_unlink( &r->chain );
     158                fd_list_unlink( &r->unmapd );
     159                free(r);
     160        }
     161}
  • extensions/app_acct/app_acct.c

    r284 r285  
    3838#include "app_acct.h"
    3939
    40 /* Default callback for the Accounting application. */
    41 static int acct_fallback( struct msg ** msg, struct avp * avp, struct session * sess, enum disp_action * act)
    42 {
    43         /* This CB should never be called */
    44         TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act);
    45        
    46         fd_log_debug("Unexpected message received!\n");
    47        
    48         return ENOTSUP;
    49 }
     40/* Mandatory AVPs for the Accounting-Answer */
     41static struct {
     42        struct dict_object * Accounting_Record_Number;
     43        struct dict_object * Accounting_Record_Type;
     44} acct_dict;
    5045
    5146
     
    5449{
    5550        struct msg_hdr *hdr = NULL;
    56         struct msg *ans, *qry;
     51        struct msg * m;
    5752        struct avp * a = NULL;
    58         struct avp_hdr * h = NULL;
     53        struct avp_hdr * art=NULL, *arn=NULL; /* We keep a pointer on the Accounting-Record-{Type, Number} AVPs from the query */
    5954        char * s;
     55        struct acct_record_list rl;
    6056       
    6157        TRACE_ENTRY("%p %p %p %p", msg, avp, sess, act);
     
    6359                return EINVAL;
    6460       
    65         qry = *msg;
    66         /* Create the answer message, including the Session-Id AVP */
     61        m = *msg;
     62       
     63        /* Prepare a new record list */
     64        CHECK_FCT( acct_rec_prepare( &rl ) );
     65       
     66        /* Maps the AVPs from the query with this record list */
     67        CHECK_FCT( acct_rec_map( &rl, m ) );
     68       
     69        /* Check that at least one AVP was mapped */
     70        CHECK_FCT( acct_rec_validate( &rl ) );
     71       
     72        /* Now, save these mapped AVPs in the database */
     73        CHECK_FCT( acct_db_insert( &rl ) );
     74       
     75        acct_rec_empty( &rl );
     76       
     77        /* OK, we can send a positive reply now */
     78       
     79        /* Get Accounting-Record-{Number,Type} values */
     80        CHECK_FCT( fd_msg_search_avp ( m, acct_dict.Accounting_Record_Type, &a) );
     81        if (a) {
     82                CHECK_FCT( fd_msg_avp_hdr( a, &art )  );
     83        }
     84        CHECK_FCT( fd_msg_search_avp ( m, acct_dict.Accounting_Record_Number, &a) );
     85        if (a) {
     86                CHECK_FCT( fd_msg_avp_hdr( a, &arn )  );
     87        }
     88       
     89        /* Create the answer message */
    6790        CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, 0 ) );
    68         ans = *msg;
     91        m = *msg;
    6992
    7093        /* Set the Origin-Host, Origin-Realm, Result-Code AVPs */
    71         CHECK_FCT( fd_msg_rescode_set( ans, "DIAMETER_SUCCESS", NULL, NULL, 1 ) );
    72 
    73         fd_log_debug("--------------Received the following Accounting message:--------------\n");
    74 
    75         CHECK_FCT( fd_sess_getsid ( sess, &s ) );
    76         fd_log_debug("Session: %s\n", s);
    77 
    78         /* We may also dump other data from the message, such as Accounting session Id, number of packets, ...  */
    79 
    80         fd_log_debug("----------------------------------------------------------------------\n");
    81 
     94        CHECK_FCT( fd_msg_rescode_set( m, "DIAMETER_SUCCESS", NULL, NULL, 1 ) );
     95       
     96        /* Add the mandatory AVPs in the ACA */
     97        if (art) {
     98                CHECK_FCT( fd_msg_avp_new ( acct_dict.Accounting_Record_Type, 0, &a ) );
     99                CHECK_FCT( fd_msg_avp_setvalue( a, art->avp_value ) );
     100                CHECK_FCT( fd_msg_avp_add( m, MSG_BRW_LAST_CHILD, a ) );
     101        }
     102        if (arn) {
     103                CHECK_FCT( fd_msg_avp_new ( acct_dict.Accounting_Record_Number, 0, &a ) );
     104                CHECK_FCT( fd_msg_avp_setvalue( a, arn->avp_value ) );
     105                CHECK_FCT( fd_msg_avp_add( m, MSG_BRW_LAST_CHILD, a ) );
     106        }
     107       
    82108        /* Send the answer */
    83         CHECK_FCT( fd_msg_send( msg, NULL, NULL ) );
    84                
     109        *act = DISP_ACT_SEND;
    85110        return 0;
    86111}
     
    94119        TRACE_ENTRY("%p", conffile);
    95120       
     121#ifndef TEST_DEBUG /* We do this differently in the test scenario */
    96122        /* Initialize the configuration and parse the file */
    97123        CHECK_FCT( acct_conf_init() );
    98124        CHECK_FCT( acct_conf_parse(conffile) );
    99125        CHECK_FCT( acct_conf_check(conffile) );
     126#endif /* TEST_DEBUG */
    100127       
    101128        /* Now initialize the database module */
    102129        CHECK_FCT( acct_db_init() );
    103130       
     131        /* Search the AVPs we will need in this file */
     132        CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &acct_dict.Accounting_Record_Number, ENOENT) );
     133        CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &acct_dict.Accounting_Record_Type, ENOENT) );
     134       
    104135        /* Register the dispatch callbacks */
    105136        memset(&data, 0, sizeof(data));
    106137        CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Base Accounting", &data.app, ENOENT) );
    107         CHECK_FCT( fd_disp_register( acct_fallback, DISP_HOW_APPID, &data, NULL ) );
    108138        CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Accounting-Request", &data.command, ENOENT) );
    109139        CHECK_FCT( fd_disp_register( acct_cb, DISP_HOW_CC, &data, NULL ) );
  • extensions/app_acct/app_acct.h

    r284 r285  
    8484        unsigned                 index; /* in case of multi */
    8585        union avp_value         *value; /* If the AVP was found in the message, this points to its value. Otherwise, NULL */
     86        union {
     87                uint32_t v32    /* Storage area for network byte-order copy of the AVP value */;
     88                uint64_t v64;
     89                char     c;     /* pointer that is passed to the database */
     90        }                        scalar;/* for scalar AVP (all types except OCTETSTRING) we copy in this area the value in network byte order */
    8691};
    8792
     
    112117int acct_rec_prepare(struct acct_record_list * records);
    113118int acct_rec_map(struct acct_record_list * records, struct msg * msg);
     119int acct_rec_validate(struct acct_record_list * records);
     120void acct_rec_empty(struct acct_record_list * records);
  • freeDiameter/extensions.c

    r14 r285  
    153153
    154154/* Now unload the extensions and free the memory */
    155 int fd_ext_fini( void )
     155int fd_ext_term( void )
    156156{
    157157        TRACE_ENTRY();
  • freeDiameter/fD.h

    r258 r285  
    8484int fd_ext_load();
    8585void fd_ext_dump(void);
    86 int fd_ext_fini(void);
     86int fd_ext_term(void);
    8787
    8888/* Messages */
  • freeDiameter/main.c

    r258 r285  
    165165        CHECK_FCT_DO( fd_rtdisp_fini(), /* Stop routing threads and destroy routing queues */ );
    166166       
    167         CHECK_FCT_DO( fd_ext_fini(), /* Cleanup all extensions */ );
     167        CHECK_FCT_DO( fd_ext_term(), /* Cleanup all extensions */ );
    168168        CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ );
    169169       
  • freeDiameter/tests/CMakeLists.txt

    r29 r285  
    5050ADD_LIBRARY(fDcore STATIC ${TEST_COMMON_SRC})
    5151
     52##############################
     53# App_acct test
     54
     55IF(BUILD_APP_ACCT)
     56        OPTION(TEST_APP_ACCT "Test app_acct extension? (Requires a configured database, see testappacct.c for details)" OFF)
     57        IF(TEST_APP_ACCT)
     58       
     59                OPTION(TEST_APP_ACCT_CONNINFO "The connection string to the database")
     60                IF(TEST_APP_ACCT_CONNINFO)
     61                        ADD_DEFINITIONS(-DTEST_CONNINFO="${TEST_APP_ACCT_CONNINFO}")
     62                ENDIF(TEST_APP_ACCT_CONNINFO)
     63       
     64                SET(TEST_LIST ${TEST_LIST} testappacct)
     65
     66                # Extension dependencies
     67                FIND_PACKAGE(PostgreSQL REQUIRED)
     68                INCLUDE_DIRECTORIES(${POSTGRESQL_INCLUDE_DIR})
     69                SET(testappacct_ADDITIONAL_LIB ${POSTGRESQL_LIBRARIES})
     70
     71                # List of source files, copied from the extension CMakeLists.
     72                SET( APP_ACCT_SRC
     73                        app_acct.h
     74                        app_acct.c
     75                        acct_db.c
     76                        acct_records.c
     77                )
     78                SET( APP_ACCT_SRC_GEN
     79                        lex.acct_conf.c
     80                        acct_conf.tab.c
     81                        acct_conf.tab.h
     82                )
     83
     84                # The extension headers
     85                INCLUDE_DIRECTORIES( "../../extensions/app_acct" )
     86
     87                SET(testappacct_ADDITIONAL "")
     88
     89                FOREACH( SRC_FILE ${APP_ACCT_SRC})
     90                   SET(testappacct_ADDITIONAL ${testappacct_ADDITIONAL} "../../extensions/app_acct/${SRC_FILE}")
     91                ENDFOREACH(SRC_FILE)
     92
     93                FOREACH( SRC_FILE ${APP_ACCT_SRC_GEN})
     94                   SET(testappacct_ADDITIONAL ${testappacct_ADDITIONAL} "${CMAKE_CURRENT_BINARY_DIR}/../../extensions/app_acct/${SRC_FILE}")
     95                ENDFOREACH(SRC_FILE)
     96
     97        ENDIF(TEST_APP_ACCT)
     98ENDIF(BUILD_APP_ACCT)
     99
    52100
    53101#############################
    54102# Compile each test
    55103FOREACH( TEST ${TEST_LIST} )
    56    ADD_EXECUTABLE(${TEST} ${TEST}.c tests.h)
    57    TARGET_LINK_LIBRARIES(${TEST} fDcore ${FD_LIBS})
     104   ADD_EXECUTABLE(${TEST} ${TEST}.c tests.h ${${TEST}_ADDITIONAL})
     105   TARGET_LINK_LIBRARIES(${TEST} fDcore ${FD_LIBS} ${${TEST}_ADDITIONAL_LIB})
    58106   ADD_TEST(${TEST} ${EXECUTABLE_OUTPUT_PATH}/${TEST})
    59107ENDFOREACH( TEST )
  • freeDiameter/tests/tests.h

    r258 r285  
    125125        int c;
    126126        int no_timeout = 0;
    127         while ((c = getopt (argc, argv, "dqn")) != -1) {
     127        while ((c = getopt (argc, argv, "dqnf:")) != -1) {
    128128                switch (c) {
    129129                        case 'd':       /* Increase verbosity of debug messages.  */
     
    139139                                break;
    140140                       
     141                        case 'f':       /* Full debug for the function with this name.  */
     142                                #ifdef DEBUG
     143                                fd_debug_one_function = optarg;
     144                                #else /* DEBUG */
     145                                TRACE_DEBUG(INFO, "Error: must compile with DEBUG support to use this feature");
     146                                return EINVAL;
     147                                #endif /* DEBUG */
     148                                break;
     149                               
    141150                        default:        /* bug: option not considered.  */
    142151                                return;
Note: See TracChangeset for help on using the changeset viewer.