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