summary refs log tree commit diff
path: root/src/core/dhcp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/dhcp')
-rw-r--r--src/core/dhcp/nm-dhcp-client.c32
-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.c103
-rw-r--r--src/core/dhcp/nm-dhcp-utils.c38
5 files changed, 101 insertions, 102 deletions
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c
index 85e48ff7..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) {
diff --git a/src/core/dhcp/nm-dhcp-dhcpcd.c b/src/core/dhcp/nm-dhcp-dhcpcd.c
index 4b3429ef..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   = NULL;
+    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..14a121e7 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)
-            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)
+        if (str->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,
diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c
index 214e94cd..a0eec6e0 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,