]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-fdb.c
tree-wide: drop duplicated blank lines
[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"
b98b483b 10#include "conf-parser.h"
23f53b99 11#include "netdev/bridge.h"
61b824c5 12#include "netdev/vxlan.h"
fc2f9534 13#include "netlink-util.h"
fc2f9534 14#include "networkd-fdb.h"
23f53b99 15#include "networkd-manager.h"
61b824c5
SS
16#include "parse-util.h"
17#include "string-util.h"
bdb397ed 18#include "string-table.h"
b5efdb8a 19#include "util.h"
0e83e7a5 20#include "vlan-util.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
1c4baffc 103 r = sd_netlink_message_get_errno(m);
ea6ec096 104 if (r < 0 && r != -EEXIST)
6a7a4e4d 105 log_link_error_errno(link, r, "Could not add FDB entry: %m");
b98b483b
AR
106
107 return 1;
108}
109
110/* send a request to the kernel to add a FDB entry in its static MAC table. */
a60a720c 111int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
4afd3348 112 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
61b824c5 113 int r;
b98b483b 114
ea6ec096 115 assert(link);
f6bb7ac5 116 assert(link->network);
ea6ec096 117 assert(link->manager);
b98b483b 118 assert(fdb_entry);
ea6ec096 119
bafa9641 120 if (fdb_entry->family == AF_INET6 && link_sysctl_ipv6_enabled(link) == 0) {
b0ab85a2
YW
121 log_link_warning(link, "An IPv6 fdb entry is requested, but IPv6 is disabled by sysctl, ignoring.");
122 return 0;
123 }
124
b98b483b 125 /* create new RTM message */
bdb397ed 126 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
b98b483b
AR
127 if (r < 0)
128 return rtnl_log_create_error(r);
129
bdb397ed 130 r = sd_rtnl_message_neigh_set_flags(req, fdb_entry->fdb_ntf_flags);
b98b483b
AR
131 if (r < 0)
132 return rtnl_log_create_error(r);
133
134 /* only NUD_PERMANENT state supported. */
135 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
136 if (r < 0)
137 return rtnl_log_create_error(r);
138
8f8404d4 139 r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb_entry->mac_addr, sizeof(fdb_entry->mac_addr));
b98b483b
AR
140 if (r < 0)
141 return rtnl_log_create_error(r);
142
143 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
f143c650 144 if (fdb_entry->vlan_id > 0) {
1c4baffc 145 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
b98b483b
AR
146 if (r < 0)
147 return rtnl_log_create_error(r);
148 }
149
c2c2793f
SS
150 if (!in_addr_is_null(fdb_entry->family, &fdb_entry->destination_addr)) {
151 r = netlink_message_append_in_addr_union(req, NDA_DST, fdb_entry->family, &fdb_entry->destination_addr);
152 if (r < 0)
153 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
154 }
155
61b824c5
SS
156 if (fdb_entry->vni <= VXLAN_VID_MAX) {
157 r = sd_netlink_message_append_u32(req, NDA_VNI, fdb_entry->vni);
158 if (r < 0)
159 return log_link_error_errno(link, r, "Could not append NDA_VNI attribute: %m");
160 }
161
b98b483b 162 /* send message to the kernel to update its internal static MAC table. */
bdb397ed 163 r = netlink_call_async(link->manager->rtnl, NULL, req, set_fdb_handler,
302a796f 164 link_netlink_destroy_callback, link);
6a7a4e4d
LP
165 if (r < 0)
166 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
b98b483b 167
1046bf9b
YW
168 link_ref(link);
169
b0ab85a2 170 return 1;
b98b483b
AR
171}
172
173/* remove and FDB entry. */
174void fdb_entry_free(FdbEntry *fdb_entry) {
9ed794a3 175 if (!fdb_entry)
b98b483b
AR
176 return;
177
9ed794a3 178 if (fdb_entry->network) {
8c34b963 179 LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
8c34b963
LP
180 assert(fdb_entry->network->n_static_fdb_entries > 0);
181 fdb_entry->network->n_static_fdb_entries--;
b98b483b 182
8519d8f5 183 if (fdb_entry->section)
1a75764a 184 hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
b98b483b
AR
185 }
186
1a75764a 187 network_config_section_free(fdb_entry->section);
b98b483b
AR
188 free(fdb_entry);
189}
190
191/* parse the HW address from config files. */
8519d8f5
LP
192int config_parse_fdb_hwaddr(
193 const char *unit,
194 const char *filename,
195 unsigned line,
196 const char *section,
197 unsigned section_line,
198 const char *lvalue,
199 int ltype,
200 const char *rvalue,
201 void *data,
202 void *userdata) {
203
b98b483b 204 Network *network = userdata;
fcbf4cb7 205 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
b98b483b
AR
206 int r;
207
208 assert(filename);
209 assert(section);
210 assert(lvalue);
211 assert(rvalue);
212 assert(data);
213
1a75764a 214 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
6a7a4e4d
LP
215 if (r < 0)
216 return log_oom();
b98b483b 217
f5bab0d9
YW
218 r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
219 if (r < 0) {
220 log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
b98b483b
AR
221 return 0;
222 }
223
224 fdb_entry = NULL;
225
226 return 0;
227}
228
229/* parse the VLAN Id from config files. */
8519d8f5
LP
230int config_parse_fdb_vlan_id(
231 const char *unit,
232 const char *filename,
233 unsigned line,
234 const char *section,
235 unsigned section_line,
236 const char *lvalue,
237 int ltype,
238 const char *rvalue,
239 void *data,
240 void *userdata) {
241
b98b483b 242 Network *network = userdata;
fcbf4cb7 243 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
b98b483b
AR
244 int r;
245
246 assert(filename);
247 assert(section);
248 assert(lvalue);
249 assert(rvalue);
250 assert(data);
251
1a75764a 252 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
6a7a4e4d
LP
253 if (r < 0)
254 return log_oom();
b98b483b 255
0e83e7a5
TJ
256 r = config_parse_vlanid(unit, filename, line, section,
257 section_line, lvalue, ltype,
258 rvalue, &fdb_entry->vlan_id, userdata);
6a7a4e4d 259 if (r < 0)
b98b483b 260 return r;
b98b483b
AR
261
262 fdb_entry = NULL;
263
264 return 0;
265}
c2c2793f
SS
266
267int config_parse_fdb_destination(
268 const char *unit,
269 const char *filename,
270 unsigned line,
271 const char *section,
272 unsigned section_line,
273 const char *lvalue,
274 int ltype,
275 const char *rvalue,
276 void *data,
277 void *userdata) {
278
279 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
280 Network *network = userdata;
281 int r;
282
283 assert(filename);
284 assert(section);
285 assert(lvalue);
286 assert(rvalue);
287 assert(data);
288
289 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
290 if (r < 0)
291 return log_oom();
292
293 r = in_addr_from_string_auto(rvalue, &fdb_entry->family, &fdb_entry->destination_addr);
294 if (r < 0)
295 return log_syntax(unit, LOG_ERR, filename, line, r,
296 "FDB destination IP address is invalid, ignoring assignment: %s",
297 rvalue);
298
299 fdb_entry = NULL;
300
301 return 0;
302}
61b824c5
SS
303
304int config_parse_fdb_vxlan_vni(
305 const char *unit,
306 const char *filename,
307 unsigned line,
308 const char *section,
309 unsigned section_line,
310 const char *lvalue,
311 int ltype,
312 const char *rvalue,
313 void *data,
314 void *userdata) {
315
316 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
317 Network *network = userdata;
318 uint32_t vni;
319 int r;
320
321 assert(filename);
322 assert(section);
323 assert(lvalue);
324 assert(rvalue);
325 assert(data);
326
327 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
328 if (r < 0)
329 return log_oom();
330
331 r = safe_atou32(rvalue, &vni);
332 if (r < 0) {
333 log_syntax(unit, LOG_ERR, filename, line, r,
334 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
335 rvalue);
336 return 0;
337 }
338
339 if (vni > VXLAN_VID_MAX) {
340 log_syntax(unit, LOG_ERR, filename, line, 0,
341 "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
342 rvalue);
343 return 0;
344 }
345
346 fdb_entry->vni = vni;
347 fdb_entry = NULL;
348
349 return 0;
350}
bdb397ed 351
bdb397ed
SS
352int config_parse_fdb_ntf_flags(
353 const char *unit,
354 const char *filename,
355 unsigned line,
356 const char *section,
357 unsigned section_line,
358 const char *lvalue,
359 int ltype,
360 const char *rvalue,
361 void *data,
362 void *userdata) {
363
364 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
365 Network *network = userdata;
366 NeighborCacheEntryFlags f;
367 int r;
368
369 assert(filename);
370 assert(section);
371 assert(lvalue);
372 assert(rvalue);
373 assert(data);
374
375 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
376 if (r < 0)
377 return log_oom();
378
379 f = fdb_ntf_flags_from_string(rvalue);
380 if (f < 0) {
381 log_syntax(unit, LOG_ERR, filename, line, 0,
382 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
383 rvalue);
384 return 0;
385 }
386
387 fdb_entry->fdb_ntf_flags = f;
388 fdb_entry = NULL;
389
390 return 0;
391}