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