]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: adjust route priority based on preference
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 28 Oct 2022 03:55:59 +0000 (12:55 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 28 Oct 2022 06:48:28 +0000 (15:48 +0900)
Even if different preference is specified, the kernel merges multiple
routes with the same preference. This is problematic when a network has
multiple routers.

Fixes #25138.

man/systemd.network.xml
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-ndisc.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h

index e35da7bd4ce37c0283878054cdcae7329e9b306f..dace6a23ed80bcfbd7c55575d65be9d0f047ec37 100644 (file)
@@ -2512,8 +2512,12 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
       <varlistentry>
         <term><varname>RouteMetric=</varname></term>
         <listitem>
-          <para>Set the routing metric for the routes received in the Router Advertisement. Takes an
-          unsigned integer in the range 0…4294967295. Defaults to 1024.</para>
+          <para>Set the routing metric for the routes received in the Router Advertisement. Takes an unsigned
+          integer in the range 0…4294967295, or three unsigned integer separated with <literal>:</literal>,
+          in that case the first one is used when the router preference is high, the second is for medium
+          preference, and the last is for low preference
+          (<literal><replaceable>high</replaceable>:<replaceable>medium</replaceable>:<replaceable>low</replaceable></literal>).
+          Defaults to <literal>512:1024:2048</literal>.</para>
         </listitem>
       </varlistentry>
 
index 6526d3378fae25f65c945049a87fa3bd743b2bc5..2a80b1766e7e2e6daacb31ebb4029270de66e8d2 100644 (file)
@@ -303,7 +303,7 @@ int config_parse_dhcp(
         return 0;
 }
 
-int config_parse_dhcp_or_ra_route_metric(
+int config_parse_dhcp_route_metric(
                 const char* unit,
                 const char *filename,
                 unsigned line,
@@ -321,7 +321,7 @@ int config_parse_dhcp_or_ra_route_metric(
 
         assert(filename);
         assert(lvalue);
-        assert(IN_SET(ltype, AF_UNSPEC, AF_INET, AF_INET6));
+        assert(IN_SET(ltype, AF_UNSPEC, AF_INET));
         assert(rvalue);
         assert(data);
 
@@ -337,16 +337,15 @@ int config_parse_dhcp_or_ra_route_metric(
                 network->dhcp_route_metric = metric;
                 network->dhcp_route_metric_set = true;
                 break;
-        case AF_INET6:
-                network->ipv6_accept_ra_route_metric = metric;
-                network->ipv6_accept_ra_route_metric_set = true;
-                break;
         case AF_UNSPEC:
                 /* For backward compatibility. */
                 if (!network->dhcp_route_metric_set)
                         network->dhcp_route_metric = metric;
-                if (!network->ipv6_accept_ra_route_metric_set)
-                        network->ipv6_accept_ra_route_metric = metric;
+                if (!network->ipv6_accept_ra_route_metric_set) {
+                        network->ipv6_accept_ra_route_metric_high = metric;
+                        network->ipv6_accept_ra_route_metric_medium = metric;
+                        network->ipv6_accept_ra_route_metric_low = metric;
+                }
                 break;
         default:
                 assert_not_reached();
@@ -355,6 +354,64 @@ int config_parse_dhcp_or_ra_route_metric(
         return 0;
 }
 
+int config_parse_ipv6_accept_ra_route_metric(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = ASSERT_PTR(userdata);
+        uint32_t metric_high, metric_medium, metric_low;
+        int r, s, t;
+
+        assert(filename);
+        assert(rvalue);
+
+        if (safe_atou32(rvalue, &metric_low) >= 0)
+                metric_high = metric_medium = metric_low;
+        else {
+                _cleanup_free_ char *high = NULL, *medium = NULL, *low = NULL;
+                const char *p = rvalue;
+
+                r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &high, &medium, &low, NULL);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r != 3 || !isempty(p)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r < 0 ? r : 0,
+                                   "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+
+                r = safe_atou32(high, &metric_high);
+                s = safe_atou32(medium, &metric_medium);
+                t = safe_atou32(low, &metric_low);
+                if (r < 0 || s < 0 || t < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r < 0 ? r : s < 0 ? s : t,
+                                   "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+
+                if (metric_high >= metric_medium || metric_medium >= metric_low) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid RouteTable=%s, ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+        }
+
+        network->ipv6_accept_ra_route_metric_high = metric_high;
+        network->ipv6_accept_ra_route_metric_medium = metric_medium;
+        network->ipv6_accept_ra_route_metric_low = metric_low;
+        network->ipv6_accept_ra_route_metric_set = true;
+
+        return 0;
+}
+
 int config_parse_dhcp_use_dns(
                 const char* unit,
                 const char *filename,
index c19bc104075d5cc7050abe728cc1a2f0b3fe9af2..6f57b78a7f373b62f4c7f4a10c6609df1fecf6b0 100644 (file)
@@ -15,6 +15,9 @@
 #define UPLINK_INDEX_SELF -2 /* the interface itself is uplink */
 
 #define DHCP_ROUTE_METRIC 1024
+#define IPV6RA_ROUTE_METRIC_HIGH    512
+#define IPV6RA_ROUTE_METRIC_MEDIUM 1024
+#define IPV6RA_ROUTE_METRIC_LOW    2048
 #define DHCP6PD_ROUTE_METRIC 256
 
 typedef struct Link Link;
@@ -89,7 +92,8 @@ const char *dhcp_option_data_type_to_string(DHCPOptionDataType d) _const_;
 DHCPOptionDataType dhcp_option_data_type_from_string(const char *d) _pure_;
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_or_ra_route_metric);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_route_metric);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_route_metric);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp);
index f31735b6ec6f6793d3188dfdb58d18caae4e5c6e..ce7dff222bbd01b8616d8b74ecdf6110e24e10f9 100644 (file)
@@ -143,6 +143,28 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
         return 1;
 }
 
+static void ndisc_set_route_priority(Link *link, Route *route) {
+        assert(link);
+        assert(route);
+
+        if (route->priority_set)
+                return; /* explicitly configured. */
+
+        switch (route->pref) {
+        case SD_NDISC_PREFERENCE_LOW:
+                route->priority = link->network->ipv6_accept_ra_route_metric_low;
+                break;
+        case SD_NDISC_PREFERENCE_MEDIUM:
+                route->priority = link->network->ipv6_accept_ra_route_metric_medium;
+                break;
+        case SD_NDISC_PREFERENCE_HIGH:
+                route->priority = link->network->ipv6_accept_ra_route_metric_high;
+                break;
+        default:
+                assert_not_reached();
+        }
+}
+
 static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
         _cleanup_(route_freep) Route *route = in;
         struct in6_addr router;
@@ -160,8 +182,7 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
         route->provider.in6 = router;
         if (!route->table_set)
                 route->table = link_get_ipv6_accept_ra_route_table(link);
-        if (!route->priority_set)
-                route->priority = link->network->ipv6_accept_ra_route_metric;
+        ndisc_set_route_priority(link, route);
         if (!route->protocol_set)
                 route->protocol = RTPROT_RA;
 
index aec5452849822ed95dc9ce0198e394779338f181..70dead97ab1f3464317b2a481d10273f4a518f8d 100644 (file)
@@ -234,7 +234,7 @@ DHCPv4.UserClass,                            config_parse_dhcp_user_or_vendor_cl
 DHCPv4.IAID,                                 config_parse_iaid,                                        AF_INET,                       0
 DHCPv4.DUIDType,                             config_parse_network_duid_type,                           0,                             0
 DHCPv4.DUIDRawData,                          config_parse_network_duid_rawdata,                        0,                             0
-DHCPv4.RouteMetric,                          config_parse_dhcp_or_ra_route_metric,                     AF_INET,                       0
+DHCPv4.RouteMetric,                          config_parse_dhcp_route_metric,                           AF_INET,                       0
 DHCPv4.RouteTable,                           config_parse_dhcp_or_ra_route_table,                      AF_INET,                       0
 DHCPv4.UseTimezone,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_timezone)
 DHCPv4.ListenPort,                           config_parse_uint16,                                      0,                             offsetof(Network, dhcp_client_port)
