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(const BridgeFDB
*fdb
, Link
*link
, link_netlink_message_handler_t callback
) {
125 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
130 assert(link
->manager
);
131 assert(link
->manager
->rtnl
);
134 /* create new RTM message */
135 r
= sd_rtnl_message_new_neigh(link
->manager
->rtnl
, &req
, RTM_NEWNEIGH
, link
->ifindex
, AF_BRIDGE
);
137 return log_link_error_errno(link
, r
, "Could not create RTM_NEWNEIGH message: %m");
139 r
= sd_rtnl_message_neigh_set_flags(req
, fdb
->ntf_flags
);
141 return log_link_error_errno(link
, r
, "Could not set neighbor flags: %m");
143 /* only NUD_PERMANENT state supported. */
144 r
= sd_rtnl_message_neigh_set_state(req
, NUD_NOARP
| NUD_PERMANENT
);
146 return log_link_error_errno(link
, r
, "Could not set neighbor state: %m");
148 r
= sd_netlink_message_append_data(req
, NDA_LLADDR
, &fdb
->mac_addr
, sizeof(fdb
->mac_addr
));
150 return log_link_error_errno(link
, r
, "Could not append NDA_LLADDR attribute: %m");
152 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
153 if (fdb
->vlan_id
> 0) {
154 r
= sd_netlink_message_append_u16(req
, NDA_VLAN
, fdb
->vlan_id
);
156 return log_link_error_errno(link
, r
, "Could not append NDA_VLAN attribute: %m");
159 if (fdb
->outgoing_ifindex
> 0) {
160 r
= sd_netlink_message_append_u32(req
, NDA_IFINDEX
, fdb
->outgoing_ifindex
);
162 return log_link_error_errno(link
, r
, "Could not append NDA_IFINDEX attribute: %m");
165 if (in_addr_is_set(fdb
->family
, &fdb
->destination_addr
)) {
166 r
= netlink_message_append_in_addr_union(req
, NDA_DST
, fdb
->family
, &fdb
->destination_addr
);
168 return log_link_error_errno(link
, r
, "Could not append NDA_DST attribute: %m");
171 if (fdb
->vni
<= VXLAN_VID_MAX
) {
172 r
= sd_netlink_message_append_u32(req
, NDA_VNI
, fdb
->vni
);
174 return log_link_error_errno(link
, r
, "Could not append NDA_VNI attribute: %m");
177 /* send message to the kernel to update its internal static MAC table. */
178 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, callback
,
179 link_netlink_destroy_callback
, link
);
181 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
188 int link_request_static_bridge_fdb(Link
*link
) {
193 assert(link
->network
);
195 link
->static_bridge_fdb_configured
= false;
197 HASHMAP_FOREACH(fdb
, link
->network
->bridge_fdb_entries_by_section
) {
198 r
= link_queue_request(link
, REQUEST_TYPE_BRIDGE_FDB
, fdb
, false,
199 &link
->static_bridge_fdb_messages
, bridge_fdb_configure_handler
, NULL
);
201 return log_link_error_errno(link
, r
, "Failed to request static bridge FDB entry: %m");
204 if (link
->static_bridge_fdb_messages
== 0) {
205 link
->static_bridge_fdb_configured
= true;
206 link_check_ready(link
);
208 log_link_debug(link
, "Setting bridge FDB entries");
209 link_set_state(link
, LINK_STATE_CONFIGURING
);
215 static bool bridge_fdb_is_ready_to_configure(BridgeFDB
*fdb
, Link
*link
) {
220 assert(link
->manager
);
222 if (!link_is_ready_to_configure(link
, false))
225 if (fdb
->outgoing_ifname
) {
226 if (link_get_by_name(link
->manager
, fdb
->outgoing_ifname
, &out
) < 0)
229 fdb
->outgoing_ifindex
= out
->ifindex
;
230 } else if (fdb
->outgoing_ifindex
> 0) {
231 if (link_get_by_index(link
->manager
, fdb
->outgoing_ifindex
, &out
) < 0)
234 if (out
&& !link_is_ready_to_configure(out
, false))
240 int request_process_bridge_fdb(Request
*req
) {
244 assert(req
->type
== REQUEST_TYPE_BRIDGE_FDB
);
246 if (!bridge_fdb_is_ready_to_configure(req
->fdb
, req
->link
))
249 return bridge_fdb_configure(req
->fdb
, req
->link
, req
->netlink_handler
);
252 void network_drop_invalid_bridge_fdb_entries(Network
*network
) {
257 HASHMAP_FOREACH(fdb
, network
->bridge_fdb_entries_by_section
)
258 if (section_is_invalid(fdb
->section
))
259 bridge_fdb_free(fdb
);
262 /* parse the HW address from config files. */
263 int config_parse_fdb_hwaddr(
265 const char *filename
,
268 unsigned section_line
,
275 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
276 Network
*network
= userdata
;
285 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
289 r
= parse_ether_addr(rvalue
, &fdb
->mac_addr
);
291 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Not a valid MAC address, ignoring assignment: %s", rvalue
);
299 /* parse the VLAN Id from config files. */
300 int config_parse_fdb_vlan_id(
302 const char *filename
,
305 unsigned section_line
,
312 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
313 Network
*network
= userdata
;
322 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
326 r
= config_parse_vlanid(unit
, filename
, line
, section
,
327 section_line
, lvalue
, ltype
,
328 rvalue
, &fdb
->vlan_id
, userdata
);
336 int config_parse_fdb_destination(
338 const char *filename
,
341 unsigned section_line
,
348 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
349 Network
*network
= userdata
;
358 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
362 r
= in_addr_from_string_auto(rvalue
, &fdb
->family
, &fdb
->destination_addr
);
364 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
365 "FDB destination IP address is invalid, ignoring assignment: %s",
374 int config_parse_fdb_vxlan_vni(
376 const char *filename
,
379 unsigned section_line
,
386 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
387 Network
*network
= userdata
;
397 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
401 r
= safe_atou32(rvalue
, &vni
);
403 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
404 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
409 if (vni
> VXLAN_VID_MAX
) {
410 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
411 "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
422 static const char* const ntf_flags_table
[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX
] = {
423 [NEIGHBOR_CACHE_ENTRY_FLAGS_USE
] = "use",
424 [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF
] = "self",
425 [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER
] = "master",
426 [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER
] = "router",
429 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(ntf_flags
, NeighborCacheEntryFlags
);
431 int config_parse_fdb_ntf_flags(
433 const char *filename
,
436 unsigned section_line
,
443 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
444 Network
*network
= userdata
;
445 NeighborCacheEntryFlags f
;
454 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
458 f
= ntf_flags_from_string(rvalue
);
460 log_syntax(unit
, LOG_WARNING
, filename
, line
, f
,
461 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
472 int config_parse_fdb_interface(
474 const char *filename
,
477 unsigned section_line
,
484 _cleanup_(bridge_fdb_free_or_set_invalidp
) BridgeFDB
*fdb
= NULL
;
485 Network
*network
= userdata
;
494 r
= bridge_fdb_new_static(network
, filename
, section_line
, &fdb
);
498 if (isempty(rvalue
)) {
499 fdb
->outgoing_ifname
= mfree(fdb
->outgoing_ifname
);
500 fdb
->outgoing_ifindex
= 0;
505 r
= parse_ifindex(rvalue
);
507 fdb
->outgoing_ifname
= mfree(fdb
->outgoing_ifname
);
508 fdb
->outgoing_ifindex
= r
;
513 if (!ifname_valid_full(rvalue
, IFNAME_VALID_ALTERNATIVE
)) {
514 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
515 "Invalid interface name in %s=, ignoring assignment: %s", lvalue
, rvalue
);
519 r
= free_and_strdup(&fdb
->outgoing_ifname
, rvalue
);
522 fdb
->outgoing_ifindex
= 0;