]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: support IPv4 route with IPv6 gateway
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 7 Oct 2020 01:04:13 +0000 (10:04 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 7 Oct 2020 15:06:19 +0000 (00:06 +0900)
src/network/networkd-dhcp4.c
src/network/networkd-ndisc.c
src/network/networkd-route.c
src/network/networkd-route.h

index b31b68756d45159f81c1850f3904e8141cb741db..81a95945122140673cc5918e6f9a9fd4b09d3f20 100644 (file)
@@ -312,6 +312,7 @@ static int link_set_dhcp_routes(Link *link) {
 
                         route->family = AF_INET;
                         route->protocol = RTPROT_DHCP;
+                        route->gw_family = AF_INET;
                         assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
                         assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
                         assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
@@ -374,6 +375,7 @@ static int link_set_dhcp_routes(Link *link) {
                                 return log_link_error_errno(link, r, "Could not allocate route: %m");
 
                         route->family = AF_INET;
+                        route->gw_family = AF_INET;
                         route->gw.in = router[0];
                         route->prefsrc.in = address;
                         route->protocol = RTPROT_DHCP;
@@ -392,6 +394,7 @@ static int link_set_dhcp_routes(Link *link) {
                                 if (rt->family != AF_INET)
                                         continue;
 
+                                rt->gw_family = AF_INET;
                                 rt->gw.in = router[0];
 
                                 r = dhcp_route_configure(rt, link);
index e2e049201a346b094081da6eb5812ff6576b32ca..d0e426f2315ff7be4c89cc3a57a07b203ea1606a 100644 (file)
@@ -513,6 +513,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         route->priority = link->network->dhcp6_route_metric;
         route->protocol = RTPROT_RA;
         route->pref = preference;
+        route->gw_family = AF_INET6;
         route->gw = gateway;
         route->lifetime = time_now + lifetime * USEC_PER_SEC;
         route->mtu = mtu;
@@ -530,6 +531,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                         continue;
 
                 route_gw->gw = gateway;
+                route_gw->gw_family = AF_INET6;
 
                 r = ndisc_route_configure(route_gw, link, rt);
                 if (r < 0)
@@ -839,6 +841,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         route->protocol = RTPROT_RA;
         route->pref = preference;
         route->gw.in6 = gateway;
+        route->gw_family = AF_INET6;
         route->dst_prefixlen = prefixlen;
         route->lifetime = time_now + lifetime * USEC_PER_SEC;
 
index c3de2c7c084f2df4c7156fc8b4b4d43863c10a63..e75d93c5619d28fadee53cb48dc72b4be39884a1 100644 (file)
@@ -298,7 +298,10 @@ void route_hash_func(const Route *route, struct siphash *state) {
                 siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
                 siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
 
-                siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
+                siphash24_compress(&route->gw_family, sizeof(route->gw_family), state);
+                if (IN_SET(route->gw_family, AF_INET, AF_INET6))
+                        siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->gw_family), state);
+
 
                 siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
 
@@ -345,10 +348,16 @@ int route_compare_func(const Route *a, const Route *b) {
                 if (r != 0)
                         return r;
 
-                r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+                r = CMP(a->gw_family, b->gw_family);
                 if (r != 0)
                         return r;
 
+                if (IN_SET(a->gw_family, AF_INET, AF_INET6)) {
+                        r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+                        if (r != 0)
+                                return r;
+                }
+
                 r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
                 if (r != 0)
                         return r;
@@ -466,6 +475,7 @@ static int route_add_internal(Manager *manager, Link *link, Set **routes, Route
         route->src_prefixlen = in->src_prefixlen;
         route->dst = in->dst;
         route->dst_prefixlen = in->dst_prefixlen;
+        route->gw_family = in->gw_family;
         route->gw = in->gw;
         route->prefsrc = in->prefsrc;
         route->scope = in->scope;
@@ -587,8 +597,8 @@ int route_remove(
                 }
                 if (!in_addr_is_null(route->family, &route->src))
                         (void) in_addr_to_string(route->family, &route->src, &src);
-                if (!in_addr_is_null(route->family, &route->gw))
-                        (void) in_addr_to_string(route->family, &route->gw, &gw);
+                if (!in_addr_is_null(route->gw_family, &route->gw))
+                        (void) in_addr_to_string(route->gw_family, &route->gw, &gw);
                 if (!in_addr_is_null(route->family, &route->prefsrc))
                         (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
 
@@ -600,10 +610,21 @@ int route_remove(
                                strna(route_type_to_string(route->type)));
         }
 
-        if (in_addr_is_null(route->family, &route->gw) == 0) {
-                r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
+        if (in_addr_is_null(route->gw_family, &route->gw) == 0) {
+                if (route->gw_family == route->family) {
+                        r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
+                } else {
+                        RouteVia rtvia = {
+                                .family = route->gw_family,
+                                .address = route->gw,
+                        };
+
+                        r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not append RTA_VIA attribute: %m");
+                }
         }
 
         if (route->dst_prefixlen) {
@@ -860,8 +881,8 @@ int route_configure(
                 }
                 if (!in_addr_is_null(route->family, &route->src))
                         (void) in_addr_to_string(route->family, &route->src, &src);
-                if (!in_addr_is_null(route->family, &route->gw))
-                        (void) in_addr_to_string(route->family, &route->gw, &gw);
+                if (!in_addr_is_null(route->gw_family, &route->gw))
+                        (void) in_addr_to_string(route->gw_family, &route->gw, &gw);
                 if (!in_addr_is_null(route->family, &route->prefsrc))
                         (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
 
@@ -879,14 +900,21 @@ int route_configure(
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
 
-        if (in_addr_is_null(route->family, &route->gw) == 0) {
-                r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
+        if (in_addr_is_null(route->gw_family, &route->gw) == 0) {
+                if (route->gw_family == route->family) {
+                        r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
+                } else {
+                        RouteVia rtvia = {
+                                .family = route->gw_family,
+                                .address = route->gw,
+                        };
 
-                r = sd_rtnl_message_route_set_family(req, route->family);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set route family: %m");
+                        r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not append RTA_VIA attribute: %m");
+                }
         }
 
         if (route->dst_prefixlen > 0) {
@@ -1109,7 +1137,7 @@ int link_set_routes(Link *link) {
                         if (rt->gateway_from_dhcp)
                                 continue;
 
-                        if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
+                        if ((in_addr_is_null(rt->gw_family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
                                 continue;
 
                         r = route_configure(rt, link, route_handler, NULL);
@@ -1137,6 +1165,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
         uint32_t ifindex;
         uint16_t type;
         unsigned char table;
+        RouteVia via;
         int r;
 
         assert(rtnl);
@@ -1211,6 +1240,16 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 if (r < 0 && r != -ENODATA) {
                         log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
                         return 0;
+                } else if (r >= 0)
+                        tmp->gw_family = AF_INET;
+
+                r = sd_netlink_message_read(message, RTA_VIA, sizeof(via), &via);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
+                        return 0;
+                } else if (r >= 0) {
+                        tmp->gw_family = via.family;
+                        tmp->gw = via.address;
                 }
 
                 r = sd_netlink_message_read_in_addr(message, RTA_SRC, &tmp->src.in);
@@ -1238,7 +1277,8 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 if (r < 0 && r != -ENODATA) {
                         log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
                         return 0;
-                }
+                } else if (r >= 0)
+                        tmp->gw_family = AF_INET6;
 
                 r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &tmp->src.in6);
                 if (r < 0 && r != -ENODATA) {
@@ -1341,8 +1381,8 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 }
                 if (!in_addr_is_null(tmp->family, &tmp->src))
                         (void) in_addr_to_string(tmp->family, &tmp->src, &buf_src);
-                if (!in_addr_is_null(tmp->family, &tmp->gw))
-                        (void) in_addr_to_string(tmp->family, &tmp->gw, &buf_gw);
+                if (!in_addr_is_null(tmp->gw_family, &tmp->gw))
+                        (void) in_addr_to_string(tmp->gw_family, &tmp->gw, &buf_gw);
                 if (!in_addr_is_null(tmp->family, &tmp->prefsrc))
                         (void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc);
 
@@ -1582,10 +1622,7 @@ int config_parse_gateway(
                 }
         }
 
-        if (n->family == AF_UNSPEC)
-                r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
-        else
-                r = in_addr_from_string(n->family, rvalue, &n->gw);
+        r = in_addr_from_string_auto(rvalue, &n->gw_family, &n->gw);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
@@ -2213,6 +2250,9 @@ static int route_section_verify(Route *route, Network *network) {
         if (section_is_invalid(route->section))
                 return -EINVAL;
 
+        if (route->family == AF_UNSPEC)
+                route->family = route->gw_family;
+
         if (route->family == AF_UNSPEC) {
                 assert(route->section);
 
@@ -2223,6 +2263,12 @@ static int route_section_verify(Route *route, Network *network) {
                                          route->section->filename, route->section->line);
         }
 
+        if (route->family == AF_INET6 && route->gw_family == AF_INET)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: IPv4 gateway is configured for IPv6 route. "
+                                         "Ignoring [Route] section from line %u.",
+                                         route->section->filename, route->section->line);
+
         if (!route->table_set && network->vrf) {
                 route->table = VRF(network->vrf)->table;
                 route->table_set = true;
@@ -2247,7 +2293,7 @@ static int route_section_verify(Route *route, Network *network) {
                 route->priority = IP6_RT_PRIO_USER;
 
         if (ordered_hashmap_isempty(network->addresses_by_section) &&
-            in_addr_is_null(route->family, &route->gw) == 0 &&
+            in_addr_is_null(route->gw_family, &route->gw) == 0 &&
             route->gateway_onlink < 0) {
                 log_warning("%s: Gateway= without static address configured. "
                             "Enabling GatewayOnLink= option.",
index 5a82c3ac58e3aad572c31104fad5ec1bf867614b..9baee22ef26b5959aa07b2012220d47db6c9c649 100644 (file)
 typedef struct Manager Manager;
 typedef struct Network Network;
 
-typedef struct MultipathRouteVia {
+/* See struct rtvia in rtnetlink.h */
+typedef struct RouteVia {
         uint16_t family;
         union in_addr_union address;
-} _packed_ MultipathRouteVia;
+} _packed_ RouteVia;
 
 typedef struct MultipathRoute {
-        MultipathRouteVia gateway;
+        RouteVia gateway;
         int ifindex;
         uint32_t weight;
 } MultipathRoute;
@@ -34,6 +35,7 @@ typedef struct Route {
         Manager *manager;
 
         int family;
+        int gw_family;
         int quickack;
         int fast_open_no_cookie;
         int ttl_propagate;