Merge branch 'nm-xfrmi'

Use XFRM interfaces instead of dummy TUN devices to avoid issues with
name resolution if supported by the kernel.

Closes strongswan/strongswan#1048
This commit is contained in:
Tobias Brunner 2023-02-22 13:44:10 +01:00
commit 292eb7893f
16 changed files with 780 additions and 273 deletions

View File

@ -1,3 +1,6 @@
charon-nm.ca_dir = <default> charon-nm.ca_dir = <default>
Directory from which to load CA certificates if no certificate is Directory from which to load CA certificates if no certificate is
configured. configured.
charon-nm.mtu = 1400
MTU for XFRM interfaces created by the NM plugin.

View File

@ -28,6 +28,16 @@ charon.plugins.kernel-netlink.hw_offload_feature_interface = lo
cannot be used to obtain the appropriate feature flag, this option can cannot be used to obtain the appropriate feature flag, this option can
be used to specify an alternative interface for offload feature detection. be used to specify an alternative interface for offload feature detection.
charon.plugins.kernel-netlink.install_routes_xfrmi = no
Whether to install routes for SAs that reference XFRM interfaces.
Whether routes via XFRM interfaces are automatically installed for SAs that
reference such an interface via _if_id_. If the traffic selectors include
the IKE traffic to the peer, this requires special care (e.g. installing
bypass policies and/or routes, or setting a mark on the IKE socket and
excluding such packets from the configured routing table via _fwmark_
option).
charon.plugins.kernel-netlink.mss = 0 charon.plugins.kernel-netlink.mss = 0
MSS to set on installed routes, 0 to disable. MSS to set on installed routes, 0 to disable.

View File

