chiark / gitweb /
sd-bus: add extra assert check
[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         /* prefixlen */
357         if (e) {
358                 r = safe_atou8(e + 1, &prefixlen);
359                 if (r < 0) {
360                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
361                                    "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
362                         return 0;
363                 }
364         } else {
365                 switch (n->family) {
366                         case AF_INET:
367                                 prefixlen = 32;
368                                 break;
369                         case AF_INET6:
370                                 prefixlen = 128;
371                                 break;
372                 }
373         }
374
375         n->family = f;
376         if (streq(lvalue, "Destination")) {
377                 n->dst_addr = buffer;
378                 n->dst_prefixlen = prefixlen;
379         } else if (streq(lvalue, "Source")) {
380                 n->src_addr = buffer;
381                 n->src_prefixlen = prefixlen;
382         } else
383                 assert_not_reached(lvalue);
384
385         n = NULL;
386
387         return 0;
388 }
389
390 int config_parse_route_priority(const char *unit,
391                                 const char *filename,
392                                 unsigned line,
393                                 const char *section,
394                                 unsigned section_line,
395                                 const char *lvalue,
396                                 int ltype,
397                                 const char *rvalue,
398                                 void *data,
399                                 void *userdata) {
400         Network *network = userdata;
401         _cleanup_route_free_ Route *n = NULL;
402         int r;
403
404         assert(filename);
405         assert(section);
406         assert(lvalue);
407         assert(rvalue);
408         assert(data);
409
410         r = route_new_static(network, section_line, &n);
411         if (r < 0)
412                 return r;
413
414         r = config_parse_unsigned(unit, filename, line, section,
415                                   section_line, lvalue, ltype,
416                                   rvalue, &n->metrics, userdata);
417         if (r < 0)
418                 return r;
419
420         n = NULL;
421
422         return 0;
423 }