]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-fdb.c
Merge pull request #17497 from anitazha/randomizeonce
[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_allocated(&network->fdb_entries_by_section, &network_config_hash_ops);
83 if (r < 0)
84 return r;
85
86 r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry);
87 if (r < 0)
88 return r;
89
90 /* return allocated FDB structure. */
91 *ret = TAKE_PTR(fdb_entry);
92
93 return 0;
94 }
95
96 static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
97 int r;
98
99 assert(link);
100
101 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
102 return 1;
103
104 r = sd_netlink_message_get_errno(m);
105 if (r < 0 && r != -EEXIST) {
106 log_link_message_warning_errno(link, m, r, "Could not add FDB entry");
107 link_enter_failed(link);
108 return 1;
109 }
110
111 return 1;
112 }
113
114 /* send a request to the kernel to add a FDB entry in its static MAC table. */
115 static int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
116 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
117 int r;
118
119 assert(link);
120 assert(link->network);
121 assert(link->manager);
122 assert(fdb_entry);
123
124 /* create new RTM message */
125 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE);
126 if (r < 0)
127 return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m");
128
129 r = sd_rtnl_message_neigh_set_flags(req, fdb_entry->fdb_ntf_flags);
130 if (r < 0)
131 return log_link_error_errno(link, r, "Could not set neighbor flags: %m");
132
133 /* only NUD_PERMANENT state supported. */
134 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
135 if (r < 0)
136 return log_link_error_errno(link, r, "Could not set neighbor state: %m");
137
138 r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb_entry->mac_addr, sizeof(fdb_entry->mac_addr));
139 if (r < 0)
140 return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m");
141
142 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
143 if (fdb_entry->vlan_id > 0) {
144 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
145 if (r < 0)
146 return log_link_error_errno(link, r, "Could not append NDA_VLAN attribute: %m");
147 }
148
149 if (!in_addr_is_null(fdb_entry->family, &fdb_entry->destination_addr)) {
150 r = netlink_message_append_in_addr_union(req, NDA_DST, fdb_entry->family, &fdb_entry->destination_addr);
151 if (r < 0)
152 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
153 }
154
155 if (fdb_entry->vni <= VXLAN_VID_MAX) {
156 r = sd_netlink_message_append_u32(req, NDA_VNI, fdb_entry->vni);
157 if (r < 0)
158 return log_link_error_errno(link, r, "Could not append NDA_VNI attribute: %m");
159 }
160
161 /* send message to the kernel to update its internal static MAC table. */
162 r = netlink_call_async(link->manager->rtnl, NULL, req, set_fdb_handler,
163 link_netlink_destroy_callback, link);
164 if (r < 0)
165 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
166
167 link_ref(link);
168
169 return 1;
170 }
171
172 int link_set_bridge_fdb(Link *link) {
173 FdbEntry *fdb_entry;
174 int r;
175
176 assert(link);
177 assert(link->network);
178
179 HASHMAP_FOREACH(fdb_entry, link->network->fdb_entries_by_section) {
180 r = fdb_entry_configure(link, fdb_entry);
181 if (r < 0)
182 return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
183 }
184
185 return 0;
186 }
187
188 void network_drop_invalid_fdb_entries(Network *network) {
189 FdbEntry *fdb_entry;
190
191 assert(network);
192
193 HASHMAP_FOREACH(fdb_entry, network->fdb_entries_by_section)
194 if (section_is_invalid(fdb_entry->section))
195 fdb_entry_free(fdb_entry);
196 }
197
198 /* parse the HW address from config files. */
199 int config_parse_fdb_hwaddr(
200 const char *unit,
201 const char *filename,
202 unsigned line,
203 const char *section,
204 unsigned section_line,
205 const char *lvalue,
206 int ltype,
207 const char *rvalue,
208 void *data,
209 void *userdata) {
210
211 Network *network = userdata;
212 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
213 int r;
214
215 assert(filename);
216 assert(section);
217 assert(lvalue);
218 assert(rvalue);
219 assert(data);
220
221 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
222 if (r < 0)
223 return log_oom();
224
225 r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
226 if (r < 0) {
227 log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
228 return 0;
229 }
230
231 fdb_entry = NULL;
232
233 return 0;
234 }
235
236 /* parse the VLAN Id from config files. */
237 int config_parse_fdb_vlan_id(
238 const char *unit,
239 const char *filename,
240 unsigned line,
241 const char *section,
242 unsigned section_line,
243 const char *lvalue,
244 int ltype,
245 const char *rvalue,
246 void *data,
247 void *userdata) {
248
249 Network *network = userdata;
250 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
251 int r;
252
253 assert(filename);
254 assert(section);
255 assert(lvalue);
256 assert(rvalue);
257 assert(data);
258
259 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
260 if (r < 0)
261 return log_oom();
262
263 r = config_parse_vlanid(unit, filename, line, section,
264 section_line, lvalue, ltype,
265 rvalue, &fdb_entry->vlan_id, userdata);
266 if (r < 0)
267 return r;
268
269 fdb_entry = NULL;
270
271 return 0;
272 }
273
274 int config_parse_fdb_destination(
275 const char *unit,
276 const char *filename,
277 unsigned line,
278 const char *section,
279 unsigned section_line,
280 const char *lvalue,
281 int ltype,
282 const char *rvalue,
283 void *data,
284 void *userdata) {
285
286 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
287 Network *network = userdata;
288 int r;
289
290 assert(filename);
291 assert(section);
292 assert(lvalue);
293 assert(rvalue);
294 assert(data);
295
296 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
297 if (r < 0)
298 return log_oom();
299
300 r = in_addr_from_string_auto(rvalue, &fdb_entry->family, &fdb_entry->destination_addr);
301 if (r < 0) {
302 log_syntax(unit, LOG_WARNING, filename, line, r,
303 "FDB destination IP address is invalid, ignoring assignment: %s",
304 rvalue);
305 return 0;
306 }
307
308 fdb_entry = NULL;
309
310 return 0;
311 }
312
313 int config_parse_fdb_vxlan_vni(
314 const char *unit,
315 const char *filename,
316 unsigned line,
317 const char *section,
318 unsigned section_line,
319 const char *lvalue,
320 int ltype,
321 const char *rvalue,
322 void *data,
323 void *userdata) {
324
325 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
326 Network *network = userdata;
327 uint32_t vni;
328 int r;
329
330 assert(filename);
331 assert(section);
332 assert(lvalue);
333 assert(rvalue);
334 assert(data);
335
336 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
337 if (r < 0)
338 return log_oom();
339
340 r = safe_atou32(rvalue, &vni);
341 if (r < 0) {
342 log_syntax(unit, LOG_WARNING, filename, line, r,
343 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
344 rvalue);
345 return 0;
346 }
347
348 if (vni > VXLAN_VID_MAX) {
349 log_syntax(unit, LOG_WARNING, filename, line, 0,
350 "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
351 rvalue);
352 return 0;
353 }
354
355 fdb_entry->vni = vni;
356 fdb_entry = NULL;
357
358 return 0;
359 }
360
361 static const char* const fdb_ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = {
362 [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use",
363 [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self",
364 [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master",
365 [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router",
366 };
367
368 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(fdb_ntf_flags, NeighborCacheEntryFlags);
369
370 int config_parse_fdb_ntf_flags(
371 const char *unit,
372 const char *filename,
373 unsigned line,
374 const char *section,
375 unsigned section_line,
376 const char *lvalue,
377 int ltype,
378 const char *rvalue,
379 void *data,
380 void *userdata) {
381
382 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
383 Network *network = userdata;
384 NeighborCacheEntryFlags f;
385 int r;
386
387 assert(filename);
388 assert(section);
389 assert(lvalue);
390 assert(rvalue);
391 assert(data);
392
393 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
394 if (r < 0)
395 return log_oom();
396
397 f = fdb_ntf_flags_from_string(rvalue);
398 if (f < 0) {
399 log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
400 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
401 rvalue);
402 return 0;
403 }
404
405 fdb_entry->fdb_ntf_flags = f;
406 fdb_entry = NULL;
407
408 return 0;
409 }