1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include "netlink-util.h"
6 #include "networkd-manager.h"
7 #include "networkd-mdb.h"
8 #include "string-util.h"
11 #define STATIC_MDB_ENTRIES_PER_NETWORK_MAX 1024U
13 /* create a new MDB entry or get an existing one. */
14 static int mdb_entry_new_static(
17 unsigned section_line
,
20 _cleanup_(network_config_section_freep
) NetworkConfigSection
*n
= NULL
;
21 _cleanup_(mdb_entry_freep
) MdbEntry
*mdb_entry
= NULL
;
26 assert(!!filename
== (section_line
> 0));
28 /* search entry in hashmap first. */
30 r
= network_config_section_new(filename
, section_line
, &n
);
34 mdb_entry
= hashmap_get(network
->mdb_entries_by_section
, n
);
36 *ret
= TAKE_PTR(mdb_entry
);
41 if (network
->n_static_mdb_entries
>= STATIC_MDB_ENTRIES_PER_NETWORK_MAX
)
44 /* allocate space for an MDB entry. */
45 mdb_entry
= new(MdbEntry
, 1);
49 /* init MDB structure. */
50 *mdb_entry
= (MdbEntry
) {
54 LIST_PREPEND(static_mdb_entries
, network
->static_mdb_entries
, mdb_entry
);
55 network
->n_static_mdb_entries
++;
58 mdb_entry
->section
= TAKE_PTR(n
);
60 r
= hashmap_ensure_allocated(&network
->mdb_entries_by_section
, &network_config_hash_ops
);
64 r
= hashmap_put(network
->mdb_entries_by_section
, mdb_entry
->section
, mdb_entry
);
69 /* return allocated MDB structure. */
70 *ret
= TAKE_PTR(mdb_entry
);
75 /* remove and MDB entry. */
76 MdbEntry
*mdb_entry_free(MdbEntry
*mdb_entry
) {
80 if (mdb_entry
->network
) {
81 LIST_REMOVE(static_mdb_entries
, mdb_entry
->network
->static_mdb_entries
, mdb_entry
);
82 assert(mdb_entry
->network
->n_static_mdb_entries
> 0);
83 mdb_entry
->network
->n_static_mdb_entries
--;
85 if (mdb_entry
->section
)
86 hashmap_remove(mdb_entry
->network
->mdb_entries_by_section
, mdb_entry
->section
);
89 network_config_section_free(mdb_entry
->section
);
91 return mfree(mdb_entry
);
94 static int set_mdb_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
98 assert(link
->bridge_mdb_messages
> 0);
100 link
->bridge_mdb_messages
--;
102 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
105 r
= sd_netlink_message_get_errno(m
);
106 if (r
== -EINVAL
&& streq_ptr(link
->kind
, "bridge") && (!link
->network
|| !link
->network
->bridge
)) {
107 /* To configure bridge MDB entries on bridge master, 1bc844ee0faa1b92e3ede00bdd948021c78d7088 (v5.4) is required. */
108 if (!link
->manager
->bridge_mdb_on_master_not_supported
) {
109 log_link_warning_errno(link
, r
, "Kernel seems not to support configuring bridge MDB entries on bridge master, ignoring: %m");
110 link
->manager
->bridge_mdb_on_master_not_supported
= true;
112 } else if (r
< 0 && r
!= -EEXIST
) {
113 log_link_message_warning_errno(link
, m
, r
, "Could not add MDB entry");
114 link_enter_failed(link
);
118 if (link
->bridge_mdb_messages
== 0) {
119 link
->bridge_mdb_configured
= true;
120 link_check_ready(link
);
126 static int link_get_bridge_master_ifindex(Link
*link
) {
129 if (link
->network
&& link
->network
->bridge
)
130 return link
->network
->bridge
->ifindex
;
132 if (streq_ptr(link
->kind
, "bridge"))
133 return link
->ifindex
;
138 /* send a request to the kernel to add an MDB entry */
139 static int mdb_entry_configure(Link
*link
, MdbEntry
*mdb_entry
) {
140 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
141 struct br_mdb_entry entry
;
145 assert(link
->network
);
146 assert(link
->manager
);
150 _cleanup_free_
char *a
= NULL
;
152 (void) in_addr_to_string(mdb_entry
->family
, &mdb_entry
->group_addr
, &a
);
153 log_link_debug(link
, "Configuring bridge MDB entry: MulticastGroupAddress=%s, VLANId=%u",
154 strna(a
), mdb_entry
->vlan_id
);
157 master
= link_get_bridge_master_ifindex(link
);
159 return log_link_error_errno(link
, SYNTHETIC_ERRNO(EINVAL
), "Invalid bridge master ifindex %i", master
);
161 entry
= (struct br_mdb_entry
) {
162 /* If MDB entry is added on bridge master, then the state must be MDB_TEMPORARY.
163 * See br_mdb_add_group() in net/bridge/br_mdb.c of kernel. */
164 .state
= master
== link
->ifindex
? MDB_TEMPORARY
: MDB_PERMANENT
,
165 .ifindex
= link
->ifindex
,
166 .vid
= mdb_entry
->vlan_id
,
169 /* create new RTM message */
170 r
= sd_rtnl_message_new_mdb(link
->manager
->rtnl
, &req
, RTM_NEWMDB
, master
);
172 return log_link_error_errno(link
, r
, "Could not create RTM_NEWMDB message: %m");
174 switch (mdb_entry
->family
) {
176 entry
.addr
.u
.ip4
= mdb_entry
->group_addr
.in
.s_addr
;
177 entry
.addr
.proto
= htobe16(ETH_P_IP
);
181 entry
.addr
.u
.ip6
= mdb_entry
->group_addr
.in6
;
182 entry
.addr
.proto
= htobe16(ETH_P_IPV6
);
186 assert_not_reached("Invalid address family");
189 r
= sd_netlink_message_append_data(req
, MDBA_SET_ENTRY
, &entry
, sizeof(entry
));
191 return log_link_error_errno(link
, r
, "Could not append MDBA_SET_ENTRY attribute: %m");
193 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, set_mdb_handler
,
194 link_netlink_destroy_callback
, link
);
196 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
203 int link_set_bridge_mdb(Link
*link
) {
208 assert(link
->manager
);
210 link
->bridge_mdb_configured
= false;
215 if (LIST_IS_EMPTY(link
->network
->static_mdb_entries
))
218 if (!link_has_carrier(link
))
219 return log_link_debug(link
, "Link does not have carrier yet, setting MDB entries later.");
221 if (link
->network
->bridge
) {
224 r
= link_get(link
->manager
, link
->network
->bridge
->ifindex
, &master
);
226 return log_link_error_errno(link
, r
, "Failed to get Link object for Bridge=%s", link
->network
->bridge
->ifname
);
228 if (!link_has_carrier(master
))
229 return log_link_debug(link
, "Bridge interface %s does not have carrier yet, setting MDB entries later.", link
->network
->bridge
->ifname
);
231 } else if (!streq_ptr(link
->kind
, "bridge")) {
232 log_link_warning(link
, "Link is neither a bridge master nor a bridge port, ignoring [BridgeMDB] sections.");
234 } else if (link
->manager
->bridge_mdb_on_master_not_supported
) {
235 log_link_debug(link
, "Kernel seems not to support configuring bridge MDB entries on bridge master, ignoring [BridgeMDB] sections.");
239 LIST_FOREACH(static_mdb_entries
, mdb_entry
, link
->network
->static_mdb_entries
) {
240 r
= mdb_entry_configure(link
, mdb_entry
);
242 return log_link_error_errno(link
, r
, "Failed to add MDB entry to multicast group database: %m");
244 link
->bridge_mdb_messages
++;
248 if (link
->bridge_mdb_messages
== 0) {
249 link
->bridge_mdb_configured
= true;
250 link_check_ready(link
);
256 /* parse the VLAN Id from config files. */
257 int config_parse_mdb_vlan_id(
259 const char *filename
,
262 unsigned section_line
,
269 _cleanup_(mdb_entry_free_or_set_invalidp
) MdbEntry
*mdb_entry
= NULL
;
270 Network
*network
= userdata
;
279 r
= mdb_entry_new_static(network
, filename
, section_line
, &mdb_entry
);
283 r
= config_parse_vlanid(unit
, filename
, line
, section
,
284 section_line
, lvalue
, ltype
,
285 rvalue
, &mdb_entry
->vlan_id
, userdata
);
294 /* parse the multicast group from config files. */
295 int config_parse_mdb_group_address(
297 const char *filename
,
300 unsigned section_line
,
307 _cleanup_(mdb_entry_free_or_set_invalidp
) MdbEntry
*mdb_entry
= NULL
;
308 Network
*network
= userdata
;
317 r
= mdb_entry_new_static(network
, filename
, section_line
, &mdb_entry
);
321 r
= in_addr_from_string_auto(rvalue
, &mdb_entry
->family
, &mdb_entry
->group_addr
);
323 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Cannot parse multicast group address: %m");
332 int mdb_entry_verify(MdbEntry
*mdb_entry
) {
333 if (section_is_invalid(mdb_entry
->section
))
336 if (mdb_entry
->family
== AF_UNSPEC
)
337 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
338 "%s: [BridgeMDB] section without MulticastGroupAddress= field configured. "
339 "Ignoring [BridgeMDB] section from line %u.",
340 mdb_entry
->section
->filename
, mdb_entry
->section
->line
);
342 if (!in_addr_is_multicast(mdb_entry
->family
, &mdb_entry
->group_addr
))
343 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
344 "%s: MulticastGroupAddress= is not a multicast address. "
345 "Ignoring [BridgeMDB] section from line %u.",
346 mdb_entry
->section
->filename
, mdb_entry
->section
->line
);
348 if (mdb_entry
->family
== AF_INET
) {
349 if (in4_addr_is_local_multicast(&mdb_entry
->group_addr
.in
))
350 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
351 "%s: MulticastGroupAddress= is a local multicast address. "
352 "Ignoring [BridgeMDB] section from line %u.",
353 mdb_entry
->section
->filename
, mdb_entry
->section
->line
);
355 if (in6_addr_is_link_local_all_nodes(&mdb_entry
->group_addr
.in6
))
356 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
357 "%s: MulticastGroupAddress= is the multicast all nodes address. "
358 "Ignoring [BridgeMDB] section from line %u.",
359 mdb_entry
->section
->filename
, mdb_entry
->section
->line
);