@ -195,6 +195,22 @@ int main(int argc, char *argv[])
lib->settings->set_default_str(lib->settings, "charon-nm.port", "0"); lib->settings->set_default_str(lib->settings, "charon-nm.port", "0");
lib->settings->set_default_str(lib->settings, "charon-nm.port_nat_t", "0"); lib->settings->set_default_str(lib->settings, "charon-nm.port_nat_t", "0");
/* install VIPs on lo as NM might modify the physical interface (this seems
* to affect IPv6 in particular), it actually installs the VIPs on the
* passed device again, but since that happens after we require them for
* installing routes, we install them ourselves too */
lib->settings->set_default_str(lib->settings,
"charon-nm.install_virtual_ip_on", "lo");
/* install routes via XFRM interfaces, if we can use them */
lib->settings->set_default_str(lib->settings,
"charon-nm.plugins.kernel-netlink.install_routes_xfrmi", "yes");
/* bypass IKE traffic from these routes in case traffic selectors conflict */
lib->settings->set_default_str(lib->settings,
"charon-nm.plugins.socket-default.fwmark", "220");
lib->settings->set_default_str(lib->settings,
"charon-nm.plugins.kernel-netlink.fwmark", "!220");
DBG1(DBG_DMN, "Starting charon NetworkManager backend (strongSwan "VERSION")"); DBG1(DBG_DMN, "Starting charon NetworkManager backend (strongSwan "VERSION")");
if (lib->integrity) if (lib->integrity)
{ {

View File

@ -1,7 +1,6 @@
/* /*
* Copyright (C) 2017 Lubomir Rintel * Copyright (C) 2017 Lubomir Rintel
* * Copyright (C) 2013-2023 Tobias Brunner
* Copyright (C) 2013-2020 Tobias Brunner
* Copyright (C) 2008-2009 Martin Willi * Copyright (C) 2008-2009 Martin Willi
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@ -15,6 +14,10 @@
* for more details. * for more details.
*/ */
#include <stdio.h>
#include <inttypes.h>
#include <net/if.h>
#include "nm_service.h" #include "nm_service.h"
#include <daemon.h> #include <daemon.h>
@ -23,8 +26,9 @@
#include <config/peer_cfg.h> #include <config/peer_cfg.h>
#include <credentials/certificates/x509.h> #include <credentials/certificates/x509.h>
#include <networking/tun_device.h> #include <networking/tun_device.h>
#include <plugins/kernel_netlink/kernel_netlink_xfrmi.h>
#include <stdio.h> #define XFRMI_DEFAULT_MTU 1400
/** /**
* Private data of NMStrongswanPlugin * Private data of NMStrongswanPlugin
@ -40,7 +44,13 @@ typedef struct {
nm_creds_t *creds; nm_creds_t *creds;
/* attribute handler for DNS/NBNS server information */ /* attribute handler for DNS/NBNS server information */
nm_handler_t *handler; nm_handler_t *handler;
/* dummy TUN device */ /* manager for XFRM interfaces, if supported */
kernel_netlink_xfrmi_t *xfrmi_manager;
/* interface ID of XFRM interface */
uint32_t xfrmi_id;
/* name of XFRM interface if one is used */
char *xfrmi;
/* dummy TUN device if not using XFRM interface */
tun_device_t *tun; tun_device_t *tun;
/* name of the connection */ /* name of the connection */
char *name; char *name;
@ -107,6 +117,24 @@ static GVariant* handler_to_variant(nm_handler_t *handler, char *variant_type,
return g_variant_builder_end (&builder); return g_variant_builder_end (&builder);
} }
/**
* Destroy any allocated XFRM or TUN interface
*/
static void delete_interface(NMStrongswanPluginPrivate *priv)
{
if (priv->xfrmi)
{
priv->xfrmi_manager->delete(priv->xfrmi_manager, priv->xfrmi);
free(priv->xfrmi);
priv->xfrmi = NULL;
}
if (priv->tun)
{
priv->tun->destroy(priv->tun);
priv->tun = NULL;
}
}
/** /**
* Signal IP config to NM, set connection as established * Signal IP config to NM, set connection as established
*/ */
@ -127,21 +155,54 @@ static void signal_ip_config(NMVpnServicePlugin *plugin,
handler = priv->handler; handler = priv->handler;
/* NM apparently requires to know the gateway */ /* NM apparently requires to know the gateway (it uses it to install a
* direct route via physical interface if conflicting routes are passed) */
other = ike_sa->get_other_host(ike_sa); other = ike_sa->get_other_host(ike_sa);
g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY,
host_to_variant(other)); host_to_variant(other));
/* systemd-resolved requires a device to properly install DNS servers, but /* systemd-resolved requires a device to properly install DNS servers, but
* Netkey does not use one. Passing the physical interface is not ideal, * Netkey does not require one. Passing the physical interface is not ideal,
* as NM fiddles around with it and systemd-resolved likes a separate * as NM fiddles around with it and systemd-resolved likes a separate
* device. So we pass a dummy TUN device along for NM etc. to play with... * device. So we pass either an XFRM interface or a dummy TUN device along
* for NM etc. to play with...
*/ */
if (priv->tun) delete_interface(priv);
if (priv->xfrmi_manager && priv->xfrmi_id)
{
char name[IFNAMSIZ];
int mtu;
/* use the interface ID to get a unique name, fine if it's cut off */
snprintf(name, sizeof(name), "nm-xfrm-%" PRIu32, priv->xfrmi_id);
mtu = lib->settings->get_int(lib->settings, "charon-nm.mtu",
XFRMI_DEFAULT_MTU);
if (priv->xfrmi_manager->create(priv->xfrmi_manager, name,
priv->xfrmi_id, NULL, mtu))
{
priv->xfrmi = strdup(name);
}
}
if (!priv->xfrmi)
{
priv->tun = tun_device_create(NULL);
}
if (priv->xfrmi)
{
g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_TUNDEV,
g_variant_new_string (priv->xfrmi));
}
else if (priv->tun)
{ {
g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_TUNDEV, g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_TUNDEV,
g_variant_new_string (priv->tun->get_name(priv->tun))); g_variant_new_string (priv->tun->get_name(priv->tun)));
} }
else
{
DBG1(DBG_CFG, "failed to create XFRM or dummy TUN device, might affect "
"DNS server installation negatively");
}
/* pass the first virtual IPs we got or use the physical IP */ /* pass the first virtual IPs we got or use the physical IP */
enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
@ -184,18 +245,16 @@ static void signal_ip_config(NMVpnServicePlugin *plugin,
host_to_variant(vip4)); host_to_variant(vip4));
g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX,
g_variant_new_uint32 (vip4->get_address(vip4).len * 8)); g_variant_new_uint32 (vip4->get_address(vip4).len * 8));
g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS,
handler_to_variant(handler, "au", INTERNAL_IP4_DNS));
g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NBNS,
handler_to_variant(handler, "au", INTERNAL_IP4_NBNS));
/* prevent NM from changing the default route. we set our own route in our /* prevent NM from changing the default route, as we set our own routes
* own routing table * in a separate routing table
*/ */
g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT,
g_variant_new_boolean (TRUE)); g_variant_new_boolean (TRUE));
g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS,
handler_to_variant(handler, "au", INTERNAL_IP4_DNS));
g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NBNS,
handler_to_variant(handler, "au", INTERNAL_IP4_NBNS));
} }
if (vip6) if (vip6)
@ -204,11 +263,12 @@ static void signal_ip_config(NMVpnServicePlugin *plugin,
host_to_variant(vip6)); host_to_variant(vip6));
g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PREFIX,
g_variant_new_uint32 (vip6->get_address(vip6).len * 8)); g_variant_new_uint32 (vip6->get_address(vip6).len * 8));
g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT,
g_variant_new_boolean (TRUE));
g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DNS, g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_DNS,
handler_to_variant(handler, "aay", INTERNAL_IP6_DNS)); handler_to_variant(handler, "aay", INTERNAL_IP6_DNS));
/* NM_VPN_PLUGIN_IP6_CONFIG_NBNS is not defined */ /* NM_VPN_PLUGIN_IP6_CONFIG_NBNS is not defined */
g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT,
g_variant_new_boolean (TRUE));
} }
ip4config = g_variant_builder_end (&ip4builder); ip4config = g_variant_builder_end (&ip4builder);
@ -646,6 +706,11 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
NM_TYPE_SETTING_CONNECTION)); NM_TYPE_SETTING_CONNECTION));
vpn = NM_SETTING_VPN(nm_connection_get_setting(connection, vpn = NM_SETTING_VPN(nm_connection_get_setting(connection,
NM_TYPE_SETTING_VPN)); NM_TYPE_SETTING_VPN));
if (priv->xfrmi_manager)
{
/* allocate a random interface ID */
priv->xfrmi_id = random();
}
if (priv->name) if (priv->name)
{ {
free(priv->name); free(priv->name);
@ -655,11 +720,6 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
priv->name); priv->name);
DBG4(DBG_CFG, "%s", DBG4(DBG_CFG, "%s",
nm_setting_to_string(NM_SETTING(vpn))); nm_setting_to_string(NM_SETTING(vpn)));
if (!priv->tun)
{
DBG1(DBG_CFG, "failed to create dummy TUN device, might affect DNS "
"server installation negatively");
}
ike.remote = (char*)nm_setting_vpn_get_data_item(vpn, "address"); ike.remote = (char*)nm_setting_vpn_get_data_item(vpn, "address");
if (!ike.remote || !*ike.remote) if (!ike.remote || !*ike.remote)
{ {
@ -989,7 +1049,7 @@ static gboolean do_disconnect(gpointer plugin)
NMStrongswanPluginPrivate *priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); NMStrongswanPluginPrivate *priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin);
enumerator_t *enumerator; enumerator_t *enumerator;
ike_sa_t *ike_sa; ike_sa_t *ike_sa;
u_int id; u_int id = 0;
/* our ike_sa pointer might be invalid, lookup sa */ /* our ike_sa pointer might be invalid, lookup sa */
enumerator = charon->controller->create_ike_sa_enumerator( enumerator = charon->controller->create_ike_sa_enumerator(
@ -999,20 +1059,27 @@ static gboolean do_disconnect(gpointer plugin)
if (priv->ike_sa == ike_sa) if (priv->ike_sa == ike_sa)
{ {
id = ike_sa->get_unique_id(ike_sa); id = ike_sa->get_unique_id(ike_sa);
enumerator->destroy(enumerator); break;
charon->controller->terminate_ike(charon->controller, id, FALSE,
controller_cb_empty, NULL, 0);
/* clear secrets as we are asked for new secrets (where we'd find
* the cached secrets from earlier connections) before we clear
* them in connect() */
priv->creds->clear(priv->creds);
return FALSE;
} }
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
g_debug("Connection not found."); if (id)
{
charon->controller->terminate_ike(charon->controller, id, FALSE,
controller_cb_empty, NULL, 0);
}
else
{
g_debug("Connection not found.");
}
/* clear secrets as we are asked for new secrets (where we'd find the cached
* secrets from earlier connections) before we clear them in connect() */
priv->creds->clear(priv->creds);
/* delete any allocated interface */
delete_interface(priv);
return FALSE; return FALSE;
} }
@ -1044,8 +1111,7 @@ static void nm_strongswan_plugin_init(NMStrongswanPlugin *plugin)
priv->listener.ike_reestablish_pre = _ike_reestablish_pre; priv->listener.ike_reestablish_pre = _ike_reestablish_pre;
priv->listener.ike_reestablish_post = _ike_reestablish_post; priv->listener.ike_reestablish_post = _ike_reestablish_post;
charon->bus->add_listener(charon->bus, &priv->listener); charon->bus->add_listener(charon->bus, &priv->listener);
priv->tun = tun_device_create(NULL); priv->xfrmi_manager = lib->get(lib, KERNEL_NETLINK_XFRMI_MANAGER);
priv->name = NULL;
} }
/** /**
@ -1058,11 +1124,7 @@ static void nm_strongswan_plugin_dispose(GObject *obj)
plugin = NM_STRONGSWAN_PLUGIN(obj); plugin = NM_STRONGSWAN_PLUGIN(obj);
priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin);
if (priv->tun) delete_interface(priv);
{
priv->tun->destroy(priv->tun);
priv->tun = NULL;
}
G_OBJECT_CLASS (nm_strongswan_plugin_parent_class)->dispose (obj); G_OBJECT_CLASS (nm_strongswan_plugin_parent_class)->dispose (obj);
} }

View File

@ -18,7 +18,8 @@ libstrongswan_kernel_netlink_la_SOURCES = \
kernel_netlink_plugin.h kernel_netlink_plugin.c \ kernel_netlink_plugin.h kernel_netlink_plugin.c \
kernel_netlink_ipsec.h kernel_netlink_ipsec.c \ kernel_netlink_ipsec.h kernel_netlink_ipsec.c \
kernel_netlink_net.h kernel_netlink_net.c \ kernel_netlink_net.h kernel_netlink_net.c \
kernel_netlink_shared.h kernel_netlink_shared.c kernel_netlink_shared.h kernel_netlink_shared.c \
kernel_netlink_xfrmi.h kernel_netlink_xfrmi.c
libstrongswan_kernel_netlink_la_LIBADD = $(DLLIB) libstrongswan_kernel_netlink_la_LIBADD = $(DLLIB)

View File

@ -63,6 +63,7 @@
#include "kernel_netlink_ipsec.h" #include "kernel_netlink_ipsec.h"
#include "kernel_netlink_shared.h" #include "kernel_netlink_shared.h"
#include "kernel_netlink_xfrmi.h"
#include <daemon.h> #include <daemon.h>
#include <utils/debug.h> #include <utils/debug.h>
@ -337,6 +338,11 @@ struct private_kernel_netlink_ipsec_t {
*/ */
netlink_socket_t *socket_xfrm; netlink_socket_t *socket_xfrm;
/**
* XFRM interface manager
*/
kernel_netlink_xfrmi_t *xfrmi;
/** /**
* Netlink xfrm socket to receive acquire and expire events * Netlink xfrm socket to receive acquire and expire events
*/ */
@ -352,6 +358,11 @@ struct private_kernel_netlink_ipsec_t {
*/ */
bool install_routes; bool install_routes;
/**
* Whether to install routes via XFRM interfaces
*/
bool install_routes_xfrmi;
/** /**
* Whether to set protocol and ports on selector installed with transport * Whether to set protocol and ports on selector installed with transport
* mode IPsec SAs * mode IPsec SAs
@ -2730,6 +2741,30 @@ static void policy_change_done(private_kernel_netlink_ipsec_t *this,
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
} }
/**
* Find an XFRM interface with the given ID
*/
static bool find_xfrmi(private_kernel_netlink_ipsec_t *this, uint32_t target,
char **if_name)
{
enumerator_t *enumerator;
char *name;
uint32_t if_id;
enumerator = this->xfrmi->create_enumerator(this->xfrmi);
while (enumerator->enumerate(enumerator, &name, &if_id, NULL, NULL))
{
if (if_id == target)
{
*if_name = strdup(name);
enumerator->destroy(enumerator);
return TRUE;
}
}
enumerator->destroy(enumerator);
return FALSE;
}
/** /**
* Install a route for the given policy if enabled and required * Install a route for the given policy if enabled and required
*/ */
@ -2759,9 +2794,15 @@ static void install_route(private_kernel_netlink_ipsec_t *this,
if (!ipsec->dst->is_anyaddr(ipsec->dst)) if (!ipsec->dst->is_anyaddr(ipsec->dst))
{ {
route->gateway = charon->kernel->get_nexthop(charon->kernel, /* if if_ids are used, install a route via XFRM interface if any,
ipsec->dst, -1, ipsec->src, * otherwise install the route via the interface we reach the peer */
&route->if_name); if (!policy->if_id || !this->xfrmi ||
!find_xfrmi(this, policy->if_id, &route->if_name))
{
route->gateway = charon->kernel->get_nexthop(charon->kernel,
ipsec->dst, -1, ipsec->src,
&route->if_name);
}
} }
else else
{ /* for shunt policies */ { /* for shunt policies */
@ -3000,12 +3041,12 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
* - this is an outbound policy (to just get one for each child) * - this is an outbound policy (to just get one for each child)
* - routing is not disabled via strongswan.conf * - routing is not disabled via strongswan.conf
* - the selector is not for a specific protocol/port * - the selector is not for a specific protocol/port
* - no XFRM interface ID is configured * - routes via XFRM interfaces are enabled or no interface ID is configured
* - we are in tunnel/BEET mode or install a bypass policy * - we are in tunnel/BEET mode or install a bypass policy
*/ */
if (policy->direction == POLICY_OUT && this->install_routes && if (policy->direction == POLICY_OUT && this->install_routes &&
!policy->sel.proto && !policy->sel.dport && !policy->sel.sport && !policy->sel.proto && !policy->sel.dport && !policy->sel.sport &&
!policy->if_id) (this->install_routes_xfrmi || !policy->if_id))
{ {
if (mapping->type == POLICY_PASS || if (mapping->type == POLICY_PASS ||
(mapping->type == POLICY_IPSEC && ipsec->cfg.mode != MODE_TRANSPORT)) (mapping->type == POLICY_IPSEC && ipsec->cfg.mode != MODE_TRANSPORT))
@ -3949,6 +3990,11 @@ METHOD(kernel_ipsec_t, destroy, void,
DESTROY_IF(this->socket_link_events); DESTROY_IF(this->socket_link_events);
DESTROY_IF(this->socket_xfrm_events); DESTROY_IF(this->socket_xfrm_events);
array_destroy_function(this->bypass, remove_port_bypass, this); array_destroy_function(this->bypass, remove_port_bypass, this);
if (this->xfrmi)
{
lib->set(lib, KERNEL_NETLINK_XFRMI_MANAGER, NULL);
kernel_netlink_xfrmi_destroy(this->xfrmi);
}
DESTROY_IF(this->socket_xfrm); DESTROY_IF(this->socket_xfrm);
enumerator = this->policies->create_enumerator(this->policies); enumerator = this->policies->create_enumerator(this->policies);
while (enumerator->enumerate(enumerator, NULL, &policy)) while (enumerator->enumerate(enumerator, NULL, &policy))
@ -4137,9 +4183,13 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
.get_priority = dlsym(RTLD_DEFAULT, .get_priority = dlsym(RTLD_DEFAULT,
"kernel_netlink_get_priority_custom"), "kernel_netlink_get_priority_custom"),
.policy_update = lib->settings->get_bool(lib->settings, .policy_update = lib->settings->get_bool(lib->settings,
"%s.plugins.kernel-netlink.policy_update", FALSE, lib->ns), "%s.plugins.kernel-netlink.policy_update",
FALSE, lib->ns),
.install_routes = lib->settings->get_bool(lib->settings, .install_routes = lib->settings->get_bool(lib->settings,
"%s.install_routes", TRUE, lib->ns), "%s.install_routes", TRUE, lib->ns),
.install_routes_xfrmi = lib->settings->get_bool(lib->settings,
"%s.plugins.kernel-netlink.install_routes_xfrmi",
FALSE, lib->ns),
.proto_port_transport = lib->settings->get_bool(lib->settings, .proto_port_transport = lib->settings->get_bool(lib->settings,
"%s.plugins.kernel-netlink.set_proto_port_transport_sa", "%s.plugins.kernel-netlink.set_proto_port_transport_sa",
FALSE, lib->ns), FALSE, lib->ns),
@ -4188,5 +4238,11 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
return NULL; return NULL;
} }
} }
this->xfrmi = kernel_netlink_xfrmi_create(TRUE);
if (this->xfrmi)
{
lib->set(lib, KERNEL_NETLINK_XFRMI_MANAGER, this->xfrmi);
}
return &this->public; return &this->public;
} }

View File

@ -0,0 +1,445 @@
/*
* Copyright (C) 2019-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.
*/
#include <net/if.h>
#include "kernel_netlink_xfrmi.h"
#include "kernel_netlink_shared.h"
#ifndef IFLA_XFRM_MAX
enum {
IFLA_XFRM_UNSPEC,
IFLA_XFRM_LINK,
IFLA_XFRM_IF_ID,
__IFLA_XFRM_MAX
};
#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1)
#endif
typedef struct private_kernel_netlink_xfrmi_t private_kernel_netlink_xfrmi_t;
/**
* Private data
*/
struct private_kernel_netlink_xfrmi_t {
/**
* Public interface
*/
kernel_netlink_xfrmi_t public;
/**
* Netlink socket
*/
netlink_socket_t *socket;
};
/**
* "up" the interface with the given name
*/
static bool interface_up(private_kernel_netlink_xfrmi_t *this, char *name)
{
netlink_buf_t request;
struct nlmsghdr *hdr;
struct ifinfomsg *msg;
memset(&request, 0, sizeof(request));
hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
hdr->nlmsg_type = RTM_SETLINK;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
msg = NLMSG_DATA(hdr);
msg->ifi_family = AF_UNSPEC;
msg->ifi_change |= IFF_UP;
msg->ifi_flags |= IFF_UP;
netlink_add_attribute(hdr, IFLA_IFNAME, chunk_from_str(name),
sizeof(request));
if (this->socket->send_ack(this->socket, hdr) != SUCCESS)
{
DBG1(DBG_KNL, "failed to bring up XFRM interface '%s'", name);
return FALSE;
}
return TRUE;
}
METHOD(kernel_netlink_xfrmi_t, create, bool,
private_kernel_netlink_xfrmi_t *this, char *name, uint32_t if_id,
char *phys, uint32_t mtu)
{
netlink_buf_t request;
struct nlmsghdr *hdr;
struct ifinfomsg *msg;
struct rtattr *linkinfo, *info_data;
uint32_t ifindex = 0;
if (phys)
{
ifindex = if_nametoindex(phys);
if (!ifindex)
{
DBG1(DBG_KNL, "physical interface '%s' not found", phys);
return FALSE;
}
}
memset(&request, 0, sizeof(request));
hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
hdr->nlmsg_type = RTM_NEWLINK;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
msg = NLMSG_DATA(hdr);
msg->ifi_family = AF_UNSPEC;
netlink_add_attribute(hdr, IFLA_IFNAME, chunk_from_str(name),
sizeof(request));
if (mtu)
{
netlink_add_attribute(hdr, IFLA_MTU, chunk_from_thing(mtu),
sizeof(request));
}
linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO);
netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"),
sizeof(request));
info_data = netlink_nested_start(hdr, sizeof(request), IFLA_INFO_DATA);
netlink_add_attribute(hdr, IFLA_XFRM_IF_ID, chunk_from_thing(if_id),
sizeof(request));
if (ifindex)
{
netlink_add_attribute(hdr, IFLA_XFRM_LINK, chunk_from_thing(ifindex),
sizeof(request));
}
netlink_nested_end(hdr, info_data);
netlink_nested_end(hdr, linkinfo);
switch (this->socket->send_ack(this->socket, hdr))
{
case SUCCESS:
return interface_up(this, name);
case ALREADY_DONE:
DBG1(DBG_KNL, "XFRM interface '%s' already exists", name);
break;
default:
DBG1(DBG_KNL, "failed to create XFRM interface '%s'", name);
break;
}
return FALSE;
}
/** enumerator over XFRM interfaces */
typedef struct {
/** public interface */
enumerator_t public;
/** message from the kernel */
struct nlmsghdr *msg;
/** current message from the kernel */
struct nlmsghdr *current;
/** remaining length */
size_t len;
/** current physical interface (if any) */
char phys[IFNAMSIZ];
} interface_enumerator_t;
METHOD(enumerator_t, destroy_enumerator, void,
interface_enumerator_t *this)
{
free(this->msg);
free(this);
}
/**
* Parse attributes nested in IFLA_INFO_DATA
*/
static void parse_info_data(struct rtattr *rta, size_t rtasize, bool *has_phys,
char *phys, uint32_t *if_id)
{
uint32_t ifindex;
while (RTA_OK(rta, rtasize))
{
switch (rta->rta_type)
{
case IFLA_XFRM_IF_ID:
if (RTA_PAYLOAD(rta) == sizeof(*if_id))
{
*if_id = *(uint32_t*)RTA_DATA(rta);
}
break;
case IFLA_XFRM_LINK:
if (RTA_PAYLOAD(rta) == sizeof(ifindex))
{
ifindex = *(uint32_t*)RTA_DATA(rta);
if (ifindex)
{
if_indextoname(ifindex, phys);
*has_phys = TRUE;
}
}
break;
default:
break;
}
rta = RTA_NEXT(rta, rtasize);
}
}
/**
* Parse attributes nested in IFLA_LINKINFO
*/
static void parse_linkinfo(struct rtattr *rta, size_t rtasize, bool *type_match,
bool *has_phys, char *phys, uint32_t *if_id)
{
while (RTA_OK(rta, rtasize))
{
switch (rta->rta_type)
{
case IFLA_INFO_KIND:
*type_match = streq("xfrm", RTA_DATA(rta));
break;
case IFLA_INFO_DATA:
parse_info_data(RTA_DATA(rta), RTA_PAYLOAD(rta), has_phys,
phys, if_id);
break;
default:
break;
}
rta = RTA_NEXT(rta, rtasize);
}
}
METHOD(enumerator_t, enumerate, bool,
interface_enumerator_t *this, va_list args)
{
char **name;
uint32_t *if_id, *mtu;
char **phys;
VA_ARGS_VGET(args, name, if_id, phys, mtu);
if (!this->current)
{
this->current = this->msg;
}
else
{
this->current = NLMSG_NEXT(this->current, this->len);
}
while (NLMSG_OK(this->current, this->len))
{
switch (this->current->nlmsg_type)
{
case NLMSG_DONE:
break;
case RTM_NEWLINK:
{
struct ifinfomsg *msg = NLMSG_DATA(this->current);
struct rtattr *rta = IFLA_RTA(msg);
size_t rtasize = IFLA_PAYLOAD(this->current);
bool type_match = FALSE, has_phys = FALSE;
*name = NULL;
while (RTA_OK(rta, rtasize))
{
switch (rta->rta_type)
{
case IFLA_IFNAME:
*name = RTA_DATA(rta);
break;
case IFLA_MTU:
if (mtu && RTA_PAYLOAD(rta) == sizeof(*mtu))
{
*mtu = *(uint32_t*)RTA_DATA(rta);
}
break;
case IFLA_LINKINFO:
parse_linkinfo(RTA_DATA(rta), RTA_PAYLOAD(rta),
&type_match, &has_phys, this->phys,
if_id);
break;
default:
break;
}
rta = RTA_NEXT(rta, rtasize);
}
if (*name && type_match)
{
if (phys)
{
*phys = has_phys ? this->phys : NULL;
}
return TRUE;
}
/* fall-through */
}
default:
this->current = NLMSG_NEXT(this->current, this->len);
continue;
}
break;
}
return FALSE;
}
METHOD(kernel_netlink_xfrmi_t, create_enumerator, enumerator_t*,
private_kernel_netlink_xfrmi_t *this)
{
netlink_buf_t request;
struct nlmsghdr *hdr, *out;
struct ifinfomsg *msg;
struct rtattr *linkinfo;
size_t len;
interface_enumerator_t *enumerator;
memset(&request, 0, sizeof(request));
hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
hdr->nlmsg_type = RTM_GETLINK;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
msg = NLMSG_DATA(hdr);
msg->ifi_family = AF_UNSPEC;
/* if the kernel doesn't know the type we set here, it will just return all
* interfaces, so we filter the type ourselves too in the callback */
linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO);
netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"),
sizeof(request));
netlink_nested_end(hdr, linkinfo);
if (this->socket->send(this->socket, hdr, &out, &len) != SUCCESS)
{
DBG2(DBG_KNL, "enumerating XFRM interfaces failed");
return enumerator_create_empty();
}
INIT(enumerator,
.public = {
.enumerate = enumerator_enumerate_default,
.venumerate = _enumerate,
.destroy = _destroy_enumerator,
},
.msg = out,
.len = len,
);
return &enumerator->public;
}
METHOD(kernel_netlink_xfrmi_t, delete, bool,
private_kernel_netlink_xfrmi_t *this, char *name)
{
netlink_buf_t request;
struct nlmsghdr *hdr;
struct ifinfomsg *msg;
struct rtattr *linkinfo;
memset(&request, 0, sizeof(request));
hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
hdr->nlmsg_type = RTM_DELLINK;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
msg = NLMSG_DATA(hdr);
msg->ifi_family = AF_UNSPEC;
netlink_add_attribute(hdr, IFLA_IFNAME, chunk_from_str(name),
sizeof(request));
/* the type doesn't seem to matter, but let's still set it */
linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO);
netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"),
sizeof(request));
netlink_nested_end(hdr, linkinfo);
switch (this->socket->send_ack(this->socket, hdr))
{
case SUCCESS:
return TRUE;
case NOT_FOUND:
DBG1(DBG_KNL, "XFRM interface '%s' not found to delete", name);
break;
default:
DBG1(DBG_KNL, "failed to delete XFRM interface '%s'", name);
break;
}
return FALSE;
}
void kernel_netlink_xfrmi_destroy(kernel_netlink_xfrmi_t *pub)
{
private_kernel_netlink_xfrmi_t *this = (private_kernel_netlink_xfrmi_t*)pub;
this->socket->destroy(this->socket);
free(this);
}
/*
* Described in header
*/
kernel_netlink_xfrmi_t *kernel_netlink_xfrmi_create(bool test)
{
private_kernel_netlink_xfrmi_t *this;
char name[IFNAMSIZ] = {};
uint32_t if_id;
INIT(this,
.public = {
.create = _create,
.create_enumerator = _create_enumerator,
.delete = _delete,
},
.socket = netlink_socket_create(NETLINK_ROUTE, NULL, FALSE),
);
if (!this->socket)
{
free(this);
return NULL;
}
if (test)
{
/* try to create an XFRM interface to see if the kernel supports it, use
* a random ID and name for the test to avoid conflicts */
if_id = random();
snprintf(name, sizeof(name), "xfrmi-test-%u", if_id);
if (!create(this, name, if_id, NULL, 0))
{
kernel_netlink_xfrmi_destroy(&this->public);
return NULL;
}
DBG2(DBG_KNL, "XFRM interfaces supported by kernel");
delete(this, name);
}
return &this->public;
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2022-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_netlink_xfrmi kernel_netlink_xfrmi
* @{ @ingroup kernel_netlink
*/
#ifndef KERNEL_NETLINK_XFRMI_H_
#define KERNEL_NETLINK_XFRMI_H_
#include <library.h>
#define KERNEL_NETLINK_XFRMI_MANAGER "kernel-netlink-xfrmi"
typedef struct kernel_netlink_xfrmi_t kernel_netlink_xfrmi_t;
/**
* Simple manager for XFRM interfaces. An instance can be retrieved via
* lib::get() under the key "kernel-netlink-xfrmi" if the kernel-netlink plugin
* is loaded and XFRM interfaces are supported by the kernel.
*/
struct kernel_netlink_xfrmi_t {
/**
* Creates an XFRM interface with the given name, interface ID and
* optional underlying physical interface and MTU.
*
* @param name name of the XFRM interface
* @param if_id interface ID (has to match SAs/policies)
* @param phys name of the underlying physical interface (optional)
* @param mtu MTU of the interface (optional, 0 for default)
* @return TRUE if interface was successfully created
*/
bool (*create)(kernel_netlink_xfrmi_t *this, char *name, uint32_t if_id,
char *phys, uint32_t mtu);
/**
* Enumerate existing XFRM interfaces.
*
* @return enumerator over (char *name, uint32_t if_id,
* char *phys, u_int mtu)
*/
enumerator_t *(*create_enumerator)(kernel_netlink_xfrmi_t *this);
/**
* Deletes the XFRM interface with the given name.
*
* @note This deletes any type of interface with the given name.
*
* @param name name of the XFRM interface
* @return TRUE if interface was successfully deleted
*/
bool (*delete)(kernel_netlink_xfrmi_t *this, char *name);
};
/**
* Create the manager.
*
* @param test test if XFRM interfaces can be created (requires CAP_NET_ADMIN)
* @return kernel_netlink_xfrmi_t instance, or NULL if test failed
*/
kernel_netlink_xfrmi_t *kernel_netlink_xfrmi_create(bool test);
/**
* Destroy the given manager. Not a method in the interface above to prevent
* users from destroying the manager.
*/
void kernel_netlink_xfrmi_destroy(kernel_netlink_xfrmi_t *this);
#endif /** KERNEL_NETLINK_XFRMI_H_ @}*/

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2019 Tobias Brunner * Copyright (C) 2019-2023 Tobias Brunner
* *
* Copyright (C) secunet Security Networks AG * Copyright (C) secunet Security Networks AG
* *
@ -19,222 +19,46 @@
#include <getopt.h> #include <getopt.h>
#include <errno.h> #include <errno.h>
#include <net/if.h> #include <net/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include "kernel_netlink_shared.h" #include "kernel_netlink_xfrmi.h"
#ifndef IFLA_XFRM_MAX
enum {
IFLA_XFRM_UNSPEC,
IFLA_XFRM_LINK,
IFLA_XFRM_IF_ID,
__IFLA_XFRM_MAX
};
#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1)
#endif
/** /**
* Create an XFRM interface with the given ID and underlying interface * Default MTU
*/ */
static int add_xfrm_interface(char *name, uint32_t xfrm_id, uint32_t ifindex) #define XFRMI_DEFAULT_MTU 1400
{
netlink_buf_t request;
struct nlmsghdr *hdr;
struct ifinfomsg *msg;
struct rtattr *linkinfo, *info_data;
netlink_socket_t *socket;
int status = 1;
socket = netlink_socket_create(NETLINK_ROUTE, NULL, FALSE);
if (!socket)
{
return 1;
}
memset(&request, 0, sizeof(request));
hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
hdr->nlmsg_type = RTM_NEWLINK;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
msg = NLMSG_DATA(hdr);
msg->ifi_family = AF_UNSPEC;
netlink_add_attribute(hdr, IFLA_IFNAME, chunk_from_str(name),
sizeof(request));
linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO);
netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"),
sizeof(request));
info_data = netlink_nested_start(hdr, sizeof(request), IFLA_INFO_DATA);
netlink_add_attribute(hdr, IFLA_XFRM_IF_ID, chunk_from_thing(xfrm_id),
sizeof(request));
netlink_add_attribute(hdr, IFLA_XFRM_LINK, chunk_from_thing(ifindex),
sizeof(request));
netlink_nested_end(hdr, info_data);
netlink_nested_end(hdr, linkinfo);
switch (socket->send_ack(socket, hdr))
{
case SUCCESS:
status = 0;
break;
case ALREADY_DONE:
fprintf(stderr, "XFRM interface already exists\n");
break;
default:
fprintf(stderr, "failed to create XFRM interface\n");
break;
}
socket->destroy(socket);
return status;
}
/** /**
* Parse attributes nested in IFLA_INFO_DATA * Manager for XFRM interfaces
*/ */
static void parse_info_data(struct rtattr *rta, size_t rtasize, char *phys, static kernel_netlink_xfrmi_t *manager;
uint32_t *if_id)
{
uint32_t ifindex;
while (RTA_OK(rta, rtasize))
{
switch (rta->rta_type)
{
case IFLA_XFRM_IF_ID:
if (RTA_PAYLOAD(rta) == sizeof(*if_id))
{
*if_id = *(uint32_t*)RTA_DATA(rta);
}
break;
case IFLA_XFRM_LINK:
if (RTA_PAYLOAD(rta) == sizeof(ifindex))
{
ifindex = *(uint32_t*)RTA_DATA(rta);
if_indextoname(ifindex, phys);
}
break;
default:
break;
}
rta = RTA_NEXT(rta, rtasize);
}
}
/** /**
* Parse attributes nested in IFLA_LINKINFO * Destroy the allocated manager
*/ */
static void parse_linkinfo(struct rtattr *rta, size_t rtasize, char *phys, static void destroy_manager()
uint32_t *if_id)
{ {
while (RTA_OK(rta, rtasize)) if (manager)
{ {
switch (rta->rta_type) kernel_netlink_xfrmi_destroy(manager);
{
case IFLA_INFO_DATA:
parse_info_data(RTA_DATA(rta), RTA_PAYLOAD(rta), phys, if_id);
break;
default:
break;
}
rta = RTA_NEXT(rta, rtasize);
} }
} }
/** /**
* List all installed XFRM interfaces * List all installed XFRM interfaces
*/ */
static int list_xfrm_interfaces() static void list_xfrm_interfaces(kernel_netlink_xfrmi_t *manager)
{ {
netlink_buf_t request; enumerator_t *enumerator;
struct nlmsghdr *hdr, *out, *current; char *name, *dev;
struct ifinfomsg *msg; uint32_t xfrm_id, mtu;
struct rtattr *linkinfo;
netlink_socket_t *socket;
size_t len;
int status = 0;
socket = netlink_socket_create(NETLINK_ROUTE, NULL, FALSE); enumerator = manager->create_enumerator(manager);
if (!socket) while (enumerator->enumerate(enumerator, &name, &xfrm_id, &dev, &mtu))
{ {
return 1; printf("%2u: %-16s dev %-12s if_id 0x%.8x [%10u] mtu %u\n",
if_nametoindex(name), name, dev ?: "-", xfrm_id, xfrm_id, mtu);
} }
enumerator->destroy(enumerator);
memset(&request, 0, sizeof(request));
hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
hdr->nlmsg_type = RTM_GETLINK;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
msg = NLMSG_DATA(hdr);
msg->ifi_family = AF_UNSPEC;
linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO);
netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"),
sizeof(request));
netlink_nested_end(hdr, linkinfo);
if (socket->send(socket, hdr, &out, &len) != SUCCESS)
{
return FAILED;
}
current = out;
while (NLMSG_OK(current, len))
{
switch (current->nlmsg_type)
{
case NLMSG_DONE:
break;
case RTM_NEWLINK:
msg = NLMSG_DATA(current);
struct rtattr *rta = IFLA_RTA(msg);
size_t rtasize = IFLA_PAYLOAD(current);
char *name = NULL, phys[IF_NAMESIZE] = {};
uint32_t if_id = 0;
while (RTA_OK(rta, rtasize))
{
switch (rta->rta_type)
{
case IFLA_IFNAME:
name = RTA_DATA(rta);
break;
case IFLA_LINKINFO:
parse_linkinfo(RTA_DATA(rta), RTA_PAYLOAD(rta),
phys, &if_id);
break;
default:
break;
}
rta = RTA_NEXT(rta, rtasize);
}
if (name)
{
printf("%2u: %-16s dev %-8s if_id 0x%.8x [%u]\n",
msg->ifi_index, name, phys, if_id, if_id);
}
/* fall through */
default:
current = NLMSG_NEXT(current, len);
continue;
}
break;
}
free(out);
socket->destroy(socket);
return status;
} }
static void usage(FILE *out, char *name) static void usage(FILE *out, char *name)
@ -247,19 +71,22 @@ static void usage(FILE *out, char *name)
fprintf(out, " -l, --list list XFRM interfaces.\n"); fprintf(out, " -l, --list list XFRM interfaces.\n");
fprintf(out, " -n, --name=NAME name of the XFRM interface.\n"); fprintf(out, " -n, --name=NAME name of the XFRM interface.\n");
fprintf(out, " -i, --id=ID optional numeric XFRM ID.\n"); fprintf(out, " -i, --id=ID optional numeric XFRM ID.\n");
fprintf(out, " -d, --dev=DEVICE underlying physical interface.\n"); fprintf(out, " -d, --dev=DEVICE optional underlying physical interface.\n");
fprintf(out, " -m, --mtu=MTU optional MTU, default: 1400 (use 0 for kernel default).\n");
fprintf(out, "\n"); fprintf(out, "\n");
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
char *name = NULL, *dev = NULL, *end; char *name = NULL, *dev = NULL, *end;
uint32_t xfrm_id = 0; uint32_t xfrm_id = 0, mtu = XFRMI_DEFAULT_MTU;
u_int ifindex;
library_init(NULL, "xfrmi"); library_init(NULL, "xfrmi");
atexit(library_deinit); atexit(library_deinit);
manager = kernel_netlink_xfrmi_create(FALSE);
atexit(destroy_manager);
while (true) while (true)
{ {
struct option long_opts[] = { struct option long_opts[] = {
@ -269,9 +96,10 @@ int main(int argc, char *argv[])
{"name", required_argument, NULL, 'n' }, {"name", required_argument, NULL, 'n' },
{"id", required_argument, NULL, 'i' }, {"id", required_argument, NULL, 'i' },
{"dev", required_argument, NULL, 'd' }, {"dev", required_argument, NULL, 'd' },
{"mtu", required_argument, NULL, 'm' },
{0,0,0,0 }, {0,0,0,0 },
}; };
switch (getopt_long(argc, argv, "hvln:i:d:", long_opts, NULL)) switch (getopt_long(argc, argv, "hvln:i:d:m:", long_opts, NULL))
{ {
case EOF: case EOF:
break; break;
@ -279,7 +107,7 @@ int main(int argc, char *argv[])
usage(stdout, argv[0]); usage(stdout, argv[0]);
return 0; return 0;
case 'l': case 'l':
list_xfrm_interfaces(); list_xfrm_interfaces(manager);
return 0; return 0;
case 'v': case 'v':
dbg_default_set_level(atoi(optarg)); dbg_default_set_level(atoi(optarg));
@ -300,24 +128,26 @@ int main(int argc, char *argv[])
case 'd': case 'd':
dev = optarg; dev = optarg;
continue; continue;
case 'm':
errno = 0;
mtu = strtoul(optarg, &end, 0);
if (errno || *end)
{
fprintf(stderr, "invalid MTU: %s\n",
errno ? strerror(errno) : end);
return 1;
}
continue;
default: default:
usage(stderr, argv[0]); usage(stderr, argv[0]);
return 1; return 1;
} }
break; break;
} }
if (!name)
if (!name || !dev)
{ {
fprintf(stderr, "please specify a name and a physical interface\n"); fprintf(stderr, "please specify a name\n");
return 1; return 1;
} }
ifindex = if_nametoindex(dev); return !manager->create(manager, name, xfrm_id, dev, mtu);
if (!ifindex)
{
fprintf(stderr, "physical interface %s not found\n", dev);
return 1;
}
return add_xfrm_interface(name, xfrm_id, ifindex);
} }

