]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
e4a71bf3 | 2 | |
e4a71bf3 | 3 | #include "alloc-util.h" |
e4a71bf3 | 4 | #include "hashmap.h" |
e4a71bf3 WKI |
5 | #include "netlink-util.h" |
6 | #include "networkd-link.h" | |
7 | #include "networkd-manager.h" | |
8 | #include "networkd-neighbor.h" | |
1939ebeb | 9 | #include "networkd-network.h" |
40ca350e | 10 | #include "networkd-queue.h" |
d1bdafd2 | 11 | #include "set.h" |
e4a71bf3 | 12 | |
2a75f23b YW |
13 | static Neighbor* neighbor_detach_impl(Neighbor *neighbor) { |
14 | assert(neighbor); | |
15 | assert(!neighbor->link || !neighbor->network); | |
e4a71bf3 WKI |
16 | |
17 | if (neighbor->network) { | |
b0ba6938 | 18 | assert(neighbor->section); |
aa9626ee | 19 | ordered_hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section); |
2a75f23b YW |
20 | neighbor->network = NULL; |
21 | return neighbor; | |
d1bdafd2 WKI |
22 | } |
23 | ||
2a75f23b | 24 | if (neighbor->link) { |
d1bdafd2 | 25 | set_remove(neighbor->link->neighbors, neighbor); |
2a75f23b YW |
26 | neighbor->link = NULL; |
27 | return neighbor; | |
28 | } | |
29 | ||
30 | return NULL; | |
31 | } | |
e4a71bf3 | 32 | |
2a75f23b YW |
33 | static void neighbor_detach(Neighbor *neighbor) { |
34 | neighbor_unref(neighbor_detach_impl(neighbor)); | |
35 | } | |
36 | ||
37 | static Neighbor* neighbor_free(Neighbor *neighbor) { | |
38 | if (!neighbor) | |
39 | return NULL; | |
40 | ||
41 | neighbor_detach_impl(neighbor); | |
42 | ||
43 | config_section_free(neighbor->section); | |
64753f35 | 44 | return mfree(neighbor); |
e4a71bf3 WKI |
45 | } |
46 | ||
2a75f23b YW |
47 | DEFINE_TRIVIAL_REF_UNREF_FUNC(Neighbor, neighbor, neighbor_free); |
48 | DEFINE_SECTION_CLEANUP_FUNCTIONS(Neighbor, neighbor_unref); | |
49 | ||
50 | static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state); | |
51 | static int neighbor_compare_func(const Neighbor *a, const Neighbor *b); | |
52 | ||
53 | DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( | |
54 | neighbor_hash_ops_detach, | |
55 | Neighbor, | |
56 | neighbor_hash_func, | |
57 | neighbor_compare_func, | |
58 | neighbor_detach); | |
59 | ||
60 | DEFINE_PRIVATE_HASH_OPS( | |
61 | neighbor_hash_ops, | |
62 | Neighbor, | |
63 | neighbor_hash_func, | |
64 | neighbor_compare_func); | |
65 | ||
66 | DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( | |
67 | neighbor_section_hash_ops, | |
68 | ConfigSection, | |
69 | config_section_hash_func, | |
70 | config_section_compare_func, | |
71 | Neighbor, | |
72 | neighbor_detach); | |
73 | ||
74 | static int neighbor_new(Neighbor **ret) { | |
75 | Neighbor *neighbor; | |
76 | ||
77 | assert(ret); | |
78 | ||
79 | neighbor = new(Neighbor, 1); | |
80 | if (!neighbor) | |
81 | return -ENOMEM; | |
82 | ||
83 | *neighbor = (Neighbor) { | |
84 | .n_ref = 1, | |
85 | }; | |
86 | ||
87 | *ret = TAKE_PTR(neighbor); | |
88 | return 0; | |
89 | } | |
0d6e933e | 90 | |
e4a71bf3 | 91 | static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) { |
307fe3cd | 92 | _cleanup_(config_section_freep) ConfigSection *n = NULL; |
2a75f23b | 93 | _cleanup_(neighbor_unrefp) Neighbor *neighbor = NULL; |
e4a71bf3 WKI |
94 | int r; |
95 | ||
96 | assert(network); | |
97 | assert(ret); | |
b0ba6938 YW |
98 | assert(filename); |
99 | assert(section_line > 0); | |
e4a71bf3 | 100 | |
307fe3cd | 101 | r = config_section_new(filename, section_line, &n); |
b0ba6938 YW |
102 | if (r < 0) |
103 | return r; | |
e4a71bf3 | 104 | |
aa9626ee | 105 | neighbor = ordered_hashmap_get(network->neighbors_by_section, n); |
b0ba6938 YW |
106 | if (neighbor) { |
107 | *ret = TAKE_PTR(neighbor); | |
108 | return 0; | |
e4a71bf3 WKI |
109 | } |
110 | ||
2a75f23b YW |
111 | r = neighbor_new(&neighbor); |
112 | if (r < 0) | |
113 | return r; | |
e4a71bf3 | 114 | |
2a75f23b YW |
115 | neighbor->network = network; |
116 | neighbor->section = TAKE_PTR(n); | |
117 | neighbor->source = NETWORK_CONFIG_SOURCE_STATIC; | |
e4a71bf3 | 118 | |
2a75f23b | 119 | r = ordered_hashmap_ensure_put(&network->neighbors_by_section, &neighbor_section_hash_ops, neighbor->section, neighbor); |
b0ba6938 YW |
120 | if (r < 0) |
121 | return r; | |
e4a71bf3 WKI |
122 | |
123 | *ret = TAKE_PTR(neighbor); | |
e4a71bf3 WKI |
124 | return 0; |
125 | } | |
126 | ||
193c4af9 | 127 | static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) { |
2a75f23b | 128 | _cleanup_(neighbor_unrefp) Neighbor *dest = NULL; |
193c4af9 YW |
129 | |
130 | assert(neighbor); | |
131 | assert(ret); | |
132 | ||
133 | dest = newdup(Neighbor, neighbor, 1); | |
134 | if (!dest) | |
135 | return -ENOMEM; | |
136 | ||
2a75f23b YW |
137 | /* Clear the reference counter and all pointers */ |
138 | dest->n_ref = 1; | |
193c4af9 YW |
139 | dest->link = NULL; |
140 | dest->network = NULL; | |
141 | dest->section = NULL; | |
142 | ||
143 | *ret = TAKE_PTR(dest); | |
144 | return 0; | |
145 | } | |
146 | ||
09d09207 | 147 | static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) { |
d1bdafd2 WKI |
148 | assert(neighbor); |
149 | ||
c01a5c05 | 150 | siphash24_compress_typesafe(neighbor->family, state); |
d1bdafd2 | 151 | |
aa9626ee | 152 | if (!IN_SET(neighbor->family, AF_INET, AF_INET6)) |
d1bdafd2 | 153 | /* treat any other address family as AF_UNSPEC */ |
aa9626ee | 154 | return; |
a811fb8b | 155 | |
aa9626ee YW |
156 | /* Equality of neighbors are given by the destination address. |
157 | * See neigh_lookup() in the kernel. */ | |
c01a5c05 | 158 | in_addr_hash_func(&neighbor->in_addr, neighbor->family, state); |
d1bdafd2 WKI |
159 | } |
160 | ||
09d09207 | 161 | static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { |
d1bdafd2 WKI |
162 | int r; |
163 | ||
164 | r = CMP(a->family, b->family); | |
165 | if (r != 0) | |
166 | return r; | |
167 | ||
aa9626ee YW |
168 | if (!IN_SET(a->family, AF_INET, AF_INET6)) |
169 | /* treat any other address family as AF_UNSPEC */ | |
170 | return 0; | |
d1bdafd2 | 171 | |
aa9626ee | 172 | return memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family)); |
d1bdafd2 WKI |
173 | } |
174 | ||
5d098f5d YW |
175 | static int neighbor_get_request(Link *link, const Neighbor *neighbor, Request **ret) { |
176 | Request *req; | |
177 | ||
178 | assert(link); | |
179 | assert(link->manager); | |
180 | assert(neighbor); | |
181 | ||
182 | req = ordered_set_get( | |
183 | link->manager->request_queue, | |
184 | &(Request) { | |
185 | .link = link, | |
186 | .type = REQUEST_TYPE_NEIGHBOR, | |
187 | .userdata = (void*) neighbor, | |
188 | .hash_func = (hash_func_t) neighbor_hash_func, | |
189 | .compare_func = (compare_func_t) neighbor_compare_func, | |
190 | }); | |
191 | if (!req) | |
192 | return -ENOENT; | |
193 | ||
194 | if (ret) | |
195 | *ret = req; | |
196 | return 0; | |
197 | } | |
198 | ||
c902fa08 | 199 | int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) { |
19f8cffc | 200 | Neighbor *existing; |
d1bdafd2 WKI |
201 | |
202 | assert(link); | |
19f8cffc | 203 | assert(in); |
d1bdafd2 | 204 | |
19f8cffc | 205 | existing = set_get(link->neighbors, in); |
193c4af9 YW |
206 | if (!existing) |
207 | return -ENOENT; | |
d1bdafd2 | 208 | |
193c4af9 YW |
209 | if (ret) |
210 | *ret = existing; | |
211 | return 0; | |
d1bdafd2 WKI |
212 | } |
213 | ||
2a75f23b | 214 | static int neighbor_attach(Link *link, Neighbor *neighbor) { |
d1bdafd2 WKI |
215 | int r; |
216 | ||
217 | assert(link); | |
193c4af9 | 218 | assert(neighbor); |
2a75f23b | 219 | assert(!neighbor->link); |
d1bdafd2 | 220 | |
2a75f23b | 221 | r = set_ensure_put(&link->neighbors, &neighbor_hash_ops_detach, neighbor); |
d1bdafd2 WKI |
222 | if (r < 0) |
223 | return r; | |
224 | if (r == 0) | |
225 | return -EEXIST; | |
226 | ||
227 | neighbor->link = link; | |
2a75f23b | 228 | neighbor_ref(neighbor); |
d1bdafd2 WKI |
229 | return 0; |
230 | } | |
231 | ||
2775e1c5 | 232 | static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const Link *link) { |
84dbb3fd | 233 | _cleanup_free_ char *state = NULL; |
2775e1c5 YW |
234 | |
235 | assert(neighbor); | |
236 | assert(str); | |
237 | ||
238 | if (!DEBUG_LOGGING) | |
239 | return; | |
240 | ||
193c4af9 | 241 | (void) network_config_state_to_string_alloc(neighbor->state, &state); |
2775e1c5 YW |
242 | |
243 | log_link_debug(link, | |
193c4af9 YW |
244 | "%s %s neighbor (%s): lladdr: %s, dst: %s", |
245 | str, strna(network_config_source_to_string(neighbor->source)), strna(state), | |
84dbb3fd ZJS |
246 | HW_ADDR_TO_STR(&neighbor->ll_addr), |
247 | IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr)); | |
2775e1c5 | 248 | } |
193c4af9 | 249 | |
54ff39f7 | 250 | static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) { |
a79a8d16 | 251 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
fceee7cc YW |
252 | int r; |
253 | ||
254 | assert(neighbor); | |
255 | assert(link); | |
256 | assert(link->ifindex > 0); | |
257 | assert(link->manager); | |
258 | assert(link->manager->rtnl); | |
54ff39f7 | 259 | assert(req); |
fceee7cc | 260 | |
2775e1c5 YW |
261 | log_neighbor_debug(neighbor, "Configuring", link); |
262 | ||
a79a8d16 | 263 | r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, |
9be0b3ab | 264 | link->ifindex, neighbor->family); |
fceee7cc | 265 | if (r < 0) |
a79a8d16 | 266 | return r; |
fceee7cc | 267 | |
754252f9 YW |
268 | r = sd_rtnl_message_neigh_set_state(m, NUD_PERMANENT); |
269 | if (r < 0) | |
270 | return r; | |
271 | ||
272 | r = netlink_message_append_hw_addr(m, NDA_LLADDR, &neighbor->ll_addr); | |
273 | if (r < 0) | |
274 | return r; | |
275 | ||
276 | r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->family, &neighbor->in_addr); | |
fceee7cc | 277 | if (r < 0) |
a79a8d16 | 278 | return r; |
fceee7cc | 279 | |
80d62d4f | 280 | return request_call_netlink_async(link->manager->rtnl, m, req); |
fceee7cc YW |
281 | } |
282 | ||
09d09207 | 283 | static int neighbor_process_request(Request *req, Link *link, Neighbor *neighbor) { |
5d098f5d | 284 | Neighbor *existing; |
8bed7c55 YW |
285 | int r; |
286 | ||
287 | assert(req); | |
ff51134c YW |
288 | assert(link); |
289 | assert(neighbor); | |
8bed7c55 YW |
290 | |
291 | if (!link_is_ready_to_configure(link, false)) | |
292 | return 0; | |
293 | ||
54ff39f7 | 294 | r = neighbor_configure(neighbor, link, req); |
8bed7c55 YW |
295 | if (r < 0) |
296 | return log_link_warning_errno(link, r, "Failed to configure neighbor: %m"); | |
297 | ||
298 | neighbor_enter_configuring(neighbor); | |
5d098f5d YW |
299 | if (neighbor_get(link, neighbor, &existing) >= 0) |
300 | neighbor_enter_configuring(existing); | |
301 | ||
8bed7c55 YW |
302 | return 1; |
303 | } | |
304 | ||
80d62d4f | 305 | static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Neighbor *neighbor) { |
7575e1f4 YW |
306 | int r; |
307 | ||
308 | assert(m); | |
309 | assert(link); | |
7575e1f4 YW |
310 | |
311 | r = sd_netlink_message_get_errno(m); | |
40ca350e YW |
312 | if (r < 0 && r != -EEXIST) { |
313 | log_link_message_warning_errno(link, m, r, "Could not set neighbor"); | |
314 | link_enter_failed(link); | |
315 | return 1; | |
316 | } | |
7575e1f4 | 317 | |
40ca350e | 318 | if (link->static_neighbor_messages == 0) { |
7575e1f4 | 319 | log_link_debug(link, "Neighbors set"); |
40ca350e | 320 | link->static_neighbors_configured = true; |
7575e1f4 YW |
321 | link_check_ready(link); |
322 | } | |
323 | ||
324 | return 1; | |
325 | } | |
326 | ||
80d62d4f | 327 | static int link_request_neighbor(Link *link, const Neighbor *neighbor) { |
2a75f23b | 328 | _cleanup_(neighbor_unrefp) Neighbor *tmp = NULL; |
5d098f5d | 329 | Neighbor *existing = NULL; |
193c4af9 YW |
330 | int r; |
331 | ||
40ca350e YW |
332 | assert(link); |
333 | assert(neighbor); | |
193c4af9 YW |
334 | assert(neighbor->source != NETWORK_CONFIG_SOURCE_FOREIGN); |
335 | ||
bbeceaf2 YW |
336 | if (neighbor->ll_addr.length != link->hw_addr.length) { |
337 | log_link_debug(link, | |
da59599d | 338 | "The link layer address length (%zu) for neighbor %s does not match with " |
bbeceaf2 YW |
339 | "the hardware address length (%zu), ignoring the setting.", |
340 | neighbor->ll_addr.length, | |
341 | IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr), | |
342 | link->hw_addr.length); | |
343 | return 0; | |
344 | } | |
345 | ||
5d098f5d YW |
346 | r = neighbor_dup(neighbor, &tmp); |
347 | if (r < 0) | |
348 | return r; | |
193c4af9 | 349 | |
5d098f5d YW |
350 | if (neighbor_get(link, neighbor, &existing) >= 0) |
351 | /* Copy state for logging below. */ | |
352 | tmp->state = existing->state; | |
193c4af9 | 353 | |
5d098f5d | 354 | log_neighbor_debug(tmp, "Requesting", link); |
09d09207 | 355 | r = link_queue_request_safe(link, REQUEST_TYPE_NEIGHBOR, |
5d098f5d | 356 | tmp, |
2a75f23b | 357 | neighbor_unref, |
09d09207 YW |
358 | neighbor_hash_func, |
359 | neighbor_compare_func, | |
360 | neighbor_process_request, | |
361 | &link->static_neighbor_messages, | |
362 | static_neighbor_configure_handler, | |
363 | NULL); | |
193c4af9 YW |
364 | if (r <= 0) |
365 | return r; | |
366 | ||
5d098f5d YW |
367 | neighbor_enter_requesting(tmp); |
368 | if (existing) | |
369 | neighbor_enter_requesting(existing); | |
370 | ||
371 | TAKE_PTR(tmp); | |
193c4af9 | 372 | return 1; |
40ca350e YW |
373 | } |
374 | ||
375 | int link_request_static_neighbors(Link *link) { | |
58f1fe9a YW |
376 | Neighbor *neighbor; |
377 | int r; | |
378 | ||
379 | assert(link); | |
380 | assert(link->network); | |
381 | assert(link->state != _LINK_STATE_INVALID); | |
382 | ||
40ca350e | 383 | link->static_neighbors_configured = false; |
58f1fe9a | 384 | |
aa9626ee | 385 | ORDERED_HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) { |
80d62d4f | 386 | r = link_request_neighbor(link, neighbor); |
58f1fe9a | 387 | if (r < 0) |
40ca350e | 388 | return log_link_warning_errno(link, r, "Could not request neighbor: %m"); |
58f1fe9a YW |
389 | } |
390 | ||
40ca350e YW |
391 | if (link->static_neighbor_messages == 0) { |
392 | link->static_neighbors_configured = true; | |
58f1fe9a YW |
393 | link_check_ready(link); |
394 | } else { | |
40ca350e | 395 | log_link_debug(link, "Requesting neighbors"); |
58f1fe9a YW |
396 | link_set_state(link, LINK_STATE_CONFIGURING); |
397 | } | |
398 | ||
399 | return 0; | |
400 | } | |
401 | ||
84c86ae1 | 402 | static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) { |
fceee7cc YW |
403 | int r; |
404 | ||
405 | assert(m); | |
84c86ae1 | 406 | assert(rreq); |
fceee7cc | 407 | |
84c86ae1 YW |
408 | Link *link = ASSERT_PTR(rreq->link); |
409 | Neighbor *neighbor = ASSERT_PTR(rreq->userdata); | |
410 | ||
411 | if (link->state == LINK_STATE_LINGER) | |
412 | return 0; | |
fceee7cc YW |
413 | |
414 | r = sd_netlink_message_get_errno(m); | |
84c86ae1 | 415 | if (r < 0) { |
fceee7cc | 416 | /* Neighbor may not exist because it already got deleted, ignore that. */ |
84c86ae1 YW |
417 | log_link_message_full_errno(link, m, |
418 | (r == -ESRCH || !neighbor->link) ? LOG_DEBUG : LOG_WARNING, | |
419 | r, "Could not remove neighbor"); | |
420 | ||
421 | if (neighbor->link) { | |
422 | /* If the neighbor cannot be removed, then assume the neighbor is already removed. */ | |
423 | log_neighbor_debug(neighbor, "Forgetting", link); | |
424 | ||
425 | Request *req; | |
426 | if (neighbor_get_request(link, neighbor, &req) >= 0) | |
427 | neighbor_enter_removed(req->userdata); | |
428 | ||
429 | neighbor_detach(neighbor); | |
430 | } | |
431 | } | |
fceee7cc YW |
432 | |
433 | return 1; | |
434 | } | |
435 | ||
c902fa08 | 436 | int neighbor_remove(Neighbor *neighbor, Link *link) { |
754252f9 | 437 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
fceee7cc YW |
438 | int r; |
439 | ||
440 | assert(neighbor); | |
c902fa08 YW |
441 | assert(link); |
442 | assert(link->manager); | |
443 | assert(link->manager->rtnl); | |
fceee7cc | 444 | |
fe0acbf7 YW |
445 | /* If the neighbor is remembered, then use the remembered object. */ |
446 | (void) neighbor_get(link, neighbor, &neighbor); | |
447 | ||
2775e1c5 YW |
448 | log_neighbor_debug(neighbor, "Removing", link); |
449 | ||
754252f9 | 450 | r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_DELNEIGH, |
9be0b3ab | 451 | link->ifindex, neighbor->family); |
fceee7cc YW |
452 | if (r < 0) |
453 | return log_link_error_errno(link, r, "Could not allocate RTM_DELNEIGH message: %m"); | |
454 | ||
754252f9 | 455 | r = netlink_message_append_in_addr_union(m, NDA_DST, neighbor->family, &neighbor->in_addr); |
fceee7cc YW |
456 | if (r < 0) |
457 | return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); | |
458 | ||
84c86ae1 | 459 | r = link_remove_request_add(link, neighbor, neighbor, link->manager->rtnl, m, neighbor_remove_handler); |
fceee7cc | 460 | if (r < 0) |
84c86ae1 | 461 | return log_link_error_errno(link, r, "Could not queue rtnetlink message: %m"); |
fceee7cc | 462 | |
193c4af9 | 463 | neighbor_enter_removing(neighbor); |
fceee7cc YW |
464 | return 0; |
465 | } | |
466 | ||
193c4af9 YW |
467 | int link_drop_foreign_neighbors(Link *link) { |
468 | Neighbor *neighbor; | |
1339b950 | 469 | int r = 0; |
59048336 YW |
470 | |
471 | assert(link); | |
193c4af9 | 472 | assert(link->network); |
59048336 | 473 | |
193c4af9 YW |
474 | /* First, mark all neighbors. */ |
475 | SET_FOREACH(neighbor, link->neighbors) { | |
476 | /* Do not remove neighbors we configured. */ | |
477 | if (neighbor->source != NETWORK_CONFIG_SOURCE_FOREIGN) | |
478 | continue; | |
59048336 | 479 | |
193c4af9 YW |
480 | /* Ignore neighbors not assigned yet or already removing. */ |
481 | if (!neighbor_exists(neighbor)) | |
482 | continue; | |
59048336 | 483 | |
193c4af9 YW |
484 | neighbor_mark(neighbor); |
485 | } | |
59048336 | 486 | |
193c4af9 | 487 | /* Next, unmark requested neighbors. They will be configured later. */ |
aa9626ee | 488 | ORDERED_HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) { |
193c4af9 | 489 | Neighbor *existing; |
59048336 | 490 | |
193c4af9 YW |
491 | if (neighbor_get(link, neighbor, &existing) >= 0) |
492 | neighbor_unmark(existing); | |
493 | } | |
59048336 | 494 | |
193c4af9 YW |
495 | SET_FOREACH(neighbor, link->neighbors) { |
496 | if (!neighbor_is_marked(neighbor)) | |
497 | continue; | |
59048336 | 498 | |
c902fa08 | 499 | RET_GATHER(r, neighbor_remove(neighbor, link)); |
193c4af9 YW |
500 | } |
501 | ||
502 | return r; | |
59048336 YW |
503 | } |
504 | ||
a0e99a37 | 505 | int link_drop_managed_neighbors(Link *link) { |
59048336 | 506 | Neighbor *neighbor; |
1339b950 | 507 | int r = 0; |
59048336 YW |
508 | |
509 | assert(link); | |
510 | ||
511 | SET_FOREACH(neighbor, link->neighbors) { | |
a0e99a37 YW |
512 | /* Do not touch nexthops managed by kernel or other tools. */ |
513 | if (neighbor->source == NETWORK_CONFIG_SOURCE_FOREIGN) | |
514 | continue; | |
515 | ||
193c4af9 YW |
516 | /* Ignore neighbors not assigned yet or already removing. */ |
517 | if (!neighbor_exists(neighbor)) | |
518 | continue; | |
519 | ||
c902fa08 | 520 | RET_GATHER(r, neighbor_remove(neighbor, link)); |
59048336 YW |
521 | } |
522 | ||
523 | return r; | |
524 | } | |
525 | ||
b4564f4e YW |
526 | void link_foreignize_neighbors(Link *link) { |
527 | Neighbor *neighbor; | |
528 | ||
529 | assert(link); | |
530 | ||
531 | SET_FOREACH(neighbor, link->neighbors) | |
532 | neighbor->source = NETWORK_CONFIG_SOURCE_FOREIGN; | |
533 | } | |
534 | ||
eab052d2 | 535 | int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { |
2a75f23b | 536 | _cleanup_(neighbor_unrefp) Neighbor *tmp = NULL; |
eab052d2 | 537 | Neighbor *neighbor = NULL; |
5d098f5d | 538 | Request *req = NULL; |
eab052d2 | 539 | uint16_t type, state; |
5d098f5d | 540 | bool is_new = false; |
19f8cffc YW |
541 | int ifindex, r; |
542 | Link *link; | |
eab052d2 YW |
543 | |
544 | assert(rtnl); | |
545 | assert(message); | |
546 | assert(m); | |
547 | ||
548 | if (sd_netlink_message_is_error(message)) { | |
549 | r = sd_netlink_message_get_errno(message); | |
550 | if (r < 0) | |
551 | log_message_warning_errno(message, r, "rtnl: failed to receive neighbor message, ignoring"); | |
552 | ||
553 | return 0; | |
554 | } | |
555 | ||
556 | r = sd_netlink_message_get_type(message, &type); | |
557 | if (r < 0) { | |
558 | log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); | |
559 | return 0; | |
560 | } else if (!IN_SET(type, RTM_NEWNEIGH, RTM_DELNEIGH)) { | |
561 | log_warning("rtnl: received unexpected message type %u when processing neighbor, ignoring.", type); | |
562 | return 0; | |
563 | } | |
564 | ||
565 | r = sd_rtnl_message_neigh_get_state(message, &state); | |
566 | if (r < 0) { | |
19f8cffc | 567 | log_warning_errno(r, "rtnl: received neighbor message with invalid state, ignoring: %m"); |
eab052d2 | 568 | return 0; |
18e530b0 YW |
569 | } else if (!FLAGS_SET(state, NUD_PERMANENT)) |
570 | /* Currently, we are interested in only static neighbors. */ | |
eab052d2 | 571 | return 0; |
eab052d2 YW |
572 | |
573 | r = sd_rtnl_message_neigh_get_ifindex(message, &ifindex); | |
574 | if (r < 0) { | |
575 | log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m"); | |
576 | return 0; | |
577 | } else if (ifindex <= 0) { | |
578 | log_warning("rtnl: received neighbor message with invalid ifindex %d, ignoring.", ifindex); | |
579 | return 0; | |
580 | } | |
581 | ||
6eab614d | 582 | r = link_get_by_index(m, ifindex, &link); |
18e530b0 | 583 | if (r < 0) |
27a21339 YW |
584 | /* when enumerating we might be out of sync, but we will get the neighbor again. Also, |
585 | * kernel sends messages about neighbors after a link is removed. So, just ignore it. */ | |
eab052d2 | 586 | return 0; |
eab052d2 | 587 | |
2a75f23b YW |
588 | r = neighbor_new(&tmp); |
589 | if (r < 0) | |
be26893c | 590 | return log_oom(); |
19f8cffc | 591 | |
5d098f5d | 592 | /* First, retrieve the fundamental information about the neighbor. */ |
19f8cffc | 593 | r = sd_rtnl_message_neigh_get_family(message, &tmp->family); |
eab052d2 YW |
594 | if (r < 0) { |
595 | log_link_warning(link, "rtnl: received neighbor message without family, ignoring."); | |
596 | return 0; | |
18e530b0 YW |
597 | } |
598 | if (tmp->family == AF_BRIDGE) /* Currently, we do not support it. */ | |
599 | return 0; | |
600 | if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { | |
19f8cffc | 601 | log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family); |
eab052d2 YW |
602 | return 0; |
603 | } | |
604 | ||
19f8cffc YW |
605 | r = netlink_message_read_in_addr_union(message, NDA_DST, tmp->family, &tmp->in_addr); |
606 | if (r < 0) { | |
607 | log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m"); | |
608 | return 0; | |
eab052d2 YW |
609 | } |
610 | ||
5d098f5d | 611 | /* Then, find the managed Neighbor and Request objects corresponding to the netlink notification. */ |
19f8cffc | 612 | (void) neighbor_get(link, tmp, &neighbor); |
5d098f5d | 613 | (void) neighbor_get_request(link, tmp, &req); |
eab052d2 | 614 | |
5d098f5d | 615 | if (type == RTM_DELNEIGH) { |
193c4af9 YW |
616 | if (neighbor) { |
617 | neighbor_enter_removed(neighbor); | |
5d098f5d | 618 | log_neighbor_debug(neighbor, "Forgetting removed", link); |
2a75f23b | 619 | neighbor_detach(neighbor); |
193c4af9 YW |
620 | } else |
621 | log_neighbor_debug(tmp, "Kernel removed unknown", link); | |
eab052d2 | 622 | |
5d098f5d YW |
623 | if (req) |
624 | neighbor_enter_removed(req->userdata); | |
eab052d2 | 625 | |
5d098f5d YW |
626 | return 0; |
627 | } | |
628 | ||
629 | /* If we did not know the neighbor, then save it. */ | |
630 | if (!neighbor) { | |
2a75f23b | 631 | r = neighbor_attach(link, tmp); |
5d098f5d | 632 | if (r < 0) { |
e924dc59 | 633 | log_link_warning_errno(link, r, "Failed to save received neighbor, ignoring: %m"); |
5d098f5d YW |
634 | return 0; |
635 | } | |
2a75f23b | 636 | neighbor = tmp; |
5d098f5d | 637 | is_new = true; |
eab052d2 YW |
638 | } |
639 | ||
5d098f5d YW |
640 | /* Also update information that cannot be obtained through netlink notification. */ |
641 | if (req && req->waiting_reply) { | |
642 | Neighbor *n = ASSERT_PTR(req->userdata); | |
643 | ||
644 | neighbor->source = n->source; | |
645 | } | |
646 | ||
647 | /* Then, update miscellaneous info. */ | |
648 | r = netlink_message_read_hw_addr(message, NDA_LLADDR, &neighbor->ll_addr); | |
649 | if (r < 0 && r != -ENODATA) | |
650 | log_link_debug_errno(link, r, "rtnl: received neighbor message without valid link layer address, ignoring: %m"); | |
651 | ||
652 | neighbor_enter_configured(neighbor); | |
653 | if (req) | |
654 | neighbor_enter_configured(req->userdata); | |
655 | ||
656 | log_neighbor_debug(neighbor, is_new ? "Remembering" : "Received remembered", link); | |
eab052d2 YW |
657 | return 1; |
658 | } | |
659 | ||
78ada14f | 660 | static int neighbor_section_verify(Neighbor *neighbor) { |
044d4b40 YW |
661 | if (section_is_invalid(neighbor->section)) |
662 | return -EINVAL; | |
663 | ||
664 | if (neighbor->family == AF_UNSPEC) | |
665 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
666 | "%s: Neighbor section without Address= configured. " | |
667 | "Ignoring [Neighbor] section from line %u.", | |
668 | neighbor->section->filename, neighbor->section->line); | |
669 | ||
4d4d7910 YW |
670 | if (neighbor->family == AF_INET6 && !socket_ipv6_is_supported()) |
671 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
672 | "%s: Neighbor section with an IPv6 destination address configured, " | |
673 | "but the kernel does not support IPv6. " | |
674 | "Ignoring [Neighbor] section from line %u.", | |
675 | neighbor->section->filename, neighbor->section->line); | |
676 | ||
17193d76 | 677 | if (neighbor->ll_addr.length == 0) |
044d4b40 YW |
678 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), |
679 | "%s: Neighbor section without LinkLayerAddress= configured. " | |
680 | "Ignoring [Neighbor] section from line %u.", | |
681 | neighbor->section->filename, neighbor->section->line); | |
682 | ||
683 | return 0; | |
684 | } | |
685 | ||
aa9626ee YW |
686 | int network_drop_invalid_neighbors(Network *network) { |
687 | _cleanup_set_free_ Set *neighbors = NULL; | |
78ada14f | 688 | Neighbor *neighbor; |
aa9626ee | 689 | int r; |
78ada14f YW |
690 | |
691 | assert(network); | |
692 | ||
aa9626ee YW |
693 | ORDERED_HASHMAP_FOREACH(neighbor, network->neighbors_by_section) { |
694 | Neighbor *dup; | |
695 | ||
696 | if (neighbor_section_verify(neighbor) < 0) { | |
2a75f23b | 697 | /* Drop invalid [Neighbor] sections. Note that neighbor_detach() will drop the |
aa9626ee | 698 | * neighbor from neighbors_by_section. */ |
2a75f23b | 699 | neighbor_detach(neighbor); |
aa9626ee YW |
700 | continue; |
701 | } | |
702 | ||
703 | /* Always use the setting specified later. So, remove the previously assigned setting. */ | |
704 | dup = set_remove(neighbors, neighbor); | |
705 | if (dup) { | |
706 | log_warning("%s: Duplicated neighbor settings for %s is specified at line %u and %u, " | |
47ac844e | 707 | "dropping the neighbor setting specified at line %u.", |
aa9626ee YW |
708 | dup->section->filename, |
709 | IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr), | |
710 | neighbor->section->line, | |
711 | dup->section->line, dup->section->line); | |
2a75f23b YW |
712 | /* neighbor_detach() will drop the neighbor from neighbors_by_section. */ |
713 | neighbor_detach(dup); | |
aa9626ee YW |
714 | } |
715 | ||
2a75f23b YW |
716 | /* Use neighbor_hash_ops, instead of neighbor_hash_ops_detach. Otherwise, the Neighbor objects |
717 | * will be detached. */ | |
aa9626ee YW |
718 | r = set_ensure_put(&neighbors, &neighbor_hash_ops, neighbor); |
719 | if (r < 0) | |
720 | return log_oom(); | |
721 | assert(r > 0); | |
722 | } | |
723 | ||
724 | return 0; | |
78ada14f YW |
725 | } |
726 | ||
727 | ||
b956364d YW |
728 | int config_parse_neighbor_address( |
729 | const char *unit, | |
730 | const char *filename, | |
731 | unsigned line, | |
732 | const char *section, | |
733 | unsigned section_line, | |
734 | const char *lvalue, | |
735 | int ltype, | |
736 | const char *rvalue, | |
737 | void *data, | |
738 | void *userdata) { | |
e4a71bf3 | 739 | |
2a75f23b | 740 | _cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL; |
99534007 | 741 | Network *network = ASSERT_PTR(userdata); |
e4a71bf3 WKI |
742 | int r; |
743 | ||
744 | assert(filename); | |
745 | assert(section); | |
746 | assert(lvalue); | |
747 | assert(rvalue); | |
e4a71bf3 WKI |
748 | |
749 | r = neighbor_new_static(network, filename, section_line, &n); | |
750 | if (r < 0) | |
d96edb2c | 751 | return log_oom(); |
e4a71bf3 | 752 | |
13b7b8bd YW |
753 | if (isempty(rvalue)) { |
754 | n->family = AF_UNSPEC; | |
755 | n->in_addr = IN_ADDR_NULL; | |
756 | TAKE_PTR(n); | |
757 | return 0; | |
758 | } | |
759 | ||
e4a71bf3 WKI |
760 | r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr); |
761 | if (r < 0) { | |
d96edb2c YW |
762 | log_syntax(unit, LOG_WARNING, filename, line, r, |
763 | "Neighbor Address is invalid, ignoring assignment: %s", rvalue); | |
e4a71bf3 WKI |
764 | return 0; |
765 | } | |
766 | ||
767 | TAKE_PTR(n); | |
e4a71bf3 WKI |
768 | return 0; |
769 | } | |
770 | ||
b956364d YW |
771 | int config_parse_neighbor_lladdr( |
772 | const char *unit, | |
773 | const char *filename, | |
774 | unsigned line, | |
775 | const char *section, | |
776 | unsigned section_line, | |
777 | const char *lvalue, | |
778 | int ltype, | |
779 | const char *rvalue, | |
780 | void *data, | |
781 | void *userdata) { | |
e4a71bf3 | 782 | |
2a75f23b | 783 | _cleanup_(neighbor_unref_or_set_invalidp) Neighbor *n = NULL; |
99534007 | 784 | Network *network = ASSERT_PTR(userdata); |
b956364d YW |
785 | int r; |
786 | ||
787 | assert(filename); | |
788 | assert(section); | |
789 | assert(lvalue); | |
790 | assert(rvalue); | |
b956364d YW |
791 | |
792 | r = neighbor_new_static(network, filename, section_line, &n); | |
793 | if (r < 0) | |
d96edb2c | 794 | return log_oom(); |
b956364d | 795 | |
13b7b8bd YW |
796 | if (isempty(rvalue)) { |
797 | n->ll_addr = HW_ADDR_NULL; | |
798 | TAKE_PTR(n); | |
799 | return 0; | |
800 | } | |
801 | ||
17193d76 | 802 | r = parse_hw_addr(rvalue, &n->ll_addr); |
e4a71bf3 | 803 | if (r < 0) { |
d96edb2c | 804 | log_syntax(unit, LOG_WARNING, filename, line, r, |
17193d76 YW |
805 | "Neighbor %s= is invalid, ignoring assignment: %s", |
806 | lvalue, rvalue); | |
e4a71bf3 WKI |
807 | return 0; |
808 | } | |
809 | ||
e4a71bf3 | 810 | TAKE_PTR(n); |
e4a71bf3 WKI |
811 | return 0; |
812 | } |