chiark / gitweb /
sd-network: expose DNS/NTP servers as strings
[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         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, &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                                        &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         Network *network = userdata;
260         _cleanup_route_free_ Route *n = NULL;
261         _cleanup_free_ char *route = NULL;
262         int r;
263
264         assert(filename);
265         assert(section);
266         assert(lvalue);
267         assert(rvalue);
268         assert(data);
269
270         if (streq(section, "Network")) {
271                 /* we are not in an Route section, so treat
272                  * this as the special '0' section */
273                 section_line = 0;
274         }
275
276         r = route_new_static(network, section_line, &n);
277         if (r < 0)
278                 return r;
279
280         r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
281         if (r < 0) {
282                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
283                            "Route is invalid, ignoring assignment: %s", route);
284                 return 0;
285         }
286
287         n = NULL;
288
289         return 0;
290 }
291
292 int config_parse_destination(const char *unit,
293                 const char *filename,
294                 unsigned line,
295                 const char *section,
296                 unsigned section_line,
297                 const char *lvalue,
298                 int ltype,
299                 const char *rvalue,
300                 void *data,
301                 void *userdata) {
302         Network *network = userdata;
303         _cleanup_route_free_ Route *n = NULL;
304         _cleanup_free_ char *address = NULL;
305         const char *e;
306         int r;
307
308         assert(filename);
309         assert(section);
310         assert(lvalue);
311         assert(rvalue);
312         assert(data);
313
314         r = route_new_static(network, section_line, &n);
315         if (r < 0)
316                 return r;
317
318         /* Destination=address/prefixlen */
319
320         /* address */
321         e = strchr(rvalue, '/');
322         if (e) {
323                 address = strndup(rvalue, e - rvalue);
324                 if (!address)
325                         return log_oom();
326         } else {
327                 address = strdup(rvalue);
328                 if (!address)
329                         return log_oom();
330         }
331
332         r = net_parse_inaddr(address, &n->family, &n->dst_addr);
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, "
347                                    "ignoring assignment: %s", e + 1);
348                         return 0;
349                 }
350
351                 n->dst_prefixlen = (unsigned char) i;
352         } else {
353                 switch (n->family) {
354                         case AF_INET:
355                                 n->dst_prefixlen = 32;
356                                 break;
357                         case AF_INET6:
358                                 n->dst_prefixlen = 128;
359                                 break;
360                 }
361         }
362
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 }