View File

@ -27,10 +27,10 @@ def handle_interfaces(ike_sa, up):
if up: if up:
logger.info("add XFRM interfaces %s and %s", ifname_in, ifname_out) logger.info("add XFRM interfaces %s and %s", ifname_in, ifname_out)
subprocess.call(["/usr/local/libexec/ipsec/xfrmi", "-n", ifname_out, subprocess.call(["ip", "link", "add", ifname_out, "type", "xfrm",
"-i", str(if_id_out), "-d", "eth0"]) "if_id", str(if_id_out), "dev", "eth0"])
subprocess.call(["/usr/local/libexec/ipsec/xfrmi", "-n", ifname_in, subprocess.call(["ip", "link", "add", ifname_in, "type", "xfrm",
"-i", str(if_id_in), "-d", "eth0"]) "if_id", str(if_id_in), "dev", "eth0"])
subprocess.call(["ip", "link", "set", ifname_out, "up"]) subprocess.call(["ip", "link", "set", ifname_out, "up"])
subprocess.call(["ip", "link", "set", ifname_in, "up"]) subprocess.call(["ip", "link", "set", ifname_in, "up"])
subprocess.call(["iptables", "-A", "FORWARD", "-o", ifname_out, subprocess.call(["iptables", "-A", "FORWARD", "-o", ifname_out,

View File

@ -1,7 +1,7 @@
moon::iptables-restore < /etc/iptables.rules moon::iptables-restore < /etc/iptables.rules
sun::iptables-restore < /etc/iptables.rules sun::iptables-restore < /etc/iptables.rules
moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-out -d eth0 -i 1337 moon::ip link add xfrm-moon-out type xfrm dev eth0 if_id 1337
moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-in -d eth0 -i 42 moon::ip link add xfrm-moon-in type xfrm dev eth0 if_id 42
moon::ip link set xfrm-moon-out up moon::ip link set xfrm-moon-out up
moon::ip link set xfrm-moon-in up moon::ip link set xfrm-moon-in up
moon::ip route add 10.2.0.0/16 dev xfrm-moon-out moon::ip route add 10.2.0.0/16 dev xfrm-moon-out

View File

@ -4,7 +4,7 @@ IF_NAME="xfrmi-${PLUTO_IF_ID_IN}"
case "${PLUTO_VERB}" in case "${PLUTO_VERB}" in
up-client) up-client)
/usr/local/libexec/ipsec/xfrmi -n "${IF_NAME}" -i "${PLUTO_IF_ID_IN}" -d eth0 ip link add "${IF_NAME}" type xfrm if_id "${PLUTO_IF_ID_IN}" dev eth0
ip link set "${IF_NAME}" up ip link set "${IF_NAME}" up
ip route add 10.1.0.0/16 dev "${IF_NAME}" ip route add 10.1.0.0/16 dev "${IF_NAME}"
iptables -A FORWARD -i "${IF_NAME}" -j ACCEPT iptables -A FORWARD -i "${IF_NAME}" -j ACCEPT

View File

@ -1,6 +1,6 @@
moon::iptables-restore < /etc/iptables.rules moon::iptables-restore < /etc/iptables.rules
sun::iptables-restore < /etc/iptables.rules sun::iptables-restore < /etc/iptables.rules
moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon -i 42 -d eth0 moon::ip link add xfrm-moon type xfrm if_id 42 dev eth0
moon::ip link set xfrm-moon up moon::ip link set xfrm-moon up
moon::ip route add 10.2.0.0/16 dev xfrm-moon moon::ip route add 10.2.0.0/16 dev xfrm-moon
moon::iptables -A FORWARD -i xfrm-moon -j ACCEPT moon::iptables -A FORWARD -i xfrm-moon -j ACCEPT

View File

@ -6,8 +6,8 @@ IF_NAME_OUT="${IF_NAME}${PLUTO_IF_ID_OUT}-out"
case "${PLUTO_VERB}" in case "${PLUTO_VERB}" in
up-client) up-client)
/usr/local/libexec/ipsec/xfrmi -n "${IF_NAME_OUT}" -i "${PLUTO_IF_ID_OUT}" -d eth0 ip link add "${IF_NAME_OUT}" type xfrm if_id "${PLUTO_IF_ID_OUT}" dev eth0
/usr/local/libexec/ipsec/xfrmi -n "${IF_NAME_IN}" -i "${PLUTO_IF_ID_IN}" -d eth0 ip link add "${IF_NAME_IN}" type xfrm if_id "${PLUTO_IF_ID_IN}" dev eth0
ip link set "${IF_NAME_OUT}" up ip link set "${IF_NAME_OUT}" up
ip link set "${IF_NAME_IN}" up ip link set "${IF_NAME_IN}" up
ip route add 10.1.0.0/16 dev "${IF_NAME_OUT}" ip route add 10.1.0.0/16 dev "${IF_NAME_OUT}"

