]> git.proxmox.com Git - systemd.git/blame - src/network/networkd-fdb.c
New upstream version 248.3
[systemd.git] / src / network / networkd-fdb.c
CommitLineData
a032b68d 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
e735f4d4 2/***
b012e921 3 Copyright © 2014 Intel Corporation. All rights reserved.
e735f4d4
MP
4***/
5
e735f4d4 6#include <net/ethernet.h>
db2df898 7#include <net/if.h>
e735f4d4 8
db2df898 9#include "alloc-util.h"
e1f67bc7 10#include "bridge.h"
d9dfd233 11#include "netlink-util.h"
d9dfd233 12#include "networkd-fdb.h"
a032b68d 13#include "networkd-link.h"
2897b343 14#include "networkd-manager.h"
a032b68d 15#include "networkd-network.h"
f2dec872 16#include "parse-util.h"
f2dec872 17#include "string-table.h"
5a920b42 18#include "vlan-util.h"
e1f67bc7 19#include "vxlan.h"
5a920b42
MP
20
21#define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U
e735f4d4 22
a032b68d
MB
23/* remove and FDB entry. */
24FdbEntry *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}
f2dec872 36
a032b68d 37DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free);
f2dec872 38
e735f4d4 39/* create a new FDB entry or get an existing one. */
bb4f798a 40static int fdb_entry_new_static(
5a920b42 41 Network *network,
6e866b33
MB
42 const char *filename,
43 unsigned section_line,
5a920b42
MP
44 FdbEntry **ret) {
45
6e866b33 46 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
b012e921 47 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
6e866b33 48 int r;
e735f4d4
MP
49
50 assert(network);
5a920b42 51 assert(ret);
a032b68d
MB
52 assert(filename);
53 assert(section_line > 0);
6e866b33 54
a032b68d
MB
55 r = network_config_section_new(filename, section_line, &n);
56 if (r < 0)
57 return r;
e735f4d4 58
a032b68d
MB
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;
e735f4d4
MP
64 }
65
a032b68d 66 if (hashmap_size(network->fdb_entries_by_section) >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
5a920b42
MP
67 return -E2BIG;
68
e735f4d4 69 /* allocate space for and FDB entry. */
6e866b33
MB
70 fdb_entry = new(FdbEntry, 1);
71 if (!fdb_entry)
e735f4d4 72 return -ENOMEM;
e735f4d4
MP
73
74 /* init FDB structure. */
6e866b33
MB
75 *fdb_entry = (FdbEntry) {
76 .network = network,
a032b68d 77 .section = TAKE_PTR(n),
f2dec872
BR
78 .vni = VXLAN_VID_MAX + 1,
79 .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
6e866b33 80 };
e735f4d4 81
3a6ce677 82 r = hashmap_ensure_put(&network->fdb_entries_by_section, &network_config_hash_ops, fdb_entry->section, fdb_entry);
a032b68d
MB
83 if (r < 0)
84 return r;
e735f4d4
MP
85
86 /* return allocated FDB structure. */
b012e921 87 *ret = TAKE_PTR(fdb_entry);
e735f4d4
MP
88
89 return 0;
90}
91
6e866b33 92static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
e735f4d4
MP
93 int r;
94
95 assert(link);
96
f2dec872
BR
97 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
98 return 1;
99
86f210e9 100 r = sd_netlink_message_get_errno(m);
f2dec872 101 if (r < 0 && r != -EEXIST) {
46cdbd49 102 log_link_message_warning_errno(link, m, r, "Could not add FDB entry");
f2dec872
BR
103 link_enter_failed(link);
104 return 1;
105 }
e735f4d4
MP
106
107 return 1;
108}
109
110/* send a request to the kernel to add a FDB entry in its static MAC table. */
a032b68d 111static int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
4c89c718 112 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
e735f4d4
MP
113 int r;
114
115 assert(link);
8a584da2 116 assert(link->network);
e735f4d4
MP
117 assert(link->manager);
118 assert(fdb_entry);
119
e735f4d4 120 /* create new RTM message */
a032b68d 121 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE);
e735f4d4 122 if (r < 0)
46cdbd49 123 return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m");
e735f4d4 124
f2dec872 125 r = sd_rtnl_message_neigh_set_flags(req, fdb_entry->fdb_ntf_flags);
e735f4d4 126 if (r < 0)
46cdbd49 127 return log_link_error_errno(link, r, "Could not set neighbor flags: %m");
e735f4d4
MP
128
129 /* only NUD_PERMANENT state supported. */
130 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
131 if (r < 0)
46cdbd49 132 return log_link_error_errno(link, r, "Could not set neighbor state: %m");
e735f4d4 133
f2dec872 134 r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb_entry->mac_addr, sizeof(fdb_entry->mac_addr));
e735f4d4 135 if (r < 0)
46cdbd49 136 return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m");
e735f4d4
MP
137
138 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
f2dec872 139 if (fdb_entry->vlan_id > 0) {
86f210e9 140 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
e735f4d4 141 if (r < 0)
46cdbd49 142 return log_link_error_errno(link, r, "Could not append NDA_VLAN attribute: %m");
e735f4d4
MP
143 }
144
3a6ce677 145 if (in_addr_is_set(fdb_entry->family, &fdb_entry->destination_addr)) {
f2dec872
BR
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
e735f4d4 157 /* send message to the kernel to update its internal static MAC table. */
f2dec872 158 r = netlink_call_async(link->manager->rtnl, NULL, req, set_fdb_handler,
6e866b33 159 link_netlink_destroy_callback, link);
e3bff60a
MP
160 if (r < 0)
161 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
e735f4d4 162
6e866b33
MB
163 link_ref(link);
164
f2dec872 165 return 1;
e735f4d4
MP
166}
167
a032b68d
MB
168int link_set_bridge_fdb(Link *link) {
169 FdbEntry *fdb_entry;
170 int r;
e735f4d4 171
a032b68d
MB
172 assert(link);
173 assert(link->network);
e735f4d4 174
a032b68d
MB
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");
e735f4d4
MP
179 }
180
a032b68d
MB
181 return 0;
182}
183
184void 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);
e735f4d4
MP
192}
193
194/* parse the HW address from config files. */
e3bff60a
MP
195int 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
e735f4d4 207 Network *network = userdata;
bb4f798a 208 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
e735f4d4
MP
209 int r;
210
211 assert(filename);
212 assert(section);
213 assert(lvalue);
214 assert(rvalue);
215 assert(data);
216
6e866b33 217 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
e3bff60a
MP
218 if (r < 0)
219 return log_oom();
e735f4d4 220
f2dec872
BR
221 r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
222 if (r < 0) {
a10f5d05 223 log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
e735f4d4
MP
224 return 0;
225 }
226
227 fdb_entry = NULL;
228
229 return 0;
230}
231
232/* parse the VLAN Id from config files. */
e3bff60a
MP
233int 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
e735f4d4 245 Network *network = userdata;
bb4f798a 246 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
e735f4d4
MP
247 int r;
248
249 assert(filename);
250 assert(section);
251 assert(lvalue);
252 assert(rvalue);
253 assert(data);
254
6e866b33 255 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
e3bff60a
MP
256 if (r < 0)
257 return log_oom();
e735f4d4 258
5a920b42
MP
259 r = config_parse_vlanid(unit, filename, line, section,
260 section_line, lvalue, ltype,
261 rvalue, &fdb_entry->vlan_id, userdata);
e3bff60a 262 if (r < 0)
e735f4d4 263 return r;
e735f4d4
MP
264
265 fdb_entry = NULL;
266
267 return 0;
268}
f2dec872
BR
269
270int 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);
7de6915e
MB
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 }
f2dec872
BR
303
304 fdb_entry = NULL;
305
306 return 0;
307}
308
309int 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) {
a10f5d05 338 log_syntax(unit, LOG_WARNING, filename, line, r,
f2dec872
BR
339 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
340 rvalue);
341 return 0;
342 }
343
344 if (vni > VXLAN_VID_MAX) {
a10f5d05 345 log_syntax(unit, LOG_WARNING, filename, line, 0,
f2dec872
BR
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
a032b68d
MB
357static 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
364DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(fdb_ntf_flags, NeighborCacheEntryFlags);
365
f2dec872
BR
366int 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;
f2dec872
BR
380 int r;
381
382 assert(filename);
383 assert(section);
384 assert(lvalue);
385 assert(rvalue);
386 assert(data);
387
388 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
389 if (r < 0)
390 return log_oom();
391
3a6ce677 392 NeighborCacheEntryFlags f = fdb_ntf_flags_from_string(rvalue);
f2dec872 393 if (f < 0) {
3a6ce677 394 log_syntax(unit, LOG_WARNING, filename, line, f,
f2dec872
BR
395 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
396 rvalue);
397 return 0;
398 }
399
400 fdb_entry->fdb_ntf_flags = f;
401 fdb_entry = NULL;
402
403 return 0;
404}