]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-route.c
networkd: split out networkd-link.h
[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 (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 }