]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-fdb.c
Merge pull request #18267 from lucaswerkmeister/truncate
[thirdparty/systemd.git] / src / network / networkd-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-fdb.h"
13 #include "networkd-link.h"
14 #include "networkd-manager.h"
15 #include "networkd-network.h"
16 #include "parse-util.h"
17 #include "string-table.h"
18 #include "vlan-util.h"
19 #include "vxlan.h"
20
21 #define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U
22
23 /* remove and FDB entry. */
24 FdbEntry *fdb_entry_free(FdbEntry *fdb_entry) {
25 if (!fdb_entry)
26 return NULL;
27
28 if (fdb_entry->network) {
29 assert(fdb_entry->section);
30 hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
31 }
32
33 network_config_section_free(fdb_entry->section);
34 return mfree(fdb_entry);
35 }
36
37 DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free);
38
39 /* create a new FDB entry or get an existing one. */
40 static int fdb_entry_new_static(
41 Network *network,
42 const char *filename,
43 unsigned section_line,
44 FdbEntry **ret) {
45
46 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
47 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
48 int r;
49
50 assert(network);
51 assert(ret);
52 assert(filename);
53 assert(section_line > 0);
54
55 r = network_config_section_new(filename, section_line, &n);
56 if (r < 0)
57 return r;
58
59 /* search entry in hashmap first. */
60 fdb_entry = hashmap_get(network->fdb_entries_by_section, n);
61 if (fdb_entry) {
62 *ret = TAKE_PTR(fdb_entry);
63 return 0;
64 }
65
66 if (hashmap_size(network->fdb_entries_by_section) >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
67 return -E2BIG;
68
69 /* allocate space for and FDB entry. */
70 fdb_entry = new(FdbEntry, 1);
71 if (!fdb_entry)
72 return -ENOMEM;
73
74 /* init FDB structure. */
75 *fdb_entry = (FdbEntry) {
76 .network = network,
77 .section = TAKE_PTR(n),
78 .vni = VXLAN_VID_MAX + 1,
79 .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
80 };
81
82 r = hashmap_ensure_put(&network->fdb_entries_by_section, &network_config_hash_ops, fdb_entry->section, fdb_entry);
83 if (r < 0)
84 return r;
85
86 /* return allocated FDB structure. */
87 *ret = TAKE_PTR(fdb_entry);
88
89 return 0;
90 }
91
92 static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
93 int r;
94
95 assert(link);
96
97 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
98 return 1;
99
100 r = sd_netlink_message_get_errno(m);
101 if (r < 0 && r != -EEXIST) {
102 log_link_message_warning_errno(link, m, r, "Could not add FDB entry");
103 link_enter_failed(link);
104 return 1;
105 }
106
107 return 1;
108 }
109
110 /* send a request to the kernel to add a FDB entry in its static MAC table. */
111 static int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
112 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
113 int r;
114
115 assert(link);
116 assert(link->network);
117 assert(link->manager);
118 assert(fdb_entry);
119
120 /* create new RTM message */
121 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE);
122 if (r < 0)
123 return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m");
124
125 r = sd_rtnl_message_neigh_set_flags(req, fdb_entry->fdb_ntf_flags);
126 if (r < 0)
127 return log_link_error_errno(link, r, "Could not set neighbor flags: %m");
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 log_link_error_errno(link, r, "Could not set neighbor state: %m");
133
134 r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb_entry->mac_addr, sizeof(fdb_entry->mac_addr));
135 if (r < 0)
136 return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m");
137
138 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
139 if (fdb_entry->vlan_id > 0) {
140 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
141 if (r < 0)
142 return log_link_error_errno(link, r, "Could not append NDA_VLAN attribute: %m");
143 }
144
145 if (!in_addr_is_null(fdb_entry->family, &fdb_entry->destination_addr)) {
146 r = netlink_message_append_in_addr_union(req, NDA_DST, fdb_entry->family, &fdb_entry->destination_addr);
147 if (r < 0)
148 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
149 }
150
151 if (fdb_entry->vni <= VXLAN_VID_MAX) {
152 r = sd_netlink_message_append_u32(req, NDA_VNI, fdb_entry->vni);
153 if (r < 0)
154 return log_link_error_errno(link, r, "Could not append NDA_VNI attribute: %m");
155 }
156
157 /* send message to the kernel to update its internal static MAC table. */
158 r = netlink_call_async(link->manager->rtnl, NULL, req, set_fdb_handler,
159 link_netlink_destroy_callback, link);
160 if (r < 0)
161 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
162
163 link_ref(link);
164
165 return 1;
166 }
167
168 int link_set_bridge_fdb(Link *link) {
169 FdbEntry *fdb_entry;
170 int r;
171
172 assert(link);
173 assert(link->network);
174
175 HASHMAP_FOREACH(fdb_entry, link->network->fdb_entries_by_section) {
176 r = fdb_entry_configure(link, fdb_entry);
177 if (r < 0)
178 return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
179 }
180
181 return 0;
182 }
183
184 void network_drop_invalid_fdb_entries(Network *network) {
185 FdbEntry *fdb_entry;
186
187 assert(network);
188
189 HASHMAP_FOREACH(fdb_entry, network->fdb_entries_by_section)
190 if (section_is_invalid(fdb_entry->section))
191 fdb_entry_free(fdb_entry);
192 }
193
194 /* parse the HW address from config files. */
195 int config_parse_fdb_hwaddr(
196 const char *unit,
197 const char *filename,
198 unsigned line,
199 const char *section,
200 unsigned section_line,
201 const char *lvalue,
202 int ltype,
203 const char *rvalue,
204 void *data,
205 void *userdata) {
206
207 Network *network = userdata;
208 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
209 int r;
210
211 assert(filename);
212 assert(section);
213 assert(lvalue);
214 assert(rvalue);
215 assert(data);
216
217 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
218 if (r < 0)
219 return log_oom();
220
221 r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
222 if (r < 0) {
223 log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
224 return 0;
225 }
226
227 fdb_entry = NULL;
228
229 return 0;
230 }
231
232 /* parse the VLAN Id from config files. */
233 int config_parse_fdb_vlan_id(
234 const char *unit,
235 const char *filename,
236 unsigned line,
237 const char *section,
238 unsigned section_line,
239 const char *lvalue,
240 int ltype,
241 const char *rvalue,
242 void *data,
243 void *userdata) {
244
245 Network *network = userdata;
246 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
247 int r;
248
249 assert(filename);
250 assert(section);
251 assert(lvalue);
252 assert(rvalue);
253 assert(data);
254
255 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
256 if (r < 0)
257 return log_oom();
258
259 r = config_parse_vlanid(unit, filename, line, section,
260 section_line, lvalue, ltype,
261 rvalue, &fdb_entry->vlan_id, userdata);
262 if (r < 0)
263 return r;
264
265 fdb_entry = NULL;
266
267 return 0;
268 }
269
270 int config_parse_fdb_destination(
271 const char *unit,
272 const char *filename,
273 unsigned line,
274 const char *section,
275 unsigned section_line,
276 const char *lvalue,
277 int ltype,
278 const char *rvalue,
279 void *data,
280 void *userdata) {
281
282 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
283 Network *network = userdata;
284 int r;
285
286 assert(filename);
287 assert(section);
288 assert(lvalue);
289 assert(rvalue);
290 assert(data);
291
292 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
293 if (r < 0)
294 return log_oom();
295
296 r = in_addr_from_string_auto(rvalue, &fdb_entry->family, &fdb_entry->destination_addr);
297 if (r < 0) {
298 log_syntax(unit, LOG_WARNING, filename, line, r,
299 "FDB destination IP address is invalid, ignoring assignment: %s",
300 rvalue);
301 return 0;
302 }
303
304 fdb_entry = NULL;
305
306 return 0;
307 }
308
309 int config_parse_fdb_vxlan_vni(
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_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
322 Network *network = userdata;
323 uint32_t vni;
324 int r;
325
326 assert(filename);
327 assert(section);
328 assert(lvalue);
329 assert(rvalue);
330 assert(data);
331
332 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
333 if (r < 0)
334 return log_oom();
335
336 r = safe_atou32(rvalue, &vni);
337 if (r < 0) {
338 log_syntax(unit, LOG_WARNING, filename, line, r,
339 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
340 rvalue);
341 return 0;
342 }
343
344 if (vni > VXLAN_VID_MAX) {
345 log_syntax(unit, LOG_WARNING, filename, line, 0,
346 "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
347 rvalue);
348 return 0;
349 }
350
351 fdb_entry->vni = vni;
352 fdb_entry = NULL;
353
354 return 0;
355 }
356
357 static const char* const fdb_ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = {
358 [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use",
359 [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self",
360 [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master",
361 [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router",
362 };
363
364 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(fdb_ntf_flags, NeighborCacheEntryFlags);
365
366 int config_parse_fdb_ntf_flags(
367 const char *unit,
368 const char *filename,
369 unsigned line,
370 const char *section,
371 unsigned section_line,
372 const char *lvalue,
373 int ltype,
374 const char *rvalue,
375 void *data,
376 void *userdata) {
377
378 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
379 Network *network = userdata;
380 NeighborCacheEntryFlags f;
381 int r;
382
383 assert(filename);
384 assert(section);
385 assert(lvalue);
386 assert(rvalue);
387 assert(data);
388
389 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
390 if (r < 0)
391 return log_oom();
392
393 f = fdb_ntf_flags_from_string(rvalue);
394 if (f < 0) {
395 log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
396 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
397 rvalue);
398 return 0;
399 }
400
401 fdb_entry->fdb_ntf_flags = f;
402 fdb_entry = NULL;
403
404 return 0;
405 }