about summary refs log tree commit diff
path: root/src/core/dhcp
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2022-05-17 15:30:19 +0200
committerSebastien Bacher <seb128@ubuntu.com>2022-05-17 15:30:19 +0200
commit1e636d8e5e986b9f2260c36bb87fb499d724085c (patch)
treeac613b9372adf622496a7d616050d7e1c09b4fba /src/core/dhcp
parentf4966e573c855d4667e6c236d8197d9949020e21 (diff)
parent1a31bc3c63474ca02c83b02add85ea4e740e5597 (diff)
Merge remote-tracking branch 'debian/debian/master' into ubuntu/master
Diffstat (limited to 'src/core/dhcp')
-rw-r--r--src/core/dhcp/nm-dhcp-client.c33
-rw-r--r--src/core/dhcp/nm-dhcp-dhclient.c21
-rw-r--r--src/core/dhcp/nm-dhcp-dhcpcd.c3
-rw-r--r--src/core/dhcp/nm-dhcp-nettools.c27
-rw-r--r--src/core/dhcp/nm-dhcp-systemd.c177
-rw-r--r--src/core/dhcp/nm-dhcp-utils.c50
6 files changed, 162 insertions, 149 deletions
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c
index ec910389..2bfd7e01 100644
--- a/src/core/dhcp/nm-dhcp-client.c
+++ b/src/core/dhcp/nm-dhcp-client.c
@@ -109,25 +109,22 @@ nm_dhcp_client_get_pid(NMDhcpClient *self)
     return NM_DHCP_CLIENT_GET_PRIVATE(self)->pid;
 }
 
-static void
-_set_effective_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take)
+void
+nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id)
 {
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
 
-    nm_assert(!client_id || g_bytes_get_size(client_id) >= 2);
+    g_return_if_fail(NM_IS_DHCP_CLIENT(self));
+    g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2);
+
+    priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
 
-    if (priv->effective_client_id == client_id
-        || (priv->effective_client_id && client_id
-            && g_bytes_equal(priv->effective_client_id, client_id))) {
-        if (take && client_id)
-            g_bytes_unref(client_id);
+    if (nm_g_bytes_equal0(priv->effective_client_id, client_id))
         return;
-    }
 
-    if (priv->effective_client_id)
-        g_bytes_unref(priv->effective_client_id);
+    g_bytes_unref(priv->effective_client_id);
     priv->effective_client_id = client_id;
-    if (!take && client_id)
+    if (client_id)
         g_bytes_ref(client_id);
 
     {
@@ -141,15 +138,6 @@ _set_effective_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take)
     }
 }
 
-void
-nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id)
-{
-    g_return_if_fail(NM_IS_DHCP_CLIENT(self));
-    g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2);
-
-    _set_effective_client_id(self, client_id, FALSE);
-}
-
 /*****************************************************************************/
 
 NM_UTILS_LOOKUP_STR_DEFINE(nm_dhcp_state_to_string,
@@ -678,7 +666,7 @@ nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error)
     if (!priv->config.v6.enforce_duid)
         own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self);
 
-    _set_effective_client_id(self, own_client_id ?: priv->config.client_id, FALSE);
+    nm_dhcp_client_set_effective_client_id(self, own_client_id ?: priv->config.client_id);
 
     addr = ipv6_lladdr_find(self);
     if (!addr) {
@@ -1209,6 +1197,7 @@ dispose(GObject *object)
 
     nm_clear_g_source_inst(&priv->no_lease_timeout_source);
     nm_clear_g_source_inst(&priv->ipv6_lladdr_timeout_source);
+    nm_clear_pointer(&priv->effective_client_id, g_bytes_unref);
 
     G_OBJECT_CLASS(nm_dhcp_client_parent_class)->dispose(object);
 }
