]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-fdb.c
tree-wide: prefer AF_xyz over PF_xyz
[thirdparty/systemd.git] / src / network / networkd-fdb.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
b98b483b 2/***
810adae9 3 Copyright © 2014 Intel Corporation. All rights reserved.
b98b483b
AR
4***/
5
b98b483b 6#include <net/ethernet.h>
cf0fbc49 7#include <net/if.h>
b98b483b 8
b5efdb8a 9#include "alloc-util.h"
737f1405 10#include "bridge.h"
b98b483b 11#include "conf-parser.h"
fc2f9534 12#include "netlink-util.h"
fc2f9534 13#include "networkd-fdb.h"
23f53b99 14#include "networkd-manager.h"
61b824c5
SS
15#include "parse-util.h"
16#include "string-util.h"
bdb397ed 17#include "string-table.h"
b5efdb8a 18#include "util.h"
0e83e7a5 19#include "vlan-util.h"
737f1405 20#include "vxlan.h"
b98b483b 21
8c34b963
LP
22#define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U
23
bdb397ed
SS
24static 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
31DEFINE_STRING_TABLE_LOOKUP(fdb_ntf_flags, NeighborCacheEntryFlags);
32
b98b483b 33/* create a new FDB entry or get an existing one. */
9560e5b3 34static int fdb_entry_new_static(
8c34b963 35 Network *network,
1a75764a
YW
36 const char *filename,
37 unsigned section_line,
8c34b963
LP
38 FdbEntry **ret) {
39
1a75764a 40 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
8e766630 41 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
1a75764a 42 int r;
b98b483b
AR
43
44 assert(network);
8c34b963 45 assert(ret);
1a75764a 46 assert(!!filename == (section_line > 0));
b98b483b
AR
47
48 /* search entry in hashmap first. */
1a75764a
YW
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);
b98b483b 55 if (fdb_entry) {
1cc6c93a 56 *ret = TAKE_PTR(fdb_entry);
b98b483b
AR
57
58 return 0;
59 }
60 }
61
8c34b963
LP
62 if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
63 return -E2BIG;
64
b98b483b 65 /* allocate space for and FDB entry. */
1a75764a
YW
66 fdb_entry = new(FdbEntry, 1);
67 if (!fdb_entry)
b98b483b 68 return -ENOMEM;
b98b483b
AR
69
70 /* init FDB structure. */
1a75764a
YW
71 *fdb_entry = (FdbEntry) {
72 .network = network,
61b824c5 73 .vni = VXLAN_VID_MAX + 1,
bdb397ed 74 .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
1a75764a 75 };
b98b483b
AR
76
77 LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
8c34b963 78 network->n_static_fdb_entries++;
b98b483b 79
1a75764a
YW
80 if (filename) {
81 fdb_entry->section = TAKE_PTR(n);
82
3e570042
YW
83 r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops);
84 if (r < 0)
85 return r;
86
1a75764a
YW
87 r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry);
88 if (r < 0)
89 return r;
b98b483b
AR
90 }
91
92 /* return allocated FDB structure. */
1cc6c93a 93 *ret = TAKE_PTR(fdb_entry);
b98b483b
AR
94
95 return 0;
96}
97
302a796f 98static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
b98b483b
AR
99 int r;
100
ea6ec096 101 assert(link);
b98b483b 102
4ff296b0
YW
103 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
104 return 1;
105
1c4baffc 106 r = sd_netlink_message_get_errno(m);
4ff296b0 107 if (r < 0 && r != -EEXIST) {
5ecb131d 108 log_link_message_warning_errno(link, m, r, "Could not add FDB entry");
4ff296b0
YW
109 link_enter_failed(link);
110 return 1;
111 }
b98b483b
AR
112
113 return 1;
114}
115
116/* send a request to the kernel to add a FDB entry in its static MAC table. */
a60a720c 117int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
4afd3348 118 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
61b824c5 119 int r;
b98b483b 120
ea6ec096 121 assert(link);
f6bb7ac5 122 assert(link->network);
ea6ec096 123 assert(link->manager);
b98b483b 124 assert(fdb_entry);
ea6ec096 125
b98b483b 126 /* create new RTM message */
40eb1b0a 127 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE);
b98b483b 128 if (r < 0)
98b02994 129 return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m");
b98b483b 130
bdb397ed 131 r = sd_rtnl_message_neigh_set_flags(req, fdb_entry->fdb_ntf_flags);
b98b483b 132 if (r < 0)
98b02994 133 return log_link_error_errno(link, r, "Could not set neighbor flags: %m");
b98b483b
AR
134
135 /* only NUD_PERMANENT state supported. */
136 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
137 if (r < 0)
98b02994 138 return log_link_error_errno(link, r, "Could not set neighbor state: %m");
b98b483b 139
8f8404d4 140 r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb_entry->mac_addr, sizeof(fdb_entry->mac_addr));
b98b483b 141 if (r < 0)
98b02994 142 return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m");
b98b483b
AR
143
144 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
f143c650 145 if (fdb_entry->vlan_id > 0) {
1c4baffc 146 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
b98b483b 147 if (r < 0)
98b02994 148 return log_link_error_errno(link, r, "Could not append NDA_VLAN attribute: %m");
b98b483b
AR
149 }
150
c2c2793f
SS
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
61b824c5
SS
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
b98b483b 163 /* send message to the kernel to update its internal static MAC table. */
bdb397ed 164 r = netlink_call_async(link->manager->rtnl, NULL, req, set_fdb_handler,
302a796f 165 link_netlink_destroy_callback, link);
6a7a4e4d
LP
166 if (r < 0)
167 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
b98b483b 168
1046bf9b
YW
169 link_ref(link);
170
b0ab85a2 171 return 1;
b98b483b
AR
172}
173
174/* remove and FDB entry. */
175void fdb_entry_free(FdbEntry *fdb_entry) {
9ed794a3 176 if (!fdb_entry)
b98b483b
AR
177 return;
178
9ed794a3 179 if (fdb_entry->network) {
8c34b963 180 LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
8c34b963
LP
181 assert(fdb_entry->network->n_static_fdb_entries > 0);
182 fdb_entry->network->n_static_fdb_entries--;
b98b483b 183
8519d8f5 184 if (fdb_entry->section)
1a75764a 185 hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
b98b483b
AR
186 }
187
1a75764a 188 network_config_section_free(fdb_entry->section);
b98b483b
AR
189 free(fdb_entry);
190}
191
192/* parse the HW address from config files. */
8519d8f5
LP
193int 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
b98b483b 205 Network *network = userdata;
fcbf4cb7 206 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
b98b483b
AR
207 int r;
208
209 assert(filename);
210 assert(section);
211 assert(lvalue);
212 assert(rvalue);
213 assert(data);
214
1a75764a 215 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
6a7a4e4d
LP
216 if (r < 0)
217 return log_oom();
b98b483b 218
f5bab0d9
YW
219 r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
220 if (r < 0) {
d96edb2c 221 log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
b98b483b
AR
222 return 0;
223 }
224
225 fdb_entry = NULL;
226
227 return 0;
228}
229
230/* parse the VLAN Id from config files. */
8519d8f5
LP
231int 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
b98b483b 243 Network *network = userdata;
fcbf4cb7 244 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
b98b483b
AR
245 int r;
246
247 assert(filename);
248 assert(section);
249 assert(lvalue);
250 assert(rvalue);
251 assert(data);
252
1a75764a 253 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
6a7a4e4d
LP
254 if (r < 0)
255 return log_oom();
b98b483b 256
0e83e7a5
TJ
257 r = config_parse_vlanid(unit, filename, line, section,
258 section_line, lvalue, ltype,
259 rvalue, &fdb_entry->vlan_id, userdata);
6a7a4e4d 260 if (r < 0)
b98b483b 261 return r;
b98b483b
AR
262
263 fdb_entry = NULL;
264
265 return 0;
266}
c2c2793f
SS
267
268int 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)
d96edb2c 296 return log_syntax(unit, LOG_WARNING, filename, line, r,
c2c2793f
SS
297 "FDB destination IP address is invalid, ignoring assignment: %s",
298 rvalue);
299
300 fdb_entry = NULL;
301
302 return 0;
303}
61b824c5
SS
304
305int 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) {
d96edb2c 334 log_syntax(unit, LOG_WARNING, filename, line, r,
61b824c5
SS
335 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
336 rvalue);
337 return 0;
338 }
339
340 if (vni > VXLAN_VID_MAX) {
d96edb2c 341 log_syntax(unit, LOG_WARNING, filename, line, 0,
61b824c5
SS
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}
bdb397ed 352
bdb397ed
SS
353int 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) {
d96edb2c 382 log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
bdb397ed
SS
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}