chiark / gitweb /
update TODO
[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 #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 (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
142                 if (route->family == AF_INET)
143                         r = sd_rtnl_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
144                 else if (route->family == AF_INET6)
145                         r = sd_rtnl_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
146                 if (r < 0)
147                         return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
148         }
149
150         r = sd_rtnl_message_route_set_scope(req, route->scope);
151         if (r < 0)
152                 return log_error_errno(r, "Could not set scope: %m");
153
154         r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
155         if (r < 0)
156                 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
157
158         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
159         if (r < 0)
160                 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
161
162         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
163         if (r < 0)
164                 return log_error_errno(r, "Could not send rtnetlink message: %m");
165
166         link_ref(link);
167
168         return 0;
169 }
170
171 int route_configure(Route *route, Link *link,
172                     sd_rtnl_message_handler_t callback) {
173         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
174         int r;
175
176         assert(link);
177         assert(link->manager);
178         assert(link->manager->rtnl);
179         assert(link->ifindex > 0);
180         assert(route->family == AF_INET || route->family == AF_INET6);
181
182         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
183                                       RTM_NEWROUTE, route->family,
184                                       route->protocol);
185         if (r < 0)
186                 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
187
188         if (!in_addr_is_null(route->family, &route->in_addr)) {
189                 if (route->family == AF_INET)
190                         r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
191                 else if (route->family == AF_INET6)
192                         r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
193                 if (r < 0)
194                         return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
195         }
196
197         if (route->dst_prefixlen) {
198                 if (route->family == AF_INET)
199                         r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
200                 else if (route->family == AF_INET6)
201                         r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
202                 if (r < 0)
203                         return log_error_errno(r, "Could not append RTA_DST attribute: %m");
204
205                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
206                 if (r < 0)
207                         return log_error_errno(r, "Could not set destination prefix length: %m");
208         }
209
210         if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
211                 if (route->family == AF_INET)
212                         r = sd_rtnl_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
213                 else if (route->family == AF_INET6)
214                         r = sd_rtnl_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
215                 if (r < 0)
216                         return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
217         }
218
219         r = sd_rtnl_message_route_set_scope(req, route->scope);
220         if (r < 0)
221                 return log_error_errno(r, "Could not set scope: %m");
222
223         r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
224         if (r < 0)
225                 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
226
227         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
228         if (r < 0)
229                 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
230
231         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
232         if (r < 0)
233                 return log_error_errno(r, "Could not send rtnetlink message: %m");
234
235         link_ref(link);
236
237         return 0;
238 }
239
240 int config_parse_gateway(const char *unit,
241                 const char *filename,
242                 unsigned line,
243                 const char *section,
244                 unsigned section_line,
245                 const char *lvalue,
246                 int ltype,
247                 const char *rvalue,
248                 void *data,
249                 void *userdata) {
250
251         Network *network = userdata;
252         _cleanup_route_free_ Route *n = NULL;
253         union in_addr_union buffer;
254         int r, f;
255
256         assert(filename);
257         assert(section);
258         assert(lvalue);
259         assert(rvalue);
260         assert(data);
261
262         if (streq(section, "Network")) {
263                 /* we are not in an Route section, so treat
264                  * this as the special '0' section */
265                 section_line = 0;
266         }
267
268         r = route_new_static(network, section_line, &n);
269         if (r < 0)
270                 return r;
271
272         r = in_addr_from_string_auto(rvalue, &f, &buffer);
273         if (r < 0) {
274                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
275                            "Route is invalid, ignoring assignment: %s", rvalue);
276                 return 0;
277         }
278
279         n->family = f;
280         n->in_addr = buffer;
281         n = NULL;
282
283         return 0;
284 }
285
286 int config_parse_destination(const char *unit,
287                 const char *filename,
288                 unsigned line,
289                 const char *section,
290                 unsigned section_line,
291                 const char *lvalue,
292                 int ltype,
293                 const char *rvalue,
294                 void *data,
295                 void *userdata) {
296
297         Network *network = userdata;
298         _cleanup_route_free_ Route *n = NULL;
299         const char *address, *e;
300         union in_addr_union buffer;
301         int r, f;
302
303         assert(filename);
304         assert(section);
305         assert(lvalue);
306         assert(rvalue);
307         assert(data);
308
309         r = route_new_static(network, section_line, &n);
310         if (r < 0)
311                 return r;
312
313         /* Destination=address/prefixlen */
314
315         /* address */
316         e = strchr(rvalue, '/');
317         if (e)
318                 address = strndupa(rvalue, e - rvalue);
319         else
320                 address = rvalue;
321
322         r = in_addr_from_string_auto(address, &f, &buffer);
323         if (r < 0) {
324                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
325                            "Destination is invalid, ignoring assignment: %s", address);
326                 return 0;
327         }
328
329         /* prefixlen */
330         if (e) {
331                 unsigned i;
332
333                 r = safe_atou(e + 1, &i);
334                 if (r < 0) {
335                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
336                                    "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
337                         return 0;
338                 }
339
340                 n->dst_prefixlen = (unsigned char) i;
341         } else {
342                 switch (n->family) {
343                         case AF_INET:
344                                 n->dst_prefixlen = 32;
345                                 break;
346                         case AF_INET6:
347                                 n->dst_prefixlen = 128;
348                                 break;
349                 }
350         }
351
352         n->family = f;
353         n->dst_addr = buffer;
354         n = NULL;
355
356         return 0;
357 }
358
359 int config_parse_route_priority(const char *unit,
360                                 const char *filename,
361                                 unsigned line,
362                                 const char *section,
363                                 unsigned section_line,
364                                 const char *lvalue,
365                                 int ltype,
366                                 const char *rvalue,
367                                 void *data,
368                                 void *userdata) {
369         Network *network = userdata;
370         _cleanup_route_free_ Route *n = NULL;
371         int r;
372
373         assert(filename);
374         assert(section);
375         assert(lvalue);
376         assert(rvalue);
377         assert(data);
378
379         r = route_new_static(network, section_line, &n);
380         if (r < 0)
381                 return r;
382
383         r = config_parse_unsigned(unit, filename, line, section,
384                                   section_line, lvalue, ltype,
385                                   rvalue, &n->metrics, userdata);
386         if (r < 0)
387                 return r;
388
389         n = NULL;
390
391         return 0;
392 }