@@ -277,7 +277,7 @@ IPv6AcceptRA.UseDomains,                     config_parse_ipv6_accept_ra_use_dom
 IPv6AcceptRA.UseMTU,                         config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_mtu)
 IPv6AcceptRA.DHCPv6Client,                   config_parse_ipv6_accept_ra_start_dhcp6_client,           0,                             offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
 IPv6AcceptRA.RouteTable,                     config_parse_dhcp_or_ra_route_table,                      AF_INET6,                      0
-IPv6AcceptRA.RouteMetric,                    config_parse_dhcp_or_ra_route_metric,                     AF_INET6,                      0
+IPv6AcceptRA.RouteMetric,                    config_parse_ipv6_accept_ra_route_metric,                 0,                             0
 IPv6AcceptRA.RouterAllowList,                config_parse_in_addr_prefixes,                            AF_INET6,                      offsetof(Network, ndisc_allow_listed_router)
 IPv6AcceptRA.RouterDenyList,                 config_parse_in_addr_prefixes,                            AF_INET6,                      offsetof(Network, ndisc_deny_listed_router)
 IPv6AcceptRA.PrefixAllowList,                config_parse_in_addr_prefixes,                            AF_INET6,                      offsetof(Network, ndisc_allow_listed_prefix)
@@ -559,7 +559,7 @@ DHCP.UserClass,                              config_parse_dhcp_user_or_vendor_cl
 DHCP.IAID,                                   config_parse_iaid,                                        AF_INET,                       0
 DHCP.DUIDType,                               config_parse_network_duid_type,                           0,                             0
 DHCP.DUIDRawData,                            config_parse_network_duid_rawdata,                        0,                             0
