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  */
24 
25 /*!
26  * \file
27  * \brief SNMP statistic module, hash table
28  * Hash Stuff;
29  * \author jmagder
30  *
31  * For an overview of its structures, please see hashTable.h
32  *
33  * Potential Performance Improvements: Pass the length of the aor strings around
34  * everywhere, so we don't have to calculate it ourselves.
35  * \ingroup snmpstats
36  * - Module: \ref snmpstats
37  */
38 
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "hashTable.h"
43 #include "../../core/dprint.h"
44 #include "../../core/mem/mem.h"
45 
46 #include <net-snmp/net-snmp-config.h>
47 #include <net-snmp/net-snmp-includes.h>
48 #include <net-snmp/agent/net-snmp-agent-includes.h>
49 #include "snmpSIPRegUserTable.h"
50 
51 
52 /*! Calculates and returns a hash index to a hash table.  The index is calculated
53  * by summing up all the characters specified with theString, and using the
54  * hashTableSize as the modulus.  */
calculateHashSlot(char * theString,int hashTableSize)55 int calculateHashSlot(char *theString, int hashTableSize)
56 {
57 	char *currentCharacter = theString;
58 	int runningTotal = 0;
59 
60 	while(*currentCharacter != '\0') {
61 		runningTotal += *currentCharacter;
62 		currentCharacter++;
63 	}
64 
65 	return runningTotal % hashTableSize;
66 }
67 
68 /*! Searches the hash table specified as theTable, of size 'size', for a record
69  * indexed with 'aor'.  If a match is found, then an aorToIndextStruct_t
70  * structure is returned.
71  *
72  * This function is called to discover the map between Kamailio's "aor"
73  * (Address of Records) indexing scheme, and the SNMPStats modules integer
74  * indexing scheme for its contact/user data.
75  *
76  * Returns: the aorToIndexStruct_t mapping structure if a match was found,
77  *          or NULL otherwise.
78  */
findHashRecord(hashSlot_t * theTable,char * aor,int size)79 aorToIndexStruct_t *findHashRecord(hashSlot_t *theTable, char *aor, int size)
80 {
81 	int hashIndex = calculateHashSlot(aor, size);
82 	int aorStringLength = strlen(aor);
83 
84 	aorToIndexStruct_t *currentRecord = theTable[hashIndex].first;
85 
86 	while(currentRecord != NULL) {
87 
88 		/* If the strings are the same length and the same in every
89 		 * other way, then return the given record. */
90 		if(currentRecord->aorLength == aorStringLength
91 				&& memcmp(currentRecord->aor, aor, aorStringLength) == 0) {
92 			return currentRecord;
93 		}
94 
95 		currentRecord = currentRecord->next;
96 	}
97 
98 	return NULL;
99 }
100 
101 
102 /*! Returns a chunk of memory large enough to store 'size' hashSlot's.  The
103  * table will contain mappings between Kamailio's "aor" user/contact indexing
104  * scheme, and SNMPStats integer indexing scheme */
createHashTable(int size)105 hashSlot_t *createHashTable(int size)
106 {
107 	hashSlot_t *hashTable = NULL;
108 	int numberOfBytes = sizeof(hashSlot_t) * size;
109 
110 	hashTable = pkg_malloc(numberOfBytes);
111 
112 	if(!hashTable) {
113 		LM_ERR("no more pkg memory");
114 		return NULL;
115 	}
116 
117 	memset(hashTable, 0, numberOfBytes);
118 
119 	return hashTable;
120 }
121 
122 
123 /*! Inserts the record specified with 'theRecord' into our hash table. */
insertHashRecord(hashSlot_t * theTable,aorToIndexStruct_t * theRecord,int size)124 void insertHashRecord(
125 		hashSlot_t *theTable, aorToIndexStruct_t *theRecord, int size)
126 {
127 	int hashIndex = calculateHashSlot(theRecord->aor, size);
128 
129 	/* Link up this record backward so that it points to whatever the last
130 	 * 'last element' was.  */
131 	theRecord->prev = theTable[hashIndex].last;
132 
133 	/* This is the first record in the hash table, so assign the first and
134 	 * last pointers to this record. */
135 	if(theTable[hashIndex].last == NULL) {
136 
137 		theTable[hashIndex].last = theRecord;
138 		theTable[hashIndex].first = theRecord;
139 
140 	} else {
141 
142 		/* Make the element that was previously the last element point
143 		 * to this new record, as its next element. */
144 		theTable[hashIndex].last->next = theRecord;
145 
146 		/* Reassign the 'final element' pointer to this new record. */
147 		theTable[hashIndex].last = theRecord;
148 	}
149 }
150 
151 /*!
152  * This function will search the provided hash table for an entry indexed by
153  * 'aor'.  If an entry is found then:
154  *
155  *   - Its numContacts counter will be decremented.
156  *   - If its numContacts counter reaches zero, then the entry will be removed
157  *     from the hash table.
158  *
159  */
deleteUser(hashSlot_t * theTable,char * aor,int hashTableSize)160 void deleteUser(hashSlot_t *theTable, char *aor, int hashTableSize)
161 {
162 	int hashIndex = calculateHashSlot(aor, hashTableSize);
163 	int searchStringLength = strlen(aor);
164 
165 	aorToIndexStruct_t *currentRecord = theTable[hashIndex].first;
166 
167 	while(currentRecord != NULL) {
168 
169 		/* First make sure both strings are the same length.  If so,
170 		 * then compare all bytes.  If this succeeds, then we need to
171 		 * link up the previous and next element together. */
172 		if(currentRecord->aorLength == searchStringLength
173 				&& memcmp(currentRecord->aor, aor, searchStringLength) == 0) {
174 
175 			currentRecord->numContacts--;
176 
177 			/* There are still contacts relying on this user, so
178 			 * don't delete anything. */
179 			if(currentRecord->numContacts > 0) {
180 				return;
181 			}
182 
183 			/* There are no more contacts relying on this user, so
184 			 * delete the row from the table. */
185 			deleteRegUserRow(currentRecord->userIndex);
186 
187 
188 			/* Maintenance of the hash table */
189 
190 			if(currentRecord->prev == NULL) {
191 				/* Edge Case: First element in list was just deleted, so set
192 					 * up the first element to point to the one after the one
193 					 * just deleted */
194 				theTable[hashIndex].first = currentRecord->next;
195 			} else {
196 				/* Not the first element, so hook up the previous node to
197 					 * the node after the one just deleted. */
198 				currentRecord->prev->next = currentRecord->next;
199 			}
200 
201 			if(currentRecord->next == NULL) {
202 				/* Edge Case: The last element has been targetted for
203 					 * deletion.  So move the pointer to the node just before
204 					 * this one.  */
205 				theTable[hashIndex].last = currentRecord->prev;
206 			} else {
207 				/* Not the last element, so hook up next nodes previous
208 					 * element to this nodes previous.  */
209 				currentRecord->next->prev = currentRecord->prev;
210 			}
211 
212 			pkg_free(currentRecord);
213 
214 			/* We are done, so just return. */
215 			return;
216 		}
217 
218 		/* Advance to the next records. */
219 		currentRecord = currentRecord->next;
220 	}
221 }
222 
223 
224 /*! Returns a aorToIndexStruct_t, holding the given 'userIndex' and 'aor'.  The
225  * structure is used to map between the "aor" (Kamailio's way of indexing
226  * users/contacts), and the SNMPStats user and contact integer indexes.
227  *
228  * NOTE: that this record does not make a copy of aor, but instead points
229  * directly to the parameter.  Therefore make sure that aor is not on the stack,
230  * and is not going to disappear before this record is deleted.
231  */
createHashRecord(int userIndex,char * aor)232 aorToIndexStruct_t *createHashRecord(int userIndex, char *aor)
233 {
234 	int aorLength = strlen(aor);
235 
236 	aorToIndexStruct_t *theRecord = pkg_malloc(
237 			sizeof(aorToIndexStruct_t) + (aorLength + 1) * sizeof(char));
238 	if(theRecord == NULL) {
239 		LM_ERR("failed to create a mapping record for %s", aor);
240 		return NULL;
241 	}
242 
243 	memset(theRecord, 0, sizeof(aorToIndexStruct_t));
244 
245 	theRecord->aor = (char *)theRecord + sizeof(aorToIndexStruct_t);
246 	memcpy(theRecord->aor, aor, aorLength);
247 	theRecord->aor[aorLength] = '\0';
248 	theRecord->aorLength = aorLength;
249 	theRecord->userIndex = userIndex;
250 	theRecord->numContacts = 1;
251 
252 	return theRecord;
253 }
254 
255 
256 /*! Debugging function.  Prints off an entire hash slot. */
printHashSlot(hashSlot_t * theTable,int index)257 void printHashSlot(hashSlot_t *theTable, int index)
258 {
259 	aorToIndexStruct_t *currentRecord = theTable[index].first;
260 
261 	LM_ERR("dumping Hash Slot #%d\n", index);
262 
263 	while(currentRecord != NULL) {
264 		LM_ERR("\tString: %s - Index: %d\n", currentRecord->aor,
265 				currentRecord->userIndex);
266 		currentRecord = currentRecord->next;
267 	}
268 }