Line data Source code
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 0 : DEFINE_STRING_TABLE_LOOKUP(fdb_ntf_flags, NeighborCacheEntryFlags);
32 :
33 : /* create a new FDB entry or get an existing one. */
34 0 : static int fdb_entry_new_static(
35 : Network *network,
36 : const char *filename,
37 : unsigned section_line,
38 : FdbEntry **ret) {
39 :
40 0 : _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
41 0 : _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
42 : int r;
43 :
44 0 : assert(network);
45 0 : assert(ret);
46 0 : assert(!!filename == (section_line > 0));
47 :
48 : /* search entry in hashmap first. */
49 0 : if (filename) {
50 0 : r = network_config_section_new(filename, section_line, &n);
51 0 : if (r < 0)
52 0 : return r;
53 :
54 0 : fdb_entry = hashmap_get(network->fdb_entries_by_section, n);
55 0 : if (fdb_entry) {
56 0 : *ret = TAKE_PTR(fdb_entry);
57 :
58 0 : return 0;
59 : }
60 : }
61 :
62 0 : if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
63 0 : return -E2BIG;
64 :
65 : /* allocate space for and FDB entry. */
66 0 : fdb_entry = new(FdbEntry, 1);
67 0 : if (!fdb_entry)
68 0 : return -ENOMEM;
69 :
70 : /* init FDB structure. */
71 0 : *fdb_entry = (FdbEntry) {
72 : .network = network,
73 : .vni = VXLAN_VID_MAX + 1,
74 : .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
75 : };
76 :
77 0 : LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
78 0 : network->n_static_fdb_entries++;
79 :
80 0 : if (filename) {
81 0 : fdb_entry->section = TAKE_PTR(n);
82 :
83 0 : r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops);
84 0 : if (r < 0)
85 0 : return r;
86 :
87 0 : r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry);
88 0 : if (r < 0)
89 0 : return r;
90 : }
91 :
92 : /* return allocated FDB structure. */
93 0 : *ret = TAKE_PTR(fdb_entry);
94 :
95 0 : return 0;
96 : }
97 :
98 0 : static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
99 : int r;
100 :
101 0 : assert(link);
102 :
103 0 : if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
104 0 : return 1;
105 :
106 0 : r = sd_netlink_message_get_errno(m);
107 0 : if (r < 0 && r != -EEXIST) {
108 0 : log_link_error_errno(link, r, "Could not add FDB entry: %m");
109 0 : link_enter_failed(link);
110 0 : return 1;
111 : }
112 :
113 0 : return 1;
114 : }
115 :
116 : /* send a request to the kernel to add a FDB entry in its static MAC table. */
117 0 : int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
118 0 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
119 : int r;
120 :
121 0 : assert(link);
122 0 : assert(link->network);
123 0 : assert(link->manager);
124 0 : assert(fdb_entry);
125 :
126 0 : if (fdb_entry->family == AF_INET6 && link_sysctl_ipv6_enabled(link) == 0) {
127 0 : log_link_warning(link, "An IPv6 fdb entry is requested, but IPv6 is disabled by sysctl, ignoring.");
128 0 : return 0;
129 : }
130 :
131 : /* create new RTM message */
132 0 : r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
133 0 : if (r < 0)
134 0 : return rtnl_log_create_error(r);
135 :
136 0 : r = sd_rtnl_message_neigh_set_flags(req, fdb_entry->fdb_ntf_flags);
137 0 : if (r < 0)
138 0 : return rtnl_log_create_error(r);
139 :
140 : /* only NUD_PERMANENT state supported. */
141 0 : r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
142 0 : if (r < 0)
143 0 : return rtnl_log_create_error(r);
144 :
145 0 : r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb_entry->mac_addr, sizeof(fdb_entry->mac_addr));
146 0 : if (r < 0)
147 0 : return rtnl_log_create_error(r);
148 :
149 : /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
150 0 : if (fdb_entry->vlan_id > 0) {
151 0 : r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
152 0 : if (r < 0)
153 0 : return rtnl_log_create_error(r);
154 : }
155 :
156 0 : if (!in_addr_is_null(fdb_entry->family, &fdb_entry->destination_addr)) {
157 0 : r = netlink_message_append_in_addr_union(req, NDA_DST, fdb_entry->family, &fdb_entry->destination_addr);
158 0 : if (r < 0)
159 0 : return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
160 : }
161 :
162 0 : if (fdb_entry->vni <= VXLAN_VID_MAX) {
163 0 : r = sd_netlink_message_append_u32(req, NDA_VNI, fdb_entry->vni);
164 0 : if (r < 0)
165 0 : 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 0 : r = netlink_call_async(link->manager->rtnl, NULL, req, set_fdb_handler,
170 : link_netlink_destroy_callback, link);
171 0 : if (r < 0)
172 0 : return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
173 :
174 0 : link_ref(link);
175 :
176 0 : return 1;
177 : }
178 :
179 : /* remove and FDB entry. */
180 0 : void fdb_entry_free(FdbEntry *fdb_entry) {
181 0 : if (!fdb_entry)
182 0 : return;
183 :
184 0 : if (fdb_entry->network) {
185 0 : LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
186 0 : assert(fdb_entry->network->n_static_fdb_entries > 0);
187 0 : fdb_entry->network->n_static_fdb_entries--;
188 :
189 0 : if (fdb_entry->section)
190 0 : hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
191 : }
192 :
193 0 : network_config_section_free(fdb_entry->section);
194 0 : free(fdb_entry);
195 : }
196 :
197 : /* parse the HW address from config files. */
198 0 : 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 0 : Network *network = userdata;
211 0 : _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
212 : int r;
213 :
214 0 : assert(filename);
215 0 : assert(section);
216 0 : assert(lvalue);
217 0 : assert(rvalue);
218 0 : assert(data);
219 :
220 0 : r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
221 0 : if (r < 0)
222 0 : return log_oom();
223 :
224 0 : r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
225 0 : if (r < 0) {
226 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
227 0 : return 0;
228 : }
229 :
230 0 : fdb_entry = NULL;
231 :
232 0 : return 0;
233 : }
234 :
235 : /* parse the VLAN Id from config files. */
236 0 : 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 0 : Network *network = userdata;
249 0 : _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
250 : int r;
251 :
252 0 : assert(filename);
253 0 : assert(section);
254 0 : assert(lvalue);
255 0 : assert(rvalue);
256 0 : assert(data);
257 :
258 0 : r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
259 0 : if (r < 0)
260 0 : return log_oom();
261 :
262 0 : r = config_parse_vlanid(unit, filename, line, section,
263 : section_line, lvalue, ltype,
264 0 : rvalue, &fdb_entry->vlan_id, userdata);
265 0 : if (r < 0)
266 0 : return r;
267 :
268 0 : fdb_entry = NULL;
269 :
270 0 : return 0;
271 : }
272 :
273 0 : 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 0 : _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
286 0 : Network *network = userdata;
287 : int r;
288 :
289 0 : assert(filename);
290 0 : assert(section);
291 0 : assert(lvalue);
292 0 : assert(rvalue);
293 0 : assert(data);
294 :
295 0 : r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
296 0 : if (r < 0)
297 0 : return log_oom();
298 :
299 0 : r = in_addr_from_string_auto(rvalue, &fdb_entry->family, &fdb_entry->destination_addr);
300 0 : if (r < 0)
301 0 : return log_syntax(unit, LOG_ERR, filename, line, r,
302 : "FDB destination IP address is invalid, ignoring assignment: %s",
303 : rvalue);
304 :
305 0 : fdb_entry = NULL;
306 :
307 0 : return 0;
308 : }
309 :
310 0 : 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 0 : _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
323 0 : Network *network = userdata;
324 : uint32_t vni;
325 : int r;
326 :
327 0 : assert(filename);
328 0 : assert(section);
329 0 : assert(lvalue);
330 0 : assert(rvalue);
331 0 : assert(data);
332 :
333 0 : r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
334 0 : if (r < 0)
335 0 : return log_oom();
336 :
337 0 : r = safe_atou32(rvalue, &vni);
338 0 : if (r < 0) {
339 0 : log_syntax(unit, LOG_ERR, filename, line, r,
340 : "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
341 : rvalue);
342 0 : return 0;
343 : }
344 :
345 0 : if (vni > VXLAN_VID_MAX) {
346 0 : log_syntax(unit, LOG_ERR, filename, line, 0,
347 : "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
348 : rvalue);
349 0 : return 0;
350 : }
351 :
352 0 : fdb_entry->vni = vni;
353 0 : fdb_entry = NULL;
354 :
355 0 : return 0;
356 : }
357 :
358 0 : 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 0 : _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
371 0 : Network *network = userdata;
372 : NeighborCacheEntryFlags f;
373 : int r;
374 :
375 0 : assert(filename);
376 0 : assert(section);
377 0 : assert(lvalue);
378 0 : assert(rvalue);
379 0 : assert(data);
380 :
381 0 : r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
382 0 : if (r < 0)
383 0 : return log_oom();
384 :
385 0 : f = fdb_ntf_flags_from_string(rvalue);
386 0 : if (f < 0) {
387 0 : log_syntax(unit, LOG_ERR, filename, line, 0,
388 : "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
389 : rvalue);
390 0 : return 0;
391 : }
392 :
393 0 : fdb_entry->fdb_ntf_flags = f;
394 0 : fdb_entry = NULL;
395 :
396 0 : return 0;
397 : }
|