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. */
bridge_fdb_free(BridgeFDB * fdb)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 config_section_free(fdb->section);
36
37 free(fdb->outgoing_ifname);
38 return mfree(fdb);
39 }
40
41 DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeFDB, bridge_fdb_free);
42
43 /* create a new FDB entry or get an existing one. */
bridge_fdb_new_static(Network * network,const char * filename,unsigned section_line,BridgeFDB ** ret)44 static int bridge_fdb_new_static(
45 Network *network,
46 const char *filename,
47 unsigned section_line,
48 BridgeFDB **ret) {
49
50 _cleanup_(config_section_freep) ConfigSection *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 = 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, &config_section_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
bridge_fdb_configure_handler(sd_netlink * rtnl,sd_netlink_message * m,Request * req,Link * link,void * userdata)96 static int bridge_fdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) {
97 int r;
98
99 assert(m);
100 assert(link);
101
102 r = sd_netlink_message_get_errno(m);
103 if (r < 0 && r != -EEXIST) {
104 log_link_message_warning_errno(link, m, r, "Could not add bridge FDB entry");
105 link_enter_failed(link);
106 return 0;
107 }
108
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;
116 }
117
118 /* send a request to the kernel to add a FDB entry in its static MAC table. */
bridge_fdb_configure_message(const BridgeFDB * fdb,Link * link,sd_netlink_message * req)119 static int bridge_fdb_configure_message(const BridgeFDB *fdb, Link *link, sd_netlink_message *req) {
120 int r;
121
122 assert(fdb);
123 assert(link);
124
125 r = sd_rtnl_message_neigh_set_flags(req, fdb->ntf_flags);
126 if (r < 0)
127 return r;
128
129 /* only NUD_PERMANENT state supported. */
130 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
131 if (r < 0)
132 return r;
133
134 r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb->mac_addr, sizeof(fdb->mac_addr));
135 if (r < 0)
136 return r;
137
138 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
139 if (fdb->vlan_id > 0) {
140 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb->vlan_id);
141 if (r < 0)
142 return r;
143 }
144
145 if (fdb->outgoing_ifindex > 0) {
146 r = sd_netlink_message_append_u32(req, NDA_IFINDEX, fdb->outgoing_ifindex);
147 if (r < 0)
148 return r;
149 }
150
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);
153 if (r < 0)
154 return r;
155 }
156
157 if (fdb->vni <= VXLAN_VID_MAX) {
158 r = sd_netlink_message_append_u32(req, NDA_VNI, fdb->vni);
159 if (r < 0)
160 return r;
161 }
162
163 return 0;
164 }
165
bridge_fdb_configure(BridgeFDB * fdb,Link * link,Request * req)166 static int bridge_fdb_configure(BridgeFDB *fdb, Link *link, Request *req) {
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);
173 assert(req);
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
183 return request_call_netlink_async(link->manager->rtnl, m, req);
184 }
185
bridge_fdb_is_ready_to_configure(BridgeFDB * fdb,Link * link)186 static 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) {
202 if (link_get_by_index(link->manager, fdb->outgoing_ifindex, &out) < 0)
203 return false;
204 }
205 if (out && !link_is_ready_to_configure(out, false))
206 return false;
207
208 return true;
209 }
210
bridge_fdb_process_request(Request * req,Link * link,void * userdata)211 static int bridge_fdb_process_request(Request *req, Link *link, void *userdata) {
212 BridgeFDB *fdb = ASSERT_PTR(userdata);
213 int r;
214
215 assert(req);
216 assert(link);
217
218 if (!bridge_fdb_is_ready_to_configure(fdb, link))
219 return 0;
220
221 r = bridge_fdb_configure(fdb, link, req);
222 if (r < 0)
223 return log_link_warning_errno(link, r, "Failed to configure bridge FDB: %m");
224
225 return 1;
226 }
227
link_request_static_bridge_fdb(Link * link)228 int 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) {
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);
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
network_drop_invalid_bridge_fdb_entries(Network * network)261 void network_drop_invalid_bridge_fdb_entries(Network *network) {
262 BridgeFDB *fdb;
263
264 assert(network);
265
266 HASHMAP_FOREACH(fdb, network->bridge_fdb_entries_by_section)
267 if (section_is_invalid(fdb->section))
268 bridge_fdb_free(fdb);
269 }
270
271 /* parse the HW address from config files. */
config_parse_fdb_hwaddr(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)272 int 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
284 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
285 Network *network = userdata;
286 int r;
287
288 assert(filename);
289 assert(section);
290 assert(lvalue);
291 assert(rvalue);
292 assert(data);
293
294 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
295 if (r < 0)
296 return log_oom();
297
298 r = parse_ether_addr(rvalue, &fdb->mac_addr);
299 if (r < 0) {
300 log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
301 return 0;
302 }
303
304 TAKE_PTR(fdb);
305 return 0;
306 }
307
308 /* parse the VLAN Id from config files. */
config_parse_fdb_vlan_id(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)309 int 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
321 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
322 Network *network = userdata;
323 int r;
324
325 assert(filename);
326 assert(section);
327 assert(lvalue);
328 assert(rvalue);
329 assert(data);
330
331 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
332 if (r < 0)
333 return log_oom();
334
335 r = config_parse_vlanid(unit, filename, line, section,
336 section_line, lvalue, ltype,
337 rvalue, &fdb->vlan_id, userdata);
338 if (r < 0)
339 return r;
340
341 TAKE_PTR(fdb);
342 return 0;
343 }
344
config_parse_fdb_destination(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)345 int 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
357 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
358 Network *network = userdata;
359 int r;
360
361 assert(filename);
362 assert(section);
363 assert(lvalue);
364 assert(rvalue);
365 assert(data);
366
367 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
368 if (r < 0)
369 return log_oom();
370
371 r = in_addr_from_string_auto(rvalue, &fdb->family, &fdb->destination_addr);
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 }
378
379 TAKE_PTR(fdb);
380 return 0;
381 }
382
config_parse_fdb_vxlan_vni(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)383 int 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
395 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
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
406 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
407 if (r < 0)
408 return log_oom();
409
410 r = safe_atou32(rvalue, &vni);
411 if (r < 0) {
412 log_syntax(unit, LOG_WARNING, filename, line, r,
413 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
414 rvalue);
415 return 0;
416 }
417
418 if (vni > VXLAN_VID_MAX) {
419 log_syntax(unit, LOG_WARNING, filename, line, 0,
420 "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
421 rvalue);
422 return 0;
423 }
424
425 fdb->vni = vni;
426
427 TAKE_PTR(fdb);
428 return 0;
429 }
430
431 static const char* const ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = {
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
438 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(ntf_flags, NeighborCacheEntryFlags);
439
config_parse_fdb_ntf_flags(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)440 int 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
452 _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL;
453 Network *network = userdata;
454 NeighborCacheEntryFlags f;
455 int r;
456
457 assert(filename);
458 assert(section);
459 assert(lvalue);
460 assert(rvalue);
461 assert(data);
462
463 r = bridge_fdb_new_static(network, filename, section_line, &fdb);
464 if (r < 0)
465 return log_oom();
466
467 f = ntf_flags_from_string(rvalue);
468 if (f < 0) {
469 log_syntax(unit, LOG_WARNING, filename, line, f,
470 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
471 rvalue);
472 return 0;
473 }
474
475 fdb->ntf_flags = f;
476
477 TAKE_PTR(fdb);
478 return 0;
479 }
480
config_parse_fdb_interface(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)481 int 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 }
536