1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Copyright (C) 2005 - 2013 Red Hat, Inc.
19 * Copyright (C) 2006 - 2008 Novell, Inc.
23 #include "nm-default.h"
25 #include <sys/socket.h>
35 #include "nm-dhcp-manager.h"
36 #include "nm-dhcp-dhclient.h"
37 #include "nm-dhcp-dhcpcd.h"
38 #include "nm-dhcp-systemd.h"
39 #include "nm-config.h"
40 #include "NetworkManagerUtils.h"
42 #define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
44 /* default to installed helper, but can be modified for testing */
45 const char *nm_dhcp_helper_path = LIBEXECDIR "/nm-dhcp-helper";
47 typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gboolean ipv6);
52 char * default_hostname;
53 } NMDhcpManagerPrivate;
55 #define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDhcpManagerPrivate))
57 G_DEFINE_TYPE (NMDhcpManager, nm_dhcp_manager, G_TYPE_OBJECT)
59 /***************************************************/
64 NMDhcpClientGetPathFunc get_path_func;
65 NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func;
68 static GSList *client_descs = NULL;
71 _nm_dhcp_client_register (GType gtype,
73 NMDhcpClientGetPathFunc get_path_func,
74 NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func)
79 g_return_if_fail (gtype != G_TYPE_INVALID);
80 g_return_if_fail (name != NULL);
82 for (iter = client_descs; iter; iter = iter->next) {
84 g_return_if_fail (desc->gtype != gtype);
85 g_return_if_fail (strcmp (desc->name, name) != 0);
88 desc = g_slice_new0 (ClientDesc);
91 desc->get_path_func = get_path_func;
92 desc->get_lease_configs_func = get_lease_configs_func;
93 client_descs = g_slist_prepend (client_descs, desc);
97 find_client_desc (const char *name, GType gtype)
101 g_return_val_if_fail (name || gtype, NULL);
103 for (iter = client_descs; iter; iter = iter->next) {
104 ClientDesc *desc = iter->data;
106 if (name && strcmp (desc->name, name) != 0)
108 if (gtype && desc->gtype != gtype)
116 is_client_enabled (const char *name, GError **error)
120 desc = find_client_desc (name, G_TYPE_INVALID);
121 if (desc && (!desc->get_path_func || desc->get_path_func()))
124 g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
125 _("'%s' support not found or not enabled."),
127 return G_TYPE_INVALID;
130 /***************************************************/
132 static NMDhcpClient *
133 get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
135 NMDhcpManagerPrivate *priv;
139 g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
140 g_return_val_if_fail (ifindex > 0, NULL);
142 priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
144 g_hash_table_iter_init (&iter, priv->clients);
145 while (g_hash_table_iter_next (&iter, NULL, &value)) {
146 NMDhcpClient *candidate = NM_DHCP_CLIENT (value);
148 if ( nm_dhcp_client_get_ifindex (candidate) == ifindex
149 && nm_dhcp_client_get_ipv6 (candidate) == ip6)
157 get_client_type (const char *client, GError **error)
162 client_gtype = is_client_enabled (client, error);
165 client_gtype = is_client_enabled ("dhclient", NULL);
166 if (client_gtype == G_TYPE_INVALID)
167 client_gtype = is_client_enabled ("dhcpcd", NULL);
168 if (client_gtype == G_TYPE_INVALID)
169 client_gtype = is_client_enabled ("internal", NULL);
170 if (client_gtype == G_TYPE_INVALID) {
171 g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
172 _("no usable DHCP client could be found."));
178 static void client_state_changed (NMDhcpClient *client,
182 const char *event_id,
183 NMDhcpManager *self);
186 remove_client (NMDhcpManager *self, NMDhcpClient *client)
188 g_signal_handlers_disconnect_by_func (client, client_state_changed, self);
190 /* Stopping the client is left up to the controlling device
191 * explicitly since we may want to quit NetworkManager but not terminate
195 g_hash_table_remove (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client);
199 client_state_changed (NMDhcpClient *client,
203 const char *event_id,
206 if (state >= NM_DHCP_STATE_TIMEOUT)
207 remove_client (self, client);
210 static NMDhcpClient *
211 client_start (NMDhcpManager *self,
214 const GByteArray *hwaddr,
218 const struct in6_addr *ipv6_ll_addr,
219 const char *dhcp_client_id,
221 const char *dhcp_anycast_addr,
222 const char *hostname,
225 NMSettingIP6ConfigPrivacy privacy,
226 const char *last_ip4_address)
228 NMDhcpManagerPrivate *priv;
229 NMDhcpClient *client;
230 gboolean success = FALSE;
232 g_return_val_if_fail (self, NULL);
233 g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
234 g_return_val_if_fail (ifindex > 0, NULL);
235 g_return_val_if_fail (uuid != NULL, NULL);
237 priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
239 /* Ensure we have a usable DHCP client */
240 g_return_val_if_fail (priv->client_type != 0, NULL);
242 /* Kill any old client instance */
243 client = get_client_for_ifindex (self, ifindex, ipv6);
245 g_object_ref (client);
246 remove_client (self, client);
247 nm_dhcp_client_stop (client, FALSE);
248 g_object_unref (client);
251 /* And make a new one */
252 client = g_object_new (priv->client_type,
253 NM_DHCP_CLIENT_INTERFACE, iface,
254 NM_DHCP_CLIENT_IFINDEX, ifindex,
255 NM_DHCP_CLIENT_HWADDR, hwaddr,
256 NM_DHCP_CLIENT_IPV6, ipv6,
257 NM_DHCP_CLIENT_UUID, uuid,
258 NM_DHCP_CLIENT_PRIORITY, priority,
259 NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT,
261 g_hash_table_insert (NM_DHCP_MANAGER_GET_PRIVATE (self)->clients, client, g_object_ref (client));
262 g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self);
265 success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy);
267 success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, fqdn, last_ip4_address);
270 remove_client (self, client);
278 get_send_hostname (NMDhcpManager *self, const char *setting_hostname)
280 NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
282 /* Always prefer the explicit dhcp-send-hostname if given */
283 return setting_hostname ? setting_hostname : priv->default_hostname;
286 /* Caller owns a reference to the NMDhcpClient on return */
288 nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
291 const GByteArray *hwaddr,
294 gboolean send_hostname,
295 const char *dhcp_hostname,
296 const char *dhcp_fqdn,
297 const char *dhcp_client_id,
299 const char *dhcp_anycast_addr,
300 const char *last_ip_address)
302 const char *hostname = NULL;
303 const char *fqdn = NULL;
305 g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
308 hostname = get_send_hostname (self, dhcp_hostname);
311 return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE, NULL,
312 dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
313 fqdn, FALSE, 0, last_ip_address);
316 /* Caller owns a reference to the NMDhcpClient on return */
318 nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
321 const GByteArray *hwaddr,
322 const struct in6_addr *ll_addr,
325 gboolean send_hostname,
326 const char *dhcp_hostname,
328 const char *dhcp_anycast_addr,
330 NMSettingIP6ConfigPrivacy privacy)
332 const char *hostname = NULL;
334 g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
337 hostname = get_send_hostname (self, dhcp_hostname);
338 return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
339 ll_addr, NULL, timeout, dhcp_anycast_addr, hostname, NULL, info_only,
344 nm_dhcp_manager_set_default_hostname (NMDhcpManager *manager, const char *hostname)
346 NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
348 g_clear_pointer (&priv->default_hostname, g_free);
350 /* Never send 'localhost'-type names to the DHCP server */
351 if (!nm_utils_is_specific_hostname (hostname))
354 priv->default_hostname = g_strdup (hostname);
358 nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
363 guint32 default_route_metric)
367 g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
368 g_return_val_if_fail (iface != NULL, NULL);
369 g_return_val_if_fail (ifindex >= -1, NULL);
370 g_return_val_if_fail (uuid != NULL, NULL);
372 desc = find_client_desc (NULL, NM_DHCP_MANAGER_GET_PRIVATE (self)->client_type);
373 if (desc && desc->get_lease_configs_func)
374 return desc->get_lease_configs_func (iface, ifindex, uuid, ipv6, default_route_metric);
378 /***************************************************/
380 NM_DEFINE_SINGLETON_GETTER (NMDhcpManager, nm_dhcp_manager_get, NM_TYPE_DHCP_MANAGER);
383 nm_dhcp_manager_init (NMDhcpManager *self)
385 NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
386 NMConfig *config = nm_config_get ();
388 GError *error = NULL;
391 for (iter = client_descs; iter; iter = iter->next) {
392 ClientDesc *desc = iter->data;
394 nm_log_dbg (LOGD_DHCP, "Registered DHCP client '%s' (%s)",
395 desc->name, g_type_name (desc->gtype));
398 /* Client-specific setup */
399 client = nm_config_get_dhcp_client (config);
400 if (nm_config_get_configure_and_quit (config)) {
401 if (g_strcmp0 (client, "internal") != 0)
402 nm_log_warn (LOGD_DHCP, "Using internal DHCP client since configure-and-quit is set.");
406 priv->client_type = get_client_type (client, &error);
407 if (priv->client_type == G_TYPE_INVALID) {
408 nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
411 nm_log_dbg (LOGD_DHCP, "Using DHCP client '%s'", find_client_desc (NULL, priv->client_type)->name);
414 g_clear_error (&error);
416 priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
418 (GDestroyNotify) g_object_unref);
422 dispose (GObject *object)
424 NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
425 GList *values, *iter;
428 values = g_hash_table_get_values (priv->clients);
429 for (iter = values; iter; iter = g_list_next (iter))
430 remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data));
431 g_list_free (values);
434 G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
438 finalize (GObject *object)
440 NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
442 g_free (priv->default_hostname);
445 g_hash_table_destroy (priv->clients);
447 G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object);
451 nm_dhcp_manager_class_init (NMDhcpManagerClass *manager_class)
453 GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
455 g_type_class_add_private (manager_class, sizeof (NMDhcpManagerPrivate));
457 /* virtual methods */
458 object_class->finalize = finalize;
459 object_class->dispose = dispose;