From 0c586c00998f26018dabe202a9b262d7bb69ebc3 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 11 Apr 2013 17:34:44 +0200 Subject: [PATCH 01/42] .gitignore: add OS X .DS_Store files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ce8ed17c1..024d9e452 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ apidoc/ *.diff *.tar.bz2 *.tar.gz +.DS_Store From b0ccd14f2aa4e65d43e5c94ecdd296b1ad213bb1 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 11 Apr 2013 19:20:33 +0200 Subject: [PATCH 02/42] semaphore: similar to thread_create(), semaphore_create() is used by Mach The compiler spits no warning, but the wrong symbol is used when calling semaphore_create() from strongSwan. Override the name with a #define to force the use of our semaphore_create(). --- src/libstrongswan/threading/semaphore.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libstrongswan/threading/semaphore.h b/src/libstrongswan/threading/semaphore.h index cdb0a6f19..34d814971 100644 --- a/src/libstrongswan/threading/semaphore.h +++ b/src/libstrongswan/threading/semaphore.h @@ -21,6 +21,11 @@ #ifndef THREADING_SEMAPHORE_H_ #define THREADING_SEMAPHORE_H_ +#ifdef __APPLE__ +/* Mach uses a semaphore_create() call, use a different name for ours */ +#define semaphore_create(x) strongswan_semaphore_create(x) +#endif /* __APPLE__ */ + typedef struct semaphore_t semaphore_t; /** From e9326eba13ef4ea957824d7e6b2cfb51a0502637 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 11 Apr 2013 19:40:31 +0200 Subject: [PATCH 03/42] socket-default: Use -1 if socket is not available, as 0 is actually a valid fd --- .../socket_default/socket_default_socket.c | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index c0b744a68..59dc5b3d9 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -162,23 +162,26 @@ METHOD(socket_t, receiver, status_t, FD_ZERO(&rfds); - if (this->ipv4) + if (this->ipv4 != -1) { FD_SET(this->ipv4, &rfds); + max_fd = max(max_fd, this->ipv4); } - if (this->ipv4_natt) + if (this->ipv4_natt != -1) { FD_SET(this->ipv4_natt, &rfds); + max_fd = max(max_fd, this->ipv4_natt); } - if (this->ipv6) + if (this->ipv6 != -1) { FD_SET(this->ipv6, &rfds); + max_fd = max(max_fd, this->ipv6); } - if (this->ipv6_natt) + if (this->ipv6_natt != -1) { FD_SET(this->ipv6_natt, &rfds); + max_fd = max(max_fd, this->ipv6_natt); } - max_fd = max(max(this->ipv4, this->ipv4_natt), max(this->ipv6, this->ipv6_natt)); DBG2(DBG_NET, "waiting for data on sockets"); oldstate = thread_cancelability(TRUE); @@ -537,20 +540,20 @@ static int open_socket(private_socket_default_socket_t *this, pktinfo = IPV6_RECVPKTINFO; break; default: - return 0; + return -1; } skt = socket(family, SOCK_DGRAM, IPPROTO_UDP); if (skt < 0) { DBG1(DBG_NET, "could not open socket: %s", strerror(errno)); - return 0; + return -1; } if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) { DBG1(DBG_NET, "unable to set SO_REUSEADDR on socket: %s", strerror(errno)); close(skt); - return 0; + return -1; } /* bind the socket */ @@ -558,7 +561,7 @@ static int open_socket(private_socket_default_socket_t *this, { DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno)); close(skt); - return 0; + return -1; } /* retrieve randomly allocated port if needed */ @@ -568,7 +571,7 @@ static int open_socket(private_socket_default_socket_t *this, { DBG1(DBG_NET, "unable to determine port: %s", strerror(errno)); close(skt); - return 0; + return -1; } switch (family) { @@ -588,7 +591,7 @@ static int open_socket(private_socket_default_socket_t *this, { DBG1(DBG_NET, "unable to set IP_PKTINFO on socket: %s", strerror(errno)); close(skt); - return 0; + return -1; } } @@ -613,19 +616,19 @@ static int open_socket(private_socket_default_socket_t *this, METHOD(socket_t, destroy, void, private_socket_default_socket_t *this) { - if (this->ipv4) + if (this->ipv4 != -1) { close(this->ipv4); } - if (this->ipv4_natt) + if (this->ipv4_natt != -1) { close(this->ipv4_natt); } - if (this->ipv6) + if (this->ipv6 != -1) { close(this->ipv6); } - if (this->ipv6_natt) + if (this->ipv6_natt != -1) { close(this->ipv6_natt); } @@ -669,34 +672,34 @@ socket_default_socket_t *socket_default_socket_create() /* we allocate IPv6 sockets first as that will reserve randomly allocated * ports also for IPv4 */ this->ipv6 = open_socket(this, AF_INET6, &this->port); - if (this->ipv6 == 0) + if (this->ipv6 == -1) { DBG1(DBG_NET, "could not open IPv6 socket, IPv6 disabled"); } else { this->ipv6_natt = open_socket(this, AF_INET6, &this->natt); - if (this->ipv6_natt == 0) + if (this->ipv6_natt == -1) { DBG1(DBG_NET, "could not open IPv6 NAT-T socket"); } } this->ipv4 = open_socket(this, AF_INET, &this->port); - if (this->ipv4 == 0) + if (this->ipv4 == -1) { DBG1(DBG_NET, "could not open IPv4 socket, IPv4 disabled"); } else { this->ipv4_natt = open_socket(this, AF_INET, &this->natt); - if (this->ipv4_natt == 0) + if (this->ipv4_natt == -1) { DBG1(DBG_NET, "could not open IPv4 NAT-T socket"); } } - if (!this->ipv4 && !this->ipv6) + if (this->ipv4 == -1 && this->ipv6 == -1) { DBG1(DBG_NET, "could not create any sockets"); destroy(this); From 6948df3220c98700a41e4c58cc29dd5292e2ff34 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 11 Apr 2013 19:47:28 +0200 Subject: [PATCH 04/42] socket-default: Don't try to send packet if we haven't a socket for given family --- .../plugins/socket_default/socket_default_socket.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index 59dc5b3d9..4e6217af3 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -329,7 +329,7 @@ METHOD(socket_t, receiver, status_t, METHOD(socket_t, sender, status_t, private_socket_default_socket_t *this, packet_t *packet) { - int sport, skt, family; + int sport, skt = -1, family; ssize_t bytes_sent; chunk_t data; host_t *src, *dst; @@ -379,9 +379,10 @@ METHOD(socket_t, sender, status_t, return FAILED; } } - else + if (skt == -1) { - DBG1(DBG_NET, "unable to locate a send socket for port %d", sport); + DBG1(DBG_NET, "no socket found to send IPv%d packet from port %d", + family == AF_INET ? 4 : 6, sport); return FAILED; } From a30727fe2b0d1d4c83aae7b00351e529ddef536b Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 11 Apr 2013 20:19:57 +0200 Subject: [PATCH 05/42] socket-default: refactor socket pair opening to a function --- .../socket_default/socket_default_socket.c | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index 4e6217af3..9c1bce303 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -614,6 +614,27 @@ static int open_socket(private_socket_default_socket_t *this, return skt; } +/** + * Open a socket pair (normal an NAT traversal) for a given address family + */ +static void open_socketpair(private_socket_default_socket_t *this, int family, + int *skt, int *skt_natt, char *label) +{ + *skt = open_socket(this, family, &this->port); + if (*skt == -1) + { + DBG1(DBG_NET, "could not open %s socket, %s disabled", label, label); + } + else + { + *skt_natt = open_socket(this, family, &this->natt); + if (*skt_natt == -1) + { + DBG1(DBG_NET, "could not open %s NAT-T socket", label); + } + } +} + METHOD(socket_t, destroy, void, private_socket_default_socket_t *this) { @@ -672,33 +693,8 @@ socket_default_socket_t *socket_default_socket_create() /* we allocate IPv6 sockets first as that will reserve randomly allocated * ports also for IPv4 */ - this->ipv6 = open_socket(this, AF_INET6, &this->port); - if (this->ipv6 == -1) - { - DBG1(DBG_NET, "could not open IPv6 socket, IPv6 disabled"); - } - else - { - this->ipv6_natt = open_socket(this, AF_INET6, &this->natt); - if (this->ipv6_natt == -1) - { - DBG1(DBG_NET, "could not open IPv6 NAT-T socket"); - } - } - - this->ipv4 = open_socket(this, AF_INET, &this->port); - if (this->ipv4 == -1) - { - DBG1(DBG_NET, "could not open IPv4 socket, IPv4 disabled"); - } - else - { - this->ipv4_natt = open_socket(this, AF_INET, &this->natt); - if (this->ipv4_natt == -1) - { - DBG1(DBG_NET, "could not open IPv4 NAT-T socket"); - } - } + open_socketpair(this, AF_INET6, &this->ipv6, &this->ipv6_natt, "IPv6"); + open_socketpair(this, AF_INET, &this->ipv4, &this->ipv4_natt, "IPv4"); if (this->ipv4 == -1 && this->ipv6 == -1) { From dc35d097b3723d10591cce4949b15ba825b05ef8 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 11 Apr 2013 20:22:06 +0200 Subject: [PATCH 06/42] socket-default: to bind to one dynamic port on OS X, create v4 socket before v6 It seems that the order of binding sockets of different address families to the same dynamic port must be v6-before-v4 on Linux, but v4-before-v6 on OS X. --- .../plugins/socket_default/socket_default_socket.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index 9c1bce303..a9c298362 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -692,9 +692,15 @@ socket_default_socket_t *socket_default_socket_create() } /* we allocate IPv6 sockets first as that will reserve randomly allocated - * ports also for IPv4 */ + * ports also for IPv4. On OS X, we have to do it the other way round + * for the same effect. */ +#ifdef __APPLE__ + open_socketpair(this, AF_INET, &this->ipv4, &this->ipv4_natt, "IPv4"); + open_socketpair(this, AF_INET6, &this->ipv6, &this->ipv6_natt, "IPv6"); +#else /* !__APPLE__ */ open_socketpair(this, AF_INET6, &this->ipv6, &this->ipv6_natt, "IPv6"); open_socketpair(this, AF_INET, &this->ipv4, &this->ipv4_natt, "IPv4"); +#endif /* __APPLE__ */ if (this->ipv4 == -1 && this->ipv6 == -1) { From 60ed88b2c7283a871ca80d8ed60f218d9731fa90 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 12 Apr 2013 13:19:59 +0200 Subject: [PATCH 07/42] child-sa: pass traffic selector to add_sa() regardless of IPsec mode This lets the kernel backend decide what to do with it, and in fact all kernel interfaces already handle this correctly. --- src/libcharon/sa/child_sa.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 463ad2e22..a8c246bfb 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -668,21 +668,18 @@ METHOD(child_sa_t, install, status_t, lifetime->time.rekey = 0; } - if (this->mode == MODE_BEET || this->mode == MODE_TRANSPORT) + /* BEET requires the bound address from the traffic selectors. + * TODO: We add just the first traffic selector for now, as the + * kernel accepts a single TS per SA only */ + if (inbound) { - /* BEET requires the bound address from the traffic selectors. - * TODO: We add just the first traffic selector for now, as the - * kernel accepts a single TS per SA only */ - if (inbound) - { - my_ts->get_first(my_ts, (void**)&dst_ts); - other_ts->get_first(other_ts, (void**)&src_ts); - } - else - { - my_ts->get_first(my_ts, (void**)&src_ts); - other_ts->get_first(other_ts, (void**)&dst_ts); - } + my_ts->get_first(my_ts, (void**)&dst_ts); + other_ts->get_first(other_ts, (void**)&src_ts); + } + else + { + my_ts->get_first(my_ts, (void**)&src_ts); + other_ts->get_first(other_ts, (void**)&dst_ts); } status = hydra->kernel_interface->add_sa(hydra->kernel_interface, From 344a4e54be887bb0ce8e32b5dbc29386a00d3887 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 20 Mar 2013 18:15:26 +0100 Subject: [PATCH 08/42] host: initialize sockaddr->sa_len if it is available --- src/libstrongswan/networking/host.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c index bffa96064..268d9b18e 100644 --- a/src/libstrongswan/networking/host.c +++ b/src/libstrongswan/networking/host.c @@ -54,6 +54,15 @@ struct private_host_t { socklen_t socklen; }; +/** + * Update the sockaddr internal sa_len option, if available + */ +static inline void update_sa_len(private_host_t *this) +{ +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + this->address.sa_len = this->socklen; +#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ +} METHOD(host_t, get_sockaddr, sockaddr_t*, private_host_t *this) @@ -440,6 +449,7 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) memcpy(&this->address4, (struct sockaddr_in*)sockaddr, sizeof(struct sockaddr_in)); this->socklen = sizeof(struct sockaddr_in); + update_sa_len(this); return &this->public; } case AF_INET6: @@ -447,6 +457,7 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) memcpy(&this->address6, (struct sockaddr_in6*)sockaddr, sizeof(struct sockaddr_in6)); this->socklen = sizeof(struct sockaddr_in6); + update_sa_len(this); return &this->public; } default: @@ -529,6 +540,7 @@ host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port) this->socklen = sizeof(struct sockaddr_in6); break; } + update_sa_len(this); return &this->public; } @@ -582,11 +594,13 @@ host_t *host_create_any(int family) case AF_INET: { this->socklen = sizeof(struct sockaddr_in); + update_sa_len(this); return &(this->public); } case AF_INET6: { this->socklen = sizeof(struct sockaddr_in6); + update_sa_len(this); return &this->public; } default: From 7749eb0d2a86d1027c801d7f6d82d281e89825a2 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 11 Apr 2013 19:51:45 +0200 Subject: [PATCH 09/42] host: print %#H format specifiers not as %any, but with the port --- src/libstrongswan/networking/host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c index 268d9b18e..65e1b7791 100644 --- a/src/libstrongswan/networking/host.c +++ b/src/libstrongswan/networking/host.c @@ -111,7 +111,7 @@ int host_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, { snprintf(buffer, sizeof(buffer), "(null)"); } - else if (is_anyaddr(this) && !spec->plus) + else if (is_anyaddr(this) && !spec->plus && !spec->hash) { snprintf(buffer, sizeof(buffer), "%%any%s", this->address.sa_family == AF_INET6 ? "6" : ""); From 4dc83e9facf050b0aba6f974f23057797c84325f Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 15 Apr 2013 16:27:31 +0200 Subject: [PATCH 10/42] host: remove unused host_t.get_differences() method --- src/libstrongswan/networking/host.c | 21 --------------------- src/libstrongswan/networking/host.h | 18 ------------------ 2 files changed, 39 deletions(-) diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c index 65e1b7791..c94a1597b 100644 --- a/src/libstrongswan/networking/host.c +++ b/src/libstrongswan/networking/host.c @@ -273,26 +273,6 @@ static bool ip_equals(private_host_t *this, private_host_t *other) return FALSE; } -/** - * Implements host_t.get_differences - */ -static host_diff_t get_differences(host_t *this, host_t *other) -{ - host_diff_t ret = HOST_DIFF_NONE; - - if (!this->ip_equals(this, other)) - { - ret |= HOST_DIFF_ADDR; - } - - if (this->get_port(this) != other->get_port(other)) - { - ret |= HOST_DIFF_PORT; - } - - return ret; -} - /** * Implements host_t.equals */ @@ -341,7 +321,6 @@ static private_host_t *host_create_empty(void) .get_address = _get_address, .get_port = _get_port, .set_port = _set_port, - .get_differences = get_differences, .ip_equals = (bool (*)(host_t *,host_t *))ip_equals, .equals = (bool (*)(host_t *,host_t *)) equals, .is_anyaddr = _is_anyaddr, diff --git a/src/libstrongswan/networking/host.h b/src/libstrongswan/networking/host.h index 25f334779..b36a131d1 100644 --- a/src/libstrongswan/networking/host.h +++ b/src/libstrongswan/networking/host.h @@ -36,16 +36,6 @@ typedef struct host_t host_t; #include -/** - * Differences between two hosts. They differ in - * address, port, or both. - */ -enum host_diff_t { - HOST_DIFF_NONE = 0, - HOST_DIFF_ADDR = 1, - HOST_DIFF_PORT = 2, -}; - /** * Representates a Host * @@ -136,14 +126,6 @@ struct host_t { */ bool (*equals) (host_t *this, host_t *other); - /** - * Compare two hosts and return the differences. - * - * @param other the other to compare - * @return differences in a combination of host_diff_t's - */ - host_diff_t (*get_differences) (host_t *this, host_t *other); - /** * Destroy this host object. */ From 2d8a01d1c60ab84c0f9185bd7c18e8aa9738221c Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 16 Apr 2013 12:08:38 +0200 Subject: [PATCH 11/42] host: add a netmask constructor taking the number of network bits --- src/libstrongswan/networking/host.c | 49 +++++++++++++++++++++++++++++ src/libstrongswan/networking/host.h | 8 +++++ 2 files changed, 57 insertions(+) diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c index c94a1597b..d275a835e 100644 --- a/src/libstrongswan/networking/host.c +++ b/src/libstrongswan/networking/host.c @@ -558,6 +558,55 @@ host_t *host_create_from_subnet(char *string, int *bits) return net; } +/* + * See header. + */ +host_t *host_create_netmask(int family, int netbits) +{ + private_host_t *this; + int bits, bytes, len = 0; + char *target; + + switch (family) + { + case AF_INET: + if (netbits < 0 || netbits > 32) + { + return NULL; + } + this = host_create_empty(); + this->socklen = sizeof(struct sockaddr_in); + target = (char*)&this->address4.sin_addr; + len = 4; + break; + case AF_INET6: + if (netbits < 0 || netbits > 128) + { + return NULL; + } + this = host_create_empty(); + this->socklen = sizeof(struct sockaddr_in6); + target = (char*)&this->address6.sin6_addr; + len = 16; + break; + default: + return NULL; + } + + memset(&this->address_max, 0, sizeof(struct sockaddr_storage)); + this->address.sa_family = family; + update_sa_len(this); + + bytes = (netbits + 7) / 8; + bits = (bytes * 8) - netbits; + + memset(target, 0xff, bytes); + memset(target + bytes, 0x00, len - bytes); + target[bytes - 1] = bits ? (u_int8_t)(0xff << bits) : 0xff; + + return &this->public; +} + /* * Described in header. */ diff --git a/src/libstrongswan/networking/host.h b/src/libstrongswan/networking/host.h index b36a131d1..ae25076ed 100644 --- a/src/libstrongswan/networking/host.h +++ b/src/libstrongswan/networking/host.h @@ -191,6 +191,14 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr); */ host_t *host_create_from_subnet(char *string, int *bits); +/** + * Create a netmask host having the first netbits bits set. + * + * @param netbits number of leading bits set in the host + * @return netmask host + */ +host_t *host_create_netmask(int family, int netbits); + /** * Create a host without an address, a "any" host. * From d947d0d61a595d394b5b35adf3d2a83da11860bb Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 16 Apr 2013 12:09:18 +0200 Subject: [PATCH 12/42] tun-device: use host_create_netmask() to calculate interface netmask --- src/libstrongswan/networking/tun_device.c | 61 +++++------------------ 1 file changed, 12 insertions(+), 49 deletions(-) diff --git a/src/libstrongswan/networking/tun_device.c b/src/libstrongswan/networking/tun_device.c index 1da87df05..691d96c8e 100644 --- a/src/libstrongswan/networking/tun_device.c +++ b/src/libstrongswan/networking/tun_device.c @@ -75,61 +75,16 @@ struct private_tun_device_t { int mtu; }; -/** - * Set the sockaddr_t from the given netmask - */ -static void set_netmask(struct ifreq *ifr, int family, u_int8_t netmask) -{ - int len, bytes, bits; - char *target; - - switch (family) - { - case AF_INET: - { - struct sockaddr_in *addr = (struct sockaddr_in*)&ifr->ifr_addr; - target = (char*)&addr->sin_addr; - len = 4; - break; - } - case AF_INET6: - { - struct sockaddr_in6 *addr = (struct sockaddr_in6*)&ifr->ifr_addr; - target = (char*)&addr->sin6_addr; - len = 16; - break; - } - default: - return; - } - - ifr->ifr_addr.sa_family = family; - - bytes = (netmask + 7) / 8; - bits = (bytes * 8) - netmask; - - memset(target, 0xff, bytes); - memset(target + bytes, 0x00, len - bytes); - target[bytes - 1] = bits ? (u_int8_t)(0xff << bits) : 0xff; -} - METHOD(tun_device_t, set_address, bool, private_tun_device_t *this, host_t *addr, u_int8_t netmask) { struct ifreq ifr; - int family; - - family = addr->get_family(addr); - if ((netmask > 32 && family == AF_INET) || netmask > 128) - { - DBG1(DBG_LIB, "failed to set address on %s: invalid netmask", - this->if_name); - return FALSE; - } + host_t *mask; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); - memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr), sizeof(sockaddr_t)); + memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr), + *addr->get_sockaddr_len(addr)); if (ioctl(this->sock, SIOCSIFADDR, &ifr) < 0) { @@ -146,7 +101,15 @@ METHOD(tun_device_t, set_address, bool, } #endif /* __APPLE__ */ - set_netmask(&ifr, family, netmask); + mask = host_create_netmask(addr->get_family(addr), netmask); + if (!mask) + { + DBG1(DBG_LIB, "invalid netmask: %d", netmask); + return FALSE; + } + memcpy(&ifr.ifr_addr, mask->get_sockaddr(mask), + *mask->get_sockaddr_len(mask)); + mask->destroy(mask); if (ioctl(this->sock, SIOCSIFNETMASK, &ifr) < 0) { From 60babe02364de90737f91c56e16a54692e5cca9c Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 12 Apr 2013 12:42:14 +0200 Subject: [PATCH 13/42] tun_device: add a getter for the underlying file descriptor --- src/libstrongswan/networking/tun_device.c | 7 +++++++ src/libstrongswan/networking/tun_device.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/libstrongswan/networking/tun_device.c b/src/libstrongswan/networking/tun_device.c index 691d96c8e..b0be8688e 100644 --- a/src/libstrongswan/networking/tun_device.c +++ b/src/libstrongswan/networking/tun_device.c @@ -192,6 +192,12 @@ METHOD(tun_device_t, get_name, char*, return this->if_name; } +METHOD(tun_device_t, get_fd, int, + private_tun_device_t *this) +{ + return this->tunfd; +} + METHOD(tun_device_t, write_packet, bool, private_tun_device_t *this, chunk_t packet) { @@ -398,6 +404,7 @@ tun_device_t *tun_device_create(const char *name_tmpl) .get_mtu = _get_mtu, .set_mtu = _set_mtu, .get_name = _get_name, + .get_fd = _get_fd, .set_address = _set_address, .up = _up, .destroy = _destroy, diff --git a/src/libstrongswan/networking/tun_device.h b/src/libstrongswan/networking/tun_device.h index b22a5d170..3bdb02a5d 100644 --- a/src/libstrongswan/networking/tun_device.h +++ b/src/libstrongswan/networking/tun_device.h @@ -94,6 +94,13 @@ struct tun_device_t { */ char *(*get_name)(tun_device_t *this); + /** + * Get the underlying tun file descriptor. + * + * @return file descriptor of this tun device + */ + int (*get_fd)(tun_device_t *this); + /** * Destroy a tun_device_t */ From 2af65b26d90a85ade6406dfc1bed244aae1767dd Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 12 Apr 2013 13:03:21 +0200 Subject: [PATCH 14/42] tun_device: add a getter for the address previously passed to set_address() --- src/libstrongswan/networking/tun_device.c | 24 +++++++++++++++++++++++ src/libstrongswan/networking/tun_device.h | 8 ++++++++ 2 files changed, 32 insertions(+) diff --git a/src/libstrongswan/networking/tun_device.c b/src/libstrongswan/networking/tun_device.c index b0be8688e..2191198e2 100644 --- a/src/libstrongswan/networking/tun_device.c +++ b/src/libstrongswan/networking/tun_device.c @@ -73,6 +73,16 @@ struct private_tun_device_t { * The current MTU */ int mtu; + + /** + * Associated address + */ + host_t *address; + + /** + * Netmask for address + */ + u_int8_t netmask; }; METHOD(tun_device_t, set_address, bool, @@ -117,9 +127,21 @@ METHOD(tun_device_t, set_address, bool, this->if_name, strerror(errno)); return FALSE; } + this->address = addr->clone(addr); + this->netmask = netmask; return TRUE; } +METHOD(tun_device_t, get_address, host_t*, + private_tun_device_t *this, u_int8_t *netmask) +{ + if (netmask && this->address) + { + *netmask = this->netmask; + } + return this->address; +} + METHOD(tun_device_t, up, bool, private_tun_device_t *this) { @@ -277,6 +299,7 @@ METHOD(tun_device_t, destroy, void, { close(this->sock); } + DESTROY_IF(this->address); free(this); } @@ -406,6 +429,7 @@ tun_device_t *tun_device_create(const char *name_tmpl) .get_name = _get_name, .get_fd = _get_fd, .set_address = _set_address, + .get_address = _get_address, .up = _up, .destroy = _destroy, }, diff --git a/src/libstrongswan/networking/tun_device.h b/src/libstrongswan/networking/tun_device.h index 3bdb02a5d..1d330f133 100644 --- a/src/libstrongswan/networking/tun_device.h +++ b/src/libstrongswan/networking/tun_device.h @@ -65,6 +65,14 @@ struct tun_device_t { */ bool (*set_address)(tun_device_t *this, host_t *addr, u_int8_t netmask); + /** + * Get the IP address previously assigned to using set_address(). + * + * @param netmask pointer receiving the configured netmask, or NULL + * @return address previously set, NULL if none + */ + host_t* (*get_address)(tun_device_t *this, u_int8_t *netmask); + /** * Bring the TUN device up * From bc6275d21c1bae70ab02db340dbd5dbbff556a9d Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 12 Apr 2013 11:32:05 +0200 Subject: [PATCH 15/42] kernel-netlink: remove obsolete pluto specific behavior --- .../plugins/kernel_netlink/kernel_netlink_ipsec.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index 9b4ade533..9a2e9f8b4 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -2638,13 +2638,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() this->replay_bmp = (this->replay_window + sizeof(u_int32_t) * 8 - 1) / (sizeof(u_int32_t) * 8); - if (streq(hydra->daemon, "pluto")) - { /* no routes for pluto, they are installed via updown script */ - this->install_routes = FALSE; - /* no policy history for pluto */ - this->policy_history = FALSE; - } - else if (streq(hydra->daemon, "starter")) + if (streq(hydra->daemon, "starter")) { /* starter has no threads, so we do not register for kernel events */ register_for_events = FALSE; } From 64f309e7356b4ab6cf4220c427d1b8b6c5cfea88 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 6 May 2013 15:30:07 +0200 Subject: [PATCH 16/42] kernel-pfkey: remove obsolete pluto specific behavior --- src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 2521af87d..b1320f724 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -2618,11 +2618,7 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() hydra->daemon), ); - if (streq(hydra->daemon, "pluto")) - { /* no routes for pluto, they are installed via updown script */ - this->install_routes = FALSE; - } - else if (streq(hydra->daemon, "starter")) + if (streq(hydra->daemon, "starter")) { /* starter has no threads, so we do not register for kernel events */ register_for_events = FALSE; } From e8002956c996e63830ea1d7026090cd0bd907abb Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 17 Apr 2013 14:48:58 +0200 Subject: [PATCH 17/42] kernel-pfroute: fix length check when receiving PF_ROUTE messages --- src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 7ac3e8a3c..7e26cc37b 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -475,7 +475,7 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) } } - if (len < sizeof(msg->rtm_msglen) || len < msg->rtm_msglen || + if (len < sizeof(*msg) || len < msg->rtm_msglen || msg->rtm_version != RTM_VERSION) { DBG2(DBG_KNL, "received corrupted PF_ROUTE message"); From 0e107f03ac05f3a09f17a4c53c241578443ba830 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Apr 2013 16:08:12 +0200 Subject: [PATCH 18/42] kernel-pfroute: use only a single PF_ROUTE socket for both events and queries --- .../kernel_pfroute/kernel_pfroute_net.c | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 7e26cc37b..84d370bb8 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -206,11 +206,6 @@ struct private_kernel_pfroute_net_t */ int socket; - /** - * PF_ROUTE socket to receive events - */ - int socket_events; - /** * sequence number for messages sent to the kernel */ @@ -455,7 +450,7 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) bool oldstate; oldstate = thread_cancelability(TRUE); - len = recvfrom(this->socket_events, buf, sizeof(buf), 0, NULL, 0); + len = recvfrom(this->socket, buf, sizeof(buf), 0, NULL, 0); thread_cancelability(oldstate); if (len < 0) @@ -760,14 +755,10 @@ METHOD(kernel_net_t, destroy, void, enumerator_t *enumerator; addr_entry_t *addr; - if (this->socket > 0) + if (this->socket != -1) { close(this->socket); } - if (this->socket_events) - { - close(this->socket_events); - } enumerator = this->addrs->create_enumerator(this->addrs); while (enumerator->enumerate(enumerator, NULL, (void**)&addr)) { @@ -787,7 +778,6 @@ METHOD(kernel_net_t, destroy, void, kernel_pfroute_net_t *kernel_pfroute_net_create() { private_kernel_pfroute_net_t *this; - bool register_for_events = TRUE; INIT(this, .public = { @@ -811,37 +801,31 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() .mutex_pfroute = mutex_create(MUTEX_TYPE_DEFAULT), ); - if (streq(hydra->daemon, "starter")) - { /* starter has no threads, so we do not register for kernel events */ - register_for_events = FALSE; - } - /* create a PF_ROUTE socket to communicate with the kernel */ this->socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); - if (this->socket < 0) + if (this->socket == -1) { DBG1(DBG_KNL, "unable to create PF_ROUTE socket"); destroy(this); return NULL; } - if (register_for_events) + if (streq(hydra->daemon, "starter")) { - /* create a PF_ROUTE socket to receive events */ - this->socket_events = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); - if (this->socket_events < 0) + /* starter has no threads, so we do not register for kernel events */ + if (shutdown(this->socket, SHUT_RD) != 0) { - DBG1(DBG_KNL, "unable to create PF_ROUTE event socket"); - destroy(this); - return NULL; + DBG1(DBG_KNL, "closing read end of PF_ROUTE socket failed: %s", + strerror(errno)); } - + } + else + { lib->processor->queue_job(lib->processor, (job_t*)callback_job_create_with_prio( (callback_job_cb_t)receive_events, this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); } - if (init_address_list(this) != SUCCESS) { DBG1(DBG_KNL, "unable to get interface list"); From 9650bf3cc7ee021bb606a06cbf370054208cbd02 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Apr 2013 16:20:11 +0200 Subject: [PATCH 19/42] kernel-pfroute: use INIT() macro for allocations --- .../kernel_pfroute/kernel_pfroute_net.c | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 84d370bb8..164ea2493 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -357,11 +357,11 @@ static void process_addr(private_kernel_pfroute_net_t *this, if (!found && ifa->ifam_type == RTM_NEWADDR) { + INIT(addr, + .ip = host->clone(host), + .refcount = 1, + ); changed = TRUE; - addr = malloc_thing(addr_entry_t); - addr->ip = host->clone(host); - addr->virtual = FALSE; - addr->refcount = 1; iface->addrs->insert_last(iface->addrs, addr); addr_map_entry_add(this, addr, iface); if (iface->usable) @@ -573,9 +573,12 @@ static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, private_kernel_pfroute_net_t *this, kernel_address_type_t which) { - address_enumerator_t *data = malloc_thing(address_enumerator_t); - data->this = this; - data->which = which; + address_enumerator_t *data; + + INIT(data, + .this = this, + .which = which, + ); this->lock->read_lock(this->lock); return enumerator_create_nested( @@ -706,22 +709,23 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this) if (!iface) { - iface = malloc_thing(iface_entry_t); + INIT(iface, + .ifindex = if_nametoindex(ifa->ifa_name), + .flags = ifa->ifa_flags, + .addrs = linked_list_create(), + .usable = hydra->kernel_interface->is_interface_usable( + hydra->kernel_interface, ifa->ifa_name), + ); memcpy(iface->ifname, ifa->ifa_name, IFNAMSIZ); - iface->ifindex = if_nametoindex(ifa->ifa_name); - iface->flags = ifa->ifa_flags; - iface->addrs = linked_list_create(); - iface->usable = hydra->kernel_interface->is_interface_usable( - hydra->kernel_interface, ifa->ifa_name); this->ifaces->insert_last(this->ifaces, iface); } if (ifa->ifa_addr->sa_family != AF_LINK) { - addr = malloc_thing(addr_entry_t); - addr->ip = host_create_from_sockaddr(ifa->ifa_addr); - addr->virtual = FALSE; - addr->refcount = 1; + INIT(addr, + .ip = host_create_from_sockaddr(ifa->ifa_addr), + .refcount = 1, + ); iface->addrs->insert_last(iface->addrs, addr); addr_map_entry_add(this, addr, iface); } From 889efae4cf6d5c0e0e7da29fbaf7d1069cb0433a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Apr 2013 16:53:35 +0200 Subject: [PATCH 20/42] kernel-pfkey: use an int to set esp_port with a sysctl on OS X --- src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index b1320f724..1fd22ab94 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -2548,8 +2548,10 @@ METHOD(kernel_ipsec_t, enable_udp_decap, bool, return FALSE; } #else /* __APPLE__ */ - if (sysctlbyname("net.inet.ipsec.esp_port", NULL, NULL, &port, - sizeof(port)) != 0) + int intport = port; + + if (sysctlbyname("net.inet.ipsec.esp_port", NULL, NULL, &intport, + sizeof(intport)) != 0) { DBG1(DBG_KNL, "could not set net.inet.ipsec.esp_port to %d: %s", port, strerror(errno)); From b1c6b68e4cfda10b0c8b3d42fe7902c210fbe882 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Apr 2013 17:58:48 +0200 Subject: [PATCH 21/42] kernel-pfroute: refactor PF_ROUTE message processing, use an enumerator --- .../kernel_pfroute/kernel_pfroute_net.c | 152 ++++++++++++++---- 1 file changed, 117 insertions(+), 35 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 164ea2493..ac2f6b247 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -40,9 +40,6 @@ /** delay before firing roam events (ms) */ #define ROAM_DELAY 100 -/** buffer size for PF_ROUTE messages */ -#define PFROUTE_BUFFER_SIZE 4096 - typedef struct addr_entry_t addr_entry_t; /** @@ -290,33 +287,91 @@ static void fire_roam_event(private_kernel_pfroute_net_t *this, bool address) } } +/** + * Data for enumerator over rtmsg sockaddrs + */ +typedef struct { + /** implements enumerator */ + enumerator_t public; + /** copy of attribute bitfield */ + int types; + /** bytes remaining in buffer */ + int remaining; + /** next sockaddr to enumerate */ + struct sockaddr *addr; +} rt_enumerator_t; + +METHOD(enumerator_t, rt_enumerate, bool, + rt_enumerator_t *this, int *xtype, struct sockaddr **addr) +{ + int i, type; + + if (this->remaining < sizeof(this->addr->sa_len) || + this->remaining < this->addr->sa_len) + { + return FALSE; + } + for (i = 0; i < RTAX_MAX; i++) + { + type = (1 << i); + if (this->types & type) + { + this->types &= ~type; + *addr = this->addr; + *xtype = i; + this->remaining -= this->addr->sa_len; + this->addr = (void*)this->addr + this->addr->sa_len; + return TRUE; + } + } + return FALSE; +} + +/** + * Create a safe enumerator over sockaddrs in ifa/ifam/rt_msg + */ +static enumerator_t *create_rtmsg_enumerator(void *hdr, size_t hdrlen) +{ + struct rt_msghdr *rthdr = hdr; + rt_enumerator_t *this; + + INIT(this, + .public = { + .enumerate = (void*)_rt_enumerate, + .destroy = (void*)free, + }, + .types = rthdr->rtm_addrs, + .remaining = rthdr->rtm_msglen - hdrlen, + .addr = hdr + hdrlen, + ); + return &this->public; +} + /** * Process an RTM_*ADDR message from the kernel */ static void process_addr(private_kernel_pfroute_net_t *this, - struct rt_msghdr *msg) + struct ifa_msghdr *ifa) { - struct ifa_msghdr *ifa = (struct ifa_msghdr*)msg; - sockaddr_t *sockaddr = (sockaddr_t*)(ifa + 1); + struct sockaddr *sockaddr; host_t *host = NULL; enumerator_t *ifaces, *addrs; iface_entry_t *iface; addr_entry_t *addr; bool found = FALSE, changed = FALSE, roam = FALSE; - int i; + enumerator_t *enumerator; + int type; - for (i = 1; i < (1 << RTAX_MAX); i <<= 1) + enumerator = create_rtmsg_enumerator(ifa, sizeof(*ifa)); + while (enumerator->enumerate(enumerator, &type, &sockaddr)) { - if (ifa->ifam_addrs & i) + if (type == RTAX_IFA) { - if (RTA_IFA & i) - { - host = host_create_from_sockaddr(sockaddr); - break; - } - sockaddr = (sockaddr_t*)((char*)sockaddr + sockaddr->sa_len); + host = host_create_from_sockaddr(sockaddr); + break; } } + enumerator->destroy(enumerator); if (!host) { @@ -391,9 +446,8 @@ static void process_addr(private_kernel_pfroute_net_t *this, * Process an RTM_IFINFO message from the kernel */ static void process_link(private_kernel_pfroute_net_t *this, - struct rt_msghdr *hdr) + struct if_msghdr *msg) { - struct if_msghdr *msg = (struct if_msghdr*)hdr; enumerator_t *enumerator; iface_entry_t *iface; bool roam = FALSE; @@ -440,17 +494,23 @@ static void process_route(private_kernel_pfroute_net_t *this, } /** - * Receives events from kernel + * Receives PF_ROUTE messages from kernel */ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) { - unsigned char buf[PFROUTE_BUFFER_SIZE]; - struct rt_msghdr *msg = (struct rt_msghdr*)buf; - int len; + struct { + union { + struct rt_msghdr rtm; + struct if_msghdr ifm; + struct ifa_msghdr ifam; + }; + char buf[sizeof(struct sockaddr_storage) * RTAX_MAX]; + } msg; + int len, hdrlen; bool oldstate; oldstate = thread_cancelability(TRUE); - len = recvfrom(this->socket, buf, sizeof(buf), 0, NULL, 0); + len = recv(this->socket, &msg, sizeof(msg), 0); thread_cancelability(oldstate); if (len < 0) @@ -458,10 +518,7 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) switch (errno) { case EINTR: - /* interrupted, try again */ - return JOB_REQUEUE_DIRECT; case EAGAIN: - /* no data ready, select again */ return JOB_REQUEUE_DIRECT; default: DBG1(DBG_KNL, "unable to receive from PF_ROUTE event socket"); @@ -470,30 +527,55 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) } } - if (len < sizeof(*msg) || len < msg->rtm_msglen || - msg->rtm_version != RTM_VERSION) + if (len < offsetof(struct rt_msghdr, rtm_flags) || len < msg.rtm.rtm_msglen) { - DBG2(DBG_KNL, "received corrupted PF_ROUTE message"); + DBG1(DBG_KNL, "received invalid PF_ROUTE message"); return JOB_REQUEUE_DIRECT; } - - switch (msg->rtm_type) + if (msg.rtm.rtm_version != RTM_VERSION) + { + DBG1(DBG_KNL, "received PF_ROUTE message with unsupported version: %d", + msg.rtm.rtm_version); + return JOB_REQUEUE_DIRECT; + } + switch (msg.rtm.rtm_type) { case RTM_NEWADDR: case RTM_DELADDR: - process_addr(this, msg); + hdrlen = sizeof(msg.ifam); break; case RTM_IFINFO: - /*case RTM_IFANNOUNCE <- what about this*/ - process_link(this, msg); + hdrlen = sizeof(msg.ifm); break; case RTM_ADD: case RTM_DELETE: - process_route(this, msg); + case RTM_GET: + hdrlen = sizeof(msg.rtm); + break; + default: + return JOB_REQUEUE_DIRECT; + } + if (msg.rtm.rtm_msglen < hdrlen) + { + DBG1(DBG_KNL, "ignoring short PF_ROUTE message"); + return JOB_REQUEUE_DIRECT; + } + switch (msg.rtm.rtm_type) + { + case RTM_NEWADDR: + case RTM_DELADDR: + process_addr(this, &msg.ifam); + break; + case RTM_IFINFO: + process_link(this, &msg.ifm); + break; + case RTM_ADD: + case RTM_DELETE: + process_route(this, &msg.rtm); + break; default: break; } - return JOB_REQUEUE_DIRECT; } From 3a7f4b5c8d6fab411708f5080db345ded76540c6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Apr 2013 18:15:25 +0200 Subject: [PATCH 22/42] kernel-pfroute: collect replies received for our own queries --- .../kernel_pfroute/kernel_pfroute_net.c | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index ac2f6b247..dc1e85e83 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -194,9 +195,19 @@ struct private_kernel_pfroute_net_t hashtable_t *addrs; /** - * mutex to lock access to the PF_ROUTE socket + * mutex to communicate exclusively with PF_KEY */ - mutex_t *mutex_pfroute; + mutex_t *mutex; + + /** + * condvar to signal if PF_KEY query got a response + */ + condvar_t *condvar; + + /** + * pid to send PF_ROUTE messages with + */ + pid_t pid; /** * PF_ROUTE socket to communicate with the kernel @@ -208,6 +219,16 @@ struct private_kernel_pfroute_net_t */ int seq; + /** + * Sequence number a query is waiting for + */ + int waiting_seq; + + /** + * Allocated reply message from kernel + */ + struct rt_msghdr *reply; + /** * time of last roam event */ @@ -576,6 +597,17 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) default: break; } + + this->mutex->lock(this->mutex); + if (msg.rtm.rtm_pid == this->pid && msg.rtm.rtm_seq == this->waiting_seq) + { + /* seems like the message someone is waiting for, deliver */ + this->reply = realloc(this->reply, msg.rtm.rtm_msglen); + memcpy(this->reply, &msg, msg.rtm.rtm_msglen); + this->condvar->signal(this->condvar); + } + this->mutex->unlock(this->mutex); + return JOB_REQUEUE_DIRECT; } @@ -854,7 +886,9 @@ METHOD(kernel_net_t, destroy, void, this->addrs->destroy(this->addrs); this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); this->lock->destroy(this->lock); - this->mutex_pfroute->destroy(this->mutex_pfroute); + this->mutex->destroy(this->mutex); + this->condvar->destroy(this->condvar); + free(this->reply); free(this); } @@ -879,12 +913,14 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() .destroy = _destroy, }, }, + .pid = getpid(), .ifaces = linked_list_create(), .addrs = hashtable_create( (hashtable_hash_t)addr_map_entry_hash, (hashtable_equals_t)addr_map_entry_equals, 16), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), - .mutex_pfroute = mutex_create(MUTEX_TYPE_DEFAULT), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), ); /* create a PF_ROUTE socket to communicate with the kernel */ From 272bcac894648dbfa3d17a6c606fe7a2966919da Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Apr 2013 18:19:24 +0200 Subject: [PATCH 23/42] kernel-pfroute: install and uninstall routes --- .../kernel_pfroute/kernel_pfroute_net.c | 131 +++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index dc1e85e83..caab3150d 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -765,18 +766,144 @@ METHOD(kernel_net_t, del_ip, status_t, return FAILED; } +/** + * Append a sockaddr_in/in6 of given type to routing message + */ +static void add_rt_addr(struct rt_msghdr *hdr, int type, host_t *addr) +{ + if (addr) + { + int len; + + len = *addr->get_sockaddr_len(addr); + memcpy((char*)hdr + hdr->rtm_msglen, addr->get_sockaddr(addr), len); + hdr->rtm_msglen += len; + hdr->rtm_addrs |= type; + } +} + +/** + * Append a subnet mask sockaddr using the given prefix to routing message + */ +static void add_rt_mask(struct rt_msghdr *hdr, int type, int family, int prefix) +{ + host_t *mask; + + mask = host_create_netmask(family, prefix); + if (mask) + { + add_rt_addr(hdr, type, mask); + mask->destroy(mask); + } +} + +/** + * Append an interface name sockaddr_dl to routing message + */ +static void add_rt_ifname(struct rt_msghdr *hdr, int type, char *name) +{ + struct sockaddr_dl sdl = { + .sdl_len = sizeof(struct sockaddr_dl), + .sdl_family = AF_LINK, + .sdl_nlen = strlen(name), + }; + + if (strlen(name) <= sizeof(sdl.sdl_data)) + { + memcpy(sdl.sdl_data, name, sdl.sdl_nlen); + memcpy((char*)hdr + hdr->rtm_msglen, &sdl, sdl.sdl_len); + hdr->rtm_msglen += sdl.sdl_len; + hdr->rtm_addrs |= type; + } +} + +/** + * Add or remove a route + */ +static status_t manage_route(private_kernel_pfroute_net_t *this, int op, + chunk_t dst_net, u_int8_t prefixlen, + host_t *gateway, char *if_name) +{ + struct { + struct rt_msghdr hdr; + char buf[sizeof(struct sockaddr_storage) * RTAX_MAX]; + } msg = { + .hdr = { + .rtm_version = RTM_VERSION, + .rtm_type = op, + .rtm_flags = RTF_UP | RTF_STATIC, + .rtm_pid = this->pid, + .rtm_seq = ++this->seq, + }, + }; + host_t *dst; + int type; + + dst = host_create_from_chunk(AF_UNSPEC, dst_net, 0); + if (!dst) + { + return FAILED; + } + + if ((dst->get_family(dst) == AF_INET && prefixlen == 32) || + (dst->get_family(dst) == AF_INET6 && prefixlen == 128)) + { + msg.hdr.rtm_flags |= RTF_HOST | RTF_GATEWAY; + } + + msg.hdr.rtm_msglen = sizeof(struct rt_msghdr); + for (type = 0; type < RTAX_MAX; type++) + { + switch (type) + { + case RTAX_DST: + add_rt_addr(&msg.hdr, RTA_DST, dst); + break; + case RTAX_NETMASK: + if (!(msg.hdr.rtm_flags & RTF_HOST)) + { + add_rt_mask(&msg.hdr, RTA_NETMASK, + dst->get_family(dst), prefixlen); + } + break; + case RTAX_GATEWAY: + /* interface name seems to replace gateway on OS X */ + if (if_name) + { + add_rt_ifname(&msg.hdr, RTA_GATEWAY, if_name); + } + else if (gateway) + { + add_rt_addr(&msg.hdr, RTA_GATEWAY, gateway); + } + break; + default: + break; + } + } + dst->destroy(dst); + + if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) != msg.hdr.rtm_msglen) + { + DBG1(DBG_KNL, "%s PF_ROUTE route failed: %s", + op == RTM_ADD ? "adding" : "deleting", strerror(errno)); + return FAILED; + } + return SUCCESS; +} + METHOD(kernel_net_t, add_route, status_t, private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) { - return FAILED; + return manage_route(this, RTM_ADD, dst_net, prefixlen, gateway, if_name); } METHOD(kernel_net_t, del_route, status_t, private_kernel_pfroute_net_t *this, chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) { - return FAILED; + return manage_route(this, RTM_DELETE, dst_net, prefixlen, gateway, if_name); } /** From 9bc342eae471831ca328e00a6bf65be1b2608f7a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 18 Apr 2013 18:26:49 +0200 Subject: [PATCH 24/42] kernel-pfroute: implement get_nexthop() --- .../kernel_pfroute/kernel_pfroute_net.c | 79 +++++++++++++++++-- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index caab3150d..face73e6b 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -746,12 +746,6 @@ METHOD(kernel_net_t, get_source_addr, host_t*, return NULL; } -METHOD(kernel_net_t, get_nexthop, host_t*, - private_kernel_pfroute_net_t *this, host_t *dest, host_t *src) -{ - return NULL; -} - METHOD(kernel_net_t, add_ip, status_t, private_kernel_pfroute_net_t *this, host_t *virtual_ip, int prefix, char *iface) @@ -906,6 +900,79 @@ METHOD(kernel_net_t, del_route, status_t, return manage_route(this, RTM_DELETE, dst_net, prefixlen, gateway, if_name); } +METHOD(kernel_net_t, get_nexthop, host_t*, + private_kernel_pfroute_net_t *this, host_t *dest, host_t *src) +{ + struct { + struct rt_msghdr hdr; + char buf[sizeof(struct sockaddr_storage) * RTAX_MAX]; + } msg = { + .hdr = { + .rtm_version = RTM_VERSION, + .rtm_type = RTM_GET, + .rtm_pid = this->pid, + .rtm_seq = ++this->seq, + }, + }; + host_t *hop = NULL; + enumerator_t *enumerator; + struct sockaddr *addr; + int type; + + msg.hdr.rtm_msglen = sizeof(struct rt_msghdr); + for (type = 0; type < RTAX_MAX; type++) + { + switch (type) + { + case RTAX_DST: + add_rt_addr(&msg.hdr, RTA_DST, dest); + break; + case RTAX_IFA: + add_rt_addr(&msg.hdr, RTA_IFA, src); + break; + default: + break; + } + } + this->mutex->lock(this->mutex); + + this->waiting_seq = msg.hdr.rtm_seq; + if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) == msg.hdr.rtm_msglen) + { + while (TRUE) + { + if (this->condvar->timed_wait(this->condvar, this->mutex, 1000)) + { /* timed out? */ + break; + } + if (this->reply->rtm_msglen < sizeof(*this->reply) || + msg.hdr.rtm_seq != this->reply->rtm_seq) + { + continue; + } + enumerator = create_rtmsg_enumerator(this->reply, + sizeof(*this->reply)); + while (enumerator->enumerate(enumerator, &type, &addr)) + { + if (type == RTAX_GATEWAY) + { + hop = host_create_from_sockaddr(addr); + break; + } + } + enumerator->destroy(enumerator); + break; + } + } + else + { + DBG1(DBG_KNL, "PF_ROUTE lookup failed: %s", strerror(errno)); + } + this->mutex->unlock(this->mutex); + + return hop; +} + /** * Initialize a list of local addresses. */ From 0fd409db772cd70972eab7cadd516e705946a882 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 10:46:04 +0200 Subject: [PATCH 25/42] kernel-pfroute: add newly appearing interfaces to the interface cache --- .../kernel_pfroute/kernel_pfroute_net.c | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index face73e6b..0d12a875b 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -472,7 +472,7 @@ static void process_link(private_kernel_pfroute_net_t *this, { enumerator_t *enumerator; iface_entry_t *iface; - bool roam = FALSE; + bool roam = FALSE, found = FALSE;; this->lock->write_lock(this->lock); enumerator = this->ifaces->create_enumerator(this->ifaces); @@ -494,10 +494,31 @@ static void process_link(private_kernel_pfroute_net_t *this, } } iface->flags = msg->ifm_flags; + found = TRUE; break; } } enumerator->destroy(enumerator); + + if (!found) + { + INIT(iface, + .ifindex = msg->ifm_index, + .flags = msg->ifm_flags, + .addrs = linked_list_create(), + ); + if (if_indextoname(iface->ifindex, iface->ifname)) + { + DBG1(DBG_KNL, "interface %s appeared", iface->ifname); + iface->usable = hydra->kernel_interface->is_interface_usable( + hydra->kernel_interface, iface->ifname); + this->ifaces->insert_last(this->ifaces, iface); + } + else + { + free(iface); + } + } this->lock->unlock(this->lock); if (roam) From 6e879a59fc41a65b28037e538b8f093eb8f0ac32 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 10:47:34 +0200 Subject: [PATCH 26/42] kernel-pfroute: rescan address list for an interface if its state changes It seems that we don't get address notifications if the interface is down on OS X. --- .../kernel_pfroute/kernel_pfroute_net.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 0d12a875b..2fda4aebb 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -464,6 +464,47 @@ static void process_addr(private_kernel_pfroute_net_t *this, } } +/** + * Re-initialize address list of an interface if it changes state + */ +static void repopulate_iface(private_kernel_pfroute_net_t *this, + iface_entry_t *iface) +{ + struct ifaddrs *ifap, *ifa; + addr_entry_t *addr; + + while (iface->addrs->remove_last(iface->addrs, (void**)&addr) == SUCCESS) + { + addr_map_entry_remove(addr, iface, this); + addr_entry_destroy(addr); + } + + if (getifaddrs(&ifap) == 0) + { + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr && streq(ifa->ifa_name, iface->ifname)) + { + switch (ifa->ifa_addr->sa_family) + { + case AF_INET: + case AF_INET6: + INIT(addr, + .ip = host_create_from_sockaddr(ifa->ifa_addr), + .refcount = 1, + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this, addr, iface); + break; + default: + break; + } + } + } + freeifaddrs(ifap); + } +} + /** * Process an RTM_IFINFO message from the kernel */ @@ -494,6 +535,7 @@ static void process_link(private_kernel_pfroute_net_t *this, } } iface->flags = msg->ifm_flags; + repopulate_iface(this, iface); found = TRUE; break; } @@ -512,6 +554,7 @@ static void process_link(private_kernel_pfroute_net_t *this, DBG1(DBG_KNL, "interface %s appeared", iface->ifname); iface->usable = hydra->kernel_interface->is_interface_usable( hydra->kernel_interface, iface->ifname); + repopulate_iface(this, iface); this->ifaces->insert_last(this->ifaces, iface); } else From d4260c5f7ffeb2ba4fd1467b7c34463150eed67d Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 10:42:23 +0200 Subject: [PATCH 27/42] kernel-pfkey: install route along with input, not forward policies As forwarding policies are not available on all systems (OS X), using the forward policy to attach the route is a bad pick. Using input policies allows OS X to install routes. --- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 1fd22ab94..ca7199e66 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -339,7 +339,7 @@ static void ipsec_sa_destroy(private_kernel_pfkey_ipsec_t *this, } typedef struct policy_sa_t policy_sa_t; -typedef struct policy_sa_fwd_t policy_sa_fwd_t; +typedef struct policy_sa_in_t policy_sa_in_t; /** * Mapping between a policy and an IPsec SA. @@ -356,10 +356,10 @@ struct policy_sa_t { }; /** - * For forward policies we also cache the traffic selectors in order to install + * For input policies we also cache the traffic selectors in order to install * the route. */ -struct policy_sa_fwd_t { +struct policy_sa_in_t { /** Generic interface */ policy_sa_t generic; @@ -371,7 +371,7 @@ struct policy_sa_fwd_t { }; /** - * Create a policy_sa(_fwd)_t object + * Create a policy_sa(_in)_t object */ static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this, policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst, @@ -379,14 +379,14 @@ static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this, { policy_sa_t *policy; - if (dir == POLICY_FWD) + if (dir == POLICY_IN) { - policy_sa_fwd_t *fwd; - INIT(fwd, + policy_sa_in_t *in; + INIT(in, .src_ts = src_ts->clone(src_ts), .dst_ts = dst_ts->clone(dst_ts), ); - policy = &fwd->generic; + policy = &in->generic; } else { @@ -398,16 +398,16 @@ static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this, } /** - * Destroy a policy_sa(_fwd)_t object + * Destroy a policy_sa(_in)_t object */ static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir, private_kernel_pfkey_ipsec_t *this) { - if (*dir == POLICY_FWD) + if (*dir == POLICY_IN) { - policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)policy; - fwd->src_ts->destroy(fwd->src_ts); - fwd->dst_ts->destroy(fwd->dst_ts); + policy_sa_in_t *in = (policy_sa_in_t*)policy; + in->src_ts->destroy(in->src_ts); + in->dst_ts->destroy(in->dst_ts); } ipsec_sa_destroy(this, policy->sa); free(policy); @@ -2027,10 +2027,10 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, * - we are in tunnel mode * - routing is not disabled via strongswan.conf */ - if (policy->direction == POLICY_FWD && + if (policy->direction == POLICY_IN && ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) { - policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; + policy_sa_in_t *in = (policy_sa_in_t*)mapping; route_entry_t *route; INIT(route, @@ -2038,9 +2038,9 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, ); if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - fwd->dst_ts, &route->src_ip) == SUCCESS) + in->dst_ts, &route->src_ip) == SUCCESS) { - /* get the nexthop to src (src as we are in POLICY_FWD).*/ + /* get the nexthop to src (src as we are in POLICY_IN).*/ route->gateway = hydra->kernel_interface->get_nexthop( hydra->kernel_interface, ipsec->src, ipsec->dst); @@ -2071,8 +2071,8 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, old->src_ip, old->if_name) != SUCCESS) { DBG1(DBG_KNL, "error uninstalling route installed with " - "policy %R === %R %N", fwd->src_ts, - fwd->dst_ts, policy_dir_names, + "policy %R === %R %N", in->src_ts, + in->dst_ts, policy_dir_names, policy->direction); } route_entry_destroy(old); @@ -2080,7 +2080,7 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, } DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", - fwd->src_ts, route->gateway, route->src_ip, route->if_name); + in->src_ts, route->gateway, route->src_ip, route->if_name); switch (hydra->kernel_interface->add_route( hydra->kernel_interface, route->dst_net, route->prefixlen, route->gateway, From f8646dd65ec1265263dc03d82f962c925509fcf7 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 12:16:12 +0200 Subject: [PATCH 28/42] kernel-pfkey: check if we have a gateway before comparing them --- src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index ca7199e66..a562e9bdd 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -251,6 +251,7 @@ static bool route_entry_equals(route_entry_t *a, route_entry_t *b) { return a->if_name && b->if_name && streq(a->if_name, b->if_name) && a->src_ip->ip_equals(a->src_ip, b->src_ip) && + a->gateway && b->gateway && a->gateway->ip_equals(a->gateway, b->gateway) && chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; } From 121783035cbc35c715beff92e28aa6d86d4cab70 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 12:17:25 +0200 Subject: [PATCH 29/42] kernel-pfroute: split /0 routes to avoid conflict with default route --- .../plugins/kernel_pfroute/kernel_pfroute_net.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 2fda4aebb..f8d3f2c44 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -897,6 +897,21 @@ static status_t manage_route(private_kernel_pfroute_net_t *this, int op, host_t *dst; int type; + if (prefixlen == 0 && dst_net.len) + { + status_t status; + chunk_t half; + + half = chunk_clonea(dst_net); + half.ptr[0] |= 0x80; + prefixlen = 1; + status = manage_route(this, op, half, prefixlen, gateway, if_name); + if (status != SUCCESS) + { + return status; + } + } + dst = host_create_from_chunk(AF_UNSPEC, dst_net, 0); if (!dst) { From 5f7f8c92cab171a0bfe20dfcea076642bedd52b0 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 14:17:22 +0200 Subject: [PATCH 30/42] kernel-pfkey: refactor route installation to a dedicate function --- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 155 +++++++++--------- 1 file changed, 81 insertions(+), 74 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index a562e9bdd..650e16ff1 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1915,6 +1915,86 @@ METHOD(kernel_ipsec_t, flush_sas, status_t, return SUCCESS; } +/** + * Try to install a route to the given inbound policy + */ +static bool install_route(private_kernel_pfkey_ipsec_t *this, + policy_entry_t *policy, policy_sa_in_t *in) +{ + route_entry_t *route, *old; + host_t *host, *src, *dst; + + if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, + in->dst_ts, &host) != SUCCESS) + { + return FALSE; + } + + /* switch src/dst, as we handle an IN policy */ + src = in->generic.sa->dst; + dst = in->generic.sa->src; + + INIT(route, + .prefixlen = policy->src.mask, + .src_ip = host, + .gateway = hydra->kernel_interface->get_nexthop( + hydra->kernel_interface, dst, src), + .dst_net = chunk_clone(policy->src.net->get_address(policy->src.net)), + ); + + /* get interface for route, using source address */ + if (!hydra->kernel_interface->get_interface(hydra->kernel_interface, + src, &route->if_name)) + { + route_entry_destroy(route); + return FALSE; + } + + if (policy->route) + { + old = policy->route; + + if (route_entry_equals(old, route)) + { /* such a route already exists */ + route_entry_destroy(route); + return TRUE; + } + /* uninstall previously installed route */ + if (hydra->kernel_interface->del_route(hydra->kernel_interface, + old->dst_net, old->prefixlen, old->gateway, + old->src_ip, old->if_name) != SUCCESS) + { + DBG1(DBG_KNL, "error uninstalling route installed with policy " + "%R === %R %N", in->src_ts, in->dst_ts, + policy_dir_names, policy->direction); + } + route_entry_destroy(old); + policy->route = NULL; + } + + DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", + in->src_ts, route->gateway, route->src_ip, route->if_name); + + switch (hydra->kernel_interface->add_route(hydra->kernel_interface, + route->dst_net, route->prefixlen, route->gateway, + route->src_ip, route->if_name)) + { + case ALREADY_DONE: + /* route exists, do not uninstall */ + route_entry_destroy(route); + return TRUE; + case SUCCESS: + /* cache the installed route */ + policy->route = route; + return TRUE; + default: + DBG1(DBG_KNL, "installing route failed: %R via %H src %H dev %s", + in->src_ts, route->gateway, route->src_ip, route->if_name); + route_entry_destroy(route); + return FALSE; + } +} + /** * Add or update a policy in the kernel. * @@ -2031,80 +2111,7 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, if (policy->direction == POLICY_IN && ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) { - policy_sa_in_t *in = (policy_sa_in_t*)mapping; - route_entry_t *route; - - INIT(route, - .prefixlen = policy->src.mask, - ); - - if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - in->dst_ts, &route->src_ip) == SUCCESS) - { - /* get the nexthop to src (src as we are in POLICY_IN).*/ - route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, ipsec->src, - ipsec->dst); - route->dst_net = chunk_clone(policy->src.net->get_address( - policy->src.net)); - - /* install route via outgoing interface */ - if (!hydra->kernel_interface->get_interface(hydra->kernel_interface, - ipsec->dst, &route->if_name)) - { - this->mutex->unlock(this->mutex); - route_entry_destroy(route); - return SUCCESS; - } - - if (policy->route) - { - route_entry_t *old = policy->route; - if (route_entry_equals(old, route)) - { - this->mutex->unlock(this->mutex); - route_entry_destroy(route); - return SUCCESS; - } - /* uninstall previously installed route */ - if (hydra->kernel_interface->del_route(hydra->kernel_interface, - old->dst_net, old->prefixlen, old->gateway, - old->src_ip, old->if_name) != SUCCESS) - { - DBG1(DBG_KNL, "error uninstalling route installed with " - "policy %R === %R %N", in->src_ts, - in->dst_ts, policy_dir_names, - policy->direction); - } - route_entry_destroy(old); - policy->route = NULL; - } - - DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", - in->src_ts, route->gateway, route->src_ip, route->if_name); - switch (hydra->kernel_interface->add_route( - hydra->kernel_interface, route->dst_net, - route->prefixlen, route->gateway, - route->src_ip, route->if_name)) - { - default: - DBG1(DBG_KNL, "unable to install source route for %H", - route->src_ip); - /* FALL */ - case ALREADY_DONE: - /* route exists, do not uninstall */ - route_entry_destroy(route); - break; - case SUCCESS: - /* cache the installed route */ - policy->route = route; - break; - } - } - else - { - free(route); - } + install_route(this, policy, (policy_sa_in_t*)mapping); } this->mutex->unlock(this->mutex); return SUCCESS; From 1a2a8bffedd633dbfd8ed7fe6ff28218cd535497 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 14:52:29 +0200 Subject: [PATCH 31/42] kernel-interface: support enumeration of virtual-only IPs --- src/libhydra/kernel/kernel_net.h | 12 ++++++------ .../plugins/kernel_netlink/kernel_netlink_net.c | 13 ++++++++++--- .../plugins/kernel_pfroute/kernel_pfroute_net.c | 4 ++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/libhydra/kernel/kernel_net.h b/src/libhydra/kernel/kernel_net.h index 0d3417f1d..165036231 100644 --- a/src/libhydra/kernel/kernel_net.h +++ b/src/libhydra/kernel/kernel_net.h @@ -35,17 +35,17 @@ typedef enum kernel_address_type_t kernel_address_type_t; */ enum kernel_address_type_t { /** normal addresses (on regular, up, non-ignored) interfaces */ - ADDR_TYPE_REGULAR = 0, + ADDR_TYPE_REGULAR = (1 << 0), /** addresses on down interfaces */ - ADDR_TYPE_DOWN = (1 << 0), + ADDR_TYPE_DOWN = (1 << 1), /** addresses on ignored interfaces */ - ADDR_TYPE_IGNORED = (1 << 1), + ADDR_TYPE_IGNORED = (1 << 2), /** addresses on loopback interfaces */ - ADDR_TYPE_LOOPBACK = (1 << 2), + ADDR_TYPE_LOOPBACK = (1 << 3), /** virtual IP addresses */ - ADDR_TYPE_VIRTUAL = (1 << 3), + ADDR_TYPE_VIRTUAL = (1 << 4), /** to enumerate all available addresses */ - ADDR_TYPE_ALL = (1 << 4) - 1, + ADDR_TYPE_ALL = (1 << 5) - 1, }; /** diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index 739a1f407..d4cb4e1ac 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -1165,6 +1165,10 @@ static bool filter_addresses(address_enumerator_t *data, { /* skip virtual interfaces added by us */ return FALSE; } + if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->refcount) + { /* address is regular, but not requested */ + return FALSE; + } if ((*in)->scope >= RT_SCOPE_LINK) { /* skip addresses with a unusable scope */ return FALSE; @@ -1209,9 +1213,12 @@ static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, private_kernel_netlink_net_t *this, kernel_address_type_t which) { - address_enumerator_t *data = malloc_thing(address_enumerator_t); - data->this = this; - data->which = which; + address_enumerator_t *data; + + INIT(data, + .this = this, + .which = which, + ); this->lock->read_lock(this->lock); return enumerator_create_nested( diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index f8d3f2c44..2aeb840b2 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -704,6 +704,10 @@ static bool filter_addresses(address_enumerator_t *data, { /* skip virtual interfaces added by us */ return FALSE; } + if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->virtual) + { /* address is regular, but not requested */ + return FALSE; + } ip = (*in)->ip; if (ip->get_family(ip) == AF_INET6) { From f52cf07532dded002c073db69c11e0c891e27bf5 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 14:22:45 +0200 Subject: [PATCH 32/42] kernel-interface: get_address_by_ts() can tell if a returned IP is virtual --- src/libhydra/kernel/kernel_interface.c | 28 +++++++++++++++++-- src/libhydra/kernel/kernel_interface.h | 3 +- .../plugins/kernel_klips/kernel_klips_ipsec.c | 2 +- .../kernel_netlink/kernel_netlink_ipsec.c | 2 +- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 2 +- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 53b8324b7..290c25a62 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -415,7 +415,8 @@ METHOD(kernel_interface_t, all_interfaces_usable, bool, } METHOD(kernel_interface_t, get_address_by_ts, status_t, - private_kernel_interface_t *this, traffic_selector_t *ts, host_t **ip) + private_kernel_interface_t *this, traffic_selector_t *ts, + host_t **ip, bool *vip) { enumerator_t *addrs; host_t *host; @@ -446,18 +447,41 @@ METHOD(kernel_interface_t, get_address_by_ts, status_t, } host->destroy(host); - addrs = create_address_enumerator(this, ADDR_TYPE_ALL); + addrs = create_address_enumerator(this, ADDR_TYPE_VIRTUAL); while (addrs->enumerate(addrs, (void**)&host)) { if (ts->includes(ts, host)) { found = TRUE; *ip = host->clone(host); + if (vip) + { + *vip = TRUE; + } break; } } addrs->destroy(addrs); + if (!found) + { + addrs = create_address_enumerator(this, ADDR_TYPE_REGULAR); + while (addrs->enumerate(addrs, (void**)&host)) + { + if (ts->includes(ts, host)) + { + found = TRUE; + *ip = host->clone(host); + if (vip) + { + *vip = FALSE; + } + break; + } + } + addrs->destroy(addrs); + } + if (!found) { DBG2(DBG_KNL, "no local address found in traffic selector %R", ts); diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index 1d2253b94..f48104322 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -451,10 +451,11 @@ struct kernel_interface_t { * * @param ts traffic selector * @param ip returned IP address (has to be destroyed) + * @param vip set to TRUE if returned address is a virtual IP * @return SUCCESS if address found */ status_t (*get_address_by_ts)(kernel_interface_t *this, - traffic_selector_t *ts, host_t **ip); + traffic_selector_t *ts, host_t **ip, bool *vip); /** * Register an ipsec kernel interface constructor on the manager. diff --git a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c index a120b3d00..2d09d33cc 100644 --- a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c +++ b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c @@ -2118,7 +2118,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, this->install_routes) { hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - src_ts, &route->src_ip); + src_ts, &route->src_ip, NULL); } if (!route->src_ip) diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index 9a2e9f8b4..b30c9533f 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -2102,7 +2102,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, ); if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - fwd->dst_ts, &route->src_ip) == SUCCESS) + fwd->dst_ts, &route->src_ip, NULL) == SUCCESS) { /* get the nexthop to src (src as we are in POLICY_FWD) */ route->gateway = hydra->kernel_interface->get_nexthop( diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 650e16ff1..5d059cf2b 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1925,7 +1925,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, host_t *host, *src, *dst; if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - in->dst_ts, &host) != SUCCESS) + in->dst_ts, &host, NULL) != SUCCESS) { return FALSE; } From ca4a14ae83a1b00b792530dc328eae164d3f66e6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 14:27:31 +0200 Subject: [PATCH 33/42] kernel-pfkey: when installing a route for a virtual IP, use its interface When installing a route over a tun device for a virtual IP, the route must be set over the tun, not the IKE interface. --- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 5d059cf2b..1aa7a6a99 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1923,9 +1923,10 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, { route_entry_t *route, *old; host_t *host, *src, *dst; + bool is_virtual; if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, - in->dst_ts, &host, NULL) != SUCCESS) + in->dst_ts, &host, &is_virtual) != SUCCESS) { return FALSE; } @@ -1942,6 +1943,14 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, .dst_net = chunk_clone(policy->src.net->get_address(policy->src.net)), ); + /* if the IP is virtual, we install the route over the interface it has + * been installed on. Otherwise we use the interface we use for IKE, as + * this is required for example on Linux. */ + if (is_virtual) + { + src = route->src_ip; + } + /* get interface for route, using source address */ if (!hydra->kernel_interface->get_interface(hydra->kernel_interface, src, &route->if_name)) From 2a2d7a4dc8d11cc5695ed6c0f25bc20830282eaf Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 15:53:45 +0200 Subject: [PATCH 34/42] kernel-pfroute: install virtual IPs using dedicated tun devices --- .../kernel_pfroute/kernel_pfroute_net.c | 96 ++++++++++++++++++- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 2aeb840b2..44f31080d 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,11 @@ struct private_kernel_pfroute_net_t */ hashtable_t *addrs; + /** + * List of tun devices we installed for virtual IPs + */ + linked_list_t *tuns; + /** * mutex to communicate exclusively with PF_KEY */ @@ -669,8 +675,9 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) /* seems like the message someone is waiting for, deliver */ this->reply = realloc(this->reply, msg.rtm.rtm_msglen); memcpy(this->reply, &msg, msg.rtm.rtm_msglen); - this->condvar->signal(this->condvar); } + /* signal on any event, add_ip()/del_ip() might wait for it */ + this->condvar->signal(this->condvar); this->mutex->unlock(this->mutex); return JOB_REQUEUE_DIRECT; @@ -815,17 +822,94 @@ METHOD(kernel_net_t, get_source_addr, host_t*, } METHOD(kernel_net_t, add_ip, status_t, - private_kernel_pfroute_net_t *this, host_t *virtual_ip, int prefix, + private_kernel_pfroute_net_t *this, host_t *vip, int prefix, char *iface) { - return FAILED; + tun_device_t *tun; + bool timeout = FALSE; + + tun = tun_device_create(NULL); + if (!tun) + { + return FAILED; + } + if (prefix == -1) + { + prefix = vip->get_address(vip).len * 8; + } + if (!tun->set_address(tun, vip, prefix) || !tun->up(tun)) + { + tun->destroy(tun); + return FAILED; + } + + /* wait until address appears */ + this->mutex->lock(this->mutex); + while (!timeout && !get_interface_name(this, vip, NULL)) + { + timeout = this->condvar->timed_wait(this->condvar, this->mutex, 1000); + } + this->mutex->unlock(this->mutex); + if (timeout) + { + DBG1(DBG_KNL, "virtual IP %H did not appear on %s", + vip, tun->get_name(tun)); + tun->destroy(tun); + return FAILED; + } + + this->lock->write_lock(this->lock); + this->tuns->insert_last(this->tuns, tun); + this->lock->unlock(this->lock); + + return SUCCESS; } METHOD(kernel_net_t, del_ip, status_t, - private_kernel_pfroute_net_t *this, host_t *virtual_ip, int prefix, + private_kernel_pfroute_net_t *this, host_t *vip, int prefix, bool wait) { - return FAILED; + enumerator_t *enumerator; + tun_device_t *tun; + host_t *addr; + bool timeout = FALSE, found = FALSE; + + this->lock->write_lock(this->lock); + enumerator = this->tuns->create_enumerator(this->tuns); + while (enumerator->enumerate(enumerator, &tun)) + { + addr = tun->get_address(tun, NULL); + if (addr && addr->ip_equals(addr, vip)) + { + this->tuns->remove_at(this->tuns, enumerator); + tun->destroy(tun); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + if (!found) + { + return NOT_FOUND; + } + /* wait until address disappears */ + if (wait) + { + this->mutex->lock(this->mutex); + while (!timeout && get_interface_name(this, vip, NULL)) + { + timeout = this->condvar->timed_wait(this->condvar, this->mutex, 1000); + } + this->mutex->unlock(this->mutex); + if (timeout) + { + DBG1(DBG_KNL, "virtual IP %H did not disappear from tun", vip); + return FAILED; + } + } + return SUCCESS; } /** @@ -1162,6 +1246,7 @@ METHOD(kernel_net_t, destroy, void, enumerator->destroy(enumerator); this->addrs->destroy(this->addrs); this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); + this->tuns->destroy(this->tuns); this->lock->destroy(this->lock); this->mutex->destroy(this->mutex); this->condvar->destroy(this->condvar); @@ -1195,6 +1280,7 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() .addrs = hashtable_create( (hashtable_hash_t)addr_map_entry_hash, (hashtable_equals_t)addr_map_entry_equals, 16), + .tuns = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), From 77b6f19694e4705db56e6ee18c4e22a16f4910f7 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 16:55:38 +0200 Subject: [PATCH 35/42] kernel-pfroute: mark IPs installed on tun device as virtual --- .../kernel_pfroute/kernel_pfroute_net.c | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 44f31080d..62664023a 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -823,8 +823,11 @@ METHOD(kernel_net_t, get_source_addr, host_t*, METHOD(kernel_net_t, add_ip, status_t, private_kernel_pfroute_net_t *this, host_t *vip, int prefix, - char *iface) + char *ifname) { + enumerator_t *ifaces, *addrs; + iface_entry_t *iface; + addr_entry_t *addr; tun_device_t *tun; bool timeout = FALSE; @@ -860,6 +863,26 @@ METHOD(kernel_net_t, add_ip, status_t, this->lock->write_lock(this->lock); this->tuns->insert_last(this->tuns, tun); + + ifaces = this->ifaces->create_enumerator(this->ifaces); + while (ifaces->enumerate(ifaces, &iface)) + { + if (streq(iface->ifname, tun->get_name(tun))) + { + addrs = iface->addrs->create_enumerator(iface->addrs); + while (addrs->enumerate(addrs, &addr)) + { + if (addr->ip->ip_equals(addr->ip, vip)) + { + addr->virtual = TRUE; + addr->refcount = 1; + } + } + addrs->destroy(addrs); + } + } + ifaces->destroy(ifaces); + this->lock->unlock(this->lock); return SUCCESS; From bd520193a486c6192494f70920702477b843d72e Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Apr 2013 16:58:06 +0200 Subject: [PATCH 36/42] kernel-pfroute: remove unused interface address refcounting --- .../plugins/kernel_pfroute/kernel_pfroute_net.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 62664023a..b1d3fd88b 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -55,9 +55,6 @@ struct addr_entry_t { /** virtual IP managed by us */ bool virtual; - - /** Number of times this IP is used, if virtual */ - u_int refcount; }; /** @@ -430,10 +427,6 @@ static void process_addr(private_kernel_pfroute_net_t *this, addr_map_entry_remove(addr, iface, this); addr_entry_destroy(addr); } - else if (ifa->ifam_type == RTM_NEWADDR && addr->virtual) - { - addr->refcount = 1; - } } } addrs->destroy(addrs); @@ -442,7 +435,6 @@ static void process_addr(private_kernel_pfroute_net_t *this, { INIT(addr, .ip = host->clone(host), - .refcount = 1, ); changed = TRUE; iface->addrs->insert_last(iface->addrs, addr); @@ -497,7 +489,6 @@ static void repopulate_iface(private_kernel_pfroute_net_t *this, case AF_INET6: INIT(addr, .ip = host_create_from_sockaddr(ifa->ifa_addr), - .refcount = 1, ); iface->addrs->insert_last(iface->addrs, addr); addr_map_entry_add(this, addr, iface); @@ -875,7 +866,6 @@ METHOD(kernel_net_t, add_ip, status_t, if (addr->ip->ip_equals(addr->ip, vip)) { addr->virtual = TRUE; - addr->refcount = 1; } } addrs->destroy(addrs); @@ -1222,7 +1212,6 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this) { INIT(addr, .ip = host_create_from_sockaddr(ifa->ifa_addr), - .refcount = 1, ); iface->addrs->insert_last(iface->addrs, addr); addr_map_entry_add(this, addr, iface); From 580b768d03c10f7ce12ebcb4168e58d752b5e0ab Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Sat, 20 Apr 2013 12:28:05 +0200 Subject: [PATCH 37/42] kernel-pfroute: add a feature flag requesting "exclude" routes If routes installed along with policies covering the peer address affect local IKE/ESP packets, they won't get routed correctly. To work around this issue, the kernel interface can install "exclude" routes for the IKE peer. Not all networking backends require this workaround, hence we export a flag for it if it is required. --- src/libhydra/kernel/kernel_interface.h | 2 ++ src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index f48104322..fd64f50c2 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -65,6 +65,8 @@ typedef enum kernel_feature_t kernel_feature_t; enum kernel_feature_t { /** IPsec can process ESPv3 (RFC 4303) TFC padded packets */ KERNEL_ESP_V3_TFC = (1<<0), + /** Networking requires an "exclude" route for IKE/ESP packets */ + KERNEL_REQUIRE_EXCLUDE_ROUTE = (1<<1), }; /** diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index b1d3fd88b..8d8d0362a 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -770,6 +770,12 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, (void*)address_enumerator_destroy); } +METHOD(kernel_net_t, get_features, kernel_feature_t, + private_kernel_pfroute_net_t *this) +{ + return KERNEL_REQUIRE_EXCLUDE_ROUTE; +} + METHOD(kernel_net_t, get_interface_name, bool, private_kernel_pfroute_net_t *this, host_t* ip, char **name) { @@ -1276,6 +1282,7 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() INIT(this, .public = { .interface = { + .get_features = _get_features, .get_interface = _get_interface_name, .create_address_enumerator = _create_address_enumerator, .get_source_addr = _get_source_addr, From df919d50d01b0572a9c33896cc5d2a43ec839635 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Sat, 20 Apr 2013 13:29:20 +0200 Subject: [PATCH 38/42] kernel-pfkey: install exclude routes if kernel-net requires them --- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 1aa7a6a99..eb2943e5e 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -179,6 +179,11 @@ struct private_kernel_pfkey_ipsec_t */ linked_list_t *policies; + /** + * List of exclude routes (exclude_route_t) + */ + linked_list_t *excludes; + /** * Hash table of IPsec SAs using policies (ipsec_sa_t) */ @@ -210,6 +215,33 @@ struct private_kernel_pfkey_ipsec_t int seq; }; +typedef struct exclude_route_t exclude_route_t; + +/** + * Exclude route definition + */ +struct exclude_route_t { + /** destination address of exclude */ + host_t *dst; + /** source address for route */ + host_t *src; + /** nexthop exclude has been installed */ + host_t *gtw; + /** references to this route */ + int refs; +}; + +/** + * clean up a route exclude entry + */ +static void exclude_route_destroy(exclude_route_t *this) +{ + this->dst->destroy(this->dst); + this->src->destroy(this->src); + this->gtw->destroy(this->gtw); + free(this); +} + typedef struct route_entry_t route_entry_t; /** @@ -230,6 +262,9 @@ struct route_entry_t { /** destination net prefixlen */ u_int8_t prefixlen; + + /** reference to exclude route, if any */ + exclude_route_t *exclude; }; /** @@ -1915,6 +1950,108 @@ METHOD(kernel_ipsec_t, flush_sas, status_t, return SUCCESS; } +/** + * Add an explicit exclude route to a routing entry + */ +static void add_exclude_route(private_kernel_pfkey_ipsec_t *this, + route_entry_t *route, host_t *src, host_t *dst) +{ + enumerator_t *enumerator; + exclude_route_t *exclude; + host_t *gtw; + + enumerator = this->excludes->create_enumerator(this->excludes); + while (enumerator->enumerate(enumerator, &exclude)) + { + if (dst->ip_equals(dst, exclude->dst)) + { + route->exclude = exclude; + exclude->refs++; + } + } + enumerator->destroy(enumerator); + + if (!route->exclude) + { + DBG2(DBG_KNL, "installing new exclude route for %H src %H", dst, src); + gtw = hydra->kernel_interface->get_nexthop(hydra->kernel_interface, + dst, NULL); + if (gtw) + { + if (hydra->kernel_interface->add_route(hydra->kernel_interface, + dst->get_address(dst), + dst->get_family(dst) == AF_INET ? 32 : 128, + gtw, src, NULL) == SUCCESS) + { + INIT(exclude, + .dst = dst->clone(dst), + .src = src->clone(src), + .gtw = gtw->clone(gtw), + .refs = 1, + ); + route->exclude = exclude; + this->excludes->insert_last(this->excludes, exclude); + } + else + { + DBG1(DBG_KNL, "installing exclude route for %H failed", dst); + } + gtw->destroy(gtw); + } + else + { + DBG1(DBG_KNL, "gateway lookup for for %H failed", dst); + } + } +} + +/** + * Remove an exclude route attached to a routing entry + */ +static void remove_exclude_route(private_kernel_pfkey_ipsec_t *this, + route_entry_t *route) +{ + if (route->exclude) + { + enumerator_t *enumerator; + exclude_route_t *exclude; + bool removed = FALSE; + host_t *dst; + + enumerator = this->excludes->create_enumerator(this->excludes); + while (enumerator->enumerate(enumerator, &exclude)) + { + if (route->exclude == exclude) + { + if (--exclude->refs == 0) + { + this->excludes->remove_at(this->excludes, enumerator); + removed = TRUE; + break; + } + } + } + enumerator->destroy(enumerator); + + if (removed) + { + dst = route->exclude->dst; + DBG2(DBG_KNL, "uninstalling exclude route for %H src %H", + dst, route->exclude->src); + if (hydra->kernel_interface->del_route(hydra->kernel_interface, + dst->get_address(dst), + dst->get_family(dst) == AF_INET ? 32 : 128, + route->exclude->gtw, route->exclude->src, + NULL) != SUCCESS) + { + DBG1(DBG_KNL, "uninstalling exclude route for %H failed", dst); + } + exclude_route_destroy(route->exclude); + } + route->exclude = NULL; + } +} + /** * Try to install a route to the given inbound policy */ @@ -1981,6 +2118,16 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, policy->route = NULL; } + /* if remote traffic selector covers the IKE peer, add an exclude route */ + if (hydra->kernel_interface->get_features( + hydra->kernel_interface) & KERNEL_REQUIRE_EXCLUDE_ROUTE) + { + if (in->src_ts->includes(in->src_ts, dst)) + { + add_exclude_route(this, route, in->generic.sa->dst, dst); + } + } + DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s", in->src_ts, route->gateway, route->src_ip, route->if_name); @@ -1990,6 +2137,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, { case ALREADY_DONE: /* route exists, do not uninstall */ + remove_exclude_route(this, route); route_entry_destroy(route); return TRUE; case SUCCESS: @@ -1999,6 +2147,7 @@ static bool install_route(private_kernel_pfkey_ipsec_t *this, default: DBG1(DBG_KNL, "installing route failed: %R via %H src %H dev %s", in->src_ts, route->gateway, route->src_ip, route->if_name); + remove_exclude_route(this, route); route_entry_destroy(route); return FALSE; } @@ -2415,6 +2564,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, "policy %R === %R %N", src_ts, dst_ts, policy_dir_names, direction); } + remove_exclude_route(this, route); } this->policies->remove(this->policies, found, NULL); @@ -2594,6 +2744,7 @@ METHOD(kernel_ipsec_t, destroy, void, (linked_list_invoke_t)policy_entry_destroy, this); this->policies->destroy(this->policies); + this->excludes->destroy(this->excludes); this->sas->destroy(this->sas); this->mutex->destroy(this->mutex); this->mutex_pfkey->destroy(this->mutex_pfkey); @@ -2628,6 +2779,7 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() }, }, .policies = linked_list_create(), + .excludes = linked_list_create(), .sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash, (hashtable_equals_t)ipsec_sa_equals, 32), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), From 470aad7e0cbeb7d927161ba425715173b521afec Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Sat, 20 Apr 2013 20:54:03 +0200 Subject: [PATCH 39/42] kernel-pfkey: be less verbose about unexpected sequence numbers --- src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index eb2943e5e..3ade6f4a5 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1126,7 +1126,7 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket } if (msg->sadb_msg_seq != this->seq) { - DBG1(DBG_KNL, "received PF_KEY message with unexpected sequence " + DBG2(DBG_KNL, "received PF_KEY message with unexpected sequence " "number, was %d expected %d", msg->sadb_msg_seq, this->seq); if (msg->sadb_msg_seq == 0) From bdaf9f97e6cd3e97f4b947e8748efd5b34c4c72e Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Sun, 21 Apr 2013 16:50:17 +0200 Subject: [PATCH 40/42] child-sa: query SAD/SPD just for what we actually need to update statistics --- src/libcharon/sa/child_sa.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index a8c246bfb..5861fe37c 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -527,12 +527,15 @@ METHOD(child_sa_t, get_usestats, void, private_child_sa_t *this, bool inbound, time_t *time, u_int64_t *bytes, u_int64_t *packets) { - if (update_usebytes(this, inbound) != FAILED) + if ((!bytes && !packets) || update_usebytes(this, inbound) != FAILED) { /* there was traffic since last update or the kernel interface * does not support querying the number of usebytes. */ - update_usetime(this, inbound); + if (time) + { + update_usetime(this, inbound); + } } if (time) { From 5c12700f9abc5efeb38d18a46edf152894476e8c Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Sun, 21 Apr 2013 17:05:08 +0200 Subject: [PATCH 41/42] kernel-interface: query SAD for last use time if SPD query didn't yield one --- src/charon-tkm/src/tkm/tkm_kernel_ipsec.c | 2 +- .../libandroidbridge/kernel/android_ipsec.c | 2 +- .../plugins/load_tester/load_tester_ipsec.c | 2 +- src/libcharon/sa/child_sa.c | 24 +++++++++++++++---- src/libhydra/kernel/kernel_interface.c | 4 ++-- src/libhydra/kernel/kernel_interface.h | 3 ++- src/libhydra/kernel/kernel_ipsec.h | 3 ++- .../plugins/kernel_klips/kernel_klips_ipsec.c | 2 +- .../kernel_netlink/kernel_netlink_ipsec.c | 8 ++++++- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 16 +++++++++++-- 10 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c index 69aefea97..a276166e2 100644 --- a/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c +++ b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c @@ -216,7 +216,7 @@ sad_failure: METHOD(kernel_ipsec_t, query_sa, status_t, private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, u_int64_t *bytes, - u_int64_t *packets) + u_int64_t *packets, u_int32_t *time) { return NOT_SUPPORTED; } diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c index 8e8534190..c37ca26ab 100644 --- a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c +++ b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c @@ -86,7 +86,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_android_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes, u_int64_t *packets) + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time) { return NOT_SUPPORTED; } diff --git a/src/libcharon/plugins/load_tester/load_tester_ipsec.c b/src/libcharon/plugins/load_tester/load_tester_ipsec.c index 4f84845a3..49e35c4ca 100644 --- a/src/libcharon/plugins/load_tester/load_tester_ipsec.c +++ b/src/libcharon/plugins/load_tester/load_tester_ipsec.c @@ -71,7 +71,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t, private_load_tester_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes, u_int64_t *packets) + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time) { return NOT_SUPPORTED; } diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 5861fe37c..34435a140 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -424,6 +424,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) { status_t status = FAILED; u_int64_t bytes, packets; + u_int32_t time; if (inbound) { @@ -432,13 +433,17 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) status = hydra->kernel_interface->query_sa(hydra->kernel_interface, this->other_addr, this->my_addr, this->my_spi, proto_ike2ip(this->protocol), this->mark_in, - &bytes, &packets); + &bytes, &packets, &time); if (status == SUCCESS) { if (bytes > this->my_usebytes) { this->my_usebytes = bytes; this->my_usepackets = packets; + if (time) + { + this->my_usetime = time; + } return SUCCESS; } return FAILED; @@ -452,13 +457,17 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) status = hydra->kernel_interface->query_sa(hydra->kernel_interface, this->my_addr, this->other_addr, this->other_spi, proto_ike2ip(this->protocol), this->mark_out, - &bytes, &packets); + &bytes, &packets, &time); if (status == SUCCESS) { if (bytes > this->other_usebytes) { this->other_usebytes = bytes; this->other_usepackets = packets; + if (time) + { + this->other_usetime = time; + } return SUCCESS; } return FAILED; @@ -471,7 +480,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) /** * updates the cached usetime */ -static void update_usetime(private_child_sa_t *this, bool inbound) +static bool update_usetime(private_child_sa_t *this, bool inbound) { enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; @@ -511,7 +520,7 @@ static void update_usetime(private_child_sa_t *this, bool inbound) if (last_use == 0) { - return; + return FALSE; } if (inbound) { @@ -521,6 +530,7 @@ static void update_usetime(private_child_sa_t *this, bool inbound) { this->other_usetime = last_use; } + return TRUE; } METHOD(child_sa_t, get_usestats, void, @@ -534,7 +544,11 @@ METHOD(child_sa_t, get_usestats, void, */ if (time) { - update_usetime(this, inbound); + if (!update_usetime(this, inbound) && !bytes && !packets) + { + /* if policy query did not yield a usetime, query SAs instead */ + update_usebytes(this, inbound); + } } } if (time) diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 290c25a62..d81fa33c7 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -208,14 +208,14 @@ METHOD(kernel_interface_t, update_sa, status_t, METHOD(kernel_interface_t, query_sa, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes, u_int64_t *packets) + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time) { if (!this->ipsec) { return NOT_SUPPORTED; } return this->ipsec->query_sa(this->ipsec, src, dst, spi, protocol, mark, - bytes, packets); + bytes, packets, time); } METHOD(kernel_interface_t, del_sa, status_t, diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index fd64f50c2..b333cae99 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -197,11 +197,12 @@ struct kernel_interface_t { * @param mark optional mark for this SA * @param[out] bytes the number of bytes processed by SA * @param[out] packets number of packets processed by SA + * @param[out] time last time of SA use * @return SUCCESS if operation completed */ status_t (*query_sa) (kernel_interface_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes, u_int64_t *packets); + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time); /** * Delete a previously installed SA from the SAD. diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h index ba67238e5..f61f3c3aa 100644 --- a/src/libhydra/kernel/kernel_ipsec.h +++ b/src/libhydra/kernel/kernel_ipsec.h @@ -155,11 +155,12 @@ struct kernel_ipsec_t { * @param mark optional mark for this SA * @param[out] bytes the number of bytes processed by SA * @param[out] packets number of packets processed by SA + * @param[out] time last time of SA use * @return SUCCESS if operation completed */ status_t (*query_sa) (kernel_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes, u_int64_t *packets); + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time); /** * Delete a previusly installed SA from the SAD. diff --git a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c index 2d09d33cc..32bea7383 100644 --- a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c +++ b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c @@ -1911,7 +1911,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_klips_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes, u_int64_t *packets) + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time) { return NOT_SUPPORTED; /* TODO */ } diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index b30c9533f..58bce6247 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1595,7 +1595,7 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes, u_int64_t *packets) + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time) { netlink_buf_t request; struct nlmsghdr *out = NULL, *hdr; @@ -1680,6 +1680,12 @@ METHOD(kernel_ipsec_t, query_sa, status_t, { *packets = sa->curlft.packets; } + if (time) + { /* curlft contains an "use" time, but that contains a timestamp + * of the first use, not the last. Last use time must be queried + * on the policy on Linux */ + *time = 0; + } status = SUCCESS; } memwipe(out, len); diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 3ade6f4a5..ecab2827a 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1804,7 +1804,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, mark_t mark, - u_int64_t *bytes, u_int64_t *packets) + u_int64_t *bytes, u_int64_t *packets, u_int32_t *time) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; @@ -1862,6 +1862,18 @@ METHOD(kernel_ipsec_t, query_sa, status_t, /* not supported by PF_KEY */ *packets = 0; } + if (time) + { +#ifdef __APPLE__ + /* OS X uses the "last" time of use in usetime */ + *time = response.lft_current->sadb_lifetime_usetime; +#else /* !__APPLE__ */ + /* on Linux, sadb_lifetime_usetime is set to the "first" time of use, + * which is actually correct according to PF_KEY. We have to query + * policies for the last usetime. */ + *time = 0; +#endif /* !__APPLE__ */ + } free(out); return SUCCESS; @@ -2435,7 +2447,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t, } else if (response.lft_current == NULL) { - DBG1(DBG_KNL, "unable to query policy %R === %R %N: kernel reports no " + DBG2(DBG_KNL, "unable to query policy %R === %R %N: kernel reports no " "use time", src_ts, dst_ts, policy_dir_names, direction); free(out); return FAILED; From c9a323c1d970b4c28ae6a3c2bba1b55c539346d1 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 6 May 2013 16:40:19 +0200 Subject: [PATCH 42/42] kernel-pfroute: allow only one thread to do a route look up simultaneously Otherwise we mess up the sequence number another thread is waiting for. --- src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 8d8d0362a..6c0b457c5 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -668,7 +668,7 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) memcpy(this->reply, &msg, msg.rtm.rtm_msglen); } /* signal on any event, add_ip()/del_ip() might wait for it */ - this->condvar->signal(this->condvar); + this->condvar->broadcast(this->condvar); this->mutex->unlock(this->mutex); return JOB_REQUEUE_DIRECT; @@ -1122,6 +1122,10 @@ METHOD(kernel_net_t, get_nexthop, host_t*, } this->mutex->lock(this->mutex); + while (this->waiting_seq) + { + this->condvar->wait(this->condvar, this->mutex); + } this->waiting_seq = msg.hdr.rtm_seq; if (send(this->socket, &msg, msg.hdr.rtm_msglen, 0) == msg.hdr.rtm_msglen) { @@ -1154,6 +1158,9 @@ METHOD(kernel_net_t, get_nexthop, host_t*, { DBG1(DBG_KNL, "PF_ROUTE lookup failed: %s", strerror(errno)); } + /* signal completion of query to a waiting thread */ + this->waiting_seq = 0; + this->condvar->signal(this->condvar); this->mutex->unlock(this->mutex); return hop;