chiark / gitweb /
acfe3f023f1dfd2a15f1df406acb535158a28232
[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
26 #include "utf8.h"
27 #include "util.h"
28 #include "conf-parser.h"
29 #include "network-internal.h"
30
31 int route_new_static(Network *network, unsigned section, Route **ret) {
32         _cleanup_route_free_ Route *route = NULL;
33
34         if (section) {
35                 uint64_t key = section;
36
37                 route = hashmap_get(network->routes_by_section, &key);
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
53         route->network = network;
54
55         LIST_PREPEND(routes, network->static_routes, route);
56
57         if (section) {
58                 route->section = section;
59                 hashmap_put(network->routes_by_section, &route->section, route);
60         }
61
62         *ret = route;
63         route = NULL;
64
65         return 0;
66 }
67
68 int route_new_dynamic(Route **ret) {
69         _cleanup_route_free_ Route *route = NULL;
70
71         route = new0(Route, 1);
72         if (!route)
73                 return -ENOMEM;
74
75         route->family = AF_UNSPEC;
76         route->scope = RT_SCOPE_UNIVERSE;
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                                        &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         if (r < 0) {
113                 log_error("Could not create RTM_DELROUTE message: %s", strerror(-r));
114                 return r;
115         }
116
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                 log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
123                 return r;
124         }
125
126         if (route->dst_prefixlen) {
127                 if (route->family == AF_INET)
128                         r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
129                 else if (route->family == AF_INET6)
130                         r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
131                 if (r < 0) {
132                         log_error("Could not append RTA_DST attribute: %s", strerror(-r));
133                         return r;
134                 }
135
136                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
137                 if (r < 0) {
138                         log_error("Could not set destination prefix length: %s", strerror(-r));
139                         return r;
140                 }
141         }
142
143         r = sd_rtnl_message_route_set_scope(req, route->scope);
144         if (r < 0) {
145                 log_error("Could not set scope: %s", strerror(-r));
146                 return r;
147         }
148
149         r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
150         if (r < 0) {
151                 log_error("Could not append RTA_PRIORITY attribute: %s", strerror(-r));
152                 return r;
153         }
154
155         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
156         if (r < 0) {
157                 log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
158                 return r;
159         }
160
161         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
162         if (r < 0) {
163                 log_error("Could not send rtnetlink message: %s", strerror(-r));
164                 return r;
165         }
166
167         link_ref(link);
168
169         return 0;
170 }
171
172 int route_configure(Route *route, Link *link,
173                     sd_rtnl_message_handler_t callback) {
174         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
175         int r;
176
177         assert(link);
178         assert(link->manager);
179         assert(link->manager->rtnl);
180         assert(link->ifindex > 0);
181         assert(route->family == AF_INET || route->family == AF_INET6);
182
183         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
184                                       RTM_NEWROUTE, route->family);
185         if (r < 0) {
186                 log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r));
187                 return r;
188         }
189
190         if (route->family == AF_INET)
191                 r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
192         else if (route->family == AF_INET6)
193                 r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
194         if (r < 0) {
195                 log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
196                 return r;
197         }
198
199         if (route->dst_prefixlen) {
200                 if (route->family == AF_INET)
201                         r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
202                 else if (route->family == AF_INET6)
203                         r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
204                 if (r < 0) {
205                         log_error("Could not append RTA_DST attribute: %s", strerror(-r));
206                         return r;
207                 }
208
209                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
210                 if (r < 0) {
211                         log_error("Could not set destination prefix length: %s", strerror(-r));
212                         return r;
213                 }
214         }
215
216         r = sd_rtnl_message_route_set_scope(req, route->scope);
217         if (r < 0) {
218                 log_error("Could not set scope: %s", strerror(-r));
219                 return r;
220         }
221
222         r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
223         if (r < 0) {
224                 log_error("Could not append RTA_PRIORITY attribute: %s", strerror(-r));
225                 return r;
226         }
227
228         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
229         if (r < 0) {
230                 log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
231                 return r;
232         }
233
234         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
235         if (r < 0) {
236                 log_error("Could not send rtnetlink message: %s", strerror(-r));
237                 return r;
238         }
239
240         link_ref(link);
241
242         return 0;
243 }
244
245 int config_parse_gateway(const char *unit,
246                 const char *filename,
247                 unsigned line,
248                 const char *section,
249                 unsigned section_line,
250                 const char *lvalue,
251                 int ltype,
252                 const char *rvalue,
253                 void *data,
254                 void *userdata) {
255         Network *network = userdata;
256         _cleanup_route_free_ Route *n = NULL;
257         _cleanup_free_ char *route = NULL;
258         int r;
259
260         assert(filename);
261         assert(section);
262         assert(lvalue);
263         assert(rvalue);
264         assert(data);
265
266         if (streq(section, "Network")) {
267                 /* we are not in an Route section, so treat
268                  * this as the special '0' section */
269                 section_line = 0;
270         }
271
272         r = route_new_static(network, section_line, &n);
273         if (r < 0)
274                 return r;
275
276         r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
277         if (r < 0) {
278                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
279                            "Route is invalid, ignoring assignment: %s", route);
280                 return 0;
281         }
282
283         n = NULL;
284
285         return 0;
286 }
287
288 int config_parse_destination(const char *unit,
289                 const char *filename,
290                 unsigned line,
291                 const char *section,
292                 unsigned section_line,
293                 const char *lvalue,
294                 int ltype,
295                 const char *rvalue,
296                 void *data,
297                 void *userdata) {
298         Network *network = userdata;
299         _cleanup_route_free_ Route *n = NULL;
300         _cleanup_free_ char *address = NULL;
301         const char *e;
302         int r;
303
304         assert(filename);
305         assert(section);
306         assert(lvalue);
307         assert(rvalue);
308         assert(data);
309
310         r = route_new_static(network, section_line, &n);
311         if (r < 0)
312                 return r;
313
314         /* Destination=address/prefixlen */
315
316         /* address */
317         e = strchr(rvalue, '/');
318         if (e) {
319                 address = strndup(rvalue, e - rvalue);
320                 if (!address)
321                         return log_oom();
322         } else {
323                 address = strdup(rvalue);
324                 if (!address)
325                         return log_oom();
326         }
327
328         r = net_parse_inaddr(address, &n->family, &n->dst_addr);
329         if (r < 0) {
330                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
331                            "Destination is invalid, ignoring assignment: %s", address);
332                 return 0;
333         }
334
335         /* prefixlen */
336         if (e) {
337                 unsigned i;
338
339                 r = safe_atou(e + 1, &i);
340                 if (r < 0) {
341                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
342                                    "Route destination prefix length is invalid, "
343                                    "ignoring assignment: %s", e + 1);
344                         return 0;
345                 }
346
347                 n->dst_prefixlen = (unsigned char) i;
348         } else {
349                 switch (n->family) {
350                         case AF_INET:
351                                 n->dst_prefixlen = 32;
352                                 break;
353                         case AF_INET6:
354                                 n->dst_prefixlen = 128;
355                                 break;
356                 }
357         }
358
359         n = NULL;
360
361         return 0;
362 }