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