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