]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-bridge-mdb.c
network: make Reload bus method synchronous
[thirdparty/systemd.git] / src / network / networkd-bridge-mdb.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <net/if.h>
4 #include <linux/if_bridge.h>
5
6 #include "netlink-util.h"
7 #include "networkd-bridge-mdb.h"
8 #include "networkd-link.h"
9 #include "networkd-manager.h"
10 #include "networkd-network.h"
11 #include "networkd-queue.h"
12 #include "string-util.h"
13 #include "vlan-util.h"
14
15 #define STATIC_BRIDGE_MDB_ENTRIES_PER_NETWORK_MAX 1024U
16
17 /* remove MDB entry. */
18 BridgeMDB *bridge_mdb_free(BridgeMDB *mdb) {
19 if (!mdb)
20 return NULL;
21
22 if (mdb->network) {
23 assert(mdb->section);
24 hashmap_remove(mdb->network->bridge_mdb_entries_by_section, mdb->section);
25 }
26
27 config_section_free(mdb->section);
28
29 return mfree(mdb);
30 }
31
32 DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeMDB, bridge_mdb_free);
33
34 /* create a new MDB entry or get an existing one. */
35 static int bridge_mdb_new_static(
36 Network *network,
37 const char *filename,
38 unsigned section_line,
39 BridgeMDB **ret) {
40
41 _cleanup_(config_section_freep) ConfigSection *n = NULL;
42 _cleanup_(bridge_mdb_freep) BridgeMDB *mdb = NULL;
43 int r;
44
45 assert(network);
46 assert(ret);
47 assert(filename);
48 assert(section_line > 0);
49
50 r = config_section_new(filename, section_line, &n);
51 if (r < 0)
52 return r;
53
54 /* search entry in hashmap first. */
55 mdb = hashmap_get(network->bridge_mdb_entries_by_section, n);
56 if (mdb) {
57 *ret = TAKE_PTR(mdb);
58 return 0;
59 }
60
61 if (hashmap_size(network->bridge_mdb_entries_by_section) >= STATIC_BRIDGE_MDB_ENTRIES_PER_NETWORK_MAX)
62 return -E2BIG;
63
64 /* allocate space for an MDB entry. */
65 mdb = new(BridgeMDB, 1);
66 if (!mdb)
67 return -ENOMEM;
68
69 /* init MDB structure. */
70 *mdb = (BridgeMDB) {
71 .network = network,
72 .section = TAKE_PTR(n),
73 };
74
75 r = hashmap_ensure_put(&network->bridge_mdb_entries_by_section, &config_section_hash_ops, mdb->section, mdb);
76 if (r < 0)
77 return r;
78
79 /* return allocated MDB structure. */
80 *ret = TAKE_PTR(mdb);
81 return 0;
82 }
83
84 static int bridge_mdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
85 int r;
86
87 assert(m);
88 assert(link);
89
90 r = sd_netlink_message_get_errno(m);
91 if (r == -EINVAL && streq_ptr(link->kind, "bridge") && link->master_ifindex <= 0) {
92 /* To configure bridge MDB entries on bridge master, 1bc844ee0faa1b92e3ede00bdd948021c78d7088 (v5.4) is required. */
93 if (!link->manager->bridge_mdb_on_master_not_supported) {
94 log_link_warning_errno(link, r, "Kernel seems not to support bridge MDB entries on bridge master, ignoring: %m");
95 link->manager->bridge_mdb_on_master_not_supported = true;
96 }
97 } else if (r < 0 && r != -EEXIST) {
98 log_link_message_warning_errno(link, m, r, "Could not add MDB entry");
99 link_enter_failed(link);
100 return 1;
101 }
102
103 if (link->static_bridge_mdb_messages == 0) {
104 link->static_bridge_mdb_configured = true;
105 link_check_ready(link);
106 }
107
108 return 1;
109 }
110
111 /* send a request to the kernel to add an MDB entry */
112 static int bridge_mdb_configure(BridgeMDB *mdb, Link *link, Request *req) {
113 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
114 struct br_mdb_entry entry;
115 int r;
116
117 assert(mdb);
118 assert(link);
119 assert(link->manager);
120 assert(req);
121
122 if (DEBUG_LOGGING)
123 log_link_debug(link, "Configuring bridge MDB entry: MulticastGroupAddress=%s, VLANId=%u",
124 IN_ADDR_TO_STRING(mdb->family, &mdb->group_addr), mdb->vlan_id);
125
126 entry = (struct br_mdb_entry) {
127 /* If MDB entry is added on bridge master, then the state must be MDB_TEMPORARY.
128 * See br_mdb_add_group() in net/bridge/br_mdb.c of kernel. */
129 .state = link->master_ifindex <= 0 ? MDB_TEMPORARY : MDB_PERMANENT,
130 .ifindex = link->ifindex,
131 .vid = mdb->vlan_id,
132 };
133
134 switch (mdb->family) {
135 case AF_INET:
136 entry.addr.u.ip4 = mdb->group_addr.in.s_addr;
137 entry.addr.proto = htobe16(ETH_P_IP);
138 break;
139
140 case AF_INET6:
141 entry.addr.u.ip6 = mdb->group_addr.in6;
142 entry.addr.proto = htobe16(ETH_P_IPV6);
143 break;
144
145 default:
146 assert_not_reached();
147 }
148
149 r = sd_rtnl_message_new_mdb(link->manager->rtnl, &m, RTM_NEWMDB,
150 link->master_ifindex > 0 ? link->master_ifindex : link->ifindex);
151 if (r < 0)
152 return r;
153
154 r = sd_netlink_message_append_data(m, MDBA_SET_ENTRY, &entry, sizeof(entry));
155 if (r < 0)
156 return r;
157
158 return request_call_netlink_async(link->manager->rtnl, m, req);
159 }
160
161 static bool bridge_mdb_is_ready_to_configure(Link *link) {
162 Link *master;
163
164 assert(link);
165
166 if (!link_is_ready_to_configure(link, false))
167 return false;
168
169 if (!link->master_set)
170 return false;
171
172 if (link->master_ifindex <= 0 && streq_ptr(link->kind, "bridge"))
173 return true; /* The interface is bridge master. */
174
175 if (link_get_master(link, &master) < 0)
176 return false;
177
178 if (!streq_ptr(master->kind, "bridge"))
179 return false;
180
181 if (!IN_SET(master->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
182 return false;
183
184 if (master->set_flags_messages > 0)
185 return false;
186
187 if (!link_has_carrier(master))
188 return false;
189
190 return true;
191 }
192
193 static int bridge_mdb_process_request(Request *req, Link *link, void *userdata) {
194 BridgeMDB *mdb = ASSERT_PTR(userdata);
195 int r;
196
197 assert(req);
198 assert(link);
199
200 if (!bridge_mdb_is_ready_to_configure(link))
201 return 0;
202
203 r = bridge_mdb_configure(mdb, link, req);
204 if (r < 0)
205 return log_link_warning_errno(link, r, "Failed to configure bridge MDB: %m");
206
207 return 1;
208 }
209
210 int link_request_static_bridge_mdb(Link *link) {
211 BridgeMDB *mdb;
212 int r;
213
214 assert(link);
215 assert(link->manager);
216
217 link->static_bridge_mdb_configured = false;
218
219 if (!link->network)
220 return 0;
221
222 if (hashmap_isempty(link->network->bridge_mdb_entries_by_section))
223 goto finish;
224
225 HASHMAP_FOREACH(mdb, link->network->bridge_mdb_entries_by_section) {
226 r = link_queue_request_full(link, REQUEST_TYPE_BRIDGE_MDB,
227 mdb, NULL,
228 trivial_hash_func,
229 trivial_compare_func,
230 bridge_mdb_process_request,
231 &link->static_bridge_mdb_messages,
232 bridge_mdb_configure_handler,
233 NULL);
234 if (r < 0)
235 return log_link_error_errno(link, r, "Failed to request MDB entry to multicast group database: %m");
236 }
237
238 finish:
239 if (link->static_bridge_mdb_messages == 0) {
240 link->static_bridge_mdb_configured = true;
241 link_check_ready(link);
242 } else {
243 log_link_debug(link, "Setting bridge MDB entries.");
244 link_set_state(link, LINK_STATE_CONFIGURING);
245 }
246
247 return 0;
248 }
249
250 static int bridge_mdb_verify(BridgeMDB *mdb) {
251 if (section_is_invalid(mdb->section))
252 return -EINVAL;
253
254 if (mdb->family == AF_UNSPEC)
255 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
256 "%s: [BridgeMDB] section without MulticastGroupAddress= field configured. "
257 "Ignoring [BridgeMDB] section from line %u.",
258 mdb->section->filename, mdb->section->line);
259
260 if (!in_addr_is_multicast(mdb->family, &mdb->group_addr))
261 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
262 "%s: MulticastGroupAddress= is not a multicast address. "
263 "Ignoring [BridgeMDB] section from line %u.",
264 mdb->section->filename, mdb->section->line);
265
266 if (mdb->family == AF_INET) {
267 if (in4_addr_is_local_multicast(&mdb->group_addr.in))
268 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
269 "%s: MulticastGroupAddress= is a local multicast address. "
270 "Ignoring [BridgeMDB] section from line %u.",
271 mdb->section->filename, mdb->section->line);
272 } else {
273 if (in6_addr_is_link_local_all_nodes(&mdb->group_addr.in6))
274 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
275 "%s: MulticastGroupAddress= is the multicast all nodes address. "
276 "Ignoring [BridgeMDB] section from line %u.",
277 mdb->section->filename, mdb->section->line);
278 }
279
280 return 0;
281 }
282
283 void network_drop_invalid_bridge_mdb_entries(Network *network) {
284 BridgeMDB *mdb;
285
286 assert(network);
287
288 HASHMAP_FOREACH(mdb, network->bridge_mdb_entries_by_section)
289 if (bridge_mdb_verify(mdb) < 0)
290 bridge_mdb_free(mdb);
291 }
292
293 /* parse the VLAN Id from config files. */
294 int config_parse_mdb_vlan_id(
295 const char *unit,
296 const char *filename,
297 unsigned line,
298 const char *section,
299 unsigned section_line,
300 const char *lvalue,
301 int ltype,
302 const char *rvalue,
303 void *data,
304 void *userdata) {
305
306 _cleanup_(bridge_mdb_free_or_set_invalidp) BridgeMDB *mdb = NULL;
307 Network *network = userdata;
308 int r;
309
310 assert(filename);
311 assert(section);
312 assert(lvalue);
313 assert(rvalue);
314 assert(data);
315
316 r = bridge_mdb_new_static(network, filename, section_line, &mdb);
317 if (r < 0)
318 return log_oom();
319
320 r = config_parse_vlanid(unit, filename, line, section,
321 section_line, lvalue, ltype,
322 rvalue, &mdb->vlan_id, userdata);
323 if (r < 0)
324 return r;
325
326 TAKE_PTR(mdb);
327 return 0;
328 }
329
330 /* parse the multicast group from config files. */
331 int config_parse_mdb_group_address(
332 const char *unit,
333 const char *filename,
334 unsigned line,
335 const char *section,
336 unsigned section_line,
337 const char *lvalue,
338 int ltype,
339 const char *rvalue,
340 void *data,
341 void *userdata) {
342
343 _cleanup_(bridge_mdb_free_or_set_invalidp) BridgeMDB *mdb = NULL;
344 Network *network = userdata;
345 int r;
346
347 assert(filename);
348 assert(section);
349 assert(lvalue);
350 assert(rvalue);
351 assert(data);
352
353 r = bridge_mdb_new_static(network, filename, section_line, &mdb);
354 if (r < 0)
355 return log_oom();
356
357 r = in_addr_from_string_auto(rvalue, &mdb->family, &mdb->group_addr);
358 if (r < 0) {
359 log_syntax(unit, LOG_WARNING, filename, line, r, "Cannot parse multicast group address: %m");
360 return 0;
361 }
362
363 TAKE_PTR(mdb);
364 return 0;
365 }