diff --git a/src/core/dhcp/nm-dhcp-dhclient.c b/src/core/dhcp/nm-dhcp-dhclient.c
index 28d40c07..d0cd5ebd 100644
--- a/src/core/dhcp/nm-dhcp-dhclient.c
+++ b/src/core/dhcp/nm-dhcp-dhclient.c
@@ -330,7 +330,7 @@ create_dhclient_config(NMDhcpDhclient     *self,
 
 static gboolean
 dhclient_start(NMDhcpClient *client,
-               const char   *mode_opt,
+               gboolean      set_mode,
                gboolean      release,
                pid_t        *out_pid,
                GError      **error)
@@ -439,14 +439,19 @@ dhclient_start(NMDhcpClient *client,
     }
 
     if (addr_family == AF_INET6) {
-        guint prefixes = client_config->v6.needed_prefixes;
+        guint       prefixes = client_config->v6.needed_prefixes;
+        const char *mode_opt;
 
         g_ptr_array_add(argv, (gpointer) "-6");
 
-        if (prefixes > 0 && nm_streq0(mode_opt, "-S")) {
-            /* -S is incompatible with -P, only use the latter */
+        if (!set_mode)
+            mode_opt = NULL;
+        else if (!client_config->v6.info_only)
+            mode_opt = "-N";
+        else if (prefixes == 0)
+            mode_opt = "-S";
+        else
             mode_opt = NULL;
-        }
 
         if (mode_opt)
             g_ptr_array_add(argv, (gpointer) mode_opt);
@@ -546,7 +551,7 @@ ip4_start(NMDhcpClient *client, GError **error)
         nm_assert(!client_config->client_id);
         nm_dhcp_client_set_effective_client_id(client, new_client_id);
     }
-    return dhclient_start(client, NULL, FALSE, NULL, error);
+    return dhclient_start(client, FALSE, FALSE, NULL, error);
 }
 
 static gboolean
@@ -581,7 +586,7 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error)
         return FALSE;
     }
 
-    return dhclient_start(client, config->v6.needed_prefixes ? "-S" : "-N", FALSE, NULL, error);
+    return dhclient_start(client, TRUE, FALSE, NULL, error);
 }
 
 static void
@@ -615,7 +620,7 @@ stop(NMDhcpClient *client, gboolean release)
     if (release) {
         pid_t rpid = -1;
 
-        if (dhclient_start(client, NULL, TRUE, &rpid, NULL)) {
+        if (dhclient_start(client, FALSE, TRUE, &rpid, NULL)) {
             /* Wait a few seconds for the release to happen */
             nm_dhcp_client_stop_pid(rpid, nm_dhcp_client_get_iface(client));
         }
diff --git a/src/core/dhcp/nm-dhcp-dhcpcd.c b/src/core/dhcp/nm-dhcp-dhcpcd.c
index 45f96ec9..280d9ce6 100644
--- a/src/core/dhcp/nm-dhcp-dhcpcd.c
+++ b/src/core/dhcp/nm-dhcp-dhcpcd.c
@@ -70,7 +70,7 @@ ip4_start(NMDhcpClient *client, GError **error)
     const NMDhcpClientConfig    *client_config;
     gs_unref_ptrarray GPtrArray *argv = NULL;
     pid_t                        pid;
-    GError                      *local;
+    gs_free_error GError        *local   = NULL;
     gs_free char                *cmd_str = NULL;
     const char                  *dhcpcd_path;
 
@@ -143,7 +143,6 @@ ip4_start(NMDhcpClient *client, GError **error)
                            NM_UTILS_ERROR_UNKNOWN,
                            "dhcpcd failed to start: %s",
                            local->message);
-        g_error_free(local);
         return FALSE;
     }
 
diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c
index d7fbe356..aac18967 100644
--- a/src/core/dhcp/nm-dhcp-nettools.c
+++ b/src/core/dhcp/nm-dhcp-nettools.c
@@ -154,6 +154,7 @@ static gboolean
 lease_parse_address(NDhcp4ClientLease *lease,
                     NML3ConfigData    *l3cd,
                     GHashTable        *options,
+                    in_addr_t         *out_address,
                     GError           **error)
 {
     struct in_addr a_address;
@@ -268,6 +269,8 @@ lease_parse_address(NDhcp4ClientLease *lease,
                                         .preferred    = a_lifetime,
                                     }));
 
+    NM_SET_OUT(out_address, a_address.s_addr);
+
     return TRUE;
 }
 
