From 6f7bc84fb819e87a9145394b0e08fd194b1497da Mon Sep 17 00:00:00 2001 From: "/C=EU/ST=EU/CN=Pablo Neira Ayuso/emailAddress=pablo@netfilter.org" Date: Sat, 2 Feb 2008 04:35:05 +0000 Subject: add IPv6 support to conntrackd --- ChangeLog | 1 + TODO | 2 +- include/cache.h | 2 +- include/ignore.h | 5 ++- include/mcast.h | 3 +- src/cache.c | 107 ++++++++++++++++++++++++++++++++------------------- src/ignore_pool.c | 65 ++++++++++++++++++------------- src/mcast.c | 18 +++++---- src/read_config_yy.y | 99 ++++++++++++++++++++++++++++++++++------------- src/stats-mode.c | 1 - src/sync-mode.c | 2 - 11 files changed, 197 insertions(+), 108 deletions(-) diff --git a/ChangeLog b/ChangeLog index dc4efab..aa9b3d2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,7 @@ o cleanup error message o add support for -E -o xml,timestamp = conntrackd = +o Add IPv6 support o Remove window tracking disabling limitation (requires Linux kernel >= 2.6.22) o syslog support (based on patch from Simon Lodal) o add CacheWriteThrough clause: external cache write through policy diff --git a/TODO b/TODO index 04923fc..9450aeb 100644 --- a/TODO +++ b/TODO @@ -18,7 +18,7 @@ by dificulty levels: = Requires some work = [ ] study better keepalived transitions - [ ] test/fix ipv6 support + [X] fix ipv6 support [X] add support setup related conntracks [ ] NAT sequence adjustment support diff --git a/include/cache.h b/include/cache.h index a2b2005..f5afbe5 100644 --- a/include/cache.h +++ b/include/cache.h @@ -75,7 +75,7 @@ struct cache_extra { struct nf_conntrack; -struct cache *cache_create(const char *name, unsigned int features, uint8_t proto, struct cache_extra *extra); +struct cache *cache_create(const char *name, unsigned int features, struct cache_extra *extra); void cache_destroy(struct cache *e); struct us_conntrack *cache_add(struct cache *c, struct nf_conntrack *ct); diff --git a/include/ignore.h b/include/ignore.h index efb375d..e5e96ff 100644 --- a/include/ignore.h +++ b/include/ignore.h @@ -7,11 +7,12 @@ struct nf_conntrack; struct ignore_pool { struct hashtable *h; + struct hashtable *h6; }; -struct ignore_pool *ignore_pool_create(uint8_t family); +struct ignore_pool *ignore_pool_create(void); void ignore_pool_destroy(struct ignore_pool *ip); -int ignore_pool_add(struct ignore_pool *ip, void *data); +int ignore_pool_add(struct ignore_pool *ip, void *data, uint8_t family); int ignore_pool_test(struct ignore_pool *ip, struct nf_conntrack *ct); #endif diff --git a/include/mcast.h b/include/mcast.h index 4e89c72..c2fd3ec 100644 --- a/include/mcast.h +++ b/include/mcast.h @@ -16,7 +16,7 @@ struct mcast_conf { } in; union { struct in_addr interface_addr; - struct in6_addr interface_addr6; + unsigned int interface_index6; } ifa; int mtu; char iface[IFNAMSIZ]; @@ -34,6 +34,7 @@ struct mcast_sock { struct sockaddr_in ipv4; struct sockaddr_in6 ipv6; } addr; + socklen_t sockaddr_len; struct mcast_stats stats; }; diff --git a/src/cache.c b/src/cache.c index 2f0e57a..73d539a 100644 --- a/src/cache.c +++ b/src/cache.c @@ -19,6 +19,7 @@ #include "cache.h" #include "jhash.h" #include "hash.h" +#include "log.h" #include "us-conntrack.h" #include "conntrackd.h" @@ -27,11 +28,9 @@ #include #include -static uint32_t hash(const void *data, struct hashtable *table) +static uint32_t __hash4(const struct nf_conntrack *ct, struct hashtable *table) { unsigned int a, b; - const struct us_conntrack *u = data; - struct nf_conntrack *ct = u->ct; a = jhash(nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC), sizeof(uint32_t), ((nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16) | @@ -51,11 +50,9 @@ static uint32_t hash(const void *data, struct hashtable *table) return ((uint64_t)jhash_2words(a, b, 0) * table->hashsize) >> 32; } -static uint32_t hash6(const void *data, struct hashtable *table) +static uint32_t __hash6(const struct nf_conntrack *ct, struct hashtable *table) { unsigned int a, b; - const struct us_conntrack *u = data; - struct nf_conntrack *ct = u->ct; a = jhash(nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC), sizeof(uint32_t)*4, ((nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16) | @@ -68,12 +65,30 @@ static uint32_t hash6(const void *data, struct hashtable *table) return ((uint64_t)jhash_2words(a, b, 0) * table->hashsize) >> 32; } +static uint32_t hash(const void *data, struct hashtable *table) +{ + int ret = 0; + const struct us_conntrack *u = data; + + switch(nfct_get_attr_u8(u->ct, ATTR_L3PROTO)) { + case AF_INET: + ret = __hash4(u->ct, table); + break; + case AF_INET6: + ret = __hash6(u->ct, table); + break; + default: + dlog(LOG_ERR, "unknown layer 3 proto in hash"); + break; + } + + return ret; +} + static int __compare(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2) { - return ((nfct_get_attr_u8(ct1, ATTR_ORIG_L3PROTO) == - nfct_get_attr_u8(ct2, ATTR_ORIG_L3PROTO)) && - (nfct_get_attr_u8(ct1, ATTR_ORIG_L4PROTO) == + return ((nfct_get_attr_u8(ct1, ATTR_ORIG_L4PROTO) == nfct_get_attr_u8(ct2, ATTR_ORIG_L4PROTO)) && (nfct_get_attr_u16(ct1, ATTR_ORIG_PORT_SRC) == nfct_get_attr_u16(ct2, ATTR_ORIG_PORT_SRC)) && @@ -85,11 +100,9 @@ static int __compare(const struct nf_conntrack *ct1, nfct_get_attr_u16(ct2, ATTR_REPL_PORT_DST))); } -static int compare(const void *data1, const void *data2) +static int +__compare4(const struct us_conntrack *u1, const struct us_conntrack *u2) { - const struct us_conntrack *u1 = data1; - const struct us_conntrack *u2 = data2; - return ((nfct_get_attr_u32(u1->ct, ATTR_ORIG_IPV4_SRC) == nfct_get_attr_u32(u2->ct, ATTR_ORIG_IPV4_SRC)) && (nfct_get_attr_u32(u1->ct, ATTR_ORIG_IPV4_DST) == @@ -101,20 +114,46 @@ static int compare(const void *data1, const void *data2) __compare(u1->ct, u2->ct)); } -static int compare6(const void *data1, const void *data2) +static int +__compare6(const struct us_conntrack *u1, const struct us_conntrack *u2) { + return ((memcmp(nfct_get_attr(u1->ct, ATTR_ORIG_IPV6_SRC), + nfct_get_attr(u2->ct, ATTR_ORIG_IPV6_SRC), + sizeof(uint32_t)*4) == 0) && + (memcmp(nfct_get_attr(u1->ct, ATTR_ORIG_IPV6_DST), + nfct_get_attr(u2->ct, ATTR_ORIG_IPV6_DST), + sizeof(uint32_t)*4) == 0) && + (memcmp(nfct_get_attr(u1->ct, ATTR_REPL_IPV6_SRC), + nfct_get_attr(u2->ct, ATTR_REPL_IPV6_SRC), + sizeof(uint32_t)*4) == 0) && + (memcmp(nfct_get_attr(u1->ct, ATTR_REPL_IPV6_DST), + nfct_get_attr(u2->ct, ATTR_REPL_IPV6_DST), + sizeof(uint32_t)*4) == 0) && + __compare(u1->ct, u2->ct)); +} + +static int compare(const void *data1, const void *data2) +{ + int ret = 0; const struct us_conntrack *u1 = data1; const struct us_conntrack *u2 = data2; - return ((nfct_get_attr_u32(u1->ct, ATTR_ORIG_IPV6_SRC) == - nfct_get_attr_u32(u2->ct, ATTR_ORIG_IPV6_SRC)) && - (nfct_get_attr_u32(u1->ct, ATTR_ORIG_IPV6_DST) == - nfct_get_attr_u32(u2->ct, ATTR_ORIG_IPV6_DST)) && - (nfct_get_attr_u32(u1->ct, ATTR_REPL_IPV6_SRC) == - nfct_get_attr_u32(u2->ct, ATTR_REPL_IPV6_SRC)) && - (nfct_get_attr_u32(u1->ct, ATTR_REPL_IPV6_DST) == - nfct_get_attr_u32(u2->ct, ATTR_REPL_IPV6_DST)) && - __compare(u1->ct, u2->ct)); + if (nfct_get_attr_u8(u1->ct, ATTR_L3PROTO) != + nfct_get_attr_u8(u2->ct, ATTR_L3PROTO)) + return ret; + + switch(nfct_get_attr_u8(u1->ct, ATTR_L3PROTO)) { + case AF_INET: + ret = __compare4(u1, u2); + break; + case AF_INET6: + ret = __compare6(u1, u2); + break; + default: + dlog(LOG_ERR, "unknown layer 3 in compare"); + break; + } + return ret; } struct cache_feature *cache_feature[CACHE_MAX_FEATURE] = { @@ -125,7 +164,6 @@ struct cache_feature *cache_feature[CACHE_MAX_FEATURE] = { struct cache *cache_create(const char *name, unsigned int features, - uint8_t proto, struct cache_extra *extra) { size_t size = sizeof(struct us_conntrack); @@ -175,22 +213,11 @@ struct cache *cache_create(const char *name, } memcpy(c->feature_offset, feature_offset, sizeof(unsigned int) * j); - switch(proto) { - case AF_INET: - c->h = hashtable_create(CONFIG(hashsize), - CONFIG(limit), - size, - hash, - compare); - break; - case AF_INET6: - c->h = hashtable_create(CONFIG(hashsize), - CONFIG(limit), - size, - hash6, - compare6); - break; - } + c->h = hashtable_create(CONFIG(hashsize), + CONFIG(limit), + size, + hash, + compare); if (!c->h) { free(c->features); diff --git a/src/ignore_pool.c b/src/ignore_pool.c index 2d898d1..027d628 100644 --- a/src/ignore_pool.c +++ b/src/ignore_pool.c @@ -26,7 +26,7 @@ #include #include -/* XXX: These should be configurable */ +/* XXX: These should be configurable, better use a rb-tree */ #define IGNORE_POOL_SIZE 128 #define IGNORE_POOL_LIMIT INT_MAX @@ -55,7 +55,7 @@ static int compare6(const void *data1, const void *data2) return memcmp(data1, data2, sizeof(uint32_t)*4) == 0; } -struct ignore_pool *ignore_pool_create(uint8_t proto) +struct ignore_pool *ignore_pool_create(void) { struct ignore_pool *ip; @@ -64,24 +64,23 @@ struct ignore_pool *ignore_pool_create(uint8_t proto) return NULL; memset(ip, 0, sizeof(struct ignore_pool)); - switch(proto) { - case AF_INET: - ip->h = hashtable_create(IGNORE_POOL_SIZE, - IGNORE_POOL_LIMIT, - sizeof(uint32_t), - hash, - compare); - break; - case AF_INET6: - ip->h = hashtable_create(IGNORE_POOL_SIZE, - IGNORE_POOL_LIMIT, - sizeof(uint32_t)*4, - hash6, - compare6); - break; + ip->h = hashtable_create(IGNORE_POOL_SIZE, + IGNORE_POOL_LIMIT, + sizeof(uint32_t), + hash, + compare); + if (!ip->h) { + free(ip); + return NULL; } - if (!ip->h) { + ip->h6 = hashtable_create(IGNORE_POOL_SIZE, + IGNORE_POOL_LIMIT, + sizeof(uint32_t)*4, + hash6, + compare6); + if (!ip->h6) { + free(ip->h); free(ip); return NULL; } @@ -92,20 +91,31 @@ struct ignore_pool *ignore_pool_create(uint8_t proto) void ignore_pool_destroy(struct ignore_pool *ip) { hashtable_destroy(ip->h); + hashtable_destroy(ip->h6); free(ip); } -int ignore_pool_add(struct ignore_pool *ip, void *data) +int ignore_pool_add(struct ignore_pool *ip, void *data, uint8_t family) { - if (!hashtable_add(ip->h, data)) - return 0; - + switch(family) { + case AF_INET: + if (!hashtable_add(ip->h, data)) + return 0; + break; + case AF_INET6: + if (!hashtable_add(ip->h6, data)) + return 0; + break; + } return 1; } static int __ignore_pool_test_ipv4(struct ignore_pool *ip, struct nf_conntrack *ct) { + if (!ip->h) + return 0; + return (hashtable_test(ip->h, nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC)) || hashtable_test(ip->h, nfct_get_attr(ct, ATTR_ORIG_IPV4_DST)) || hashtable_test(ip->h, nfct_get_attr(ct, ATTR_REPL_IPV4_SRC)) || @@ -115,10 +125,13 @@ __ignore_pool_test_ipv4(struct ignore_pool *ip, struct nf_conntrack *ct) static int __ignore_pool_test_ipv6(struct ignore_pool *ip, struct nf_conntrack *ct) { - return (hashtable_test(ip->h, nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC)) || - hashtable_test(ip->h, nfct_get_attr(ct, ATTR_ORIG_IPV6_DST)) || - hashtable_test(ip->h, nfct_get_attr(ct, ATTR_REPL_IPV6_SRC)) || - hashtable_test(ip->h, nfct_get_attr(ct, ATTR_REPL_IPV6_DST))); + if (!ip->h6) + return 0; + + return (hashtable_test(ip->h6, nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC)) || + hashtable_test(ip->h6, nfct_get_attr(ct, ATTR_ORIG_IPV6_DST)) || + hashtable_test(ip->h6, nfct_get_attr(ct, ATTR_REPL_IPV6_SRC)) || + hashtable_test(ip->h6, nfct_get_attr(ct, ATTR_REPL_IPV6_DST))); } int ignore_pool_test(struct ignore_pool *ip, struct nf_conntrack *ct) diff --git a/src/mcast.c b/src/mcast.c index 8307f26..f945511 100644 --- a/src/mcast.c +++ b/src/mcast.c @@ -51,17 +51,20 @@ struct mcast_sock *mcast_server_create(struct mcast_conf *conf) m->addr.ipv4.sin_family = AF_INET; m->addr.ipv4.sin_port = htons(conf->port); m->addr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY); + + m->sockaddr_len = sizeof(struct sockaddr_in); break; case AF_INET6: memcpy(&mreq.ipv6.ipv6mr_multiaddr, &conf->in.inet_addr6, sizeof(uint32_t) * 4); - memcpy(&mreq.ipv6.ipv6mr_interface, &conf->ifa.interface_addr6, - sizeof(uint32_t) * 4); + mreq.ipv6.ipv6mr_interface = conf->ifa.interface_index6; m->addr.ipv6.sin6_family = AF_INET6; m->addr.ipv6.sin6_port = htons(conf->port); m->addr.ipv6.sin6_addr = in6addr_any; + + m->sockaddr_len = sizeof(struct sockaddr_in6); break; } @@ -93,8 +96,7 @@ struct mcast_sock *mcast_server_create(struct mcast_conf *conf) return NULL; } - if (bind(m->fd, (struct sockaddr *) &m->addr, - sizeof(struct sockaddr)) == -1) { + if (bind(m->fd, (struct sockaddr *) &m->addr, m->sockaddr_len) == -1) { debug("mcast_sock_server_create:bind"); close(m->fd); free(m); @@ -139,6 +141,7 @@ __mcast_client_create_ipv4(struct mcast_sock *m, struct mcast_conf *conf) m->addr.ipv4.sin_family = AF_INET; m->addr.ipv4.sin_port = htons(conf->port); m->addr.ipv4.sin_addr = conf->in.inet_addr; + m->sockaddr_len = sizeof(struct sockaddr_in); if (setsockopt(m->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &no, sizeof(int)) < 0) { @@ -168,6 +171,7 @@ __mcast_client_create_ipv6(struct mcast_sock *m, struct mcast_conf *conf) memcpy(&m->addr.ipv6.sin6_addr, &conf->in.inet_addr6, sizeof(struct in6_addr)); + m->sockaddr_len = sizeof(struct sockaddr_in6); if (setsockopt(m->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no, sizeof(int)) < 0) { @@ -177,8 +181,8 @@ __mcast_client_create_ipv6(struct mcast_sock *m, struct mcast_conf *conf) } if (setsockopt(m->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, - &conf->ifa.interface_addr, - sizeof(struct in_addr)) == -1) { + &conf->ifa.interface_index6, + sizeof(unsigned int)) == -1) { debug("mcast_sock_client_create:setsockopt3"); close(m->fd); return -1; @@ -247,7 +251,7 @@ ssize_t mcast_send(struct mcast_sock *m, void *data, int size) size, 0, (struct sockaddr *) &m->addr, - sizeof(struct sockaddr)); + m->sockaddr_len); if (ret == -1) { debug("mcast_sock_send"); m->stats.error++; diff --git a/src/read_config_yy.y b/src/read_config_yy.y index 0ba5331..86fee9b 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -177,37 +177,63 @@ ignore_traffic_options : ignore_traffic_option : T_IPV4_ADDR T_IP { union inet_address ip; - int family = 0; memset(&ip, 0, sizeof(union inet_address)); - if (inet_aton($2, &ip.ipv4)) - family = AF_INET; -#ifdef HAVE_INET_PTON_IPV6 - else if (inet_pton(AF_INET6, $2, &ip.ipv6) > 0) - family = AF_INET6; -#endif + if (!inet_aton($2, &ip.ipv4)) { + fprintf(stderr, "%s is not a valid IPv4, ignoring", $2); + break; + } + + if (!STATE(ignore_pool)) { + STATE(ignore_pool) = ignore_pool_create(); + if (!STATE(ignore_pool)) { + fprintf(stderr, "Can't create ignore pool!\n"); + exit(EXIT_FAILURE); + } + } + + if (!ignore_pool_add(STATE(ignore_pool), &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 (!family) { - fprintf(stderr, "%s is not a valid IP, ignoring", $2); +ignore_traffic_option : T_IPV6_ADDR T_IP +{ + union inet_address ip; + + memset(&ip, 0, sizeof(union inet_address)); + +#ifdef HAVE_INET_PTON_IPV6 + if (inet_pton(AF_INET6, $2, &ip.ipv6) <= 0) { + fprintf(stderr, "%s is not a valid IPv6, ignoring", $2); break; } +#else + fprintf(stderr, "Cannot find inet_pton(), IPv6 unsupported!"); + break; +#endif if (!STATE(ignore_pool)) { - STATE(ignore_pool) = ignore_pool_create(family); + STATE(ignore_pool) = ignore_pool_create(); if (!STATE(ignore_pool)) { fprintf(stderr, "Can't create ignore pool!\n"); exit(EXIT_FAILURE); } } - if (!ignore_pool_add(STATE(ignore_pool), &ip)) { + if (!ignore_pool_add(STATE(ignore_pool), &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"); } + }; multicast_line : T_MULTICAST '{' multicast_options '}'; @@ -235,8 +261,13 @@ multicast_option : T_IPV4_ADDR T_IP multicast_option : T_IPV6_ADDR T_IP { #ifdef HAVE_INET_PTON_IPV6 - if (inet_pton(AF_INET6, $2, &conf.mcast.in) <= 0) + if (inet_pton(AF_INET6, $2, &conf.mcast.in) <= 0) { fprintf(stderr, "%s is not a valid IPv6 address\n", $2); + break; + } +#else + fprintf(stderr, "Cannot find inet_pton(), IPv6 unsupported!"); + break; #endif if (conf.mcast.ipproto == AF_INET) { @@ -247,6 +278,19 @@ multicast_option : T_IPV6_ADDR T_IP } conf.mcast.ipproto = AF_INET6; + + if (conf.mcast.iface[0] && !conf.mcast.ifa.interface_index6) { + unsigned int idx; + + idx = if_nametoindex($2); + if (!idx) { + fprintf(stderr, "%s is an invalid interface.\n", $2); + break; + } + + conf.mcast.ifa.interface_index6 = idx; + conf.mcast.ipproto = AF_INET6; + } }; multicast_option : T_IPV4_IFACE T_IP @@ -268,24 +312,25 @@ multicast_option : T_IPV4_IFACE T_IP multicast_option : T_IPV6_IFACE T_IP { -#ifdef HAVE_INET_PTON_IPV6 - if (inet_pton(AF_INET6, $2, &conf.mcast.ifa) <= 0) - fprintf(stderr, "%s is not a valid IPv6 address\n", $2); -#endif - - if (conf.mcast.ipproto == AF_INET) { - fprintf(stderr, "Your multicast interface is IPv6 but " - "is binded to an IPv4 interface? Surely " - "this is not what you want\n"); - break; - } - - conf.mcast.ipproto = AF_INET6; -}; + fprintf(stderr, "IPv6_interface not required for IPv6, ignoring.\n"); +} multicast_option : T_IFACE T_STRING { strncpy(conf.mcast.iface, $2, IFNAMSIZ); + + if (conf.mcast.ipproto == AF_INET6) { + unsigned int idx; + + idx = if_nametoindex($2); + if (!idx) { + fprintf(stderr, "%s is an invalid interface.\n", $2); + break; + } + + conf.mcast.ifa.interface_index6 = idx; + conf.mcast.ipproto = AF_INET6; + } }; multicast_option : T_BACKLOG T_NUMBER @@ -690,7 +735,7 @@ init_config(char *filename) /* create empty pool */ if (!STATE(ignore_pool)) { - STATE(ignore_pool) = ignore_pool_create(CONFIG(family)); + STATE(ignore_pool) = ignore_pool_create(); if (!STATE(ignore_pool)) { fprintf(stderr, "Can't create ignore pool!\n"); exit(EXIT_FAILURE); diff --git a/src/stats-mode.c b/src/stats-mode.c index 9e6089c..b6ae2bd 100644 --- a/src/stats-mode.c +++ b/src/stats-mode.c @@ -38,7 +38,6 @@ static int init_stats(void) STATE_STATS(cache) = cache_create("stats", LIFETIME, - CONFIG(family), NULL); if (!STATE_STATS(cache)) { dlog(LOG_ERR, "can't allocate memory for the " diff --git a/src/sync-mode.c b/src/sync-mode.c index b9fc25c..a81309f 100644 --- a/src/sync-mode.c +++ b/src/sync-mode.c @@ -151,7 +151,6 @@ static int init_sync(void) STATE_SYNC(internal) = cache_create("internal", STATE_SYNC(sync)->internal_cache_flags, - CONFIG(family), STATE_SYNC(sync)->internal_cache_extra); if (!STATE_SYNC(internal)) { @@ -167,7 +166,6 @@ static int init_sync(void) STATE_SYNC(external) = cache_create("external", STATE_SYNC(sync)->external_cache_flags, - CONFIG(family), NULL); if (!STATE_SYNC(external)) { -- cgit v1.2.3