]>
Commit | Line | Data |
---|---|---|
f579559b TG |
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 | ||
f579559b TG |
22 | #include "util.h" |
23 | #include "conf-parser.h" | |
fc2f9534 LP |
24 | #include "netlink-util.h" |
25 | ||
26 | #include "networkd.h" | |
27 | #include "networkd-route.h" | |
f579559b | 28 | |
f0213e37 TG |
29 | int route_new(Route **ret, unsigned char rtm_protocol) { |
30 | _cleanup_route_free_ Route *route = NULL; | |
31 | ||
32 | route = new0(Route, 1); | |
33 | if (!route) | |
34 | return -ENOMEM; | |
35 | ||
36 | route->family = AF_UNSPEC; | |
37 | route->scope = RT_SCOPE_UNIVERSE; | |
38 | route->protocol = rtm_protocol; | |
39 | ||
40 | *ret = route; | |
41 | route = NULL; | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
f048a16b | 46 | int route_new_static(Network *network, unsigned section, Route **ret) { |
f579559b | 47 | _cleanup_route_free_ Route *route = NULL; |
f0213e37 | 48 | int r; |
f579559b | 49 | |
6ae115c1 | 50 | if (section) { |
16aa63a0 TG |
51 | route = hashmap_get(network->routes_by_section, |
52 | UINT_TO_PTR(section)); | |
6ae115c1 TG |
53 | if (route) { |
54 | *ret = route; | |
55 | route = NULL; | |
56 | ||
57 | return 0; | |
58 | } | |
59 | } | |
60 | ||
f0213e37 TG |
61 | r = route_new(&route, RTPROT_STATIC); |
62 | if (r < 0) | |
63 | return r; | |
801bd9e8 | 64 | |
f579559b TG |
65 | route->network = network; |
66 | ||
3d3d4255 | 67 | LIST_PREPEND(routes, network->static_routes, route); |
f579559b | 68 | |
6ae115c1 TG |
69 | if (section) { |
70 | route->section = section; | |
16aa63a0 TG |
71 | hashmap_put(network->routes_by_section, |
72 | UINT_TO_PTR(route->section), route); | |
6ae115c1 TG |
73 | } |
74 | ||
f579559b TG |
75 | *ret = route; |
76 | route = NULL; | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | void route_free(Route *route) { | |
82 | if (!route) | |
83 | return; | |
84 | ||
f048a16b | 85 | if (route->network) { |
3d3d4255 | 86 | LIST_REMOVE(routes, route->network->static_routes, route); |
f579559b | 87 | |
f048a16b TG |
88 | if (route->section) |
89 | hashmap_remove(route->network->routes_by_section, | |
16aa63a0 | 90 | UINT_TO_PTR(route->section)); |
f048a16b | 91 | } |
6ae115c1 | 92 | |
f579559b TG |
93 | free(route); |
94 | } | |
95 | ||
5c1d3fc9 | 96 | int route_drop(Route *route, Link *link, |
1c4baffc TG |
97 | sd_netlink_message_handler_t callback) { |
98 | _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; | |
5c1d3fc9 UTL |
99 | int r; |
100 | ||
101 | assert(link); | |
102 | assert(link->manager); | |
103 | assert(link->manager->rtnl); | |
104 | assert(link->ifindex > 0); | |
105 | assert(route->family == AF_INET || route->family == AF_INET6); | |
106 | ||
107 | r = sd_rtnl_message_new_route(link->manager->rtnl, &req, | |
28cc555d DW |
108 | RTM_DELROUTE, route->family, |
109 | route->protocol); | |
f647962d MS |
110 | if (r < 0) |
111 | return log_error_errno(r, "Could not create RTM_DELROUTE message: %m"); | |
5c1d3fc9 | 112 | |
59580681 GL |
113 | if (!in_addr_is_null(route->family, &route->in_addr)) { |
114 | if (route->family == AF_INET) | |
1c4baffc | 115 | r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); |
59580681 | 116 | else if (route->family == AF_INET6) |
1c4baffc | 117 | r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); |
f647962d MS |
118 | if (r < 0) |
119 | return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); | |
5c1d3fc9 UTL |
120 | } |
121 | ||
122 | if (route->dst_prefixlen) { | |
123 | if (route->family == AF_INET) | |
1c4baffc | 124 | r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in); |
5c1d3fc9 | 125 | else if (route->family == AF_INET6) |
1c4baffc | 126 | r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6); |
f647962d MS |
127 | if (r < 0) |
128 | return log_error_errno(r, "Could not append RTA_DST attribute: %m"); | |
5c1d3fc9 UTL |
129 | |
130 | r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); | |
f647962d MS |
131 | if (r < 0) |
132 | return log_error_errno(r, "Could not set destination prefix length: %m"); | |
5c1d3fc9 UTL |
133 | } |
134 | ||
9e7e4408 TG |
135 | if (route->src_prefixlen) { |
136 | if (route->family == AF_INET) | |
1c4baffc | 137 | r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in); |
9e7e4408 | 138 | else if (route->family == AF_INET6) |
1c4baffc | 139 | r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6); |
9e7e4408 TG |
140 | if (r < 0) |
141 | return log_error_errno(r, "Could not append RTA_DST attribute: %m"); | |
142 | ||
143 | r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); | |
144 | if (r < 0) | |
145 | return log_error_errno(r, "Could not set source prefix length: %m"); | |
146 | } | |
147 | ||
46b0c76e ERB |
148 | if (!in_addr_is_null(route->family, &route->prefsrc_addr)) { |
149 | if (route->family == AF_INET) | |
1c4baffc | 150 | r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in); |
46b0c76e | 151 | else if (route->family == AF_INET6) |
1c4baffc | 152 | r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6); |
f647962d MS |
153 | if (r < 0) |
154 | return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); | |
46b0c76e ERB |
155 | } |
156 | ||
5c1d3fc9 | 157 | r = sd_rtnl_message_route_set_scope(req, route->scope); |
f647962d MS |
158 | if (r < 0) |
159 | return log_error_errno(r, "Could not set scope: %m"); | |
5c1d3fc9 | 160 | |
1c4baffc | 161 | r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics); |
f647962d MS |
162 | if (r < 0) |
163 | return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); | |
5c1d3fc9 | 164 | |
1c4baffc | 165 | r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); |
f647962d MS |
166 | if (r < 0) |
167 | return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); | |
5c1d3fc9 | 168 | |
1c4baffc | 169 | r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); |
f647962d MS |
170 | if (r < 0) |
171 | return log_error_errno(r, "Could not send rtnetlink message: %m"); | |
5c1d3fc9 | 172 | |
563c69c6 TG |
173 | link_ref(link); |
174 | ||
5c1d3fc9 UTL |
175 | return 0; |
176 | } | |
177 | ||
f882c247 | 178 | int route_configure(Route *route, Link *link, |
1c4baffc TG |
179 | sd_netlink_message_handler_t callback) { |
180 | _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; | |
f579559b TG |
181 | int r; |
182 | ||
f579559b | 183 | assert(link); |
f882c247 TG |
184 | assert(link->manager); |
185 | assert(link->manager->rtnl); | |
f579559b TG |
186 | assert(link->ifindex > 0); |
187 | assert(route->family == AF_INET || route->family == AF_INET6); | |
188 | ||
151b9b96 | 189 | r = sd_rtnl_message_new_route(link->manager->rtnl, &req, |
28cc555d DW |
190 | RTM_NEWROUTE, route->family, |
191 | route->protocol); | |
f647962d MS |
192 | if (r < 0) |
193 | return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); | |
f579559b | 194 | |
59580681 GL |
195 | if (!in_addr_is_null(route->family, &route->in_addr)) { |
196 | if (route->family == AF_INET) | |
1c4baffc | 197 | r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); |
59580681 | 198 | else if (route->family == AF_INET6) |
1c4baffc | 199 | r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); |
f647962d MS |
200 | if (r < 0) |
201 | return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); | |
f579559b TG |
202 | } |
203 | ||
0a0dc69b TG |
204 | if (route->dst_prefixlen) { |
205 | if (route->family == AF_INET) | |
1c4baffc | 206 | r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in); |
0a0dc69b | 207 | else if (route->family == AF_INET6) |
1c4baffc | 208 | r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6); |
f647962d MS |
209 | if (r < 0) |
210 | return log_error_errno(r, "Could not append RTA_DST attribute: %m"); | |
6ae115c1 | 211 | |
ae4c67a7 | 212 | r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); |
f647962d MS |
213 | if (r < 0) |
214 | return log_error_errno(r, "Could not set destination prefix length: %m"); | |
1f01fb4f TG |
215 | } |
216 | ||
9e7e4408 TG |
217 | if (route->src_prefixlen) { |
218 | if (route->family == AF_INET) | |
1c4baffc | 219 | r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in); |
9e7e4408 | 220 | else if (route->family == AF_INET6) |
1c4baffc | 221 | r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6); |
9e7e4408 TG |
222 | if (r < 0) |
223 | return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); | |
224 | ||
225 | r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); | |
226 | if (r < 0) | |
227 | return log_error_errno(r, "Could not set source prefix length: %m"); | |
228 | } | |
229 | ||
46b0c76e ERB |
230 | if (!in_addr_is_null(route->family, &route->prefsrc_addr)) { |
231 | if (route->family == AF_INET) | |
1c4baffc | 232 | r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in); |
46b0c76e | 233 | else if (route->family == AF_INET6) |
1c4baffc | 234 | r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6); |
f647962d MS |
235 | if (r < 0) |
236 | return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); | |
46b0c76e ERB |
237 | } |
238 | ||
5c1d3fc9 | 239 | r = sd_rtnl_message_route_set_scope(req, route->scope); |
f647962d MS |
240 | if (r < 0) |
241 | return log_error_errno(r, "Could not set scope: %m"); | |
5c1d3fc9 | 242 | |
1c4baffc | 243 | r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics); |
f647962d MS |
244 | if (r < 0) |
245 | return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); | |
5c1d3fc9 | 246 | |
1c4baffc | 247 | r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); |
f647962d MS |
248 | if (r < 0) |
249 | return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); | |
f579559b | 250 | |
1c4baffc | 251 | r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); |
f647962d MS |
252 | if (r < 0) |
253 | return log_error_errno(r, "Could not send rtnetlink message: %m"); | |
f579559b | 254 | |
563c69c6 TG |
255 | link_ref(link); |
256 | ||
f579559b TG |
257 | return 0; |
258 | } | |
259 | ||
260 | int config_parse_gateway(const char *unit, | |
261 | const char *filename, | |
262 | unsigned line, | |
263 | const char *section, | |
71a61510 | 264 | unsigned section_line, |
f579559b TG |
265 | const char *lvalue, |
266 | int ltype, | |
267 | const char *rvalue, | |
268 | void *data, | |
269 | void *userdata) { | |
44e7b949 | 270 | |
6ae115c1 | 271 | Network *network = userdata; |
f579559b | 272 | _cleanup_route_free_ Route *n = NULL; |
44e7b949 LP |
273 | union in_addr_union buffer; |
274 | int r, f; | |
f579559b TG |
275 | |
276 | assert(filename); | |
6ae115c1 | 277 | assert(section); |
f579559b TG |
278 | assert(lvalue); |
279 | assert(rvalue); | |
280 | assert(data); | |
281 | ||
92fe133a TG |
282 | if (streq(section, "Network")) { |
283 | /* we are not in an Route section, so treat | |
284 | * this as the special '0' section */ | |
285 | section_line = 0; | |
286 | } | |
287 | ||
f048a16b | 288 | r = route_new_static(network, section_line, &n); |
f579559b TG |
289 | if (r < 0) |
290 | return r; | |
291 | ||
44e7b949 | 292 | r = in_addr_from_string_auto(rvalue, &f, &buffer); |
f579559b | 293 | if (r < 0) { |
12ca818f | 294 | log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue); |
f579559b TG |
295 | return 0; |
296 | } | |
297 | ||
44e7b949 LP |
298 | n->family = f; |
299 | n->in_addr = buffer; | |
f579559b TG |
300 | n = NULL; |
301 | ||
302 | return 0; | |
303 | } | |
6ae115c1 | 304 | |
0d07e595 JK |
305 | int config_parse_preferred_src(const char *unit, |
306 | const char *filename, | |
307 | unsigned line, | |
308 | const char *section, | |
309 | unsigned section_line, | |
310 | const char *lvalue, | |
311 | int ltype, | |
312 | const char *rvalue, | |
313 | void *data, | |
314 | void *userdata) { | |
315 | ||
316 | Network *network = userdata; | |
317 | _cleanup_route_free_ Route *n = NULL; | |
318 | union in_addr_union buffer; | |
319 | int r, f; | |
320 | ||
321 | assert(filename); | |
322 | assert(section); | |
323 | assert(lvalue); | |
324 | assert(rvalue); | |
325 | assert(data); | |
326 | ||
327 | r = route_new_static(network, section_line, &n); | |
328 | if (r < 0) | |
329 | return r; | |
330 | ||
331 | r = in_addr_from_string_auto(rvalue, &f, &buffer); | |
332 | if (r < 0) { | |
333 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
334 | "Preferred source is invalid, ignoring assignment: %s", rvalue); | |
335 | return 0; | |
336 | } | |
337 | ||
338 | n->family = f; | |
339 | n->prefsrc_addr = buffer; | |
340 | n = NULL; | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
6ae115c1 TG |
345 | int config_parse_destination(const char *unit, |
346 | const char *filename, | |
347 | unsigned line, | |
348 | const char *section, | |
349 | unsigned section_line, | |
350 | const char *lvalue, | |
351 | int ltype, | |
352 | const char *rvalue, | |
353 | void *data, | |
354 | void *userdata) { | |
44e7b949 | 355 | |
6ae115c1 TG |
356 | Network *network = userdata; |
357 | _cleanup_route_free_ Route *n = NULL; | |
44e7b949 LP |
358 | const char *address, *e; |
359 | union in_addr_union buffer; | |
9e7e4408 | 360 | unsigned char prefixlen; |
44e7b949 | 361 | int r, f; |
6ae115c1 TG |
362 | |
363 | assert(filename); | |
364 | assert(section); | |
365 | assert(lvalue); | |
366 | assert(rvalue); | |
367 | assert(data); | |
368 | ||
f048a16b | 369 | r = route_new_static(network, section_line, &n); |
6ae115c1 TG |
370 | if (r < 0) |
371 | return r; | |
372 | ||
9e7e4408 | 373 | /* Destination|Source=address/prefixlen */ |
6ae115c1 | 374 | |
ae4c67a7 | 375 | /* address */ |
6ae115c1 | 376 | e = strchr(rvalue, '/'); |
44e7b949 LP |
377 | if (e) |
378 | address = strndupa(rvalue, e - rvalue); | |
379 | else | |
380 | address = rvalue; | |
6ae115c1 | 381 | |
44e7b949 | 382 | r = in_addr_from_string_auto(address, &f, &buffer); |
6ae115c1 | 383 | if (r < 0) { |
12ca818f | 384 | log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address); |
6ae115c1 TG |
385 | return 0; |
386 | } | |
387 | ||
935c0d26 | 388 | if (f != AF_INET && f != AF_INET6) { |
12ca818f | 389 | log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address); |
935c0d26 TG |
390 | return 0; |
391 | } | |
392 | ||
ae4c67a7 TG |
393 | /* prefixlen */ |
394 | if (e) { | |
9e7e4408 | 395 | r = safe_atou8(e + 1, &prefixlen); |
ae4c67a7 | 396 | if (r < 0) { |
12ca818f | 397 | log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1); |
ae4c67a7 TG |
398 | return 0; |
399 | } | |
ae4c67a7 | 400 | } else { |
935c0d26 | 401 | switch (f) { |
ae4c67a7 | 402 | case AF_INET: |
9e7e4408 | 403 | prefixlen = 32; |
ae4c67a7 TG |
404 | break; |
405 | case AF_INET6: | |
9e7e4408 | 406 | prefixlen = 128; |
ae4c67a7 TG |
407 | break; |
408 | } | |
409 | } | |
410 | ||
44e7b949 | 411 | n->family = f; |
9e7e4408 TG |
412 | if (streq(lvalue, "Destination")) { |
413 | n->dst_addr = buffer; | |
414 | n->dst_prefixlen = prefixlen; | |
415 | } else if (streq(lvalue, "Source")) { | |
416 | n->src_addr = buffer; | |
417 | n->src_prefixlen = prefixlen; | |
418 | } else | |
419 | assert_not_reached(lvalue); | |
420 | ||
6ae115c1 TG |
421 | n = NULL; |
422 | ||
423 | return 0; | |
424 | } | |
5d8e593d SS |
425 | |
426 | int config_parse_route_priority(const char *unit, | |
427 | const char *filename, | |
428 | unsigned line, | |
429 | const char *section, | |
430 | unsigned section_line, | |
431 | const char *lvalue, | |
432 | int ltype, | |
433 | const char *rvalue, | |
434 | void *data, | |
435 | void *userdata) { | |
436 | Network *network = userdata; | |
437 | _cleanup_route_free_ Route *n = NULL; | |
5d8e593d SS |
438 | int r; |
439 | ||
440 | assert(filename); | |
441 | assert(section); | |
442 | assert(lvalue); | |
443 | assert(rvalue); | |
444 | assert(data); | |
445 | ||
446 | r = route_new_static(network, section_line, &n); | |
447 | if (r < 0) | |
448 | return r; | |
449 | ||
450 | r = config_parse_unsigned(unit, filename, line, section, | |
451 | section_line, lvalue, ltype, | |
452 | rvalue, &n->metrics, userdata); | |
453 | if (r < 0) | |
454 | return r; | |
455 | ||
456 | n = NULL; | |
457 | ||
458 | return 0; | |
459 | } | |
769b56a3 TG |
460 | |
461 | int config_parse_route_scope(const char *unit, | |
462 | const char *filename, | |
463 | unsigned line, | |
464 | const char *section, | |
465 | unsigned section_line, | |
466 | const char *lvalue, | |
467 | int ltype, | |
468 | const char *rvalue, | |
469 | void *data, | |
470 | void *userdata) { | |
471 | Network *network = userdata; | |
472 | _cleanup_route_free_ Route *n = NULL; | |
473 | int r; | |
474 | ||
475 | assert(filename); | |
476 | assert(section); | |
477 | assert(lvalue); | |
478 | assert(rvalue); | |
479 | assert(data); | |
480 | ||
481 | r = route_new_static(network, section_line, &n); | |
482 | if (r < 0) | |
483 | return r; | |
484 | ||
485 | if (streq(rvalue, "host")) | |
486 | n->scope = RT_SCOPE_HOST; | |
487 | else if (streq(rvalue, "link")) | |
488 | n->scope = RT_SCOPE_LINK; | |
489 | else if (streq(rvalue, "global")) | |
490 | n->scope = RT_SCOPE_UNIVERSE; | |
491 | else { | |
12ca818f | 492 | log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue); |
769b56a3 TG |
493 | return 0; |
494 | } | |
495 | ||
496 | n = NULL; | |
497 | ||
498 | return 0; | |
499 | } |