1/* 2 * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org> 3 * (C) 2011 by Vyatta Inc. <http://www.vyatta.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20#include "cache.h" 21#include "jhash.h" 22#include "hash.h" 23#include "log.h" 24#include "conntrackd.h" 25 26#include <libnetfilter_conntrack/libnetfilter_conntrack.h> 27#include <errno.h> 28#include <stdlib.h> 29#include <string.h> 30#include <time.h> 31 32struct cache_feature *cache_feature[CACHE_MAX_FEATURE] = { 33 [TIMER_FEATURE] = &timer_feature, 34}; 35 36struct cache *cache_create(const char *name, enum cache_type type, 37 unsigned int features, 38 struct cache_extra *extra, 39 struct cache_ops *ops) 40{ 41 size_t size = sizeof(struct cache_object); 42 int i, j = 0; 43 struct cache *c; 44 struct cache_feature *feature_array[CACHE_MAX_FEATURE] = {}; 45 unsigned int feature_offset[CACHE_MAX_FEATURE] = {}; 46 unsigned int feature_type[CACHE_MAX_FEATURE] = {}; 47 48 if (type == CACHE_T_NONE || type >= CACHE_T_MAX) 49 return NULL; 50 51 c = malloc(sizeof(struct cache)); 52 if (!c) 53 return NULL; 54 memset(c, 0, sizeof(struct cache)); 55 56 strcpy(c->name, name); 57 c->type = type; 58 59 for (i = 0; i < CACHE_MAX_FEATURE; i++) { 60 if ((1 << i) & features) { 61 feature_array[j] = cache_feature[i]; 62 feature_offset[j] = size; 63 feature_type[i] = j; 64 size += cache_feature[i]->size; 65 j++; 66 } 67 } 68 69 memcpy(c->feature_type, feature_type, sizeof(feature_type)); 70 71 c->features = malloc(sizeof(struct cache_feature) * j); 72 if (!c->features) { 73 free(c); 74 return NULL; 75 } 76 memcpy(c->features, feature_array, sizeof(struct cache_feature) * j); 77 c->num_features = j; 78 79 c->extra_offset = size; 80 c->extra = extra; 81 if (extra) 82 size += extra->size; 83 84 c->feature_offset = malloc(sizeof(unsigned int) * j); 85 if (!c->feature_offset) { 86 free(c->features); 87 free(c); 88 return NULL; 89 } 90 memcpy(c->feature_offset, feature_offset, sizeof(unsigned int) * j); 91 92 if (!ops || !ops->hash || !ops->cmp || 93 !ops->alloc || !ops->copy || !ops->free) { 94 free(c->feature_offset); 95 free(c->features); 96 free(c); 97 return NULL; 98 } 99 c->ops = ops; 100 101 c->h = hashtable_create(CONFIG(hashsize), 102 CONFIG(limit), 103 c->ops->hash, 104 c->ops->cmp); 105 if (!c->h) { 106 free(c->features); 107 free(c->feature_offset); 108 free(c); 109 return NULL; 110 } 111 c->object_size = size; 112 113 return c; 114} 115 116void cache_destroy(struct cache *c) 117{ 118 cache_flush(c); 119 hashtable_destroy(c->h); 120 free(c->features); 121 free(c->feature_offset); 122 free(c); 123} 124 125struct cache_object *cache_object_new(struct cache *c, void *ptr) 126{ 127 struct cache_object *obj; 128 129 obj = calloc(c->object_size, 1); 130 if (obj == NULL) { 131 errno = ENOMEM; 132 c->stats.add_fail_enomem++; 133 return NULL; 134 } 135 obj->cache = c; 136 137 obj->ptr = c->ops->alloc(); 138 if (obj->ptr == NULL) { 139 free(obj); 140 errno = ENOMEM; 141 c->stats.add_fail_enomem++; 142 return NULL; 143 } 144 c->ops->copy(obj->ptr, ptr, NFCT_CP_OVERRIDE); 145 obj->status = C_OBJ_NONE; 146 c->stats.objects++; 147 148 return obj; 149} 150 151void cache_object_free(struct cache_object *obj) 152{ 153 obj->cache->stats.objects--; 154 obj->cache->ops->free(obj->ptr); 155 156 free(obj); 157} 158 159int cache_object_put(struct cache_object *obj) 160{ 161 if (--obj->refcnt == 0) { 162 cache_del(obj->cache, obj); 163 cache_object_free(obj); 164 return 1; 165 } 166 return 0; 167} 168 169void cache_object_get(struct cache_object *obj) 170{ 171 obj->refcnt++; 172} 173 174void cache_object_set_status(struct cache_object *obj, int status) 175{ 176 if (status == C_OBJ_DEAD) { 177 obj->cache->stats.del_ok++; 178 obj->cache->stats.active--; 179 } 180 obj->status = status; 181} 182 183static int __add(struct cache *c, struct cache_object *obj, int id) 184{ 185 int ret; 186 unsigned int i; 187 char *data = obj->data; 188 189 ret = hashtable_add(c->h, &obj->hashnode, id); 190 if (ret == -1) 191 return -1; 192 193 for (i = 0; i < c->num_features; i++) { 194 c->features[i]->add(obj, data); 195 data += c->features[i]->size; 196 } 197 198 if (c->extra && c->extra->add) 199 c->extra->add(obj, ((char *) obj) + c->extra_offset); 200 201 c->stats.active++; 202 obj->lifetime = obj->lastupdate = time_cached(); 203 obj->status = C_OBJ_NEW; 204 obj->refcnt++; 205 return 0; 206} 207 208int cache_add(struct cache *c, struct cache_object *obj, int id) 209{ 210 int ret; 211 212 ret = __add(c, obj, id); 213 if (ret == -1) { 214 c->stats.add_fail++; 215 if (errno == ENOSPC) 216 c->stats.add_fail_enospc++; 217 return -1; 218 } 219 c->stats.add_ok++; 220 return 0; 221} 222 223void cache_update(struct cache *c, struct cache_object *obj, int id, void *ptr) 224{ 225 char *data = obj->data; 226 unsigned int i; 227 228 c->ops->copy(obj->ptr, ptr, NFCT_CP_META); 229 230 for (i = 0; i < c->num_features; i++) { 231 c->features[i]->update(obj, data); 232 data += c->features[i]->size; 233 } 234 235 if (c->extra && c->extra->update) 236 c->extra->update(obj, ((char *) obj) + c->extra_offset); 237 238 c->stats.upd_ok++; 239 obj->lastupdate = time_cached(); 240 obj->status = C_OBJ_ALIVE; 241} 242 243static void __del(struct cache *c, struct cache_object *obj) 244{ 245 unsigned i; 246 char *data = obj->data; 247 248 for (i = 0; i < c->num_features; i++) { 249 c->features[i]->destroy(obj, data); 250 data += c->features[i]->size; 251 } 252 253 if (c->extra && c->extra->destroy) 254 c->extra->destroy(obj, ((char *) obj) + c->extra_offset); 255 256 hashtable_del(c->h, &obj->hashnode); 257} 258 259void cache_del(struct cache *c, struct cache_object *obj) 260{ 261 /* 262 * Do not increase stats if we are trying to 263 * kill an entry was previously deleted via 264 * __cache_del_timer. 265 */ 266 if (obj->status != C_OBJ_DEAD) { 267 c->stats.del_ok++; 268 c->stats.active--; 269 } 270 __del(c, obj); 271} 272 273struct cache_object *cache_update_force(struct cache *c, void *ptr) 274{ 275 struct cache_object *obj; 276 int id; 277 278 obj = cache_find(c, ptr, &id); 279 if (obj) { 280 if (obj->status != C_OBJ_DEAD) { 281 cache_update(c, obj, id, ptr); 282 return obj; 283 } else { 284 cache_del(c, obj); 285 cache_object_free(obj); 286 } 287 } 288 obj = cache_object_new(c, ptr); 289 if (obj == NULL) 290 return NULL; 291 292 if (cache_add(c, obj, id) == -1) { 293 cache_object_free(obj); 294 return NULL; 295 } 296 297 return obj; 298} 299 300struct cache_object *cache_find(struct cache *c, void *ptr, int *id) 301{ 302 *id = hashtable_hash(c->h, ptr); 303 return ((struct cache_object *) hashtable_find(c->h, ptr, *id)); 304} 305 306void *cache_get_extra(struct cache_object *obj) 307{ 308 return (char*)obj + obj->cache->extra_offset; 309} 310 311void cache_stats(const struct cache *c, int fd) 312{ 313 char buf[512]; 314 int size; 315 316 size = sprintf(buf, "cache %s:\n" 317 "current active connections:\t%12u\n" 318 "connections created:\t\t%12u\tfailed:\t%12u\n" 319 "connections updated:\t\t%12u\tfailed:\t%12u\n" 320 "connections destroyed:\t\t%12u\tfailed:\t%12u\n\n", 321 c->name, 322 c->stats.active, 323 c->stats.add_ok, 324 c->stats.add_fail, 325 c->stats.upd_ok, 326 c->stats.upd_fail, 327 c->stats.del_ok, 328 c->stats.del_fail); 329 send(fd, buf, size, 0); 330} 331 332void cache_stats_extended(const struct cache *c, int fd) 333{ 334 char buf[512]; 335 int size; 336 337 size = snprintf(buf, sizeof(buf), 338 "cache:%s\tactive objects:\t\t%12u\n" 339 "\tactive/total entries:\t\t%12u/%12u\n" 340 "\tcreation OK/failed:\t\t%12u/%12u\n" 341 "\t\tno memory available:\t%12u\n" 342 "\t\tno space left in cache:\t%12u\n" 343 "\tupdate OK/failed:\t\t%12u/%12u\n" 344 "\t\tentry not found:\t%12u\n" 345 "\tdeletion created/failed:\t%12u/%12u\n" 346 "\t\tentry not found:\t%12u\n\n", 347 c->name, c->stats.objects, 348 c->stats.active, hashtable_counter(c->h), 349 c->stats.add_ok, 350 c->stats.add_fail, 351 c->stats.add_fail_enomem, 352 c->stats.add_fail_enospc, 353 c->stats.upd_ok, 354 c->stats.upd_fail, 355 c->stats.upd_fail_enoent, 356 c->stats.del_ok, 357 c->stats.del_fail, 358 c->stats.del_fail_enoent); 359 360 send(fd, buf, size, 0); 361} 362 363void cache_iterate(struct cache *c, 364 void *data, 365 int (*iterate)(void *data1, void *data2)) 366{ 367 hashtable_iterate(c->h, data, iterate); 368} 369 370void cache_iterate_limit(struct cache *c, void *data, 371 uint32_t from, uint32_t steps, 372 int (*iterate)(void *data1, void *data2)) 373{ 374 hashtable_iterate_limit(c->h, data, from, steps, iterate); 375} 376 377void cache_dump(struct cache *c, int fd, int type) 378{ 379 struct __dump_container tmp = { 380 .fd = fd, 381 .type = type 382 }; 383 hashtable_iterate(c->h, (void *) &tmp, c->ops->dump_step); 384} 385 386int cache_commit(struct cache *c, struct nfct_handle *h, int clientfd) 387{ 388 return c->ops->commit(c, h, clientfd); 389} 390 391static int do_flush(void *data, void *n) 392{ 393 struct cache *c = data; 394 struct cache_object *obj = n; 395 396 cache_del(c, obj); 397 cache_object_free(obj); 398 return 0; 399} 400 401void cache_flush(struct cache *c) 402{ 403 hashtable_iterate(c->h, c, do_flush); 404 c->stats.flush++; 405} 406