]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-route.c
networkd: address - rework firewall rules lifetime
[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 "util.h"
23 #include "conf-parser.h"
24 #include "netlink-util.h"
25
26 #include "networkd.h"
27 #include "networkd-route.h"
28
29 int 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
46 int route_new_static(Network *network, unsigned section, Route **ret) {
47 _cleanup_route_free_ Route *route = NULL;
48 int r;
49
50 if (section) {
51 route = hashmap_get(network->routes_by_section,
52 UINT_TO_PTR(section));
53 if (route) {
54 *ret = route;
55 route = NULL;
56
57 return 0;
58 }
59 }
60
61 r = route_new(&route, RTPROT_STATIC);
62 if (r < 0)
63 return r;
64
65 route->network = network;
66
67 LIST_PREPEND(routes, network->static_routes, route);
68
69 if (section) {
70 route->section = section;
71 hashmap_put(network->routes_by_section,
72 UINT_TO_PTR(route->section), route);
73 }
74
75 *ret = route;
76 route = NULL;
77
78 return 0;
79 }
80
81 void route_free(Route *route) {
82 if (!route)
83 return;
84
85 if (route->network) {
86 LIST_REMOVE(routes, route->network->static_routes, route);
87
88 if (route->section)
89 hashmap_remove(route->network->routes_by_section,
90 UINT_TO_PTR(route->section));
91 }
92
93 free(route);
94 }
95
96 int route_remove(Route *route, Link *link,
97 sd_netlink_message_handler_t callback) {
98 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
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,
108 RTM_DELROUTE, route->family,
109 route->protocol);
110 if (r < 0)
111 return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
112
113 if (!in_addr_is_null(route->family, &route->in_addr)) {
114 if (route->family == AF_INET)
115 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
116 else if (route->family == AF_INET6)
117 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
118 if (r < 0)
119 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
120 }
121
122 if (route->dst_prefixlen) {
123 if (route->family == AF_INET)
124 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
125 else if (route->family == AF_INET6)
126 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
127 if (r < 0)
128 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
129
130 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
131 if (r < 0)
132 return log_error_errno(r, "Could not set destination prefix length: %m");
133 }
134
135 if (route->src_prefixlen) {
136 if (route->family == AF_INET)
137 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
138 else if (route->family == AF_INET6)
139 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
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
148 if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
149 if (route->family == AF_INET)
150 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
151 else if (route->family == AF_INET6)
152 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
153 if (r < 0)
154 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
155 }
156
157 r = sd_rtnl_message_route_set_scope(req, route->scope);
158 if (r < 0)
159 return log_error_errno(r, "Could not set scope: %m");
160
161 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
162 if (r < 0)
163 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
164
165 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
166 if (r < 0)
167 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
168
169 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
170 if (r < 0)
171 return log_error_errno(r, "Could not send rtnetlink message: %m");
172
173 link_ref(link);
174
175 return 0;
176 }
177
178 int route_configure(Route *route, Link *link,
179 sd_netlink_message_handler_t callback) {
180 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
181 int r;
182
183 assert(link);
184 assert(link->manager);
185 assert(link->manager->rtnl);
186 assert(link->ifindex > 0);
187 assert(route->family == AF_INET || route->family == AF_INET6);
188
189 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
190 RTM_NEWROUTE, route->family,
191 route->protocol);
192 if (r < 0)
193 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
194
195 if (!in_addr_is_null(route->family, &route->in_addr)) {
196 if (route->family == AF_INET)
197 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
198 else if (route->family == AF_INET6)
199 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
200 if (r < 0)
201 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
202 }
203
204 if (route->dst_prefixlen) {
205 if (route->family == AF_INET)
206 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
207 else if (route->family == AF_INET6)
208 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
209 if (r < 0)
210 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
211
212 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
213 if (r < 0)
214 return log_error_errno(r, "Could not set destination prefix length: %m");
215 }
216
217 if (route->src_prefixlen) {
218 if (route->family == AF_INET)
219 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
220 else if (route->family == AF_INET6)
221 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
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
230 if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
231 if (route->family == AF_INET)
232 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
233 else if (route->family == AF_INET6)
234 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
235 if (r < 0)
236 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
237 }
238
239 r = sd_rtnl_message_route_set_scope(req, route->scope);
240 if (r < 0)
241 return log_error_errno(r, "Could not set scope: %m");
242
243 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics);
244 if (r < 0)
245 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
246
247 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
248 if (r < 0)
249 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
250
251 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
252 if (r < 0)
253 return log_error_errno(r, "Could not send rtnetlink message: %m");
254
255 link_ref(link);
256
257 return 0;
258 }
259
260 int config_parse_gateway(const char *unit,
261 const char *filename,
262 unsigned line,
263 const char *section,
264 unsigned section_line,
265 const char *lvalue,
266 int ltype,
267 const char *rvalue,
268 void *data,
269 void *userdata) {
270
271 Network *network = userdata;
272 _cleanup_route_free_ Route *n = NULL;
273 union in_addr_union buffer;
274 int r, f;
275
276 assert(filename);
277 assert(section);
278 assert(lvalue);
279 assert(rvalue);
280 assert(data);
281
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
288 r = route_new_static(network, section_line, &n);
289 if (r < 0)
290 return r;
291
292 r = in_addr_from_string_auto(rvalue, &f, &buffer);
293 if (r < 0) {
294 log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
295 return 0;
296 }
297
298 n->family = f;
299 n->in_addr = buffer;
300 n = NULL;
301
302 return 0;
303 }
304
305 int 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
345 int 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) {
355
356 Network *network = userdata;
357 _cleanup_route_free_ Route *n = NULL;
358 const char *address, *e;
359 union in_addr_union buffer;
360 unsigned char prefixlen;
361 int r, f;
362
363 assert(filename);
364 assert(section);
365 assert(lvalue);
366 assert(rvalue);
367 assert(data);
368
369 r = route_new_static(network, section_line, &n);
370 if (r < 0)
371 return r;
372
373 /* Destination|Source=address/prefixlen */
374
375 /* address */
376 e = strchr(rvalue, '/');
377 if (e)
378 address = strndupa(rvalue, e - rvalue);
379 else
380 address = rvalue;
381
382 r = in_addr_from_string_auto(address, &f, &buffer);
383 if (r < 0) {
384 log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address);
385 return 0;
386 }
387
388 if (f != AF_INET && f != AF_INET6) {
389 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address);
390 return 0;
391 }
392
393 /* prefixlen */
394 if (e) {
395 r = safe_atou8(e + 1, &prefixlen);
396 if (r < 0) {
397 log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
398 return 0;
399 }
400 } else {
401 switch (f) {
402 case AF_INET:
403 prefixlen = 32;
404 break;
405 case AF_INET6:
406 prefixlen = 128;
407 break;
408 }
409 }
410
411 n->family = f;
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
421 n = NULL;
422
423 return 0;
424 }
425
426 int 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;
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 }
460
461 int 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 {
492 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
493 return 0;
494 }
495
496 n = NULL;
497
498 return 0;
499 }