View File

@ -1,7 +1,7 @@
moon::iptables-restore < /etc/iptables.rules moon::iptables-restore < /etc/iptables.rules
sun::iptables-restore < /etc/iptables.rules sun::iptables-restore < /etc/iptables.rules
moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-out -d eth0 -i 1337 moon::ip link add xfrm-moon-out type xfrm dev eth0 if_id 1337
moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-in -d eth0 -i 42 moon::ip link add xfrm-moon-in type xfrm dev eth0 if_id 42
moon::ip link set xfrm-moon-out up moon::ip link set xfrm-moon-out up
moon::ip link set xfrm-moon-in up moon::ip link set xfrm-moon-in up
moon::ip route add 10.2.0.0/16 dev xfrm-moon-out moon::ip route add 10.2.0.0/16 dev xfrm-moon-out

View File

@ -1,7 +1,7 @@
moon::iptables-restore < /etc/iptables.rules moon::iptables-restore < /etc/iptables.rules
carol::iptables-restore < /etc/iptables.rules carol::iptables-restore < /etc/iptables.rules
dave::iptables-restore < /etc/iptables.rules dave::iptables-restore < /etc/iptables.rules
moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon -i 42 -d eth0 moon::ip link add xfrm-moon type xfrm if_id 42 dev eth0
moon::ip link set xfrm-moon up moon::ip link set xfrm-moon up
moon::ip route add 10.3.0.0/28 dev xfrm-moon moon::ip route add 10.3.0.0/28 dev xfrm-moon
moon::iptables -A FORWARD -i xfrm-moon -j ACCEPT moon::iptables -A FORWARD -i xfrm-moon -j ACCEPT