]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-dhcp4.c
network: honor MTUBytes= setting
[thirdparty/systemd.git] / src / network / networkd-dhcp4.c
index c8e07028f2f5dd5a9f56e1fe5647ba826b8dd82b..7907988a9bb9937c41f9c4411bee2f4df6c52987 100644 (file)
@@ -1,12 +1,11 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#include <netinet/ether.h>
+#include <netinet/in.h>
 #include <linux/if.h>
 
 #include "alloc-util.h"
 #include "hostname-util.h"
 #include "parse-util.h"
-#include "netdev/vrf.h"
 #include "network-internal.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
@@ -14,9 +13,7 @@
 #include "string-util.h"
 #include "sysctl-util.h"
 
-static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
-                               void *userdata) {
-        Link *link = userdata;
+static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
@@ -54,7 +51,8 @@ static int route_scope_from_address(const Route *route, const struct in_addr *se
 static int link_set_dhcp_routes(Link *link) {
         _cleanup_free_ sd_dhcp_route **static_routes = NULL;
         bool classless_route = false, static_route = false;
-        struct in_addr gateway, address;
+        const struct in_addr *router;
+        struct in_addr address;
         int r, n, i;
         uint32_t table;
 
@@ -69,11 +67,7 @@ static int link_set_dhcp_routes(Link *link) {
         if (!link->network->dhcp_use_routes)
                 return 0;
 
-        /* When the interface is part of an VRF use the VRFs routing table, unless
-         * there is a another table specified. */
-        table = link->network->dhcp_route_table;
-        if (!link->network->dhcp_route_table_set && link->network->vrf != NULL)
-                table = VRF(link->network->vrf)->table;
+        table = link_get_dhcp_route_table(link);
 
         r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
         if (r < 0)
@@ -125,26 +119,21 @@ static int link_set_dhcp_routes(Link *link) {
                 link->dhcp4_messages++;
         }
 
-        r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
-        if (r == -ENODATA)
-                log_link_info_errno(link, r, "DHCP: No gateway received from DHCP server: %m");
+        r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+        if (IN_SET(r, 0, -ENODATA))
+                log_link_info(link, "DHCP: No gateway received from DHCP server.");
         else if (r < 0)
                 log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
+        else if (in4_addr_is_null(&router[0]))
+                log_link_info(link, "DHCP: Received gateway is null.");
 
         /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
            a Router option, the DHCP client MUST ignore the Router option. */
         if (classless_route && static_route)
                 log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option");
 
-        if (r >= 0 && !classless_route) {
-                _cleanup_(route_freep) Route *route = NULL;
-                _cleanup_(route_freep) Route *route_gw = NULL;
-
-                r = route_new(&route);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not allocate route: %m");
-
-                route->protocol = RTPROT_DHCP;
+        if (r > 0 && !classless_route && !in4_addr_is_null(&router[0])) {
+                _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL;
 
                 r = route_new(&route_gw);
                 if (r < 0)
@@ -154,7 +143,7 @@ static int link_set_dhcp_routes(Link *link) {
                  * route for the gw host so that we can route no matter the
                  * netmask or existing kernel route tables. */
                 route_gw->family = AF_INET;
-                route_gw->dst.in = gateway;
+                route_gw->dst.in = router[0];
                 route_gw->dst_prefixlen = 32;
                 route_gw->prefsrc.in = address;
                 route_gw->scope = RT_SCOPE_LINK;
@@ -168,9 +157,14 @@ static int link_set_dhcp_routes(Link *link) {
 
                 link->dhcp4_messages++;
 
+                r = route_new(&route);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not allocate route: %m");
+
                 route->family = AF_INET;
-                route->gw.in = gateway;
+                route->gw.in = router[0];
                 route->prefsrc.in = address;
+                route->protocol = RTPROT_DHCP;
                 route->priority = link->network->dhcp_route_metric;
                 route->table = table;
 
@@ -189,9 +183,9 @@ static int link_set_dhcp_routes(Link *link) {
 
 static int dhcp_lease_lost(Link *link) {
         _cleanup_(address_freep) Address *address = NULL;
+        const struct in_addr *router;
         struct in_addr addr;
         struct in_addr netmask;
-        struct in_addr gateway;
         unsigned prefixlen = 0;
         int r;
 
@@ -216,41 +210,38 @@ static int dhcp_lease_lost(Link *link) {
                                         assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
                                         assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
 
-                                        route_remove(route, link,
-                                                     link_route_remove_handler);
+                                        route_remove(route, link, NULL);
                                 }
                         }
                 }
-        }
 
-        r = address_new(&address);
-        if (r >= 0) {
-                r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
-                if (r >= 0) {
+                r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+                if (r > 0 && !in4_addr_is_null(&router[0])) {
                         _cleanup_(route_freep) Route *route_gw = NULL;
                         _cleanup_(route_freep) Route *route = NULL;
 
                         r = route_new(&route_gw);
                         if (r >= 0) {
                                 route_gw->family = AF_INET;
-                                route_gw->dst.in = gateway;
+                                route_gw->dst.in = router[0];
                                 route_gw->dst_prefixlen = 32;
                                 route_gw->scope = RT_SCOPE_LINK;
 
-                                route_remove(route_gw, link,
-                                             link_route_remove_handler);
+                                route_remove(route_gw, link, NULL);
                         }
 
                         r = route_new(&route);
                         if (r >= 0) {
                                 route->family = AF_INET;
-                                route->gw.in = gateway;
+                                route->gw.in = router[0];
 
-                                route_remove(route, link,
-                                             link_route_remove_handler);
+                                route_remove(route, link, NULL);
                         }
                 }
+        }
 
+        r = address_new(&address);
+        if (r >= 0) {
                 r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
                 if (r >= 0) {
                         r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
@@ -303,9 +294,7 @@ static int dhcp_lease_lost(Link *link) {
         return 0;
 }
 
-static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
-                                 void *userdata) {
-        Link *link = userdata;
+static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
@@ -319,6 +308,9 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
 
         link_set_dhcp_routes(link);
 
+        /* Add back static routes since kernel removes while DHCPv4 address is removed from when lease expires */
+        link_request_set_routes(link);
+
         if (link->dhcp4_messages == 0) {
                 link->dhcp4_configured = true;
                 link_check_ready(link);
@@ -406,10 +398,10 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
 }
 
 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
+        const struct in_addr *router;
         sd_dhcp_lease *lease;
         struct in_addr address;
         struct in_addr netmask;
-        struct in_addr gateway;
         unsigned prefixlen;
         uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
         int r;
@@ -431,20 +423,20 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
 
         prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
 
-        r = sd_dhcp_lease_get_router(lease, &gateway);
+        r = sd_dhcp_lease_get_router(lease, &router);
         if (r < 0 && r != -ENODATA)
                 return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
 
-        if (r >= 0)
+        if (r > 0 && !in4_addr_is_null(&router[0]))
                 log_struct(LOG_INFO,
                            LOG_LINK_INTERFACE(link),
                            LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
                                             ADDRESS_FMT_VAL(address),
                                             prefixlen,
-                                            ADDRESS_FMT_VAL(gateway)),
+                                            ADDRESS_FMT_VAL(router[0])),
                            "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
                            "PREFIXLEN=%u", prefixlen,
-                           "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway));
+                           "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0]));
         else
                 log_struct(LOG_INFO,
                            LOG_LINK_INTERFACE(link),
@@ -482,7 +474,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
                         if (r < 0)
                                 log_link_warning_errno(link, r, "Unable to shorten overlong DHCP hostname '%s', ignoring: %m", dhcpname);
                         if (r == 1)
-                                log_link_notice(link, "Overlong DCHP hostname received, shortened from '%s' to '%s'", dhcpname, hostname);
+                                log_link_notice(link, "Overlong DHCP hostname received, shortened from '%s' to '%s'", dhcpname, hostname);
                 }
 
                 if (hostname) {
@@ -519,31 +511,76 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
 
         return 0;
 }
-static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
+
+static int dhcp_server_is_black_listed(Link *link, sd_dhcp_client *client) {
+        sd_dhcp_lease *lease;
+        struct in_addr addr;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(client);
+
+        r = sd_dhcp_client_get_lease(client, &lease);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to get DHCP lease: %m");
+
+        r = sd_dhcp_lease_get_server_identifier(lease, &addr);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to get DHCP server ip address: %m");
+
+        if (set_contains(link->network->dhcp_black_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
+                log_struct(LOG_DEBUG,
+                           LOG_LINK_INTERFACE(link),
+                           LOG_LINK_MESSAGE(link, "DHCPv4 ip '%u.%u.%u.%u' found in black listed ip addresses, ignoring offer",
+                                            ADDRESS_FMT_VAL(addr)));
+                return true;
+        }
+
+        return false;
+}
+
+static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
         Link *link = userdata;
-        int r = 0;
+        int r;
 
         assert(link);
         assert(link->network);
         assert(link->manager);
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return;
+                return 0;
 
         switch (event) {
-                case SD_DHCP_CLIENT_EVENT_EXPIRED:
                 case SD_DHCP_CLIENT_EVENT_STOP:
+
+                        if (link_ipv4ll_fallback_enabled(link)) {
+                                assert(link->ipv4ll);
+
+                                log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address");
+
+                                r = sd_ipv4ll_start(link->ipv4ll);
+                                if (r < 0)
+                                        return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
+                        }
+
+                        if (link->network->dhcp_send_release)
+                                (void) sd_dhcp_client_send_release(client);
+
+                        _fallthrough_;
+                case SD_DHCP_CLIENT_EVENT_EXPIRED:
                 case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
+
                         if (link->network->dhcp_critical) {
                                 log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it.");
-                                return;
+                                return 0;
                         }
 
                         if (link->dhcp_lease) {
                                 r = dhcp_lease_lost(link);
                                 if (r < 0) {
                                         link_enter_failed(link);
-                                        return;
+                                        return r;
                                 }
                         }
 
@@ -551,7 +588,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                                 r = dhcp_lease_acquired(client, link);
                                 if (r < 0) {
                                         link_enter_failed(link);
-                                        return;
+                                        return r;
                                 }
                         }
 
@@ -560,16 +597,23 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                         r = dhcp_lease_renew(client, link);
                         if (r < 0) {
                                 link_enter_failed(link);
-                                return;
+                                return r;
                         }
                         break;
                 case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
                         r = dhcp_lease_acquired(client, link);
                         if (r < 0) {
                                 link_enter_failed(link);
-                                return;
+                                return r;
                         }
                         break;
+                case SD_DHCP_CLIENT_EVENT_SELECTING:
+                        r = dhcp_server_is_black_listed(link, client);
+                        if (r < 0)
+                                return r;
+                        if (r != 0)
+                                return -ENOMSG;
+                        break;
                 default:
                         if (event < 0)
                                 log_link_warning_errno(link, event, "DHCP error: Client failed: %m");
@@ -578,7 +622,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                         break;
         }
 
-        return;
+        return 0;
 }
 
 static int dhcp4_set_hostname(Link *link) {
@@ -677,10 +721,12 @@ int dhcp4_set_client_identifier(Link *link) {
 
                 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
                         r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client,
+                                                             link->network->iaid_set,
                                                              link->network->iaid,
                                                              duid->llt_time);
                 else
                         r = sd_dhcp_client_set_iaid_duid(link->dhcp_client,
+                                                         link->network->iaid_set,
                                                          link->network->iaid,
                                                          duid->type,
                                                          duid->raw_data_len > 0 ? duid->raw_data : NULL,
@@ -695,7 +741,7 @@ int dhcp4_set_client_identifier(Link *link) {
 
                 if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
                         r = sd_dhcp_client_set_duid_llt(link->dhcp_client,
-                                                             duid->llt_time);
+                                                        duid->llt_time);
                 else
                         r = sd_dhcp_client_set_duid(link->dhcp_client,
                                                     duid->type,
@@ -823,5 +869,11 @@ int dhcp4_configure(Link *link) {
                         return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set listen port: %m");
         }
 
+        if (link->network->dhcp_max_attempts > 0) {
+                r = sd_dhcp_client_set_max_attempts(link->dhcp_client, link->network->dhcp_max_attempts);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set max attempts: %m");
+        }
+
         return dhcp4_set_client_identifier(link);
 }