]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-fdb.c
43aaebb285c7784c1174c3a1d2f06bc38df2853b
[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 /* remove and FDB entry. */
34 FdbEntry *fdb_entry_free(FdbEntry *fdb_entry) {
35 if (!fdb_entry)
36 return NULL;
37
38 if (fdb_entry->network) {
39 LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
40 assert(fdb_entry->network->n_static_fdb_entries > 0);
41 fdb_entry->network->n_static_fdb_entries--;
42
43 if (fdb_entry->section)
44 hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
45 }
46
47 network_config_section_free(fdb_entry->section);
48 return mfree(fdb_entry);
49 }
50
51 DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free);
52
53 /* create a new FDB entry or get an existing one. */
54 static int fdb_entry_new_static(
55 Network *network,
56 const char *filename,
57 unsigned section_line,
58 FdbEntry **ret) {
59
60 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
61 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
62 int r;
63
64 assert(network);
65 assert(ret);
66 assert(!!filename == (section_line > 0));
67
68 /* search entry in hashmap first. */
69 if (filename) {
70 r = network_config_section_new(filename, section_line, &n);
71 if (r < 0)
72 return r;
73
74 fdb_entry = hashmap_get(network->fdb_entries_by_section, n);
75 if (fdb_entry) {
76 *ret = TAKE_PTR(fdb_entry);
77
78 return 0;
79 }
80 }
81
82 if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
83 return -E2BIG;
84
85 /* allocate space for and FDB entry. */
86 fdb_entry = new(FdbEntry, 1);
87 if (!fdb_entry)
88 return -ENOMEM;
89
90 /* init FDB structure. */
91 *fdb_entry = (FdbEntry) {
92 .network = network,
93 .vni = VXLAN_VID_MAX + 1,
94 .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
95 };
96
97 LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
98 network->n_static_fdb_entries++;
99
100 if (filename) {
101 fdb_entry->section = TAKE_PTR(n);
102
103 r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops);
104 if (r < 0)
105 return r;
106
107 r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry);
108 if (r < 0)
109 return r;
110 }
111
112 /* return allocated FDB structure. */
113 *ret = TAKE_PTR(fdb_entry);
114
115 return 0;
116 }
117
118 static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
119 int r;
120
121 assert(link);
122
123 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
124 return 1;
125
126 r = sd_netlink_message_get_errno(m);
127 if (r < 0 && r != -EEXIST) {
128 log_link_message_warning_errno(link, m, r, "Could not add FDB entry");
129 link_enter_failed(link);
130 return 1;
131 }
132
133 return 1;
134 }
135
136 /* send a request to the kernel to add a FDB entry in its static MAC table. */
137 int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
138 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
139 int r;
140
141 assert(link);
142 assert(link->network);
143 assert(link->manager);
144 assert(fdb_entry);
145
146 /* create new RTM message */
147 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE);
148 if (r < 0)
149 return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m");
150
151 r = sd_rtnl_message_neigh_set_flags(req, fdb_entry->fdb_ntf_flags);
152 if (r < 0)
153 return log_link_error_errno(link, r, "Could not set neighbor flags: %m");
154
155 /* only NUD_PERMANENT state supported. */
156 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
157 if (r < 0)
158 return log_link_error_errno(link, r, "Could not set neighbor state: %m");
159
160 r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb_entry->mac_addr, sizeof(fdb_entry->mac_addr));
161 if (r < 0)
162 return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m");
163
164 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
165 if (fdb_entry->vlan_id > 0) {
166 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
167 if (r < 0)
168 return log_link_error_errno(link, r, "Could not append NDA_VLAN attribute: %m");
169 }
170
171 if (!in_addr_is_null(fdb_entry->family, &fdb_entry->destination_addr)) {
172 r = netlink_message_append_in_addr_union(req, NDA_DST, fdb_entry->family, &fdb_entry->destination_addr);
173 if (r < 0)
174 return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
175 }
176
177 if (fdb_entry->vni <= VXLAN_VID_MAX) {
178 r = sd_netlink_message_append_u32(req, NDA_VNI, fdb_entry->vni);
179 if (r < 0)
180 return log_link_error_errno(link, r, "Could not append NDA_VNI attribute: %m");
181 }
182
183 /* send message to the kernel to update its internal static MAC table. */
184 r = netlink_call_async(link->manager->rtnl, NULL, req, set_fdb_handler,
185 link_netlink_destroy_callback, link);
186 if (r < 0)
187 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
188
189 link_ref(link);
190
191 return 1;
192 }
193
194 /* parse the HW address from config files. */
195 int 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
207 Network *network = userdata;
208 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
209 int r;
210
211 assert(filename);
212 assert(section);
213 assert(lvalue);
214 assert(rvalue);
215 assert(data);
216
217 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
218 if (r < 0)
219 return log_oom();
220
221 r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
222 if (r < 0) {
223 log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
224 return 0;
225 }
226
227 fdb_entry = NULL;
228
229 return 0;
230 }
231
232 /* parse the VLAN Id from config files. */
233 int 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
245 Network *network = userdata;
246 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
247 int r;
248
249 assert(filename);
250 assert(section);
251 assert(lvalue);
252 assert(rvalue);
253 assert(data);
254
255 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
256 if (r < 0)
257 return log_oom();
258
259 r = config_parse_vlanid(unit, filename, line, section,
260 section_line, lvalue, ltype,
261 rvalue, &fdb_entry->vlan_id, userdata);
262 if (r < 0)
263 return r;
264
265 fdb_entry = NULL;
266
267 return 0;
268 }
269
270 int 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);
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 }
303
304 fdb_entry = NULL;
305
306 return 0;
307 }
308
309 int 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) {
338 log_syntax(unit, LOG_WARNING, filename, line, r,
339 "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
340 rvalue);
341 return 0;
342 }
343
344 if (vni > VXLAN_VID_MAX) {
345 log_syntax(unit, LOG_WARNING, filename, line, 0,
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
357 int config_parse_fdb_ntf_flags(
358 const char *unit,
359 const char *filename,
360 unsigned line,
361 const char *section,
362 unsigned section_line,
363 const char *lvalue,
364 int ltype,
365 const char *rvalue,
366 void *data,
367 void *userdata) {
368
369 _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
370 Network *network = userdata;
371 NeighborCacheEntryFlags f;
372 int r;
373
374 assert(filename);
375 assert(section);
376 assert(lvalue);
377 assert(rvalue);
378 assert(data);
379
380 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
381 if (r < 0)
382 return log_oom();
383
384 f = fdb_ntf_flags_from_string(rvalue);
385 if (f < 0) {
386 log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
387 "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
388 rvalue);
389 return 0;
390 }
391
392 fdb_entry->fdb_ntf_flags = f;
393 fdb_entry = NULL;
394
395 return 0;
396 }