]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-route.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / network / networkd-route.c
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
22 #include "conf-parser.h"
23 #include "in-addr-util.h"
24 #include "netlink-util.h"
25 #include "networkd.h"
26 #include "string-util.h"
27 #include "util.h"
28 #include "networkd-route.h"
29
30 int route_new(Route **ret) {
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;
39 route->protocol = RTPROT_UNSPEC;
40 route->table = RT_TABLE_DEFAULT;
41
42 *ret = route;
43 route = NULL;
44
45 return 0;
46 }
47
48 int route_new_static(Network *network, unsigned section, Route **ret) {
49 _cleanup_route_free_ Route *route = NULL;
50 int r;
51
52 if (section) {
53 route = hashmap_get(network->routes_by_section,
54 UINT_TO_PTR(section));
55 if (route) {
56 *ret = route;
57 route = NULL;
58
59 return 0;
60 }
61 }
62
63 r = route_new(&route);
64 if (r < 0)
65 return r;
66
67 route->protocol = RTPROT_STATIC;
68 route->network = network;
69
70 LIST_PREPEND(routes, network->static_routes, route);
71
72 if (section) {
73 route->section = section;
74 hashmap_put(network->routes_by_section,
75 UINT_TO_PTR(route->section), route);
76 }
77
78 *ret = route;
79 route = NULL;
80
81 return 0;
82 }
83
84 void route_free(Route *route) {
85 if (!route)
86 return;
87
88 if (route->network) {
89 LIST_REMOVE(routes, route->network->static_routes, route);
90
91 if (route->section)
92 hashmap_remove(route->network->routes_by_section,
93 UINT_TO_PTR(route->section));
94 }
95
96 free(route);
97 }
98
99 static 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
124 static 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
163 static const struct hash_ops route_hash_ops = {
164 .hash = route_hash_func,
165 .compare = route_compare_func
166 };
167
168 int route_remove(Route *route, Link *link,
169 sd_netlink_message_handler_t callback) {
170 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
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,
180 RTM_DELROUTE, route->family,
181 route->protocol);
182 if (r < 0)
183 return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
184
185 if (!in_addr_is_null(route->family, &route->in_addr)) {
186 if (route->family == AF_INET)
187 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
188 else if (route->family == AF_INET6)
189 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
190 if (r < 0)
191 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
192 }
193
194 if (route->dst_prefixlen) {
195 if (route->family == AF_INET)
196 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
197 else if (route->family == AF_INET6)
198 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
199 if (r < 0)
200 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
201
202 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
203 if (r < 0)
204 return log_error_errno(r, "Could not set destination prefix length: %m");
205 }
206
207 if (route->src_prefixlen) {
208 if (route->family == AF_INET)
209 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
210 else if (route->family == AF_INET6)
211 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
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
220 if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
221 if (route->family == AF_INET)
222 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
223 else if (route->family == AF_INET6)
224 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
225 if (r < 0)
226 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
227 }
228
229 r = sd_rtnl_message_route_set_scope(req, route->scope);
230 if (r < 0)
231 return log_error_errno(r, "Could not set scope: %m");
232
233 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
234 if (r < 0)
235 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
236
237 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
238 if (r < 0)
239 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
240
241 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
242 if (r < 0)
243 return log_error_errno(r, "Could not send rtnetlink message: %m");
244
245 link_ref(link);
246
247 return 0;
248 }
249
250 int route_configure(Route *route, Link *link,
251 sd_netlink_message_handler_t callback) {
252 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
253 int r;
254
255 assert(link);
256 assert(link->manager);
257 assert(link->manager->rtnl);
258 assert(link->ifindex > 0);
259 assert(route->family == AF_INET || route->family == AF_INET6);
260
261 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
262 RTM_NEWROUTE, route->family,
263 route->protocol);
264 if (r < 0)
265 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
266
267 if (!in_addr_is_null(route->family, &route->in_addr)) {
268 if (route->family == AF_INET)
269 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
270 else if (route->family == AF_INET6)
271 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
272 if (r < 0)
273 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
274 }
275
276 if (route->dst_prefixlen) {
277 if (route->family == AF_INET)
278 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
279 else if (route->family == AF_INET6)
280 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
281 if (r < 0)
282 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
283
284 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
285 if (r < 0)
286 return log_error_errno(r, "Could not set destination prefix length: %m");
287 }
288
289 if (route->src_prefixlen) {
290 if (route->family == AF_INET)
291 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
292 else if (route->family == AF_INET6)
293 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
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
302 if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
303 if (route->family == AF_INET)
304 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
305 else if (route->family == AF_INET6)
306 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
307 if (r < 0)
308 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
309 }
310
311 r = sd_rtnl_message_route_set_scope(req, route->scope);
312 if (r < 0)
313 return log_error_errno(r, "Could not set scope: %m");
314
315 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
316 if (r < 0)
317 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
318
319 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
320 if (r < 0)
321 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
322
323 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
324 if (r < 0)
325 return log_error_errno(r, "Could not send rtnetlink message: %m");
326
327 link_ref(link);
328
329 return 0;
330 }
331
332 int config_parse_gateway(const char *unit,
333 const char *filename,
334 unsigned line,
335 const char *section,
336 unsigned section_line,
337 const char *lvalue,
338 int ltype,
339 const char *rvalue,
340 void *data,
341 void *userdata) {
342
343 Network *network = userdata;
344 _cleanup_route_free_ Route *n = NULL;
345 union in_addr_union buffer;
346 int r, f;
347
348 assert(filename);
349 assert(section);
350 assert(lvalue);
351 assert(rvalue);
352 assert(data);
353
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
360 r = route_new_static(network, section_line, &n);
361 if (r < 0)
362 return r;
363
364 r = in_addr_from_string_auto(rvalue, &f, &buffer);
365 if (r < 0) {
366 log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
367 return 0;
368 }
369
370 n->family = f;
371 n->in_addr = buffer;
372 n = NULL;
373
374 return 0;
375 }
376
377 int 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
417 int 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) {
427
428 Network *network = userdata;
429 _cleanup_route_free_ Route *n = NULL;
430 const char *address, *e;
431 union in_addr_union buffer;
432 unsigned char prefixlen;
433 int r, f;
434
435 assert(filename);
436 assert(section);
437 assert(lvalue);
438 assert(rvalue);
439 assert(data);
440
441 r = route_new_static(network, section_line, &n);
442 if (r < 0)
443 return r;
444
445 /* Destination|Source=address/prefixlen */
446
447 /* address */
448 e = strchr(rvalue, '/');
449 if (e)
450 address = strndupa(rvalue, e - rvalue);
451 else
452 address = rvalue;
453
454 r = in_addr_from_string_auto(address, &f, &buffer);
455 if (r < 0) {
456 log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address);
457 return 0;
458 }
459
460 if (f != AF_INET && f != AF_INET6) {
461 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address);
462 return 0;
463 }
464
465 /* prefixlen */
466 if (e) {
467 r = safe_atou8(e + 1, &prefixlen);
468 if (r < 0) {
469 log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
470 return 0;
471 }
472 } else {
473 switch (f) {
474 case AF_INET:
475 prefixlen = 32;
476 break;
477 case AF_INET6:
478 prefixlen = 128;
479 break;
480 }
481 }
482
483 n->family = f;
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
493 n = NULL;
494
495 return 0;
496 }
497
498 int 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;
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 }
532
533 int 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 {
564 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
565 return 0;
566 }
567
568 n = NULL;
569
570 return 0;
571 }