]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
networkd: add support to configure preferred source of static routes
[thirdparty/systemd.git] / src / network / networkd-route.c
CommitLineData
f579559b
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
f579559b
TG
22#include "util.h"
23#include "conf-parser.h"
fc2f9534
LP
24#include "netlink-util.h"
25
26#include "networkd.h"
27#include "networkd-route.h"
f579559b 28
f048a16b 29int route_new_static(Network *network, unsigned section, Route **ret) {
f579559b
TG
30 _cleanup_route_free_ Route *route = NULL;
31
6ae115c1 32 if (section) {
16aa63a0
TG
33 route = hashmap_get(network->routes_by_section,
34 UINT_TO_PTR(section));
6ae115c1
TG
35 if (route) {
36 *ret = route;
37 route = NULL;
38
39 return 0;
40 }
41 }
42
f579559b
TG
43 route = new0(Route, 1);
44 if (!route)
45 return -ENOMEM;
46
801bd9e8 47 route->family = AF_UNSPEC;
5c1d3fc9 48 route->scope = RT_SCOPE_UNIVERSE;
28cc555d 49 route->protocol = RTPROT_STATIC;
801bd9e8 50
f579559b
TG
51 route->network = network;
52
3d3d4255 53 LIST_PREPEND(routes, network->static_routes, route);
f579559b 54
6ae115c1
TG
55 if (section) {
56 route->section = section;
16aa63a0
TG
57 hashmap_put(network->routes_by_section,
58 UINT_TO_PTR(route->section), route);
6ae115c1
TG
59 }
60
f579559b
TG
61 *ret = route;
62 route = NULL;
63
64 return 0;
65}
66
28cc555d 67int route_new_dynamic(Route **ret, unsigned char rtm_protocol) {
f048a16b
TG
68 _cleanup_route_free_ Route *route = NULL;
69
70 route = new0(Route, 1);
71 if (!route)
72 return -ENOMEM;
73
801bd9e8 74 route->family = AF_UNSPEC;
5c1d3fc9 75 route->scope = RT_SCOPE_UNIVERSE;
28cc555d 76 route->protocol = rtm_protocol;
801bd9e8 77
f048a16b
TG
78 *ret = route;
79 route = NULL;
80
81 return 0;
82}
83
f579559b
TG
84void route_free(Route *route) {
85 if (!route)
86 return;
87
f048a16b 88 if (route->network) {
3d3d4255 89 LIST_REMOVE(routes, route->network->static_routes, route);
f579559b 90
f048a16b
TG
91 if (route->section)
92 hashmap_remove(route->network->routes_by_section,
16aa63a0 93 UINT_TO_PTR(route->section));
f048a16b 94 }
6ae115c1 95
f579559b
TG
96 free(route);
97}
98
5c1d3fc9 99int route_drop(Route *route, Link *link,
1c4baffc
TG
100 sd_netlink_message_handler_t callback) {
101 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
5c1d3fc9
UTL
102 int r;
103
104 assert(link);
105 assert(link->manager);
106 assert(link->manager->rtnl);
107 assert(link->ifindex > 0);
108 assert(route->family == AF_INET || route->family == AF_INET6);
109
110 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
111 RTM_DELROUTE, route->family,
112 route->protocol);
f647962d
MS
113 if (r < 0)
114 return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 115
59580681
GL
116 if (!in_addr_is_null(route->family, &route->in_addr)) {
117 if (route->family == AF_INET)
1c4baffc 118 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
59580681 119 else if (route->family == AF_INET6)
1c4baffc 120 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
f647962d
MS
121 if (r < 0)
122 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
5c1d3fc9
UTL
123 }
124
125 if (route->dst_prefixlen) {
126 if (route->family == AF_INET)
1c4baffc 127 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
5c1d3fc9 128 else if (route->family == AF_INET6)
1c4baffc 129 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
f647962d
MS
130 if (r < 0)
131 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
5c1d3fc9
UTL
132
133 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
134 if (r < 0)
135 return log_error_errno(r, "Could not set destination prefix length: %m");
5c1d3fc9
UTL
136 }
137
9e7e4408
TG
138 if (route->src_prefixlen) {
139 if (route->family == AF_INET)
1c4baffc 140 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
9e7e4408 141 else if (route->family == AF_INET6)
1c4baffc 142 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
9e7e4408
TG
143 if (r < 0)
144 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
145
146 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
147 if (r < 0)
148 return log_error_errno(r, "Could not set source prefix length: %m");
149 }
150
46b0c76e
ERB
151 if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
152 if (route->family == AF_INET)
1c4baffc 153 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
46b0c76e 154 else if (route->family == AF_INET6)
1c4baffc 155 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
f647962d
MS
156 if (r < 0)
157 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
158 }
159
5c1d3fc9 160 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
161 if (r < 0)
162 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 163
1c4baffc 164 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
f647962d
MS
165 if (r < 0)
166 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 167
1c4baffc 168 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
f647962d
MS
169 if (r < 0)
170 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
5c1d3fc9 171
1c4baffc 172 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
173 if (r < 0)
174 return log_error_errno(r, "Could not send rtnetlink message: %m");
5c1d3fc9 175
563c69c6
TG
176 link_ref(link);
177
5c1d3fc9
UTL
178 return 0;
179}
180
f882c247 181int route_configure(Route *route, Link *link,
1c4baffc
TG
182 sd_netlink_message_handler_t callback) {
183 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
f579559b
TG
184 int r;
185
f579559b 186 assert(link);
f882c247
TG
187 assert(link->manager);
188 assert(link->manager->rtnl);
f579559b
TG
189 assert(link->ifindex > 0);
190 assert(route->family == AF_INET || route->family == AF_INET6);
191
151b9b96 192 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
193 RTM_NEWROUTE, route->family,
194 route->protocol);
f647962d
MS
195 if (r < 0)
196 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
f579559b 197
59580681
GL
198 if (!in_addr_is_null(route->family, &route->in_addr)) {
199 if (route->family == AF_INET)
1c4baffc 200 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
59580681 201 else if (route->family == AF_INET6)
1c4baffc 202 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
f647962d
MS
203 if (r < 0)
204 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
f579559b
TG
205 }
206
0a0dc69b
TG
207 if (route->dst_prefixlen) {
208 if (route->family == AF_INET)
1c4baffc 209 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
0a0dc69b 210 else if (route->family == AF_INET6)
1c4baffc 211 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
f647962d
MS
212 if (r < 0)
213 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
6ae115c1 214
ae4c67a7 215 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
216 if (r < 0)
217 return log_error_errno(r, "Could not set destination prefix length: %m");
1f01fb4f
TG
218 }
219
9e7e4408
TG
220 if (route->src_prefixlen) {
221 if (route->family == AF_INET)
1c4baffc 222 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
9e7e4408 223 else if (route->family == AF_INET6)
1c4baffc 224 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
9e7e4408
TG
225 if (r < 0)
226 return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
227
228 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
229 if (r < 0)
230 return log_error_errno(r, "Could not set source prefix length: %m");
231 }
232
46b0c76e
ERB
233 if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
234 if (route->family == AF_INET)
1c4baffc 235 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
46b0c76e 236 else if (route->family == AF_INET6)
1c4baffc 237 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
f647962d
MS
238 if (r < 0)
239 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
240 }
241
5c1d3fc9 242 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
243 if (r < 0)
244 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 245
1c4baffc 246 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
f647962d
MS
247 if (r < 0)
248 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 249
1c4baffc 250 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
f647962d
MS
251 if (r < 0)
252 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
f579559b 253
1c4baffc 254 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
255 if (r < 0)
256 return log_error_errno(r, "Could not send rtnetlink message: %m");
f579559b 257
563c69c6
TG
258 link_ref(link);
259
f579559b
TG
260 return 0;
261}
262
263int config_parse_gateway(const char *unit,
264 const char *filename,
265 unsigned line,
266 const char *section,
71a61510 267 unsigned section_line,
f579559b
TG
268 const char *lvalue,
269 int ltype,
270 const char *rvalue,
271 void *data,
272 void *userdata) {
44e7b949 273
6ae115c1 274 Network *network = userdata;
f579559b 275 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
276 union in_addr_union buffer;
277 int r, f;
f579559b
TG
278
279 assert(filename);
6ae115c1 280 assert(section);
f579559b
TG
281 assert(lvalue);
282 assert(rvalue);
283 assert(data);
284
92fe133a
TG
285 if (streq(section, "Network")) {
286 /* we are not in an Route section, so treat
287 * this as the special '0' section */
288 section_line = 0;
289 }
290
f048a16b 291 r = route_new_static(network, section_line, &n);
f579559b
TG
292 if (r < 0)
293 return r;
294
44e7b949 295 r = in_addr_from_string_auto(rvalue, &f, &buffer);
f579559b
TG
296 if (r < 0) {
297 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
44e7b949 298 "Route is invalid, ignoring assignment: %s", rvalue);
f579559b
TG
299 return 0;
300 }
301
44e7b949
LP
302 n->family = f;
303 n->in_addr = buffer;
f579559b
TG
304 n = NULL;
305
306 return 0;
307}
6ae115c1 308
0d07e595
JK
309int config_parse_preferred_src(const char *unit,
310 const char *filename,
311 unsigned line,
312 const char *section,
313 unsigned section_line,
314 const char *lvalue,
315 int ltype,
316 const char *rvalue,
317 void *data,
318 void *userdata) {
319
320 Network *network = userdata;
321 _cleanup_route_free_ Route *n = NULL;
322 union in_addr_union buffer;
323 int r, f;
324
325 assert(filename);
326 assert(section);
327 assert(lvalue);
328 assert(rvalue);
329 assert(data);
330
331 r = route_new_static(network, section_line, &n);
332 if (r < 0)
333 return r;
334
335 r = in_addr_from_string_auto(rvalue, &f, &buffer);
336 if (r < 0) {
337 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
338 "Preferred source is invalid, ignoring assignment: %s", rvalue);
339 return 0;
340 }
341
342 n->family = f;
343 n->prefsrc_addr = buffer;
344 n = NULL;
345
346 return 0;
347}
348
6ae115c1
TG
349int config_parse_destination(const char *unit,
350 const char *filename,
351 unsigned line,
352 const char *section,
353 unsigned section_line,
354 const char *lvalue,
355 int ltype,
356 const char *rvalue,
357 void *data,
358 void *userdata) {
44e7b949 359
6ae115c1
TG
360 Network *network = userdata;
361 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
362 const char *address, *e;
363 union in_addr_union buffer;
9e7e4408 364 unsigned char prefixlen;
44e7b949 365 int r, f;
6ae115c1
TG
366
367 assert(filename);
368 assert(section);
369 assert(lvalue);
370 assert(rvalue);
371 assert(data);
372
f048a16b 373 r = route_new_static(network, section_line, &n);
6ae115c1
TG
374 if (r < 0)
375 return r;
376
9e7e4408 377 /* Destination|Source=address/prefixlen */
6ae115c1 378
ae4c67a7 379 /* address */
6ae115c1 380 e = strchr(rvalue, '/');
44e7b949
LP
381 if (e)
382 address = strndupa(rvalue, e - rvalue);
383 else
384 address = rvalue;
6ae115c1 385
44e7b949 386 r = in_addr_from_string_auto(address, &f, &buffer);
6ae115c1
TG
387 if (r < 0) {
388 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
389 "Destination is invalid, ignoring assignment: %s", address);
390 return 0;
391 }
392
935c0d26
TG
393 if (f != AF_INET && f != AF_INET6) {
394 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
395 "Unknown address family, ignoring assignment: %s", address);
396 return 0;
397 }
398
ae4c67a7
TG
399 /* prefixlen */
400 if (e) {
9e7e4408 401 r = safe_atou8(e + 1, &prefixlen);
ae4c67a7
TG
402 if (r < 0) {
403 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
44e7b949 404 "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
ae4c67a7
TG
405 return 0;
406 }
ae4c67a7 407 } else {
935c0d26 408 switch (f) {
ae4c67a7 409 case AF_INET:
9e7e4408 410 prefixlen = 32;
ae4c67a7
TG
411 break;
412 case AF_INET6:
9e7e4408 413 prefixlen = 128;
ae4c67a7
TG
414 break;
415 }
416 }
417
44e7b949 418 n->family = f;
9e7e4408
TG
419 if (streq(lvalue, "Destination")) {
420 n->dst_addr = buffer;
421 n->dst_prefixlen = prefixlen;
422 } else if (streq(lvalue, "Source")) {
423 n->src_addr = buffer;
424 n->src_prefixlen = prefixlen;
425 } else
426 assert_not_reached(lvalue);
427
6ae115c1
TG
428 n = NULL;
429
430 return 0;
431}
5d8e593d
SS
432
433int config_parse_route_priority(const char *unit,
434 const char *filename,
435 unsigned line,
436 const char *section,
437 unsigned section_line,
438 const char *lvalue,
439 int ltype,
440 const char *rvalue,
441 void *data,
442 void *userdata) {
443 Network *network = userdata;
444 _cleanup_route_free_ Route *n = NULL;
5d8e593d
SS
445 int r;
446
447 assert(filename);
448 assert(section);
449 assert(lvalue);
450 assert(rvalue);
451 assert(data);
452
453 r = route_new_static(network, section_line, &n);
454 if (r < 0)
455 return r;
456
457 r = config_parse_unsigned(unit, filename, line, section,
458 section_line, lvalue, ltype,
459 rvalue, &n->metrics, userdata);
460 if (r < 0)
461 return r;
462
463 n = NULL;
464
465 return 0;
466}
769b56a3
TG
467
468int config_parse_route_scope(const char *unit,
469 const char *filename,
470 unsigned line,
471 const char *section,
472 unsigned section_line,
473 const char *lvalue,
474 int ltype,
475 const char *rvalue,
476 void *data,
477 void *userdata) {
478 Network *network = userdata;
479 _cleanup_route_free_ Route *n = NULL;
480 int r;
481
482 assert(filename);
483 assert(section);
484 assert(lvalue);
485 assert(rvalue);
486 assert(data);
487
488 r = route_new_static(network, section_line, &n);
489 if (r < 0)
490 return r;
491
492 if (streq(rvalue, "host"))
493 n->scope = RT_SCOPE_HOST;
494 else if (streq(rvalue, "link"))
495 n->scope = RT_SCOPE_LINK;
496 else if (streq(rvalue, "global"))
497 n->scope = RT_SCOPE_UNIVERSE;
498 else {
499 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
500 "Unknown route scope: %s", rvalue);
501 return 0;
502 }
503
504 n = NULL;
505
506 return 0;
507}