]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-bridge-fdb.c
mkosi: Switch to fedora 40
[thirdparty/systemd.git] / src / network / networkd-bridge-fdb.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
b98b483b 2/***
810adae9 3 Copyright © 2014 Intel Corporation. All rights reserved.
b98b483b
AR
4***/
5
b98b483b 6#include <net/ethernet.h>
cf0fbc49 7#include <net/if.h>
b98b483b 8
b5efdb8a 9#include "alloc-util.h"
737f1405 10#include "bridge.h"
fc2f9534 11#include "netlink-util.h"
9671ae9d 12#include "networkd-bridge-fdb.h"
ee446d57 13#include "networkd-link.h"
23f53b99 14#include "networkd-manager.h"
ee446d57 15#include "networkd-network.h"
e5b35bf6
YW
16#include "networkd-queue.h"
17#include "networkd-util.h"
61b824c5 18#include "parse-util.h"
bdb397ed 19#include "string-table.h"
0e83e7a5 20#include "vlan-util.h"
737f1405 21#include "vxlan.h"
b98b483b 22
9671ae9d 23#define STATIC_BRIDGE_FDB_ENTRIES_PER_NETWORK_MAX 1024U
8c34b963 24
df3a18f8 25/* remove and FDB entry. */
9671ae9d
YW
26BridgeFDB *bridge_fdb_free(BridgeFDB *fdb) {
27 if (!fdb)
df3a18f8
YW
28 return NULL;
29
9671ae9d
YW
30 if (fdb->network) {
31 assert(fdb->section);
32 hashmap_remove(fdb->network->bridge_fdb_entries_by_section, fdb->section);
df3a18f8
YW
33 }
34
307fe3cd 35 config_section_free(fdb->section);
af99cdf4
SS
36
37 free(fdb->outgoing_ifname);
9671ae9d 38 return mfree(fdb);
df3a18f8
YW
39}
40
307fe3cd 41DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeFDB, bridge_fdb_free);
df3a18f8 42
b98b483b 43/* create a new FDB entry or get an existing one. */
9671ae9d 44static int bridge_fdb_new_static(
8c34b963 45 Network *network,
1a75764a
YW
46 const char *filename,
47 unsigned section_line,
9671ae9d 48 BridgeFDB **ret) {
8c34b963 49
307fe3cd 50 _cleanup_(config_section_freep) ConfigSection *n = NULL;
9671ae9d 51 _cleanup_(bridge_fdb_freep) BridgeFDB *fdb = NULL;
1a75764a 52 int r;
b98b483b
AR
53
54 assert(network);
8c34b963 55 assert(ret);
62ed9442
YW
56 assert(filename);
57 assert(section_line > 0);
1a75764a 58
307fe3cd 59 r = config_section_new(filename, section_line, &n);
62ed9442
YW
60 if (r < 0)
61 return r;
b98b483b 62
62ed9442 63 /* search entry in hashmap first. */
9671ae9d
YW
64 fdb = hashmap_get(network->bridge_fdb_entries_by_section, n);
65 if (fdb) {
66 *ret = TAKE_PTR(fdb);
62ed9442 67 return 0;
b98b483b
AR
68 }
69
9671ae9d 70 if (hashmap_size(network->bridge_fdb_entries_by_section) >= STATIC_BRIDGE_FDB_ENTRIES_PER_NETWORK_MAX)
8c34b963
LP
71 return -E2BIG;
72
b98b483b 73 /* allocate space for and FDB entry. */
9671ae9d
YW
74 fdb = new(BridgeFDB, 1);
75 if (!fdb)
b98b483b 76 return -ENOMEM;
b98b483b
AR
77
78 /* init FDB structure. */
9671ae9d 79 *fdb = (BridgeFDB) {
1a75764a 80 .network = network,
62ed9442 81 .section = TAKE_PTR(n),
61b824c5 82 .vni = VXLAN_VID_MAX + 1,
9671ae9d 83 .ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
1a75764a 84 };
b98b483b 85
307fe3cd 86 r = hashmap_ensure_put(&network->bridge_fdb_entries_by_section, &config_section_hash_ops, fdb->section, fdb);
62ed9442
YW
87 if (r < 0)
88 return r;
b98b483b
AR
89
90 /* return allocated FDB structure. */
9671ae9d 91 *ret = TAKE_PTR(fdb);
b98b483b
AR
92
93 return 0;
94}
95
80d62d4f 96static int bridge_fdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
b98b483b
AR
97 int r;
98
80d62d4f 99 assert(m);
ea6ec096 100 assert(link);
4ff296b0 101
1c4baffc 102 r = sd_netlink_message_get_errno(m);
4ff296b0 103 if (r < 0 && r != -EEXIST) {
e5b35bf6 104 log_link_message_warning_errno(link, m, r, "Could not add bridge FDB entry");
4ff296b0 105 link_enter_failed(link);
e5b35bf6 106 return 0;
4ff296b0 107 }
b98b483b 108
e5b35bf6
YW
109 if (link->static_bridge_fdb_messages == 0) {
110 log_link_debug(link, "Bridge FDB entries set");
111 link->static_bridge_fdb_configured = true;
112 link_check_ready(link);
113 }
114
115 return 0;
b98b483b
AR
116}
117
118/* send a request to the kernel to add a FDB entry in its static MAC table. */
bd187da8 119static int bridge_fdb_configure_message(const BridgeFDB *fdb, Link *link, sd_netlink_message *req) {
61b824c5 120 int r;
b98b483b 121
e5b35bf6 122 assert(fdb);
ea6ec096 123 assert(link);
b98b483b 124
9671ae9d 125 r = sd_rtnl_message_neigh_set_flags(req, fdb->ntf_flags);
b98b483b 126 if (r < 0)
bd187da8 127 return r;
b98b483b
AR
128
129 /* only NUD_PERMANENT state supported. */
130 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
131 if (r < 0)
bd187da8 132 return r;
b98b483b 133
9671ae9d 134 r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb->mac_addr, sizeof(fdb->mac_addr));
b98b483b 135 if (r < 0)
bd187da8 136 return r;
b98b483b
AR
137
138 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
9671ae9d
YW
139 if (fdb->vlan_id > 0) {
140 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb->vlan_id);
b98b483b 141 if (r < 0)
bd187da8 142 return r;
b98b483b
AR
143 }
144
af99cdf4
SS
145 if (fdb->outgoing_ifindex > 0) {
146 r = sd_netlink_message_append_u32(req, NDA_IFINDEX, fdb->outgoing_ifindex);
147 if (r < 0)
bd187da8 148 return r;
af99cdf4
SS
149 }
150
9671ae9d
YW
151 if (in_addr_is_set(fdb->family, &fdb->destination_addr)) {
152 r = netlink_message_append_in_addr_union(req, NDA_DST, fdb->family, &fdb->destination_addr);
c2c2793f 153 if (r < 0)
bd187da8 154 return r;
c2c2793f
SS
155 }
156
9671ae9d
YW
157 if (fdb->vni <= VXLAN_VID_MAX) {
158 r = sd_netlink_message_append_u32(req, NDA_VNI, fdb->vni);
61b824c5 159 if (r < 0)
bd187da8 160 return r;
61b824c5
SS
161 }
162
bd187da8 163 return 0;
b98b483b
AR
164}
165
54ff39f7 166static int bridge_fdb_configure(BridgeFDB *fdb, Link *link, Request *req) {
a79a8d16
YW
167 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
168 int r;
169
170 assert(fdb);
171 assert(link);
172 assert(link->manager);
54ff39f7 173 assert(req);
a79a8d16
YW
174
175 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE);
176 if (r < 0)
177 return r;
178
179 r = bridge_fdb_configure_message(fdb, link, m);
180 if (r < 0)
181 return r;
182
80d62d4f 183 return request_call_netlink_async(link->manager->rtnl, m, req);
a79a8d16
YW
184}
185
af99cdf4
SS
186static bool bridge_fdb_is_ready_to_configure(BridgeFDB *fdb, Link *link) {
187 Link *out = NULL;
188
189 assert(fdb);
190 assert(link);
191 assert(link->manager);
192
193 if (!link_is_ready_to_configure(link, false))
194 return false;
195
196 if (fdb->outgoing_ifname) {
197 if (link_get_by_name(link->manager, fdb->outgoing_ifname, &out) < 0)
198 return false;
199
200 fdb->outgoing_ifindex = out->ifindex;
201 } else if (fdb->outgoing_ifindex > 0) {
6eab614d 202 if (link_get_by_index(link->manager, fdb->outgoing_ifindex, &out) < 0)
af99cdf4
SS
203 return false;
204 }
205 if (out && !link_is_ready_to_configure(out, false))
206 return false;
207
208 return true;
209}
210
09d09207 211static int bridge_fdb_process_request(Request *req, Link *link, void *userdata) {
ff51134c 212 BridgeFDB *fdb = ASSERT_PTR(userdata);
bd187da8
ZJS
213 int r;
214
e5b35bf6 215 assert(req);
ff51134c 216 assert(link);
e5b35bf6 217
a79a8d16 218 if (!bridge_fdb_is_ready_to_configure(fdb, link))
e5b35bf6
YW
219 return 0;
220
54ff39f7 221 r = bridge_fdb_configure(fdb, link, req);
bd187da8 222 if (r < 0)
a79a8d16 223 return log_link_warning_errno(link, r, "Failed to configure bridge FDB: %m");
bd187da8
ZJS
224
225 return 1;
e5b35bf6
YW
226}
227
8bed7c55
YW
228int link_request_static_bridge_fdb(Link *link) {
229 BridgeFDB *fdb;
230 int r;
231
232 assert(link);
233 assert(link->network);
234
235 link->static_bridge_fdb_configured = false;
236
237 HASHMAP_FOREACH(fdb, link->network->bridge_fdb_entries_by_section) {
09d09207
YW
238 r = link_queue_request_full(link, REQUEST_TYPE_BRIDGE_FDB,
239 fdb, NULL,
240 trivial_hash_func,
241 trivial_compare_func,
242 bridge_fdb_process_request,
243 &link->static_bridge_fdb_messages,
244 bridge_fdb_configure_handler,
245 NULL);
8bed7c55
YW
246 if (r < 0)
247 return log_link_error_errno(link, r, "Failed to request static bridge FDB entry: %m");
248 }
249
250 if (link->static_bridge_fdb_messages == 0) {
251 link->static_bridge_fdb_configured = true;
252 link_check_ready(link);
253 } else {
254 log_link_debug(link, "Setting bridge FDB entries");
255 link_set_state(link, LINK_STATE_CONFIGURING);
256 }
257
258 return 0;
259}
260
9671ae9d
YW
261void network_drop_invalid_bridge_fdb_entries(Network *network) {
262 BridgeFDB *fdb;
e6ad630d
YW
263
264 assert(network);
265
9671ae9d
YW
266 HASHMAP_FOREACH(fdb, network->bridge_fdb_entries_by_section)
267 if (section_is_invalid(fdb->section))
268 bridge_fdb_free(fdb);
e6ad630d
YW
269}
270
b98b483b 271/* parse the HW address from config files. */
8519d8f5
LP
272int config_parse_fdb_hwaddr(
273 const char *unit,
274 const char *filename,
275 unsigned line,
276 const char *section,
277 unsigned section_line,
278 const char *lvalue,
279 int ltype,
280 const char *rvalue,
281 void *data,
282 void *userdata) {
283
9671ae9d 284 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
b98b483b 285 Network *network = userdata;
b98b483b
AR
286 int r;
287
288 assert(filename);
289 assert(section);
290 assert(lvalue);
291 assert(rvalue);
292 assert(data);
293
9671ae9d 294 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
6a7a4e4d
LP
295 if (r < 0)
296 return log_oom();
b98b483b 297
227e9ce2 298 r = parse_ether_addr(rvalue, &fdb->mac_addr);
f5bab0d9 299 if (r < 0) {
d96edb2c 300 log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
b98b483b
AR
301 return 0;
302 }
303
9671ae9d 304 TAKE_PTR(fdb);
b98b483b
AR
305 return 0;
306}
307
308/* parse the VLAN Id from config files. */
8519d8f5
LP
309int config_parse_fdb_vlan_id(
310 const char *unit,
311 const char *filename,
312 unsigned line,
313 const char *section,
314 unsigned section_line,
315 const char *lvalue,
316 int ltype,
317 const char *rvalue,
318 void *data,
319 void *userdata) {
320
9671ae9d 321 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
b98b483b 322 Network *network = userdata;
b98b483b
AR
323 int r;
324
325 assert(filename);
326 assert(section);
327 assert(lvalue);
328 assert(rvalue);
329 assert(data);
330
9671ae9d 331 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
6a7a4e4d
LP
332 if (r < 0)
333 return log_oom();
b98b483b 334
0e83e7a5
TJ
335 r = config_parse_vlanid(unit, filename, line, section,
336 section_line, lvalue, ltype,
9671ae9d 337 rvalue, &fdb->vlan_id, userdata);
6a7a4e4d 338 if (r < 0)
b98b483b 339 return r;
b98b483b 340
9671ae9d 341 TAKE_PTR(fdb);
b98b483b
AR
342 return 0;
343}
c2c2793f
SS
344
345int config_parse_fdb_destination(
346 const char *unit,
347 const char *filename,
348 unsigned line,
349 const char *section,
350 unsigned section_line,
351 const char *lvalue,
352 int ltype,
353 const char *rvalue,
354 void *data,
355 void *userdata) {
356
9671ae9d 357 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
c2c2793f
SS
358 Network *network = userdata;
359 int r;
360
361 assert(filename);
362 assert(section);
363 assert(lvalue);
364 assert(rvalue);
365 assert(data);
366
9671ae9d 367 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
c2c2793f
SS
368 if (r < 0)
369 return log_oom();
370
9671ae9d 371 r = in_addr_from_string_auto(rvalue, &fdb->family, &fdb->destination_addr);
e5f1b999
LP
372 if (r < 0) {
373 log_syntax(unit, LOG_WARNING, filename, line, r,
374 "FDB destination IP address is invalid, ignoring assignment: %s",
375 rvalue);
376 return 0;
377 }
c2c2793f 378
9671ae9d 379 TAKE_PTR(fdb);
c2c2793f
SS
380 return 0;
381}
61b824c5
SS
382
383int config_parse_fdb_vxlan_vni(
384 const char *unit,
385 const char *filename,
386 unsigned line,
387 const char *section,
388 unsigned section_line,
389 const char *lvalue,
390 int ltype,
391 const char *rvalue,
392 void *data,
393 void *userdata) {
394
9671ae9d 395 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
61b824c5
SS
396 Network *network = userdata;
397 uint32_t vni;
398 int r;
399
400 assert(filename);
401 assert(section);
402 assert(lvalue);
403 assert(rvalue);
404 assert(data);
405
9671ae9d 406 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
61b824c5
SS
407 if (r < 0)
408 return log_oom();
409
410 r = safe_atou32(rvalue, &vni);
411 if (r < 0) {
d96edb2c 412 log_syntax(unit, LOG_WARNING, filename, line, r,
61b824c5
SS
413 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
414 rvalue);
415 return 0;
416 }
417
418 if (vni > VXLAN_VID_MAX) {
d96edb2c 419 log_syntax(unit, LOG_WARNING, filename, line, 0,
61b824c5
SS
420 "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
421 rvalue);
422 return 0;
423 }
424
9671ae9d 425 fdb->vni = vni;
61b824c5 426
9671ae9d 427 TAKE_PTR(fdb);
61b824c5
SS
428 return 0;
429}
bdb397ed 430
9671ae9d 431static const char* const ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = {
75ffb011
YW
432 [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use",
433 [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self",
434 [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master",
435 [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router",
436};
437
9671ae9d 438DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(ntf_flags, NeighborCacheEntryFlags);
75ffb011 439
bdb397ed
SS
440int config_parse_fdb_ntf_flags(
441 const char *unit,
442 const char *filename,
443 unsigned line,
444 const char *section,
445 unsigned section_line,
446 const char *lvalue,
447 int ltype,
448 const char *rvalue,
449 void *data,
450 void *userdata) {
451
9671ae9d 452 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
bdb397ed 453 Network *network = userdata;
9671ae9d 454 NeighborCacheEntryFlags f;
bdb397ed
SS
455 int r;
456
457 assert(filename);
458 assert(section);
459 assert(lvalue);
460 assert(rvalue);
461 assert(data);
462
9671ae9d 463 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
bdb397ed
SS
464 if (r < 0)
465 return log_oom();
466
9671ae9d 467 f = ntf_flags_from_string(rvalue);
bdb397ed 468 if (f < 0) {
7211c853 469 log_syntax(unit, LOG_WARNING, filename, line, f,
bdb397ed
SS
470 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
471 rvalue);
472 return 0;
473 }
474
9671ae9d 475 fdb->ntf_flags = f;
bdb397ed 476
9671ae9d 477 TAKE_PTR(fdb);
bdb397ed
SS
478 return 0;
479}
af99cdf4
SS
480
481int config_parse_fdb_interface(
482 const char *unit,
483 const char *filename,
484 unsigned line,
485 const char *section,
486 unsigned section_line,
487 const char *lvalue,
488 int ltype,
489 const char *rvalue,
490 void *data,
491 void *userdata) {
492
493 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
494 Network *network = userdata;
495 int r;
496
497 assert(filename);
498 assert(section);
499 assert(lvalue);
500 assert(rvalue);
501 assert(data);
502
503 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
504 if (r < 0)
505 return log_oom();
506
507 if (isempty(rvalue)) {
508 fdb->outgoing_ifname = mfree(fdb->outgoing_ifname);
509 fdb->outgoing_ifindex = 0;
510 TAKE_PTR(fdb);
511 return 0;
512 }
513
514 r = parse_ifindex(rvalue);
515 if (r > 0) {
516 fdb->outgoing_ifname = mfree(fdb->outgoing_ifname);
517 fdb->outgoing_ifindex = r;
518 TAKE_PTR(fdb);
519 return 0;
520 }
521
522 if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
523 log_syntax(unit, LOG_WARNING, filename, line, 0,
524 "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
525 return 0;
526 }
527
528 r = free_and_strdup(&fdb->outgoing_ifname, rvalue);
529 if (r < 0)
530 return log_oom();
531 fdb->outgoing_ifindex = 0;
532
533 TAKE_PTR(fdb);
534 return 0;
535}