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