]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
Merge pull request #1693 from ssahani/word
[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 22#include "conf-parser.h"
bb7ae737 23#include "in-addr-util.h"
fc2f9534 24#include "netlink-util.h"
fc2f9534 25#include "networkd.h"
07630cea
LP
26#include "string-util.h"
27#include "util.h"
fc2f9534 28#include "networkd-route.h"
f579559b 29
ed9e361a 30int route_new(Route **ret) {
f0213e37
TG
31 _cleanup_route_free_ Route *route = NULL;
32
33 route = new0(Route, 1);
34 if (!route)
35 return -ENOMEM;
36
37 route->family = AF_UNSPEC;
38 route->scope = RT_SCOPE_UNIVERSE;
ed9e361a 39 route->protocol = RTPROT_UNSPEC;
bb7ae737 40 route->table = RT_TABLE_DEFAULT;
f0213e37
TG
41
42 *ret = route;
43 route = NULL;
44
45 return 0;
46}
47
f048a16b 48int route_new_static(Network *network, unsigned section, Route **ret) {
f579559b 49 _cleanup_route_free_ Route *route = NULL;
f0213e37 50 int r;
f579559b 51
6ae115c1 52 if (section) {
16aa63a0
TG
53 route = hashmap_get(network->routes_by_section,
54 UINT_TO_PTR(section));
6ae115c1
TG
55 if (route) {
56 *ret = route;
57 route = NULL;
58
59 return 0;
60 }
61 }
62
ed9e361a 63 r = route_new(&route);
f0213e37
TG
64 if (r < 0)
65 return r;
801bd9e8 66
ed9e361a 67 route->protocol = RTPROT_STATIC;
f579559b
TG
68 route->network = network;
69
3d3d4255 70 LIST_PREPEND(routes, network->static_routes, route);
f579559b 71
6ae115c1
TG
72 if (section) {
73 route->section = section;
16aa63a0
TG
74 hashmap_put(network->routes_by_section,
75 UINT_TO_PTR(route->section), route);
6ae115c1
TG
76 }
77
f579559b
TG
78 *ret = route;
79 route = NULL;
80
81 return 0;
82}
83
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
bb7ae737
TG
99static void route_hash_func(const void *b, struct siphash *state) {
100 const Route *route = b;
101
102 assert(route);
103
104 siphash24_compress(&route->family, sizeof(route->family), state);
105
106 switch (route->family) {
107 case AF_INET:
108 case AF_INET6:
109 /* Equality of routes are given by the 4-touple
110 (dst_prefix,dst_prefixlen,tos,priority,table) */
111 siphash24_compress(&route->dst_addr, FAMILY_ADDRESS_SIZE(route->family), state);
112 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
113 siphash24_compress(&route->tos, sizeof(route->tos), state);
114 siphash24_compress(&route->priority, sizeof(route->priority), state);
115 siphash24_compress(&route->table, sizeof(route->table), state);
116
117 break;
118 default:
119 /* treat any other address family as AF_UNSPEC */
120 break;
121 }
122}
123
124static int route_compare_func(const void *_a, const void *_b) {
125 const Route *a = _a, *b = _b;
126
127 if (a->family < b->family)
128 return -1;
129 if (a->family > b->family)
130 return 1;
131
132 switch (a->family) {
133 case AF_INET:
134 case AF_INET6:
135 //TODO: check IPv6 routes
136 if (a->dst_prefixlen < b->dst_prefixlen)
137 return -1;
138 if (a->dst_prefixlen > b->dst_prefixlen)
139 return 1;
140
141 if (a->tos < b->tos)
142 return -1;
143 if (a->tos > b->tos)
144 return 1;
145
146 if (a->priority < b->priority)
147 return -1;
148 if (a->priority > b->priority)
149 return 1;
150
151 if (a->table < b->table)
152 return -1;
153 if (a->table > b->table)
154 return 1;
155
156 return memcmp(&a->dst_addr, &b->dst_addr, FAMILY_ADDRESS_SIZE(a->family));
157 default:
158 /* treat any other address family as AF_UNSPEC */
159 return 0;
160 }
161}
162
163static const struct hash_ops route_hash_ops = {
164 .hash = route_hash_func,
165 .compare = route_compare_func
166};
167
91b5f997 168int route_remove(Route *route, Link *link,
1c4baffc
TG
169 sd_netlink_message_handler_t callback) {
170 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
5c1d3fc9
UTL
171 int r;
172
173 assert(link);
174 assert(link->manager);
175 assert(link->manager->rtnl);
176 assert(link->ifindex > 0);
177 assert(route->family == AF_INET || route->family == AF_INET6);
178
179 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
180 RTM_DELROUTE, route->family,
181 route->protocol);
f647962d
MS
182 if (r < 0)
183 return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 184
59580681
GL
185 if (!in_addr_is_null(route->family, &route->in_addr)) {
186 if (route->family == AF_INET)
1c4baffc 187 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
59580681 188 else if (route->family == AF_INET6)
1c4baffc 189 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
f647962d
MS
190 if (r < 0)
191 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
5c1d3fc9
UTL
192 }
193
194 if (route->dst_prefixlen) {
195 if (route->family == AF_INET)
1c4baffc 196 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
5c1d3fc9 197 else if (route->family == AF_INET6)
1c4baffc 198 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
f647962d
MS
199 if (r < 0)
200 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
5c1d3fc9
UTL
201
202 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
203 if (r < 0)
204 return log_error_errno(r, "Could not set destination prefix length: %m");
5c1d3fc9
UTL
205 }
206
9e7e4408
TG
207 if (route->src_prefixlen) {
208 if (route->family == AF_INET)
1c4baffc 209 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
9e7e4408 210 else if (route->family == AF_INET6)
1c4baffc 211 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
9e7e4408
TG
212 if (r < 0)
213 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
214
215 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
216 if (r < 0)
217 return log_error_errno(r, "Could not set source prefix length: %m");
218 }
219
46b0c76e
ERB
220 if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
221 if (route->family == AF_INET)
1c4baffc 222 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
46b0c76e 223 else if (route->family == AF_INET6)
1c4baffc 224 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
f647962d
MS
225 if (r < 0)
226 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
227 }
228
5c1d3fc9 229 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
230 if (r < 0)
231 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 232
1c4baffc 233 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
f647962d
MS
234 if (r < 0)
235 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 236
1c4baffc 237 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
f647962d
MS
238 if (r < 0)
239 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
5c1d3fc9 240
1c4baffc 241 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
242 if (r < 0)
243 return log_error_errno(r, "Could not send rtnetlink message: %m");
5c1d3fc9 244
563c69c6
TG
245 link_ref(link);
246
5c1d3fc9
UTL
247 return 0;
248}
249
f882c247 250int route_configure(Route *route, Link *link,
1c4baffc
TG
251 sd_netlink_message_handler_t callback) {
252 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
f579559b
TG
253 int r;
254
f579559b 255 assert(link);
f882c247
TG
256 assert(link->manager);
257 assert(link->manager->rtnl);
f579559b
TG
258 assert(link->ifindex > 0);
259 assert(route->family == AF_INET || route->family == AF_INET6);
260
151b9b96 261 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
262 RTM_NEWROUTE, route->family,
263 route->protocol);
f647962d
MS
264 if (r < 0)
265 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
f579559b 266
59580681
GL
267 if (!in_addr_is_null(route->family, &route->in_addr)) {
268 if (route->family == AF_INET)
1c4baffc 269 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
59580681 270 else if (route->family == AF_INET6)
1c4baffc 271 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
f647962d
MS
272 if (r < 0)
273 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
f579559b
TG
274 }
275
0a0dc69b
TG
276 if (route->dst_prefixlen) {
277 if (route->family == AF_INET)
1c4baffc 278 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
0a0dc69b 279 else if (route->family == AF_INET6)
1c4baffc 280 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
f647962d
MS
281 if (r < 0)
282 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
6ae115c1 283
ae4c67a7 284 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
285 if (r < 0)
286 return log_error_errno(r, "Could not set destination prefix length: %m");
1f01fb4f
TG
287 }
288
9e7e4408
TG
289 if (route->src_prefixlen) {
290 if (route->family == AF_INET)
1c4baffc 291 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
9e7e4408 292 else if (route->family == AF_INET6)
1c4baffc 293 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
9e7e4408
TG
294 if (r < 0)
295 return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
296
297 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
298 if (r < 0)
299 return log_error_errno(r, "Could not set source prefix length: %m");
300 }
301
46b0c76e
ERB
302 if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
303 if (route->family == AF_INET)
1c4baffc 304 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
46b0c76e 305 else if (route->family == AF_INET6)
1c4baffc 306 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
f647962d
MS
307 if (r < 0)
308 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
309 }
310
5c1d3fc9 311 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
312 if (r < 0)
313 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 314
1c4baffc 315 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
f647962d
MS
316 if (r < 0)
317 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 318
1c4baffc 319 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
f647962d
MS
320 if (r < 0)
321 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
f579559b 322
1c4baffc 323 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
324 if (r < 0)
325 return log_error_errno(r, "Could not send rtnetlink message: %m");
f579559b 326
563c69c6
TG
327 link_ref(link);
328
f579559b
TG
329 return 0;
330}
331
332int config_parse_gateway(const char *unit,
333 const char *filename,
334 unsigned line,
335 const char *section,
71a61510 336 unsigned section_line,
f579559b
TG
337 const char *lvalue,
338 int ltype,
339 const char *rvalue,
340 void *data,
341 void *userdata) {
44e7b949 342
6ae115c1 343 Network *network = userdata;
f579559b 344 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
345 union in_addr_union buffer;
346 int r, f;
f579559b
TG
347
348 assert(filename);
6ae115c1 349 assert(section);
f579559b
TG
350 assert(lvalue);
351 assert(rvalue);
352 assert(data);
353
92fe133a
TG
354 if (streq(section, "Network")) {
355 /* we are not in an Route section, so treat
356 * this as the special '0' section */
357 section_line = 0;
358 }
359
f048a16b 360 r = route_new_static(network, section_line, &n);
f579559b
TG
361 if (r < 0)
362 return r;
363
44e7b949 364 r = in_addr_from_string_auto(rvalue, &f, &buffer);
f579559b 365 if (r < 0) {
12ca818f 366 log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
f579559b
TG
367 return 0;
368 }
369
44e7b949
LP
370 n->family = f;
371 n->in_addr = buffer;
f579559b
TG
372 n = NULL;
373
374 return 0;
375}
6ae115c1 376
0d07e595
JK
377int config_parse_preferred_src(const char *unit,
378 const char *filename,
379 unsigned line,
380 const char *section,
381 unsigned section_line,
382 const char *lvalue,
383 int ltype,
384 const char *rvalue,
385 void *data,
386 void *userdata) {
387
388 Network *network = userdata;
389 _cleanup_route_free_ Route *n = NULL;
390 union in_addr_union buffer;
391 int r, f;
392
393 assert(filename);
394 assert(section);
395 assert(lvalue);
396 assert(rvalue);
397 assert(data);
398
399 r = route_new_static(network, section_line, &n);
400 if (r < 0)
401 return r;
402
403 r = in_addr_from_string_auto(rvalue, &f, &buffer);
404 if (r < 0) {
405 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
406 "Preferred source is invalid, ignoring assignment: %s", rvalue);
407 return 0;
408 }
409
410 n->family = f;
411 n->prefsrc_addr = buffer;
412 n = NULL;
413
414 return 0;
415}
416
6ae115c1
TG
417int config_parse_destination(const char *unit,
418 const char *filename,
419 unsigned line,
420 const char *section,
421 unsigned section_line,
422 const char *lvalue,
423 int ltype,
424 const char *rvalue,
425 void *data,
426 void *userdata) {
44e7b949 427
6ae115c1
TG
428 Network *network = userdata;
429 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
430 const char *address, *e;
431 union in_addr_union buffer;
9e7e4408 432 unsigned char prefixlen;
44e7b949 433 int r, f;
6ae115c1
TG
434
435 assert(filename);
436 assert(section);
437 assert(lvalue);
438 assert(rvalue);
439 assert(data);
440
f048a16b 441 r = route_new_static(network, section_line, &n);
6ae115c1
TG
442 if (r < 0)
443 return r;
444
9e7e4408 445 /* Destination|Source=address/prefixlen */
6ae115c1 446
ae4c67a7 447 /* address */
6ae115c1 448 e = strchr(rvalue, '/');
44e7b949
LP
449 if (e)
450 address = strndupa(rvalue, e - rvalue);
451 else
452 address = rvalue;
6ae115c1 453
44e7b949 454 r = in_addr_from_string_auto(address, &f, &buffer);
6ae115c1 455 if (r < 0) {
12ca818f 456 log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address);
6ae115c1
TG
457 return 0;
458 }
459
935c0d26 460 if (f != AF_INET && f != AF_INET6) {
12ca818f 461 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address);
935c0d26
TG
462 return 0;
463 }
464
ae4c67a7
TG
465 /* prefixlen */
466 if (e) {
9e7e4408 467 r = safe_atou8(e + 1, &prefixlen);
ae4c67a7 468 if (r < 0) {
12ca818f 469 log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
ae4c67a7
TG
470 return 0;
471 }
ae4c67a7 472 } else {
935c0d26 473 switch (f) {
ae4c67a7 474 case AF_INET:
9e7e4408 475 prefixlen = 32;
ae4c67a7
TG
476 break;
477 case AF_INET6:
9e7e4408 478 prefixlen = 128;
ae4c67a7
TG
479 break;
480 }
481 }
482
44e7b949 483 n->family = f;
9e7e4408
TG
484 if (streq(lvalue, "Destination")) {
485 n->dst_addr = buffer;
486 n->dst_prefixlen = prefixlen;
487 } else if (streq(lvalue, "Source")) {
488 n->src_addr = buffer;
489 n->src_prefixlen = prefixlen;
490 } else
491 assert_not_reached(lvalue);
492
6ae115c1
TG
493 n = NULL;
494
495 return 0;
496}
5d8e593d
SS
497
498int config_parse_route_priority(const char *unit,
499 const char *filename,
500 unsigned line,
501 const char *section,
502 unsigned section_line,
503 const char *lvalue,
504 int ltype,
505 const char *rvalue,
506 void *data,
507 void *userdata) {
508 Network *network = userdata;
509 _cleanup_route_free_ Route *n = NULL;
5d8e593d
SS
510 int r;
511
512 assert(filename);
513 assert(section);
514 assert(lvalue);
515 assert(rvalue);
516 assert(data);
517
518 r = route_new_static(network, section_line, &n);
519 if (r < 0)
520 return r;
521
522 r = config_parse_unsigned(unit, filename, line, section,
523 section_line, lvalue, ltype,
524 rvalue, &n->metrics, userdata);
525 if (r < 0)
526 return r;
527
528 n = NULL;
529
530 return 0;
531}
769b56a3
TG
532
533int config_parse_route_scope(const char *unit,
534 const char *filename,
535 unsigned line,
536 const char *section,
537 unsigned section_line,
538 const char *lvalue,
539 int ltype,
540 const char *rvalue,
541 void *data,
542 void *userdata) {
543 Network *network = userdata;
544 _cleanup_route_free_ Route *n = NULL;
545 int r;
546
547 assert(filename);
548 assert(section);
549 assert(lvalue);
550 assert(rvalue);
551 assert(data);
552
553 r = route_new_static(network, section_line, &n);
554 if (r < 0)
555 return r;
556
557 if (streq(rvalue, "host"))
558 n->scope = RT_SCOPE_HOST;
559 else if (streq(rvalue, "link"))
560 n->scope = RT_SCOPE_LINK;
561 else if (streq(rvalue, "global"))
562 n->scope = RT_SCOPE_UNIVERSE;
563 else {
12ca818f 564 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
769b56a3
TG
565 return 0;
566 }
567
568 n = NULL;
569
570 return 0;
571}