@@ -326,6 +329,7 @@ lease_parse_address_list(NDhcp4ClientLease       *lease,
 static void
 lease_parse_routes(NDhcp4ClientLease *lease,
                    NML3ConfigData    *l3cd,
+                   in_addr_t          lease_address,
                    GHashTable        *options,
                    NMStrBuf          *sbuf)
 {
@@ -343,6 +347,17 @@ lease_parse_routes(NDhcp4ClientLease *lease,
     int           r;
     guint         i;
 
+    /* Routes can be in option 33 (static-route), 121 (classless-static-route) and 249 (a non-standard classless-static-route).
+     * Option 249 (Microsoft Classless Static Route), is described here:
+     * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dhcpe/f9c19c79-1c7f-4746-b555-0c0fc523f3f9
+     *
+     * We will anyway parse all these 3 options and add them to the "options" hash (as distinct entries).
+     * We will however also parse one of the options into the "l3cd" for configuring routing.
+     * Thereby we prefer 121 over 249 over 33.
+     *
+     * Preferring 121 over 33 is defined by RFC 3443.
+     * Preferring 121 over 249 over 33 is made up as it makes sense (the MS docs are not very clear).
+     */
     for (i = 0; i < 2; i++) {
         const guint8 option_code = (i == 0) ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
                                             : NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE;
@@ -373,10 +388,11 @@ lease_parse_routes(NDhcp4ClientLease *lease,
 
             nm_l3_config_data_add_route_4(l3cd,
                                           &((const NMPlatformIP4Route){
+                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .network       = dest,
                                               .plen          = plen,
                                               .gateway       = gateway,
-                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
+                                              .pref_src      = lease_address,
                                               .table_any     = TRUE,
                                               .table_coerced = 0,
                                               .metric_any    = TRUE,
@@ -416,10 +432,11 @@ lease_parse_routes(NDhcp4ClientLease *lease,
 
             nm_l3_config_data_add_route_4(l3cd,
                                           &((const NMPlatformIP4Route){
+                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .network       = dest,
                                               .plen          = plen,
                                               .gateway       = gateway,
-                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
+                                              .pref_src      = lease_address,
                                               .table_any     = TRUE,
                                               .table_coerced = 0,
                                               .metric_any    = TRUE,
@@ -464,6 +481,7 @@ lease_parse_routes(NDhcp4ClientLease *lease,
                                           &((const NMPlatformIP4Route){
                                               .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .gateway       = gateway,
+                                              .pref_src      = lease_address,
                                               .table_any     = TRUE,
                                               .table_coerced = 0,
                                               .metric_any    = TRUE,
@@ -547,6 +565,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
     const char                             *v_str;
     guint16                                 v_u16;
     in_addr_t                               v_inaddr;
+    in_addr_t                               lease_address;
     struct in_addr                          v_inaddr_s;
     int                                     r;
 
@@ -556,7 +575,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
 
     options = nm_dhcp_option_create_options_dict();
 
-    if (!lease_parse_address(lease, l3cd, options, error))
+    if (!lease_parse_address(lease, l3cd, options, &lease_address, error))
         return NULL;
 
     r = n_dhcp4_client_lease_get_server_identifier(lease, &v_inaddr_s);
@@ -575,7 +594,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
                                           v_inaddr);
     }
 
-    lease_parse_routes(lease, l3cd, options, &sbuf);
+    lease_parse_routes(lease, l3cd, lease_address, options, &sbuf);
 
     lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, options, &sbuf);
 
diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c
index 0884def3..4a718de9 100644
--- a/src/core/dhcp/nm-dhcp-systemd.c
+++ b/src/core/dhcp/nm-dhcp-systemd.c
@@ -81,16 +81,20 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
     const struct in_addr                   *addr_list;
     char                                    addr_str[NM_UTILS_INET_ADDRSTRLEN];
     const char                             *s;
-    nm_auto_free_gstring GString           *str            = NULL;
-    nm_auto_free sd_dhcp_route            **routes         = NULL;
-    const char *const                      *search_domains = NULL;
+    nm_auto_free_gstring GString           *str              = NULL;
+    nm_auto_free sd_dhcp_route            **routes_static    = NULL;
+    nm_auto_free sd_dhcp_route            **routes_classless = NULL;
+    const char *const                      *search_domains   = NULL;
+    guint32                                 default_route_metric_offset;
     guint16                                 mtu;
-    int                                     i, num;
+    int                                     i;
+    int                                     num;
+    int                                     is_classless;
+    int                                     n_routes_static;
+    int                                     n_routes_classless;
     const void                             *data;
     gsize                                   data_len;
     gboolean                                has_router_from_classless = FALSE;
-    gboolean                                has_classless_route       = FALSE;
-    gboolean                                has_static_route          = FALSE;
     const gint32                            ts      = nm_utils_get_monotonic_timestamp_sec();
     gint64                                  ts_time = time(NULL);
     struct in_addr                          a_address;
@@ -226,44 +230,27 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
         nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_HOST_NAME, s);
     }
 
-    num = sd_dhcp_lease_get_routes(lease, &routes);
-    if (num > 0) {
-        nm_auto_free_gstring GString *str_classless               = NULL;
-        nm_auto_free_gstring GString *str_static                  = NULL;
-        guint32                       default_route_metric_offset = 0;
+    default_route_metric_offset = 0;
+    n_routes_static             = sd_dhcp_lease_get_static_routes(lease, &routes_static);
+    n_routes_classless          = sd_dhcp_lease_get_classless_routes(lease, &routes_classless);
+    for (is_classless = 1; is_classless >= 0; is_classless--) {
+        int                   n_routes = (is_classless ? n_routes_classless : n_routes_static);
+        sd_dhcp_route *const *routes   = (is_classless ? routes_classless : routes_static);
 
-        for (i = 0; i < num; i++) {
-            switch (sd_dhcp_route_get_option(routes[i])) {
-            case NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE:
-                has_classless_route = TRUE;
-                break;
-            case NM_DHCP_OPTION_DHCP4_STATIC_ROUTE:
-                has_static_route = TRUE;
-                break;
-            }
-        }
+        if (n_routes <= 0)
+            continue;
 
-        if (has_classless_route)
-            str_classless = g_string_sized_new(30);
-        if (has_static_route)
-            str_static = g_string_sized_new(30);
+        nm_gstring_prepare(&str);
 
-        for (i = 0; i < num; i++) {
+        for (i = 0; i < n_routes; i++) {
             char           network_net_str[NM_UTILS_INET_ADDRSTRLEN];
             char           gateway_str[NM_UTILS_INET_ADDRSTRLEN];
             guint8         r_plen;
             struct in_addr r_network;
             struct in_addr r_gateway;
             in_addr_t      network_net;
-            int            option;
             guint32        m;
 
-            option = sd_dhcp_route_get_option(routes[i]);
-            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)
                 continue;
             if (sd_dhcp_route_get_destination_prefix_length(routes[i], &r_plen) < 0 || r_plen > 32)
@@ -275,31 +262,28 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
             _nm_utils_inet4_ntop(network_net, network_net_str);
             _nm_utils_inet4_ntop(r_gateway.s_addr, gateway_str);
 
-            g_string_append_printf(
-                nm_gstring_add_space_delimiter(option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
-                                                   ? str_classless
-                                                   : str_static),
-                "%s/%d %s",
-                network_net_str,
-                (int) r_plen,
-                gateway_str);
+            g_string_append_printf(nm_gstring_add_space_delimiter(str),
+                                   "%s/%d %s",
+                                   network_net_str,
+                                   (int) r_plen,
+                                   gateway_str);
 
-            if (option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE && has_classless_route) {
+            if (!is_classless && n_routes_classless > 0) {
                 /* 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 (r_plen == 0 && 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
-                 * route. */
-                continue;
-            }
-
             if (r_plen == 0) {
+                if (!is_classless) {
+                    /* for option 33 (static route), RFC 2132 says:
+                     *
+                     * The default route (0.0.0.0) is an illegal destination for a static
+                     * route. */
+                    continue;
+                }
+
                 /* if there are multiple default routes, we add them with differing
                  * metrics. */
                 m                         = default_route_metric_offset++;
@@ -309,10 +293,11 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
 
             nm_l3_config_data_add_route_4(l3cd,
                                           &((const NMPlatformIP4Route){
+                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .network       = network_net,
                                               .plen          = r_plen,
                                               .gateway       = r_gateway.s_addr,
-                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
+                                              .pref_src      = a_address.s_addr,
                                               .metric_any    = TRUE,
                                               .metric        = m,
                                               .table_any     = TRUE,
@@ -320,21 +305,18 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
                                           }));
         }
 
-        if (str_classless && str_classless->len > 0)
+        if (str->len > 0) {
             nm_dhcp_option_add_option(options,
                                       AF_INET,
-                                      NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE,
-                                      str_classless->str);
-        if (str_static && str_static->len > 0)
-            nm_dhcp_option_add_option(options,
-                                      AF_INET,
-                                      NM_DHCP_OPTION_DHCP4_STATIC_ROUTE,
-                                      str_static->str);
+                                      is_classless ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
+                                                   : NM_DHCP_OPTION_DHCP4_STATIC_ROUTE,
+                                      str->str);
+        }
     }
 
     num = sd_dhcp_lease_get_router(lease, &a_router);
     if (num > 0) {
-        guint32 default_route_metric_offset = 0;
+        default_route_metric_offset = 0;
 
         nm_gstring_prepare(&str);
         for (i = 0; i < num; i++) {
@@ -366,6 +348,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
                                           &((const NMPlatformIP4Route){
                                               .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .gateway       = a_router[i].s_addr,
+                                              .pref_src      = a_address.s_addr,
                                               .table_any     = TRUE,
                                               .table_coerced = 0,
                                               .metric_any    = TRUE,
@@ -768,14 +751,12 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx,
     gs_unref_hashtable GHashTable          *options = NULL;
     struct in6_addr                         tmp_addr;
     const struct in6_addr                  *dns;
-    uint32_t                                lft_pref, lft_valid;
     char                                    addr_str[NM_UTILS_INET_ADDRSTRLEN];
     char                                  **domains;
     char                                  **ntp_fqdns;
     const struct in6_addr                  *ntp_addrs;
     const char                             *s;
-    nm_auto_free_gstring GString           *str               = NULL;
-    gboolean                                has_any_addresses = FALSE;
+    nm_auto_free_gstring GString           *str = NULL;
     int                                     num, i;
 
     nm_assert(lease);
@@ -784,36 +765,45 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx,
 
     options = nm_dhcp_option_create_options_dict();
 
-    sd_dhcp6_lease_reset_address_iter(lease);
-    nm_gstring_prepare(&str);
-    while (sd_dhcp6_lease_get_address(lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
-        const NMPlatformIP6Address address = {
-            .plen        = 128,
-            .address     = tmp_addr,
-            .timestamp   = ts,
-            .lifetime    = lft_valid,
-            .preferred   = lft_pref,
-            .addr_source = NM_IP_CONFIG_SOURCE_DHCP,
-        };
-
-        nm_l3_config_data_add_address_6(l3cd, &address);
+    if (!info_only) {
+        gboolean has_any_addresses = FALSE;
+        uint32_t lft_pref;
+        uint32_t lft_valid;
 
-        _nm_utils_inet6_ntop(&tmp_addr, addr_str);
-        g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
+        sd_dhcp6_lease_reset_address_iter(lease);
+        nm_gstring_prepare(&str);
+        while (sd_dhcp6_lease_get_address(lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
+            const NMPlatformIP6Address address = {
+                .plen        = 128,
+                .address     = tmp_addr,
+                .timestamp   = ts,
+                .lifetime    = lft_valid,
+                .preferred   = lft_pref,
+                .addr_source = NM_IP_CONFIG_SOURCE_DHCP,
+            };
+
+            nm_l3_config_data_add_address_6(l3cd, &address);
+
+            _nm_utils_inet6_ntop(&tmp_addr, addr_str);
+            g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
 
-        has_any_addresses = TRUE;
-    }
+            has_any_addresses = TRUE;
+        }
 
-    if (str->len) {
-        nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS, str->str);
-    }
+        if (str->len) {
+            nm_dhcp_option_add_option(options,
+                                      AF_INET6,
+                                      NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS,
+                                      str->str);
+        }
 
-    if (!info_only && !has_any_addresses) {
-        g_set_error_literal(error,
-                            NM_MANAGER_ERROR,
-                            NM_MANAGER_ERROR_FAILED,
-                            "no address received in managed mode");
-        return NULL;
+        if (!has_any_addresses) {
+            g_set_error_literal(error,
+                                NM_MANAGER_ERROR,
+                                NM_MANAGER_ERROR_FAILED,
+                                "no address received in managed mode");
+            return NULL;
+        }
     }
 
     num = sd_dhcp6_lease_get_dns(lease, &dns);
@@ -983,11 +973,10 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error)
 
     _LOGT("dhcp-client6: set %p", sd_client);
 
-    if (client_config->v6.info_only) {
-        sd_dhcp6_client_set_address_request(sd_client, 0);
-        if (client_config->v6.needed_prefixes == 0)
-            sd_dhcp6_client_set_information_request(sd_client, 1);
-    }
+    sd_dhcp6_client_set_address_request(sd_client, !client_config->v6.info_only);
+    sd_dhcp6_client_set_information_request(sd_client,
+                                            client_config->v6.info_only
+                                                && client_config->v6.needed_prefixes == 0);
 
     r = sd_dhcp6_client_set_iaid(sd_client, client_config->v6.iaid);
     if (r < 0) {
diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c
index 214e94cd..081e2841 100644
--- a/src/core/dhcp/nm-dhcp-utils.c
+++ b/src/core/dhcp/nm-dhcp-utils.c
@@ -28,7 +28,8 @@ static gboolean
 ip4_process_dhcpcd_rfc3442_routes(const char     *iface,
                                   const char     *str,
                                   NML3ConfigData *l3cd,
-                                  guint32        *gwaddr)
+                                  in_addr_t       address,
+                                  guint32        *out_gwaddr)
 {
     gs_free const char **routes = NULL;
     const char         **r;
@@ -79,7 +80,7 @@ ip4_process_dhcpcd_rfc3442_routes(const char     *iface,
         have_routes = TRUE;
         if (rt_cidr == 0 && rt_addr == 0) {
             /* FIXME: how to handle multiple routers? */
-            *gwaddr = rt_route;
+            *out_gwaddr = rt_route;
         } else {
             _LOG2I(LOGD_DHCP4,
                    iface,
@@ -91,13 +92,13 @@ ip4_process_dhcpcd_rfc3442_routes(const char     *iface,
             nm_l3_config_data_add_route_4(
                 l3cd,
                 &((const NMPlatformIP4Route){
+                    .rt_source  = NM_IP_CONFIG_SOURCE_DHCP,
                     .network    = nm_utils_ip4_address_clear_host_address(rt_addr, rt_cidr),
                     .plen       = rt_cidr,
                     .gateway    = rt_route,
-                    .rt_source  = NM_IP_CONFIG_SOURCE_DHCP,
+                    .pref_src   = address,
                     .metric_any = TRUE,
                     .table_any  = TRUE,
-
                 }));
         }
     }
@@ -158,7 +159,8 @@ static gboolean
 ip4_process_dhclient_rfc3442_routes(const char     *iface,
                                     const char     *str,
                                     NML3ConfigData *l3cd,
-                                    guint32        *gwaddr)
+                                    in_addr_t       address,
+                                    guint32        *out_gwaddr)
 {
     gs_free const char **octets = NULL;
     const char *const   *o;
@@ -182,13 +184,14 @@ ip4_process_dhclient_rfc3442_routes(const char     *iface,
         have_routes = TRUE;
         if (!route.plen) {
             /* gateway passed as classless static route */
-            *gwaddr = route.gateway;
+            *out_gwaddr = route.gateway;
         } else {
             char b1[INET_ADDRSTRLEN];
             char b2[INET_ADDRSTRLEN];
 
             /* normal route */
             route.rt_source     = NM_IP_CONFIG_SOURCE_DHCP;
+            route.pref_src      = address;
             route.table_any     = TRUE;
             route.table_coerced = 0;
             route.metric_any    = TRUE;
@@ -212,14 +215,15 @@ static gboolean
 ip4_process_classless_routes(const char     *iface,
                              GHashTable     *options,
                              NML3ConfigData *l3cd,
-                             guint32        *gwaddr)
+                             in_addr_t       address,
+                             guint32        *out_gwaddr)
 {
     const char *str, *p;
 
     g_return_val_if_fail(options != NULL, FALSE);
     g_return_val_if_fail(l3cd != NULL, FALSE);
 
-    *gwaddr = 0;
+    *out_gwaddr = 0;
 
     /* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a
      * slightly different format:
@@ -266,14 +270,17 @@ ip4_process_classless_routes(const char     *iface,
 
     if (strchr(str, '/')) {
         /* dhcpcd format */
-        return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, gwaddr);
+        return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, address, out_gwaddr);
     }
 
-    return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, gwaddr);
+    return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, address, out_gwaddr);
 }
 
 static void
-process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *l3cd)
+process_classful_routes(const char     *iface,
+                        GHashTable     *options,
+                        NML3ConfigData *l3cd,
+                        in_addr_t       address)
 {
     gs_free const char **searches = NULL;
     const char         **s;
@@ -293,6 +300,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *
     }
 
     for (s = searches; *s; s += 2) {
+        char               sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
         NMPlatformIP4Route route;
         guint32            rt_addr, rt_route;
 
@@ -321,6 +329,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *
             route.plen = 32;
         }
         route.gateway       = rt_route;
+        route.pref_src      = address;
         route.rt_source     = NM_IP_CONFIG_SOURCE_DHCP;
         route.table_any     = TRUE;
         route.table_coerced = 0;
@@ -334,7 +343,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *
         _LOG2I(LOGD_DHCP,
                iface,
                "  static route %s",
-               nm_platform_ip4_route_to_string(&route, NULL, 0));
+               nm_platform_ip4_route_to_string(&route, sbuf, sizeof(sbuf)));
     }
 }
 
@@ -422,8 +431,8 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
     /* Routes: if the server returns classless static routes, we MUST ignore
      * the 'static_routes' option.
      */
-    if (!ip4_process_classless_routes(iface, options, l3cd, &gateway))
-        process_classful_routes(iface, options, l3cd);
+    if (!ip4_process_classless_routes(iface, options, l3cd, address.address, &gateway))
+        process_classful_routes(iface, options, l3cd, address.address);
 
     if (gateway) {
         _LOG2I(LOGD_DHCP4, iface, "  gateway %s", _nm_utils_inet4_ntop(gateway, sbuf));
@@ -453,6 +462,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
         const NMPlatformIP4Route r = {
             .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
             .gateway       = gateway,
+            .pref_src      = address.address,
             .table_any     = TRUE,
             .table_coerced = 0,
             .metric_any    = TRUE,
@@ -659,8 +669,13 @@ nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx,
         _LOG2I(LOGD_DHCP6, iface, "  preferred_lft %u", address.preferred);
     }
 
-    str = g_hash_table_lookup(options, "ip6_address");
-    if (str) {
+    if (!info_only) {
+        str = g_hash_table_lookup(options, "ip6_address");
+        if (!str) {
+            /* No address in Managed mode is a hard error */
+            return NULL;
+        }
+
         if (!inet_pton(AF_INET6, str, &tmp_addr)) {
             _LOG2W(LOGD_DHCP6, iface, "(%s): DHCP returned invalid address '%s'", iface, str);
             return NULL;
@@ -670,9 +685,6 @@ nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx,
         address.addr_source = NM_IP_CONFIG_SOURCE_DHCP;
         nm_l3_config_data_add_address_6(l3cd, &address);
         _LOG2I(LOGD_DHCP6, iface, "  address %s", str);
-    } else if (info_only == FALSE) {
-        /* No address in Managed mode is a hard error */
-        return NULL;
     }
 
     str = g_hash_table_lookup(options, "host_name");