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