Commit 4ec37b03 authored by Lubomir Rintel's avatar Lubomir Rintel 🥕

dhcp-manager: add ability to specify the number of IPv6 prefixes to request

Utilizes RFC 3633 prefix option in role of requesting router to ask the
delegating router for prefixes. In future we'll be able to use the
addresses from those prefixes on ipv6.method=shared connections.
parent 7d195856
......@@ -5992,7 +5992,8 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
priv->dhcp_timeout,
priv->dhcp_anycast_address,
(priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)));
nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)),
0);
if (tmp)
g_byte_array_free (tmp, TRUE);
......
......@@ -41,6 +41,7 @@
enum {
SIGNAL_STATE_CHANGED,
SIGNAL_PREFIX_DELEGATED,
LAST_SIGNAL
};
......@@ -511,7 +512,8 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
const struct in6_addr *ll_addr,
const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy)
NMSettingIP6ConfigPrivacy privacy,
guint needed_prefixes)
{
NMDhcpClientPrivate *priv;
gs_free char *str = NULL;
......@@ -544,7 +546,8 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
ll_addr,
info_only,
privacy,
priv->duid);
priv->duid,
needed_prefixes);
}
void
......@@ -709,6 +712,7 @@ nm_dhcp_client_handle_event (gpointer unused,
guint32 new_state;
GHashTable *str_options = NULL;
GObject *ip_config = NULL;
NMPlatformIP6Address prefix = { 0, };
g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
g_return_val_if_fail (iface != NULL, FALSE);
......@@ -754,6 +758,7 @@ nm_dhcp_client_handle_event (gpointer unused,
g_warn_if_fail (g_hash_table_size (str_options));
if (g_hash_table_size (str_options)) {
if (priv->ipv6) {
prefix = nm_dhcp_utils_ip6_prefix_from_options (str_options);
ip_config = (GObject *) nm_dhcp_utils_ip6_config_from_options (priv->ifindex,
priv->iface,
str_options,
......@@ -765,17 +770,26 @@ nm_dhcp_client_handle_event (gpointer unused,
str_options,
priv->priority);
}
/* Fail if no valid IP config was received */
if (ip_config == NULL) {
_LOGW ("client bound but IP config not received");
new_state = NM_DHCP_STATE_FAIL;
g_clear_pointer (&str_options, g_hash_table_unref);
}
}
}
nm_dhcp_client_set_state (self, new_state, ip_config, str_options);
if (!IN6_IS_ADDR_UNSPECIFIED (&prefix.address)) {
/* If we got an IPv6 prefix to delegate, we don't change the state
* of the DHCP client instance. Instead, we just signal the prefix
* to the device. */
g_signal_emit (G_OBJECT (self),
signals[SIGNAL_PREFIX_DELEGATED], 0,
&prefix);
} else {
/* Fail if no valid IP config was received */
if (new_state == NM_DHCP_STATE_BOUND && ip_config == NULL) {
_LOGW ("client bound but IP config not received");
new_state = NM_DHCP_STATE_FAIL;
g_clear_pointer (&str_options, g_hash_table_unref);
}
nm_dhcp_client_set_state (self, new_state, ip_config, str_options);
}
if (str_options)
g_hash_table_destroy (str_options);
......@@ -972,5 +986,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
G_STRUCT_OFFSET (NMDhcpClientClass, state_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE, G_TYPE_STRING);
}
signals[SIGNAL_PREFIX_DELEGATED] =
g_signal_new (NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMDhcpClientClass, state_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_POINTER);
}
......@@ -40,6 +40,7 @@
#define NM_DHCP_CLIENT_TIMEOUT "timeout"
#define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed"
#define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated"
typedef enum {
NM_DHCP_STATE_UNKNOWN = 0,
......@@ -73,7 +74,8 @@ typedef struct {
const struct in6_addr *ll_addr,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid);
const GByteArray *duid,
guint needed_prefixes);
void (*stop) (NMDhcpClient *self,
gboolean release,
......@@ -133,7 +135,8 @@ gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
const struct in6_addr *ll_addr,
const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy);
NMSettingIP6ConfigPrivacy privacy,
guint needed_prefixes);
void nm_dhcp_client_stop (NMDhcpClient *self, gboolean release);
......
......@@ -330,7 +330,8 @@ dhclient_start (NMDhcpClient *client,
const char *mode_opt,
const GByteArray *duid,
gboolean release,
pid_t *out_pid)
pid_t *out_pid,
int prefixes)
{
NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
......@@ -424,6 +425,8 @@ dhclient_start (NMDhcpClient *client,
g_ptr_array_add (argv, (gpointer) "-6");
if (mode_opt)
g_ptr_array_add (argv, (gpointer) mode_opt);
while (prefixes--)
g_ptr_array_add (argv, (gpointer) "-P");
}
g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */
g_ptr_array_add (argv, (gpointer) nm_dhcp_helper_path);
......@@ -503,7 +506,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
if (priv->conf_file) {
if (new_client_id)
nm_dhcp_client_set_client_id (client, new_client_id);
success = dhclient_start (client, NULL, NULL, FALSE, NULL);
success = dhclient_start (client, NULL, NULL, FALSE, NULL, 0);
} else
_LOGW ("error creating dhclient configuration file");
......@@ -516,7 +519,8 @@ ip6_start (NMDhcpClient *client,
const struct in6_addr *ll_addr,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
const GByteArray *duid,
guint needed_prefixes)
{
NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
......@@ -532,7 +536,7 @@ ip6_start (NMDhcpClient *client,
return FALSE;
}
return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE, NULL);
return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE, NULL, needed_prefixes);
}
static void
......@@ -557,7 +561,7 @@ stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
if (release) {
pid_t rpid = -1;
if (dhclient_start (client, NULL, duid, TRUE, &rpid)) {
if (dhclient_start (client, NULL, duid, TRUE, &rpid, 0)) {
/* Wait a few seconds for the release to happen */
nm_dhcp_client_stop_pid (rpid, nm_dhcp_client_get_iface (client));
}
......
......@@ -185,7 +185,8 @@ ip6_start (NMDhcpClient *client,
const struct in6_addr *ll_addr,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
const GByteArray *duid,
guint needed_prefixes)
{
NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client);
......
......@@ -166,7 +166,8 @@ client_start (NMDhcpManager *self,
const char *fqdn,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const char *last_ip4_address)
const char *last_ip4_address,
guint needed_prefixes)
{
NMDhcpManagerPrivate *priv;
NMDhcpClient *client;
......@@ -206,7 +207,7 @@ client_start (NMDhcpManager *self,
g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self);
if (ipv6)
success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy);
success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy, needed_prefixes);
else
success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, fqdn, last_ip4_address);
......@@ -254,7 +255,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
}
return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE, NULL,
dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
fqdn, FALSE, 0, last_ip_address);
fqdn, FALSE, 0, last_ip_address, 0);
}
/* Caller owns a reference to the NMDhcpClient on return */
......@@ -271,7 +272,8 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
guint32 timeout,
const char *dhcp_anycast_addr,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy)
NMSettingIP6ConfigPrivacy privacy,
guint needed_prefixes)
{
const char *hostname = NULL;
......@@ -281,7 +283,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
hostname = get_send_hostname (self, dhcp_hostname);
return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
ll_addr, NULL, timeout, dhcp_anycast_addr, hostname, NULL, info_only,
privacy, NULL);
privacy, NULL, needed_prefixes);
}
void
......
......@@ -69,7 +69,8 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager,
guint32 timeout,
const char *dhcp_anycast_addr,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy);
NMSettingIP6ConfigPrivacy privacy,
guint needed_prefixes);
GSList * nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
const char *iface,
......
......@@ -894,7 +894,8 @@ ip6_start (NMDhcpClient *client,
const struct in6_addr *ll_addr,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
const GByteArray *duid,
guint needed_prefixes)
{
NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client);
NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
......@@ -916,7 +917,12 @@ ip6_start (NMDhcpClient *client,
return FALSE;
}
_LOGT ("dhcp-client6: set %p", priv->client4);
if (needed_prefixes > 0) {
_LOGW ("dhcp-client6: prefix delegation not yet supported, won't supply %d prefixes\n",
needed_prefixes);
}
_LOGT ("dhcp-client6: set %p", priv->client6);
if (info_only)
sd_dhcp6_client_set_information_request (priv->client6, 1);
......
......@@ -604,6 +604,54 @@ ip6_add_domain_search (gpointer data, gpointer user_data)
nm_ip6_config_add_search (NM_IP6_CONFIG (user_data), (const char *) data);
}
NMPlatformIP6Address
nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options)
{
gs_strfreev gchar **split_addr = NULL;
NMPlatformIP6Address address = { 0, };
struct in6_addr tmp_addr;
char *str = NULL;
int prefix;
g_return_val_if_fail (options != NULL, address);
str = g_hash_table_lookup (options, "ip6_prefix");
if (!str)
return address;
split_addr = g_strsplit (str, "/", 2);
if (split_addr[0] == NULL && split_addr[1] == NULL) {
nm_log_warn (LOGD_DHCP6, "DHCP returned prefix without length '%s'", str);
return address;
}
if (!inet_pton (AF_INET6, split_addr[0], &tmp_addr)) {
nm_log_warn (LOGD_DHCP6, "DHCP returned invalid prefix '%s'", str);
return address;
}
prefix = _nm_utils_ascii_str_to_int64 (split_addr[1], 10, 0, 128, -1);
if (prefix < 0) {
nm_log_warn (LOGD_DHCP6, "DHCP returned prefix with invalid length '%s'", str);
return address;
}
address.address = tmp_addr;
address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
address.plen = prefix;
address.timestamp = nm_utils_get_monotonic_timestamp_s ();
str = g_hash_table_lookup (options, "max_life");
if (str)
address.lifetime = strtoul (str, NULL, 10);
str = g_hash_table_lookup (options, "preferred_life");
if (str)
address.preferred = strtoul (str, NULL, 10);
return address;
}
NMIP6Config *
nm_dhcp_utils_ip6_config_from_options (int ifindex,
const char *iface,
......
......@@ -35,6 +35,8 @@ NMIP6Config *nm_dhcp_utils_ip6_config_from_options (int ifindex,
guint priority,
gboolean info_only);
NMPlatformIP6Address nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options);
char * nm_dhcp_utils_duid_to_string (const GByteArray *duid);
GBytes * nm_dhcp_utils_client_id_string_to_bytes (const char *client_id);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment