LCOV - code coverage report
Current view: top level - network - networkd-route.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 10 837 1.2 %
Date: 2019-08-22 15:41:25 Functions: 2 48 4.2 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <linux/icmpv6.h>
       4             : 
       5             : #include "alloc-util.h"
       6             : #include "conf-parser.h"
       7             : #include "in-addr-util.h"
       8             : #include "missing_network.h"
       9             : #include "netlink-util.h"
      10             : #include "networkd-ipv4ll.h"
      11             : #include "networkd-manager.h"
      12             : #include "networkd-route.h"
      13             : #include "parse-util.h"
      14             : #include "set.h"
      15             : #include "string-table.h"
      16             : #include "string-util.h"
      17             : #include "strxcpyx.h"
      18             : #include "sysctl-util.h"
      19             : #include "util.h"
      20             : 
      21             : #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
      22             : 
      23           0 : static unsigned routes_max(void) {
      24             :         static thread_local unsigned cached = 0;
      25             : 
      26           0 :         _cleanup_free_ char *s4 = NULL, *s6 = NULL;
      27           0 :         unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
      28             : 
      29           0 :         if (cached > 0)
      30           0 :                 return cached;
      31             : 
      32           0 :         if (sysctl_read("net/ipv4/route/max_size", &s4) >= 0) {
      33           0 :                 truncate_nl(s4);
      34           0 :                 if (safe_atou(s4, &val4) >= 0 &&
      35           0 :                     val4 == 2147483647U)
      36             :                         /* This is the default "no limit" value in the kernel */
      37           0 :                         val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
      38             :         }
      39             : 
      40           0 :         if (sysctl_read("net/ipv6/route/max_size", &s6) >= 0) {
      41           0 :                 truncate_nl(s6);
      42           0 :                 (void) safe_atou(s6, &val6);
      43             :         }
      44             : 
      45           0 :         cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
      46           0 :                  MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
      47           0 :         return cached;
      48             : }
      49             : 
      50           0 : int route_new(Route **ret) {
      51           0 :         _cleanup_(route_freep) Route *route = NULL;
      52             : 
      53           0 :         route = new(Route, 1);
      54           0 :         if (!route)
      55           0 :                 return -ENOMEM;
      56             : 
      57           0 :         *route = (Route) {
      58             :                 .family = AF_UNSPEC,
      59             :                 .scope = RT_SCOPE_UNIVERSE,
      60             :                 .protocol = RTPROT_UNSPEC,
      61             :                 .type = RTN_UNICAST,
      62             :                 .table = RT_TABLE_MAIN,
      63             :                 .lifetime = USEC_INFINITY,
      64             :                 .quickack = -1,
      65             :                 .fast_open_no_cookie = -1,
      66             :                 .gateway_onlink = -1,
      67             :                 .ttl_propagate = -1,
      68             :         };
      69             : 
      70           0 :         *ret = TAKE_PTR(route);
      71             : 
      72           0 :         return 0;
      73             : }
      74             : 
      75           0 : static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
      76           0 :         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
      77           0 :         _cleanup_(route_freep) Route *route = NULL;
      78             :         int r;
      79             : 
      80           0 :         assert(network);
      81           0 :         assert(ret);
      82           0 :         assert(!!filename == (section_line > 0));
      83             : 
      84           0 :         if (filename) {
      85           0 :                 r = network_config_section_new(filename, section_line, &n);
      86           0 :                 if (r < 0)
      87           0 :                         return r;
      88             : 
      89           0 :                 route = hashmap_get(network->routes_by_section, n);
      90           0 :                 if (route) {
      91           0 :                         *ret = TAKE_PTR(route);
      92             : 
      93           0 :                         return 0;
      94             :                 }
      95             :         }
      96             : 
      97           0 :         if (network->n_static_routes >= routes_max())
      98           0 :                 return -E2BIG;
      99             : 
     100           0 :         r = route_new(&route);
     101           0 :         if (r < 0)
     102           0 :                 return r;
     103             : 
     104           0 :         route->protocol = RTPROT_STATIC;
     105           0 :         route->network = network;
     106           0 :         LIST_PREPEND(routes, network->static_routes, route);
     107           0 :         network->n_static_routes++;
     108             : 
     109           0 :         if (filename) {
     110           0 :                 route->section = TAKE_PTR(n);
     111             : 
     112           0 :                 r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops);
     113           0 :                 if (r < 0)
     114           0 :                         return r;
     115             : 
     116           0 :                 r = hashmap_put(network->routes_by_section, route->section, route);
     117           0 :                 if (r < 0)
     118           0 :                         return r;
     119             :         }
     120             : 
     121           0 :         *ret = TAKE_PTR(route);
     122             : 
     123           0 :         return 0;
     124             : }
     125             : 
     126           0 : void route_free(Route *route) {
     127           0 :         if (!route)
     128           0 :                 return;
     129             : 
     130           0 :         if (route->network) {
     131           0 :                 LIST_REMOVE(routes, route->network->static_routes, route);
     132             : 
     133           0 :                 assert(route->network->n_static_routes > 0);
     134           0 :                 route->network->n_static_routes--;
     135             : 
     136           0 :                 if (route->section)
     137           0 :                         hashmap_remove(route->network->routes_by_section, route->section);
     138             :         }
     139             : 
     140           0 :         network_config_section_free(route->section);
     141             : 
     142           0 :         if (route->link) {
     143           0 :                 set_remove(route->link->routes, route);
     144           0 :                 set_remove(route->link->routes_foreign, route);
     145             :         }
     146             : 
     147           0 :         sd_event_source_unref(route->expire);
     148             : 
     149           0 :         free(route);
     150             : }
     151             : 
     152           0 : static void route_hash_func(const Route *route, struct siphash *state) {
     153           0 :         assert(route);
     154             : 
     155           0 :         siphash24_compress(&route->family, sizeof(route->family), state);
     156             : 
     157           0 :         switch (route->family) {
     158           0 :         case AF_INET:
     159             :         case AF_INET6:
     160             :                 /* Equality of routes are given by the 4-touple
     161             :                    (dst_prefix,dst_prefixlen,tos,priority,table) */
     162           0 :                 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
     163           0 :                 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
     164           0 :                 siphash24_compress(&route->tos, sizeof(route->tos), state);
     165           0 :                 siphash24_compress(&route->priority, sizeof(route->priority), state);
     166           0 :                 siphash24_compress(&route->table, sizeof(route->table), state);
     167             : 
     168           0 :                 break;
     169           0 :         default:
     170             :                 /* treat any other address family as AF_UNSPEC */
     171           0 :                 break;
     172             :         }
     173           0 : }
     174             : 
     175           0 : static int route_compare_func(const Route *a, const Route *b) {
     176             :         int r;
     177             : 
     178           0 :         r = CMP(a->family, b->family);
     179           0 :         if (r != 0)
     180           0 :                 return r;
     181             : 
     182           0 :         switch (a->family) {
     183           0 :         case AF_INET:
     184             :         case AF_INET6:
     185           0 :                 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
     186           0 :                 if (r != 0)
     187           0 :                         return r;
     188             : 
     189           0 :                 r = CMP(a->tos, b->tos);
     190           0 :                 if (r != 0)
     191           0 :                         return r;
     192             : 
     193           0 :                 r = CMP(a->priority, b->priority);
     194           0 :                 if (r != 0)
     195           0 :                         return r;
     196             : 
     197           0 :                 r = CMP(a->table, b->table);
     198           0 :                 if (r != 0)
     199           0 :                         return r;
     200             : 
     201           0 :                 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
     202           0 :                 if (r != 0)
     203           0 :                         return r;
     204             : 
     205           0 :                 return memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
     206           0 :         default:
     207             :                 /* treat any other address family as AF_UNSPEC */
     208           0 :                 return 0;
     209             :         }
     210             : }
     211             : 
     212             : DEFINE_PRIVATE_HASH_OPS(route_hash_ops, Route, route_hash_func, route_compare_func);
     213             : 
     214           0 : static void route_full_hash_func(const Route *route, struct siphash *state) {
     215           0 :         assert(route);
     216             : 
     217           0 :         siphash24_compress(&route->family, sizeof(route->family), state);
     218             : 
     219           0 :         switch (route->family) {
     220           0 :         case AF_INET:
     221             :         case AF_INET6:
     222           0 :                 siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
     223           0 :                 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
     224           0 :                 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
     225           0 :                 siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
     226           0 :                 siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
     227           0 :                 siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
     228             : 
     229           0 :                 siphash24_compress(&route->tos, sizeof(route->tos), state);
     230           0 :                 siphash24_compress(&route->priority, sizeof(route->priority), state);
     231           0 :                 siphash24_compress(&route->table, sizeof(route->table), state);
     232           0 :                 siphash24_compress(&route->protocol, sizeof(route->protocol), state);
     233           0 :                 siphash24_compress(&route->scope, sizeof(route->scope), state);
     234           0 :                 siphash24_compress(&route->type, sizeof(route->type), state);
     235             : 
     236           0 :                 break;
     237           0 :         default:
     238             :                 /* treat any other address family as AF_UNSPEC */
     239           0 :                 break;
     240             :         }
     241           0 : }
     242             : 
     243           0 : static int route_full_compare_func(const Route *a, const Route *b) {
     244             :         int r;
     245             : 
     246           0 :         r = CMP(a->family, b->family);
     247           0 :         if (r != 0)
     248           0 :                 return r;
     249             : 
     250           0 :         switch (a->family) {
     251           0 :         case AF_INET:
     252             :         case AF_INET6:
     253           0 :                 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
     254           0 :                 if (r != 0)
     255           0 :                         return r;
     256             : 
     257           0 :                 r = CMP(a->src_prefixlen, b->src_prefixlen);
     258           0 :                 if (r != 0)
     259           0 :                         return r;
     260             : 
     261           0 :                 r = CMP(a->tos, b->tos);
     262           0 :                 if (r != 0)
     263           0 :                         return r;
     264             : 
     265           0 :                 r = CMP(a->priority, b->priority);
     266           0 :                 if (r != 0)
     267           0 :                         return r;
     268             : 
     269           0 :                 r = CMP(a->table, b->table);
     270           0 :                 if (r != 0)
     271           0 :                         return r;
     272             : 
     273           0 :                 r = CMP(a->protocol, b->protocol);
     274           0 :                 if (r != 0)
     275           0 :                         return r;
     276             : 
     277           0 :                 r = CMP(a->scope, b->scope);
     278           0 :                 if (r != 0)
     279           0 :                         return r;
     280             : 
     281           0 :                 r = CMP(a->type, b->type);
     282           0 :                 if (r != 0)
     283           0 :                         return r;
     284             : 
     285           0 :                 r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
     286           0 :                 if (r != 0)
     287           0 :                         return r;
     288             : 
     289           0 :                 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
     290           0 :                 if (r != 0)
     291           0 :                         return r;
     292             : 
     293           0 :                 r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
     294           0 :                 if (r != 0)
     295           0 :                         return r;
     296             : 
     297           0 :                 return memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
     298           0 :         default:
     299             :                 /* treat any other address family as AF_UNSPEC */
     300           0 :                 return 0;
     301             :         }
     302             : }
     303             : 
     304           0 : DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
     305             :                 route_full_hash_ops,
     306             :                 Route,
     307             :                 route_full_hash_func,
     308             :                 route_full_compare_func,
     309             :                 route_free);
     310             : 
     311           0 : bool route_equal(Route *r1, Route *r2) {
     312           0 :         if (r1 == r2)
     313           0 :                 return true;
     314             : 
     315           0 :         if (!r1 || !r2)
     316           0 :                 return false;
     317             : 
     318           0 :         return route_compare_func(r1, r2) == 0;
     319             : }
     320             : 
     321           0 : int route_get(Link *link,
     322             :               int family,
     323             :               const union in_addr_union *dst,
     324             :               unsigned char dst_prefixlen,
     325             :               const union in_addr_union *gw,
     326             :               unsigned char tos,
     327             :               uint32_t priority,
     328             :               uint32_t table,
     329             :               Route **ret) {
     330             : 
     331             :         Route route, *existing;
     332             : 
     333           0 :         assert(link);
     334           0 :         assert(dst);
     335             : 
     336           0 :         route = (Route) {
     337             :                 .family = family,
     338           0 :                 .dst = *dst,
     339             :                 .dst_prefixlen = dst_prefixlen,
     340           0 :                 .gw = gw ? *gw : IN_ADDR_NULL,
     341             :                 .tos = tos,
     342             :                 .priority = priority,
     343             :                 .table = table,
     344             :         };
     345             : 
     346           0 :         existing = set_get(link->routes, &route);
     347           0 :         if (existing) {
     348           0 :                 if (ret)
     349           0 :                         *ret = existing;
     350           0 :                 return 1;
     351             :         }
     352             : 
     353           0 :         existing = set_get(link->routes_foreign, &route);
     354           0 :         if (existing) {
     355           0 :                 if (ret)
     356           0 :                         *ret = existing;
     357           0 :                 return 0;
     358             :         }
     359             : 
     360           0 :         return -ENOENT;
     361             : }
     362             : 
     363           0 : static int route_add_internal(
     364             :                 Link *link,
     365             :                 Set **routes,
     366             :                 int family,
     367             :                 const union in_addr_union *dst,
     368             :                 unsigned char dst_prefixlen,
     369             :                 const union in_addr_union *gw,
     370             :                 unsigned char tos,
     371             :                 uint32_t priority,
     372             :                 uint32_t table,
     373             :                 Route **ret) {
     374             : 
     375           0 :         _cleanup_(route_freep) Route *route = NULL;
     376             :         int r;
     377             : 
     378           0 :         assert(link);
     379           0 :         assert(routes);
     380           0 :         assert(dst);
     381             : 
     382           0 :         r = route_new(&route);
     383           0 :         if (r < 0)
     384           0 :                 return r;
     385             : 
     386           0 :         route->family = family;
     387           0 :         route->dst = *dst;
     388           0 :         route->dst_prefixlen = dst_prefixlen;
     389           0 :         route->dst = *dst;
     390           0 :         route->gw = gw ? *gw : IN_ADDR_NULL;
     391           0 :         route->tos = tos;
     392           0 :         route->priority = priority;
     393           0 :         route->table = table;
     394             : 
     395           0 :         r = set_ensure_allocated(routes, &route_hash_ops);
     396           0 :         if (r < 0)
     397           0 :                 return r;
     398             : 
     399           0 :         r = set_put(*routes, route);
     400           0 :         if (r < 0)
     401           0 :                 return r;
     402           0 :         if (r == 0)
     403           0 :                 return -EEXIST;
     404             : 
     405           0 :         route->link = link;
     406             : 
     407           0 :         if (ret)
     408           0 :                 *ret = route;
     409             : 
     410           0 :         route = NULL;
     411             : 
     412           0 :         return 0;
     413             : }
     414             : 
     415           0 : int route_add_foreign(
     416             :                 Link *link,
     417             :                 int family,
     418             :                 const union in_addr_union *dst,
     419             :                 unsigned char dst_prefixlen,
     420             :                 const union in_addr_union *gw,
     421             :                 unsigned char tos,
     422             :                 uint32_t priority,
     423             :                 uint32_t table,
     424             :                 Route **ret) {
     425             : 
     426           0 :         return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, gw, tos, priority, table, ret);
     427             : }
     428             : 
     429           0 : int route_add(Link *link,
     430             :               int family,
     431             :               const union in_addr_union *dst,
     432             :               unsigned char dst_prefixlen,
     433             :               const union in_addr_union *gw,
     434             :               unsigned char tos,
     435             :               uint32_t priority,
     436             :               uint32_t table,
     437             :               Route **ret) {
     438             : 
     439             :         Route *route;
     440             :         int r;
     441             : 
     442           0 :         r = route_get(link, family, dst, dst_prefixlen, gw, tos, priority, table, &route);
     443           0 :         if (r == -ENOENT) {
     444             :                 /* Route does not exist, create a new one */
     445           0 :                 r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, gw, tos, priority, table, &route);
     446           0 :                 if (r < 0)
     447           0 :                         return r;
     448           0 :         } else if (r == 0) {
     449             :                 /* Take over a foreign route */
     450           0 :                 r = set_ensure_allocated(&link->routes, &route_hash_ops);
     451           0 :                 if (r < 0)
     452           0 :                         return r;
     453             : 
     454           0 :                 r = set_put(link->routes, route);
     455           0 :                 if (r < 0)
     456           0 :                         return r;
     457             : 
     458           0 :                 set_remove(link->routes_foreign, route);
     459           0 :         } else if (r == 1) {
     460             :                 /* Route exists, do nothing */
     461             :                 ;
     462             :         } else
     463           0 :                 return r;
     464             : 
     465           0 :         if (ret)
     466           0 :                 *ret = route;
     467             : 
     468           0 :         return 0;
     469             : }
     470             : 
     471           0 : void route_update(Route *route,
     472             :                   const union in_addr_union *src,
     473             :                   unsigned char src_prefixlen,
     474             :                   const union in_addr_union *gw,
     475             :                   const union in_addr_union *prefsrc,
     476             :                   unsigned char scope,
     477             :                   unsigned char protocol,
     478             :                   unsigned char type) {
     479             : 
     480           0 :         assert(route);
     481           0 :         assert(src || src_prefixlen == 0);
     482             : 
     483           0 :         route->src = src ? *src : IN_ADDR_NULL;
     484           0 :         route->src_prefixlen = src_prefixlen;
     485           0 :         route->gw = gw ? *gw : IN_ADDR_NULL;
     486           0 :         route->prefsrc = prefsrc ? *prefsrc : IN_ADDR_NULL;
     487           0 :         route->scope = scope;
     488           0 :         route->protocol = protocol;
     489           0 :         route->type = type;
     490           0 : }
     491             : 
     492           0 : static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
     493             :         int r;
     494             : 
     495           0 :         assert(m);
     496           0 :         assert(link);
     497           0 :         assert(link->ifname);
     498             : 
     499           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     500           0 :                 return 1;
     501             : 
     502           0 :         r = sd_netlink_message_get_errno(m);
     503           0 :         if (r < 0 && r != -ESRCH)
     504           0 :                 log_link_warning_errno(link, r, "Could not drop route: %m");
     505             : 
     506           0 :         return 1;
     507             : }
     508             : 
     509           0 : int route_remove(Route *route, Link *link,
     510             :                  link_netlink_message_handler_t callback) {
     511             : 
     512           0 :         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
     513             :         int r;
     514             : 
     515           0 :         assert(link);
     516           0 :         assert(link->manager);
     517           0 :         assert(link->manager->rtnl);
     518           0 :         assert(link->ifindex > 0);
     519           0 :         assert(IN_SET(route->family, AF_INET, AF_INET6));
     520             : 
     521           0 :         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
     522             :                                       RTM_DELROUTE, route->family,
     523           0 :                                       route->protocol);
     524           0 :         if (r < 0)
     525           0 :                 return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m");
     526             : 
     527           0 :         if (DEBUG_LOGGING) {
     528           0 :                 _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
     529             :                 char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
     530             : 
     531           0 :                 if (!in_addr_is_null(route->family, &route->dst)) {
     532           0 :                         (void) in_addr_to_string(route->family, &route->dst, &dst);
     533           0 :                         (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
     534             :                 }
     535           0 :                 if (!in_addr_is_null(route->family, &route->src))
     536           0 :                         (void) in_addr_to_string(route->family, &route->src, &src);
     537           0 :                 if (!in_addr_is_null(route->family, &route->gw))
     538           0 :                         (void) in_addr_to_string(route->family, &route->gw, &gw);
     539           0 :                 if (!in_addr_is_null(route->family, &route->prefsrc))
     540           0 :                         (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
     541             : 
     542           0 :                 log_link_debug(link, "Removing route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
     543             :                                strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
     544             :                                format_route_scope(route->scope, scope, sizeof(scope)),
     545             :                                format_route_table(route->table, table, sizeof(table)),
     546             :                                format_route_protocol(route->protocol, protocol, sizeof(protocol)),
     547             :                                strna(route_type_to_string(route->type)));
     548             :         }
     549             : 
     550           0 :         if (in_addr_is_null(route->family, &route->gw) == 0) {
     551           0 :                 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
     552           0 :                 if (r < 0)
     553           0 :                         return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
     554             :         }
     555             : 
     556           0 :         if (route->dst_prefixlen) {
     557           0 :                 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
     558           0 :                 if (r < 0)
     559           0 :                         return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
     560             : 
     561           0 :                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
     562           0 :                 if (r < 0)
     563           0 :                         return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
     564             :         }
     565             : 
     566           0 :         if (route->src_prefixlen) {
     567           0 :                 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
     568           0 :                 if (r < 0)
     569           0 :                         return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
     570             : 
     571           0 :                 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
     572           0 :                 if (r < 0)
     573           0 :                         return log_link_error_errno(link, r, "Could not set source prefix length: %m");
     574             :         }
     575             : 
     576           0 :         if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
     577           0 :                 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
     578           0 :                 if (r < 0)
     579           0 :                         return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
     580             :         }
     581             : 
     582           0 :         r = sd_rtnl_message_route_set_scope(req, route->scope);
     583           0 :         if (r < 0)
     584           0 :                 return log_link_error_errno(link, r, "Could not set scope: %m");
     585             : 
     586           0 :         r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
     587           0 :         if (r < 0)
     588           0 :                 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
     589             : 
     590           0 :         if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
     591           0 :                 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
     592           0 :                 if (r < 0)
     593           0 :                         return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
     594             :         }
     595             : 
     596           0 :         r = netlink_call_async(link->manager->rtnl, NULL, req,
     597             :                                callback ?: route_remove_handler,
     598             :                                link_netlink_destroy_callback, link);
     599           0 :         if (r < 0)
     600           0 :                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
     601             : 
     602           0 :         link_ref(link);
     603             : 
     604           0 :         return 0;
     605             : }
     606             : 
     607           0 : int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
     608           0 :         Route *route = userdata;
     609             :         int r;
     610             : 
     611           0 :         assert(route);
     612             : 
     613           0 :         r = route_remove(route, route->link, NULL);
     614           0 :         if (r < 0)
     615           0 :                 log_warning_errno(r, "Could not remove route: %m");
     616             :         else
     617           0 :                 route_free(route);
     618             : 
     619           0 :         return 1;
     620             : }
     621             : 
     622           0 : int route_configure(
     623             :                 Route *route,
     624             :                 Link *link,
     625             :                 link_netlink_message_handler_t callback) {
     626             : 
     627           0 :         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
     628           0 :         _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
     629             :         usec_t lifetime;
     630             :         int r;
     631             : 
     632           0 :         assert(link);
     633           0 :         assert(link->manager);
     634           0 :         assert(link->manager->rtnl);
     635           0 :         assert(link->ifindex > 0);
     636           0 :         assert(IN_SET(route->family, AF_INET, AF_INET6));
     637           0 :         assert(callback);
     638             : 
     639           0 :         if (route->family == AF_INET6 && link_sysctl_ipv6_enabled(link) == 0) {
     640           0 :                 log_link_warning(link, "An IPv6 route is requested, but IPv6 is disabled by sysctl, ignoring.");
     641           0 :                 return 0;
     642             :         }
     643             : 
     644           0 :         if (route_get(link, route->family, &route->dst, route->dst_prefixlen, &route->gw, route->tos, route->priority, route->table, NULL) <= 0 &&
     645           0 :             set_size(link->routes) >= routes_max())
     646           0 :                 return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
     647             :                                             "Too many routes are configured, refusing: %m");
     648             : 
     649           0 :         if (DEBUG_LOGGING) {
     650           0 :                 _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
     651             :                 char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
     652             : 
     653           0 :                 if (!in_addr_is_null(route->family, &route->dst)) {
     654           0 :                         (void) in_addr_to_string(route->family, &route->dst, &dst);
     655           0 :                         (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
     656             :                 }
     657           0 :                 if (!in_addr_is_null(route->family, &route->src))
     658           0 :                         (void) in_addr_to_string(route->family, &route->src, &src);
     659           0 :                 if (!in_addr_is_null(route->family, &route->gw))
     660           0 :                         (void) in_addr_to_string(route->family, &route->gw, &gw);
     661           0 :                 if (!in_addr_is_null(route->family, &route->prefsrc))
     662           0 :                         (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
     663             : 
     664           0 :                 log_link_debug(link, "Configuring route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
     665             :                                strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
     666             :                                format_route_scope(route->scope, scope, sizeof(scope)),
     667             :                                format_route_table(route->table, table, sizeof(table)),
     668             :                                format_route_protocol(route->protocol, protocol, sizeof(protocol)),
     669             :                                strna(route_type_to_string(route->type)));
     670             :         }
     671             : 
     672           0 :         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
     673           0 :                                       RTM_NEWROUTE, route->family,
     674           0 :                                       route->protocol);
     675           0 :         if (r < 0)
     676           0 :                 return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
     677             : 
     678           0 :         if (in_addr_is_null(route->family, &route->gw) == 0) {
     679           0 :                 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
     680           0 :                 if (r < 0)
     681           0 :                         return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
     682             : 
     683           0 :                 r = sd_rtnl_message_route_set_family(req, route->family);
     684           0 :                 if (r < 0)
     685           0 :                         return log_link_error_errno(link, r, "Could not set route family: %m");
     686             :         }
     687             : 
     688           0 :         if (route->dst_prefixlen) {
     689           0 :                 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
     690           0 :                 if (r < 0)
     691           0 :                         return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
     692             : 
     693           0 :                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
     694           0 :                 if (r < 0)
     695           0 :                         return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
     696             :         }
     697             : 
     698           0 :         if (route->src_prefixlen) {
     699           0 :                 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
     700           0 :                 if (r < 0)
     701           0 :                         return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
     702             : 
     703           0 :                 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
     704           0 :                 if (r < 0)
     705           0 :                         return log_link_error_errno(link, r, "Could not set source prefix length: %m");
     706             :         }
     707             : 
     708           0 :         if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
     709           0 :                 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
     710           0 :                 if (r < 0)
     711           0 :                         return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
     712             :         }
     713             : 
     714           0 :         r = sd_rtnl_message_route_set_scope(req, route->scope);
     715           0 :         if (r < 0)
     716           0 :                 return log_link_error_errno(link, r, "Could not set scope: %m");
     717             : 
     718           0 :         if (route->gateway_onlink >= 0)
     719           0 :                 SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
     720             : 
     721           0 :         r = sd_rtnl_message_route_set_flags(req, route->flags);
     722           0 :         if (r < 0)
     723           0 :                 return log_link_error_errno(link, r, "Could not set flags: %m");
     724             : 
     725           0 :         if (route->table != RT_TABLE_MAIN) {
     726           0 :                 if (route->table < 256) {
     727           0 :                         r = sd_rtnl_message_route_set_table(req, route->table);
     728           0 :                         if (r < 0)
     729           0 :                                 return log_link_error_errno(link, r, "Could not set route table: %m");
     730             :                 } else {
     731           0 :                         r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
     732           0 :                         if (r < 0)
     733           0 :                                 return log_link_error_errno(link, r, "Could not set route table: %m");
     734             : 
     735             :                         /* Table attribute to allow more than 256. */
     736           0 :                         r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
     737           0 :                         if (r < 0)
     738           0 :                                 return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m");
     739             :                 }
     740             :         }
     741             : 
     742           0 :         r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
     743           0 :         if (r < 0)
     744           0 :                 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
     745             : 
     746           0 :         r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
     747           0 :         if (r < 0)
     748           0 :                 return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
     749             : 
     750           0 :         if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
     751           0 :                 r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
     752           0 :                         DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC));
     753           0 :                 if (r < 0)
     754           0 :                         return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m");
     755             :         }
     756             : 
     757           0 :         r = sd_rtnl_message_route_set_type(req, route->type);
     758           0 :         if (r < 0)
     759           0 :                 return log_link_error_errno(link, r, "Could not set route type: %m");
     760             : 
     761           0 :         if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
     762           0 :                 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
     763           0 :                 if (r < 0)
     764           0 :                         return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
     765             :         }
     766             : 
     767           0 :         if (route->ttl_propagate >= 0) {
     768           0 :                 r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
     769           0 :                 if (r < 0)
     770           0 :                         return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
     771             :         }
     772             : 
     773           0 :         r = sd_netlink_message_open_container(req, RTA_METRICS);
     774           0 :         if (r < 0)
     775           0 :                 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
     776             : 
     777           0 :         if (route->mtu > 0) {
     778           0 :                 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
     779           0 :                 if (r < 0)
     780           0 :                         return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m");
     781             :         }
     782             : 
     783           0 :         if (route->initcwnd > 0) {
     784           0 :                 r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
     785           0 :                 if (r < 0)
     786           0 :                         return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m");
     787             :         }
     788             : 
     789           0 :         if (route->initrwnd > 0) {
     790           0 :                 r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
     791           0 :                 if (r < 0)
     792           0 :                         return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m");
     793             :         }
     794             : 
     795           0 :         if (route->quickack >= 0) {
     796           0 :                 r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
     797           0 :                 if (r < 0)
     798           0 :                         return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m");
     799             :         }
     800             : 
     801           0 :         if (route->fast_open_no_cookie >= 0) {
     802           0 :                 r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
     803           0 :                 if (r < 0)
     804           0 :                         return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m");
     805             :         }
     806             : 
     807           0 :         r = sd_netlink_message_close_container(req);
     808           0 :         if (r < 0)
     809           0 :                 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
     810             : 
     811           0 :         r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
     812             :                                link_netlink_destroy_callback, link);
     813           0 :         if (r < 0)
     814           0 :                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
     815             : 
     816           0 :         link_ref(link);
     817             : 
     818           0 :         lifetime = route->lifetime;
     819             : 
     820           0 :         r = route_add(link, route->family, &route->dst, route->dst_prefixlen, &route->gw, route->tos, route->priority, route->table, &route);
     821           0 :         if (r < 0)
     822           0 :                 return log_link_error_errno(link, r, "Could not add route: %m");
     823             : 
     824             :         /* TODO: drop expiration handling once it can be pushed into the kernel */
     825           0 :         route->lifetime = lifetime;
     826             : 
     827           0 :         if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
     828           0 :                 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
     829           0 :                                       route->lifetime, 0, route_expire_handler, route);
     830           0 :                 if (r < 0)
     831           0 :                         return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
     832             :         }
     833             : 
     834           0 :         sd_event_source_unref(route->expire);
     835           0 :         route->expire = TAKE_PTR(expire);
     836             : 
     837           0 :         return 1;
     838             : }
     839             : 
     840           3 : int network_add_ipv4ll_route(Network *network) {
     841           3 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
     842             :         int r;
     843             : 
     844           3 :         assert(network);
     845             : 
     846           3 :         if (!network->ipv4ll_route)
     847           3 :                 return 0;
     848             : 
     849             :         /* IPv4LLRoute= is in [Network] section. */
     850           0 :         r = route_new_static(network, NULL, 0, &n);
     851           0 :         if (r < 0)
     852           0 :                 return r;
     853             : 
     854           0 :         r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
     855           0 :         if (r < 0)
     856           0 :                 return r;
     857             : 
     858           0 :         n->family = AF_INET;
     859           0 :         n->dst_prefixlen = 16;
     860           0 :         n->scope = RT_SCOPE_LINK;
     861           0 :         n->scope_set = true;
     862           0 :         n->table_set = true;
     863           0 :         n->priority = IPV4LL_ROUTE_METRIC;
     864           0 :         n->protocol = RTPROT_STATIC;
     865             : 
     866           0 :         TAKE_PTR(n);
     867           0 :         return 0;
     868             : }
     869             : 
     870           3 : int network_add_default_route_on_device(Network *network) {
     871           3 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
     872             :         int r;
     873             : 
     874           3 :         assert(network);
     875             : 
     876           3 :         if (!network->default_route_on_device)
     877           3 :                 return 0;
     878             : 
     879             :         /* DefaultRouteOnDevice= is in [Network] section. */
     880           0 :         r = route_new_static(network, NULL, 0, &n);
     881           0 :         if (r < 0)
     882           0 :                 return r;
     883             : 
     884           0 :         r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
     885           0 :         if (r < 0)
     886           0 :                 return r;
     887             : 
     888           0 :         n->family = AF_INET;
     889             : 
     890           0 :         TAKE_PTR(n);
     891           0 :         return 0;
     892             : }
     893             : 
     894             : static const char * const route_type_table[__RTN_MAX] = {
     895             :         [RTN_UNICAST]     = "unicast",
     896             :         [RTN_LOCAL]       = "local",
     897             :         [RTN_BROADCAST]   = "broadcast",
     898             :         [RTN_ANYCAST]     = "anycast",
     899             :         [RTN_MULTICAST]   = "multicast",
     900             :         [RTN_BLACKHOLE]   = "blackhole",
     901             :         [RTN_UNREACHABLE] = "unreachable",
     902             :         [RTN_PROHIBIT]    = "prohibit",
     903             :         [RTN_THROW]       = "throw",
     904             :         [RTN_NAT]         = "nat",
     905             :         [RTN_XRESOLVE]    = "xresolve",
     906             : };
     907             : 
     908             : assert_cc(__RTN_MAX <= UCHAR_MAX);
     909           0 : DEFINE_STRING_TABLE_LOOKUP(route_type, int);
     910             : 
     911             : static const char * const route_scope_table[] = {
     912             :         [RT_SCOPE_UNIVERSE] = "global",
     913             :         [RT_SCOPE_SITE]     = "site",
     914             :         [RT_SCOPE_LINK]     = "link",
     915             :         [RT_SCOPE_HOST]     = "host",
     916             :         [RT_SCOPE_NOWHERE]  = "nowhere",
     917             : };
     918             : 
     919           0 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int);
     920             : 
     921           0 : const char *format_route_scope(int scope, char *buf, size_t size) {
     922             :         const char *s;
     923           0 :         char *p = buf;
     924             : 
     925           0 :         s = route_scope_to_string(scope);
     926           0 :         if (s)
     927           0 :                 strpcpy(&p, size, s);
     928             :         else
     929           0 :                 strpcpyf(&p, size, "%d", scope);
     930             : 
     931           0 :         return buf;
     932             : }
     933             : 
     934             : static const char * const route_table_table[] = {
     935             :         [RT_TABLE_DEFAULT] = "default",
     936             :         [RT_TABLE_MAIN]    = "main",
     937             :         [RT_TABLE_LOCAL]   = "local",
     938             : };
     939             : 
     940           0 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
     941             : 
     942           0 : const char *format_route_table(int table, char *buf, size_t size) {
     943             :         const char *s;
     944           0 :         char *p = buf;
     945             : 
     946           0 :         s = route_table_to_string(table);
     947           0 :         if (s)
     948           0 :                 strpcpy(&p, size, s);
     949             :         else
     950           0 :                 strpcpyf(&p, size, "%d", table);
     951             : 
     952           0 :         return buf;
     953             : }
     954             : 
     955             : static const char * const route_protocol_table[] = {
     956             :         [RTPROT_KERNEL] = "kernel",
     957             :         [RTPROT_BOOT]   = "boot",
     958             :         [RTPROT_STATIC] = "static",
     959             : };
     960             : 
     961           0 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_protocol, int);
     962             : 
     963             : static const char * const route_protocol_full_table[] = {
     964             :         [RTPROT_REDIRECT] = "redirect",
     965             :         [RTPROT_KERNEL]   = "kernel",
     966             :         [RTPROT_BOOT]     = "boot",
     967             :         [RTPROT_STATIC]   = "static",
     968             :         [RTPROT_GATED]    = "gated",
     969             :         [RTPROT_RA]       = "ra",
     970             :         [RTPROT_MRT]      = "mrt",
     971             :         [RTPROT_ZEBRA]    = "zebra",
     972             :         [RTPROT_BIRD]     = "bird",
     973             :         [RTPROT_DNROUTED] = "dnrouted",
     974             :         [RTPROT_XORP]     = "xorp",
     975             :         [RTPROT_NTK]      = "ntk",
     976             :         [RTPROT_DHCP]     = "dhcp",
     977             :         [RTPROT_MROUTED]  = "mrouted",
     978             :         [RTPROT_BABEL]    = "babel",
     979             :         [RTPROT_BGP]      = "bgp",
     980             :         [RTPROT_ISIS]     = "isis",
     981             :         [RTPROT_OSPF]     = "ospf",
     982             :         [RTPROT_RIP]      = "rip",
     983             :         [RTPROT_EIGRP]    = "eigrp",
     984             : };
     985             : 
     986           0 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(route_protocol_full, int);
     987             : 
     988           0 : const char *format_route_protocol(int protocol, char *buf, size_t size) {
     989             :         const char *s;
     990           0 :         char *p = buf;
     991             : 
     992           0 :         s = route_protocol_full_to_string(protocol);
     993           0 :         if (s)
     994           0 :                 strpcpy(&p, size, s);
     995             :         else
     996           0 :                 strpcpyf(&p, size, "%d", protocol);
     997             : 
     998           0 :         return buf;
     999             : }
    1000             : 
    1001           0 : int config_parse_gateway(
    1002             :                 const char *unit,
    1003             :                 const char *filename,
    1004             :                 unsigned line,
    1005             :                 const char *section,
    1006             :                 unsigned section_line,
    1007             :                 const char *lvalue,
    1008             :                 int ltype,
    1009             :                 const char *rvalue,
    1010             :                 void *data,
    1011             :                 void *userdata) {
    1012             : 
    1013           0 :         Network *network = userdata;
    1014           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1015             :         int r;
    1016             : 
    1017           0 :         assert(filename);
    1018           0 :         assert(section);
    1019           0 :         assert(lvalue);
    1020           0 :         assert(rvalue);
    1021           0 :         assert(data);
    1022             : 
    1023           0 :         if (streq(section, "Network")) {
    1024             :                 /* we are not in an Route section, so treat
    1025             :                  * this as the special '0' section */
    1026           0 :                 r = route_new_static(network, NULL, 0, &n);
    1027             :         } else
    1028           0 :                 r = route_new_static(network, filename, section_line, &n);
    1029           0 :         if (r < 0)
    1030           0 :                 return r;
    1031             : 
    1032           0 :         if (n->family == AF_UNSPEC)
    1033           0 :                 r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
    1034             :         else
    1035           0 :                 r = in_addr_from_string(n->family, rvalue, &n->gw);
    1036           0 :         if (r < 0) {
    1037           0 :                 log_syntax(unit, LOG_ERR, filename, line, r,
    1038             :                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
    1039           0 :                 return 0;
    1040             :         }
    1041             : 
    1042           0 :         TAKE_PTR(n);
    1043           0 :         return 0;
    1044             : }
    1045             : 
    1046           0 : int config_parse_preferred_src(
    1047             :                 const char *unit,
    1048             :                 const char *filename,
    1049             :                 unsigned line,
    1050             :                 const char *section,
    1051             :                 unsigned section_line,
    1052             :                 const char *lvalue,
    1053             :                 int ltype,
    1054             :                 const char *rvalue,
    1055             :                 void *data,
    1056             :                 void *userdata) {
    1057             : 
    1058           0 :         Network *network = userdata;
    1059           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1060             :         int r;
    1061             : 
    1062           0 :         assert(filename);
    1063           0 :         assert(section);
    1064           0 :         assert(lvalue);
    1065           0 :         assert(rvalue);
    1066           0 :         assert(data);
    1067             : 
    1068           0 :         r = route_new_static(network, filename, section_line, &n);
    1069           0 :         if (r < 0)
    1070           0 :                 return r;
    1071             : 
    1072           0 :         if (n->family == AF_UNSPEC)
    1073           0 :                 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
    1074             :         else
    1075           0 :                 r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
    1076           0 :         if (r < 0) {
    1077           0 :                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
    1078             :                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
    1079           0 :                 return 0;
    1080             :         }
    1081             : 
    1082           0 :         TAKE_PTR(n);
    1083           0 :         return 0;
    1084             : }
    1085             : 
    1086           0 : int config_parse_destination(
    1087             :                 const char *unit,
    1088             :                 const char *filename,
    1089             :                 unsigned line,
    1090             :                 const char *section,
    1091             :                 unsigned section_line,
    1092             :                 const char *lvalue,
    1093             :                 int ltype,
    1094             :                 const char *rvalue,
    1095             :                 void *data,
    1096             :                 void *userdata) {
    1097             : 
    1098           0 :         Network *network = userdata;
    1099           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1100             :         union in_addr_union *buffer;
    1101             :         unsigned char *prefixlen;
    1102             :         int r;
    1103             : 
    1104           0 :         assert(filename);
    1105           0 :         assert(section);
    1106           0 :         assert(lvalue);
    1107           0 :         assert(rvalue);
    1108           0 :         assert(data);
    1109             : 
    1110           0 :         r = route_new_static(network, filename, section_line, &n);
    1111           0 :         if (r < 0)
    1112           0 :                 return r;
    1113             : 
    1114           0 :         if (streq(lvalue, "Destination")) {
    1115           0 :                 buffer = &n->dst;
    1116           0 :                 prefixlen = &n->dst_prefixlen;
    1117           0 :         } else if (streq(lvalue, "Source")) {
    1118           0 :                 buffer = &n->src;
    1119           0 :                 prefixlen = &n->src_prefixlen;
    1120             :         } else
    1121           0 :                 assert_not_reached(lvalue);
    1122             : 
    1123           0 :         if (n->family == AF_UNSPEC)
    1124           0 :                 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
    1125             :         else
    1126           0 :                 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
    1127           0 :         if (r < 0) {
    1128           0 :                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
    1129             :                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
    1130           0 :                 return 0;
    1131             :         }
    1132             : 
    1133           0 :         TAKE_PTR(n);
    1134           0 :         return 0;
    1135             : }
    1136             : 
    1137           0 : int config_parse_route_priority(
    1138             :                 const char *unit,
    1139             :                 const char *filename,
    1140             :                 unsigned line,
    1141             :                 const char *section,
    1142             :                 unsigned section_line,
    1143             :                 const char *lvalue,
    1144             :                 int ltype,
    1145             :                 const char *rvalue,
    1146             :                 void *data,
    1147             :                 void *userdata) {
    1148             : 
    1149           0 :         Network *network = userdata;
    1150           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1151             :         int r;
    1152             : 
    1153           0 :         assert(filename);
    1154           0 :         assert(section);
    1155           0 :         assert(lvalue);
    1156           0 :         assert(rvalue);
    1157           0 :         assert(data);
    1158             : 
    1159           0 :         r = route_new_static(network, filename, section_line, &n);
    1160           0 :         if (r < 0)
    1161           0 :                 return r;
    1162             : 
    1163           0 :         r = safe_atou32(rvalue, &n->priority);
    1164           0 :         if (r < 0) {
    1165           0 :                 log_syntax(unit, LOG_ERR, filename, line, r,
    1166             :                            "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
    1167           0 :                 return 0;
    1168             :         }
    1169             : 
    1170           0 :         TAKE_PTR(n);
    1171           0 :         return 0;
    1172             : }
    1173             : 
    1174           0 : int config_parse_route_scope(
    1175             :                 const char *unit,
    1176             :                 const char *filename,
    1177             :                 unsigned line,
    1178             :                 const char *section,
    1179             :                 unsigned section_line,
    1180             :                 const char *lvalue,
    1181             :                 int ltype,
    1182             :                 const char *rvalue,
    1183             :                 void *data,
    1184             :                 void *userdata) {
    1185             : 
    1186           0 :         Network *network = userdata;
    1187           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1188             :         int r;
    1189             : 
    1190           0 :         assert(filename);
    1191           0 :         assert(section);
    1192           0 :         assert(lvalue);
    1193           0 :         assert(rvalue);
    1194           0 :         assert(data);
    1195             : 
    1196           0 :         r = route_new_static(network, filename, section_line, &n);
    1197           0 :         if (r < 0)
    1198           0 :                 return r;
    1199             : 
    1200           0 :         r = route_scope_from_string(rvalue);
    1201           0 :         if (r < 0) {
    1202           0 :                 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
    1203           0 :                 return 0;
    1204             :         }
    1205             : 
    1206           0 :         n->scope = r;
    1207           0 :         n->scope_set = true;
    1208           0 :         TAKE_PTR(n);
    1209           0 :         return 0;
    1210             : }
    1211             : 
    1212           0 : int config_parse_route_table(
    1213             :                 const char *unit,
    1214             :                 const char *filename,
    1215             :                 unsigned line,
    1216             :                 const char *section,
    1217             :                 unsigned section_line,
    1218             :                 const char *lvalue,
    1219             :                 int ltype,
    1220             :                 const char *rvalue,
    1221             :                 void *data,
    1222             :                 void *userdata) {
    1223             : 
    1224           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1225           0 :         Network *network = userdata;
    1226             :         int r;
    1227             : 
    1228           0 :         assert(filename);
    1229           0 :         assert(section);
    1230           0 :         assert(lvalue);
    1231           0 :         assert(rvalue);
    1232           0 :         assert(data);
    1233             : 
    1234           0 :         r = route_new_static(network, filename, section_line, &n);
    1235           0 :         if (r < 0)
    1236           0 :                 return r;
    1237             : 
    1238           0 :         r = route_table_from_string(rvalue);
    1239           0 :         if (r >= 0)
    1240           0 :                 n->table = r;
    1241             :         else {
    1242           0 :                 r = safe_atou32(rvalue, &n->table);
    1243           0 :                 if (r < 0) {
    1244           0 :                         log_syntax(unit, LOG_ERR, filename, line, r,
    1245             :                                    "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
    1246           0 :                         return 0;
    1247             :                 }
    1248             :         }
    1249             : 
    1250           0 :         n->table_set = true;
    1251           0 :         TAKE_PTR(n);
    1252           0 :         return 0;
    1253             : }
    1254             : 
    1255           0 : int config_parse_gateway_onlink(
    1256             :                 const char *unit,
    1257             :                 const char *filename,
    1258             :                 unsigned line,
    1259             :                 const char *section,
    1260             :                 unsigned section_line,
    1261             :                 const char *lvalue,
    1262             :                 int ltype,
    1263             :                 const char *rvalue,
    1264             :                 void *data,
    1265             :                 void *userdata) {
    1266             : 
    1267           0 :         Network *network = userdata;
    1268           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1269             :         int r;
    1270             : 
    1271           0 :         assert(filename);
    1272           0 :         assert(section);
    1273           0 :         assert(lvalue);
    1274           0 :         assert(rvalue);
    1275           0 :         assert(data);
    1276             : 
    1277           0 :         r = route_new_static(network, filename, section_line, &n);
    1278           0 :         if (r < 0)
    1279           0 :                 return r;
    1280             : 
    1281           0 :         r = parse_boolean(rvalue);
    1282           0 :         if (r < 0) {
    1283           0 :                 log_syntax(unit, LOG_ERR, filename, line, r,
    1284             :                            "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
    1285           0 :                 return 0;
    1286             :         }
    1287             : 
    1288           0 :         n->gateway_onlink = r;
    1289             : 
    1290           0 :         TAKE_PTR(n);
    1291           0 :         return 0;
    1292             : }
    1293             : 
    1294           0 : int config_parse_ipv6_route_preference(
    1295             :                 const char *unit,
    1296             :                 const char *filename,
    1297             :                 unsigned line,
    1298             :                 const char *section,
    1299             :                 unsigned section_line,
    1300             :                 const char *lvalue,
    1301             :                 int ltype,
    1302             :                 const char *rvalue,
    1303             :                 void *data,
    1304             :                 void *userdata) {
    1305             : 
    1306           0 :         Network *network = userdata;
    1307           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1308             :         int r;
    1309             : 
    1310           0 :         r = route_new_static(network, filename, section_line, &n);
    1311           0 :         if (r < 0)
    1312           0 :                 return r;
    1313             : 
    1314           0 :         if (streq(rvalue, "low"))
    1315           0 :                 n->pref = ICMPV6_ROUTER_PREF_LOW;
    1316           0 :         else if (streq(rvalue, "medium"))
    1317           0 :                 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
    1318           0 :         else if (streq(rvalue, "high"))
    1319           0 :                 n->pref = ICMPV6_ROUTER_PREF_HIGH;
    1320             :         else {
    1321           0 :                 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route preference: %s", rvalue);
    1322           0 :                 return 0;
    1323             :         }
    1324             : 
    1325           0 :         TAKE_PTR(n);
    1326           0 :         return 0;
    1327             : }
    1328             : 
    1329           0 : int config_parse_route_protocol(
    1330             :                 const char *unit,
    1331             :                 const char *filename,
    1332             :                 unsigned line,
    1333             :                 const char *section,
    1334             :                 unsigned section_line,
    1335             :                 const char *lvalue,
    1336             :                 int ltype,
    1337             :                 const char *rvalue,
    1338             :                 void *data,
    1339             :                 void *userdata) {
    1340             : 
    1341           0 :         Network *network = userdata;
    1342           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1343             :         int r;
    1344             : 
    1345           0 :         r = route_new_static(network, filename, section_line, &n);
    1346           0 :         if (r < 0)
    1347           0 :                 return r;
    1348             : 
    1349           0 :         r = route_protocol_from_string(rvalue);
    1350           0 :         if (r >= 0)
    1351           0 :                 n->protocol = r;
    1352             :         else {
    1353           0 :                 r = safe_atou8(rvalue , &n->protocol);
    1354           0 :                 if (r < 0) {
    1355           0 :                         log_syntax(unit, LOG_ERR, filename, line, r,
    1356             :                                    "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
    1357           0 :                         return 0;
    1358             :                 }
    1359             :         }
    1360             : 
    1361           0 :         TAKE_PTR(n);
    1362           0 :         return 0;
    1363             : }
    1364             : 
    1365           0 : int config_parse_route_type(
    1366             :                 const char *unit,
    1367             :                 const char *filename,
    1368             :                 unsigned line,
    1369             :                 const char *section,
    1370             :                 unsigned section_line,
    1371             :                 const char *lvalue,
    1372             :                 int ltype,
    1373             :                 const char *rvalue,
    1374             :                 void *data,
    1375             :                 void *userdata) {
    1376             : 
    1377           0 :         Network *network = userdata;
    1378           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1379             :         int t, r;
    1380             : 
    1381           0 :         r = route_new_static(network, filename, section_line, &n);
    1382           0 :         if (r < 0)
    1383           0 :                 return r;
    1384             : 
    1385           0 :         t = route_type_from_string(rvalue);
    1386           0 :         if (t < 0) {
    1387           0 :                 log_syntax(unit, LOG_ERR, filename, line, 0,
    1388             :                            "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
    1389           0 :                 return 0;
    1390             :         }
    1391             : 
    1392           0 :         n->type = (unsigned char) t;
    1393             : 
    1394           0 :         TAKE_PTR(n);
    1395           0 :         return 0;
    1396             : }
    1397             : 
    1398           0 : int config_parse_tcp_window(
    1399             :                 const char *unit,
    1400             :                 const char *filename,
    1401             :                 unsigned line,
    1402             :                 const char *section,
    1403             :                 unsigned section_line,
    1404             :                 const char *lvalue,
    1405             :                 int ltype,
    1406             :                 const char *rvalue,
    1407             :                 void *data,
    1408             :                 void *userdata) {
    1409             : 
    1410           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1411           0 :         Network *network = userdata;
    1412             :         uint64_t k;
    1413             :         int r;
    1414             : 
    1415           0 :         assert(filename);
    1416           0 :         assert(section);
    1417           0 :         assert(lvalue);
    1418           0 :         assert(rvalue);
    1419           0 :         assert(data);
    1420             : 
    1421           0 :         r = route_new_static(network, filename, section_line, &n);
    1422           0 :         if (r < 0)
    1423           0 :                 return r;
    1424             : 
    1425           0 :         r = parse_size(rvalue, 1024, &k);
    1426           0 :         if (r < 0) {
    1427           0 :                 log_syntax(unit, LOG_ERR, filename, line, r,
    1428             :                            "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
    1429           0 :                 return 0;
    1430             :         }
    1431           0 :         if (k > UINT32_MAX) {
    1432           0 :                 log_syntax(unit, LOG_ERR, filename, line, 0,
    1433             :                            "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
    1434           0 :                 return 0;
    1435             :         }
    1436             : 
    1437           0 :         if (streq(lvalue, "InitialCongestionWindow"))
    1438           0 :                 n->initcwnd = k;
    1439           0 :         else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
    1440           0 :                 n->initrwnd = k;
    1441             :         else
    1442           0 :                 assert_not_reached("Invalid TCP window type.");
    1443             : 
    1444           0 :         TAKE_PTR(n);
    1445           0 :         return 0;
    1446             : }
    1447             : 
    1448           0 : int config_parse_quickack(
    1449             :                 const char *unit,
    1450             :                 const char *filename,
    1451             :                 unsigned line,
    1452             :                 const char *section,
    1453             :                 unsigned section_line,
    1454             :                 const char *lvalue,
    1455             :                 int ltype,
    1456             :                 const char *rvalue,
    1457             :                 void *data,
    1458             :                 void *userdata) {
    1459             : 
    1460           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1461           0 :         Network *network = userdata;
    1462             :         int k, r;
    1463             : 
    1464           0 :         assert(filename);
    1465           0 :         assert(section);
    1466           0 :         assert(lvalue);
    1467           0 :         assert(rvalue);
    1468           0 :         assert(data);
    1469             : 
    1470           0 :         r = route_new_static(network, filename, section_line, &n);
    1471           0 :         if (r < 0)
    1472           0 :                 return r;
    1473             : 
    1474           0 :         k = parse_boolean(rvalue);
    1475           0 :         if (k < 0) {
    1476           0 :                 log_syntax(unit, LOG_ERR, filename, line, k,
    1477             :                            "Failed to parse TCP quickack, ignoring: %s", rvalue);
    1478           0 :                 return 0;
    1479             :         }
    1480             : 
    1481           0 :         n->quickack = !!k;
    1482           0 :         TAKE_PTR(n);
    1483           0 :         return 0;
    1484             : }
    1485             : 
    1486           0 : int config_parse_fast_open_no_cookie(
    1487             :                 const char *unit,
    1488             :                 const char *filename,
    1489             :                 unsigned line,
    1490             :                 const char *section,
    1491             :                 unsigned section_line,
    1492             :                 const char *lvalue,
    1493             :                 int ltype,
    1494             :                 const char *rvalue,
    1495             :                 void *data,
    1496             :                 void *userdata) {
    1497             : 
    1498           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1499           0 :         Network *network = userdata;
    1500             :         int k, r;
    1501             : 
    1502           0 :         assert(filename);
    1503           0 :         assert(section);
    1504           0 :         assert(lvalue);
    1505           0 :         assert(rvalue);
    1506           0 :         assert(data);
    1507             : 
    1508           0 :         r = route_new_static(network, filename, section_line, &n);
    1509           0 :         if (r < 0)
    1510           0 :                 return r;
    1511             : 
    1512           0 :         k = parse_boolean(rvalue);
    1513           0 :         if (k < 0) {
    1514           0 :                 log_syntax(unit, LOG_ERR, filename, line, k,
    1515             :                            "Failed to parse TCP fastopen no cookie, ignoring: %s", rvalue);
    1516           0 :                 return 0;
    1517             :         }
    1518             : 
    1519           0 :         n->fast_open_no_cookie = k;
    1520           0 :         TAKE_PTR(n);
    1521           0 :         return 0;
    1522             : }
    1523             : 
    1524           0 : int config_parse_route_mtu(
    1525             :                 const char *unit,
    1526             :                 const char *filename,
    1527             :                 unsigned line,
    1528             :                 const char *section,
    1529             :                 unsigned section_line,
    1530             :                 const char *lvalue,
    1531             :                 int ltype,
    1532             :                 const char *rvalue,
    1533             :                 void *data,
    1534             :                 void *userdata) {
    1535             : 
    1536           0 :         Network *network = userdata;
    1537           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1538             :         int r;
    1539             : 
    1540           0 :         assert(filename);
    1541           0 :         assert(section);
    1542           0 :         assert(lvalue);
    1543           0 :         assert(rvalue);
    1544           0 :         assert(data);
    1545             : 
    1546           0 :         r = route_new_static(network, filename, section_line, &n);
    1547           0 :         if (r < 0)
    1548           0 :                 return r;
    1549             : 
    1550           0 :         r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
    1551           0 :         if (r < 0)
    1552           0 :                 return r;
    1553             : 
    1554           0 :         TAKE_PTR(n);
    1555           0 :         return 0;
    1556             : }
    1557             : 
    1558           0 : int config_parse_route_ttl_propagate(
    1559             :                 const char *unit,
    1560             :                 const char *filename,
    1561             :                 unsigned line,
    1562             :                 const char *section,
    1563             :                 unsigned section_line,
    1564             :                 const char *lvalue,
    1565             :                 int ltype,
    1566             :                 const char *rvalue,
    1567             :                 void *data,
    1568             :                 void *userdata) {
    1569             : 
    1570           0 :         Network *network = userdata;
    1571           0 :         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
    1572             :         int r, k;
    1573             : 
    1574           0 :         assert(filename);
    1575           0 :         assert(section);
    1576           0 :         assert(lvalue);
    1577           0 :         assert(rvalue);
    1578           0 :         assert(data);
    1579             : 
    1580           0 :         r = route_new_static(network, filename, section_line, &n);
    1581           0 :         if (r < 0)
    1582           0 :                 return r;
    1583             : 
    1584           0 :         k = parse_boolean(rvalue);
    1585           0 :         if (k < 0) {
    1586           0 :                 log_syntax(unit, LOG_ERR, filename, line, k,
    1587             :                            "Failed to parse TTLPropagate= value, ignoring: %s", rvalue);
    1588           0 :                 return 0;
    1589             :         }
    1590             : 
    1591           0 :         n->ttl_propagate = k;
    1592             : 
    1593           0 :         TAKE_PTR(n);
    1594           0 :         return 0;
    1595             : }
    1596             : 
    1597           0 : int route_section_verify(Route *route, Network *network) {
    1598           0 :         if (section_is_invalid(route->section))
    1599           0 :                 return -EINVAL;
    1600             : 
    1601           0 :         if (route->family == AF_UNSPEC) {
    1602           0 :                 assert(route->section);
    1603             : 
    1604           0 :                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
    1605             :                                          "%s: Route section without Gateway=, Destination=, Source=, "
    1606             :                                          "or PreferredSource= field configured. "
    1607             :                                          "Ignoring [Route] section from line %u.",
    1608             :                                          route->section->filename, route->section->line);
    1609             :         }
    1610             : 
    1611           0 :         if (route->family != AF_INET6) {
    1612           0 :                 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
    1613           0 :                         route->table = RT_TABLE_LOCAL;
    1614             : 
    1615           0 :                 if (!route->scope_set) {
    1616           0 :                         if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
    1617           0 :                                 route->scope = RT_SCOPE_HOST;
    1618           0 :                         else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST))
    1619           0 :                                 route->scope = RT_SCOPE_LINK;
    1620             :                 }
    1621             :         }
    1622             : 
    1623           0 :         if (network->n_static_addresses == 0 &&
    1624           0 :             in_addr_is_null(route->family, &route->gw) == 0 &&
    1625           0 :             route->gateway_onlink < 0) {
    1626           0 :                 log_warning("%s: Gateway= without static address configured. "
    1627             :                             "Enabling GatewayOnLink= option.",
    1628             :                             network->filename);
    1629           0 :                 route->gateway_onlink = true;
    1630             :         }
    1631             : 
    1632           0 :         return 0;
    1633             : }

Generated by: LCOV version 1.14