Branch data 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 : : }
|