chiark / gitweb /
networkctl: add new switch "-a" to "networkctl status" to show verbose status of...
[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                 route = hashmap_get(network->routes_by_section,
36                                     UINT_TO_PTR(section));
37                 if (route) {
38                         *ret = route;
39                         route = NULL;
40
41                         return 0;
42                 }
43         }
44
45         route = new0(Route, 1);
46         if (!route)
47                 return -ENOMEM;
48
49         route->family = AF_UNSPEC;
50         route->scope = RT_SCOPE_UNIVERSE;
51         route->protocol = RTPROT_STATIC;
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,
60                             UINT_TO_PTR(route->section), route);
61         }
62
63         *ret = route;
64         route = NULL;
65
66         return 0;
67 }
68
69 int route_new_dynamic(Route **ret, unsigned char rtm_protocol) {
70         _cleanup_route_free_ Route *route = NULL;
71
72         route = new0(Route, 1);
73         if (!route)
74                 return -ENOMEM;
75
76         route->family = AF_UNSPEC;
77         route->scope = RT_SCOPE_UNIVERSE;
78         route->protocol = rtm_protocol;
79
80         *ret = route;
81         route = NULL;
82
83         return 0;
84 }
85
86 void route_free(Route *route) {
87         if (!route)
88                 return;
89
90         if (route->network) {
91                 LIST_REMOVE(routes, route->network->static_routes, route);
92
93                 if (route->section)
94                         hashmap_remove(route->network->routes_by_section,
95                                        UINT_TO_PTR(route->section));
96         }
97
98         free(route);
99 }
100
101 int route_drop(Route *route, Link *link,
102                sd_rtnl_message_handler_t callback) {
103         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
104         int r;
105
106         assert(link);
107         assert(link->manager);
108         assert(link->manager->rtnl);
109         assert(link->ifindex > 0);
110         assert(route->family == AF_INET || route->family == AF_INET6);
111
112         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
113                                       RTM_DELROUTE, route->family,
114                                       route->protocol);
115         if (r < 0) {
116                 log_error("Could not create RTM_DELROUTE message: %s", strerror(-r));
117                 return r;
118         }
119
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                 log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
126                 return r;
127         }
128
129         if (route->dst_prefixlen) {
130                 if (route->family == AF_INET)
131                         r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
132                 else if (route->family == AF_INET6)
133                         r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
134                 if (r < 0) {
135                         log_error("Could not append RTA_DST attribute: %s", strerror(-r));
136                         return r;
137                 }
138
139                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
140                 if (r < 0) {
141                         log_error("Could not set destination prefix length: %s", strerror(-r));
142                         return r;
143                 }
144         }
145
146         r = sd_rtnl_message_route_set_scope(req, route->scope);
147         if (r < 0) {
148                 log_error("Could not set scope: %s", strerror(-r));
149                 return r;
150         }
151
152         r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
153         if (r < 0) {
154                 log_error("Could not append RTA_PRIORITY attribute: %s", strerror(-r));
155                 return r;
156         }
157
158         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
159         if (r < 0) {
160                 log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
161                 return r;
162         }
163
164         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
165         if (r < 0) {
166                 log_error("Could not send rtnetlink message: %s", strerror(-r));
167                 return r;
168         }
169
170         link_ref(link);
171
172         return 0;
173 }
174
175 int route_configure(Route *route, Link *link,
176                     sd_rtnl_message_handler_t callback) {
177         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
178         int r;
179
180         assert(link);
181         assert(link->manager);
182         assert(link->manager->rtnl);
183         assert(link->ifindex > 0);
184         assert(route->family == AF_INET || route->family == AF_INET6);
185
186         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
187                                       RTM_NEWROUTE, route->family,
188                                       route->protocol);
189         if (r < 0) {
190                 log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r));
191                 return r;
192         }
193
194         if (route->family == AF_INET)
195                 r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
196         else if (route->family == AF_INET6)
197                 r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
198         if (r < 0) {
199                 log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
200                 return r;
201         }
202
203         if (route->dst_prefixlen) {
204                 if (route->family == AF_INET)
205                         r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
206                 else if (route->family == AF_INET6)
207                         r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
208                 if (r < 0) {
209                         log_error("Could not append RTA_DST attribute: %s", strerror(-r));
210                         return r;
211                 }
212
213                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
214                 if (r < 0) {
215                         log_error("Could not set destination prefix length: %s", strerror(-r));
216                         return r;
217                 }
218         }
219
220         r = sd_rtnl_message_route_set_scope(req, route->scope);
221         if (r < 0) {
222                 log_error("Could not set scope: %s", strerror(-r));
223                 return r;
224         }
225
226         r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
227         if (r < 0) {
228                 log_error("Could not append RTA_PRIORITY attribute: %s", strerror(-r));
229                 return r;
230         }
231
232         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
233         if (r < 0) {
234                 log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
235                 return r;
236         }
237
238         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
239         if (r < 0) {
240                 log_error("Could not send rtnetlink message: %s", strerror(-r));
241                 return r;
242         }
243
244         link_ref(link);
245
246         return 0;
247 }
248
249 int config_parse_gateway(const char *unit,
250                 const char *filename,
251                 unsigned line,
252                 const char *section,
253                 unsigned section_line,
254                 const char *lvalue,
255                 int ltype,
256                 const char *rvalue,
257                 void *data,
258                 void *userdata) {
259
260         Network *network = userdata;
261         _cleanup_route_free_ Route *n = NULL;
262         union in_addr_union buffer;
263         int r, f;
264
265         assert(filename);
266         assert(section);
267         assert(lvalue);
268         assert(rvalue);
269         assert(data);
270
271         if (streq(section, "Network")) {
272                 /* we are not in an Route section, so treat
273                  * this as the special '0' section */
274                 section_line = 0;
275         }
276
277         r = route_new_static(network, section_line, &n);
278         if (r < 0)
279                 return r;
280
281         r = in_addr_from_string_auto(rvalue, &f, &buffer);
282         if (r < 0) {
283                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
284                            "Route is invalid, ignoring assignment: %s", rvalue);
285                 return 0;
286         }
287
288         n->family = f;
289         n->in_addr = buffer;
290         n = NULL;
291
292         return 0;
293 }
294
295 int config_parse_destination(const char *unit,
296                 const char *filename,
297                 unsigned line,
298                 const char *section,
299                 unsigned section_line,
300                 const char *lvalue,
301                 int ltype,
302                 const char *rvalue,
303                 void *data,
304                 void *userdata) {
305
306         Network *network = userdata;
307         _cleanup_route_free_ Route *n = NULL;
308         const char *address, *e;
309         union in_addr_union buffer;
310         int r, f;
311
312         assert(filename);
313         assert(section);
314         assert(lvalue);
315         assert(rvalue);
316         assert(data);
317
318         r = route_new_static(network, section_line, &n);
319         if (r < 0)
320                 return r;
321
322         /* Destination=address/prefixlen */
323
324         /* address */
325         e = strchr(rvalue, '/');
326         if (e)
327                 address = strndupa(rvalue, e - rvalue);
328         else
329                 address = rvalue;
330
331         r = in_addr_from_string_auto(address, &f, &buffer);
332         if (r < 0) {
333                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
334                            "Destination is invalid, ignoring assignment: %s", address);
335                 return 0;
336         }
337
338         /* prefixlen */
339         if (e) {
340                 unsigned i;
341
342                 r = safe_atou(e + 1, &i);
343                 if (r < 0) {
344                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
345                                    "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
346                         return 0;
347                 }
348
349                 n->dst_prefixlen = (unsigned char) i;
350         } else {
351                 switch (n->family) {
352                         case AF_INET:
353                                 n->dst_prefixlen = 32;
354                                 break;
355                         case AF_INET6:
356                                 n->dst_prefixlen = 128;
357                                 break;
358                 }
359         }
360
361         n->family = f;
362         n->dst_addr = buffer;
363         n = NULL;
364
365         return 0;
366 }
367
368 int config_parse_route_priority(const char *unit,
369                                 const char *filename,
370                                 unsigned line,
371                                 const char *section,
372                                 unsigned section_line,
373                                 const char *lvalue,
374                                 int ltype,
375                                 const char *rvalue,
376                                 void *data,
377                                 void *userdata) {
378         Network *network = userdata;
379         _cleanup_route_free_ Route *n = NULL;
380         int r;
381
382         assert(filename);
383         assert(section);
384         assert(lvalue);
385         assert(rvalue);
386         assert(data);
387
388         r = route_new_static(network, section_line, &n);
389         if (r < 0)
390                 return r;
391
392         r = config_parse_unsigned(unit, filename, line, section,
393                                   section_line, lvalue, ltype,
394                                   rvalue, &n->metrics, userdata);
395         if (r < 0)
396                 return r;
397
398         n = NULL;
399
400         return 0;
401 }