Merge branch 'nm-ipv6'

Adds support for IPv6 to the NetworkManager backend and plugin.

Fixes #1143, #2586.
This commit is contained in:
Tobias Brunner 2019-03-14 13:46:33 +01:00
commit 4f8c00e3ed
5 changed files with 203 additions and 81 deletions

View File

@ -1,4 +1,5 @@
/* /*
* Copyright (C) 2016 Tobias Brunner
* Copyright (C) 2009 Martin Willi * Copyright (C) 2009 Martin Willi
* HSR Hochschule fuer Technik Rapperswil * HSR Hochschule fuer Technik Rapperswil
* *
@ -16,6 +17,7 @@
#include "nm_handler.h" #include "nm_handler.h"
#include <daemon.h> #include <daemon.h>
#include <collections/array.h>
typedef struct private_nm_handler_t private_nm_handler_t; typedef struct private_nm_handler_t private_nm_handler_t;
@ -30,38 +32,56 @@ struct private_nm_handler_t {
nm_handler_t public; nm_handler_t public;
/** /**
* list of received DNS server attributes, pointer to 4 byte data * Received DNS server attributes, chunk_t
*/ */
linked_list_t *dns; array_t *dns;
/** /**
* list of received NBNS server attributes, pointer to 4 byte data * Received IPv6 DNS server attributes, chunk_t
*/ */
linked_list_t *nbns; array_t *dns6;
/**
* Received NBNS server attributes, chunk_t
*/
array_t *nbns;
}; };
METHOD(attribute_handler_t, handle, bool, METHOD(attribute_handler_t, handle, bool,
private_nm_handler_t *this, ike_sa_t *ike_sa, private_nm_handler_t *this, ike_sa_t *ike_sa,
configuration_attribute_type_t type, chunk_t data) configuration_attribute_type_t type, chunk_t data)
{ {
linked_list_t *list; array_t *list;
switch (type) switch (type)
{ {
case INTERNAL_IP4_DNS: case INTERNAL_IP4_DNS:
list = this->dns; list = this->dns;
break; break;
case INTERNAL_IP6_DNS:
list = this->dns6;
break;
case INTERNAL_IP4_NBNS: case INTERNAL_IP4_NBNS:
list = this->nbns; list = this->nbns;
break; break;
default: default:
return FALSE; return FALSE;
} }
if (data.len != 4) data = chunk_clone(data);
{ array_insert(list, ARRAY_TAIL, &data);
return FALSE; return TRUE;
} }
list->insert_last(list, chunk_clone(data).ptr);
METHOD(enumerator_t, enumerate_dns6, bool,
enumerator_t *this, va_list args)
{
configuration_attribute_type_t *type;
chunk_t *data;
VA_ARGS_VGET(args, type, data);
*type = INTERNAL_IP6_DNS;
*data = chunk_empty;
this->venumerate = (void*)return_false;
return TRUE; return TRUE;
} }
@ -74,7 +94,8 @@ METHOD(enumerator_t, enumerate_nbns, bool,
VA_ARGS_VGET(args, type, data); VA_ARGS_VGET(args, type, data);
*type = INTERNAL_IP4_NBNS; *type = INTERNAL_IP4_NBNS;
*data = chunk_empty; *data = chunk_empty;
this->venumerate = (void*)return_false; /* enumerate IPv6 DNS server as next attribute ... */
this->venumerate = _enumerate_dns6;
return TRUE; return TRUE;
} }
@ -113,54 +134,44 @@ METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*,
return enumerator_create_empty(); return enumerator_create_empty();
} }
CALLBACK(filter_chunks, bool,
void *null, enumerator_t *orig, va_list args)
{
chunk_t *out;
char *ptr;
VA_ARGS_VGET(args, out);
if (orig->enumerate(orig, &ptr))
{
*out = chunk_create(ptr, 4);
return TRUE;
}
return FALSE;
}
METHOD(nm_handler_t, create_enumerator, enumerator_t*, METHOD(nm_handler_t, create_enumerator, enumerator_t*,
private_nm_handler_t *this, configuration_attribute_type_t type) private_nm_handler_t *this, configuration_attribute_type_t type)
{ {
linked_list_t *list; array_t *list;
switch (type) switch (type)
{ {
case INTERNAL_IP4_DNS: case INTERNAL_IP4_DNS:
list = this->dns; list = this->dns;
break; break;
case INTERNAL_IP6_DNS:
list = this->dns6;
break;
case INTERNAL_IP4_NBNS: case INTERNAL_IP4_NBNS:
list = this->nbns; list = this->nbns;
break; break;
default: default:
return enumerator_create_empty(); return enumerator_create_empty();
} }
return enumerator_create_filter(list->create_enumerator(list), return array_create_enumerator(list);
filter_chunks, NULL, NULL);
} }
METHOD(nm_handler_t, reset, void, METHOD(nm_handler_t, reset, void,
private_nm_handler_t *this) private_nm_handler_t *this)
{ {
void *data; chunk_t chunk;
while (this->dns->remove_last(this->dns, (void**)&data) == SUCCESS) while (array_remove(this->dns, ARRAY_TAIL, &chunk))
{ {
free(data); chunk_free(&chunk);
} }
while (this->nbns->remove_last(this->nbns, (void**)&data) == SUCCESS) while (array_remove(this->dns6, ARRAY_TAIL, &chunk))
{ {
free(data); chunk_free(&chunk);
}
while (array_remove(this->nbns, ARRAY_TAIL, &chunk))
{
chunk_free(&chunk);
} }
} }
@ -168,8 +179,9 @@ METHOD(nm_handler_t, destroy, void,
private_nm_handler_t *this) private_nm_handler_t *this)
{ {
reset(this); reset(this);
this->dns->destroy(this->dns); array_destroy(this->dns);
this->nbns->destroy(this->nbns); array_destroy(this->dns6);
array_destroy(this->nbns);
free(this); free(this);
} }
@ -191,8 +203,9 @@ nm_handler_t *nm_handler_create()
.reset = _reset, .reset = _reset,
.destroy = _destroy, .destroy = _destroy,
}, },
.dns = linked_list_create(), .dns = array_create(sizeof(chunk_t), 0),
.nbns = linked_list_create(), .dns6 = array_create(sizeof(chunk_t), 0),
.nbns = array_create(sizeof(chunk_t), 0),
); );
return &this->public; return &this->public;

