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