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