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