1/*
2 * (C) 2006-2012 by Pablo Neira Ayuso <pablo@netfilter.org>
3 * (C) 2011-2012 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 "filter.h"
21#include "bitops.h"
22#include "jhash.h"
23#include "hash.h"
24#include "vector.h"
25#include "conntrackd.h"
26#include "log.h"
27
28#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
29#include <stdlib.h>
30#include <string.h>
31#include <errno.h>
32#include <limits.h>
33
34struct ct_filter {
35	int logic[CT_FILTER_MAX];
36	u_int32_t l4protomap[IPPROTO_MAX/32];
37	u_int16_t statemap[IPPROTO_MAX];
38	struct hashtable *h;
39	struct hashtable *h6;
40	struct vector *v;
41	struct vector *v6;
42};
43
44/* XXX: These should be configurable, better use a rb-tree */
45#define FILTER_POOL_SIZE 128
46#define FILTER_POOL_LIMIT INT_MAX
47
48static uint32_t ct_filter_hash(const void *data, const struct hashtable *table)
49{
50	const uint32_t *f = data;
51
52	return jhash_1word(*f, 0) % table->hashsize;
53}
54
55static uint32_t ct_filter_hash6(const void *data, const struct hashtable *table)
56{
57	return jhash2(data, 4, 0) % table->hashsize;
58}
59
60static int ct_filter_compare(const void *data1, const void *data2)
61{
62	const struct ct_filter_ipv4_hnode *f1 = data1;
63	const uint32_t *f2 = data2;
64
65	return f1->ip == *f2;
66}
67
68static int ct_filter_compare6(const void *data1, const void *data2)
69{
70	const struct ct_filter_ipv6_hnode *f = data1;
71
72	return memcmp(f->ipv6, data2, sizeof(uint32_t)*4) == 0;
73}
74
75struct ct_filter *ct_filter_create(void)
76{
77	int i;
78	struct ct_filter *filter;
79
80	filter = calloc(sizeof(struct ct_filter), 1);
81	if (!filter)
82		return NULL;
83
84	filter->h = hashtable_create(FILTER_POOL_SIZE,
85				     FILTER_POOL_LIMIT,
86				     ct_filter_hash,
87				     ct_filter_compare);
88	if (!filter->h) {
89		free(filter);
90		return NULL;
91	}
92
93	filter->h6 = hashtable_create(FILTER_POOL_SIZE,
94				      FILTER_POOL_LIMIT,
95				      ct_filter_hash6,
96				      ct_filter_compare6);
97	if (!filter->h6) {
98		free(filter->h);
99		free(filter);
100		return NULL;
101	}
102
103	filter->v = vector_create(sizeof(struct ct_filter_netmask_ipv4));
104	if (!filter->v) {
105		free(filter->h6);
106		free(filter->h);
107		free(filter);
108		return NULL;
109	}
110
111	filter->v6 = vector_create(sizeof(struct ct_filter_netmask_ipv6));
112	if (!filter->v6) {
113		free(filter->v);
114		free(filter->h6);
115		free(filter->h);
116		free(filter);
117		return NULL;
118	}
119
120	for (i=0; i<CT_FILTER_MAX; i++)
121		filter->logic[i] = -1;
122
123	return filter;
124}
125
126void ct_filter_destroy(struct ct_filter *filter)
127{
128	hashtable_destroy(filter->h);
129	hashtable_destroy(filter->h6);
130	vector_destroy(filter->v);
131	vector_destroy(filter->v6);
132	free(filter);
133}
134
135/* this is ugly, but it simplifies read_config_yy.y */
136static struct ct_filter *__filter_alloc(struct ct_filter *filter)
137{
138	if (!STATE(us_filter)) {
139		STATE(us_filter) = ct_filter_create();
140		if (!STATE(us_filter)) {
141			fprintf(stderr, "Can't create ignore pool!\n");
142			exit(EXIT_FAILURE);
143		}
144	}
145
146	return STATE(us_filter);
147}
148
149void ct_filter_set_logic(struct ct_filter *filter,
150			 enum ct_filter_type type,
151			 enum ct_filter_logic logic)
152{
153	filter = __filter_alloc(filter);
154	filter->logic[type] = logic;
155}
156
157int ct_filter_add_ip(struct ct_filter *filter, void *data, uint8_t family)
158{
159	int id;
160	filter = __filter_alloc(filter);
161
162	switch(family) {
163		case AF_INET:
164			id = hashtable_hash(filter->h, data);
165			if (!hashtable_find(filter->h, data, id)) {
166				struct ct_filter_ipv4_hnode *n;
167				n = malloc(sizeof(struct ct_filter_ipv4_hnode));
168				if (n == NULL)
169					return 0;
170				memcpy(&n->ip, data, sizeof(uint32_t));
171				hashtable_add(filter->h, &n->node, id);
172				return 0;
173			}
174			break;
175		case AF_INET6:
176			id = hashtable_hash(filter->h6, data);
177			if (!hashtable_find(filter->h6, data, id)) {
178				struct ct_filter_ipv6_hnode *n;
179				n = malloc(sizeof(struct ct_filter_ipv6_hnode));
180				if (n == NULL)
181					return 0;
182				memcpy(n->ipv6, data, sizeof(uint32_t)*4);
183				hashtable_add(filter->h6, &n->node, id);
184				return 0;
185			}
186			break;
187	}
188	return 1;
189}
190
191static int cmp_ipv4_addr(const void *a, const void *b)
192{
193	return memcmp(a, b, sizeof(struct ct_filter_netmask_ipv4)) == 0;
194}
195
196static int cmp_ipv6_addr(const void *a, const void *b)
197{
198	return memcmp(a, b, sizeof(struct ct_filter_netmask_ipv6)) == 0;
199}
200
201int ct_filter_add_netmask(struct ct_filter *filter, void *data, uint8_t family)
202{
203	filter = __filter_alloc(filter);
204
205	switch(family) {
206		case AF_INET:
207			if (vector_iterate(filter->v, data, cmp_ipv4_addr)) {
208				errno = EEXIST;
209				return 0;
210			}
211			vector_add(filter->v, data);
212			break;
213		case AF_INET6:
214			if (vector_iterate(filter->v, data, cmp_ipv6_addr)) {
215				errno = EEXIST;
216				return 0;
217			}
218			vector_add(filter->v6, data);
219			break;
220	}
221	return 1;
222}
223
224void ct_filter_add_proto(struct ct_filter *f, int protonum)
225{
226	f = __filter_alloc(f);
227
228	set_bit_u32(protonum, f->l4protomap);
229}
230
231void ct_filter_add_state(struct ct_filter *f, int protonum, int val)
232{
233	f = __filter_alloc(f);
234
235	set_bit_u16(val, &f->statemap[protonum]);
236}
237
238static inline int
239__ct_filter_test_ipv4(struct ct_filter *f, const struct nf_conntrack *ct)
240{
241	int id_src, id_dst;
242	uint32_t src, dst;
243
244	/* we only use the real source and destination address */
245	src = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC);
246	dst = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC);
247
248	id_src = hashtable_hash(f->h, &src);
249	id_dst = hashtable_hash(f->h, &dst);
250
251	return hashtable_find(f->h, &src, id_src) ||
252	       hashtable_find(f->h, &dst, id_dst);
253}
254
255static inline int
256__ct_filter_test_ipv6(struct ct_filter *f, const struct nf_conntrack *ct)
257{
258	int id_src, id_dst;
259	const uint32_t *src, *dst;
260
261	src = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC);
262	dst = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC);
263
264	id_src = hashtable_hash(f->h6, src);
265	id_dst = hashtable_hash(f->h6, dst);
266
267	return hashtable_find(f->h6, src, id_src) ||
268	       hashtable_find(f->h6, dst, id_dst);
269}
270
271static int
272__ct_filter_test_mask4(const void *ptr, const void *ct)
273{
274	const struct ct_filter_netmask_ipv4 *elem = ptr;
275	const uint32_t src = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC);
276	const uint32_t dst = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC);
277
278	return ((elem->ip & elem->mask) == (src & elem->mask) ||
279		(elem->ip & elem->mask) == (dst & elem->mask));
280}
281
282static int
283__ct_filter_test_mask6(const void *ptr, const void *ct)
284{
285	const struct ct_filter_netmask_ipv6 *elem = ptr;
286	const uint32_t *src = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC);
287	const uint32_t *dst = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC);
288
289	return (((elem->ip[0] & elem->mask[0]) == (src[0] & elem->mask[0]) &&
290		 (elem->ip[1] & elem->mask[1]) == (src[1] & elem->mask[1]) &&
291		 (elem->ip[2] & elem->mask[2]) == (src[2] & elem->mask[2]) &&
292		 (elem->ip[3] & elem->mask[3]) == (src[3] & elem->mask[3])) ||
293		((elem->ip[0] & elem->mask[0]) == (dst[0] & elem->mask[0]) &&
294		 (elem->ip[1] & elem->mask[1]) == (dst[1] & elem->mask[1]) &&
295		 (elem->ip[2] & elem->mask[2]) == (dst[2] & elem->mask[2]) &&
296		 (elem->ip[3] & elem->mask[3]) == (dst[3] & elem->mask[3])));
297}
298
299static int
300__ct_filter_test_state(struct ct_filter *f, const struct nf_conntrack *ct)
301{
302	uint16_t val = 0;
303	uint8_t protonum = nfct_get_attr_u8(ct, ATTR_L4PROTO);
304
305	switch(protonum) {
306	case IPPROTO_TCP:
307		if (!nfct_attr_is_set(ct, ATTR_TCP_STATE))
308			return -1;
309
310		val = nfct_get_attr_u8(ct, ATTR_TCP_STATE);
311		break;
312	default:
313		return -1;
314	}
315
316	return test_bit_u16(val, &f->statemap[protonum]);
317}
318
319static int
320ct_filter_check(struct ct_filter *f, const struct nf_conntrack *ct)
321{
322	int ret, protonum = nfct_get_attr_u8(ct, ATTR_L4PROTO);
323
324	/* no event filtering at all */
325	if (f == NULL)
326		return 1;
327
328	if (f->logic[CT_FILTER_L4PROTO] != -1) {
329		ret = test_bit_u32(protonum, f->l4protomap);
330		if (ret ^ f->logic[CT_FILTER_L4PROTO])
331			return 0;
332	}
333
334	if (f->logic[CT_FILTER_ADDRESS] != -1) {
335		switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
336		case AF_INET:
337			ret = vector_iterate(f->v, ct, __ct_filter_test_mask4);
338			if (ret ^ f->logic[CT_FILTER_ADDRESS])
339				return 0;
340			ret = __ct_filter_test_ipv4(f, ct);
341			if (ret ^ f->logic[CT_FILTER_ADDRESS])
342				return 0;
343			break;
344		case AF_INET6:
345			ret = vector_iterate(f->v6, ct, __ct_filter_test_mask6);
346			if (ret ^ f->logic[CT_FILTER_ADDRESS])
347				return 0;
348			ret = __ct_filter_test_ipv6(f, ct);
349			if (ret ^ f->logic[CT_FILTER_ADDRESS])
350				return 0;
351			break;
352		default:
353			break;
354		}
355	}
356
357	if (f->logic[CT_FILTER_STATE] != -1) {
358		ret = __ct_filter_test_state(f, ct);
359		/* ret is -1 if we don't know what to do */
360		if (ret != -1 && ret ^ f->logic[CT_FILTER_STATE])
361			return 0;
362	}
363
364	return 1;
365}
366
367static inline int ct_filter_sanity_check(const struct nf_conntrack *ct)
368{
369	if (!nfct_attr_is_set(ct, ATTR_L3PROTO)) {
370		dlog(LOG_ERR, "missing layer 3 protocol");
371		return 0;
372	}
373
374	switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
375	case AF_INET:
376		if (!nfct_attr_is_set(ct, ATTR_IPV4_SRC) ||
377		    !nfct_attr_is_set(ct, ATTR_IPV4_DST)) {
378		    	dlog(LOG_ERR, "missing IPv4 address. "
379				      "You forgot to load "
380				      "nf_conntrack_ipv4?");
381			return 0;
382		}
383		break;
384	case AF_INET6:
385		if (!nfct_attr_is_set(ct, ATTR_IPV6_SRC) ||
386		    !nfct_attr_is_set(ct, ATTR_IPV6_DST)) {
387		    	dlog(LOG_ERR, "missing IPv6 address. "
388				      "You forgot to load "
389				      "nf_conntrack_ipv6?");
390			return 0;
391		}
392		break;
393	}
394	return 1;
395}
396
397/* we do user-space filtering for dump and resyncs */
398int ct_filter_conntrack(const struct nf_conntrack *ct, int userspace)
399{
400	/* missing mandatory attributes in object */
401	if (!ct_filter_sanity_check(ct))
402		return 1;
403
404	if (userspace && !ct_filter_check(STATE(us_filter), ct))
405		return 1;
406
407	return 0;
408}
409
410struct exp_filter {
411	struct list_head 	list;
412};
413
414struct exp_filter *exp_filter_create(void)
415{
416	struct exp_filter *f;
417
418	f = calloc(1, sizeof(struct exp_filter));
419	if (f == NULL)
420		return NULL;
421
422	INIT_LIST_HEAD(&f->list);
423	return f;
424}
425
426struct exp_filter_item {
427	struct list_head	head;
428	char			helper_name[NFCT_HELPER_NAME_MAX];
429};
430
431/* this is ugly, but it simplifies read_config_yy.y */
432static struct exp_filter *exp_filter_alloc(void)
433{
434	if (STATE(exp_filter) == NULL) {
435		STATE(exp_filter) = exp_filter_create();
436		if (STATE(exp_filter) == NULL) {
437			fprintf(stderr, "Can't init expectation filtering!\n");
438			return NULL;
439		}
440	}
441	return STATE(exp_filter);;
442}
443
444int exp_filter_add(struct exp_filter *f, const char *helper_name)
445{
446	struct exp_filter_item *item;
447
448	f = exp_filter_alloc();
449	if (f == NULL)
450		return -1;
451
452	list_for_each_entry(item, &f->list, head) {
453		if (strncasecmp(item->helper_name, helper_name,
454				NFCT_HELPER_NAME_MAX) == 0) {
455			return -1;
456		}
457	}
458	item = calloc(1, sizeof(struct exp_filter_item));
459	if (item == NULL)
460		return -1;
461
462	strncpy(item->helper_name, helper_name, NFCT_HELPER_NAME_MAX);
463	list_add(&item->head, &f->list);
464	return 0;
465}
466
467int exp_filter_find(struct exp_filter *f, const struct nf_expect *exp)
468{
469	struct exp_filter_item *item;
470
471	/* if filtering is not active, accept everything. */
472	if (f == NULL)
473		return 1;
474
475	list_for_each_entry(item, &f->list, head) {
476		const char *name;
477
478		if (nfexp_attr_is_set(exp, ATTR_EXP_HELPER_NAME))
479			name = nfexp_get_attr(exp, ATTR_EXP_HELPER_NAME);
480		else {
481			/* No helper name, this is likely to be a kernel older
482			 * which does not include the helper name, just skip
483			 * this so we don't crash.
484			 */
485			return 0;
486		}
487
488		/* we allow partial matching to support things like sip-PORT. */
489		if (strncasecmp(item->helper_name, name,
490				strlen(item->helper_name)) == 0) {
491			return 1;
492		}
493	}
494	return 0;
495}
496