From b2edf895af82914ab09a842641a45b7a806e9b1e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 25 Nov 2008 23:34:48 +0100 Subject: filter: CIDR-based filtering support This patch adds CIDR-based filtering support. The current implementation is O(n). This patch also introduces the vector data type which is used to store the IP address and the network mask. Signed-off-by: Pablo Neira Ayuso --- src/filter.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'src/filter.c') diff --git a/src/filter.c b/src/filter.c index f6da8bb..905d10f 100644 --- a/src/filter.c +++ b/src/filter.c @@ -20,12 +20,14 @@ #include "bitops.h" #include "jhash.h" #include "hash.h" +#include "vector.h" #include "conntrackd.h" #include "log.h" #include #include #include +#include #include struct ct_filter { @@ -34,6 +36,8 @@ struct ct_filter { u_int16_t statemap[IPPROTO_MAX]; struct hashtable *h; struct hashtable *h6; + struct vector *v; + struct vector *v6; }; /* XXX: These should be configurable, better use a rb-tree */ @@ -95,6 +99,23 @@ struct ct_filter *ct_filter_create(void) return NULL; } + filter->v = vector_create(sizeof(struct ct_filter_netmask_ipv4)); + if (!filter->v) { + free(filter->h6); + free(filter->h); + free(filter); + return NULL; + } + + filter->v6 = vector_create(sizeof(struct ct_filter_netmask_ipv6)); + if (!filter->v6) { + free(filter->v); + free(filter->h6); + free(filter->h); + free(filter); + return NULL; + } + for (i=0; ilogic[i] = -1; @@ -105,6 +126,8 @@ void ct_filter_destroy(struct ct_filter *filter) { hashtable_destroy(filter->h); hashtable_destroy(filter->h6); + vector_destroy(filter->v); + vector_destroy(filter->v6); free(filter); } @@ -147,6 +170,39 @@ int ct_filter_add_ip(struct ct_filter *filter, void *data, uint8_t family) return 1; } +static int cmp_ipv4_addr(const void *a, const void *b) +{ + return memcmp(a, b, sizeof(struct ct_filter_netmask_ipv4)) == 0; +} + +static int cmp_ipv6_addr(const void *a, const void *b) +{ + return memcmp(a, b, sizeof(struct ct_filter_netmask_ipv6)) == 0; +} + +int ct_filter_add_netmask(struct ct_filter *filter, void *data, uint8_t family) +{ + filter = __filter_alloc(filter); + + switch(family) { + case AF_INET: + if (vector_iterate(filter->v, data, cmp_ipv4_addr)) { + errno = EEXIST; + return 0; + } + vector_add(filter->v, data); + break; + case AF_INET6: + if (vector_iterate(filter->v, data, cmp_ipv6_addr)) { + errno = EEXIST; + return 0; + } + vector_add(filter->v6, data); + break; + } + return 1; +} + void ct_filter_add_proto(struct ct_filter *f, int protonum) { f = __filter_alloc(f); @@ -176,6 +232,34 @@ __ct_filter_test_ipv6(struct ct_filter *f, struct nf_conntrack *ct) hashtable_test(f->h6, nfct_get_attr(ct, ATTR_REPL_IPV6_SRC))); } +static int +__ct_filter_test_mask4(const void *ptr, const void *ct) +{ + const struct ct_filter_netmask_ipv4 *elem = ptr; + const uint32_t src = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC); + const uint32_t dst = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC); + + return ((elem->ip & elem->mask) == (src & elem->mask) || + (elem->ip & elem->mask) == (dst & elem->mask)); +} + +static int +__ct_filter_test_mask6(const void *ptr, const void *ct) +{ + const struct ct_filter_netmask_ipv6 *elem = ptr; + const uint32_t *src = nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC); + const uint32_t *dst = nfct_get_attr(ct, ATTR_REPL_IPV6_SRC); + + return (((elem->ip[0] & elem->mask[0]) == (src[0] & elem->mask[0]) && + (elem->ip[1] & elem->mask[1]) == (src[1] & elem->mask[1]) && + (elem->ip[2] & elem->mask[2]) == (src[2] & elem->mask[2]) && + (elem->ip[3] & elem->mask[3]) == (src[3] & elem->mask[3])) || + ((elem->ip[0] & elem->mask[0]) == (dst[0] & elem->mask[0]) && + (elem->ip[1] & elem->mask[1]) == (dst[1] & elem->mask[1]) && + (elem->ip[2] & elem->mask[2]) == (dst[2] & elem->mask[2]) && + (elem->ip[3] & elem->mask[3]) == (dst[3] & elem->mask[3]))); +} + static int __ct_filter_test_state(struct ct_filter *f, struct nf_conntrack *ct) { uint16_t val = 0; @@ -212,11 +296,17 @@ int ct_filter_check(struct ct_filter *f, struct nf_conntrack *ct) if (f->logic[CT_FILTER_ADDRESS] != -1) { switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) { case AF_INET: + ret = vector_iterate(f->v, ct, __ct_filter_test_mask4); + if (ret ^ f->logic[CT_FILTER_ADDRESS]) + return 0; ret = __ct_filter_test_ipv4(f, ct); if (ret ^ f->logic[CT_FILTER_ADDRESS]) return 0; break; case AF_INET6: + ret = vector_iterate(f->v6, ct, __ct_filter_test_mask6); + if (ret ^ f->logic[CT_FILTER_ADDRESS]) + return 0; ret = __ct_filter_test_ipv6(f, ct); if (ret ^ f->logic[CT_FILTER_ADDRESS]) return 0; -- cgit v1.2.3