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