Skip to content

Commit 324e342

Browse files
committed
network: add NextHop= setting in [Route] section
1 parent 69e244e commit 324e342

File tree

7 files changed

+157
-14
lines changed

7 files changed

+157
-14
lines changed

man/systemd.network.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,13 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
15541554
times. If an empty string is assigned, then the all previous assignments are cleared.</para>
15551555
</listitem>
15561556
</varlistentry>
1557+
<varlistentry>
1558+
<term><varname>NextHop=</varname></term>
1559+
<listitem>
1560+
<para>Specifies the nexthop id. Takes an unsigned integer in the range 1…4294967295.
1561+
If set, the corresponding [NextHop] section must be configured. Defaults to unset.</para>
1562+
</listitem>
1563+
</varlistentry>
15571564
</variablelist>
15581565
</refsect1>
15591566

src/network/networkd-network-gperf.gperf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ Route.QuickAck, config_parse_route_boolean,
185185
Route.FastOpenNoCookie, config_parse_route_boolean, 0, 0
186186
Route.TTLPropagate, config_parse_route_boolean, 0, 0
187187
Route.MultiPathRoute, config_parse_multipath_route, 0, 0
188+
Route.NextHop, config_parse_route_nexthop, 0, 0
188189
NextHop.Id, config_parse_nexthop_id, 0, 0
189190
NextHop.Gateway, config_parse_nexthop_gateway, 0, 0
190191
NextHop.Family, config_parse_nexthop_family, 0, 0

src/network/networkd-nexthop.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,23 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
132132
nexthop_compare_func,
133133
nexthop_free);
134134

135+
int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
136+
NextHop *nh;
137+
138+
assert(manager);
139+
140+
if (id == 0)
141+
return -EINVAL;
142+
143+
nh = hashmap_get(manager->nexthops_by_id, UINT32_TO_PTR(id));
144+
if (!nh)
145+
return -ENOENT;
146+
147+
if (ret)
148+
*ret = nh;
149+
return 0;
150+
}
151+
135152
static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) {
136153
NextHop *existing;
137154

src/network/networkd-nexthop.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ void network_drop_invalid_nexthops(Network *network);
3535

3636
int link_set_nexthops(Link *link);
3737

38+
int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret);
3839
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
3940

4041
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);

src/network/networkd-route.c

