1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014 Intel Corporation. All rights reserved.
6 #include <net/ethernet.h>
9 #include "alloc-util.h"
11 #include "netlink-util.h"
12 #include "networkd-bridge-fdb.h"
13 #include "networkd-link.h"
14 #include "networkd-manager.h"
15 #include "networkd-network.h"
16 #include "networkd-queue.h"
17 #include "networkd-util.h"
18 #include "parse-util.h"
19 #include "string-table.h"
20 #include "vlan-util.h"
23 #define STATIC_BRIDGE_FDB_ENTRIES_PER_NETWORK_MAX 1024U
25 /* remove and FDB entry. */
26 BridgeFDB
*bridge_fdb_free(BridgeFDB
*fdb
) {
32 hashmap_remove(fdb
->network
->bridge_fdb_entries_by_section
, fdb
->section
);
35 network_config_section_free(fdb
->section
);
37 free(fdb
->outgoing_ifname
);
41 DEFINE_NETWORK_SECTION_FUNCTIONS(BridgeFDB
, bridge_fdb_free
);
43 /* create a new FDB entry or get an existing one. */
44 static int bridge_fdb_new_static(
47 unsigned section_line
,
50 _cleanup_(network_config_section_freep
) NetworkConfigSection
*n
= NULL
;
51 _cleanup_(bridge_fdb_freep
) BridgeFDB
*fdb
= NULL
;
57 assert(section_line
> 0);
59 r
= network_config_section_new(filename
, section_line
, &n
);
63 /* search entry in hashmap first. */
64 fdb
= hashmap_get(network
->bridge_fdb_entries_by_section
, n
);
70 if (hashmap_size(network
->bridge_fdb_entries_by_section
) >= STATIC_BRIDGE_FDB_ENTRIES_PER_NETWORK_MAX
)
73 /* allocate space for and FDB entry. */
74 fdb
= new(BridgeFDB
, 1);
78 /* init FDB structure. */
81 .section
= TAKE_PTR(n
),
82 .vni
= VXLAN_VID_MAX
+ 1,
83 .ntf_flags
= NEIGHBOR_CACHE_ENTRY_FLAGS_SELF
,
86 r
= hashmap_ensure_put(&network
->bridge_fdb_entries_by_section
, &network_config_hash_ops
, fdb
->section
, fdb
);
90 /* return allocated FDB structure. */
96 static int bridge_fdb_configure_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
100 assert(link
->static_bridge_fdb_messages
> 0);
102 link
->static_bridge_fdb_messages
--;
104 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
107 r
= sd_netlink_message_get_errno(m
);
108 if (r
< 0 && r
!= -EEXIST
) {
109 log_link_message_warning_errno(link
, m
, r
, "Could not add bridge FDB entry");
110 link_enter_failed(link
);
114 if (link
->static_bridge_fdb_messages
== 0) {
115 log_link_debug(link
, "Bridge FDB entries set");
116 link
->static_bridge_fdb_configured
= true;
117 link_check_ready(link
);
123 /* send a request to the kernel to add a FDB entry in its static MAC table. */
124 static int bridge_fdb_configure_message(const BridgeFDB
*fdb
, Link
*link
, sd_netlink_message
*req
) {
130 r
= sd_rtnl_message_neigh_set_flags(req
, fdb
->ntf_flags
);
134 /* only NUD_PERMANENT state supported. */
135 r
= sd_rtnl_message_neigh_set_state(req
, NUD_NOARP
| NUD_PERMANENT
);
139 r
= sd_netlink_message_append_data(req
, NDA_LLADDR
, &fdb
->mac_addr
, sizeof(fdb
->mac_addr
));
143 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
144 if (fdb
->vlan_id
> 0) {
145 r
= sd_netlink_message_append_u16(req
, NDA_VLAN
, fdb
->vlan_id
);
150 if (fdb
->outgoing_ifindex
> 0) {
151 r
= sd_netlink_message_append_u32(req
, NDA_IFINDEX
, fdb
->outgoing_ifindex
);
156 if (in_addr_is_set(fdb
->family
, &fdb
->destination_addr
)) {
157 r
= netlink_message_append_in_addr_union(req
, NDA_DST
, fdb
->family
, &fdb
->destination_addr
);
162 if (fdb
->vni
<= VXLAN_VID_MAX
) {
163 r
= sd_netlink_message_append_u32(req
, NDA_VNI
, fdb
->vni
);
171 int link_request_static_bridge_fdb(Link
*link
) {
176 assert(link
->network
);
178 link
->static_bridge_fdb_configured
= false;
180 HASHMAP_FOREACH(fdb
, link
->network
->bridge_fdb_entries_by_section
) {
181 r
= link_queue_request(link
, REQUEST_TYPE_BRIDGE_FDB
, fdb
, false,
182 &link
->static_bridge_fdb_messages
, bridge_fdb_configure_handler
, NULL
);
184 return log_link_error_errno(link
, r
, "Failed to request static bridge FDB entry: %m");
187 if (link
->static_bridge_fdb_messages
== 0) {
188 link
->static_bridge_fdb_configured
= true;
189 link_check_ready(link
);
191 log_link_debug(link
, "Setting bridge FDB entries");
192 link_set_state(link
, LINK_STATE_CONFIGURING
);
198 static bool bridge_fdb_is_ready_to_configure(BridgeFDB
*fdb
, Link
*link
) {
203 assert(link
->manager
);
205 if (!link_is_ready_to_configure(link
, false))
208 if (fdb
->outgoing_ifname
) {
209 if (link_get_by_name(link
->manager
, fdb
->outgoing_ifname
, &out
) < 0)
212 fdb
->outgoing_ifindex
= out
->ifindex
;
213 } else if (fdb
->outgoing_ifindex
> 0) {
214 if (link_get_by_index(link
->manager
, fdb
->outgoing_ifindex
, &out
) < 0)
217 if (out
&& !link_is_ready_to_configure(out
, false))
223 int request_process_bridge_fdb(Request
*req
) {
229 assert(req
->type
== REQUEST_TYPE_BRIDGE_FDB
);
230 assert_se(link
= req
->link
);
232 if (!bridge_fdb_is_ready_to_configure(req
->fdb
, link
))
235 /* create new RTM message */
236 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
237 r
= sd_rtnl_message_new_neigh(link
->manager
->rtnl
, &m
, RTM_NEWNEIGH
, link
->ifindex
, AF_BRIDGE
);
239 return log_link_error_errno(link
, r
, "Could not allocate netlink message: %m");
241 r
= bridge_fdb_configure_message(req
->fdb
, link
, m
);
243 return log_link_error_errno(link
, r
, "Could not create netlink message: %m");
245 /* send message to the kernel to update its internal static MAC table. */
246 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, m
, req
->netlink_handler
,
247 link_netlink_destroy_callback
, link
);
249 return log_link_error_errno(link
, r
, "Could not send netlink message: %m");
256 void network_drop_invalid_bridge_fdb_entries(Network
*network
) {
261 HASHMAP_FOREACH(fdb
, network
->bridge_fdb_entries_by_section
)
262 if (section_is_invalid(fdb
->section
))
263 bridge_fdb_free(fdb
);
266 /* parse the HW address from config files. */
267 int config_parse_fdb_hwaddr(
269 const char *filename
,
272 unsigned section_line
,
279 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
280 Network
*network
= userdata
;
289 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
293 r
= parse_ether_addr(rvalue
, &fdb
->mac_addr
);
295 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Not a valid MAC address, ignoring assignment: %s", rvalue
);
303 /* parse the VLAN Id from config files. */
304 int config_parse_fdb_vlan_id(
306 const char *filename
,
309 unsigned section_line
,
316 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
317 Network
*network
= userdata
;
326 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
330 r
= config_parse_vlanid(unit
, filename
, line
, section
,
331 section_line
, lvalue
, ltype
,
332 rvalue
, &fdb
->vlan_id
, userdata
);
340 int config_parse_fdb_destination(
342 const char *filename
,
345 unsigned section_line
,
352 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
353 Network
*network
= userdata
;
362 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
366 r
= in_addr_from_string_auto(rvalue
, &fdb
->family
, &fdb
->destination_addr
);
368 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
369 "FDB destination IP address is invalid, ignoring assignment: %s",
378 int config_parse_fdb_vxlan_vni(
380 const char *filename
,
383 unsigned section_line
,
390 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
391 Network
*network
= userdata
;
401 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
405 r
= safe_atou32(rvalue
, &vni
);
407 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
408 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
413 if (vni
> VXLAN_VID_MAX
) {
414 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
415 "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
426 static const char* const ntf_flags_table
[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX
] = {
427 [NEIGHBOR_CACHE_ENTRY_FLAGS_USE
] = "use",
428 [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF
] = "self",
429 [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER
] = "master",
430 [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER
] = "router",
433 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(ntf_flags
, NeighborCacheEntryFlags
);
435 int config_parse_fdb_ntf_flags(
437 const char *filename
,
440 unsigned section_line
,
447 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
448 Network
*network
= userdata
;
449 NeighborCacheEntryFlags f
;
458 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
462 f
= ntf_flags_from_string(rvalue
);
464 log_syntax(unit
, LOG_WARNING
, filename
, line
, f
,
465 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
476 int config_parse_fdb_interface(
478 const char *filename
,
481 unsigned section_line
,
488 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
489 Network
*network
= userdata
;
498 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
502 if (isempty(rvalue
)) {
503 fdb
->outgoing_ifname
= mfree(fdb
->outgoing_ifname
);
504 fdb
->outgoing_ifindex
= 0;
509 r
= parse_ifindex(rvalue
);
511 fdb
->outgoing_ifname
= mfree(fdb
->outgoing_ifname
);
512 fdb
->outgoing_ifindex
= r
;
517 if (!ifname_valid_full(rvalue
, IFNAME_VALID_ALTERNATIVE
)) {
518 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
519 "Invalid interface name in %s=, ignoring assignment: %s", lvalue
, rvalue
);
523 r
= free_and_strdup(&fdb
->outgoing_ifname
, rvalue
);
526 fdb
->outgoing_ifindex
= 0;