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