summaryrefslogtreecommitdiffstats
path: root/src/filter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/filter.c')
-rw-r--r--src/filter.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/filter.c b/src/filter.c
new file mode 100644
index 0000000..6e4d64a
--- /dev/null
+++ b/src/filter.c
@@ -0,0 +1,250 @@
+/*
+ * (C) 2006-2008 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "filter.h"
+#include "bitops.h"
+#include "jhash.h"
+#include "hash.h"
+#include "conntrackd.h"
+#include "log.h"
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+struct ct_filter {
+ int logic[CT_FILTER_MAX];
+ u_int32_t l4protomap[IPPROTO_MAX/32];
+ u_int16_t statemap[IPPROTO_MAX];
+ struct hashtable *h;
+ struct hashtable *h6;
+};
+
+/* XXX: These should be configurable, better use a rb-tree */
+#define FILTER_POOL_SIZE 128
+#define FILTER_POOL_LIMIT INT_MAX
+
+static uint32_t hash(const void *data, struct hashtable *table)
+{
+ const uint32_t *f = data;
+
+ return jhash_1word(*f, 0) % table->hashsize;
+}
+
+static uint32_t hash6(const void *data, struct hashtable *table)
+{
+ return jhash(data, sizeof(uint32_t)*4, 0) % table->hashsize;
+}
+
+static int compare(const void *data1, const void *data2)
+{
+ const uint32_t *f1 = data1;
+ const uint32_t *f2 = data2;
+
+ return *f1 == *f2;
+}
+
+static int compare6(const void *data1, const void *data2)
+{
+ return memcmp(data1, data2, sizeof(uint32_t)*4) == 0;
+}
+
+struct ct_filter *ct_filter_create(void)
+{
+ int i;
+ struct ct_filter *filter;
+
+ filter = calloc(sizeof(struct ct_filter), 1);
+ if (!filter)
+ return NULL;
+
+ filter->h = hashtable_create(FILTER_POOL_SIZE,
+ FILTER_POOL_LIMIT,
+ sizeof(uint32_t),
+ hash,
+ compare);
+ if (!filter->h) {
+ free(filter);
+ return NULL;
+ }
+
+ filter->h6 = hashtable_create(FILTER_POOL_SIZE,
+ FILTER_POOL_LIMIT,
+ sizeof(uint32_t)*4,
+ hash6,
+ compare6);
+ if (!filter->h6) {
+ free(filter->h);
+ free(filter);
+ return NULL;
+ }
+
+ for (i=0; i<CT_FILTER_MAX; i++)
+ filter->logic[i] = -1;
+
+ return filter;
+}
+
+void ct_filter_destroy(struct ct_filter *filter)
+{
+ hashtable_destroy(filter->h);
+ hashtable_destroy(filter->h6);
+ free(filter);
+}
+
+/* this is ugly, but it simplifies read_config_yy.y */
+static struct ct_filter *__filter_alloc(struct ct_filter *filter)
+{
+ if (!STATE(us_filter)) {
+ STATE(us_filter) = ct_filter_create();
+ if (!STATE(us_filter)) {
+ fprintf(stderr, "Can't create ignore pool!\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return STATE(us_filter);
+}
+
+void ct_filter_set_logic(struct ct_filter *filter,
+ enum ct_filter_type type,
+ enum ct_filter_logic logic)
+{
+ filter = __filter_alloc(filter);
+ filter->logic[type] = logic;
+}
+
+int ct_filter_add_ip(struct ct_filter *filter, void *data, uint8_t family)
+{
+ filter = __filter_alloc(filter);
+
+ switch(family) {
+ case AF_INET:
+ if (!hashtable_add(filter->h, data))
+ return 0;
+ break;
+ case AF_INET6:
+ if (!hashtable_add(filter->h6, data))
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+void ct_filter_add_proto(struct ct_filter *f, int protonum)
+{
+ f = __filter_alloc(f);
+
+ set_bit_u32(protonum, f->l4protomap);
+}
+
+void ct_filter_add_state(struct ct_filter *f, int protonum, int val)
+{
+ f = __filter_alloc(f);
+
+ set_bit_u16(val, &f->statemap[protonum]);
+}
+
+static int
+__ct_filter_test_ipv4(struct ct_filter *f, struct nf_conntrack *ct)
+{
+ if (!f->h)
+ return 0;
+
+ return (hashtable_test(f->h, nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC)) ||
+ hashtable_test(f->h, nfct_get_attr(ct, ATTR_ORIG_IPV4_DST)) ||
+ hashtable_test(f->h, nfct_get_attr(ct, ATTR_REPL_IPV4_SRC)) ||
+ hashtable_test(f->h, nfct_get_attr(ct, ATTR_REPL_IPV4_DST)));
+}
+
+static int
+__ct_filter_test_ipv6(struct ct_filter *f, struct nf_conntrack *ct)
+{
+ if (!f->h6)
+ return 0;
+
+ return (hashtable_test(f->h6, nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC)) ||
+ hashtable_test(f->h6, nfct_get_attr(ct, ATTR_ORIG_IPV6_DST)) ||
+ hashtable_test(f->h6, nfct_get_attr(ct, ATTR_REPL_IPV6_SRC)) ||
+ hashtable_test(f->h6, nfct_get_attr(ct, ATTR_REPL_IPV6_DST)));
+}
+
+static int __ct_filter_test_state(struct ct_filter *f, struct nf_conntrack *ct)
+{
+ uint16_t val = 0;
+ uint8_t protonum = nfct_get_attr_u8(ct, ATTR_L4PROTO);
+
+ switch(protonum) {
+ case IPPROTO_TCP:
+ val = nfct_get_attr_u8(ct, ATTR_TCP_STATE);
+ break;
+ default:
+ return -1;
+ }
+
+ return test_bit_u16(val, &f->statemap[protonum]);
+}
+
+int ct_filter_check(struct ct_filter *f, struct nf_conntrack *ct)
+{
+ int ret, protonum = nfct_get_attr_u8(ct, ATTR_L4PROTO);
+
+ /* no event filtering at all */
+ if (f == NULL)
+ return 1;
+
+ if (f->logic[CT_FILTER_L4PROTO] != -1) {
+ ret = test_bit_u32(protonum, f->l4protomap);
+ if (ret == 0 && f->logic[CT_FILTER_L4PROTO])
+ return 0;
+ else if (ret == 1 && !f->logic[CT_FILTER_L4PROTO])
+ return 0;
+ }
+
+ if (f->logic[CT_FILTER_ADDRESS] != -1) {
+ switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
+ case AF_INET:
+ ret = __ct_filter_test_ipv4(f, ct);
+ if (ret == 0 && f->logic[CT_FILTER_ADDRESS])
+ return 0;
+ else if (ret == 1 && !f->logic[CT_FILTER_ADDRESS])
+ return 0;
+ break;
+ case AF_INET6:
+ ret = __ct_filter_test_ipv6(f, ct);
+ if (ret == 0 && f->logic[CT_FILTER_ADDRESS])
+ return 0;
+ else if (ret == 1 && !f->logic[CT_FILTER_ADDRESS])
+ return 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (f->logic[CT_FILTER_STATE] != -1) {
+ ret = __ct_filter_test_state(f, ct);
+ if (ret == 0 && f->logic[CT_FILTER_STATE])
+ return 0;
+ else if (ret == 1 && !f->logic[CT_FILTER_STATE])
+ return 0;
+ }
+
+ return 1;
+}