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_message_warning_errno(link
, m
, r
, "Could not set neighbor, ignoring");
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_link_error_errno(link
, r
, "Could not allocate RTM_NEWNEIGH message: %m");
131 r
= sd_rtnl_message_neigh_set_state(req
, NUD_PERMANENT
);
133 return log_link_error_errno(link
, 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_link_error_errno(link
, r
, "Could not set flags: %m");
139 r
= sd_netlink_message_append_data(req
, NDA_LLADDR
, &neighbor
->lladdr
, neighbor
->lladdr_size
);
141 return log_link_error_errno(link
, 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_link_error_errno(link
, 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_link_error_errno(link
, 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_message_warning_errno(link
, m
, r
, "Could not remove neighbor");
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_link_error_errno(link
, 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_link_error_errno(link
, 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_link_error_errno(link
, 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
);
212 siphash24_compress(&neighbor
->lladdr_size
, sizeof(neighbor
->lladdr_size
), state
);
214 switch (neighbor
->family
) {
217 /* Equality of neighbors are given by the pair (addr,lladdr) */
218 siphash24_compress(&neighbor
->in_addr
, FAMILY_ADDRESS_SIZE(neighbor
->family
), state
);
221 /* treat any other address family as AF_UNSPEC */
225 siphash24_compress(&neighbor
->lladdr
, neighbor
->lladdr_size
, state
);
228 static int neighbor_compare_func(const Neighbor
*a
, const Neighbor
*b
) {
231 r
= CMP(a
->family
, b
->family
);
235 r
= CMP(a
->lladdr_size
, b
->lladdr_size
);
242 r
= memcmp(&a
->in_addr
, &b
->in_addr
, FAMILY_ADDRESS_SIZE(a
->family
));
247 return memcmp(&a
->lladdr
, &b
->lladdr
, a
->lladdr_size
);
250 DEFINE_PRIVATE_HASH_OPS(neighbor_hash_ops
, Neighbor
, neighbor_hash_func
, neighbor_compare_func
);
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
;
259 neighbor
= (Neighbor
) {
263 .lladdr_size
= lladdr_size
,
266 existing
= set_get(link
->neighbors
, &neighbor
);
273 existing
= set_get(link
->neighbors_foreign
, &neighbor
);
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
;
292 neighbor
= new(Neighbor
, 1);
296 *neighbor
= (Neighbor
) {
300 .lladdr_size
= lladdr_size
,
303 r
= set_ensure_allocated(neighbors
, &neighbor_hash_ops
);
307 r
= set_put(*neighbors
, neighbor
);
313 neighbor
->link
= link
;
323 int neighbor_add(Link
*link
, int family
, const union in_addr_union
*addr
, const union lladdr_union
*lladdr
, size_t lladdr_size
, Neighbor
**ret
) {
327 r
= neighbor_get(link
, family
, addr
, lladdr
, lladdr_size
, &neighbor
);
329 /* Neighbor doesn't exist, make a new one */
330 r
= neighbor_add_internal(link
, &link
->neighbors
, family
, addr
, lladdr
, lladdr_size
, &neighbor
);
334 /* Neighbor is foreign, claim it as recognized */
335 r
= set_ensure_allocated(&link
->neighbors
, &neighbor_hash_ops
);
339 r
= set_put(link
->neighbors
, neighbor
);
343 set_remove(link
->neighbors_foreign
, neighbor
);
345 /* Neighbor already exists */
354 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
) {
355 return neighbor_add_internal(link
, &link
->neighbors_foreign
, family
, addr
, lladdr
, lladdr_size
, ret
);
358 bool neighbor_equal(const Neighbor
*n1
, const Neighbor
*n2
) {
365 return neighbor_compare_func(n1
, n2
) == 0;
368 int neighbor_section_verify(Neighbor
*neighbor
) {
369 if (section_is_invalid(neighbor
->section
))
372 if (neighbor
->family
== AF_UNSPEC
)
373 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
374 "%s: Neighbor section without Address= configured. "
375 "Ignoring [Neighbor] section from line %u.",
376 neighbor
->section
->filename
, neighbor
->section
->line
);
378 if (neighbor
->lladdr_size
== 0)
379 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
380 "%s: Neighbor section without LinkLayerAddress= configured. "
381 "Ignoring [Neighbor] section from line %u.",
382 neighbor
->section
->filename
, neighbor
->section
->line
);
387 int config_parse_neighbor_address(
389 const char *filename
,
392 unsigned section_line
,
399 Network
*network
= userdata
;
400 _cleanup_(neighbor_free_or_set_invalidp
) Neighbor
*n
= NULL
;
409 r
= neighbor_new_static(network
, filename
, section_line
, &n
);
413 r
= in_addr_from_string_auto(rvalue
, &n
->family
, &n
->in_addr
);
415 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Neighbor Address is invalid, ignoring assignment: %s", rvalue
);
424 int config_parse_neighbor_lladdr(
426 const char *filename
,
429 unsigned section_line
,
436 Network
*network
= userdata
;
437 _cleanup_(neighbor_free_or_set_invalidp
) Neighbor
*n
= NULL
;
446 r
= neighbor_new_static(network
, filename
, section_line
, &n
);
450 r
= ether_addr_from_string(rvalue
, &n
->lladdr
.mac
);
452 n
->lladdr_size
= sizeof(n
->lladdr
.mac
);
454 r
= in_addr_from_string_auto(rvalue
, &family
, &n
->lladdr
.ip
);
456 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
457 "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s",
461 n
->lladdr_size
= family
== AF_INET
? sizeof(n
->lladdr
.ip
.in
) : sizeof(n
->lladdr
.ip
.in6
);
469 int config_parse_neighbor_hwaddr(
471 const char *filename
,
474 unsigned section_line
,
481 Network
*network
= userdata
;
482 _cleanup_(neighbor_free_or_set_invalidp
) Neighbor
*n
= NULL
;
491 r
= neighbor_new_static(network
, filename
, section_line
, &n
);
495 r
= ether_addr_from_string(rvalue
, &n
->lladdr
.mac
);
497 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
498 "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue
);
502 n
->lladdr_size
= sizeof(n
->lladdr
.mac
);