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