Lines changed: 128 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ void route_hash_func(const Route *route, struct siphash *state) {
328328
siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
329329

330330
siphash24_compress(&route->advmss, sizeof(route->advmss), state);
331+
siphash24_compress(&route->nexthop_id, sizeof(route->nexthop_id), state);
331332

332333
break;
333334
default:
@@ -416,6 +417,10 @@ int route_compare_func(const Route *a, const Route *b) {
416417
if (r != 0)
417418
return r;
418419

420+
r = CMP(a->nexthop_id, b->nexthop_id);
421+
if (r != 0)
422+
return r;
423+
419424
return 0;
420425
default:
421426
/* treat any other address family as AF_UNSPEC */
@@ -479,7 +484,7 @@ static int route_get(const Manager *manager, const Link *link, const Route *in,
479484
return -ENOENT;
480485
}
481486

482-
static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) {
487+
static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh) {
483488
assert(dest);
484489
assert(src);
485490

@@ -498,9 +503,14 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) {
498503
dest->initcwnd = src->initcwnd;
499504
dest->initrwnd = src->initrwnd;
500505
dest->lifetime = src->lifetime;
501-
dest->advmss= src->advmss;
506+
dest->advmss = src->advmss;
507+
dest->nexthop_id = src->nexthop_id;
502508

503-
if (m) {
509+
if (nh) {
510+
dest->gw_family = nh->family;
511+
dest->gw = nh->gw;
512+
dest->gw_weight = src->gw_weight;
513+
} else if (m) {
504514
dest->gw_family = m->gateway.family;
505515
dest->gw = m->gateway.address;
506516
dest->gw_weight = m->weight;
@@ -523,7 +533,7 @@ static int route_add_internal(Manager *manager, Link *link, Set **routes, const
523533
if (r < 0)
524534
return r;
525535

526-
route_copy(route, in, NULL);
536+
route_copy(route, in, NULL, NULL);
527537

528538
r = set_ensure_put(routes, &route_hash_ops, route);
529539
if (r < 0)
@@ -547,7 +557,7 @@ static int route_add_foreign(Manager *manager, Link *link, const Route *in, Rout
547557
return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret);
548558
}
549559

550-
static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, Route **ret) {
560+
static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, const NextHop *nh, Route **ret) {
551561
_cleanup_(route_freep) Route *tmp = NULL;
552562
bool is_new = false;
553563
Route *route;
@@ -556,14 +566,21 @@ static int route_add(Manager *manager, Link *link, const Route *in, const Multip
556566
assert(manager || link);
557567
assert(in);
558568

559-
if (m) {
569+
if (nh) {
570+
r = route_new(&tmp);
571+
if (r < 0)
572+
return r;
573+
574+
route_copy(tmp, in, NULL, nh);
575+
in = tmp;
576+
} else if (m) {
560577
assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex));
561578

562579
r = route_new(&tmp);
563580
if (r < 0)
564581
return r;
565582

566-
route_copy(tmp, in, m);
583+
route_copy(tmp, in, m, NULL);
567584
in = tmp;
568585
}
569586

@@ -722,14 +739,20 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req
722739
if (r < 0)
723740
return log_link_error_errno(link, r, "Could not set route type: %m");
724741

725-
if (!route_type_is_reject(route)) {
742+
if (!route_type_is_reject(route) && route->nexthop_id == 0) {
726743
assert(link); /* Those routes must be attached to a specific link */
727744

728745
r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
729746
if (r < 0)
730747
return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
731748
}
732749

750+
if (route->nexthop_id > 0) {
751+
r = sd_netlink_message_append_u32(req, RTA_NH_ID, route->nexthop_id);
752+
if (r < 0)
753+
return log_link_error_errno(link, r, "Could not append RTA_NH_ID attribute: %m");
754+
}
755+
733756
r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
734757
if (r < 0)
735758
return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
@@ -893,7 +916,7 @@ int link_drop_foreign_routes(Link *link) {
893916
continue;
894917

895918
if (link_has_route(link, route))
896-
k = route_add(NULL, link, route, NULL, NULL);
919+
k = route_add(NULL, link, route, NULL, NULL, NULL);
897920
else
898921
k = route_remove(route, NULL, link, NULL);
899922
if (k < 0 && r >= 0)
@@ -947,24 +970,28 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
947970

948971
static int route_add_and_setup_timer(Link *link, const Route *route, const MultipathRoute *m, Route **ret) {
949972
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
973+
NextHop *nh = NULL;
950974
Route *nr;
951975
int r, k;
952976

953977
assert(link);
978+
assert(link->manager);
954979
assert(route);
955980

981+
(void) manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh);
982+
956983
if (route_type_is_reject(route))
957-
k = route_add(link->manager, NULL, route, NULL, &nr);
984+
k = route_add(link->manager, NULL, route, NULL, NULL, &nr);
958985
else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex)
959-
k = route_add(NULL, link, route, m, &nr);
986+
k = route_add(NULL, link, route, m, nh, &nr);
960987
else {
961988
Link *link_gw;
962989

963990
r = link_get(link->manager, m->ifindex, &link_gw);
964991
if (r < 0)
965992
return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
966993

967-
k = route_add(NULL, link_gw, route, m, &nr);
994+
k = route_add(NULL, link_gw, route, m, NULL, &nr);
968995
}
969996
if (k < 0)
970997
return log_link_error_errno(link, k, "Could not add route: %m");
@@ -1258,6 +1285,9 @@ static bool route_has_gateway(const Route *route) {
12581285
if (!ordered_set_isempty(route->multipath_routes))
12591286
return true;
12601287

1288+
if (route->nexthop_id > 0)
1289+
return true;
1290+
12611291
return false;
12621292
}
12631293

@@ -1356,13 +1386,30 @@ int link_set_routes(Link *link) {
13561386
static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
13571387
_cleanup_(route_freep) Route *nr = NULL;
13581388
Route *route = NULL;
1389+
NextHop *nh = NULL;
13591390
int r;
13601391

13611392
assert(manager);
13621393
assert(tmp);
13631394
assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
13641395

1365-
if (m) {
1396+
(void) manager_get_nexthop_by_id(manager, tmp->nexthop_id, &nh);
1397+
1398+
if (nh) {
1399+
if (link && link != nh->link)
1400+
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
1401+
"rtnl: received RTA_OIF and ifindex of nexthop corresponding to RTA_NH_ID do not match, ignoring.");
1402+
1403+
link = nh->link;
1404+
1405+
r = route_new(&nr);
1406+
if (r < 0)
1407+
return log_oom();
1408+
1409+
route_copy(nr, tmp, NULL, nh);
1410+
1411+
tmp = nr;
1412+
} else if (m) {
13661413
if (link)
13671414
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
13681415
"rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
@@ -1381,7 +1428,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const
13811428
if (r < 0)
13821429
return log_oom();
13831430

1384-
route_copy(nr, tmp, m);
1431+
route_copy(nr, tmp, m, NULL);
13851432

13861433
tmp = nr;
13871434
}
@@ -1573,6 +1620,12 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
15731620
return 0;
15741621
}
15751622

1623+
r = sd_netlink_message_read_u32(message, RTA_NH_ID, &tmp->nexthop_id);
1624+
if (r < 0 && r != -ENODATA) {
1625+
log_link_warning_errno(link, r, "rtnl: received route message with invalid nexthop id, ignoring: %m");
1626+
return 0;
1627+
}
1628+
15761629
r = sd_netlink_message_enter_container(message, RTA_METRICS);
15771630
if (r < 0 && r != -ENODATA) {
15781631
log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m");
@@ -1966,6 +2019,59 @@ int config_parse_route_scope(
19662019
return 0;
19672020
}
19682021

2022+
int config_parse_route_nexthop(
2023+
const char *unit,
2024+
const char *filename,
2025+
unsigned line,
2026+
const char *section,
2027+
unsigned section_line,
2028+
const char *lvalue,
2029+
int ltype,
2030+
const char *rvalue,
2031+
void *data,
2032+
void *userdata) {
2033+
2034+
Network *network = userdata;
2035+
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2036+
uint32_t id;
2037+
int r;
2038+
2039+
assert(filename);
2040+
assert(section);
2041+
assert(lvalue);
2042+
assert(rvalue);
2043+
assert(data);
2044+
2045+
r = route_new_static(network, filename, section_line, &n);
2046+
if (r == -ENOMEM)
2047+
return log_oom();
2048+
if (r < 0) {
2049+
log_syntax(unit, LOG_WARNING, filename, line, r,
2050+
"Failed to allocate route, ignoring assignment: %m");
2051+
return 0;
2052+
}
2053+
2054+
if (isempty(rvalue)) {
2055+
n->nexthop_id = 0;
2056+
TAKE_PTR(n);
2057+
return 0;
2058+
}
2059+
2060+
r = safe_atou32(rvalue, &id);
2061+
if (r < 0) {
2062+
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nexthop ID, ignoring assignment: %s", rvalue);
2063+
return 0;
2064+
}
2065+
if (id == 0) {
2066+
log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid nexthop ID, ignoring assignment: %s", rvalue);
2067+
return 0;
2068+
}
2069+
2070+
n->nexthop_id = id;
2071+
TAKE_PTR(n);
2072+
return 0;
2073+
}
2074+
19692075
int config_parse_route_table(
19702076
const char *unit,
19712077
const char *filename,
@@ -2656,6 +2762,14 @@ static int route_section_verify(Route *route, Network *network) {
26562762
route->section->filename, route->section->line);
26572763
}
26582764

2765+
if (route->nexthop_id > 0 &&
2766+
(in_addr_is_set(route->gw_family, &route->gw) ||
2767+
!ordered_set_isempty(route->multipath_routes)))
2768+
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2769+
"%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
2770+
"Ignoring [Route] section from line %u.",
2771+
route->section->filename, route->section->line);
2772+
26592773
return 0;
26602774
}
26612775

src/network/networkd-route.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ typedef struct Route {
4444
unsigned char pref;
4545
unsigned flags;
4646
int gateway_onlink;
47+
uint32_t nexthop_id;
4748

4849
bool scope_set:1;
4950
bool table_set:1;
@@ -105,3 +106,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
105106
CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
106107
CONFIG_PARSER_PROTOTYPE(config_parse_tcp_advmss);
107108
CONFIG_PARSER_PROTOTYPE(config_parse_route_table_names);
109+
CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop);

test/fuzz/fuzz-network-parser/directives.network

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ Metric=
166166
TTLPropagate=
167167
MultiPathRoute=
168168
TCPAdvertisedMaximumSegmentSize=
169+
NextHop=
169170
[Network]
170171
IPv6DuplicateAddressDetection=
171172
IPMasquerade=

0 commit comments

Comments
 (0)