diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2008-11-25 23:34:48 +0100 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2008-11-25 23:34:48 +0100 |
commit | b2edf895af82914ab09a842641a45b7a806e9b1e (patch) | |
tree | 2b2890418f2f39bd12587288411420e9a0b9b369 | |
parent | 6262a4a7b7139fb5636228cb0f5a1e72f848d871 (diff) | |
download | conntrack-tools-b2edf895af82914ab09a842641a45b7a806e9b1e.tar.gz conntrack-tools-b2edf895af82914ab09a842641a45b7a806e9b1e.zip |
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 <pablo@netfilter.org>
-rw-r--r-- | doc/sync/alarm/conntrackd.conf | 3 | ||||
-rw-r--r-- | doc/sync/ftfw/conntrackd.conf | 3 | ||||
-rw-r--r-- | doc/sync/notrack/conntrackd.conf | 3 | ||||
-rw-r--r-- | include/Makefile.am | 2 | ||||
-rw-r--r-- | include/cidr.h | 8 | ||||
-rw-r--r-- | include/filter.h | 15 | ||||
-rw-r--r-- | include/vector.h | 13 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/cidr.c | 59 | ||||
-rw-r--r-- | src/filter.c | 90 | ||||
-rw-r--r-- | src/read_config_lex.l | 6 | ||||
-rw-r--r-- | src/read_config_yy.y | 86 | ||||
-rw-r--r-- | src/vector.c | 88 |
13 files changed, 358 insertions, 22 deletions
diff --git a/doc/sync/alarm/conntrackd.conf b/doc/sync/alarm/conntrackd.conf index 6995d6c..31a1a4d 100644 --- a/doc/sync/alarm/conntrackd.conf +++ b/doc/sync/alarm/conntrackd.conf @@ -191,6 +191,9 @@ General { IPv4_address 192.168.0.1 IPv4_address 192.168.1.1 IPv4_address 192.168.100.100 # dedicated link ip + # + # You can also specify networks in format IP/cidr. + # IPv4_address 192.168.0.0/24 } # diff --git a/doc/sync/ftfw/conntrackd.conf b/doc/sync/ftfw/conntrackd.conf index 3a2ed0e..ae2fe78 100644 --- a/doc/sync/ftfw/conntrackd.conf +++ b/doc/sync/ftfw/conntrackd.conf @@ -195,6 +195,9 @@ General { IPv4_address 192.168.0.1 IPv4_address 192.168.1.1 IPv4_address 192.168.100.100 # dedicated link ip + # + # You can also specify networks in format IP/cidr. + # IPv4_address 192.168.0.0/24 } # diff --git a/doc/sync/notrack/conntrackd.conf b/doc/sync/notrack/conntrackd.conf index e9835e8..d0e141c 100644 --- a/doc/sync/notrack/conntrackd.conf +++ b/doc/sync/notrack/conntrackd.conf @@ -179,6 +179,9 @@ General { IPv4_address 192.168.0.1 IPv4_address 192.168.1.1 IPv4_address 192.168.100.100 # dedicated link ip + # + # You can also specify networks in format IP/cidr. + # IPv4_address 192.168.0.0/24 } # diff --git a/include/Makefile.am b/include/Makefile.am index 3287a0c..4d22993 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,6 +2,6 @@ noinst_HEADERS = alarm.h jhash.h slist.h cache.h linux_list.h linux_rbtree.h \ sync.h conntrackd.h local.h us-conntrack.h \ debug.h log.h hash.h mcast.h conntrack.h \ - network.h filter.h queue.h \ + network.h filter.h queue.h vector.h \ traffic_stats.h netlink.h fds.h event.h bitops.h diff --git a/include/cidr.h b/include/cidr.h new file mode 100644 index 0000000..f8a4e2a --- /dev/null +++ b/include/cidr.h @@ -0,0 +1,8 @@ +#ifndef _CIDR_H_ + +uint32_t ipv4_cidr2mask_host(uint8_t cidr); +uint32_t ipv4_cidr2mask_net(uint8_t cidr); +void ipv6_cidr2mask_host(uint8_t cidr, uint32_t *res); +void ipv6_cidr2mask_net(uint8_t cidr, uint32_t *res); + +#endif diff --git a/include/filter.h b/include/filter.h index de0754e..567be34 100644 --- a/include/filter.h +++ b/include/filter.h @@ -2,11 +2,13 @@ #define _FILTER_H_ #include <stdint.h> +#include <string.h> +#include <netinet/in.h> enum ct_filter_type { CT_FILTER_L4PROTO, CT_FILTER_STATE, - CT_FILTER_ADDRESS, + CT_FILTER_ADDRESS, /* also for netmask */ CT_FILTER_MAX }; @@ -15,12 +17,23 @@ enum ct_filter_logic { CT_FILTER_POSITIVE = 1, }; +struct ct_filter_netmask_ipv4 { + uint32_t ip; + uint32_t mask; +}; + +struct ct_filter_netmask_ipv6 { + uint32_t ip[4]; + uint32_t mask[4]; +}; + struct nf_conntrack; struct ct_filter; struct ct_filter *ct_filter_create(void); void ct_filter_destroy(struct ct_filter *filter); int ct_filter_add_ip(struct ct_filter *filter, void *data, uint8_t family); +int ct_filter_add_netmask(struct ct_filter *filter, void *data, uint8_t family); void ct_filter_add_proto(struct ct_filter *filter, int protonum); void ct_filter_add_state(struct ct_filter *f, int protonum, int state); void ct_filter_set_logic(struct ct_filter *f, diff --git a/include/vector.h b/include/vector.h new file mode 100644 index 0000000..5b05cba --- /dev/null +++ b/include/vector.h @@ -0,0 +1,13 @@ +#ifndef _VECTOR_H_ +#define _VECTOR_H_ + +#include <stdlib.h> + +struct vector; + +struct vector *vector_create(size_t size); +void vector_destroy(struct vector *v); +int vector_add(struct vector *v, void *data); +int vector_iterate(struct vector *v, const void *data, int (*fcn)(const void *a, const void *b)); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 82f7dfe..64ed2b5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,13 +11,13 @@ conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp conntrack_LDFLAGS = $(all_libraries) @LIBNETFILTER_CONNTRACK_LIBS@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ - local.c log.c mcast.c netlink.c \ + local.c log.c mcast.c netlink.c vector.c \ filter.c fds.c event.c \ cache.c cache_iterators.c \ cache_lifetime.c cache_timer.c cache_wt.c \ sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \ traffic_stats.c stats-mode.c \ - network.c \ + network.c cidr.c \ build.c parse.c \ read_config_yy.y read_config_lex.l diff --git a/src/cidr.c b/src/cidr.c new file mode 100644 index 0000000..d43dabc --- /dev/null +++ b/src/cidr.c @@ -0,0 +1,59 @@ +/* + * (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 <stdint.h> +#include <string.h> +#include <netinet/in.h> +#include "cidr.h" + +/* returns the netmask in host byte order */ +uint32_t ipv4_cidr2mask_host(uint8_t cidr) +{ + return 0xFFFFFFFF << (32 - cidr); +} + +/* returns the netmask in network byte order */ +uint32_t ipv4_cidr2mask_net(uint8_t cidr) +{ + return htonl(ipv4_cidr2mask_host(cidr)); +} + +void ipv6_cidr2mask_host(uint8_t cidr, uint32_t *res) +{ + int i, j; + + memset(res, 0, sizeof(uint32_t)*4); + for (i = 0; i < 4 && cidr > 32; i++) { + res[i] = 0xFFFFFFFF; + cidr -= 32; + } + res[i] = 0xFFFFFFFF << (32 - cidr); + for (j = i+1; j < 4; j++) { + res[j] = 0; + } +} + +void ipv6_cidr2mask_net(uint8_t cidr, uint32_t *res) +{ + int i; + + ipv6_cidr2mask_host(cidr, res); + for (i=0; i<4; i++) + res[i] = htonl(res[i]); +} + 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 <libnetfilter_conntrack/libnetfilter_conntrack.h> #include <stdlib.h> #include <string.h> +#include <errno.h> #include <limits.h> 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; i<CT_FILTER_MAX; i++) filter->logic[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; diff --git a/src/read_config_lex.l b/src/read_config_lex.l index cbb6ca8..67c95d3 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -36,14 +36,16 @@ is_on [o|O][n|N] is_off [o|O][f|F][f|F] integer [0-9]+ path \/[^\"\n ]* +ip4_cidr \/[0-2]*[0-9]+ ip4_end [0-9]*[0-9]+ ip4_part [0-2]*{ip4_end} -ip4 {ip4_part}\.{ip4_part}\.{ip4_part}\.{ip4_part} +ip4 {ip4_part}\.{ip4_part}\.{ip4_part}\.{ip4_part}{ip4_cidr}? hex_255 [0-9a-fA-F]{1,4} +ip6_cidr \/[0-1]*[0-9]*[0-9]+ ip6_part {hex_255}":"? ip6_form1 {ip6_part}{0,16}"::"{ip6_part}{0,16} ip6_form2 ({hex_255}":"){16}{hex_255} -ip6 {ip6_form1}|{ip6_form2} +ip6 {ip6_form1}{ip6_cidr}?|{ip6_form2}{ip6_cidr}? string [a-zA-Z][a-zA-Z0-9\.]* persistent [P|p][E|e][R|r][S|s][I|i][S|s][T|t][E|e][N|n][T|T] nack [N|n][A|a][C|c][K|k] diff --git a/src/read_config_yy.y b/src/read_config_yy.y index 06ada52..32ddeff 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -26,6 +26,7 @@ #include <errno.h> #include "conntrackd.h" #include "bitops.h" +#include "cidr.h" #include <syslog.h> #include <libnetfilter_conntrack/libnetfilter_conntrack.h> #include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h> @@ -780,27 +781,55 @@ filter_address_list : filter_address_item : T_IPV4_ADDR T_IP { union inet_address ip; + char *slash; + unsigned int cidr = 32; memset(&ip, 0, sizeof(union inet_address)); + slash = strchr($2, '/'); + if (slash) { + *slash = '\0'; + cidr = atoi(slash+1); + if (cidr > 32) { + fprintf(stderr, "%s/%d is not a valid network, " + "ignoring\n", $2, cidr); + break; + } + } + if (!inet_aton($2, &ip.ipv4)) { fprintf(stderr, "%s is not a valid IPv4, ignoring", $2); break; } - if (!ct_filter_add_ip(STATE(us_filter), &ip, AF_INET)) { - if (errno == EEXIST) - fprintf(stderr, "IP %s is repeated " - "in the ignore pool\n", $2); - if (errno == ENOSPC) - fprintf(stderr, "Too many IP in the ignore pool!\n"); + if (slash && cidr < 32) { + /* network byte order */ + struct ct_filter_netmask_ipv4 tmp = { + .ip = ip.ipv4, + .mask = ipv4_cidr2mask_net(cidr) + }; + + if (!ct_filter_add_netmask(STATE(us_filter), &tmp, AF_INET)) { + if (errno == EEXIST) + fprintf(stderr, "Netmask %s is repeated " + "in the ignore pool\n", $2); + } + } else { + if (!ct_filter_add_ip(STATE(us_filter), &ip, AF_INET)) { + if (errno == EEXIST) + fprintf(stderr, "IP %s is repeated " + "in the ignore pool\n", $2); + if (errno == ENOSPC) + fprintf(stderr, "Too many IP in the " + "ignore pool!\n"); + } } - __kernel_filter_start(); + /* host byte order */ struct nfct_filter_ipv4 filter_ipv4 = { - .addr = htonl(ip.ipv4), - .mask = 0xffffffff, + .addr = ntohl(ip.ipv4), + .mask = ipv4_cidr2mask_host(cidr), }; nfct_filter_add_attr(STATE(filter), NFCT_FILTER_SRC_IPV4, &filter_ipv4); @@ -810,9 +839,22 @@ filter_address_item : T_IPV4_ADDR T_IP filter_address_item : T_IPV6_ADDR T_IP { union inet_address ip; + char *slash; + int cidr; memset(&ip, 0, sizeof(union inet_address)); + slash = strchr($2, '/'); + if (slash) { + *slash = '\0'; + cidr = atoi(slash+1); + if (cidr > 128) { + fprintf(stderr, "%s/%d is not a valid network, " + "ignoring\n", $2, cidr); + break; + } + } + #ifdef HAVE_INET_PTON_IPV6 if (inet_pton(AF_INET6, $2, &ip.ipv6) <= 0) { fprintf(stderr, "%s is not a valid IPv6, ignoring", $2); @@ -822,13 +864,25 @@ filter_address_item : T_IPV6_ADDR T_IP fprintf(stderr, "Cannot find inet_pton(), IPv6 unsupported!"); break; #endif - - if (!ct_filter_add_ip(STATE(us_filter), &ip, AF_INET6)) { - if (errno == EEXIST) - fprintf(stderr, "IP %s is repeated " - "in the ignore pool\n", $2); - if (errno == ENOSPC) - fprintf(stderr, "Too many IP in the ignore pool!\n"); + if (slash && cidr < 128) { + struct ct_filter_netmask_ipv6 tmp; + + memcpy(tmp.ip, ip.ipv6, sizeof(uint32_t)*4); + ipv6_cidr2mask_net(cidr, tmp.mask); + if (!ct_filter_add_netmask(STATE(us_filter), &tmp, AF_INET6)) { + if (errno == EEXIST) + fprintf(stderr, "Netmask %s is repeated " + "in the ignore pool\n", $2); + } + } else { + if (!ct_filter_add_ip(STATE(us_filter), &ip, AF_INET6)) { + if (errno == EEXIST) + fprintf(stderr, "IP %s is repeated " + "in the ignore pool\n", $2); + if (errno == ENOSPC) + fprintf(stderr, "Too many IP in the " + "ignore pool!\n"); + } } }; diff --git a/src/vector.c b/src/vector.c new file mode 100644 index 0000000..c81e7ce --- /dev/null +++ b/src/vector.c @@ -0,0 +1,88 @@ +/* + * (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 "vector.h" + +#include <stdlib.h> +#include <string.h> + +struct vector { + char *data; + unsigned int cur_elems; + unsigned int max_elems; + size_t size; +}; + +#define DEFAULT_VECTOR_MEMBERS 8 +#define DEFAULT_VECTOR_GROWTH 8 + +struct vector *vector_create(size_t size) +{ + struct vector *v; + + v = calloc(sizeof(struct vector), 1); + if (v == NULL) + return NULL; + + v->size = size; + v->cur_elems = 0; + v->max_elems = DEFAULT_VECTOR_MEMBERS; + + v->data = calloc(size * DEFAULT_VECTOR_MEMBERS, 1); + if (v->data == NULL) { + free(v); + return NULL; + } + + return v; +} + +void vector_destroy(struct vector *v) +{ + free(v->data); + free(v); +} + +int vector_add(struct vector *v, void *data) +{ + if (v->cur_elems >= v->max_elems) { + v->max_elems += DEFAULT_VECTOR_GROWTH; + v->data = realloc(v->data, v->max_elems * v->size); + if (v->data == NULL) { + v->max_elems -= DEFAULT_VECTOR_GROWTH; + return -1; + } + } + memcpy(v->data + (v->size * v->cur_elems), data, v->size); + v->cur_elems++; + return 0; +} + +int vector_iterate(struct vector *v, + const void *data, + int (*fcn)(const void *a, const void *b)) +{ + unsigned int i; + + for (i=0; i<v->cur_elems; i++) { + char *ptr = v->data + (v->size * i); + if (fcn(ptr, data)) + return 1; + } + return 0; +} |