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