]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-neighbor.c
network: set key destructor in several hash_ops
[thirdparty/systemd.git] / src / network / networkd-neighbor.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "sd-netlink.h"
4
5 #include "alloc-util.h"
6 #include "conf-parser.h"
7 #include "ether-addr-util.h"
8 #include "hashmap.h"
9 #include "in-addr-util.h"
10 #include "netlink-util.h"
11 #include "networkd-link.h"
12 #include "networkd-manager.h"
13 #include "networkd-neighbor.h"
14 #include "set.h"
15
16 void neighbor_free(Neighbor *neighbor) {
17 if (!neighbor)
18 return;
19
20 if (neighbor->network) {
21 LIST_REMOVE(neighbors, neighbor->network->neighbors, neighbor);
22 assert(neighbor->network->n_neighbors > 0);
23 neighbor->network->n_neighbors--;
24
25 if (neighbor->section)
26 hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
27 }
28
29 network_config_section_free(neighbor->section);
30
31 if (neighbor->link) {
32 set_remove(neighbor->link->neighbors, neighbor);
33 set_remove(neighbor->link->neighbors_foreign, neighbor);
34 }
35
36 free(neighbor);
37 }
38
39 static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
40 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
41 _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
42 int r;
43
44 assert(network);
45 assert(ret);
46 assert(!!filename == (section_line > 0));
47
48 if (filename) {
49 r = network_config_section_new(filename, section_line, &n);
50 if (r < 0)
51 return r;
52
53 neighbor = hashmap_get(network->neighbors_by_section, n);
54 if (neighbor) {
55 *ret = TAKE_PTR(neighbor);
56
57 return 0;
58 }
59 }
60
61 neighbor = new(Neighbor, 1);
62 if (!neighbor)
63 return -ENOMEM;
64
65 *neighbor = (Neighbor) {
66 .network = network,
67 .family = AF_UNSPEC,
68 };
69
70 LIST_APPEND(neighbors, network->neighbors, neighbor);
71 network->n_neighbors++;
72
73 if (filename) {
74 neighbor->section = TAKE_PTR(n);
75
76 r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops);
77 if (r < 0)
78 return r;
79
80 r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor);
81 if (r < 0)
82 return r;
83 }
84
85 *ret = TAKE_PTR(neighbor);
86
87 return 0;
88 }
89
90 static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
91 int r;
92
93 assert(m);
94 assert(link);
95 assert(link->neighbor_messages > 0);
96
97 link->neighbor_messages--;
98
99 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
100 return 1;
101
102 r = sd_netlink_message_get_errno(m);
103 if (r < 0 && r != -EEXIST)
104 /* Neighbor may not exist yet. So, do not enter failed state here. */
105 log_link_message_warning_errno(link, m, r, "Could not set neighbor, ignoring");
106
107 if (link->neighbor_messages == 0) {
108 log_link_debug(link, "Neighbors set");
109 link->neighbors_configured = true;
110 link_check_ready(link);
111 }
112
113 return 1;
114 }
115
116 int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) {
117 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
118 int r;
119
120 assert(neighbor);
121 assert(link);
122 assert(link->ifindex > 0);
123 assert(link->manager);
124 assert(link->manager->rtnl);
125
126 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH,
127 link->ifindex, neighbor->family);
128 if (r < 0)
129 return log_link_error_errno(link, r, "Could not allocate RTM_NEWNEIGH message: %m");
130
131 r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT);
132 if (r < 0)
133 return log_link_error_errno(link, r, "Could not set state: %m");
134
135 r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE);
136 if (r < 0)
137 return log_link_error_errno(link, r, "Could not set flags: %m");
138
139 r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr, neighbor->lladdr_size);
140 if (r < 0)
141 return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m");
142
143 r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr);
144 if (r < 0)
145 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
146
147 r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_configure_handler,
148 link_netlink_destroy_callback, link);
149 if (r < 0)
150 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
151
152 link->neighbor_messages++;
153 link_ref(link);
154
155 r = neighbor_add(link, neighbor->family, &neighbor->in_addr, &neighbor->lladdr, neighbor->lladdr_size, NULL);
156 if (r < 0)
157 return log_link_error_errno(link, r, "Could not add neighbor: %m");
158
159 return 0;
160 }
161
162 static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
163 int r;
164
165 assert(m);
166 assert(link);
167
168 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
169 return 1;
170
171 r = sd_netlink_message_get_errno(m);
172 if (r < 0 && r != -ESRCH)
173 /* Neighbor may not exist because it already got deleted, ignore that. */
174 log_link_message_warning_errno(link, m, r, "Could not remove neighbor");
175
176 return 1;
177 }
178
179 int neighbor_remove(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) {
180 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
181 int r;
182
183 assert(neighbor);
184 assert(link);
185 assert(link->ifindex > 0);
186 assert(link->manager);
187 assert(link->manager->rtnl);
188
189 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_DELNEIGH,
190 link->ifindex, neighbor->family);
191 if (r < 0)
192 return log_link_error_errno(link, r, "Could not allocate RTM_DELNEIGH message: %m");
193
194 r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr);
195 if (r < 0)
196 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
197
198 r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_remove_handler,
199 link_netlink_destroy_callback, link);
200 if (r < 0)
201 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
202
203 link_ref(link);
204
205 return 0;
206 }
207
208 static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
209 assert(neighbor);
210
211 siphash24_compress(&neighbor->family, sizeof(neighbor->family), state);
212 siphash24_compress(&neighbor->lladdr_size, sizeof(neighbor->lladdr_size), state);
213
214 switch (neighbor->family) {
215 case AF_INET:
216 case AF_INET6:
217 /* Equality of neighbors are given by the pair (addr,lladdr) */
218 siphash24_compress(&neighbor->in_addr, FAMILY_ADDRESS_SIZE(neighbor->family), state);
219 break;
220 default:
221 /* treat any other address family as AF_UNSPEC */
222 break;
223 }
224
225 siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state);
226 }
227
228 static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
229 int r;
230
231 r = CMP(a->family, b->family);
232 if (r != 0)
233 return r;
234
235 r = CMP(a->lladdr_size, b->lladdr_size);
236 if (r != 0)
237 return r;
238
239 switch (a->family) {
240 case AF_INET:
241 case AF_INET6:
242 r = memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family));
243 if (r != 0)
244 return r;
245 }
246
247 return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size);
248 }
249
250 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free);
251
252 int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
253 Neighbor neighbor, *existing;
254
255 assert(link);
256 assert(addr);
257 assert(lladdr);
258
259 neighbor = (Neighbor) {
260 .family = family,
261 .in_addr = *addr,
262 .lladdr = *lladdr,
263 .lladdr_size = lladdr_size,
264 };
265
266 existing = set_get(link->neighbors, &neighbor);
267 if (existing) {
268 if (ret)
269 *ret = existing;
270 return 1;
271 }
272
273 existing = set_get(link->neighbors_foreign, &neighbor);
274 if (existing) {
275 if (ret)
276 *ret = existing;
277 return 0;
278 }
279
280 return -ENOENT;
281 }
282
283 static int neighbor_add_internal(Link *link, Set **neighbors, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
284 _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
285 int r;
286
287 assert(link);
288 assert(neighbors);
289 assert(addr);
290 assert(lladdr);
291
292 neighbor = new(Neighbor, 1);
293 if (!neighbor)
294 return -ENOMEM;
295
296 *neighbor = (Neighbor) {
297 .family = family,
298 .in_addr = *addr,
299 .lladdr = *lladdr,
300 .lladdr_size = lladdr_size,
301 };
302
303 r = set_ensure_put(neighbors, &neighbor_hash_ops, neighbor);
304 if (r < 0)
305 return r;
306 if (r == 0)
307 return -EEXIST;
308
309 neighbor->link = link;
310
311 if (ret)
312 *ret = neighbor;
313 TAKE_PTR(neighbor);
314
315 return 0;
316 }
317
318 int neighbor_add(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
319 Neighbor *neighbor;
320 int r;
321
322 r = neighbor_get(link, family, addr, lladdr, lladdr_size, &neighbor);
323 if (r == -ENOENT) {
324 /* Neighbor doesn't exist, make a new one */
325 r = neighbor_add_internal(link, &link->neighbors, family, addr, lladdr, lladdr_size, &neighbor);
326 if (r < 0)
327 return r;
328 } else if (r == 0) {
329 /* Neighbor is foreign, claim it as recognized */
330 r = set_ensure_put(&link->neighbors, &neighbor_hash_ops, neighbor);
331 if (r < 0)
332 return r;
333
334 set_remove(link->neighbors_foreign, neighbor);
335 } else if (r == 1) {
336 /* Neighbor already exists */
337 } else
338 return r;
339
340 if (ret)
341 *ret = neighbor;
342 return 0;
343 }
344
345 int neighbor_add_foreign(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
346 return neighbor_add_internal(link, &link->neighbors_foreign, family, addr, lladdr, lladdr_size, ret);
347 }
348
349 bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) {
350 if (n1 == n2)
351 return true;
352
353 if (!n1 || !n2)
354 return false;
355
356 return neighbor_compare_func(n1, n2) == 0;
357 }
358
359 int neighbor_section_verify(Neighbor *neighbor) {
360 if (section_is_invalid(neighbor->section))
361 return -EINVAL;
362
363 if (neighbor->family == AF_UNSPEC)
364 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
365 "%s: Neighbor section without Address= configured. "
366 "Ignoring [Neighbor] section from line %u.",
367 neighbor->section->filename, neighbor->section->line);
368
369 if (neighbor->lladdr_size == 0)
370 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
371 "%s: Neighbor section without LinkLayerAddress= configured. "
372 "Ignoring [Neighbor] section from line %u.",
373 neighbor->section->filename, neighbor->section->line);
374
375 return 0;
376 }
377
378 int config_parse_neighbor_address(
379 const char *unit,
380 const char *filename,
381 unsigned line,
382 const char *section,
383 unsigned section_line,
384 const char *lvalue,
385 int ltype,
386 const char *rvalue,
387 void *data,
388 void *userdata) {
389
390 Network *network = userdata;
391 _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
392 int r;
393
394 assert(filename);
395 assert(section);
396 assert(lvalue);
397 assert(rvalue);
398 assert(data);
399
400 r = neighbor_new_static(network, filename, section_line, &n);
401 if (r < 0)
402 return log_oom();
403
404 r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
405 if (r < 0) {
406 log_syntax(unit, LOG_WARNING, filename, line, r,
407 "Neighbor Address is invalid, ignoring assignment: %s", rvalue);
408 return 0;
409 }
410
411 TAKE_PTR(n);
412
413 return 0;
414 }
415
416 int config_parse_neighbor_lladdr(
417 const char *unit,
418 const char *filename,
419 unsigned line,
420 const char *section,
421 unsigned section_line,
422 const char *lvalue,
423 int ltype,
424 const char *rvalue,
425 void *data,
426 void *userdata) {
427
428 Network *network = userdata;
429 _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
430 int family, r;
431
432 assert(filename);
433 assert(section);
434 assert(lvalue);
435 assert(rvalue);
436 assert(data);
437
438 r = neighbor_new_static(network, filename, section_line, &n);
439 if (r < 0)
440 return log_oom();
441
442 r = ether_addr_from_string(rvalue, &n->lladdr.mac);
443 if (r >= 0)
444 n->lladdr_size = sizeof(n->lladdr.mac);
445 else {
446 r = in_addr_from_string_auto(rvalue, &family, &n->lladdr.ip);
447 if (r < 0) {
448 log_syntax(unit, LOG_WARNING, filename, line, r,
449 "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s",
450 rvalue);
451 return 0;
452 }
453 n->lladdr_size = family == AF_INET ? sizeof(n->lladdr.ip.in) : sizeof(n->lladdr.ip.in6);
454 }
455
456 TAKE_PTR(n);
457
458 return 0;
459 }
460
461 int config_parse_neighbor_hwaddr(
462 const char *unit,
463 const char *filename,
464 unsigned line,
465 const char *section,
466 unsigned section_line,
467 const char *lvalue,
468 int ltype,
469 const char *rvalue,
470 void *data,
471 void *userdata) {
472
473 Network *network = userdata;
474 _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
475 int r;
476
477 assert(filename);
478 assert(section);
479 assert(lvalue);
480 assert(rvalue);
481 assert(data);
482
483 r = neighbor_new_static(network, filename, section_line, &n);
484 if (r < 0)
485 return log_oom();
486
487 r = ether_addr_from_string(rvalue, &n->lladdr.mac);
488 if (r < 0) {
489 log_syntax(unit, LOG_WARNING, filename, line, r,
490 "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue);
491 return 0;
492 }
493
494 n->lladdr_size = sizeof(n->lladdr.mac);
495 TAKE_PTR(n);
496
497 return 0;
498 }