aboutsummaryrefslogtreecommitdiffstats
path: root/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c')
-rw-r--r--src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c63
1 files changed, 62 insertions, 1 deletions
diff --git a/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c
index 58900b89e..3fe6823b7 100644
--- a/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -41,6 +41,7 @@
#include <processing/jobs/acquire_job.h>
#include <processing/jobs/rekey_child_sa_job.h>
#include <processing/jobs/delete_child_sa_job.h>
+#include <processing/jobs/update_sa_job.h>
/** required for Linux 2.6.26 kernel and later */
#ifndef XFRM_STATE_AF_UNSPEC
@@ -52,6 +53,11 @@
#define PRIO_HIGH 2000
/**
+ * Create ORable bitfield of XFRM NL groups
+ */
+#define XFRMNLGRP(x) (1<<(XFRMNLGRP_##x-1))
+
+/**
* returns a pointer to the first rtattr following the nlmsghdr *nlh and the
* 'usual' netlink data x like 'struct xfrm_usersa_info'
*/
@@ -312,6 +318,27 @@ static void host2xfrm(host_t *host, xfrm_address_t *xfrm)
}
/**
+ * convert a struct xfrm_address to a host_t
+ */
+static host_t* xfrm2host(int family, xfrm_address_t *xfrm, u_int16_t port)
+{
+ chunk_t chunk;
+
+ switch (family)
+ {
+ case AF_INET:
+ chunk = chunk_create((u_char*)&xfrm->a4, sizeof(xfrm->a4));
+ break;
+ case AF_INET6:
+ chunk = chunk_create((u_char*)&xfrm->a6, sizeof(xfrm->a6));
+ break;
+ default:
+ return NULL;
+ }
+ return host_create_from_chunk(family, chunk, ntohs(port));
+}
+
+/**
* convert a traffic selector address range to subnet and its mask.
*/
static void ts2subnet(traffic_selector_t* ts,
@@ -484,6 +511,37 @@ static void process_expire(private_kernel_netlink_ipsec_t *this, struct nlmsghdr
}
/**
+ * process a XFRM_MSG_MAPPING from kernel
+ */
+static void process_mapping(private_kernel_netlink_ipsec_t *this,
+ struct nlmsghdr *hdr)
+{
+ job_t *job;
+ u_int32_t spi, reqid;
+ struct xfrm_user_mapping *mapping;
+ host_t *host;
+
+ mapping = (struct xfrm_user_mapping*)NLMSG_DATA(hdr);
+ spi = mapping->id.spi;
+ reqid = mapping->reqid;
+
+ DBG2(DBG_KNL, "received a XFRM_MSG_MAPPING");
+
+ if (proto_kernel2ike(mapping->id.proto) == PROTO_ESP)
+ {
+ host = xfrm2host(mapping->id.family, &mapping->new_saddr,
+ mapping->new_sport);
+ if (host)
+ {
+ DBG1(DBG_KNL, "NAT mappings of ESP CHILD_SA with SPI %.8x and "
+ "reqid {%d} changed, queueing update job", ntohl(spi), reqid);
+ job = (job_t*)update_sa_job_create(reqid, host);
+ charon->processor->queue_job(charon->processor, job);
+ }
+ }
+}
+
+/**
* Receives events from kernel
*/
static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this)
@@ -531,6 +589,9 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this)
case XFRM_MSG_EXPIRE:
process_expire(this, hdr);
break;
+ case XFRM_MSG_MAPPING:
+ process_mapping(this, hdr);
+ break;
default:
break;
}
@@ -1686,7 +1747,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
{
charon->kill(charon, "unable to create XFRM event socket");
}
- addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE;
+ addr.nl_groups = XFRMNLGRP(ACQUIRE) | XFRMNLGRP(EXPIRE) | XFRMNLGRP(MAPPING);
if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr)))
{
charon->kill(charon, "unable to bind XFRM event socket");