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
< 0 && r
!= -EEXIST
) {
107 log_link_message_warning_errno(link
, m
, r
, "Could not add MDB entry");
108 link_enter_failed(link
);
112 if (link
->bridge_mdb_messages
== 0) {
113 link
->bridge_mdb_configured
= true;
114 link_check_ready(link
);
120 static int link_get_bridge_master_ifindex(Link
*link
) {
123 if (link
->network
&& link
->network
->bridge
)
124 return link
->network
->bridge
->ifindex
;
126 if (streq_ptr(link
->kind
, "bridge"))
127 return link
->ifindex
;
132 /* send a request to the kernel to add an MDB entry */
133 static int mdb_entry_configure(Link
*link
, MdbEntry
*mdb_entry
) {
134 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
135 struct br_mdb_entry entry
;
139 assert(link
->network
);
140 assert(link
->manager
);
144 _cleanup_free_
char *a
= NULL
;
146 (void) in_addr_to_string(mdb_entry
->family
, &mdb_entry
->group_addr
, &a
);
147 log_link_debug(link
, "Configuring bridge MDB entry: MulticastGroupAddress=%s, VLANId=%u",
148 strna(a
), mdb_entry
->vlan_id
);
151 master
= link_get_bridge_master_ifindex(link
);
153 return log_link_error_errno(link
, SYNTHETIC_ERRNO(EINVAL
), "Invalid bridge master ifindex %i", master
);
155 entry
= (struct br_mdb_entry
) {
156 /* If MDB entry is added on bridge master, then the state must be MDB_TEMPORARY.
157 * See br_mdb_add_group() in net/bridge/br_mdb.c of kernel. */
158 .state
= master
== link
->ifindex
? MDB_TEMPORARY
: MDB_PERMANENT
,
159 .ifindex
= link
->ifindex
,
160 .vid
= mdb_entry
->vlan_id
,
163 /* create new RTM message */
164 r
= sd_rtnl_message_new_mdb(link
->manager
->rtnl
, &req
, RTM_NEWMDB
, master
);
166 return log_link_error_errno(link
, r
, "Could not create RTM_NEWMDB message: %m");
168 switch (mdb_entry
->family
) {
170 entry
.addr
.u
.ip4
= mdb_entry
->group_addr
.in
.s_addr
;
171 entry
.addr
.proto
= htobe16(ETH_P_IP
);
175 entry
.addr
.u
.ip6
= mdb_entry
->group_addr
.in6
;
176 entry
.addr
.proto
= htobe16(ETH_P_IPV6
);
180 assert_not_reached("Invalid address family");
183 r
= sd_netlink_message_append_data(req
, MDBA_SET_ENTRY
, &entry
, sizeof(entry
));
185 return log_link_error_errno(link
, r
, "Could not append MDBA_SET_ENTRY attribute: %m");
187 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, set_mdb_handler
,
188 link_netlink_destroy_callback
, link
);
190 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
197 int link_set_bridge_mdb(Link
*link
) {
203 link
->bridge_mdb_configured
= false;
208 if (LIST_IS_EMPTY(link
->network
->static_mdb_entries
))
211 if (!link_has_carrier(link
))
212 return log_link_debug(link
, "Link does not have carrier yet, setting MDB entries later.");
214 if (link
->network
->bridge
) {
217 r
= link_get(link
->manager
, link
->network
->bridge
->ifindex
, &master
);
219 return log_link_error_errno(link
, r
, "Failed to get Link object for Bridge=%s", link
->network
->bridge
->ifname
);
221 if (!link_has_carrier(master
))
222 return log_link_debug(link
, "Bridge interface %s does not have carrier yet, setting MDB entries later.", link
->network
->bridge
->ifname
);
224 } else if (!streq_ptr(link
->kind
, "bridge")) {
225 log_link_warning(link
, "Link is neither a bridge master nor a bridge port, ignoring [BridgeMDB] sections.");
229 LIST_FOREACH(static_mdb_entries
, mdb_entry
, link
->network
->static_mdb_entries
) {
230 r
= mdb_entry_configure(link
, mdb_entry
);
232 return log_link_error_errno(link
, r
, "Failed to add MDB entry to multicast group database: %m");
234 link
->bridge_mdb_messages
++;
238 if (link
->bridge_mdb_messages
== 0) {
239 link
->bridge_mdb_configured
= true;
240 link_check_ready(link
);
246 /* parse the VLAN Id from config files. */
247 int config_parse_mdb_vlan_id(
249 const char *filename
,
252 unsigned section_line
,
259 _cleanup_(mdb_entry_free_or_set_invalidp
) MdbEntry
*mdb_entry
= NULL
;
260 Network
*network
= userdata
;
269 r
= mdb_entry_new_static(network
, filename
, section_line
, &mdb_entry
);
273 r
= config_parse_vlanid(unit
, filename
, line
, section
,
274 section_line
, lvalue
, ltype
,
275 rvalue
, &mdb_entry
->vlan_id
, userdata
);
284 /* parse the multicast group from config files. */
285 int config_parse_mdb_group_address(
287 const char *filename
,
290 unsigned section_line
,
297 _cleanup_(mdb_entry_free_or_set_invalidp
) MdbEntry
*mdb_entry
= NULL
;
298 Network
*network
= userdata
;
307 r
= mdb_entry_new_static(network
, filename
, section_line
, &mdb_entry
);
311 r
= in_addr_from_string_auto(rvalue
, &mdb_entry
->family
, &mdb_entry
->group_addr
);
313 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Cannot parse multicast group address: %m");
322 int mdb_entry_verify(MdbEntry
*mdb_entry
) {
323 if (section_is_invalid(mdb_entry
->section
))
326 if (mdb_entry
->family
== AF_UNSPEC
)
327 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
328 "%s: [BridgeMDB] section without MulticastGroupAddress= field configured. "
329 "Ignoring [BridgeMDB] section from line %u.",
330 mdb_entry
->section
->filename
, mdb_entry
->section
->line
);
332 if (!in_addr_is_multicast(mdb_entry
->family
, &mdb_entry
->group_addr
))
333 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
334 "%s: MulticastGroupAddress= is not a multicast address. "
335 "Ignoring [BridgeMDB] section from line %u.",
336 mdb_entry
->section
->filename
, mdb_entry
->section
->line
);
338 if (mdb_entry
->family
== AF_INET
) {
339 if (in4_addr_is_local_multicast(&mdb_entry
->group_addr
.in
))
340 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
341 "%s: MulticastGroupAddress= is a local multicast address. "
342 "Ignoring [BridgeMDB] section from line %u.",
343 mdb_entry
->section
->filename
, mdb_entry
->section
->line
);
345 if (in6_addr_is_link_local_all_nodes(&mdb_entry
->group_addr
.in6
))
346 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
347 "%s: MulticastGroupAddress= is the multicast all nodes address. "
348 "Ignoring [BridgeMDB] section from line %u.",
349 mdb_entry
->section
->filename
, mdb_entry
->section
->line
);