You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/snmpstats/snmpSIPRegUserTable.c

398 lines
11 KiB

/*
* $Id$
*
* SNMPStats Module
* Copyright (C) 2006 SOMA Networks, INC.
* Written by: Jeffrey Magder (jmagder@somanetworks.com)
*
* This file is part of Kamailio, a free SIP server.
*
* Kamailio is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* Kamailio is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* History:
* --------
* 2006-11-23 initial version (jmagder)
*
* Note: this file originally auto-generated by mib2c using
* mib2c.array-user.conf
*
* This file implements the kamailioSIPRegUserTable. For a full description of
* the table, please see the KAMAILIO-SIP-SERVER-MIB.
*
* Understanding this code will be much simpler with the following information:
*
* 1) All rows are indexed by an integer user index. This is different from the
* usrloc module, which indexes by strings. This less natural indexing
* scheme was required due to SNMP String index limitations. (for example,
* SNMP has maximum index lengths.)
*
* 2) We need a quick way of mapping usrloc indices to our integer indices. For
* this reason a string indexed Hash Table was created, with each entry mapping
* to an integer user index.
*
* This hash table is used by the kamailioSIPContactTable (the hash table also
* maps a user to its contacts), as well as the kamailioSIPRegUserLookupTable.
* The hash table is also used for quick lookups when a user expires. (i.e, it
* gives us a more direct reference, instead of having to search the whole
* table).
*
* 3) We are informed about new/expired users via a callback mechanism from the
* usrloc module. Because of NetSNMP inefficiencies, we had to abstract this
* process. Specifically:
*
* - It can take a long time for the NetSNMP code base to populate a table with
* a large number of records.
*
* - We rely on callbacks for updated user information.
*
* Clearly, using the SNMPStats module in this situation could lead to some
* big performance loses if we don't find another way to deal with this. The
* solution was to use an interprocess communications buffer.
*
* Instead of adding the record directly to the table, the callback functions
* now adds either an add/delete command to the interprocessBuffer. When an
* snmp request is recieved by the SNMPStats sub-process, it will consume
* this interprocess buffer, adding and deleting users. When it is finished,
* it can service the SNMP request.
*
* This doesn't remove the NetSNMP inefficiency, but instead moves it to a
* non-critical path. Such an approach allows SNMP support with almost no
* overhead to the rest of Kamailio.
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/library/snmp_assert.h>
#include "hashTable.h"
#include "interprocess_buffer.h"
#include "utilities.h"
#include "snmpSIPRegUserTable.h"
#include "snmpstats_globals.h"
#include "../../sr_module.h"
#include "../../locking.h"
#include "../usrloc/usrloc.h"
static netsnmp_handler_registration *my_handler = NULL;
static netsnmp_table_array_callbacks cb;
oid kamailioSIPRegUserTable_oid[] = { kamailioSIPRegUserTable_TABLE_OID };
size_t kamailioSIPRegUserTable_oid_len = OID_LENGTH(kamailioSIPRegUserTable_oid);
/* If the usrloc module is loaded, this function will grab hooks into its
* callback registration function, and add handleContactCallbacks() as the
* callback for UL_CONTACT_INSERT and UL_CONTACT_EXPIRE.
*
* Returns 1 on success, and zero otherwise. */
int registerForUSRLOCCallbacks(void)
{
bind_usrloc_t bind_usrloc;
usrloc_api_t ul;
bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
if (!bind_usrloc)
{
LM_ERR("Can't find ul_bind_usrloc\n");
goto error;
}
if (bind_usrloc(&ul) < 0 || ul.register_ulcb == NULL)
{
LM_ERR("Can't bind usrloc\n");
goto error;
}
ul.register_ulcb(UL_CONTACT_INSERT, handleContactCallbacks, NULL);
ul.register_ulcb(UL_CONTACT_EXPIRE, handleContactCallbacks, NULL);
return 1;
error:
LM_INFO("failed to register for callbacks with the USRLOC module.");
LM_INFO("kamailioSIPContactTable and kamailioSIPUserTable will be"
" unavailable");
return 0;
}
/* Removes an SNMP row indexed by userIndex, and frees the string and index it
* pointed to. */
void deleteRegUserRow(int userIndex)
{
kamailioSIPRegUserTable_context *theRow;
netsnmp_index indexToRemove;
oid indexToRemoveOID;
indexToRemoveOID = userIndex;
indexToRemove.oids = &indexToRemoveOID;
indexToRemove.len = 1;
theRow = CONTAINER_FIND(cb.container, &indexToRemove);
/* The userURI is shared memory, the index.oids was allocated from
* pkg_malloc(), and theRow was made with the NetSNMP API which uses
* malloc() */
if (theRow != NULL) {
CONTAINER_REMOVE(cb.container, &indexToRemove);
pkg_free(theRow->kamailioSIPUserUri);
pkg_free(theRow->index.oids);
free(theRow);
}
}
/*
* Adds or updates a user:
*
* - If a user with the name userName exists, its 'number of contacts' count
* will be incremented.
* - If the user doesn't exist, the user will be added to the table, and its
* number of contacts' count set to 1.
*/
void updateUser(char *userName)
{
int userIndex;
aorToIndexStruct_t *newRecord;
aorToIndexStruct_t *existingRecord =
findHashRecord(hashTable, userName, HASH_SIZE);
/* We found an existing record, so we need to update its 'number of
* contacts' count. */
if (existingRecord != NULL)
{
existingRecord->numContacts++;
return;
}
/* Make a new row, and insert a record of it into our mapping data
* structures */
userIndex = createRegUserRow(userName);
if (userIndex == 0)
{
LM_ERR("kamailioSIPRegUserTable ran out of memory."
" Not able to add user: %s", userName);
return;
}
newRecord = createHashRecord(userIndex, userName);
/* If we couldn't create a record in the hash table, then we won't be
* able to access this row properly later. So remove the row from the
* table and fail. */
if (newRecord == NULL) {
deleteRegUserRow(userIndex);
LM_ERR("kamailioSIPRegUserTable was not able to push %s into the hash."
" User not added to this table\n", userName);
return;
}
/* Insert the new record of the mapping data structure into the hash
* table */
/*insertHashRecord(hashTable,
createHashRecord(userIndex, userName),
HASH_SIZE);*/
insertHashRecord(hashTable,
newRecord,
HASH_SIZE);
}
/* Creates a row and inserts it.
*
* Returns: The rows userIndex on success, and 0 otherwise. */
int createRegUserRow(char *stringToRegister)
{
int static index = 0;
index++;
kamailioSIPRegUserTable_context *theRow;
oid *OIDIndex;
int stringLength;
theRow = SNMP_MALLOC_TYPEDEF(kamailioSIPRegUserTable_context);
if (theRow == NULL) {
LM_ERR("failed to create a row for kamailioSIPRegUserTable\n");
return 0;
}
OIDIndex = pkg_malloc(sizeof(oid));
if (OIDIndex == NULL) {
free(theRow);
LM_ERR("failed to create a row for kamailioSIPRegUserTable\n");
return 0;
}
stringLength = strlen(stringToRegister);
OIDIndex[0] = index;
theRow->index.len = 1;
theRow->index.oids = OIDIndex;
theRow->kamailioSIPUserIndex = index;
theRow->kamailioSIPUserUri = (unsigned char*)pkg_malloc(stringLength* sizeof(char));
if(theRow->kamailioSIPUserUri== NULL)
{
pkg_free(OIDIndex);
free(theRow);
LM_ERR("failed to create a row for kamailioSIPRegUserTable\n");
return 0;
}
memcpy(theRow->kamailioSIPUserUri, stringToRegister, stringLength);
theRow->kamailioSIPUserUri_len = stringLength;
theRow->kamailioSIPUserAuthenticationFailures = 0;
CONTAINER_INSERT(cb.container, theRow);
return index;
}
/* Initializes the kamailioSIPRegUserTable module. */
void init_kamailioSIPRegUserTable(void)
{
/* Register this table with the master agent */
initialize_table_kamailioSIPRegUserTable();
/* We need to create a default row, so create DefaultUser */
static char *defaultUser = "DefaultUser";
createRegUserRow(defaultUser);
}
/*
* Initialize the kamailioSIPRegUserTable table by defining its contents and how
* it's structured
*/
void initialize_table_kamailioSIPRegUserTable(void)
{
netsnmp_table_registration_info *table_info;
if(my_handler) {
snmp_log(LOG_ERR, "initialize_table_kamailioSIPRegUserTable_hand"
"ler called again\n");
return;
}
memset(&cb, 0x00, sizeof(cb));
/* create the table structure itself */
table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
my_handler =
netsnmp_create_handler_registration(
"kamailioSIPRegUserTable",
netsnmp_table_array_helper_handler,
kamailioSIPRegUserTable_oid,
kamailioSIPRegUserTable_oid_len,
HANDLER_CAN_RONLY);
if (!my_handler || !table_info) {
snmp_log(LOG_ERR, "malloc failed in initialize_table_kamailio"
"SIPRegUserTable_handler\n");
return; /** mallocs failed */
}
netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED);
table_info->min_column = kamailioSIPRegUserTable_COL_MIN;
table_info->max_column = kamailioSIPRegUserTable_COL_MAX;
cb.get_value = kamailioSIPRegUserTable_get_value;
cb.container = netsnmp_container_find("kamailioSIPRegUserTable_primary:"
"kamailioSIPRegUserTable:" "table_container");
DEBUGMSGTL(("initialize_table_kamailioSIPRegUserTable",
"Registering table kamailioSIPRegUserTable "
"as a table array\n"));
netsnmp_table_container_register(my_handler, table_info, &cb,
cb.container, 1);
}
/* Handles SNMP GET requests. */
int kamailioSIPRegUserTable_get_value(
netsnmp_request_info *request,
netsnmp_index *item,
netsnmp_table_request_info *table_info )
{
/* First things first, we need to consume the interprocess buffer, in
* case something has changed. We want to return the freshest data. */
consumeInterprocessBuffer();
netsnmp_variable_list *var = request->requestvb;
kamailioSIPRegUserTable_context *context =
(kamailioSIPRegUserTable_context *)item;
switch(table_info->colnum)
{
case COLUMN_KAMAILIOSIPUSERURI:
/** SnmpAdminString = ASN_OCTET_STR */
snmp_set_var_typed_value(var, ASN_OCTET_STR,
(unsigned char*)context->kamailioSIPUserUri,
context->kamailioSIPUserUri_len );
break;
case COLUMN_KAMAILIOSIPUSERAUTHENTICATIONFAILURES:
/** COUNTER = ASN_COUNTER */
snmp_set_var_typed_value(var, ASN_COUNTER,
(unsigned char*)
&context->kamailioSIPUserAuthenticationFailures,
sizeof(
context->kamailioSIPUserAuthenticationFailures));
break;
default: /** We shouldn't get here */
snmp_log(LOG_ERR, "unknown column in "
"kamailioSIPRegUserTable_get_value\n");
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
}
const kamailioSIPRegUserTable_context *
kamailioSIPRegUserTable_get_by_idx(netsnmp_index * hdr)
{
return (const kamailioSIPRegUserTable_context *)
CONTAINER_FIND(cb.container, hdr );
}