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