]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-nexthop.c
Merge pull request #16143 from fbuihuu/fstab-generator-fix
[thirdparty/systemd.git] / src / network / networkd-nexthop.c
CommitLineData
c16c7808
SS
1/* SPDX-License-Identifier: LGPL-2.1+
2 * Copyright © 2019 VMware, Inc.
3 */
4
5#include <linux/nexthop.h>
6
7#include "alloc-util.h"
8#include "conf-parser.h"
9#include "in-addr-util.h"
10#include "netlink-util.h"
11#include "networkd-manager.h"
12#include "networkd-nexthop.h"
13#include "parse-util.h"
14#include "set.h"
15#include "string-util.h"
16#include "util.h"
17
18int nexthop_new(NextHop **ret) {
19 _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
20
21 nexthop = new(NextHop, 1);
22 if (!nexthop)
23 return -ENOMEM;
24
25 *nexthop = (NextHop) {
26 .family = AF_UNSPEC,
27 };
28
29 *ret = TAKE_PTR(nexthop);
30
31 return 0;
32}
33
34static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
35 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
36 _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
37 int r;
38
39 assert(network);
40 assert(ret);
41 assert(!!filename == (section_line > 0));
42
43 if (filename) {
44 r = network_config_section_new(filename, section_line, &n);
45 if (r < 0)
46 return r;
47
48 nexthop = hashmap_get(network->nexthops_by_section, n);
49 if (nexthop) {
50 *ret = TAKE_PTR(nexthop);
51
52 return 0;
53 }
54 }
55
56 r = nexthop_new(&nexthop);
57 if (r < 0)
58 return r;
59
60 nexthop->protocol = RTPROT_STATIC;
61 nexthop->network = network;
62 LIST_PREPEND(nexthops, network->static_nexthops, nexthop);
63 network->n_static_nexthops++;
64
65 if (filename) {
66 nexthop->section = TAKE_PTR(n);
67
68 r = hashmap_ensure_allocated(&network->nexthops_by_section, &network_config_hash_ops);
69 if (r < 0)
70 return r;
71
72 r = hashmap_put(network->nexthops_by_section, nexthop->section, nexthop);
73 if (r < 0)
74 return r;
75 }
76
77 *ret = TAKE_PTR(nexthop);
78
79 return 0;
80}
81
82void nexthop_free(NextHop *nexthop) {
83 if (!nexthop)
84 return;
85
86 if (nexthop->network) {
87 LIST_REMOVE(nexthops, nexthop->network->static_nexthops, nexthop);
88
89 assert(nexthop->network->n_static_nexthops > 0);
90 nexthop->network->n_static_nexthops--;
91
92 if (nexthop->section)
93 hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
94 }
95
96 network_config_section_free(nexthop->section);
97
98 if (nexthop->link) {
99 set_remove(nexthop->link->nexthops, nexthop);
100 set_remove(nexthop->link->nexthops_foreign, nexthop);
101 }
102
103 free(nexthop);
104}
105
106static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
107 assert(nexthop);
108
109 siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
110 siphash24_compress(&nexthop->oif, sizeof(nexthop->oif), state);
111 siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
112
113 switch (nexthop->family) {
114 case AF_INET:
115 case AF_INET6:
116 siphash24_compress(&nexthop->gw, FAMILY_ADDRESS_SIZE(nexthop->family), state);
117
118 break;
119 default:
120 /* treat any other address family as AF_UNSPEC */
121 break;
122 }
123}
124
125static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
126 int r;
127
128 r = CMP(a->id, b->id);
129 if (r != 0)
130 return r;
131
132 r = CMP(a->oif, b->oif);
133 if (r != 0)
134 return r;
135
136 r = CMP(a->family, b->family);
137 if (r != 0)
138 return r;
139
140 switch (a->family) {
141 case AF_INET:
142 case AF_INET6:
143
144 r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
145 if (r != 0)
146 return r;
147
148 return 0;
149 default:
150 /* treat any other address family as AF_UNSPEC */
151 return 0;
152 }
153}
154
155DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
156 nexthop_hash_ops,
157 NextHop,
158 nexthop_hash_func,
159 nexthop_compare_func,
160 nexthop_free);
161
162bool nexthop_equal(NextHop *r1, NextHop *r2) {
163 if (r1 == r2)
164 return true;
165
166 if (!r1 || !r2)
167 return false;
168
169 return nexthop_compare_func(r1, r2) == 0;
170}
171
172int nexthop_get(Link *link, NextHop *in, NextHop **ret) {
173 NextHop *existing;
174
175 assert(link);
176 assert(in);
177
178 existing = set_get(link->nexthops, in);
179 if (existing) {
180 if (ret)
181 *ret = existing;
182 return 1;
183 }
184
185 existing = set_get(link->nexthops_foreign, in);
186 if (existing) {
187 if (ret)
188 *ret = existing;
189 return 0;
190 }
191
192 return -ENOENT;
193}
194
195static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop **ret) {
196 _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
197 int r;
198
199 assert(link);
200 assert(nexthops);
201 assert(in);
202
203 r = nexthop_new(&nexthop);
204 if (r < 0)
205 return r;
206
207 nexthop->id = in->id;
208 nexthop->oif = in->oif;
209 nexthop->family = in->family;
210 nexthop->gw = in->gw;
211
de7fef4b 212 r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop);
c16c7808
SS
213 if (r < 0)
214 return r;
215 if (r == 0)
216 return -EEXIST;
217
218 nexthop->link = link;
219
220 if (ret)
221 *ret = nexthop;
222
223 nexthop = NULL;
224
225 return 0;
226}
227
228int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret) {
229 return nexthop_add_internal(link, &link->nexthops_foreign, in, ret);
230}
231
232int nexthop_add(Link *link, NextHop *in, NextHop **ret) {
233 NextHop *nexthop;
234 int r;
235
236 r = nexthop_get(link, in, &nexthop);
237 if (r == -ENOENT) {
238 /* NextHop does not exist, create a new one */
239 r = nexthop_add_internal(link, &link->nexthops, in, &nexthop);
240 if (r < 0)
241 return r;
242 } else if (r == 0) {
243 /* Take over a foreign nexthop */
de7fef4b 244 r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop);
c16c7808
SS
245 if (r < 0)
246 return r;
247
248 set_remove(link->nexthops_foreign, nexthop);
249 } else if (r == 1) {
250 /* NextHop exists, do nothing */
251 ;
252 } else
253 return r;
254
255 if (ret)
256 *ret = nexthop;
257
258 return 0;
259}
260
261static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
262 int r;
263
264 assert(m);
265 assert(link);
266 assert(link->ifname);
267
268 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
269 return 1;
270
271 r = sd_netlink_message_get_errno(m);
272 if (r < 0 && r != -ESRCH)
5ecb131d 273 log_link_message_warning_errno(link, m, r, "Could not drop nexthop, ignoring");
c16c7808
SS
274
275 return 1;
276}
277
278int nexthop_remove(NextHop *nexthop, Link *link,
279 link_netlink_message_handler_t callback) {
280
281 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
282 int r;
283
284 assert(link);
285 assert(link->manager);
286 assert(link->manager->rtnl);
287 assert(link->ifindex > 0);
288 assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
289
290 r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
291 RTM_DELNEXTHOP, nexthop->family,
292 nexthop->protocol);
293 if (r < 0)
294 return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m");
295
296 if (DEBUG_LOGGING) {
297 _cleanup_free_ char *gw = NULL;
298
299 if (!in_addr_is_null(nexthop->family, &nexthop->gw))
300 (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
301
302 log_link_debug(link, "Removing nexthop: gw: %s", strna(gw));
303 }
304
305 if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
306 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, nexthop->family, &nexthop->gw);
307 if (r < 0)
308 return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
309 }
310
311 r = netlink_call_async(link->manager->rtnl, NULL, req,
312 callback ?: nexthop_remove_handler,
313 link_netlink_destroy_callback, link);
314 if (r < 0)
315 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
316
317 link_ref(link);
318
319 return 0;
320}
321
322int nexthop_configure(
323 NextHop *nexthop,
324 Link *link,
325 link_netlink_message_handler_t callback) {
326 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
327 int r;
328
329 assert(link);
330 assert(link->manager);
331 assert(link->manager->rtnl);
332 assert(link->ifindex > 0);
333 assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
334 assert(callback);
335
336 if (DEBUG_LOGGING) {
337 _cleanup_free_ char *gw = NULL;
338
339 if (!in_addr_is_null(nexthop->family, &nexthop->gw))
340 (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
341
342 log_link_debug(link, "Configuring nexthop: gw: %s", strna(gw));
343 }
344
345 r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
346 RTM_NEWNEXTHOP, nexthop->family,
347 nexthop->protocol);
348 if (r < 0)
349 return log_link_error_errno(link, r, "Could not create RTM_NEWNEXTHOP message: %m");
350
351 r = sd_netlink_message_append_u32(req, NHA_ID, nexthop->id);
352 if (r < 0)
353 return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
354
355 r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex);
356 if (r < 0)
357 return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m");
358
359 if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
360 r = netlink_message_append_in_addr_union(req, NHA_GATEWAY, nexthop->family, &nexthop->gw);
361 if (r < 0)
362 return log_link_error_errno(link, r, "Could not append NHA_GATEWAY attribute: %m");
363
364 r = sd_rtnl_message_nexthop_set_family(req, nexthop->family);
365 if (r < 0)
366 return log_link_error_errno(link, r, "Could not set nexthop family: %m");
367 }
368
369 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
370 link_netlink_destroy_callback, link);
371 if (r < 0)
372 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
373
374 link_ref(link);
375
376 r = nexthop_add(link, nexthop, &nexthop);
377 if (r < 0)
378 return log_link_error_errno(link, r, "Could not add nexthop: %m");
379
380 return 1;
381}
382
383int nexthop_section_verify(NextHop *nh) {
384 if (section_is_invalid(nh->section))
385 return -EINVAL;
386
387 if (in_addr_is_null(nh->family, &nh->gw) < 0)
388 return -EINVAL;
389
390 return 0;
391}
392
393int config_parse_nexthop_id(
394 const char *unit,
395 const char *filename,
396 unsigned line,
397 const char *section,
398 unsigned section_line,
399 const char *lvalue,
400 int ltype,
401 const char *rvalue,
402 void *data,
403 void *userdata) {
404
405 _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
406 Network *network = userdata;
407 int r;
408
409 assert(filename);
410 assert(section);
411 assert(lvalue);
412 assert(rvalue);
413 assert(data);
414
415 r = nexthop_new_static(network, filename, section_line, &n);
416 if (r < 0)
417 return r;
418
419 r = safe_atou32(rvalue, &n->id);
420 if (r < 0) {
421 log_syntax(unit, LOG_ERR, filename, line, r,
422 "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue);
423 return 0;
424 }
425
426 TAKE_PTR(n);
427 return 0;
428}
429
430int config_parse_nexthop_gateway(
431 const char *unit,
432 const char *filename,
433 unsigned line,
434 const char *section,
435 unsigned section_line,
436 const char *lvalue,
437 int ltype,
438 const char *rvalue,
439 void *data,
440 void *userdata) {
441
442 _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
443 Network *network = userdata;
444 int r;
445
446 assert(filename);
447 assert(section);
448 assert(lvalue);
449 assert(rvalue);
450 assert(data);
451
452 r = nexthop_new_static(network, filename, section_line, &n);
453 if (r < 0)
454 return r;
455
456 r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
457 if (r < 0) {
458 log_syntax(unit, LOG_ERR, filename, line, r,
459 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
460 return 0;
461 }
462
463 TAKE_PTR(n);
464 return 0;
465}