diff options
| author | Michael Biebl <biebl@debian.org> | 2019-07-31 10:51:42 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-07-31 10:51:42 +0200 |
| commit | 2e5fa45ddfbb5cffa1e78221f1cea706e2f298af (patch) | |
| tree | 86f69d36c56de3074280456eddc854a780b8e04b /src/dhcp | |
| parent | 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (diff) | |
New upstream version 1.19.90 upstream/1.19.90
Diffstat (limited to 'src/dhcp')
| -rw-r--r-- | src/dhcp/nm-dhcp-client-logging.h | 1 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-client.c | 108 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-client.h | 41 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhclient-utils.c | 1 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhclient-utils.h | 1 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhclient.c | 3 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhcpcanon.c | 14 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-dhcpcd.c | 18 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-helper-api.h | 1 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-helper.c | 1 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-listener.c | 17 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-listener.h | 1 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-manager.c | 73 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-manager.h | 5 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-nettools.c | 1278 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-options.c | 278 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-options.h | 204 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-systemd.c | 425 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-utils.c | 1 | ||||
| -rw-r--r-- | src/dhcp/nm-dhcp-utils.h | 1 | ||||
| -rw-r--r-- | src/dhcp/tests/test-dhcp-dhclient.c | 1 | ||||
| -rw-r--r-- | src/dhcp/tests/test-dhcp-utils.c | 1 |
22 files changed, 2183 insertions, 291 deletions
diff --git a/src/dhcp/nm-dhcp-client-logging.h b/src/dhcp/nm-dhcp-client-logging.h index 1ed47170..d9b53f58 100644 --- a/src/dhcp/nm-dhcp-client-logging.h +++ b/src/dhcp/nm-dhcp-client-logging.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index d494eff0..9f585c5d 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* 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, or (at your option) @@ -52,6 +51,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient, PROP_ADDR_FAMILY, PROP_FLAGS, PROP_HWADDR, + PROP_BROADCAST_HWADDR, PROP_IFACE, PROP_IFINDEX, PROP_MULTI_IDX, @@ -66,6 +66,7 @@ typedef struct _NMDhcpClientPrivate { NMDedupMultiIndex *multi_idx; char * iface; GBytes * hwaddr; + GBytes * bcast_hwaddr; char * uuid; GBytes * client_id; char * hostname; @@ -144,6 +145,14 @@ nm_dhcp_client_get_hw_addr (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->hwaddr; } +GBytes * +nm_dhcp_client_get_broadcast_hw_addr (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->bcast_hwaddr; +} + guint32 nm_dhcp_client_get_route_table (NMDhcpClient *self) { @@ -476,8 +485,9 @@ nm_dhcp_client_start_timeout (NMDhcpClient *self) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_if_fail (priv->timeout_id == 0); + /* Set up a timeout on the transaction to kill it after the timeout */ - g_assert (priv->timeout_id == 0); if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) return; @@ -530,6 +540,36 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, error); } +gboolean +nm_dhcp_client_accept (NMDhcpClient *self, + GError **error) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + if (NM_DHCP_CLIENT_GET_CLASS (self)->accept) { + return NM_DHCP_CLIENT_GET_CLASS (self)->accept (self, + error); + } + + return TRUE; +} + +gboolean +nm_dhcp_client_decline (NMDhcpClient *self, + const char *error_message, + GError **error) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + if (NM_DHCP_CLIENT_GET_CLASS (self)->decline) { + return NM_DHCP_CLIENT_GET_CLASS (self)->decline (self, + error_message, + error); + } + + return TRUE; +} + static GBytes * get_duid (NMDhcpClient *self) { @@ -690,6 +730,23 @@ bytearray_variant_to_string (NMDhcpClient *self, GVariant *value, const char *ke return converted; } +static int +label_is_unknown_xyz (const char *label) +{ + if (!NM_STR_HAS_PREFIX (label, "unknown_")) + return -EINVAL; + + label += NM_STRLEN ("unknown_"); + if ( label[0] != '2' + || !g_ascii_isdigit (label[1]) + || !g_ascii_isdigit (label[2]) + || label[3] != '\0') + return -EINVAL; + + return _nm_utils_ascii_str_to_int64 (label, 10, 224, 254, -EINVAL); +} + + #define OLD_TAG "old_" #define NEW_TAG "new_" @@ -713,14 +770,41 @@ maybe_add_option (NMDhcpClient *self, "dhcp_message_type")) return; - if (g_str_has_prefix (key, NEW_TAG)) + if (NM_STR_HAS_PREFIX (key, NEW_TAG)) key += NM_STRLEN (NEW_TAG); - if (!key[0]) + if (NM_STR_HAS_PREFIX (key, "private_") || !key[0]) return; str_value = bytearray_variant_to_string (self, value, key); - if (str_value) + if (str_value) { + int priv_opt_num; + g_hash_table_insert (hash, g_strdup (key), str_value); + + /* dhclient has no special labels for private dhcp options: it uses "unknown_xyz" + * labels for that. We need to identify those to alias them to our "private_xyz" + * format unsed in the internal dchp plugins. + */ + if ((priv_opt_num = label_is_unknown_xyz (key)) > 0) { + gs_free guint8 *check_val = NULL; + char *hex_str = NULL; + gsize len; + + /* dhclient passes values from dhcp private options in its own "string" format: + * if the raw values are printable as ascii strings, it will pass the string + * representation; if the values are not printable as an ascii string, it will + * pass a string displaying the hex values (hex string). Try to enforce passing + * always an hex string, converting string representation if needed. + */ + check_val = nm_utils_hexstr2bin_alloc (str_value, FALSE, TRUE, ":", 0, &len); + hex_str = nm_utils_bin2hexstr_full (check_val ?: (guint8 *) str_value, + check_val ? len : strlen (str_value), + ':', FALSE, NULL); + g_hash_table_insert (hash, + g_strdup_printf ("private_%d", priv_opt_num), + hex_str); + } + } } gboolean @@ -839,6 +923,9 @@ get_property (GObject *object, guint prop_id, case PROP_HWADDR: g_value_set_boxed (value, priv->hwaddr); break; + case PROP_BROADCAST_HWADDR: + g_value_set_boxed (value, priv->bcast_hwaddr); + break; case PROP_ADDR_FAMILY: g_value_set_int (value, priv->addr_family); break; @@ -900,6 +987,10 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->hwaddr = g_value_dup_boxed (value); break; + case PROP_BROADCAST_HWADDR: + /* construct-only */ + priv->bcast_hwaddr = g_value_dup_boxed (value); + break; case PROP_ADDR_FAMILY: /* construct-only */ priv->addr_family = g_value_get_int (value); @@ -966,6 +1057,7 @@ dispose (GObject *object) g_clear_pointer (&priv->uuid, g_free); g_clear_pointer (&priv->client_id, g_bytes_unref); g_clear_pointer (&priv->hwaddr, g_bytes_unref); + g_clear_pointer (&priv->bcast_hwaddr, g_bytes_unref); G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); @@ -1010,6 +1102,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_BROADCAST_HWADDR] = + g_param_spec_boxed (NM_DHCP_CLIENT_BROADCAST_HWADDR, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_ADDR_FAMILY] = g_param_spec_int (NM_DHCP_CLIENT_ADDR_FAMILY, "", "", 0, G_MAXINT, AF_UNSPEC, diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index 1db7eac6..9eb76f33 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* 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, or (at your option) @@ -35,17 +34,18 @@ #define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_CLIENT)) #define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass)) -#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family" -#define NM_DHCP_CLIENT_FLAGS "flags" -#define NM_DHCP_CLIENT_HWADDR "hwaddr" -#define NM_DHCP_CLIENT_IFINDEX "ifindex" -#define NM_DHCP_CLIENT_INTERFACE "iface" -#define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" -#define NM_DHCP_CLIENT_HOSTNAME "hostname" -#define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric" -#define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" -#define NM_DHCP_CLIENT_TIMEOUT "timeout" -#define NM_DHCP_CLIENT_UUID "uuid" +#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family" +#define NM_DHCP_CLIENT_FLAGS "flags" +#define NM_DHCP_CLIENT_HWADDR "hwaddr" +#define NM_DHCP_CLIENT_BROADCAST_HWADDR "broadcast-hwaddr" +#define NM_DHCP_CLIENT_IFINDEX "ifindex" +#define NM_DHCP_CLIENT_INTERFACE "iface" +#define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" +#define NM_DHCP_CLIENT_HOSTNAME "hostname" +#define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric" +#define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" +#define NM_DHCP_CLIENT_TIMEOUT "timeout" +#define NM_DHCP_CLIENT_UUID "uuid" #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed" #define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated" @@ -83,6 +83,13 @@ typedef struct { const char *last_ip4_address, GError **error); + gboolean (*accept) (NMDhcpClient *self, + GError **error); + + gboolean (*decline) (NMDhcpClient *self, + const char *error_message, + GError **error); + gboolean (*ip6_start) (NMDhcpClient *self, const char *anycast_addr, const struct in6_addr *ll_addr, @@ -123,6 +130,8 @@ GBytes *nm_dhcp_client_get_duid (NMDhcpClient *self); GBytes *nm_dhcp_client_get_hw_addr (NMDhcpClient *self); +GBytes *nm_dhcp_client_get_broadcast_hw_addr (NMDhcpClient *self); + guint32 nm_dhcp_client_get_route_table (NMDhcpClient *self); void nm_dhcp_client_set_route_table (NMDhcpClient *self, guint32 route_table); @@ -156,6 +165,13 @@ gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self, guint needed_prefixes, GError **error); +gboolean nm_dhcp_client_accept (NMDhcpClient *self, + GError **error); + +gboolean nm_dhcp_client_decline (NMDhcpClient *self, + const char *error_message, + GError **error); + void nm_dhcp_client_stop (NMDhcpClient *self, gboolean release); /* Backend helpers for subclasses */ @@ -200,5 +216,6 @@ extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd; extern const NMDhcpClientFactory _nm_dhcp_client_factory_internal; +extern const NMDhcpClientFactory _nm_dhcp_client_factory_nettools; #endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */ diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index 85ca3704..bf3df399 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * 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 diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h index 57a711db..8ca893c3 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.h +++ b/src/dhcp/nm-dhcp-dhclient-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* 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, or (at your option) diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index b655a1eb..54b50479 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-dhclient.c - dhclient specific hooks for NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -376,7 +375,7 @@ dhclient_start (NMDhcpClient *client, iface = nm_dhcp_client_get_iface (client); uuid = nm_dhcp_client_get_uuid (client); - pid_file = g_strdup_printf (RUNSTATEDIR "/dhclient%s-%s.pid", + pid_file = g_strdup_printf (NMRUNDIR "/dhclient%s-%s.pid", _addr_family_to_path_part (addr_family), iface); diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c index 868cc9dd..2d2113cc 100644 --- a/src/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/dhcp/nm-dhcp-dhcpcanon.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-dhcpcanon.c - dhcpcanon specific hooks for NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -186,18 +185,6 @@ ip4_start (NMDhcpClient *client, error); } -static gboolean -ip6_start (NMDhcpClient *client, - const char *dhcp_anycast_addr, - const struct in6_addr *ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError **error) -{ - nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "dhcpcanon plugin does not support IPv6"); - return FALSE; -} - static void stop (NMDhcpClient *client, gboolean release) { @@ -257,7 +244,6 @@ nm_dhcp_dhcpcanon_class_init (NMDhcpDhcpcanonClass *dhcpcanon_class) object_class->dispose = dispose; client_class->ip4_start = ip4_start; - client_class->ip6_start = ip6_start; client_class->stop = stop; } diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c index 2a7482b1..c300bbe2 100644 --- a/src/dhcp/nm-dhcp-dhcpcd.c +++ b/src/dhcp/nm-dhcp-dhcpcd.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-dhcpcd.c - dhcpcd specific hooks for NetworkManager * * Copyright (C) 2008 Roy Marples @@ -100,9 +99,9 @@ ip4_start (NMDhcpClient *client, iface = nm_dhcp_client_get_iface (client); /* dhcpcd does not allow custom pidfiles; the pidfile is always - * RUNDIR "dhcpcd-<ifname>.pid". + * RUNSTATEDIR "dhcpcd-<ifname>.pid". */ - priv->pid_file = g_strdup_printf (RUNDIR "/dhcpcd-%s.pid", iface); + priv->pid_file = g_strdup_printf (RUNSTATEDIR "/dhcpcd-%s.pid", iface); dhcpcd_path = nm_dhcp_dhcpcd_get_path (); if (!dhcpcd_path) { @@ -180,18 +179,6 @@ ip4_start (NMDhcpClient *client, return TRUE; } -static gboolean -ip6_start (NMDhcpClient *client, - const char *dhcp_anycast_addr, - const struct in6_addr *ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError **error) -{ - nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "dhcpcd plugin does not support IPv6"); - return FALSE; -} - static void stop (NMDhcpClient *client, gboolean release) { @@ -251,7 +238,6 @@ nm_dhcp_dhcpcd_class_init (NMDhcpDhcpcdClass *dhcpcd_class) object_class->dispose = dispose; client_class->ip4_start = ip4_start; - client_class->ip6_start = ip6_start; client_class->stop = stop; } diff --git a/src/dhcp/nm-dhcp-helper-api.h b/src/dhcp/nm-dhcp-helper-api.h index 58344215..c1a3c71c 100644 --- a/src/dhcp/nm-dhcp-helper-api.h +++ b/src/dhcp/nm-dhcp-helper-api.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c index 8f753a61..9acc4045 100644 --- a/src/dhcp/nm-dhcp-helper.c +++ b/src/dhcp/nm-dhcp-helper.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dhcp/nm-dhcp-listener.c b/src/dhcp/nm-dhcp-listener.c index 049c4e55..88aafeb0 100644 --- a/src/dhcp/nm-dhcp-listener.c +++ b/src/dhcp/nm-dhcp-listener.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* 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, or (at your option) @@ -39,7 +38,7 @@ /*****************************************************************************/ -const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { +const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5] = { /* the order here matters, as we will try the plugins in this order to find * the first available plugin. */ @@ -53,15 +52,16 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { &_nm_dhcp_client_factory_dhcpcd, #endif &_nm_dhcp_client_factory_internal, + &_nm_dhcp_client_factory_nettools, }; /*****************************************************************************/ typedef struct { - NMDBusManager * dbus_mgr; - gulong new_conn_id; - gulong dis_conn_id; - GHashTable * connections; + NMDBusManager *dbus_mgr; + gulong new_conn_id; + gulong dis_conn_id; + GHashTable *connections; } NMDhcpListenerPrivate; struct _NMDhcpListener { @@ -282,7 +282,7 @@ nm_dhcp_listener_init (NMDhcpListener *self) /* Maps GDBusConnection :: signal-id */ priv->connections = g_hash_table_new (nm_direct_hash, NULL); - priv->dbus_mgr = nm_dbus_manager_get (); + priv->dbus_mgr = g_object_ref (nm_dbus_manager_get ()); /* Register the socket our DHCP clients will return lease info on */ nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG); @@ -303,10 +303,11 @@ dispose (GObject *object) nm_clear_g_signal_handler (priv->dbus_mgr, &priv->new_conn_id); nm_clear_g_signal_handler (priv->dbus_mgr, &priv->dis_conn_id); - priv->dbus_mgr = NULL; g_clear_pointer (&priv->connections, g_hash_table_destroy); + g_clear_object (&priv->dbus_mgr); + G_OBJECT_CLASS (nm_dhcp_listener_parent_class)->dispose (object); } diff --git a/src/dhcp/nm-dhcp-listener.h b/src/dhcp/nm-dhcp-listener.h index 3018b97a..d9724062 100644 --- a/src/dhcp/nm-dhcp-listener.h +++ b/src/dhcp/nm-dhcp-listener.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* 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, or (at your option) diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index fe843a2c..304a7b99 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -90,6 +89,54 @@ _client_factory_available (const NMDhcpClientFactory *client_factory) return NULL; } +static const NMDhcpClientFactory * +_client_factory_get_effective (const NMDhcpClientFactory *client_factory, + int addr_family) +{ + nm_auto_unref_gtypeclass NMDhcpClientClass *klass = NULL; + + nm_assert (client_factory); + nm_assert_addr_family (addr_family); + + /* currently, the chosen DHCP plugin for IPv4 and IPv6 is configured in NetworkManager.conf + * and cannot be reloaded. It would be nice to configure the plugin per address family + * or to be able to reload it. + * + * Note that certain options in NetworkManager.conf depend on the chosen DHCP plugin. + * See "dhcp-plugin:" in "Device List Format" (`man NetworkManager.conf`). + * Supporting reloading the plugin would also require to re-evalate the decisions from + * the "Device List Format". Likewise, having per-address family plugins would make the + * "main.dhcp" setting and "dhcp-plugin:" match non-sensical because these configurations + * currently are address family independet. + * + * So actually, we don't want that complexity. We want to phase out all plugins in favor + * of the internal plugin. + * However, certain existing plugins are well known to not support an address family. + * In those cases, we should just silently fallback to the internal plugin. + * + * This could be a problem with forward compatibility if we ever intended to add IPv6 support + * to those plugins. But we don't intend to do so. The internal plugin is the way forward and + * not extending other plugins. */ + + if (client_factory == &_nm_dhcp_client_factory_internal) { + /* already using internal plugin. Nothing to do. */ + return client_factory; + } + + klass = g_type_class_ref (client_factory->get_type ()); + + nm_assert (NM_IS_DHCP_CLIENT_CLASS (klass)); + + if (addr_family == AF_INET6) { + return klass->ip6_start + ? client_factory + : &_nm_dhcp_client_factory_internal; + } + return klass->ip4_start + ? client_factory + : &_nm_dhcp_client_factory_internal; +} + /*****************************************************************************/ static NMDhcpClient * @@ -157,6 +204,7 @@ client_start (NMDhcpManager *self, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, @@ -177,6 +225,7 @@ client_start (NMDhcpManager *self, NMDhcpClient *client; gboolean success = FALSE; gsize hwaddr_len; + const NMDhcpClientFactory *client_factory; g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); g_return_val_if_fail (iface, NULL); @@ -185,10 +234,11 @@ client_start (NMDhcpManager *self, g_return_val_if_fail (!dhcp_client_id || g_bytes_get_size (dhcp_client_id) >= 2, NULL); g_return_val_if_fail (!error || !*error, NULL); - if (!hwaddr) { + if (!hwaddr || !bcast_hwaddr) { nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - "missing MAC address"); + "missing %s address", + hwaddr ? "broadcast" : "MAC"); return NULL; } @@ -201,24 +251,33 @@ client_start (NMDhcpManager *self, g_return_val_if_reached (NULL) ; } + nm_assert (g_bytes_get_size (hwaddr) == g_bytes_get_size (bcast_hwaddr)); + priv = NM_DHCP_MANAGER_GET_PRIVATE (self); - nm_assert (priv->client_factory); + client_factory = _client_factory_get_effective (priv->client_factory, addr_family); /* Kill any old client instance */ client = get_client_for_ifindex (self, addr_family, ifindex); if (client) { + /* FIXME: we cannot just call synchronously "stop()" and forget about the client. + * We need to wait for the client to be fully stopped because most/all clients + * cannot quit right away. + * + * FIXME(shutdown): also fix this during shutdown, to wait for all DHCP clients + * to be fully stopped. */ remove_client (self, client); nm_dhcp_client_stop (client, FALSE); g_object_unref (client); } - client = g_object_new (priv->client_factory->get_type (), + client = g_object_new (client_factory->get_type (), NM_DHCP_CLIENT_MULTI_IDX, multi_idx, NM_DHCP_CLIENT_ADDR_FAMILY, addr_family, NM_DHCP_CLIENT_INTERFACE, iface, NM_DHCP_CLIENT_IFINDEX, ifindex, NM_DHCP_CLIENT_HWADDR, hwaddr, + NM_DHCP_CLIENT_BROADCAST_HWADDR, bcast_hwaddr, NM_DHCP_CLIENT_UUID, uuid, NM_DHCP_CLIENT_HOSTNAME, hostname, NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table, @@ -291,6 +350,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, @@ -341,6 +401,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, iface, ifindex, hwaddr, + bcast_hwaddr, uuid, route_table, route_metric, @@ -365,6 +426,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const struct in6_addr *ll_addr, const char *uuid, guint32 route_table, @@ -396,6 +458,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, iface, ifindex, hwaddr, + bcast_hwaddr, uuid, route_table, route_metric, diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index f8f39e53..ff0d6f54 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -50,6 +49,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, @@ -67,6 +67,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const struct in6_addr *ll_addr, const char *uuid, guint32 route_table, @@ -85,7 +86,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, /* For testing only */ extern const char* nm_dhcp_helper_path; -extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4]; +extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5]; void nmtst_dhcp_manager_unget (gpointer singleton_instance); diff --git a/src/dhcp/nm-dhcp-nettools.c b/src/dhcp/nm-dhcp-nettools.c new file mode 100644 index 00000000..3aa92180 --- /dev/null +++ b/src/dhcp/nm-dhcp-nettools.c @@ -0,0 +1,1278 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2014-2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <net/if_arp.h> + +#include "nm-sd-adapt-shared.h" +#include "hostname-util.h" + +#include "nm-glib-aux/nm-dedup-multi.h" +#include "nm-std-aux/unaligned.h" + +#include "nm-utils.h" +#include "nm-config.h" +#include "nm-dhcp-utils.h" +#include "nm-dhcp-options.h" +#include "nm-core-utils.h" +#include "NetworkManagerUtils.h" +#include "platform/nm-platform.h" +#include "nm-dhcp-client-logging.h" +#include "n-dhcp4/src/n-dhcp4.h" +#include "systemd/nm-sd-utils-shared.h" + +/*****************************************************************************/ + +#define NM_TYPE_DHCP_NETTOOLS (nm_dhcp_nettools_get_type ()) +#define NM_DHCP_NETTOOLS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettools)) +#define NM_DHCP_NETTOOLS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettoolsClass)) +#define NM_IS_DHCP_NETTOOLS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_NETTOOLS)) +#define NM_IS_DHCP_NETTOOLS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_NETTOOLS)) +#define NM_DHCP_NETTOOLS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettoolsClass)) + +typedef struct _NMDhcpNettools NMDhcpNettools; +typedef struct _NMDhcpNettoolsClass NMDhcpNettoolsClass; + +static GType nm_dhcp_nettools_get_type (void); + +/*****************************************************************************/ + +typedef struct { + NDhcp4Client *client; + NDhcp4ClientProbe *probe; + NDhcp4ClientLease *lease; + GIOChannel *channel; + guint event_id; +} NMDhcpNettoolsPrivate; + +struct _NMDhcpNettools { + NMDhcpClient parent; + NMDhcpNettoolsPrivate _priv; +}; + +struct _NMDhcpNettoolsClass { + NMDhcpClientClass parent; +}; + +G_DEFINE_TYPE (NMDhcpNettools, nm_dhcp_nettools, NM_TYPE_DHCP_CLIENT) + +#define NM_DHCP_NETTOOLS_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDhcpNettools, NM_IS_DHCP_NETTOOLS) + +/*****************************************************************************/ + +#define DHCP_MAX_FQDN_LENGTH 255 + +enum { + DHCP_FQDN_FLAG_S = (1 << 0), + DHCP_FQDN_FLAG_O = (1 << 1), + DHCP_FQDN_FLAG_E = (1 << 2), + DHCP_FQDN_FLAG_N = (1 << 3), +}; + +enum { + NM_IN_ADDR_CLASS_A, + NM_IN_ADDR_CLASS_B, + NM_IN_ADDR_CLASS_C, + NM_IN_ADDR_CLASS_INVALID, +}; + +static int +in_addr_class (struct in_addr addr) +{ + switch (ntohl (addr.s_addr) >> 24) { + case 0 ... 127: + return NM_IN_ADDR_CLASS_A; + case 128 ... 191: + return NM_IN_ADDR_CLASS_B; + case 192 ... 223: + return NM_IN_ADDR_CLASS_C; + default: + return NM_IN_ADDR_CLASS_INVALID; + } +} + +static gboolean +lease_option_consume (void *out, + size_t n_out, + uint8_t **datap, + size_t *n_datap) +{ + if (*n_datap < n_out) + return FALSE; + + memcpy (out, *datap, n_out); + *datap += n_out; + *n_datap -= n_out; + return TRUE; +} + +static gboolean +lease_option_next_in_addr (struct in_addr *addrp, + uint8_t **datap, + size_t *n_datap) +{ + return lease_option_consume (addrp, sizeof (struct in_addr), datap, n_datap); +} + +static gboolean +lease_option_next_route (struct in_addr *destp, + uint8_t *plenp, + struct in_addr *gatewayp, + gboolean classless, + uint8_t **datap, + size_t *n_datap) +{ + struct in_addr dest = {}, gateway; + uint8_t *data = *datap; + size_t n_data = *n_datap; + uint8_t plen; + + if (classless) { + if (!lease_option_consume (&plen, sizeof (plen), &data, &n_data)) + return FALSE; + + if (plen > 32) + return FALSE; + + if (!lease_option_consume (&dest, plen / 8, &data, &n_data)) + return FALSE; + } else { + if (!lease_option_next_in_addr (&dest, &data, &n_data)) + return FALSE; + + switch (in_addr_class (dest)) { + case NM_IN_ADDR_CLASS_A: + plen = 8; + break; + case NM_IN_ADDR_CLASS_B: + plen = 16; + break; + case NM_IN_ADDR_CLASS_C: + plen = 24; + break; + case NM_IN_ADDR_CLASS_INVALID: + return FALSE; + } + } + + dest.s_addr = nm_utils_ip4_address_clear_host_address (dest.s_addr, plen); + + if (!lease_option_next_in_addr (&gateway, &data, &n_data)) + return FALSE; + + *destp = dest; + *plenp = plen; + *gatewayp = gateway; + *datap = data; + *n_datap = n_data; + return TRUE; +} + +static gboolean +lease_option_print_label (GString *str, size_t n_label, uint8_t **datap, size_t *n_datap) +{ + for (size_t i = 0; i < n_label; ++i) { + uint8_t c; + + if (!lease_option_consume(&c, sizeof (c), datap, n_datap)) + return FALSE; + + switch (c) { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + case '-': + case '_': + g_string_append_c(str, c); + break; + case '.': + case '\\': + g_string_append_printf(str, "\\%c", c); + break; + default: + g_string_append_printf(str, "\\%3d", c); + } + } + + return TRUE; +} + +static gboolean +lease_option_print_domain_name (GString *str, uint8_t *cache, size_t *n_cachep, uint8_t **datap, size_t *n_datap) +{ + uint8_t *domain; + size_t n_domain, n_cache = *n_cachep; + uint8_t **domainp = datap; + size_t *n_domainp = n_datap; + gboolean first = TRUE; + uint8_t c; + + /* + * We are given two adjacent memory regions. The @cache contains alreday parsed + * domain names, and the @datap contains the remaining data to parse. + * + * A domain name is formed from a sequence of labels. Each label start with + * a length byte, where the two most significant bits are unset. A zero-length + * label indicates the end of the domain name. + * + * Alternatively, a label can be followed by an offset (indicated by the two + * most significant bits being set in the next byte that is read). The offset + * is an offset into the cache, where the next label of the domain name can + * be found. + * + * Note, that each time a jump to an offset is performed, the size of the + * cache shrinks, so this is guaranteed to terminate. + */ + if (cache + n_cache != *datap) + return FALSE; + + for (;;) { + if (!lease_option_consume(&c, sizeof (c), domainp, n_domainp)) + return FALSE; + + switch (c & 0xC0) { + case 0x00: /* label length */ + { + size_t n_label = c; + + if (n_label == 0) { + /* + * We reached the final label of the domain name. Adjust + * the cache to include the consumed data, and return. + */ + *n_cachep = *datap - cache; + return TRUE; + } + + if (!first) { + g_string_append_c(str, '.'); + first = FALSE; + } + + if (!lease_option_print_label (str, n_label, domainp, n_domainp)) + return FALSE; + + break; + } + case 0xC0: /* back pointer */ + { + size_t offset = (c & 0x3F) << 16; + + /* + * The offset is given as two bytes (in big endian), where the + * two high bits are masked out. + */ + + if (!lease_option_consume (&c, sizeof (c), domainp, n_domainp)) + return FALSE; + + offset += c; + + if (offset >= n_cache) + return FALSE; + + domain = cache + offset; + n_domain = n_cache - offset; + n_cache = offset; + + domainp = &domain; + n_domainp = &n_domain; + + break; + } + default: + return FALSE; + } + } +} + +static gboolean +lease_get_in_addr (NDhcp4ClientLease *lease, + guint8 option, + struct in_addr *addrp) { + struct in_addr addr; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, option, &data, &n_data); + if (r) + return FALSE; + + if (!lease_option_next_in_addr (&addr, &data, &n_data)) + return FALSE; + + if (n_data != 0) + return FALSE; + + *addrp = addr; + return TRUE; +} + +static gboolean +lease_get_u16 (NDhcp4ClientLease *lease, + uint8_t option, + uint16_t *u16p) +{ + uint8_t *data; + size_t n_data; + uint16_t be16; + int r; + + r = n_dhcp4_client_lease_query (lease, option, &data, &n_data); + if (r) + return FALSE; + + if (n_data != sizeof (be16)) + return FALSE; + + memcpy (&be16, data, sizeof (be16)); + + *u16p = ntohs(be16); + return TRUE; +} + +#define LOG_LEASE(domain, ...) \ + G_STMT_START { \ + _LOG2I ((domain), (iface), " "__VA_ARGS__); \ + } G_STMT_END + +static gboolean +lease_parse_address (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options, + GError **error) +{ + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + const gint64 ts = nm_utils_get_monotonic_timestamp_ns (); + struct in_addr a_address; + struct in_addr a_netmask; + guint32 a_plen; + guint64 a_lifetime; + + n_dhcp4_client_lease_get_yiaddr (lease, &a_address); + if (a_address.s_addr == INADDR_ANY) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get address from lease"); + return FALSE; + } + + /* n_dhcp4_client_lease_get_lifetime() never fails */ + n_dhcp4_client_lease_get_lifetime (lease, &a_lifetime); + + if (!lease_get_in_addr (lease, NM_DHCP_OPTION_DHCP4_SUBNET_MASK, &a_netmask)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get netmask from lease"); + return FALSE; + } + + nm_utils_inet4_ntop (a_address.s_addr, addr_str); + a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr); + + LOG_LEASE (LOGD_DHCP4, "address %s/%u", addr_str, a_plen); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, + addr_str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_SUBNET_MASK, + nm_utils_inet4_ntop (a_netmask.s_addr, addr_str)); + + LOG_LEASE (LOGD_DHCP4, "expires in %u seconds", + (guint) ((a_lifetime - ts)/1000000000)); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME, + (guint64) (a_lifetime / 1000000000)); + + nm_ip4_config_add_address (ip4_config, + &((const NMPlatformIP4Address) { + .address = a_address.s_addr, + .peer_address = a_address.s_addr, + .plen = a_plen, + .addr_source = NM_IP_CONFIG_SOURCE_DHCP, + .timestamp = ts / 1000000000, + .lifetime = (a_lifetime - ts) / 1000000000, + .preferred = (a_lifetime - ts) / 1000000000, + })); + + return TRUE; +} + +static void +lease_parse_domain_name_servers (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + struct in_addr addr; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, &data, &n_data); + if (r) + return; + + nm_gstring_prepare (&str); + + while (lease_option_next_in_addr (&addr, &data, &n_data)) { + + nm_utils_inet4_ntop (addr.s_addr, addr_str); + g_string_append (nm_gstring_add_space_delimiter (str), addr_str); + + if ( addr.s_addr == 0 + || nm_ip4_addr_is_localhost (addr.s_addr)) { + /* Skip localhost addresses, like also networkd does. + * See https://github.com/systemd/systemd/issues/4524. */ + continue; + } + nm_ip4_config_add_nameserver (ip4_config, addr.s_addr); + } + + LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, + str->str); +} + +static void +lease_parse_routes (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options, + guint32 route_table, + guint32 route_metric) +{ + nm_auto_free_gstring GString *str = NULL; + char dest_str[NM_UTILS_INET_ADDRSTRLEN]; + char gateway_str[NM_UTILS_INET_ADDRSTRLEN]; + const char *s; + struct in_addr dest, gateway; + uint8_t plen; + guint32 m; + gboolean has_router_from_classless = FALSE, has_classless = FALSE; + guint32 default_route_metric = route_metric; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, &data, &n_data); + if (!r) { + nm_gstring_prepare (&str); + + has_classless = TRUE; + + while (lease_option_next_route (&dest, &plen, &gateway, TRUE, &data, &n_data)) { + + nm_utils_inet4_ntop (dest.s_addr, dest_str); + nm_utils_inet4_ntop (gateway.s_addr, gateway_str); + + LOG_LEASE (LOGD_DHCP4, + "classless static route %s/%d gw %s", + dest_str, + (int) plen, + gateway_str); + g_string_append_printf (nm_gstring_add_space_delimiter (str), + "%s/%d %s", + dest_str, + (int) plen, + gateway_str); + + if (plen == 0) { + /* if there are multiple default routes, we add them with differing + * metrics. */ + m = default_route_metric; + if (default_route_metric < G_MAXUINT32) + default_route_metric++; + + has_router_from_classless = TRUE; + } else { + m = route_metric; + } + + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .network = dest.s_addr, + .plen = plen, + .gateway = gateway.s_addr, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .metric = m, + .table_coerced = nm_platform_route_table_coerce (route_table), + }), + NULL); + } + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, + str->str); + } + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, &data, &n_data); + if (!r) { + nm_gstring_prepare (&str); + + while (lease_option_next_route (&dest, &plen, &gateway, FALSE, &data, &n_data)) { + + nm_utils_inet4_ntop (dest.s_addr, dest_str); + nm_utils_inet4_ntop (gateway.s_addr, gateway_str); + + LOG_LEASE (LOGD_DHCP4, + "static route %s/%d gw %s", + dest_str, + (int) plen, + gateway_str); + g_string_append_printf (nm_gstring_add_space_delimiter (str), + "%s/%d %s", + dest_str, + (int) plen, + gateway_str); + + if (has_classless) { + /* RFC 3443: if the DHCP server returns both a Classless Static Routes + * option and a Static Routes option, the DHCP client MUST ignore the + * Static Routes option. */ + continue; + } + + if (plen == 0) { + /* for option 33 (static route), RFC 2132 says: + * + * The default route (0.0.0.0) is an illegal destination for a static + * route. */ + continue; + } + + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .network = dest.s_addr, + .plen = plen, + .gateway = gateway.s_addr, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .metric = route_metric, + .table_coerced = nm_platform_route_table_coerce (route_table), + }), + NULL); + } + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, + str->str); + } + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_ROUTER, &data, &n_data); + if (!r) { + nm_gstring_prepare (&str); + + while (lease_option_next_in_addr (&gateway, &data, &n_data)) { + s = nm_utils_inet4_ntop (gateway.s_addr, gateway_str); + g_string_append (nm_gstring_add_space_delimiter (str), s); + + if (gateway.s_addr == 0) { + /* silently skip 0.0.0.0 */ + continue; + } + + if (has_router_from_classless) { + /* If the DHCP server returns both a Classless Static Routes option and a + * Router option, the DHCP client MUST ignore the Router option [RFC 3442]. + * + * Be more lenient and ignore the Router option only if Classless Static + * Routes contain a default gateway (as other DHCP backends do). + */ + continue; + } + + /* if there are multiple default routes, we add them with differing + * metrics. */ + m = default_route_metric; + if (default_route_metric < G_MAXUINT32) + default_route_metric++; + + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = gateway.s_addr, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = m, + }), + NULL); + } + LOG_LEASE (LOGD_DHCP4, "router %s", str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_ROUTER, + str->str); + } +} + +static void +lease_parse_mtu (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + uint16_t mtu; + + if (!lease_get_u16 (lease, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, &mtu)) + return; + + if (mtu < 68) + return; + + LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, + mtu); + nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); +} + +static void +lease_parse_metered (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + gboolean metered = FALSE; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_VENDOR_SPECIFIC, &data, &n_data); + if (r) { + metered = FALSE; + } else { + metered = !!memmem (data, n_data, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED")); + } + + LOG_LEASE (LOGD_DHCP4, "%s", metered ? "metered" : "unmetered"); + nm_ip4_config_set_metered (ip4_config, metered); +} + +static void +lease_parse_ntps (NDhcp4ClientLease *lease, + const char *iface, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + struct in_addr addr; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_NTP_SERVER, &data, &n_data); + if (r) + return; + + nm_gstring_prepare (&str); + + while (lease_option_next_in_addr (&addr, &data, &n_data)) { + nm_utils_inet4_ntop (addr.s_addr, addr_str); + g_string_append (nm_gstring_add_space_delimiter (str), addr_str); + } + + LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NTP_SERVER, str->str); +} + +static void +lease_parse_hostname (NDhcp4ClientLease *lease, + const char *iface, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_HOST_NAME, &data, &n_data); + if (r) + return; + + str = g_string_new_len ((char *)data, n_data); + + if (is_localhost(str->str)) + return; + + LOG_LEASE (LOGD_DHCP4, "hostname '%s'", str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, str->str); +} + +static void +lease_parse_domainname (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + gs_strfreev char **domains = NULL; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, &data, &n_data); + if (r) + return; + + str = g_string_new_len ((char *)data, n_data); + + /* Multiple domains sometimes stuffed into option 15 "Domain Name". */ + domains = g_strsplit (str->str, " ", 0); + nm_gstring_prepare (&str); + + for (char **d = domains; *d; d++) { + if (is_localhost(*d)) + return; + + g_string_append (nm_gstring_add_space_delimiter (str), *d); + nm_ip4_config_add_domain (ip4_config, *d); + } + LOG_LEASE (LOGD_DHCP4, "domain name '%s'", str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, str->str); +} + +static void +lease_parse_search_domains (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + uint8_t *data, *cache; + size_t n_data, n_cache = 0; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, &data, &n_data); + if (r) + return; + + cache = data; + + nm_gstring_prepare (&str); + + for (;;) { + nm_auto_free_gstring GString *domain = NULL; + + nm_gstring_prepare (&domain); + + if (!lease_option_print_domain_name (domain, cache, &n_cache, &data, &n_data)) + break; + + g_string_append (nm_gstring_add_space_delimiter (str), domain->str); + nm_ip4_config_add_search (ip4_config, domain->str); + } + LOG_LEASE (LOGD_DHCP4, "domain search '%s'", str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, + str->str); +} + +static void +lease_parse_root_path (NDhcp4ClientLease *lease, + const char *iface, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_ROOT_PATH, &data, &n_data); + if (r) + return; + + str = g_string_new_len ((char *)data, n_data); + LOG_LEASE (LOGD_DHCP4, "root path '%s'", str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH, str->str); +} + +static void +lease_parse_wpad (NDhcp4ClientLease *lease, + const char *iface, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, &data, &n_data); + if (r) + return; + + str = g_string_new_len ((char *)data, n_data); + LOG_LEASE (LOGD_DHCP4, "wpad '%s'", str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, + str->str); +} + +static NMIP4Config * +lease_to_ip4_config (NMDedupMultiIndex *multi_idx, + const char *iface, + int ifindex, + NDhcp4ClientLease *lease, + guint32 route_table, + guint32 route_metric, + GHashTable **out_options, + GError **error) +{ + gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; + + g_return_val_if_fail (lease != NULL, NULL); + + ip4_config = nm_ip4_config_new (multi_idx, ifindex); + options = out_options ? nm_dhcp_option_create_options_dict () : NULL; + + if (!lease_parse_address (lease, iface, ip4_config, options, error)) + return NULL; + + lease_parse_routes (lease, iface, ip4_config, options, route_table, route_metric); + lease_parse_domain_name_servers (lease, iface, ip4_config, options); + lease_parse_domainname (lease, iface, ip4_config, options); + lease_parse_search_domains (lease, iface, ip4_config, options); + lease_parse_mtu (lease, iface, ip4_config, options); + lease_parse_metered (lease, iface, ip4_config, options); + + lease_parse_hostname (lease, iface, options); + lease_parse_ntps (lease, iface, options); + lease_parse_root_path (lease, iface, options); + lease_parse_wpad (lease, iface, options); + + NM_SET_OUT (out_options, g_steal_pointer (&options)); + return g_steal_pointer (&ip4_config); +} + +/*****************************************************************************/ + +static void +bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease) +{ + const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)); + gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; + GError *error = NULL; + + _LOGT ("lease available"); + + ip4_config = lease_to_ip4_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), + iface, + nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), + lease, + nm_dhcp_client_get_route_table (NM_DHCP_CLIENT (self)), + nm_dhcp_client_get_route_metric (NM_DHCP_CLIENT (self)), + &options, + &error); + if (!ip4_config) { + _LOGW ("%s", error->message); + g_clear_error (&error); + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); + return; + } + + nm_dhcp_option_add_requests_to_options (options, _nm_dhcp_option_dhcp4_options); + + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), + NM_DHCP_STATE_BOUND, + NM_IP_CONFIG_CAST (ip4_config), + options); +} + +static gboolean +dhcp4_event_handle (NMDhcpNettools *self, + NDhcp4ClientEvent *event) +{ + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + int r; + + _LOGT ("client event %d", event->event); + + switch (event->event) { + case N_DHCP4_CLIENT_EVENT_OFFER: + /* always accept the first lease */ + r = n_dhcp4_client_lease_select (event->offer.lease); + if (r) { + _LOGW ("selecting lease failed: %d", r); + } + break; + case N_DHCP4_CLIENT_EVENT_EXPIRED: + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_EXPIRE, NULL, NULL); + break; + case N_DHCP4_CLIENT_EVENT_RETRACTED: + case N_DHCP4_CLIENT_EVENT_CANCELLED: + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); + break; + case N_DHCP4_CLIENT_EVENT_GRANTED: + priv->lease = n_dhcp4_client_lease_ref (event->granted.lease); + bound4_handle (self, event->granted.lease); + break; + case N_DHCP4_CLIENT_EVENT_EXTENDED: + bound4_handle (self, event->extended.lease); + break; + case N_DHCP4_CLIENT_EVENT_DOWN: + /* ignore down events, they are purely informational */ + break; + default: + _LOGW ("unhandled DHCP event %d", event->event); + break; + } + + return TRUE; +} + +static gboolean +dhcp4_event_cb (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + NMDhcpNettools *self = data; + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + NDhcp4ClientEvent *event; + int r; + + r = n_dhcp4_client_dispatch (priv->client); + if (r < 0) + return G_SOURCE_CONTINUE; + + while (!n_dhcp4_client_pop_event (priv->client, &event) && event) { + dhcp4_event_handle (self, event); + } + + return G_SOURCE_CONTINUE; +} + +static gboolean +nettools_create (NMDhcpNettools *self, + const char *dhcp_anycast_addr, + GError **error) +{ + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + nm_auto (n_dhcp4_client_config_freep) NDhcp4ClientConfig *config = NULL; + nm_auto (n_dhcp4_client_unrefp) NDhcp4Client *client = NULL; + GBytes *hwaddr; + GBytes *bcast_hwaddr; + const uint8_t *hwaddr_arr; + const uint8_t *bcast_hwaddr_arr; + gsize hwaddr_len; + gsize bcast_hwaddr_len; + GBytes *client_id; + gs_unref_bytes GBytes *client_id_new = NULL; + const uint8_t *client_id_arr; + size_t client_id_len; + int r, fd, arp_type, transport; + + g_return_val_if_fail (!priv->client, FALSE); + + hwaddr = nm_dhcp_client_get_hw_addr (NM_DHCP_CLIENT (self)); + if ( !hwaddr + || !(hwaddr_arr = g_bytes_get_data (hwaddr, &hwaddr_len)) + || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); + return FALSE; + } + + bcast_hwaddr = nm_dhcp_client_get_broadcast_hw_addr (NM_DHCP_CLIENT (self)); + bcast_hwaddr_arr = g_bytes_get_data (bcast_hwaddr, &bcast_hwaddr_len); + + switch (arp_type) { + case ARPHRD_ETHER: + transport = N_DHCP4_TRANSPORT_ETHERNET; + break; + case ARPHRD_INFINIBAND: + transport = N_DHCP4_TRANSPORT_INFINIBAND; + break; + default: + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "unsupported ARP type"); + return FALSE; + } + + /* Note that we always set a client-id. In particular for infiniband that is necessary, + * see https://tools.ietf.org/html/rfc4390#section-2.1 . */ + client_id = nm_dhcp_client_get_client_id (NM_DHCP_CLIENT (self)); + if (!client_id) { + client_id_new = nm_utils_dhcp_client_id_mac (arp_type, hwaddr_arr, hwaddr_len); + client_id = client_id_new; + } + + if ( !(client_id_arr = g_bytes_get_data (client_id, &client_id_len)) + || client_id_len < 2) { + + /* invalid client-ids are not expected. */ + nm_assert_not_reached (); + + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "no valid IPv4 client-id"); + return FALSE; + } + + r = n_dhcp4_client_config_new (&config); + if (r) { + nm_utils_error_set_errno (error, r, "failed to create client-config: %s"); + return FALSE; + } + + n_dhcp4_client_config_set_ifindex (config, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self))); + n_dhcp4_client_config_set_transport (config, transport); + n_dhcp4_client_config_set_mac (config, hwaddr_arr, hwaddr_len); + n_dhcp4_client_config_set_broadcast_mac (config, bcast_hwaddr_arr, bcast_hwaddr_len); + r = n_dhcp4_client_config_set_client_id (config, client_id_arr, client_id_len); + if (r) { + nm_utils_error_set_errno (error, r, "failed to set client-id: %s"); + return FALSE; + } + + r = n_dhcp4_client_new (&client, config); + if (r) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "failed to create client: error %d", r); + return FALSE; + } + + priv->client = client; + client = NULL; + + n_dhcp4_client_get_fd (priv->client, &fd); + priv->channel = g_io_channel_unix_new (fd); + priv->event_id = g_io_add_watch (priv->channel, G_IO_IN, dhcp4_event_cb, self); + + return TRUE; +} + +static gboolean +_accept (NMDhcpClient *client, + GError **error) +{ + NMDhcpNettools *self = NM_DHCP_NETTOOLS (client); + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + int r; + + g_return_val_if_fail (priv->lease, FALSE); + + _LOGT ("accept"); + + r = n_dhcp4_client_lease_accept (priv->lease); + if (r) { + nm_utils_error_set_errno (error, r, "failed to accept lease: %s"); + return FALSE; + } + + priv->lease = n_dhcp4_client_lease_unref (priv->lease); + + return TRUE; +} + +static gboolean +decline (NMDhcpClient *client, + const char *error_message, + GError **error) +{ + NMDhcpNettools *self = NM_DHCP_NETTOOLS (client); + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + int r; + + g_return_val_if_fail (priv->lease, FALSE); + + _LOGT ("dhcp4-client: decline"); + + r = n_dhcp4_client_lease_decline (priv->lease, error_message); + if (r) { + nm_utils_error_set_errno (error, r, "failed to decline lease: %s"); + return FALSE; + } + + priv->lease = n_dhcp4_client_lease_unref (priv->lease); + + return TRUE; +} + +static gboolean +ip4_start (NMDhcpClient *client, + const char *dhcp_anycast_addr, + const char *last_ip4_address, + GError **error) +{ + nm_auto (n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL; + NMDhcpNettools *self = NM_DHCP_NETTOOLS (client); + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + struct in_addr last_addr = { 0 }; + const char *hostname; + int r, i; + + g_return_val_if_fail (!priv->probe, FALSE); + + if (!nettools_create (self, dhcp_anycast_addr, error)) + return FALSE; + + r = n_dhcp4_client_probe_config_new (&config); + if (r) { + nm_utils_error_set_errno (error, r, "failed to create dhcp-client-probe-config: %s"); + return FALSE; + } + + /* + * FIXME: + * Select, or configure, a reasonable start delay, to protect poor servers beeing flooded. + */ + n_dhcp4_client_probe_config_set_start_delay (config, 1); + + if (last_ip4_address) { + inet_pton (AF_INET, last_ip4_address, &last_addr); + n_dhcp4_client_probe_config_set_requested_ip (config, last_addr); + } + + /* Add requested options */ + for (i = 0; _nm_dhcp_option_dhcp4_options[i].name; i++) { + if (_nm_dhcp_option_dhcp4_options[i].include) { + nm_assert (_nm_dhcp_option_dhcp4_options[i].option_num <= 255); + n_dhcp4_client_probe_config_request_option (config, + _nm_dhcp_option_dhcp4_options[i].option_num); + } + } + + hostname = nm_dhcp_client_get_hostname (client); + if (hostname) { + if (nm_dhcp_client_get_use_fqdn (client)) { + uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH]; + + buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */ + DHCP_FQDN_FLAG_E; /* Canonical wire format */ + buffer[1] = 0; /* RCODE1 (deprecated) */ + buffer[2] = 0; /* RCODE2 (deprecated) */ + + r = nm_sd_dns_name_to_wire_format (hostname, + buffer + 3, + sizeof (buffer) - 3, + FALSE); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to convert DHCP FQDN: %s"); + return FALSE; + } + + r = n_dhcp4_client_probe_config_append_option (config, + NM_DHCP_OPTION_DHCP4_CLIENT_FQDN, + buffer, + 3 + r); + if (r) { + nm_utils_error_set_errno (error, r, "failed to set DHCP FQDN: %s"); + return FALSE; + } + } else { + r = n_dhcp4_client_probe_config_append_option (config, + NM_DHCP_OPTION_DHCP4_HOST_NAME, + hostname, + strlen (hostname)); + if (r) { + nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s"); + return FALSE; + } + } + } + + r = n_dhcp4_client_probe (priv->client, &priv->probe, config); + if (r) { + nm_utils_error_set_errno (error, r, "failed to start DHCP client: %s"); + return FALSE; + } + + _LOGT ("dhcp-client4: start %p", (gpointer) priv->client); + + nm_dhcp_client_start_timeout (client); + return TRUE; +} + +static gboolean +ip6_start (NMDhcpClient *client, + const char *dhcp_anycast_addr, + const struct in6_addr *ll_addr, + NMSettingIP6ConfigPrivacy privacy, + guint needed_prefixes, + GError **error) +{ + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "nettools plugin does not support IPv6"); + return FALSE; +} + +static void +stop (NMDhcpClient *client, + gboolean release) +{ + NMDhcpNettools *self = NM_DHCP_NETTOOLS (client); + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + + NM_DHCP_CLIENT_CLASS (nm_dhcp_nettools_parent_class)->stop (client, release); + + _LOGT ("dhcp-client4: stop %p", + (gpointer) priv->client); + + priv->probe = n_dhcp4_client_probe_free (priv->probe); +} + +/*****************************************************************************/ + +static void +nm_dhcp_nettools_init (NMDhcpNettools *self) +{ +} + +static void +dispose (GObject *object) +{ + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE ((NMDhcpNettools *) object); + + nm_clear_pointer (&priv->channel, g_io_channel_unref); + nm_clear_g_source (&priv->event_id); + nm_clear_pointer (&priv->lease, n_dhcp4_client_lease_unref); + nm_clear_pointer (&priv->probe, n_dhcp4_client_probe_free); + nm_clear_pointer (&priv->client, n_dhcp4_client_unref); + + G_OBJECT_CLASS (nm_dhcp_nettools_parent_class)->dispose (object); +} + +static void +nm_dhcp_nettools_class_init (NMDhcpNettoolsClass *class) +{ + NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = dispose; + + client_class->ip4_start = ip4_start; + client_class->ip6_start = ip6_start; + client_class->accept = _accept; + client_class->decline = decline; + client_class->stop = stop; +} + +const NMDhcpClientFactory _nm_dhcp_client_factory_nettools = { + .name = "nettools", + .get_type = nm_dhcp_nettools_get_type, + .get_path = NULL, +}; diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c new file mode 100644 index 00000000..21963070 --- /dev/null +++ b/src/dhcp/nm-dhcp-options.c @@ -0,0 +1,278 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-dhcp-options.h" + + +#define REQPREFIX "requested_" + +#define REQ(_num, _name, _include) \ + { \ + .name = REQPREFIX""_name, \ + .option_num = _num, \ + .include = _include, \ + } + +const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = { + REQ (NM_DHCP_OPTION_DHCP4_SUBNET_MASK, "subnet_mask", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_TIME_OFFSET, "time_offset", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, "domain_name_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_HOST_NAME, "host_name", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, "domain_name", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, "interface_mtu", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_BROADCAST, "broadcast_address", TRUE ), + /* RFC 3442: The Classless Static Routes option code MUST appear in the parameter + * request list prior to both the Router option code and the Static + * Routes option code, if present. */ + REQ (NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, "rfc3442_classless_static_routes", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_ROUTER, "routers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, "static_routes", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, "nis_domain", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_NIS_SERVERS, "nis_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_NTP_SERVER, "ntp_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_SERVER_ID, "dhcp_server_identifier", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, "domain_search", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE, "ms_classless_static_routes", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, "wpad", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_ROOT_PATH, "root_path", TRUE ), + + REQ (NM_DHCP_OPTION_DHCP4_TIME_SERVERS, "time_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IEN116_NAME_SERVERS, "ien116_name_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LOG_SERVERS, "log_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_COOKIE_SERVERS, "cookie_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LPR_SERVERS, "lpr_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IMPRESS_SERVERS, "impress_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_RESOURCE_LOCATION_SERVERS, "resource_location_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_BOOT_FILE_SIZE, "boot_size", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_MERIT_DUMP, "merit_dump", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SWAP_SERVER, "swap_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_EXTENSIONS_PATH, "extensions_path", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ENABLE_IP_FORWARDING, "ip_forwarding", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ENABLE_SRC_ROUTING, "non_local_source_routing", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_POLICY_FILTER, "policy_filter", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_INTERFACE_MDR, "max_dgram_reassembly", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_INTERFACE_TTL, "default_ip_ttl", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_INTERFACE_MTU_AGING_TIMEOUT, "path_mtu_aging_timeout", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PATH_MTU_PLATEAU_TABLE, "path_mtu_plateau_table", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ALL_SUBNETS_LOCAL, "all_subnets_local", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PERFORM_MASK_DISCOVERY, "perform_mask_discovery", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_MASK_SUPPLIER, "mask_supplier", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ROUTER_DISCOVERY, "router_discovery", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ROUTER_SOLICITATION_ADDR, "router_solicitation_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TRAILER_ENCAPSULATION, "trailer_encapsulation", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ARP_CACHE_TIMEOUT, "arp_cache_timeout", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IEEE802_3_ENCAPSULATION, "ieee802_3_encapsulation", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_DEFAULT_TCP_TTL, "default_tcp_ttl", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TCP_KEEPALIVE_INTERVAL, "tcp_keepalive_internal", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TCP_KEEPALIVE_GARBAGE, "tcp_keepalive_garbage", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_VENDOR_SPECIFIC, "vendor_encapsulated_options", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER, "netbios_name_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NETBIOS_DD_SERVER, "netbios_dd_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_FONT_SERVERS, "font_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_X_DISPLAY_MANAGER, "x_display_manager", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME, "dhcp_lease_time", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME, "dhcp_renewal_time", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME, "dhcp_rebinding_time", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_CLIENT_ID, "dhcp_client_identifier", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE, "tcode", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NWIP_DOMAIN, "nwip_domain", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NWIP_SUBOPTIONS, "nwip_suboptions", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NISPLUS_DOMAIN, "nisplus_domain", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NISPLUS_SERVERS, "nisplus_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TFTP_SERVER_NAME, "tftp_server_name", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, "bootfile_name", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_MOBILE_IP_HOME_AGENT, "mobile_ip_home_agent", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SMTP_SERVER, "smtp_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_POP_SERVER, "pop_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NNTP_SERVER, "nntp_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_WWW_SERVER, "www_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_FINGER_SERVER, "finger_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IRC_SERVER, "irc_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_STREETTALK_SERVER, "streettalk_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_STREETTALK_DIR_ASSIST_SERVER, "streettalk_directory_assistance_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SLP_DIRECTORY_AGENT, "slp_directory_agent", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SLP_SERVICE_SCOPE, "slp_service_scope", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_CLIENT_FQDN, "fqdn", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_RELAY_AGENT_INFORMATION, "relay_agent_information", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NDS_SERVERS, "nds_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NDS_TREE_NAME, "nds_tree_name", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NDS_CONTEXT, "nds_context", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_BCMS_CONTROLLER_NAMES, "bcms_controller_names", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_BCMS_CONTROLLER_ADDRESS, "bcms_controller_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_CLIENT_LAST_TRANSACTION, "client_last_transaction_time", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ASSOCIATED_IP, "associated_ip", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PXE_SYSTEM_TYPE, "pxe_system_type", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PXE_INTERFACE_ID, "pxe_interface_id", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PXE_CLIENT_ID, "pxe_client_id", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_UAP_SERVERS, "uap_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_GEOCONF_CIVIC, "geoconf_civic", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_ADDRESS, "netinfo_server_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_TAG, "netinfo_server_tag", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_DEFAULT_URL, "default_url", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_AUTO_CONFIG, "auto_config", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NAME_SERVICE_SEARCH, "name_service_search", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SUBNET_SELECTION, "subnet_selection", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_VIVCO, "vivco", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_VIVSO, "vivso", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PANA_AGENT, "pana_agent", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_V4_LOST, "v4_lost", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SIP_UA_CS_DOMAINS, "sip_ua_cs_domains", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IPV4_ADDRESS_ANDSF, "ipv4_address_andsf", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_RDNSS_SELECTION, "rndss_selection", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TFTP_SERVER_ADDRESS, "tftp_server_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_V4_PORTPARAMS, "v4_portparams", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_V4_CAPTIVE_PORTAL, "v4_captive_portal", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LOADER_CONFIGFILE, "loader_configfile", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LOADER_PATHPREFIX, "loader_pathprefix", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LOADER_REBOOTTIME, "loader_reboottime", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_OPTION_6RD, "option_6rd", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_V4_ACCESS_DOMAIN, "v4_access_domain", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_224, "private_224", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_225, "private_225", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_226, "private_226", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_227, "private_227", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_228, "private_228", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_229, "private_229", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_230, "private_230", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_231, "private_231", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_232, "private_232", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_233, "private_233", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_234, "private_234", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_235, "private_235", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_236, "private_236", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_237, "private_237", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_238, "private_238", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_239, "private_239", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_240, "private_240", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_241, "private_241", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_242, "private_242", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_243, "private_243", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_244, "private_244", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_245, "private_245", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_246, "private_246", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_247, "private_247", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_248, "private_248", FALSE ), + /* NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE */ + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_250, "private_250", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_251, "private_251", FALSE ), + /* NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY */ + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_253, "private_253", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_254, "private_254", FALSE ), + + /* Internal values */ + REQ (NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, "ip_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NM_EXPIRY, "expiry", FALSE ), + + { 0 } +}; + +const NMDhcpOption _nm_dhcp_option_dhcp6_options[] = { + REQ (NM_DHCP_OPTION_DHCP6_CLIENTID, "dhcp6_client_id", FALSE ), + + /* Don't request server ID by default; some servers don't reply to + * Information Requests that request the Server ID. + */ + REQ (NM_DHCP_OPTION_DHCP6_SERVERID, "dhcp6_server_id", FALSE ), + + REQ (NM_DHCP_OPTION_DHCP6_DNS_SERVERS, "dhcp6_name_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP6_DOMAIN_LIST, "dhcp6_domain_search", TRUE ), + REQ (NM_DHCP_OPTION_DHCP6_SNTP_SERVERS, "dhcp6_sntp_servers", TRUE ), + + /* Internal values */ + REQ (NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS, "ip6_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_PREFIXLEN, "ip6_prefixlen", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_PREFERRED_LIFE, "preferred_life", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_MAX_LIFE, "max_life", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_STARTS, "starts", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_LIFE_STARTS, "life_starts", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_RENEW, "renew", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_REBIND, "rebind", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_IAID, "iaid", FALSE ), + + { 0 } +}; + + +const char * +nm_dhcp_option_request_string (const NMDhcpOption *requests, guint option) +{ + guint i = 0; + + while (requests[i].name) { + if (requests[i].option_num == option) + return requests[i].name + NM_STRLEN (REQPREFIX); + i++; + } + + /* Option should always be found */ + nm_assert_not_reached (); + return NULL; +} + +void +nm_dhcp_option_take_option (GHashTable *options, + const NMDhcpOption *requests, + guint option, + char *value) +{ + nm_assert (options); + nm_assert (requests); + nm_assert (value); + + g_hash_table_insert (options, + (gpointer) nm_dhcp_option_request_string (requests, option), + value); +} + +void +nm_dhcp_option_add_option (GHashTable *options, const NMDhcpOption *requests, guint option, const char *value) +{ + if (options) + nm_dhcp_option_take_option (options, requests, option, g_strdup (value)); +} + +void +nm_dhcp_option_add_option_u64 (GHashTable *options, const NMDhcpOption *requests, guint option, guint64 value) +{ + if (options) + nm_dhcp_option_take_option (options, requests, option, g_strdup_printf ("%" G_GUINT64_FORMAT, value)); +} + +void +nm_dhcp_option_add_requests_to_options (GHashTable *options, const NMDhcpOption *requests) +{ + guint i; + + if (!options) + return; + + for (i = 0; requests[i].name; i++) { + if (requests[i].include) + g_hash_table_insert (options, (gpointer) requests[i].name, g_strdup ("1")); + } +} + +GHashTable * +nm_dhcp_option_create_options_dict (void) +{ + return g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); +} + diff --git a/src/dhcp/nm-dhcp-options.h b/src/dhcp/nm-dhcp-options.h new file mode 100644 index 00000000..0dc7a276 --- /dev/null +++ b/src/dhcp/nm-dhcp-options.h @@ -0,0 +1,204 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#ifndef __NM_DHCP_OPTIONS_H__ +#define __NM_DHCP_OPTIONS_H__ + +typedef enum { + NM_DHCP_OPTION_DHCP4_PAD = 0, + NM_DHCP_OPTION_DHCP4_SUBNET_MASK = 1, + NM_DHCP_OPTION_DHCP4_TIME_OFFSET = 2, + NM_DHCP_OPTION_DHCP4_ROUTER = 3, + NM_DHCP_OPTION_DHCP4_TIME_SERVERS = 4, + NM_DHCP_OPTION_DHCP4_IEN116_NAME_SERVERS = 5, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER = 6, + NM_DHCP_OPTION_DHCP4_LOG_SERVERS = 7, + NM_DHCP_OPTION_DHCP4_COOKIE_SERVERS = 8, + NM_DHCP_OPTION_DHCP4_LPR_SERVERS = 9, + NM_DHCP_OPTION_DHCP4_IMPRESS_SERVERS = 10, + NM_DHCP_OPTION_DHCP4_RESOURCE_LOCATION_SERVERS = 11, + NM_DHCP_OPTION_DHCP4_HOST_NAME = 12, + NM_DHCP_OPTION_DHCP4_BOOT_FILE_SIZE = 13, + NM_DHCP_OPTION_DHCP4_MERIT_DUMP = 14, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME = 15, + NM_DHCP_OPTION_DHCP4_SWAP_SERVER = 16, + NM_DHCP_OPTION_DHCP4_ROOT_PATH = 17, + NM_DHCP_OPTION_DHCP4_EXTENSIONS_PATH = 18, + NM_DHCP_OPTION_DHCP4_ENABLE_IP_FORWARDING = 19, + NM_DHCP_OPTION_DHCP4_ENABLE_SRC_ROUTING = 20, + NM_DHCP_OPTION_DHCP4_POLICY_FILTER = 21, + NM_DHCP_OPTION_DHCP4_INTERFACE_MDR = 22, + NM_DHCP_OPTION_DHCP4_INTERFACE_TTL = 23, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU_AGING_TIMEOUT = 24, + NM_DHCP_OPTION_DHCP4_PATH_MTU_PLATEAU_TABLE = 25, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU = 26, + NM_DHCP_OPTION_DHCP4_ALL_SUBNETS_LOCAL = 27, + NM_DHCP_OPTION_DHCP4_BROADCAST = 28, + NM_DHCP_OPTION_DHCP4_PERFORM_MASK_DISCOVERY = 29, + NM_DHCP_OPTION_DHCP4_MASK_SUPPLIER = 30, + NM_DHCP_OPTION_DHCP4_ROUTER_DISCOVERY = 31, + NM_DHCP_OPTION_DHCP4_ROUTER_SOLICITATION_ADDR = 32, + NM_DHCP_OPTION_DHCP4_STATIC_ROUTE = 33, + NM_DHCP_OPTION_DHCP4_TRAILER_ENCAPSULATION = 34, + NM_DHCP_OPTION_DHCP4_ARP_CACHE_TIMEOUT = 35, + NM_DHCP_OPTION_DHCP4_IEEE802_3_ENCAPSULATION = 36, + NM_DHCP_OPTION_DHCP4_DEFAULT_TCP_TTL = 37, + NM_DHCP_OPTION_DHCP4_TCP_KEEPALIVE_INTERVAL = 38, + NM_DHCP_OPTION_DHCP4_TCP_KEEPALIVE_GARBAGE = 39, + NM_DHCP_OPTION_DHCP4_NIS_DOMAIN = 40, + NM_DHCP_OPTION_DHCP4_NIS_SERVERS = 41, + NM_DHCP_OPTION_DHCP4_NTP_SERVER = 42, + NM_DHCP_OPTION_DHCP4_VENDOR_SPECIFIC = 43, + NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER = 44, + NM_DHCP_OPTION_DHCP4_NETBIOS_DD_SERVER = 45, + NM_DHCP_OPTION_DHCP4_FONT_SERVERS = 48, + NM_DHCP_OPTION_DHCP4_X_DISPLAY_MANAGER = 49, + NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME = 51, + NM_DHCP_OPTION_DHCP4_SERVER_ID = 54, + NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME = 58, + NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME = 59, + NM_DHCP_OPTION_DHCP4_CLIENT_ID = 61, + NM_DHCP_OPTION_DHCP4_NWIP_DOMAIN = 62, + NM_DHCP_OPTION_DHCP4_NWIP_SUBOPTIONS = 63, + NM_DHCP_OPTION_DHCP4_NISPLUS_DOMAIN = 64, + NM_DHCP_OPTION_DHCP4_NISPLUS_SERVERS = 65, + NM_DHCP_OPTION_DHCP4_TFTP_SERVER_NAME = 66, + NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME = 67, + NM_DHCP_OPTION_DHCP4_MOBILE_IP_HOME_AGENT = 68, + NM_DHCP_OPTION_DHCP4_SMTP_SERVER = 69, + NM_DHCP_OPTION_DHCP4_POP_SERVER = 70, + NM_DHCP_OPTION_DHCP4_NNTP_SERVER = 71, + NM_DHCP_OPTION_DHCP4_WWW_SERVER = 72, + NM_DHCP_OPTION_DHCP4_FINGER_SERVER = 73, + NM_DHCP_OPTION_DHCP4_IRC_SERVER = 74, + NM_DHCP_OPTION_DHCP4_STREETTALK_SERVER = 75, + NM_DHCP_OPTION_DHCP4_STREETTALK_DIR_ASSIST_SERVER = 76, + NM_DHCP_OPTION_DHCP4_SLP_DIRECTORY_AGENT = 78, + NM_DHCP_OPTION_DHCP4_SLP_SERVICE_SCOPE = 79, + NM_DHCP_OPTION_DHCP4_CLIENT_FQDN = 81, + NM_DHCP_OPTION_DHCP4_RELAY_AGENT_INFORMATION = 82, + NM_DHCP_OPTION_DHCP4_NDS_SERVERS = 85, + NM_DHCP_OPTION_DHCP4_NDS_TREE_NAME = 86, + NM_DHCP_OPTION_DHCP4_NDS_CONTEXT = 87, + NM_DHCP_OPTION_DHCP4_BCMS_CONTROLLER_NAMES = 88, + NM_DHCP_OPTION_DHCP4_BCMS_CONTROLLER_ADDRESS = 89, + NM_DHCP_OPTION_DHCP4_CLIENT_LAST_TRANSACTION = 91, + NM_DHCP_OPTION_DHCP4_ASSOCIATED_IP = 92, + NM_DHCP_OPTION_DHCP4_PXE_SYSTEM_TYPE = 93, + NM_DHCP_OPTION_DHCP4_PXE_INTERFACE_ID = 94, + NM_DHCP_OPTION_DHCP4_PXE_CLIENT_ID = 97, + NM_DHCP_OPTION_DHCP4_UAP_SERVERS = 98, + NM_DHCP_OPTION_DHCP4_GEOCONF_CIVIC = 99, + NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE = 101, + NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_ADDRESS = 112, + NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_TAG = 113, + NM_DHCP_OPTION_DHCP4_DEFAULT_URL = 114, + NM_DHCP_OPTION_DHCP4_AUTO_CONFIG = 116, + NM_DHCP_OPTION_DHCP4_NAME_SERVICE_SEARCH = 117, + NM_DHCP_OPTION_DHCP4_SUBNET_SELECTION = 118, + NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST = 119, + NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE = 121, + NM_DHCP_OPTION_DHCP4_VIVCO = 124, + NM_DHCP_OPTION_DHCP4_VIVSO = 125, + NM_DHCP_OPTION_DHCP4_PANA_AGENT = 136, + NM_DHCP_OPTION_DHCP4_V4_LOST = 137, + NM_DHCP_OPTION_DHCP4_SIP_UA_CS_DOMAINS = 141, + NM_DHCP_OPTION_DHCP4_IPV4_ADDRESS_ANDSF = 142, + NM_DHCP_OPTION_DHCP4_RDNSS_SELECTION = 146, + NM_DHCP_OPTION_DHCP4_TFTP_SERVER_ADDRESS = 150, + NM_DHCP_OPTION_DHCP4_V4_PORTPARAMS = 159, + NM_DHCP_OPTION_DHCP4_V4_CAPTIVE_PORTAL = 160, + NM_DHCP_OPTION_DHCP4_LOADER_CONFIGFILE = 209, + NM_DHCP_OPTION_DHCP4_LOADER_PATHPREFIX = 210, + NM_DHCP_OPTION_DHCP4_LOADER_REBOOTTIME = 211, + NM_DHCP_OPTION_DHCP4_OPTION_6RD = 212, + NM_DHCP_OPTION_DHCP4_V4_ACCESS_DOMAIN = 213, + NM_DHCP_OPTION_DHCP4_PRIVATE_224 = 224, + NM_DHCP_OPTION_DHCP4_PRIVATE_225 = 225, + NM_DHCP_OPTION_DHCP4_PRIVATE_226 = 226, + NM_DHCP_OPTION_DHCP4_PRIVATE_227 = 227, + NM_DHCP_OPTION_DHCP4_PRIVATE_228 = 228, + NM_DHCP_OPTION_DHCP4_PRIVATE_229 = 229, + NM_DHCP_OPTION_DHCP4_PRIVATE_230 = 230, + NM_DHCP_OPTION_DHCP4_PRIVATE_231 = 231, + NM_DHCP_OPTION_DHCP4_PRIVATE_232 = 232, + NM_DHCP_OPTION_DHCP4_PRIVATE_233 = 233, + NM_DHCP_OPTION_DHCP4_PRIVATE_234 = 234, + NM_DHCP_OPTION_DHCP4_PRIVATE_235 = 235, + NM_DHCP_OPTION_DHCP4_PRIVATE_236 = 236, + NM_DHCP_OPTION_DHCP4_PRIVATE_237 = 237, + NM_DHCP_OPTION_DHCP4_PRIVATE_238 = 238, + NM_DHCP_OPTION_DHCP4_PRIVATE_239 = 239, + NM_DHCP_OPTION_DHCP4_PRIVATE_240 = 240, + NM_DHCP_OPTION_DHCP4_PRIVATE_241 = 241, + NM_DHCP_OPTION_DHCP4_PRIVATE_242 = 242, + NM_DHCP_OPTION_DHCP4_PRIVATE_243 = 243, + NM_DHCP_OPTION_DHCP4_PRIVATE_244 = 244, + NM_DHCP_OPTION_DHCP4_PRIVATE_245 = 245, + NM_DHCP_OPTION_DHCP4_PRIVATE_246 = 246, + NM_DHCP_OPTION_DHCP4_PRIVATE_247 = 247, + NM_DHCP_OPTION_DHCP4_PRIVATE_248 = 248, + NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, + NM_DHCP_OPTION_DHCP4_PRIVATE_250 = 250, + NM_DHCP_OPTION_DHCP4_PRIVATE_251 = 251, + NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY = 252, + NM_DHCP_OPTION_DHCP4_PRIVATE_253 = 253, + NM_DHCP_OPTION_DHCP4_PRIVATE_254 = 254, + NM_DHCP_OPTION_DHCP4_END = 255, + /* Internal values */ + NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS = 1024, + NM_DHCP_OPTION_DHCP4_NM_EXPIRY = 1025, +} NMDhcpOptionDhcp4Options; + +typedef enum { + NM_DHCP_OPTION_DHCP6_CLIENTID = 1, + NM_DHCP_OPTION_DHCP6_SERVERID = 2, + NM_DHCP_OPTION_DHCP6_DNS_SERVERS = 23, + NM_DHCP_OPTION_DHCP6_DOMAIN_LIST = 24, + NM_DHCP_OPTION_DHCP6_SNTP_SERVERS = 31, + /* Internal values */ + NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS = 1026, + NM_DHCP_OPTION_DHCP6_NM_PREFIXLEN = 1027, + NM_DHCP_OPTION_DHCP6_NM_PREFERRED_LIFE = 1028, + NM_DHCP_OPTION_DHCP6_NM_MAX_LIFE = 1029, + NM_DHCP_OPTION_DHCP6_NM_STARTS = 1030, + NM_DHCP_OPTION_DHCP6_NM_LIFE_STARTS = 1031, + NM_DHCP_OPTION_DHCP6_NM_RENEW = 1032, + NM_DHCP_OPTION_DHCP6_NM_REBIND = 1033, + NM_DHCP_OPTION_DHCP6_NM_IAID = 1034, + +} NMDhcpOptionDhcp6Options; + +typedef struct { + const char *name; + uint16_t option_num; + bool include; +} NMDhcpOption; + +extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[]; +extern const NMDhcpOption _nm_dhcp_option_dhcp6_options[]; + +const char *nm_dhcp_option_request_string (const NMDhcpOption *requests, guint option); +void nm_dhcp_option_take_option (GHashTable *options, const NMDhcpOption *requests, guint option, char *value); +void nm_dhcp_option_add_option (GHashTable *options, const NMDhcpOption *requests, guint option, const char *value); +void nm_dhcp_option_add_option_u64 (GHashTable *options, const NMDhcpOption *requests, guint option, guint64 value); +void nm_dhcp_option_add_requests_to_options (GHashTable *options, const NMDhcpOption *requests); +GHashTable *nm_dhcp_option_create_options_dict (void); + +#endif /* __NM_DHCP_OPTIONS_H__ */ diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index 1cd5ba27..073a6da0 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -1,8 +1,8 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* 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, or (at your option) - * any later version. +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -32,11 +32,13 @@ #include "nm-utils.h" #include "nm-config.h" #include "nm-dhcp-utils.h" +#include "nm-dhcp-options.h" #include "nm-core-utils.h" #include "NetworkManagerUtils.h" #include "platform/nm-platform.h" #include "nm-dhcp-client-logging.h" #include "systemd/nm-sd.h" +#include "systemd/nm-sd-utils-dhcp.h" /*****************************************************************************/ @@ -79,160 +81,10 @@ G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT) /*****************************************************************************/ -#define DHCP_OPTION_NIS_DOMAIN 40 -#define DHCP_OPTION_NIS_SERVERS 41 - -/* Internal values */ -#define DHCP_OPTION_IP_ADDRESS 1024 -#define DHCP_OPTION_EXPIRY 1025 -#define DHCP6_OPTION_IP_ADDRESS 1026 -#define DHCP6_OPTION_PREFIXLEN 1027 -#define DHCP6_OPTION_PREFERRED_LIFE 1028 -#define DHCP6_OPTION_MAX_LIFE 1029 -#define DHCP6_OPTION_STARTS 1030 -#define DHCP6_OPTION_LIFE_STARTS 1031 -#define DHCP6_OPTION_RENEW 1032 -#define DHCP6_OPTION_REBIND 1033 -#define DHCP6_OPTION_IAID 1034 - -typedef struct { - const char *name; - uint16_t option_num; - bool include; -} ReqOption; - -#define REQPREFIX "requested_" - -#define REQ(_num, _name, _include) \ - { \ - .name = REQPREFIX""_name, \ - .option_num = _num, \ - .include = _include, \ - } - -static const ReqOption dhcp4_requests[] = { - REQ (SD_DHCP_OPTION_SUBNET_MASK, "subnet_mask", TRUE ), - REQ (SD_DHCP_OPTION_TIME_OFFSET, "time_offset", TRUE ), - REQ (SD_DHCP_OPTION_DOMAIN_NAME_SERVER, "domain_name_servers", TRUE ), - REQ (SD_DHCP_OPTION_HOST_NAME, "host_name", TRUE ), - REQ (SD_DHCP_OPTION_DOMAIN_NAME, "domain_name", TRUE ), - REQ (SD_DHCP_OPTION_INTERFACE_MTU, "interface_mtu", TRUE ), - REQ (SD_DHCP_OPTION_BROADCAST, "broadcast_address", TRUE ), - - /* RFC 3442: The Classless Static Routes option code MUST appear in the parameter - * request list prior to both the Router option code and the Static - * Routes option code, if present. */ - REQ (SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, "rfc3442_classless_static_routes", TRUE ), - REQ (SD_DHCP_OPTION_ROUTER, "routers", TRUE ), - REQ (SD_DHCP_OPTION_STATIC_ROUTE, "static_routes", TRUE ), - - REQ (DHCP_OPTION_NIS_DOMAIN, "nis_domain", TRUE ), - REQ (DHCP_OPTION_NIS_SERVERS, "nis_servers", TRUE ), - REQ (SD_DHCP_OPTION_NTP_SERVER, "ntp_servers", TRUE ), - REQ (SD_DHCP_OPTION_SERVER_IDENTIFIER, "dhcp_server_identifier", TRUE ), - REQ (SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, "domain_search", TRUE ), - REQ (SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, "ms_classless_static_routes", TRUE ), - REQ (SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, "wpad", TRUE ), - REQ (SD_DHCP_OPTION_ROOT_PATH, "root_path", TRUE ), - - /* Internal values */ - REQ (SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, "expiry", FALSE ), - REQ (SD_DHCP_OPTION_CLIENT_IDENTIFIER, "dhcp_client_identifier", FALSE ), - REQ (DHCP_OPTION_IP_ADDRESS, "ip_address", FALSE ), - - { 0 } -}; - -static const ReqOption dhcp6_requests[] = { - REQ (SD_DHCP6_OPTION_CLIENTID, "dhcp6_client_id", FALSE ), - - /* Don't request server ID by default; some servers don't reply to - * Information Requests that request the Server ID. - */ - REQ (SD_DHCP6_OPTION_SERVERID, "dhcp6_server_id", FALSE ), - - REQ (SD_DHCP6_OPTION_DNS_SERVERS, "dhcp6_name_servers", TRUE ), - REQ (SD_DHCP6_OPTION_DOMAIN_LIST, "dhcp6_domain_search", TRUE ), - REQ (SD_DHCP6_OPTION_SNTP_SERVERS, "dhcp6_sntp_servers", TRUE ), - - /* Internal values */ - REQ (DHCP6_OPTION_IP_ADDRESS, "ip6_address", FALSE ), - REQ (DHCP6_OPTION_PREFIXLEN, "ip6_prefixlen", FALSE ), - REQ (DHCP6_OPTION_PREFERRED_LIFE, "preferred_life", FALSE ), - REQ (DHCP6_OPTION_MAX_LIFE, "max_life", FALSE ), - REQ (DHCP6_OPTION_STARTS, "starts", FALSE ), - REQ (DHCP6_OPTION_LIFE_STARTS, "life_starts", FALSE ), - REQ (DHCP6_OPTION_RENEW, "renew", FALSE ), - REQ (DHCP6_OPTION_REBIND, "rebind", FALSE ), - REQ (DHCP6_OPTION_IAID, "iaid", FALSE ), - - { 0 } -}; - -static void -take_option (GHashTable *options, - const ReqOption *requests, - guint option, - char *value) -{ - guint i; - - nm_assert (options); - nm_assert (requests); - nm_assert (value); - - for (i = 0; requests[i].name; i++) { - nm_assert (g_str_has_prefix (requests[i].name, REQPREFIX)); - if (requests[i].option_num == option) { - g_hash_table_insert (options, - (gpointer) (requests[i].name + NM_STRLEN (REQPREFIX)), - value); - return; - } - } - - /* Option should always be found */ - nm_assert_not_reached (); -} - -static void -add_option (GHashTable *options, const ReqOption *requests, guint option, const char *value) -{ - if (options) - take_option (options, requests, option, g_strdup (value)); -} - -static void -add_option_u64 (GHashTable *options, const ReqOption *requests, guint option, guint64 value) -{ - if (options) - take_option (options, requests, option, g_strdup_printf ("%" G_GUINT64_FORMAT, value)); -} - -static void -add_requests_to_options (GHashTable *options, const ReqOption *requests) -{ - guint i; - - if (!options) - return; - - for (i = 0; requests[i].name; i++) { - if (requests[i].include) - g_hash_table_insert (options, (gpointer) requests[i].name, g_strdup ("1")); - } -} - -static GHashTable * -create_options_dict (void) -{ - return g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); -} - #define LOG_LEASE(domain, ...) \ G_STMT_START { \ if (log_lease) { \ - _LOG2I ((domain), (iface), " "__VA_ARGS__); \ + _LOG2D ((domain), (iface), " "__VA_ARGS__); \ } \ } G_STMT_END @@ -267,9 +119,14 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, gint64 ts_time = time (NULL); struct in_addr a_address; struct in_addr a_netmask; + struct in_addr server_id; + struct in_addr broadcast; const struct in_addr *a_router; guint32 a_plen; guint32 a_lifetime; + guint32 renewal; + guint32 rebinding; + gs_free nm_sd_dhcp_option *private_options = NULL; g_return_val_if_fail (lease != NULL, NULL); @@ -290,26 +147,36 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, ip4_config = nm_ip4_config_new (multi_idx, ifindex); - options = out_options ? create_options_dict () : NULL; + options = out_options ? nm_dhcp_option_create_options_dict () : NULL; nm_utils_inet4_ntop (a_address.s_addr, addr_str); - LOG_LEASE (LOGD_DHCP4, "address %s", addr_str); - add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, addr_str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS), + addr_str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, addr_str); a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr); - LOG_LEASE (LOGD_DHCP4, "plen %u", (guint) a_plen); - add_option (options, - dhcp4_requests, - SD_DHCP_OPTION_SUBNET_MASK, - nm_utils_inet4_ntop (a_netmask.s_addr, addr_str)); - - LOG_LEASE (LOGD_DHCP4, "expires in %u seconds (at %lld)", + LOG_LEASE (LOGD_DHCP4, "%s '%u'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_SUBNET_MASK), + (guint) a_plen); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_SUBNET_MASK, + nm_utils_inet4_ntop (a_netmask.s_addr, addr_str)); + + LOG_LEASE (LOGD_DHCP4, "%s '%u' seconds (at %lld)", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME), (guint) a_lifetime, (long long) (ts_time + a_lifetime)); - add_option_u64 (options, - dhcp4_requests, - SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, - (guint64) (ts_time + a_lifetime)); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME, + a_lifetime); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_NM_EXPIRY, + (guint64) (ts_time + a_lifetime)); nm_ip4_config_add_address (ip4_config, &((const NMPlatformIP4Address) { @@ -322,6 +189,28 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, .preferred = a_lifetime, })); + if (sd_dhcp_lease_get_server_identifier (lease, &server_id) >= 0) { + nm_utils_inet4_ntop (server_id.s_addr, addr_str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_SERVER_ID), + addr_str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_SERVER_ID, + addr_str); + } + + if (sd_dhcp_lease_get_broadcast (lease, &broadcast) >= 0) { + nm_utils_inet4_ntop (broadcast.s_addr, addr_str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_BROADCAST), + addr_str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_BROADCAST, + addr_str); + } + num = sd_dhcp_lease_get_dns (lease, &addr_list); if (num > 0) { nm_gstring_prepare (&str); @@ -337,8 +226,13 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, } nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr); } - LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", str->str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, str->str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, + str->str); } num = sd_dhcp_lease_get_search_domains (lease, (char ***) &search_domains); @@ -348,16 +242,23 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, g_string_append (nm_gstring_add_space_delimiter (str), search_domains[i]); nm_ip4_config_add_search (ip4_config, search_domains[i]); } - LOG_LEASE (LOGD_DHCP4, "domain search '%s'", str->str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, str->str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, + str->str); } if (sd_dhcp_lease_get_domainname (lease, &s) >= 0) { gs_strfreev char **domains = NULL; char **d; - LOG_LEASE (LOGD_DHCP4, "domain name '%s'", s); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, s); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME), + s); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, s); /* Multiple domains sometimes stuffed into option 15 "Domain Name". * As systemd escapes such characters, split them at \\032. */ @@ -367,8 +268,10 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, } if (sd_dhcp_lease_get_hostname (lease, &s) >= 0) { - LOG_LEASE (LOGD_DHCP4, "hostname '%s'", s); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_HOST_NAME, s); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME), + s); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, s); } num = sd_dhcp_lease_get_routes (lease, &routes); @@ -379,10 +282,10 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, for (i = 0; i < num; i++) { switch (sd_dhcp_route_get_option (routes[i])) { - case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: + case NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE: has_classless_route = TRUE; break; - case SD_DHCP_OPTION_STATIC_ROUTE: + case NM_DHCP_OPTION_DHCP4_STATIC_ROUTE: has_static_route = TRUE; break; } @@ -404,8 +307,8 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, guint32 m; option = sd_dhcp_route_get_option (routes[i]); - if (!NM_IN_SET (option, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, - SD_DHCP_OPTION_STATIC_ROUTE)) + if (!NM_IN_SET (option, NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, + NM_DHCP_OPTION_DHCP4_STATIC_ROUTE)) continue; if (sd_dhcp_route_get_destination (routes[i], &r_network) < 0) @@ -422,14 +325,14 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, nm_utils_inet4_ntop (r_gateway.s_addr, gateway_str); LOG_LEASE (LOGD_DHCP4, - "%sstatic route %s/%d gw %s", - option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE - ? "classless " + "%sstatic_route %s/%d gw %s", + option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE + ? "rfc3442_classless_" : "", network_net_str, (int) r_plen, gateway_str); - g_string_append_printf (nm_gstring_add_space_delimiter ( option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE + g_string_append_printf (nm_gstring_add_space_delimiter ( option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE ? str_classless : str_static), "%s/%d %s", @@ -437,7 +340,7 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, (int) r_plen, gateway_str); - if ( option == SD_DHCP_OPTION_STATIC_ROUTE + if ( option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE && has_classless_route) { /* RFC 3443: if the DHCP server returns both a Classless Static Routes * option and a Static Routes option, the DHCP client MUST ignore the @@ -446,7 +349,7 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, } if ( r_plen == 0 - && option == SD_DHCP_OPTION_STATIC_ROUTE) { + && option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE) { /* for option 33 (static route), RFC 2132 says: * * The default route (0.0.0.0) is an illegal destination for a static @@ -478,9 +381,15 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, } if (str_classless && str_classless->len > 0) - add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, str_classless->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, + str_classless->str); if (str_static && str_static->len > 0) - add_option (options, dhcp4_requests, SD_DHCP_OPTION_STATIC_ROUTE, str_static->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, + str_static->str); } num = sd_dhcp_lease_get_router (lease, &a_router); @@ -524,14 +433,21 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, }), NULL); } - LOG_LEASE (LOGD_DHCP4, "router %s", str->str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str->str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROUTER), + str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROUTER, str->str); } if ( sd_dhcp_lease_get_mtu (lease, &mtu) >= 0 && mtu) { - LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu); - add_option_u64 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu); + LOG_LEASE (LOGD_DHCP4, "%s '%u'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU), + mtu); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, + mtu); nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); } @@ -542,19 +458,77 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, nm_utils_inet4_ntop (addr_list[i].s_addr, addr_str); g_string_append (nm_gstring_add_space_delimiter (str), addr_str); } - LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", str->str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, str->str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NTP_SERVER), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_NTP_SERVER, + str->str); } if (sd_dhcp_lease_get_root_path (lease, &s) >= 0) { - LOG_LEASE (LOGD_DHCP4, "root path '%s'", s); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROOT_PATH, s); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH), + s); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH, s); + } + + if (sd_dhcp_lease_get_t1 (lease, &renewal) >= 0) { + LOG_LEASE (LOGD_DHCP4, "%s '%u'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME), + renewal); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME, + renewal); + } + + if (sd_dhcp_lease_get_t2 (lease, &rebinding) >= 0) { + LOG_LEASE (LOGD_DHCP4, "%s '%u'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME), + rebinding); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME, + rebinding); + } + + if (sd_dhcp_lease_get_timezone (lease, &s) >= 0) { + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE), + s); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE, + s); } if (sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len) >= 0) metered = !!memmem (data, data_len, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED")); nm_ip4_config_set_metered (ip4_config, metered); + num = nm_sd_dhcp_lease_get_private_options (lease, &private_options); + if (num > 0) { + for (i = 0; i < num; i++) { + char *option_string; + + option_string = nm_utils_bin2hexstr_full (private_options[i].data, + private_options[i].data_len, + ':', FALSE, NULL); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, private_options[i].code), + option_string); + if (!options) { + g_free (option_string); + continue; + } + nm_dhcp_option_take_option (options, + _nm_dhcp_option_dhcp4_options, + private_options[i].code, + option_string); + } + } NM_SET_OUT (out_options, g_steal_pointer (&options)); return g_steal_pointer (&ip4_config); } @@ -627,7 +601,7 @@ bound4_handle (NMDhcpSystemd *self) return; } - add_requests_to_options (options, dhcp4_requests); + nm_dhcp_option_add_requests_to_options (options, _nm_dhcp_option_dhcp4_options); dhcp_lease_save (lease, priv->lease_file); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), @@ -636,13 +610,13 @@ bound4_handle (NMDhcpSystemd *self) options); } -static void +static int dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - g_assert (priv->client4 == client); + nm_assert (priv->client4 == client); _LOGD ("client event %d", event); @@ -658,10 +632,14 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data) case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE: bound4_handle (self); break; + case SD_DHCP_CLIENT_EVENT_SELECTING: + break; default: _LOGW ("unhandled DHCP event %d", event); break; } + + return 0; } static gboolean @@ -776,10 +754,10 @@ ip4_start (NMDhcpClient *client, } /* Add requested options */ - for (i = 0; dhcp4_requests[i].name; i++) { - if (dhcp4_requests[i].include) { - nm_assert (dhcp4_requests[i].option_num <= 255); - r = sd_dhcp_client_set_request_option (sd_client, dhcp4_requests[i].option_num); + for (i = 0; _nm_dhcp_option_dhcp4_options[i].name; i++) { + if (_nm_dhcp_option_dhcp4_options[i].include) { + nm_assert (_nm_dhcp_option_dhcp4_options[i].option_num <= 255); + r = sd_dhcp_client_set_request_option (sd_client, _nm_dhcp_option_dhcp4_options[i].option_num); nm_assert (r >= 0 || r == -EEXIST); } } @@ -846,7 +824,7 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, ip6_config = nm_ip6_config_new (multi_idx, ifindex); - options = out_options ? create_options_dict () : NULL; + options = out_options ? nm_dhcp_option_create_options_dict () : NULL; sd_dhcp6_lease_reset_address_iter (lease); nm_gstring_prepare (&str); @@ -866,12 +844,15 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, nm_utils_inet6_ntop (&tmp_addr, addr_str); g_string_append (nm_gstring_add_space_delimiter (str), addr_str); - LOG_LEASE (LOGD_DHCP6, - "address %s", + LOG_LEASE (LOGD_DHCP6, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS), nm_platform_ip6_address_to_string (&address, sbuf, sizeof (sbuf))); }; if (str->len) - add_option (options, dhcp6_requests, DHCP6_OPTION_IP_ADDRESS, str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp6_options, + NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS, + str->str); if ( !info_only && nm_ip6_config_get_num_addresses (ip6_config) == 0) { @@ -890,8 +871,13 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, g_string_append (nm_gstring_add_space_delimiter (str), addr_str); nm_ip6_config_add_nameserver (ip6_config, &dns[i]); } - LOG_LEASE (LOGD_DHCP6, "nameserver %s", str->str); - add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DNS_SERVERS, str->str); + LOG_LEASE (LOGD_DHCP6, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_DNS_SERVERS), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp6_options, + NM_DHCP_OPTION_DHCP6_DNS_SERVERS, + str->str); } num = sd_dhcp6_lease_get_domains (lease, &domains); @@ -901,8 +887,13 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, g_string_append (nm_gstring_add_space_delimiter (str), domains[i]); nm_ip6_config_add_search (ip6_config, domains[i]); } - LOG_LEASE (LOGD_DHCP6, "domain name '%s'", str->str); - add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DOMAIN_LIST, str->str); + LOG_LEASE (LOGD_DHCP6, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_DOMAIN_LIST), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp6_options, + NM_DHCP_OPTION_DHCP6_DOMAIN_LIST, + str->str); } NM_SET_OUT (out_options, g_steal_pointer (&options)); @@ -955,7 +946,7 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data) NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - g_assert (priv->client6 == client); + nm_assert (priv->client6 == client); _LOGD ("client event %d", event); @@ -1075,9 +1066,9 @@ ip6_start (NMDhcpClient *client, } /* Add requested options */ - for (i = 0; dhcp6_requests[i].name; i++) { - if (dhcp6_requests[i].include) { - r = sd_dhcp6_client_set_request_option (sd_client, dhcp6_requests[i].option_num); + for (i = 0; _nm_dhcp_option_dhcp6_options[i].name; i++) { + if (_nm_dhcp_option_dhcp6_options[i].include) { + r = sd_dhcp6_client_set_request_option (sd_client, _nm_dhcp_option_dhcp6_options[i].option_num); nm_assert (r >= 0 || r == -EEXIST); } } diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index 7aa867c0..3f9368f2 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* 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, or (at your option) diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h index 5c127bd1..39ae7693 100644 --- a/src/dhcp/nm-dhcp-utils.h +++ b/src/dhcp/nm-dhcp-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* 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, or (at your option) diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index 1eac3643..2cc7e5ca 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * 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 diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c index 118082a5..e4d4c348 100644 --- a/src/dhcp/tests/test-dhcp-utils.c +++ b/src/dhcp/tests/test-dhcp-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* 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, or (at your option) |