-DHCP.RouteMetric,                            config_parse_dhcp_or_ra_route_metric,                     AF_UNSPEC,                     0
+DHCP.RouteMetric,                            config_parse_dhcp_route_metric,                           AF_UNSPEC,                     0
 DHCP.RouteTable,                             config_parse_dhcp_or_ra_route_table,                      AF_INET,                       0
 DHCP.UseTimezone,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_timezone)
 DHCP.ListenPort,                             config_parse_uint16,                                      0,                             offsetof(Network, dhcp_client_port)
@@ -567,7 +567,7 @@ DHCP.RapidCommit,                            config_parse_bool,
 DHCP.ForceDHCPv6PDOtherInformation,          config_parse_warn_compat,                                 DISABLED_LEGACY,               0
 DHCPv4.UseDomainName,                        config_parse_dhcp_use_domains,                            AF_INET,                       0
 DHCPv4.CriticalConnection,                   config_parse_tristate,                                    0,                             offsetof(Network, dhcp_critical)
-DHCPv6.RouteMetric,                          config_parse_dhcp_or_ra_route_metric,                     AF_INET6,                      0
+DHCPv6.RouteMetric,                          config_parse_ipv6_accept_ra_route_metric,                 AF_INET6,                      0
 DHCPv6.ForceDHCPv6PDOtherInformation,        config_parse_warn_compat,                                 DISABLED_LEGACY,               0
 DHCPv6PrefixDelegation.SubnetId,             config_parse_dhcp_pd_subnet_id,                           0,                             offsetof(Network, dhcp_pd_subnet_id)
 DHCPv6PrefixDelegation.Announce,             config_parse_bool,                                        0,                             offsetof(Network, dhcp_pd_announce)
index a6c5b442389346e72eec77c6f4e9320bc44b5889..41a0766f078ee5d53967a8f82edd5423ffb53ca7 100644 (file)
@@ -481,7 +481,9 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .ipv6_accept_ra_use_onlink_prefix = true,
                 .ipv6_accept_ra_use_mtu = true,
                 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
-                .ipv6_accept_ra_route_metric = DHCP_ROUTE_METRIC,
+                .ipv6_accept_ra_route_metric_high = IPV6RA_ROUTE_METRIC_HIGH,
+                .ipv6_accept_ra_route_metric_medium = IPV6RA_ROUTE_METRIC_MEDIUM,
+                .ipv6_accept_ra_route_metric_low = IPV6RA_ROUTE_METRIC_LOW,
                 .ipv6_accept_ra_start_dhcp6_client = IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES,
 
                 .can_termination = -1,
index a2743f97434b3734a853ee422f36b917d609e5de..79e8ceb06b41d038155434152c1a2d0244f586f7 100644 (file)
@@ -315,7 +315,9 @@ struct Network {
         IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client;
         uint32_t ipv6_accept_ra_route_table;
         bool ipv6_accept_ra_route_table_set;
-        uint32_t ipv6_accept_ra_route_metric;
+        uint32_t ipv6_accept_ra_route_metric_high;
+        uint32_t ipv6_accept_ra_route_metric_medium;
+        uint32_t ipv6_accept_ra_route_metric_low;
         bool ipv6_accept_ra_route_metric_set;
         Set *ndisc_deny_listed_router;
         Set *ndisc_allow_listed_router;