Branch data 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 : : }
|