Merge branch 'libipsec-raw-esp'

This adds support for sending/receiving ESP packets without UDP
encapsulation to libipsec and kernel-libipsec.  Only Linux is currently
supported and the feature is disabled by default.
This commit is contained in:
Tobias Brunner
2023-05-23 13:19:57 +02:00
58 changed files with 1382 additions and 82 deletions
+7
View File
@@ -5,3 +5,10 @@ charon.plugins.kernel-libipsec.allow_peer_ts = no
installed for such traffic (via TUN device) usually prevents further IKE
traffic. The fwmark options for the _kernel-netlink_ and _socket-default_
plugins can be used to circumvent that problem.
charon.plugins.kernel-libipsec.fwmark = charon.plugins.socket-default.fwmark
Firewall mark to set on outbound raw ESP packets.
charon.plugins.kernel-libipsec.raw_esp = no
Whether to send and receive ESP packets without UDP encapsulation if
supported on this platform and no NAT is detected.
@@ -85,19 +85,14 @@ struct private_android_service_t {
bool use_dns_proxy;
};
/**
* Outbound callback
*/
static void send_esp(void *data, esp_packet_t *packet)
CALLBACK(send_esp, void,
void *data, esp_packet_t *packet, bool encap)
{
charon->sender->send_no_marker(charon->sender, (packet_t*)packet);
}
/**
* Inbound callback
*/
static void deliver_plain(private_android_service_t *this,
ip_packet_t *packet)
CALLBACK(deliver_plain, void,
private_android_service_t *this, ip_packet_t *packet)
{
chunk_t encoding;
ssize_t len;
@@ -122,10 +117,8 @@ static void deliver_plain(private_android_service_t *this,
packet->destroy(packet);
}
/**
* Receiver callback
*/
static void receiver_esp_cb(void *data, packet_t *packet)
CALLBACK(receiver_esp_cb, void,
void *data, packet_t *packet)
{
esp_packet_t *esp_packet;
@@ -359,14 +352,10 @@ static bool setup_tun_device(private_android_service_t *this,
if (!already_registered)
{
charon->receiver->add_esp_cb(charon->receiver,
(receiver_esp_cb_t)receiver_esp_cb, NULL);
ipsec->processor->register_inbound(ipsec->processor,
(ipsec_inbound_cb_t)deliver_plain, this);
ipsec->processor->register_outbound(ipsec->processor,
(ipsec_outbound_cb_t)send_esp, NULL);
this->dns_proxy->register_cb(this->dns_proxy,
(dns_proxy_response_cb_t)deliver_plain, this);
charon->receiver->add_esp_cb(charon->receiver, receiver_esp_cb, NULL);
ipsec->processor->register_inbound(ipsec->processor, deliver_plain, this);
ipsec->processor->register_outbound(ipsec->processor, send_esp, NULL);
this->dns_proxy->register_cb(this->dns_proxy, deliver_plain, this);
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create((callback_job_cb_t)handle_plain, this,
@@ -422,14 +411,10 @@ static void close_tun_device(private_android_service_t *this)
this->tunfd = -1;
this->lock->unlock(this->lock);
this->dns_proxy->unregister_cb(this->dns_proxy,
(dns_proxy_response_cb_t)deliver_plain);
ipsec->processor->unregister_outbound(ipsec->processor,
(ipsec_outbound_cb_t)send_esp);
ipsec->processor->unregister_inbound(ipsec->processor,
(ipsec_inbound_cb_t)deliver_plain);
charon->receiver->del_esp_cb(charon->receiver,
(receiver_esp_cb_t)receiver_esp_cb);
this->dns_proxy->unregister_cb(this->dns_proxy, deliver_plain);
ipsec->processor->unregister_outbound(ipsec->processor, send_esp);
ipsec->processor->unregister_inbound(ipsec->processor, deliver_plain);
charon->receiver->del_esp_cb(charon->receiver, receiver_esp_cb);
close(tunfd);
}
@@ -71,6 +71,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
private_kernel_android_ipsec_t *this, kernel_ipsec_sa_id_t *id,
kernel_ipsec_add_sa_t *data)
{
if (!data->encap)
{
DBG1(DBG_ESP, "failed to add SAD entry: only UDP encapsulation is "
"supported");
return FAILED;
}
return ipsec->sas->add_sa(ipsec->sas, id->src, id->dst, id->spi, id->proto,
data->reqid, id->mark, data->tfc, data->lifetime,
data->enc_alg, data->enc_key, data->int_alg, data->int_key,
@@ -82,6 +88,12 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
private_kernel_android_ipsec_t *this, kernel_ipsec_sa_id_t *id,
kernel_ipsec_update_sa_t *data)
{
if (!data->new_encap)
{
DBG1(DBG_ESP, "failed to update SAD entry: can't deactivate UDP "
"encapsulation");
return NOT_SUPPORTED;
}
return ipsec->sas->update_sa(ipsec->sas, id->spi, id->proto, data->cpi,
id->src, id->dst, data->new_src, data->new_dst, data->encap,
data->new_encap, id->mark);
@@ -15,7 +15,8 @@ endif
libstrongswan_kernel_libipsec_la_SOURCES = \
kernel_libipsec_plugin.h kernel_libipsec_plugin.c \
kernel_libipsec_ipsec.h kernel_libipsec_ipsec.c \
kernel_libipsec_router.h kernel_libipsec_router.c
kernel_libipsec_router.h kernel_libipsec_router.c \
kernel_libipsec_esp_handler.h kernel_libipsec_esp_handler.c
libstrongswan_kernel_libipsec_la_LIBADD = $(top_builddir)/src/libipsec/libipsec.la
@@ -0,0 +1,351 @@
/*
* Copyright (C) 2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/* for struct in6_pktinfo */
#define _GNU_SOURCE
#include "kernel_libipsec_esp_handler.h"
#ifdef __linux__
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <ipsec.h>
#include <collections/blocking_queue.h>
#include <processing/jobs/callback_job.h>
typedef struct private_kernel_libipsec_esp_handler_t private_kernel_libipsec_esp_handler_t;
/**
* Private data
*/
struct private_kernel_libipsec_esp_handler_t {
/**
* Public interface
*/
kernel_libipsec_esp_handler_t public;
/**
* Queue for outbound ESP packets (esp_packet_t*)
*/
blocking_queue_t *queue;
/**
* Socket to send/receive IPv4 ESP packets
*/
int skt_v4;
/**
* Socket to send/receive IPv6 ESP packets
*/
int skt_v6;
};
METHOD(kernel_libipsec_esp_handler_t, send_, void,
private_kernel_libipsec_esp_handler_t *this, esp_packet_t *packet)
{
this->queue->enqueue(this->queue, packet);
}
CALLBACK(send_esp, job_requeue_t,
private_kernel_libipsec_esp_handler_t *this)
{
packet_t *packet;
host_t *source, *destination;
chunk_t data;
struct msghdr msg = {};
struct cmsghdr *cmsg;
struct iovec iov;
char ancillary[64] = {};
ssize_t len;
int skt;
packet = (packet_t*)this->queue->dequeue(this->queue);
data = packet->get_data(packet);
source = packet->get_source(packet);
destination = packet->get_destination(packet);
DBG2(DBG_NET, "sending raw ESP packet: from %H to %H (%zu data bytes)",
source, destination, data.len);
/* the port of the destination address acts as protocol selector for RAW
* sockets, for IPv4 the kernel ignores it, for IPv6 it does not and
* complains if it isn't zero or doesn't match the one set on the socket */
destination->set_port(destination, 0);
msg.msg_name = destination->get_sockaddr(destination);
msg.msg_namelen = *destination->get_sockaddr_len(destination);
iov.iov_base = data.ptr;
iov.iov_len = data.len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = ancillary;
if (source->get_family(source) == AF_INET)
{
struct in_pktinfo *pktinfo;
const struct sockaddr_in *sin;
msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
sin = (struct sockaddr_in*)source->get_sockaddr(source);
memcpy(&pktinfo->ipi_spec_dst, &sin->sin_addr, sizeof(struct in_addr));
skt = this->skt_v4;
}
else
{
struct in6_pktinfo *pktinfo;
const struct sockaddr_in6 *sin;
msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
sin = (struct sockaddr_in6*)source->get_sockaddr(source);
memcpy(&pktinfo->ipi6_addr, &sin->sin6_addr, sizeof(struct in6_addr));
skt = this->skt_v6;
}
len = sendmsg(skt, &msg, 0);
if (len != data.len)
{
DBG1(DBG_KNL, "error writing to ESP socket: %s", strerror(errno));
}
packet->destroy(packet);
return JOB_REQUEUE_DIRECT;
}
CALLBACK(receive_esp, bool,
private_kernel_libipsec_esp_handler_t *this, int fd, watcher_event_t event)
{
char buf[2048];
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iov;
char ancillary[64];
union {
struct sockaddr_in in4;
struct sockaddr_in6 in6;
} src;
host_t *source, *destination = NULL;
packet_t *packet;
chunk_t data;
ssize_t len;
msg.msg_name = &src;
msg.msg_namelen = sizeof(src);
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = ancillary;
msg.msg_controllen = sizeof(ancillary);
msg.msg_flags = 0;
len = recvmsg(fd, &msg, MSG_DONTWAIT|MSG_TRUNC);
if (len < 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
DBG1(DBG_KNL, "receiving from ESP socket failed: %s",
strerror(errno));
}
return TRUE;
}
else if (msg.msg_flags & MSG_TRUNC)
{
DBG1(DBG_KNL, "ESP packet with length %zd exceeds buffer size of %zu",
len, sizeof(buf));
return TRUE;
}
data = chunk_create(buf, len);
/* skip the IP header returned by IPv4 raw sockets */
if (fd == this->skt_v4)
{
data = chunk_skip(data, sizeof(struct iphdr));
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_PKTINFO)
{
const struct in_pktinfo *pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
struct sockaddr_in dst = {
.sin_family = AF_INET,
};
memcpy(&dst.sin_addr, &pktinfo->ipi_addr, sizeof(dst.sin_addr));
destination = host_create_from_sockaddr((sockaddr_t*)&dst);
}
else if (cmsg->cmsg_level == IPPROTO_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO)
{
const struct in6_pktinfo *pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
};
memcpy(&dst.sin6_addr, &pktinfo->ipi6_addr, sizeof(dst.sin6_addr));
destination = host_create_from_sockaddr((sockaddr_t*)&dst);
}
if (destination)
{
break;
}
}
if (!destination)
{
DBG1(DBG_KNL, "error reading destination IP address for ESP packet");
return TRUE;
}
source = host_create_from_sockaddr((sockaddr_t*)&src);
DBG2(DBG_NET, "received raw ESP packet: from %#H to %#H (%zu data bytes)",
source, destination, data.len);
packet = packet_create();
packet->set_source(packet, source);
packet->set_destination(packet, destination);
packet->set_data(packet, chunk_clone(data));
ipsec->processor->queue_inbound(ipsec->processor,
esp_packet_create_from_packet(packet));
return TRUE;
}
METHOD(kernel_libipsec_esp_handler_t, destroy, void,
private_kernel_libipsec_esp_handler_t *this)
{
if (this->skt_v4 >= 0)
{
lib->watcher->remove(lib->watcher, this->skt_v4);
close(this->skt_v4);
}
if (this->skt_v6 >= 0)
{
lib->watcher->remove(lib->watcher, this->skt_v6);
close(this->skt_v6);
}
this->queue->destroy_offset(this->queue, offsetof(esp_packet_t, destroy));
free(this);
}
/**
* Create a RAW socket for the given address family
*/
static int create_socket(int family)
{
const char *fwmark;
mark_t mark;
int skt, on = 1;
skt = socket(family, SOCK_RAW, IPPROTO_ESP);
if (skt == -1)
{
DBG1(DBG_KNL, "opening RAW socket for ESP failed: %s", strerror(errno));
return -1;
}
if (setsockopt(skt, family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6,
family == AF_INET ? IP_PKTINFO : IPV6_RECVPKTINFO,
&on, sizeof(on)) == -1)
{
DBG1(DBG_KNL, "unable to set PKTINFO on ESP socket: %s",
strerror(errno));
close(skt);
return -1;
}
fwmark = lib->settings->get_str(lib->settings,
"%s.plugins.kernel-libipsec.fwmark",
lib->settings->get_str(lib->settings,
"%s.plugins.socket-default.fwmark", NULL, lib->ns),
lib->ns);
if (fwmark && mark_from_string(fwmark, MARK_OP_NONE, &mark) &&
setsockopt(skt, SOL_SOCKET, SO_MARK, &mark.value, sizeof(mark.value)) < 0)
{
DBG1(DBG_KNL, "unable to set SO_MARK on ESP socket: %s",
strerror(errno));
}
return skt;
}
/*
* Described in header
*/
kernel_libipsec_esp_handler_t *kernel_libipsec_esp_handler_create()
{
private_kernel_libipsec_esp_handler_t *this;
if (!lib->caps->keep(lib->caps, CAP_NET_RAW))
{ /* required to open SOCK_RAW sockets and according to capabilities(7)
* it is also required to use the socket */
DBG1(DBG_KNL, "kernel-libipsec requires CAP_NET_RAW capability to send "
"and receive ESP packets without UDP encapsulation");
return NULL;
}
INIT(this,
.public = {
.send = _send_,
.destroy = _destroy,
},
.queue = blocking_queue_create(),
.skt_v4 = create_socket(AF_INET),
.skt_v6 = create_socket(AF_INET6),
);
if (this->skt_v4 == -1 && this->skt_v6 == -1)
{
destroy(this);
return NULL;
}
if (this->skt_v4 >= 0)
{
lib->watcher->add(lib->watcher, this->skt_v4, WATCHER_READ,
receive_esp, this);
}
if (this->skt_v6 >= 0)
{
lib->watcher->add(lib->watcher, this->skt_v6, WATCHER_READ,
receive_esp, this);
}
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create(send_esp, this, NULL,
(callback_job_cancel_t)return_false));
return &this->public;
}
#else /* __linux__ */
kernel_libipsec_esp_handler_t *kernel_libipsec_esp_handler_create()
{
return NULL;
}
#endif /* __linux__ */
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup kernel_libipsec_esp_handler kernel_libipsec_esp_handler
* @{ @ingroup kernel_libipsec
*/
#ifndef KERNEL_LIBIPSEC_ESP_HANDLER_H_
#define KERNEL_LIBIPSEC_ESP_HANDLER_H_
#include <esp_packet.h>
typedef struct kernel_libipsec_esp_handler_t kernel_libipsec_esp_handler_t;
/**
* Class that sends and receives raw ESP packets.
*/
struct kernel_libipsec_esp_handler_t {
/**
* Send the given ESP packet without UDP encapsulation.
*
* @param packet ESP packet to send
*/
void (*send)(kernel_libipsec_esp_handler_t *this, esp_packet_t *packet);
/**
* Destroy the given instance.
*/
void (*destroy)(kernel_libipsec_esp_handler_t *this);
};
/**
* Create a kernel_libipsec_esp_handler_t instance.
*
* @return created instance, NULL if not supported
*/
kernel_libipsec_esp_handler_t *kernel_libipsec_esp_handler_create();
#endif /** KERNEL_LIBIPSEC_ESP_HANDLER_H_ @}*/
@@ -56,6 +56,11 @@ struct private_kernel_libipsec_ipsec_t {
* Whether the remote TS may equal the IKE peer
*/
bool allow_peer_ts;
/**
* Whether UDP encapsulation is required
*/
bool require_encap;
};
typedef struct exclude_route_t exclude_route_t;
@@ -241,8 +246,8 @@ static void acquire(uint32_t reqid)
METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
private_kernel_libipsec_ipsec_t *this)
{
return KERNEL_REQUIRE_UDP_ENCAPSULATION | KERNEL_ESP_V3_TFC |
KERNEL_SA_USE_TIME;
return KERNEL_ESP_V3_TFC | KERNEL_SA_USE_TIME |
(this->require_encap ? KERNEL_REQUIRE_UDP_ENCAPSULATION : 0);
}
METHOD(kernel_ipsec_t, get_spi, status_t,
@@ -263,6 +268,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
private_kernel_libipsec_ipsec_t *this, kernel_ipsec_sa_id_t *id,
kernel_ipsec_add_sa_t *data)
{
if (this->require_encap && !data->encap)
{
DBG1(DBG_ESP, "failed to add SAD entry: only UDP encapsulation is "
"supported");
return FAILED;
}
return ipsec->sas->add_sa(ipsec->sas, id->src, id->dst, id->spi, id->proto,
data->reqid, id->mark, data->tfc, data->lifetime,
data->enc_alg, data->enc_key, data->int_alg, data->int_key,
@@ -698,6 +709,7 @@ kernel_libipsec_ipsec_t *kernel_libipsec_ipsec_create()
.excludes = linked_list_create(),
.allow_peer_ts = lib->settings->get_bool(lib->settings,
"%s.plugins.kernel-libipsec.allow_peer_ts", FALSE, lib->ns),
.require_encap = !lib->get(lib, "kernel-libipsec-esp-handler"),
);
ipsec->events->register_listener(ipsec->events, &this->ipsec_listener);
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012-2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
@@ -17,6 +17,7 @@
#include "kernel_libipsec_plugin.h"
#include "kernel_libipsec_ipsec.h"
#include "kernel_libipsec_router.h"
#include "kernel_libipsec_esp_handler.h"
#include <daemon.h>
#include <ipsec.h>
@@ -45,6 +46,11 @@ struct private_kernel_libipsec_plugin_t {
* Packet router
*/
kernel_libipsec_router_t *router;
/**
* Raw ESP handler
*/
kernel_libipsec_esp_handler_t *esp_handler;
};
METHOD(plugin_t, get_name, char*,
@@ -92,6 +98,11 @@ METHOD(plugin_t, destroy, void,
lib->set(lib, "kernel-libipsec-tun", NULL);
this->tun->destroy(this->tun);
}
if (this->esp_handler)
{
lib->set(lib, "kernel-libipsec-esp-handler", NULL);
this->esp_handler->destroy(this->esp_handler);
}
libipsec_deinit();
free(this);
}
@@ -146,5 +157,17 @@ plugin_t *kernel_libipsec_plugin_create()
/* set TUN device as default to install VIPs */
lib->settings->set_str(lib->settings, "%s.install_virtual_ip_on",
this->tun->get_name(this->tun), lib->ns);
if (lib->settings->get_bool(lib->settings,
"%s.plugins.kernel-libipsec.raw_esp", FALSE, lib->ns))
{
this->esp_handler = kernel_libipsec_esp_handler_create();
if (!this->esp_handler)
{
DBG1(DBG_KNL, "only UDP-encapsulated ESP packets supported by "
"kernel-libipsec on this platform");
}
lib->set(lib, "kernel-libipsec-esp-handler", this->esp_handler);
}
return &this->public.plugin;
}
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include "kernel_libipsec_router.h"
#include "kernel_libipsec_esp_handler.h"
#include <daemon.h>
#include <ipsec.h>
@@ -76,6 +77,11 @@ struct private_kernel_libipsec_router_t {
* Pipe to signal handle_plain() about changes regarding TUN devices
*/
int notify[2];
/**
* ESP handler to send raw ESP packets
*/
kernel_libipsec_esp_handler_t *esp_handler;
};
/**
@@ -94,28 +100,32 @@ static bool tun_entry_equals(tun_entry_t *a, tun_entry_t *b)
return a->addr->ip_equals(a->addr, b->addr);
}
/**
* Outbound callback
*/
static void send_esp(void *data, esp_packet_t *packet)
CALLBACK(send_esp, void,
private_kernel_libipsec_router_t *this, esp_packet_t *packet, bool encap)
{
charon->sender->send_no_marker(charon->sender, (packet_t*)packet);
if (encap)
{
charon->sender->send_no_marker(charon->sender, (packet_t*)packet);
}
else if (this->esp_handler)
{
this->esp_handler->send(this->esp_handler, packet);
}
else
{ /* shouldn't happen as UDP encap is forced without ESP handler */
packet->destroy(packet);
}
}
/**
* Receiver callback
*/
static void receiver_esp_cb(void *data, packet_t *packet)
CALLBACK(receiver_esp_cb, void,
void *data, packet_t *packet)
{
ipsec->processor->queue_inbound(ipsec->processor,
esp_packet_create_from_packet(packet));
}
/**
* Inbound callback
*/
static void deliver_plain(private_kernel_libipsec_router_t *this,
ip_packet_t *packet)
CALLBACK(deliver_plain, void,
private_kernel_libipsec_router_t *this, ip_packet_t *packet)
{
tun_device_t *tun;
tun_entry_t *entry, lookup = {
@@ -292,12 +302,9 @@ METHOD(kernel_libipsec_router_t, get_tun_name, char*,
METHOD(kernel_libipsec_router_t, destroy, void,
private_kernel_libipsec_router_t *this)
{
charon->receiver->del_esp_cb(charon->receiver,
(receiver_esp_cb_t)receiver_esp_cb);
ipsec->processor->unregister_outbound(ipsec->processor,
(ipsec_outbound_cb_t)send_esp);
ipsec->processor->unregister_inbound(ipsec->processor,
(ipsec_inbound_cb_t)deliver_plain);
charon->receiver->del_esp_cb(charon->receiver, receiver_esp_cb);
ipsec->processor->unregister_outbound(ipsec->processor, send_esp);
ipsec->processor->unregister_inbound(ipsec->processor, deliver_plain);
charon->kernel->remove_listener(charon->kernel, &this->public.listener);
this->lock->destroy(this->lock);
this->tuns->destroy(this->tuns);
@@ -333,7 +340,8 @@ kernel_libipsec_router_t *kernel_libipsec_router_create()
},
.tun = {
.tun = lib->get(lib, "kernel-libipsec-tun"),
}
},
.esp_handler = lib->get(lib, "kernel-libipsec-esp-handler"),
);
if (pipe(this->notify) != 0 ||
@@ -351,11 +359,9 @@ kernel_libipsec_router_t *kernel_libipsec_router_create()
this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
charon->kernel->add_listener(charon->kernel, &this->public.listener);
ipsec->processor->register_outbound(ipsec->processor, send_esp, NULL);
ipsec->processor->register_inbound(ipsec->processor,
(ipsec_inbound_cb_t)deliver_plain, this);
charon->receiver->add_esp_cb(charon->receiver,
(receiver_esp_cb_t)receiver_esp_cb, NULL);
ipsec->processor->register_outbound(ipsec->processor, send_esp, this);
ipsec->processor->register_inbound(ipsec->processor, deliver_plain, this);
charon->receiver->add_esp_cb(charon->receiver, receiver_esp_cb, NULL);
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create((callback_job_cb_t)handle_plain, this,
NULL, (callback_job_cancel_t)return_false));
+5 -4
View File
@@ -169,12 +169,12 @@ static job_requeue_t process_inbound(private_ipsec_processor_t *this)
* Send an ESP packet using the registered outbound callback
*/
static void send_outbound(private_ipsec_processor_t *this,
esp_packet_t *packet)
esp_packet_t *packet, bool encap)
{
this->lock->read_lock(this->lock);
if (this->outbound.cb)
{
this->outbound.cb(this->outbound.data, packet);
this->outbound.cb(this->outbound.data, packet, encap);
}
else
{
@@ -194,7 +194,7 @@ static job_requeue_t process_outbound(private_ipsec_processor_t *this)
ip_packet_t *packet;
ipsec_sa_t *sa;
host_t *src, *dst;
bool acquire = FALSE;
bool acquire = FALSE, encap = FALSE;
packet = (ip_packet_t*)this->outbound_queue->dequeue(this->outbound_queue);
@@ -242,9 +242,10 @@ static job_requeue_t process_outbound(private_ipsec_processor_t *this)
return JOB_REQUEUE_DIRECT;
}
sa->update_usestats(sa, packet->get_encoding(packet).len);
encap = sa->get_encap(sa);
ipsec->sas->checkin(ipsec->sas, sa);
policy->destroy(policy);
send_outbound(this, esp_packet);
send_outbound(this, esp_packet, encap);
return JOB_REQUEUE_DIRECT;
}
+4 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2012-2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
@@ -43,8 +43,10 @@ typedef void (*ipsec_inbound_cb_t)(void *data, ip_packet_t *packet);
*
* @param data data supplied during registration of the callback
* @param packet ESP packet to send
* @param encap TRUE to send the packet with UDP encapsulation
*/
typedef void (*ipsec_outbound_cb_t)(void *data, esp_packet_t *packet);
typedef void (*ipsec_outbound_cb_t)(void *data, esp_packet_t *packet,
bool encap);
/**
* IPsec processor
+21 -6
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2012-2023 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
*
@@ -69,6 +69,11 @@ struct private_ipsec_sa_t {
*/
ipsec_mode_t mode;
/**
* TRUE if UDP encapsulation should be used when sending
*/
bool encap;
/**
* TRUE if extended sequence numbers are used
*/
@@ -133,6 +138,18 @@ METHOD(ipsec_sa_t, set_destination, void,
this->dst = addr->clone(addr);
}
METHOD(ipsec_sa_t, get_encap, bool,
private_ipsec_sa_t *this)
{
return this->encap;
}
METHOD(ipsec_sa_t, set_encap, void,
private_ipsec_sa_t *this, bool encap)
{
this->encap = encap;
}
METHOD(ipsec_sa_t, get_spi, uint32_t,
private_ipsec_sa_t *this)
{
@@ -285,11 +302,6 @@ ipsec_sa_t *ipsec_sa_create(uint32_t spi, host_t *src, host_t *dst,
DBG1(DBG_ESP, " IPsec SA: protocol not supported");
return NULL;
}
if (!encap)
{
DBG1(DBG_ESP, " IPsec SA: only UDP encapsulation is supported");
return NULL;
}
if (esn)
{
DBG1(DBG_ESP, " IPsec SA: ESN not supported");
@@ -313,6 +325,8 @@ ipsec_sa_t *ipsec_sa_create(uint32_t spi, host_t *src, host_t *dst,
.get_destination = _get_destination,
.set_source = _set_source,
.set_destination = _set_destination,
.get_encap = _get_encap,
.set_encap = _set_encap,
.get_spi = _get_spi,
.get_reqid = _get_reqid,
.get_protocol = _get_protocol,
@@ -333,6 +347,7 @@ ipsec_sa_t *ipsec_sa_create(uint32_t spi, host_t *src, host_t *dst,
.protocol = protocol,
.reqid = reqid,
.mode = mode,
.encap = encap,
.esn = esn,
.inbound = inbound,
);
+15 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2012-2023 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
*
@@ -66,6 +66,20 @@ struct ipsec_sa_t {
*/
void (*set_destination)(ipsec_sa_t *this, host_t *addr);
/**
* Get whether UDP encapsulation should be used for this SA
*
* @return TRUE if encapsulation should be used, FALSE otherwise
*/
bool (*get_encap)(ipsec_sa_t *this);
/**
* Set whether UDP encapsulation should be used for this SA
*
* @param encap TRUE if encapsulation should be used, FALSE otherwise
*/
void (*set_encap)(ipsec_sa_t *this, bool encap);
/**
* Get the SPI for this SA
*
+2 -8
View File
@@ -502,7 +502,7 @@ METHOD(ipsec_sa_mgr_t, get_spi, status_t,
METHOD(ipsec_sa_mgr_t, add_sa, status_t,
private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, uint32_t spi,
uint8_t protocol, uint32_t reqid, mark_t mark, uint32_t tfc,
uint8_t protocol, uint32_t reqid, mark_t mark, uint32_t tfc,
lifetime_cfg_t *lifetime, uint16_t enc_alg, chunk_t enc_key,
uint16_t int_alg, chunk_t int_key, ipsec_mode_t mode, uint16_t ipcomp,
uint16_t cpi, bool initiator, bool encap, bool esn, bool inbound,
@@ -568,13 +568,6 @@ METHOD(ipsec_sa_mgr_t, update_sa, status_t,
DBG2(DBG_ESP, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
ntohl(spi), src, dst, new_src, new_dst);
if (!new_encap)
{
DBG1(DBG_ESP, "failed to update SAD entry: can't deactivate UDP "
"encapsulation");
return NOT_SUPPORTED;
}
this->mutex->lock(this->mutex);
if (this->sas->find_first(this->sas, match_entry_by_spi_src_dst_cb,
(void**)&entry, spi, src, dst) &&
@@ -582,6 +575,7 @@ METHOD(ipsec_sa_mgr_t, update_sa, status_t,
{
entry->sa->set_source(entry->sa, new_src);
entry->sa->set_destination(entry->sa, new_dst);
entry->sa->set_encap(entry->sa, new_encap);
/* checkin the entry */
entry->locked = FALSE;
entry->condvar->signal(entry->condvar);
@@ -0,0 +1,11 @@
A connection between the hosts <b>moon</b> and <b>sun</b> is set up.
The authentication is based on <b>X.509 certificates</b> and the <b>kernel-libipsec</b>
plugin is used for userland IPsec ESP encryption. In this scenario, UDP encapsulation
isn't enforced by the plugin as sending of raw ESP packets is enabled.
<b>Firewall marks</b> are used to make the direct ESP connection possible and
still allow IKE traffic to flow freely between the two hosts.
<p/>
Upon the successful establishment of the IPsec tunnel, an updown script automatically
inserts iptables-based firewall rules that let pass the traffic tunneled via the
<b>ipsec0</b> tun interface. In order to test both host-to-host tunnel and firewall,
<b>moon</b> pings <b>sun</b>.
@@ -0,0 +1,5 @@
moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::swanctl --list-sas --raw 2> /dev/null::host-host.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=500 local-id=moon.strongswan.org remote-host=192.168.0.2 remote-port=500 remote-id=sun.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*host-host.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128.*local-ts=\[192.168.0.1/32] remote-ts=\[192.168.0.2/32]::YES
sun::swanctl --list-sas --raw 2> /dev/null::host-host.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=500 remote-id=moon.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*host-host.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128.*local-ts=\[192.168.0.2/32] remote-ts=\[192.168.0.1/32]::YES
sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
@@ -0,0 +1,24 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-libipsec kernel-netlink socket-default updown
multiple_authentication = no
plugins {
kernel-netlink {
fwmark = !0x42
}
socket-default {
fwmark = 0x42
}
kernel-libipsec {
allow_peer_ts = yes
raw_esp = yes
}
}
}
@@ -0,0 +1,26 @@
connections {
host-host {
local_addrs = 192.168.0.1
remote_addrs = 192.168.0.2
local {
auth = pubkey
certs = moonCert.pem
id = moon.strongswan.org
}
remote {
auth = pubkey
id = sun.strongswan.org
}
children {
host-host {
updown = /etc/updown
esp_proposals = aes128-sha256-x25519
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}
@@ -0,0 +1,59 @@
#!/bin/sh
TUN_NAME=ipsec0
# use protocol specific options to set ports
case "$PLUTO_MY_PROTOCOL" in
1) # ICMP
ICMP_TYPE_OPTION="--icmp-type"
;;
58) # ICMPv6
ICMP_TYPE_OPTION="--icmpv6-type"
;;
*)
;;
esac
# are there port numbers?
if [ "$PLUTO_MY_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
S_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
D_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
else
S_MY_PORT="--sport $PLUTO_MY_PORT"
D_MY_PORT="--dport $PLUTO_MY_PORT"
fi
fi
if [ "$PLUTO_PEER_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
# the syntax is --icmp[v6]-type type[/code], so add it to the existing option
S_MY_PORT="$S_MY_PORT/$PLUTO_PEER_PORT"
D_MY_PORT="$D_MY_PORT/$PLUTO_PEER_PORT"
else
S_PEER_PORT="--sport $PLUTO_PEER_PORT"
D_PEER_PORT="--dport $PLUTO_PEER_PORT"
fi
fi
case "$PLUTO_VERB" in
up-host)
iptables -I OUTPUT 1 -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
iptables -I INPUT 1 -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
down-host)
iptables -D OUTPUT -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
iptables -D INPUT -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
esac
@@ -0,0 +1,24 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-libipsec kernel-netlink socket-default updown
multiple_authentication = no
plugins {
kernel-netlink {
fwmark = !0x42
}
socket-default {
fwmark = 0x42
}
kernel-libipsec {
allow_peer_ts = yes
raw_esp = yes
}
}
}
@@ -0,0 +1,26 @@
connections {
host-host {
local_addrs = 192.168.0.2
remote_addrs = 192.168.0.1
local {
auth = pubkey
certs = sunCert.pem
id = sun.strongswan.org
}
remote {
auth = pubkey
id = moon.strongswan.org
}
children {
host-host {
updown = /etc/updown
esp_proposals = aes128-sha256-x25519
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}
@@ -0,0 +1,59 @@
#!/bin/sh
TUN_NAME=ipsec0
# use protocol specific options to set ports
case "$PLUTO_MY_PROTOCOL" in
1) # ICMP
ICMP_TYPE_OPTION="--icmp-type"
;;
58) # ICMPv6
ICMP_TYPE_OPTION="--icmpv6-type"
;;
*)
;;
esac
# are there port numbers?
if [ "$PLUTO_MY_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
S_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
D_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
else
S_MY_PORT="--sport $PLUTO_MY_PORT"
D_MY_PORT="--dport $PLUTO_MY_PORT"
fi
fi
if [ "$PLUTO_PEER_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
# the syntax is --icmp[v6]-type type[/code], so add it to the existing option
S_MY_PORT="$S_MY_PORT/$PLUTO_PEER_PORT"
D_MY_PORT="$D_MY_PORT/$PLUTO_PEER_PORT"
else
S_PEER_PORT="--sport $PLUTO_PEER_PORT"
D_PEER_PORT="--dport $PLUTO_PEER_PORT"
fi
fi
case "$PLUTO_VERB" in
up-host)
iptables -I OUTPUT 1 -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
iptables -I INPUT 1 -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
down-host)
iptables -D OUTPUT -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
iptables -D INPUT -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
esac
@@ -0,0 +1,7 @@
moon::swanctl --terminate --ike host-host 2> /dev/null
moon::systemctl stop strongswan
sun::systemctl stop strongswan
moon::iptables-restore < /etc/iptables.flush
sun::iptables-restore < /etc/iptables.flush
moon::sysctl --pattern net.ipv4.conf.all.rp_filter --system
sun::sysctl --pattern net.ipv4.conf.all.rp_filter --system
@@ -0,0 +1,9 @@
moon::sysctl -w net.ipv4.conf.all.rp_filter=2
sun::sysctl -w net.ipv4.conf.all.rp_filter=2
moon::iptables-restore < /etc/iptables.rules
sun::iptables-restore < /etc/iptables.rules
moon::systemctl start strongswan
sun::systemctl start strongswan
sun::expect-connection host-host
moon::expect-connection host-host
moon::swanctl --initiate --child host-host 2> /dev/null
@@ -0,0 +1,25 @@
#!/bin/bash
#
# This configuration file provides information on the
# guest instances used for this test
# All guest instances that are required for this test
#
VIRTHOSTS="moon winnetou sun"
# Corresponding block diagram
#
DIAGRAM="m-w-s.png"
# Guest instances on which tcpdump is to be started
#
TCPDUMPHOSTS="sun"
# Guest instances on which IPsec is started
# Used for IPsec logging purposes
#
IPSECHOSTS="moon sun"
# charon controlled by swanctl
#
SWANCTL=1
@@ -1,5 +1,5 @@
alice::ping6 -c 3 -W 1 -i 0.2 -s 8184 -p deadbeef ip6-bob.strongswan.org::8192 bytes from ip6-bob.strongswan.org.*: icmp_seq=3::YES
moon ::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.2 remote-port=4500 remote-id=sun.strongswan.org initiator=yes.*nat-remote=yes nat-any=yes encr-alg=AES_CBC encr-keysize=256 integ-alg=HMAC_SHA2_384_192 prf-alg=PRF_HMAC_SHA2_384 dh-group=ECP_384.*child-sas.*net-net.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP encap=yes.*encr-alg=AES_GCM_16 encr-keysize=256.*local-ts=\[fec1::/16\[ipv6-icmp]] remote-ts=\[fec2::/16\[ipv6-icmp]]::YES
sun ::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=4500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org.*nat-remote=yes nat-any=yes encr-alg=AES_CBC encr-keysize=256 integ-alg=HMAC_SHA2_384_192 prf-alg=PRF_HMAC_SHA2_384 dh-group=ECP_384.*child-sas.*net-net.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP encap=yes.*encr-alg=AES_GCM_16 encr-keysize=256.*local-ts=\[fec2::/16\[ipv6-icmp]] remote-ts=\[fec1::/16\[ipv6-icmp]]::YES
moon ::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.2 remote-port=4500 remote-id=sun.strongswan.org initiator=yes.*nat-remote=yes nat-any=yes encr-alg=AES_CBC encr-keysize=256 integ-alg=HMAC_SHA2_384_192 prf-alg=PRF_HMAC_SHA2_384 dh-group=ECP_384.*child-sas.*net-net.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP encap=yes.*encr-alg=AES_GCM_16 encr-keysize=256.*local-ts=\[fec1:\:/16\[ipv6-icmp]] remote-ts=\[fec2:\:/16\[ipv6-icmp]]::YES
sun ::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=4500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org.*nat-remote=yes nat-any=yes encr-alg=AES_CBC encr-keysize=256 integ-alg=HMAC_SHA2_384_192 prf-alg=PRF_HMAC_SHA2_384 dh-group=ECP_384.*child-sas.*net-net.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP encap=yes.*encr-alg=AES_GCM_16 encr-keysize=256.*local-ts=\[fec2:\:/16\[ipv6-icmp]] remote-ts=\[fec1:\:/16\[ipv6-icmp]]::YES
sun::tcpdump::IP moon.strongswan.org.\(4500\|ipsec-nat-t\) > sun.strongswan.org.\(4500\|ipsec-nat-t\): UDP-encap: ESP::YES
sun::tcpdump::IP sun.strongswan.org.\(4500\|ipsec-nat-t\) > moon.strongswan.org.\(4500\|ipsec-nat-t\): UDP-encap: ESP::YES
@@ -0,0 +1,11 @@
An IPv6 ESP tunnel connection between the gateways <b>moon</b> and <b>sun</b> is set up.
It connects the two IPv6 subnets hiding behind their respective gateways.
The authentication is based on <b>X.509 certificates</b> and the <b>kernel-libipsec</b>
plugin is used for userland IPsec ESP encryption. In this scenario, UDP encapsulation
isn't enforced by the plugin as sending of raw ESP packets is enabled.
<p/>
Upon the successful establishment of the IPsec tunnel, an updown script automatically
inserts iptables-based firewall rules that let pass the traffic tunneled via the
<b>ipsec0</b> tun interface. In order to test both tunnel and firewall, client <b>alice</b>
behind gateway <b>moon</b> sends an IPv6 ICMP request to client <b>bob</b> behind <b>sun</b>
using the ping6 command.
@@ -0,0 +1,5 @@
alice::ping6 -c 1 -p deadbeef ip6-bob.strongswan.org::64 bytes from ip6-bob.strongswan.org.*: icmp_seq=1::YES
moon::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=fec0:\:1 local-port=500 local-id=moon.strongswan.org remote-host=fec0:\:2 remote-port=500 remote-id=sun.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[fec1:\:/16] remote-ts=\[fec2:\:/16]::YES
sun ::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=fec0:\:2 local-port=500 local-id=sun.strongswan.org remote-host=fec0:\:1 remote-port=500 remote-id=moon.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[fec2:\:/16] remote-ts=\[fec1:\:/16]::YES
sun::tcpdump::IP6 ip6-moon.strongswan.org > ip6-sun.strongswan.org: ESP::YES
sun::tcpdump::IP6 ip6-sun.strongswan.org > ip6-moon.strongswan.org: ESP::YES
@@ -0,0 +1,15 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 gcm pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-libipsec kernel-netlink socket-default updown
multiple_authentication = no
plugins {
kernel-libipsec {
raw_esp = yes
}
}
}
@@ -0,0 +1,36 @@
connections {
gw-gw {
local_addrs = fec0::1
remote_addrs = fec0::2
local {
auth = pubkey
certs = moonCert.pem
id = moon.strongswan.org
}
remote {
auth = pubkey
id = sun.strongswan.org
}
children {
net-net {
local_ts = fec1::0/16
remote_ts = fec2::0/16
updown = /etc/updown
esp_proposals = aes128gcm128-x25519
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}
authorities {
strongswan {
cacert = strongswanCert.pem
crl_uris = http://ip6-winnetou.strongswan.org/strongswan.crl
}
}
@@ -0,0 +1,59 @@
#!/bin/sh
TUN_NAME=ipsec0
# use protocol specific options to set ports
case "$PLUTO_MY_PROTOCOL" in
1) # ICMP
ICMP_TYPE_OPTION="--icmp-type"
;;
58) # ICMPv6
ICMP_TYPE_OPTION="--icmpv6-type"
;;
*)
;;
esac
# are there port numbers?
if [ "$PLUTO_MY_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
S_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
D_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
else
S_MY_PORT="--sport $PLUTO_MY_PORT"
D_MY_PORT="--dport $PLUTO_MY_PORT"
fi
fi
if [ "$PLUTO_PEER_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
# the syntax is --icmp[v6]-type type[/code], so add it to the existing option
S_MY_PORT="$S_MY_PORT/$PLUTO_PEER_PORT"
D_MY_PORT="$D_MY_PORT/$PLUTO_PEER_PORT"
else
S_PEER_PORT="--sport $PLUTO_PEER_PORT"
D_PEER_PORT="--dport $PLUTO_PEER_PORT"
fi
fi
case "$PLUTO_VERB" in
up-client-v6)
ip6tables -I FORWARD 1 -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
ip6tables -I FORWARD 1 -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
down-client-v6)
ip6tables -D FORWARD -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
ip6tables -D FORWARD -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
esac
@@ -0,0 +1,15 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 gcm pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici ker_nel-libipsec kernel-netlink socket-default updown
multiple_authentication = no
plugins {
kernel-libipsec {
raw_esp = yes
}
}
}
@@ -0,0 +1,37 @@
connections {
gw-gw {
local_addrs = fec0::2
remote_addrs = fec0::1
local {
auth = pubkey
certs = sunCert.pem
id = sun.strongswan.org
}
remote {
auth = pubkey
id = moon.strongswan.org
}
children {
net-net {
local_ts = fec2::0/16
remote_ts = fec1::0/16
updown = /etc/updown
updown = /usr/local/libexec/ipsec/_updown iptables
esp_proposals = aes128gcm128-x25519
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}
authorities {
strongswan {
cacert = strongswanCert.pem
crl_uris = http://ip6-winnetou.strongswan.org/strongswan.crl
}
}
@@ -0,0 +1,59 @@
#!/bin/sh
TUN_NAME=ipsec0
# use protocol specific options to set ports
case "$PLUTO_MY_PROTOCOL" in
1) # ICMP
ICMP_TYPE_OPTION="--icmp-type"
;;
58) # ICMPv6
ICMP_TYPE_OPTION="--icmpv6-type"
;;
*)
;;
esac
# are there port numbers?
if [ "$PLUTO_MY_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
S_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
D_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
else
S_MY_PORT="--sport $PLUTO_MY_PORT"
D_MY_PORT="--dport $PLUTO_MY_PORT"
fi
fi
if [ "$PLUTO_PEER_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
# the syntax is --icmp[v6]-type type[/code], so add it to the existing option
S_MY_PORT="$S_MY_PORT/$PLUTO_PEER_PORT"
D_MY_PORT="$D_MY_PORT/$PLUTO_PEER_PORT"
else
S_PEER_PORT="--sport $PLUTO_PEER_PORT"
D_PEER_PORT="--dport $PLUTO_PEER_PORT"
fi
fi
case "$PLUTO_VERB" in
up-client-v6)
ip6tables -I FORWARD 1 -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
ip6tables -I FORWARD 1 -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
down-client-v6)
ip6tables -D FORWARD -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
ip6tables -D FORWARD -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
esac
@@ -0,0 +1,11 @@
moon::swanctl --terminate --ike gw-gw 2> /dev/null
moon::systemctl stop strongswan
sun::systemctl stop strongswan
alice::"ip route del fec2:\:/16 via fec1:\:1"
moon::"ip route del fec2:\:/16 via fec0:\:2"
sun::"ip route del fec1:\:/16 via fec0:\:1"
bob::"ip route del fec1:\:/16 via fec2:\:1"
moon::iptables-restore < /etc/iptables.flush
sun::iptables-restore < /etc/iptables.flush
moon::ip6tables-restore < /etc/ip6tables.flush
sun::ip6tables-restore < /etc/ip6tables.flush
@@ -0,0 +1,13 @@
moon::iptables-restore < /etc/iptables.drop
sun::iptables-restore < /etc/iptables.drop
moon::ip6tables-restore < /etc/ip6tables.rules
sun::ip6tables-restore < /etc/ip6tables.rules
alice::"ip route add fec2:\:/16 via fec1:\:1"
moon::"ip route add fec2:\:/16 via fec0:\:2"
sun::"ip route add fec1:\:/16 via fec0:\:1"
bob::"ip route add fec1:\:/16 via fec2:\:1"
moon::systemctl start strongswan
sun::systemctl start strongswan
moon::expect-connection gw-gw
sun::expect-connection gw-gw
moon::swanctl --initiate --child net-net 2> /dev/null
@@ -0,0 +1,29 @@
#!/bin/bash
#
# This configuration file provides information on the
# guest instances used for this test
# All guest instances that are required for this test
#
VIRTHOSTS="alice moon winnetou sun bob"
# Corresponding block diagram
#
DIAGRAM="a-m-w-s-b-ip6.png"
# Guest instances on which tcpdump is to be started
#
TCPDUMPHOSTS="sun"
# Guest instances on which IPsec is started
# Used for IPsec logging purposes
#
IPSECHOSTS="moon sun"
# IP protocol used by IPsec is IPv6
#
IPV6=1
# charon controlled by swanctl
#
SWANCTL=1
@@ -0,0 +1,9 @@
A connection between the subnets behind the gateways <b>moon</b> and <b>sun</b> is set up.
The authentication is based on <b>X.509 certificates</b> and the <b>kernel-libipsec</b>
plugin is used for userland IPsec ESP encryption. In this scenario, UDP encapsulation
isn't enforced by the plugin as sending of raw ESP packets is enabled.
<p/>
Upon the successful establishment of the IPsec tunnel, an updown script automatically
inserts iptables-based firewall rules that let pass the traffic tunneled via the
<b>ipsec0</b> tun interface. In order to test both tunnel and firewall, client <b>alice</b>
behind gateway <b>moon</b> pings client <b>bob</b> located behind gateway <b>sun</b>.
@@ -0,0 +1,5 @@
alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES
moon:: swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=500 local-id=moon.strongswan.org remote-host=192.168.0.2 remote-port=500 remote-id=sun.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[10.2.0.0/16]::YES
sun::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=500 remote-id=moon.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*reqid=1 state=INSTALLED mode=TUNNEL protocol=ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.0/16]::YES
sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
@@ -0,0 +1,15 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 gcm pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici kernel-libipsec kernel-netlink socket-default updown
multiple_authentication = no
plugins {
kernel-libipsec {
raw_esp = yes
}
}
}
@@ -0,0 +1,29 @@
connections {
gw-gw {
local_addrs = 192.168.0.1
remote_addrs = 192.168.0.2
local {
auth = pubkey
certs = moonCert.pem
id = moon.strongswan.org
}
remote {
auth = pubkey
id = sun.strongswan.org
}
children {
net-net {
local_ts = 10.1.0.0/16
remote_ts = 10.2.0.0/16
updown = /etc/updown
esp_proposals = aes128gcm128-x25519
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}
@@ -0,0 +1,59 @@
#!/bin/sh
TUN_NAME=ipsec0
# use protocol specific options to set ports
case "$PLUTO_MY_PROTOCOL" in
1) # ICMP
ICMP_TYPE_OPTION="--icmp-type"
;;
58) # ICMPv6
ICMP_TYPE_OPTION="--icmpv6-type"
;;
*)
;;
esac
# are there port numbers?
if [ "$PLUTO_MY_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
S_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
D_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
else
S_MY_PORT="--sport $PLUTO_MY_PORT"
D_MY_PORT="--dport $PLUTO_MY_PORT"
fi
fi
if [ "$PLUTO_PEER_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
# the syntax is --icmp[v6]-type type[/code], so add it to the existing option
S_MY_PORT="$S_MY_PORT/$PLUTO_PEER_PORT"
D_MY_PORT="$D_MY_PORT/$PLUTO_PEER_PORT"
else
S_PEER_PORT="--sport $PLUTO_PEER_PORT"
D_PEER_PORT="--dport $PLUTO_PEER_PORT"
fi
fi
case "$PLUTO_VERB" in
up-client)
iptables -I FORWARD 1 -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
iptables -I FORWARD 1 -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
down-client)
iptables -D FORWARD -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
iptables -D FORWARD -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
esac
@@ -0,0 +1,15 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 x509 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce aes sha1 sha2 gcm pem pkcs1 curve25519 gmp x509 curl revocation hmac kdf vici ker_nel-libipsec kernel-netlink socket-default updown
multiple_authentication = no
plugins {
kernel-libipsec {
raw_esp = yes
}
}
}
@@ -0,0 +1,30 @@
connections {
gw-gw {
local_addrs = 192.168.0.2
remote_addrs = 192.168.0.1
local {
auth = pubkey
certs = sunCert.pem
id = sun.strongswan.org
}
remote {
auth = pubkey
id = moon.strongswan.org
}
children {
net-net {
local_ts = 10.2.0.0/16
remote_ts = 10.1.0.0/16
updown = /etc/updown
updown = /usr/local/libexec/ipsec/_updown iptables
esp_proposals = aes128gcm128-x25519
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}
@@ -0,0 +1,59 @@
#!/bin/sh
TUN_NAME=ipsec0
# use protocol specific options to set ports
case "$PLUTO_MY_PROTOCOL" in
1) # ICMP
ICMP_TYPE_OPTION="--icmp-type"
;;
58) # ICMPv6
ICMP_TYPE_OPTION="--icmpv6-type"
;;
*)
;;
esac
# are there port numbers?
if [ "$PLUTO_MY_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
S_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
D_MY_PORT="$ICMP_TYPE_OPTION $PLUTO_MY_PORT"
else
S_MY_PORT="--sport $PLUTO_MY_PORT"
D_MY_PORT="--dport $PLUTO_MY_PORT"
fi
fi
if [ "$PLUTO_PEER_PORT" != 0 ]
then
if [ -n "$ICMP_TYPE_OPTION" ]
then
# the syntax is --icmp[v6]-type type[/code], so add it to the existing option
S_MY_PORT="$S_MY_PORT/$PLUTO_PEER_PORT"
D_MY_PORT="$D_MY_PORT/$PLUTO_PEER_PORT"
else
S_PEER_PORT="--sport $PLUTO_PEER_PORT"
D_PEER_PORT="--dport $PLUTO_PEER_PORT"
fi
fi
case "$PLUTO_VERB" in
up-client)
iptables -I FORWARD 1 -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
iptables -I FORWARD 1 -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
down-client)
iptables -D FORWARD -o $TUN_NAME -p $PLUTO_PEER_PROTOCOL \
-s $PLUTO_MY_CLIENT $S_MY_PORT \
-d $PLUTO_PEER_CLIENT $D_PEER_PORT -j ACCEPT
iptables -D FORWARD -i $TUN_NAME -p $PLUTO_MY_PROTOCOL \
-s $PLUTO_PEER_CLIENT $S_PEER_PORT \
-d $PLUTO_MY_CLIENT $D_MY_PORT -j ACCEPT
;;
esac
@@ -0,0 +1,5 @@
moon::swanctl --terminate --ike gw-gw 2> /dev/null
moon::systemctl stop strongswan
sun::systemctl stop strongswan
moon::iptables-restore < /etc/iptables.flush
sun::iptables-restore < /etc/iptables.flush
@@ -0,0 +1,7 @@
moon::iptables-restore < /etc/iptables.rules
sun::iptables-restore < /etc/iptables.rules
moon::systemctl start strongswan
sun::systemctl start strongswan
moon::expect-connection gw-gw
sun::expect-connection gw-gw
moon::swanctl --initiate --child net-net 2> /dev/null
@@ -0,0 +1,25 @@
#!/bin/bash
#
# This configuration file provides information on the
# guest instances used for this test
# All guest instances that are required for this test
#
VIRTHOSTS="alice moon winnetou sun bob"
# Corresponding block diagram
#
DIAGRAM="a-m-w-s-b.png"
# Guest instances on which tcpdump is to be started
#
TCPDUMPHOSTS="sun"
# Guest instances on which IPsec is started
# Used for IPsec logging purposes
#
IPSECHOSTS="moon sun"
# charon controlled by swanctl
#
SWANCTL=1