gateway: introduce and use removeGatewayFromList function
[olsrd.git] / src / olsr_cookie.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2008, Hannes Gredler (hannes@gredler.at)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  */
40
41 #include "olsr.h"
42 #include "defs.h"
43 #include "olsr_cookie.h"
44 #include "log.h"
45
46 #include <assert.h>
47
48 /* Root directory of the cookies we have in the system */
49 static struct olsr_cookie_info *cookies[COOKIE_ID_MAX] = { 0 };
50
51 /*
52  * Allocate a cookie for the next available cookie id.
53  */
54 struct olsr_cookie_info *
55 olsr_alloc_cookie(const char *cookie_name, olsr_cookie_type cookie_type)
56 {
57   struct olsr_cookie_info *ci;
58   int ci_index;
59
60   /*
61    * Look for an unused index.
62    * For ease of troubleshooting (non-zero patterns) we start at index 1.
63    */
64   for (ci_index = 1; ci_index < COOKIE_ID_MAX; ci_index++) {
65     if (!cookies[ci_index]) {
66       break;
67     }
68   }
69
70   assert(ci_index < COOKIE_ID_MAX);     /* increase COOKIE_ID_MAX */
71
72   ci = calloc(1, sizeof(struct olsr_cookie_info));
73   cookies[ci_index] = ci;
74
75   /* Now populate the cookie info */
76   ci->ci_id = ci_index;
77   ci->ci_type = cookie_type;
78   if (cookie_name) {
79     ci->ci_name = strdup(cookie_name);
80   }
81
82   /* Init the free list */
83   if (cookie_type == OLSR_COOKIE_TYPE_MEMORY) {
84     list_head_init(&ci->ci_free_list);
85   }
86
87   return ci;
88 }
89
90 /*
91  * Free a cookie that is no longer being used.
92  */
93 void
94 olsr_free_cookie(struct olsr_cookie_info *ci)
95 {
96   struct list_node *memory_list;
97
98   /* Mark the cookie as unused */
99   cookies[ci->ci_id] = NULL;
100
101   /* Free name if set */
102   if (ci->ci_name) {
103     free(ci->ci_name);
104   }
105
106   /* Flush all the memory on the free list */
107   if (ci->ci_type == OLSR_COOKIE_TYPE_MEMORY) {
108     while (!list_is_empty(&ci->ci_free_list)) {
109       memory_list = ci->ci_free_list.next;
110       list_remove(memory_list);
111       free(memory_list);
112     }
113   }
114
115   free(ci);
116 }
117
118 /*
119  * Flush all cookies. This is really only called upon shutdown.
120  */
121 void
122 olsr_delete_all_cookies(void)
123 {
124   int ci_index;
125
126   /*
127    * Walk the full index range and kill 'em all.
128    */
129   for (ci_index = 1; ci_index < COOKIE_ID_MAX; ci_index++) {
130     if (!cookies[ci_index]) {
131       continue;
132     }
133     olsr_free_cookie(cookies[ci_index]);
134   }
135 }
136
137 /*
138  * Set the size for fixed block allocations.
139  * This is only allowed for memory cookies.
140  */
141 void
142 olsr_cookie_set_memory_size(struct olsr_cookie_info *ci, size_t size)
143 {
144   if (!ci) {
145     return;
146   }
147
148   assert(ci->ci_type == OLSR_COOKIE_TYPE_MEMORY);
149   ci->ci_size = size;
150 }
151
152 /*
153  * Basic sanity checking for a passed-in cookie-id.
154  */
155 static bool
156 olsr_cookie_valid(olsr_cookie_t cookie_id)
157 {
158   if ((cookie_id < COOKIE_ID_MAX) && cookies[cookie_id]) {
159     return true;
160   }
161   return false;
162 }
163
164 /*
165  * Increment usage state for a given cookie.
166  */
167 void
168 olsr_cookie_usage_incr(olsr_cookie_t cookie_id)
169 {
170   if (olsr_cookie_valid(cookie_id)) {
171     cookies[cookie_id]->ci_usage++;
172     cookies[cookie_id]->ci_changes++;
173   }
174 }
175
176 /*
177  * Decrement usage state for a given cookie.
178  */
179 void
180 olsr_cookie_usage_decr(olsr_cookie_t cookie_id)
181 {
182   if (olsr_cookie_valid(cookie_id)) {
183     cookies[cookie_id]->ci_usage--;
184     cookies[cookie_id]->ci_changes++;
185   }
186 }
187
188 /*
189  * Return a cookie name.
190  * Mostly used for logging purposes.
191  */
192 char *
193 olsr_cookie_name(olsr_cookie_t cookie_id)
194 {
195   static char unknown[] = "unknown";
196
197   if (olsr_cookie_valid(cookie_id)) {
198     return (cookies[cookie_id])->ci_name;
199   }
200
201   return unknown;
202 }
203
204 /*
205  * Allocate a fixed amount of memory based on a passed in cookie type.
206  */
207 void *
208 olsr_cookie_malloc(struct olsr_cookie_info *ci)
209 {
210   void *ptr;
211   struct olsr_cookie_mem_brand *branding;
212   struct list_node *free_list_node;
213
214 #ifdef OLSR_COOKIE_DEBUG
215   bool reuse = false;
216 #endif /* OLSR_COOKIE_DEBUG */
217
218   /*
219    * Check first if we have reusable memory.
220    */
221   if (!ci->ci_free_list_usage) {
222
223     /*
224      * No reusable memory block on the free_list.
225      */
226     ptr = calloc(1, ci->ci_size + sizeof(struct olsr_cookie_mem_brand));
227
228     if (!ptr) {
229       const char *const err_msg = strerror(errno);
230       OLSR_PRINTF(1, "OUT OF MEMORY: %s\n", err_msg);
231       olsr_syslog(OLSR_LOG_ERR, "olsrd: out of memory!: %s\n", err_msg);
232       olsr_exit(ci->ci_name, EXIT_FAILURE);
233     }
234     assert(ptr);
235   } else {
236
237     /*
238      * There is a memory block on the free list.
239      * Carve it out of the list, and clean.
240      */
241     free_list_node = ci->ci_free_list.next;
242     list_remove(free_list_node);
243     ptr = (void *)free_list_node;
244     memset(ptr, 0, ci->ci_size);
245     ci->ci_free_list_usage--;
246 #ifdef OLSR_COOKIE_DEBUG
247     reuse = true;
248 #endif /* OLSR_COOKIE_DEBUG */
249   }
250
251   /*
252    * Now brand mark the end of the memory block with a short signature
253    * indicating presence of a cookie. This will be checked against
254    * When the block is freed to detect corruption.
255    */
256   branding = (struct olsr_cookie_mem_brand *)ARM_NOWARN_ALIGN(((unsigned char *)ptr + ci->ci_size));
257   memcpy(&branding->cmb_sig[0], "cookie", 6);
258   branding->cmb_id = ci->ci_id;
259
260   /* Stats keeping */
261   olsr_cookie_usage_incr(ci->ci_id);
262
263 #ifdef OLSR_COOKIE_DEBUG
264   OLSR_PRINTF(1, "MEMORY: alloc %s, %p, %u bytes%s\n", ci->ci_name, ptr, ci->ci_size, reuse ? ", reuse" : "");
265 #endif /* OLSR_COOKIE_DEBUG */
266
267   return ptr;
268 }
269
270 /*
271  * Free a memory block owned by a given cookie.
272  * Run some corruption checks.
273  */
274 void
275 olsr_cookie_free(struct olsr_cookie_info *ci, void *ptr)
276 {
277   struct olsr_cookie_mem_brand *branding;
278   struct list_node *free_list_node;
279
280 #ifdef OLSR_COOKIE_DEBUG
281   bool reuse = false;
282 #endif /* OLSR_COOKIE_DEBUG */
283
284   branding = (struct olsr_cookie_mem_brand *)ARM_NOWARN_ALIGN(((unsigned char *)ptr + ci->ci_size));
285
286   /*
287    * Verify if there has been a memory overrun, or
288    * the wrong owner is trying to free this.
289    */
290   assert(!memcmp(&branding->cmb_sig, "cookie", 6) && branding->cmb_id == ci->ci_id);
291
292   /* Kill the brand */
293   memset(branding, 0, sizeof(*branding));
294
295   /*
296    * Rather than freeing the memory right away, try to reuse at a later
297    * point. Keep at least ten percent of the active used blocks or at least
298    * ten blocks on the free list.
299    */
300   if ((ci->ci_free_list_usage < COOKIE_FREE_LIST_THRESHOLD) || (ci->ci_free_list_usage < ci->ci_usage / COOKIE_FREE_LIST_THRESHOLD)) {
301
302     free_list_node = (struct list_node *)ptr;
303     list_node_init(free_list_node);
304     list_add_before(&ci->ci_free_list, free_list_node);
305     ci->ci_free_list_usage++;
306 #ifdef OLSR_COOKIE_DEBUG
307     reuse = true;
308 #endif /* OLSR_COOKIE_DEBUG */
309   } else {
310
311     /*
312      * No interest in reusing memory.
313      */
314     free(ptr);
315   }
316
317   /* Stats keeping */
318   olsr_cookie_usage_decr(ci->ci_id);
319
320 #ifdef OLSR_COOKIE_DEBUG
321   OLSR_PRINTF(1, "MEMORY: free %s, %p, %u bytes%s\n", ci->ci_name, ptr, ci->ci_size, reuse ? ", reuse" : "");
322 #endif /* OLSR_COOKIE_DEBUG */
323
324 }
325
326 /*
327  * Local Variables:
328  * c-basic-offset: 2
329  * indent-tabs-mode: nil
330  * End:
331  */