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