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