1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "sd-netlink.h"
5 #include "alloc-util.h"
6 #include "conf-parser.h"
7 #include "ether-addr-util.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"
16 void neighbor_free(Neighbor
*neighbor
) {
20 if (neighbor
->network
) {
21 LIST_REMOVE(neighbors
, neighbor
->network
->neighbors
, neighbor
);
22 assert(neighbor
->network
->n_neighbors
> 0);
23 neighbor
->network
->n_neighbors
--;
25 if (neighbor
->section
)
26 hashmap_remove(neighbor
->network
->neighbors_by_section
, neighbor
->section
);
29 network_config_section_free(neighbor
->section
);
32 set_remove(neighbor
->link
->neighbors
, neighbor
);
33 set_remove(neighbor
->link
->neighbors_foreign
, neighbor
);
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
;
46 assert(!!filename
== (section_line
> 0));
49 r
= network_config_section_new(filename
, section_line
, &n
);
53 neighbor
= hashmap_get(network
->neighbors_by_section
, n
);
55 *ret
= TAKE_PTR(neighbor
);
61 neighbor
= new(Neighbor
, 1);
65 *neighbor
= (Neighbor
) {
70 LIST_APPEND(neighbors
, network
->neighbors
, neighbor
);
71 network
->n_neighbors
++;
74 neighbor
->section
= TAKE_PTR(n
);
76 r
= hashmap_ensure_allocated(&network
->neighbors_by_section
, &network_config_hash_ops
);
80 r
= hashmap_put(network
->neighbors_by_section
, neighbor
->section
, neighbor
);
85 *ret
= TAKE_PTR(neighbor
);
90 static int neighbor_configure_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
95 assert(link
->neighbor_messages
> 0);
97 link
->neighbor_messages
--;
99 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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_warning_errno(link
, r
, "Could not set neighbor, ignoring: %m");
107 if (link
->neighbor_messages
== 0) {
108 log_link_debug(link
, "Neighbors set");
109 link
->neighbors_configured
= true;
110 link_check_ready(link
);
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
;
122 assert(link
->ifindex
> 0);
123 assert(link
->manager
);
124 assert(link
->manager
->rtnl
);
126 r
= sd_rtnl_message_new_neigh(link
->manager
->rtnl
, &req
, RTM_NEWNEIGH
,
127 link
->ifindex
, neighbor
->family
);
129 return log_error_errno(r
, "Could not allocate RTM_NEWNEIGH message: %m");
131 r
= sd_rtnl_message_neigh_set_state(req
, NUD_PERMANENT
);
133 return log_error_errno(r
, "Could not set state: %m");
135 r
= sd_netlink_message_set_flags(req
, NLM_F_REQUEST
| NLM_F_CREATE
| NLM_F_REPLACE
);
137 return log_error_errno(r
, "Could not set flags: %m");
139 r
= sd_netlink_message_append_data(req
, NDA_LLADDR
, &neighbor
->lladdr
, neighbor
->lladdr_size
);
141 return log_error_errno(r
, "Could not append NDA_LLADDR attribute: %m");
143 r
= netlink_message_append_in_addr_union(req
, NDA_DST
, neighbor
->family
, &neighbor
->in_addr
);
145 return log_error_errno(r
, "Could not append NDA_DST attribute: %m");
147 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, callback
?: neighbor_configure_handler
,
148 link_netlink_destroy_callback
, link
);
150 return log_error_errno(r
, "Could not send rtnetlink message: %m");
152 link
->neighbor_messages
++;
155 r
= neighbor_add(link
, neighbor
->family
, &neighbor
->in_addr
, &neighbor
->lladdr
, neighbor
->lladdr_size
, NULL
);
157 return log_link_error_errno(link
, r
, "Could not add neighbor: %m");
162 static int neighbor_remove_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
168 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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_warning_errno(link
, r
, "Could not remove neighbor: %m");
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
;
185 assert(link
->ifindex
> 0);
186 assert(link
->manager
);
187 assert(link
->manager
->rtnl
);
189 r
= sd_rtnl_message_new_neigh(link
->manager
->rtnl
, &req
, RTM_DELNEIGH
,
190 link
->ifindex
, neighbor
->family
);
192 return log_error_errno(r
, "Could not allocate RTM_DELNEIGH message: %m");
194 r
= netlink_message_append_in_addr_union(req
, NDA_DST
, neighbor
->family
, &neighbor
->in_addr
);
196 return log_error_errno(r
, "Could not append NDA_DST attribute: %m");
198 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, callback
?: neighbor_remove_handler
,
199 link_netlink_destroy_callback
, link
);
201 return log_error_errno(r
, "Could not send rtnetlink message: %m");
208 static void neighbor_hash_func(const Neighbor
*neighbor
, struct siphash
*state
) {
211 siphash24_compress(&neighbor
->family
, sizeof(neighbor
->family
), state
);
213 switch (neighbor
->family
) {
216 /* Equality of neighbors are given by the pair (addr,lladdr) */
217 siphash24_compress(&neighbor
->in_addr
, FAMILY_ADDRESS_SIZE(neighbor
->family
), state
);
218 siphash24_compress(&neighbor
->lladdr
, neighbor
->lladdr_size
, state
);
221 /* treat any other address family as AF_UNSPEC */
226 static int neighbor_compare_func(const Neighbor
*a
, const Neighbor
*b
) {
229 r
= CMP(a
->family
, b
->family
);
233 r
= CMP(a
->lladdr_size
, b
->lladdr_size
);
240 r
= memcmp(&a
->in_addr
, &b
->in_addr
, FAMILY_ADDRESS_SIZE(a
->family
));
245 return memcmp(&a
->lladdr
, &b
->lladdr
, a
->lladdr_size
);
248 DEFINE_PRIVATE_HASH_OPS(neighbor_hash_ops
, Neighbor
, neighbor_hash_func
, neighbor_compare_func
);
250 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
;
257 neighbor
= (Neighbor
) {
261 .lladdr_size
= lladdr_size
,
264 existing
= set_get(link
->neighbors
, &neighbor
);
271 existing
= set_get(link
->neighbors_foreign
, &neighbor
);
281 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 _cleanup_(neighbor_freep
) Neighbor
*neighbor
= NULL
;
290 neighbor
= new(Neighbor
, 1);
294 *neighbor
= (Neighbor
) {
298 .lladdr_size
= lladdr_size
,
301 r
= set_ensure_allocated(neighbors
, &neighbor_hash_ops
);
305 r
= set_put(*neighbors
, neighbor
);
311 neighbor
->link
= link
;
321 int neighbor_add(Link
*link
, int family
, const union in_addr_union
*addr
, const union lladdr_union
*lladdr
, size_t lladdr_size
, Neighbor
**ret
) {
325 r
= neighbor_get(link
, family
, addr
, lladdr
, lladdr_size
, &neighbor
);
327 /* Neighbor doesn't exist, make a new one */
328 r
= neighbor_add_internal(link
, &link
->neighbors
, family
, addr
, lladdr
, lladdr_size
, &neighbor
);
332 /* Neighbor is foreign, claim it as recognized */
333 r
= set_ensure_allocated(&link
->neighbors
, &neighbor_hash_ops
);
337 r
= set_put(link
->neighbors
, neighbor
);
341 set_remove(link
->neighbors_foreign
, neighbor
);
343 /* Neighbor already exists */
352 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 return neighbor_add_internal(link
, &link
->neighbors_foreign
, family
, addr
, lladdr
, lladdr_size
, ret
);
356 bool neighbor_equal(const Neighbor
*n1
, const Neighbor
*n2
) {
363 return neighbor_compare_func(n1
, n2
) == 0;
366 int neighbor_section_verify(Neighbor
*neighbor
) {
367 if (section_is_invalid(neighbor
->section
))
370 if (neighbor
->family
== AF_UNSPEC
)
371 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
);
376 if (neighbor
->lladdr_size
== 0)
377 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
);
385 int config_parse_neighbor_address(
387 const char *filename
,
390 unsigned section_line
,
397 Network
*network
= userdata
;
398 _cleanup_(neighbor_free_or_set_invalidp
) Neighbor
*n
= NULL
;
407 r
= neighbor_new_static(network
, filename
, section_line
, &n
);
411 r
= in_addr_from_string_auto(rvalue
, &n
->family
, &n
->in_addr
);
413 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Neighbor Address is invalid, ignoring assignment: %s", rvalue
);
422 int config_parse_neighbor_lladdr(
424 const char *filename
,
427 unsigned section_line
,
434 Network
*network
= userdata
;
435 _cleanup_(neighbor_free_or_set_invalidp
) Neighbor
*n
= NULL
;
444 r
= neighbor_new_static(network
, filename
, section_line
, &n
);
448 r
= ether_addr_from_string(rvalue
, &n
->lladdr
.mac
);
450 n
->lladdr_size
= sizeof(n
->lladdr
.mac
);
452 r
= in_addr_from_string_auto(rvalue
, &family
, &n
->lladdr
.ip
);
454 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
455 "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s",
459 n
->lladdr_size
= family
== AF_INET
? sizeof(n
->lladdr
.ip
.in
) : sizeof(n
->lladdr
.ip
.in6
);
467 int config_parse_neighbor_hwaddr(
469 const char *filename
,
472 unsigned section_line
,
479 Network
*network
= userdata
;
480 _cleanup_(neighbor_free_or_set_invalidp
) Neighbor
*n
= NULL
;
489 r
= neighbor_new_static(network
, filename
, section_line
, &n
);
493 r
= ether_addr_from_string(rvalue
, &n
->lladdr
.mac
);
495 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
496 "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue
);
500 n
->lladdr_size
= sizeof(n
->lladdr
.mac
);