1 /*
2 * SNMPStats Module
3 * Copyright (C) 2006 SOMA Networks, INC.
4 * Written by: Jeffrey Magder (jmagder@somanetworks.com)
5 *
6 * This file is part of Kamailio, a free SIP server.
7 *
8 * Kamailio is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version
12 *
13 * Kamailio is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 *
23 * Note: this file originally auto-generated by mib2c using
24 * mib2c.array-user.conf
25 *
26 * This file implements the kamailioSIPRegUserTable. For a full description of
27 * the table, please see the KAMAILIO-SIP-SERVER-MIB.
28 *
29 * Understanding this code will be much simpler with the following information:
30 *
31 * 1) All rows are indexed by an integer user index. This is different from the
32 * usrloc module, which indexes by strings. This less natural indexing
33 * scheme was required due to SNMP String index limitations. (for example,
34 * SNMP has maximum index lengths.)
35 *
36 * 2) We need a quick way of mapping usrloc indices to our integer indices. For
37 * this reason a string indexed Hash Table was created, with each entry mapping
38 * to an integer user index.
39 *
40 * This hash table is used by the kamailioSIPContactTable (the hash table also
41 * maps a user to its contacts), as well as the kamailioSIPRegUserLookupTable.
42 * The hash table is also used for quick lookups when a user expires. (i.e, it
43 * gives us a more direct reference, instead of having to search the whole
44 * table).
45 *
46 * 3) We are informed about new/expired users via a callback mechanism from the
47 * usrloc module. Because of NetSNMP inefficiencies, we had to abstract this
48 * process. Specifically:
49 *
50 * - It can take a long time for the NetSNMP code base to populate a table with
51 * a large number of records.
52 *
53 * - We rely on callbacks for updated user information.
54 *
55 * Clearly, using the SNMPStats module in this situation could lead to some
56 * big performance loses if we don't find another way to deal with this. The
57 * solution was to use an interprocess communications buffer.
58 *
59 * Instead of adding the record directly to the table, the callback functions
60 * now adds either an add/delete command to the interprocessBuffer. When an
61 * snmp request is received by the SNMPStats sub-process, it will consume
62 * this interprocess buffer, adding and deleting users. When it is finished,
63 * it can service the SNMP request.
64 *
65 * This doesn't remove the NetSNMP inefficiency, but instead moves it to a
66 * non-critical path. Such an approach allows SNMP support with almost no
67 * overhead to the rest of Kamailio.
68 */
69
70 #include <net-snmp/net-snmp-config.h>
71 #include <net-snmp/net-snmp-includes.h>
72 #include <net-snmp/agent/net-snmp-agent-includes.h>
73
74 #include <net-snmp/library/snmp_assert.h>
75
76 #include "hashTable.h"
77 #include "interprocess_buffer.h"
78 #include "utilities.h"
79 #include "snmpSIPRegUserTable.h"
80 #include "snmpstats_globals.h"
81
82 #include "../../core/sr_module.h"
83 #include "../../core/locking.h"
84 #include "../usrloc/usrloc.h"
85
86 static netsnmp_handler_registration *my_handler = NULL;
87 static netsnmp_table_array_callbacks cb;
88
89 oid kamailioSIPRegUserTable_oid[] = {kamailioSIPRegUserTable_TABLE_OID};
90 size_t kamailioSIPRegUserTable_oid_len =
91 OID_LENGTH(kamailioSIPRegUserTable_oid);
92
93
94 /* If the usrloc module is loaded, this function will grab hooks into its
95 * callback registration function, and add handleContactCallbacks() as the
96 * callback for UL_CONTACT_INSERT and UL_CONTACT_EXPIRE.
97 *
98 * Returns 1 on success, and zero otherwise. */
registerForUSRLOCCallbacks(void)99 int registerForUSRLOCCallbacks(void)
100 {
101 bind_usrloc_t bind_usrloc;
102 usrloc_api_t ul;
103
104 bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
105 if(!bind_usrloc) {
106 LM_ERR("Can't find ul_bind_usrloc\n");
107 goto error;
108 }
109 if(bind_usrloc(&ul) < 0 || ul.register_ulcb == NULL) {
110 LM_ERR("Can't bind usrloc\n");
111 goto error;
112 }
113
114 ul.register_ulcb(UL_CONTACT_INSERT, handleContactCallbacks, NULL);
115
116 ul.register_ulcb(UL_CONTACT_EXPIRE, handleContactCallbacks, NULL);
117
118 return 1;
119
120 error:
121 LM_INFO("failed to register for callbacks with the USRLOC module.");
122 LM_INFO("kamailioSIPContactTable and kamailioSIPUserTable will be"
123 " unavailable");
124 return 0;
125 }
126
127
128 /* Removes an SNMP row indexed by userIndex, and frees the string and index it
129 * pointed to. */
deleteRegUserRow(int userIndex)130 void deleteRegUserRow(int userIndex)
131 {
132
133 kamailioSIPRegUserTable_context *theRow;
134
135 netsnmp_index indexToRemove;
136 oid indexToRemoveOID;
137
138 indexToRemoveOID = userIndex;
139 indexToRemove.oids = &indexToRemoveOID;
140 indexToRemove.len = 1;
141
142 theRow = CONTAINER_FIND(cb.container, &indexToRemove);
143
144 /* The userURI is shared memory, the index.oids was allocated from
145 * pkg_malloc(), and theRow was made with the NetSNMP API which uses
146 * malloc() */
147 if(theRow != NULL) {
148 CONTAINER_REMOVE(cb.container, &indexToRemove);
149 pkg_free(theRow->kamailioSIPUserUri);
150 pkg_free(theRow->index.oids);
151 free(theRow);
152 }
153 }
154
155 /*
156 * Adds or updates a user:
157 *
158 * - If a user with the name userName exists, its 'number of contacts' count
159 * will be incremented.
160 * - If the user doesn't exist, the user will be added to the table, and its
161 * number of contacts' count set to 1.
162 */
updateUser(char * userName)163 void updateUser(char *userName)
164 {
165 int userIndex;
166
167 aorToIndexStruct_t *newRecord;
168
169 aorToIndexStruct_t *existingRecord =
170 findHashRecord(hashTable, userName, HASH_SIZE);
171
172 /* We found an existing record, so we need to update its 'number of
173 * contacts' count. */
174 if(existingRecord != NULL) {
175 existingRecord->numContacts++;
176 return;
177 }
178
179 /* Make a new row, and insert a record of it into our mapping data
180 * structures */
181 userIndex = createRegUserRow(userName);
182
183 if(userIndex == 0) {
184 LM_ERR("kamailioSIPRegUserTable ran out of memory."
185 " Not able to add user: %s",
186 userName);
187 return;
188 }
189
190 newRecord = createHashRecord(userIndex, userName);
191
192 /* If we couldn't create a record in the hash table, then we won't be
193 * able to access this row properly later. So remove the row from the
194 * table and fail. */
195 if(newRecord == NULL) {
196 deleteRegUserRow(userIndex);
197 LM_ERR("kamailioSIPRegUserTable was not able to push %s into the hash."
198 " User not added to this table\n",
199 userName);
200 return;
201 }
202
203 /* Insert the new record of the mapping data structure into the hash
204 * table */
205 /*insertHashRecord(hashTable,
206 createHashRecord(userIndex, userName),
207 HASH_SIZE);*/
208
209 insertHashRecord(hashTable, newRecord, HASH_SIZE);
210 }
211
212
213 /* Creates a row and inserts it.
214 *
215 * Returns: The rows userIndex on success, and 0 otherwise. */
createRegUserRow(char * stringToRegister)216 int createRegUserRow(char *stringToRegister)
217 {
218 int static index = 0;
219
220 index++;
221
222 kamailioSIPRegUserTable_context *theRow;
223
224 oid *OIDIndex;
225 int stringLength;
226
227 theRow = SNMP_MALLOC_TYPEDEF(kamailioSIPRegUserTable_context);
228
229 if(theRow == NULL) {
230 LM_ERR("failed to create a row for kamailioSIPRegUserTable\n");
231 return 0;
232 }
233
234 OIDIndex = pkg_malloc(sizeof(oid));
235
236 if(OIDIndex == NULL) {
237 free(theRow);
238 LM_ERR("failed to create a row for kamailioSIPRegUserTable\n");
239 return 0;
240 }
241
242 stringLength = strlen(stringToRegister);
243
244 OIDIndex[0] = index;
245
246 theRow->index.len = 1;
247 theRow->index.oids = OIDIndex;
248 theRow->kamailioSIPUserIndex = index;
249
250 theRow->kamailioSIPUserUri =
251 (unsigned char *)pkg_malloc(stringLength * sizeof(char));
252 if(theRow->kamailioSIPUserUri == NULL) {
253 pkg_free(OIDIndex);
254 free(theRow);
255 LM_ERR("failed to create a row for kamailioSIPRegUserTable\n");
256 return 0;
257 }
258 memcpy(theRow->kamailioSIPUserUri, stringToRegister, stringLength);
259
260 theRow->kamailioSIPUserUri_len = stringLength;
261
262 theRow->kamailioSIPUserAuthenticationFailures = 0;
263
264 CONTAINER_INSERT(cb.container, theRow);
265
266 return index;
267 }
268
269 /* Initializes the kamailioSIPRegUserTable module. */
init_kamailioSIPRegUserTable(void)270 void init_kamailioSIPRegUserTable(void)
271 {
272 /* Register this table with the master agent */
273 initialize_table_kamailioSIPRegUserTable();
274
275 /* We need to create a default row, so create DefaultUser */
276 static char *defaultUser = "DefaultUser";
277
278 createRegUserRow(defaultUser);
279 }
280
281
282 /*
283 * Initialize the kamailioSIPRegUserTable table by defining its contents and how
284 * it's structured
285 */
initialize_table_kamailioSIPRegUserTable(void)286 void initialize_table_kamailioSIPRegUserTable(void)
287 {
288 netsnmp_table_registration_info *table_info;
289
290 if(my_handler) {
291 snmp_log(LOG_ERR, "initialize_table_kamailioSIPRegUserTable_hand"
292 "ler called again\n");
293 return;
294 }
295
296 memset(&cb, 0x00, sizeof(cb));
297
298 /* create the table structure itself */
299 table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
300 if(table_info==NULL) {
301 snmp_log(LOG_ERR, "failed to allocate table_info\n");
302 return;
303 }
304
305 my_handler = netsnmp_create_handler_registration("kamailioSIPRegUserTable",
306 netsnmp_table_array_helper_handler, kamailioSIPRegUserTable_oid,
307 kamailioSIPRegUserTable_oid_len, HANDLER_CAN_RONLY);
308
309 if(!my_handler) {
310 SNMP_FREE(table_info);
311 snmp_log(LOG_ERR, "malloc failed in initialize_table_kamailio"
312 "SIPRegUserTable_handler\n");
313 return; /** mallocs failed */
314 }
315
316 netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED);
317
318 table_info->min_column = kamailioSIPRegUserTable_COL_MIN;
319 table_info->max_column = kamailioSIPRegUserTable_COL_MAX;
320
321 cb.get_value = kamailioSIPRegUserTable_get_value;
322 cb.container = netsnmp_container_find("kamailioSIPRegUserTable_primary:"
323 "kamailioSIPRegUserTable:"
324 "table_container");
325
326 DEBUGMSGTL(("initialize_table_kamailioSIPRegUserTable",
327 "Registering table kamailioSIPRegUserTable "
328 "as a table array\n"));
329
330 netsnmp_table_container_register(
331 my_handler, table_info, &cb, cb.container, 1);
332 }
333
334
335 /* Handles SNMP GET requests. */
kamailioSIPRegUserTable_get_value(netsnmp_request_info * request,netsnmp_index * item,netsnmp_table_request_info * table_info)336 int kamailioSIPRegUserTable_get_value(netsnmp_request_info *request,
337 netsnmp_index *item, netsnmp_table_request_info *table_info)
338 {
339 /* First things first, we need to consume the interprocess buffer, in
340 * case something has changed. We want to return the freshest data. */
341 consumeInterprocessBuffer();
342
343 netsnmp_variable_list *var = request->requestvb;
344
345 kamailioSIPRegUserTable_context *context =
346 (kamailioSIPRegUserTable_context *)item;
347
348 switch(table_info->colnum) {
349
350 case COLUMN_KAMAILIOSIPUSERURI:
351 /** SnmpAdminString = ASN_OCTET_STR */
352 snmp_set_var_typed_value(var, ASN_OCTET_STR,
353 (unsigned char *)context->kamailioSIPUserUri,
354 context->kamailioSIPUserUri_len);
355 break;
356
357 case COLUMN_KAMAILIOSIPUSERAUTHENTICATIONFAILURES:
358 /** COUNTER = ASN_COUNTER */
359 snmp_set_var_typed_value(var, ASN_COUNTER,
360 (unsigned char *)&context
361 ->kamailioSIPUserAuthenticationFailures,
362 sizeof(context->kamailioSIPUserAuthenticationFailures));
363 break;
364
365 default: /** We shouldn't get here */
366 snmp_log(LOG_ERR, "unknown column in "
367 "kamailioSIPRegUserTable_get_value\n");
368
369 return SNMP_ERR_GENERR;
370 }
371
372 return SNMP_ERR_NOERROR;
373 }
374
375
kamailioSIPRegUserTable_get_by_idx(netsnmp_index * hdr)376 const kamailioSIPRegUserTable_context *kamailioSIPRegUserTable_get_by_idx(
377 netsnmp_index *hdr)
378 {
379 return (const kamailioSIPRegUserTable_context *)CONTAINER_FIND(
380 cb.container, hdr);
381 }