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