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