View File

@ -39,10 +39,11 @@ struct nm_handler_t {
* Create an enumerator over received attributes of a given kind. * Create an enumerator over received attributes of a given kind.
* *
* @param type type of attributes to enumerate * @param type type of attributes to enumerate
* @return enumerator over attribute data (chunk_t) * @return enumerator over attribute data (chunk_t*)
*/ */
enumerator_t* (*create_enumerator)(nm_handler_t *this, enumerator_t* (*create_enumerator)(nm_handler_t *this,
configuration_attribute_type_t type); configuration_attribute_type_t type);
/** /**
* Reset state, flush all received attributes. * Reset state, flush all received attributes.
*/ */

View File

@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2017 Lubomir Rintel * Copyright (C) 2017 Lubomir Rintel
* *
* Copyright (C) 2013 Tobias Brunner * Copyright (C) 2013-2019 Tobias Brunner
* Copyright (C) 2008-2009 Martin Willi * Copyright (C) 2008-2009 Martin Willi
* HSR Hochschule fuer Technik Rapperswil * HSR Hochschule fuer Technik Rapperswil
* *
@ -51,21 +51,54 @@ typedef struct {
NM_TYPE_STRONGSWAN_PLUGIN, NMStrongswanPluginPrivate)) NM_TYPE_STRONGSWAN_PLUGIN, NMStrongswanPluginPrivate))
/** /**
* convert enumerated handler chunks to a UINT_ARRAY GValue * Convert an address chunk to a GValue
*/ */
static GVariant* handler_to_variant(nm_handler_t *handler, static GVariant *addr_to_variant(chunk_t addr)
{
GVariantBuilder builder;
int i;
switch (addr.len)
{
case 4:
return g_variant_new_uint32 (*(uint32_t*)addr.ptr);
case 16:
g_variant_builder_init (&builder, G_VARIANT_TYPE ("ay"));
for (i = 0; i < addr.len; i++)
{
g_variant_builder_add (&builder, "y", addr.ptr[i]);
}
return g_variant_builder_end (&builder);
default:
return NULL;
}
}
/**
* Convert a host to a GValue
*/
static GVariant *host_to_variant(host_t *host)
{
return addr_to_variant(host->get_address(host));
}
/**
* Convert enumerated handler chunks to a GValue
*/
static GVariant* handler_to_variant(nm_handler_t *handler, char *variant_type,
configuration_attribute_type_t type) configuration_attribute_type_t type)
{ {
GVariantBuilder builder; GVariantBuilder builder;
enumerator_t *enumerator; enumerator_t *enumerator;
chunk_t chunk; chunk_t *chunk;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("au")); g_variant_builder_init (&builder, G_VARIANT_TYPE (variant_type));
enumerator = handler->create_enumerator(handler, type); enumerator = handler->create_enumerator(handler, type);
while (enumerator->enumerate(enumerator, &chunk)) while (enumerator->enumerate(enumerator, &chunk))
{ {
g_variant_builder_add (&builder, "u", *(uint32_t*)chunk.ptr); g_variant_builder_add_value (&builder, addr_to_variant(*chunk));
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
@ -73,57 +106,132 @@ static GVariant* handler_to_variant(nm_handler_t *handler,
} }
/** /**
* signal IPv4 config to NM, set connection as established * Signal IP config to NM, set connection as established
*/ */
static void signal_ipv4_config(NMVpnServicePlugin *plugin, static void signal_ip_config(NMVpnServicePlugin *plugin,
ike_sa_t *ike_sa, child_sa_t *child_sa) ike_sa_t *ike_sa, child_sa_t *child_sa)
{ {
NMStrongswanPluginPrivate *priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin); NMStrongswanPluginPrivate *priv = NM_STRONGSWAN_PLUGIN_GET_PRIVATE(plugin);
GVariantBuilder builder; GVariantBuilder builder, ip4builder, ip6builder;
GVariant *ip4config, *ip6config;
enumerator_t *enumerator; enumerator_t *enumerator;
host_t *me, *other; host_t *me, *other, *vip4 = NULL, *vip6 = NULL;
nm_handler_t *handler; nm_handler_t *handler;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_init (&ip4builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_init (&ip6builder, G_VARIANT_TYPE_VARDICT);
handler = priv->handler; handler = priv->handler;
/* NM apparently requires to know the gateway */ /* NM apparently requires to know the gateway */
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_IP4_CONFIG_EXT_GATEWAY, g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY,
g_variant_new_uint32 (*(uint32_t*)other->get_address(other).ptr)); host_to_variant(other));
/* NM installs this IP address on the interface above, so we use the VIP if /* pass the first virtual IPs we got or use the physical IP */
* we got one.
*/
enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
if (!enumerator->enumerate(enumerator, &me)) while (enumerator->enumerate(enumerator, &me))
{ {
me = ike_sa->get_my_host(ike_sa); switch (me->get_family(me))
{
case AF_INET:
if (!vip4)
{
vip4 = me;
}
break;
case AF_INET6:
if (!vip6)
{
vip6 = me;
}
break;
}
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, if (!vip4 && !vip6)
g_variant_new_uint32 (*(uint32_t*)other->get_address(me).ptr)); {
me = ike_sa->get_my_host(ike_sa);
switch (me->get_family(me))
{
case AF_INET:
vip4 = me;
break;
case AF_INET6:
vip6 = me;
break;
}
}
g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, if (vip4)
g_variant_new_uint32 (me->get_address(me).len * 8)); {
g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS,
host_to_variant(vip4));
g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_PREFIX,
g_variant_new_uint32 (vip4->get_address(vip4).len * 8));
/* prevent NM from changing the default route. we set our own route in our /* prevent NM from changing the default route. we set our own route in our
* own routing table * own routing table
*/ */
g_variant_builder_add (&builder, "{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 (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_DNS, g_variant_builder_add (&ip4builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NBNS,
handler_to_variant(handler, INTERNAL_IP4_DNS)); handler_to_variant(handler, "au", INTERNAL_IP4_NBNS));
}
g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_NBNS, if (vip6)
handler_to_variant(handler, INTERNAL_IP4_NBNS)); {
g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS,
host_to_variant(vip6));
g_variant_builder_add (&ip6builder, "{sv}", NM_VPN_PLUGIN_IP6_CONFIG_PREFIX,
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,
handler_to_variant(handler, "aay", INTERNAL_IP6_DNS));
/* NM_VPN_PLUGIN_IP6_CONFIG_NBNS is not defined */
}
ip4config = g_variant_builder_end (&ip4builder);
if (g_variant_n_children (ip4config))
{
g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP4,
g_variant_new_boolean (TRUE));
}
else
{
g_variant_unref (ip4config);
ip4config = NULL;
}
ip6config = g_variant_builder_end (&ip6builder);
if (g_variant_n_children (ip6config))
{
g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_CONFIG_HAS_IP6,
g_variant_new_boolean (TRUE));
}
else
{
g_variant_unref (ip6config);
ip6config = NULL;
}
handler->reset(handler); handler->reset(handler);
nm_vpn_service_plugin_set_ip4_config(plugin, g_variant_builder_end (&builder)); nm_vpn_service_plugin_set_config (plugin, g_variant_builder_end (&builder));
if (ip4config)
{
nm_vpn_service_plugin_set_ip4_config (plugin, ip4config);
}
if (ip6config)
{
nm_vpn_service_plugin_set_ip6_config (plugin, ip6config);
}
} }
/** /**
@ -184,7 +292,7 @@ static bool child_updown(listener_t *listener, ike_sa_t *ike_sa,
{ /* disable initiate-failure-detection hooks */ { /* disable initiate-failure-detection hooks */
private->listener.ike_state_change = NULL; private->listener.ike_state_change = NULL;
private->listener.child_state_change = NULL; private->listener.child_state_change = NULL;
signal_ipv4_config(private->plugin, ike_sa, child_sa); signal_ip_config(private->plugin, ike_sa, child_sa);
} }
else else
{ {
@ -526,7 +634,7 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
/** /**
* Set up configurations * Set up configurations
*/ */
ike_cfg = ike_cfg_create(IKEV2, TRUE, encap, "0.0.0.0", ike_cfg = ike_cfg_create(IKEV2, TRUE, encap, "%any",
charon->socket->get_port(charon->socket, FALSE), charon->socket->get_port(charon->socket, FALSE),
(char*)address, IKEV2_UDP_PORT, (char*)address, IKEV2_UDP_PORT,
FRAGMENTATION_YES, 0); FRAGMENTATION_YES, 0);
@ -564,7 +672,8 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
peer_cfg = peer_cfg_create(priv->name, ike_cfg, &peer); peer_cfg = peer_cfg_create(priv->name, ike_cfg, &peer);
if (virtual) if (virtual)
{ {
peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET));
peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET6));
} }
auth = auth_cfg_create(); auth = auth_cfg_create();
auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_class); auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_class);
@ -612,9 +721,9 @@ static gboolean connect_(NMVpnServicePlugin *plugin, NMConnection *connection,
} }
ts = traffic_selector_create_dynamic(0, 0, 65535); ts = traffic_selector_create_dynamic(0, 0, 65535);
child_cfg->add_traffic_selector(child_cfg, TRUE, ts); child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, ts = traffic_selector_create_from_cidr("0.0.0.0/0", 0, 0, 65535);
"0.0.0.0", 0, child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
"255.255.255.255", 65535); ts = traffic_selector_create_from_cidr("::/0", 0, 0, 65535);
child_cfg->add_traffic_selector(child_cfg, FALSE, ts); child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
peer_cfg->add_child_cfg(peer_cfg, child_cfg); peer_cfg->add_child_cfg(peer_cfg, child_cfg);

View File

@ -4,12 +4,11 @@
srcdir=`dirname $0` srcdir=`dirname $0`
test -z "$srcdir" && srcdir=. test -z "$srcdir" && srcdir=.
REQUIRED_AUTOMAKE_VERSION=1.7 REQUIRED_AUTOMAKE_VERSION=1.7
PKG_NAME=NetworkManager-strongswan
which gnome-autogen.sh || { which gnome-autogen.sh || {
echo "You need to install gnome-common from the GNOME CVS" echo "You need to install gnome-common from the GNOME CVS"
exit 1 exit 1
} }
USE_GNOME2_MACROS=1 . gnome-autogen.sh . gnome-autogen.sh

View File

@ -665,7 +665,7 @@ strongswan_plugin_ui_widget_interface_init (NMVpnEditorInterface *iface_class)
static guint32 static guint32
get_capabilities (NMVpnEditorPlugin *iface) get_capabilities (NMVpnEditorPlugin *iface)
{ {
return 0; return NM_VPN_EDITOR_PLUGIN_CAPABILITY_IPV6;
} }
static NMVpnEditor * static NMVpnEditor *