]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
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" |
737f1405 | 10 | #include "bridge.h" |
fc2f9534 | 11 | #include "netlink-util.h" |
9671ae9d | 12 | #include "networkd-bridge-fdb.h" |
ee446d57 | 13 | #include "networkd-link.h" |
23f53b99 | 14 | #include "networkd-manager.h" |
ee446d57 | 15 | #include "networkd-network.h" |
e5b35bf6 YW |
16 | #include "networkd-queue.h" |
17 | #include "networkd-util.h" | |
61b824c5 | 18 | #include "parse-util.h" |
bdb397ed | 19 | #include "string-table.h" |
0e83e7a5 | 20 | #include "vlan-util.h" |
737f1405 | 21 | #include "vxlan.h" |
b98b483b | 22 | |
9671ae9d | 23 | #define STATIC_BRIDGE_FDB_ENTRIES_PER_NETWORK_MAX 1024U |
8c34b963 | 24 | |
df3a18f8 | 25 | /* remove and FDB entry. */ |
9671ae9d YW |
26 | BridgeFDB *bridge_fdb_free(BridgeFDB *fdb) { |
27 | if (!fdb) | |
df3a18f8 YW |
28 | return NULL; |
29 | ||
9671ae9d YW |
30 | if (fdb->network) { |
31 | assert(fdb->section); | |
32 | hashmap_remove(fdb->network->bridge_fdb_entries_by_section, fdb->section); | |
df3a18f8 YW |
33 | } |
34 | ||
307fe3cd | 35 | config_section_free(fdb->section); |
af99cdf4 SS |
36 | |
37 | free(fdb->outgoing_ifname); | |
9671ae9d | 38 | return mfree(fdb); |
df3a18f8 YW |
39 | } |
40 | ||
307fe3cd | 41 | DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeFDB, bridge_fdb_free); |
df3a18f8 | 42 | |
b98b483b | 43 | /* create a new FDB entry or get an existing one. */ |
9671ae9d | 44 | static int bridge_fdb_new_static( |
8c34b963 | 45 | Network *network, |
1a75764a YW |
46 | const char *filename, |
47 | unsigned section_line, | |
9671ae9d | 48 | BridgeFDB **ret) { |
8c34b963 | 49 | |
307fe3cd | 50 | _cleanup_(config_section_freep) ConfigSection *n = NULL; |
9671ae9d | 51 | _cleanup_(bridge_fdb_freep) BridgeFDB *fdb = NULL; |
1a75764a | 52 | int r; |
b98b483b AR |
53 | |
54 | assert(network); | |
8c34b963 | 55 | assert(ret); |
62ed9442 YW |
56 | assert(filename); |
57 | assert(section_line > 0); | |
1a75764a | 58 | |
307fe3cd | 59 | r = config_section_new(filename, section_line, &n); |
62ed9442 YW |
60 | if (r < 0) |
61 | return r; | |
b98b483b | 62 | |
62ed9442 | 63 | /* search entry in hashmap first. */ |
9671ae9d YW |
64 | fdb = hashmap_get(network->bridge_fdb_entries_by_section, n); |
65 | if (fdb) { | |
66 | *ret = TAKE_PTR(fdb); | |
62ed9442 | 67 | return 0; |
b98b483b AR |
68 | } |
69 | ||
9671ae9d | 70 | if (hashmap_size(network->bridge_fdb_entries_by_section) >= STATIC_BRIDGE_FDB_ENTRIES_PER_NETWORK_MAX) |
8c34b963 LP |
71 | return -E2BIG; |
72 | ||
b98b483b | 73 | /* allocate space for and FDB entry. */ |
9671ae9d YW |
74 | fdb = new(BridgeFDB, 1); |
75 | if (!fdb) | |
b98b483b | 76 | return -ENOMEM; |
b98b483b AR |
77 | |
78 | /* init FDB structure. */ | |
9671ae9d | 79 | *fdb = (BridgeFDB) { |
1a75764a | 80 | .network = network, |
62ed9442 | 81 | .section = TAKE_PTR(n), |
61b824c5 | 82 | .vni = VXLAN_VID_MAX + 1, |
9671ae9d | 83 | .ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF, |
1a75764a | 84 | }; |
b98b483b | 85 | |
307fe3cd | 86 | r = hashmap_ensure_put(&network->bridge_fdb_entries_by_section, &config_section_hash_ops, fdb->section, fdb); |
62ed9442 YW |
87 | if (r < 0) |
88 | return r; | |
b98b483b AR |
89 | |
90 | /* return allocated FDB structure. */ | |
9671ae9d | 91 | *ret = TAKE_PTR(fdb); |
b98b483b AR |
92 | |
93 | return 0; | |
94 | } | |
95 | ||
80d62d4f | 96 | static int bridge_fdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { |
b98b483b AR |
97 | int r; |
98 | ||
80d62d4f | 99 | assert(m); |
ea6ec096 | 100 | assert(link); |
4ff296b0 | 101 | |
1c4baffc | 102 | r = sd_netlink_message_get_errno(m); |
4ff296b0 | 103 | if (r < 0 && r != -EEXIST) { |
e5b35bf6 | 104 | log_link_message_warning_errno(link, m, r, "Could not add bridge FDB entry"); |
4ff296b0 | 105 | link_enter_failed(link); |
e5b35bf6 | 106 | return 0; |
4ff296b0 | 107 | } |
b98b483b | 108 | |
e5b35bf6 YW |
109 | if (link->static_bridge_fdb_messages == 0) { |
110 | log_link_debug(link, "Bridge FDB entries set"); | |
111 | link->static_bridge_fdb_configured = true; | |
112 | link_check_ready(link); | |
113 | } | |
114 | ||
115 | return 0; | |
b98b483b AR |
116 | } |
117 | ||
118 | /* send a request to the kernel to add a FDB entry in its static MAC table. */ | |
bd187da8 | 119 | static int bridge_fdb_configure_message(const BridgeFDB *fdb, Link *link, sd_netlink_message *req) { |
61b824c5 | 120 | int r; |
b98b483b | 121 | |
e5b35bf6 | 122 | assert(fdb); |
ea6ec096 | 123 | assert(link); |
b98b483b | 124 | |
9671ae9d | 125 | r = sd_rtnl_message_neigh_set_flags(req, fdb->ntf_flags); |
b98b483b | 126 | if (r < 0) |
bd187da8 | 127 | return r; |
b98b483b AR |
128 | |
129 | /* only NUD_PERMANENT state supported. */ | |
130 | r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT); | |
131 | if (r < 0) | |
bd187da8 | 132 | return r; |
b98b483b | 133 | |
9671ae9d | 134 | r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb->mac_addr, sizeof(fdb->mac_addr)); |
b98b483b | 135 | if (r < 0) |
bd187da8 | 136 | return r; |
b98b483b AR |
137 | |
138 | /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */ | |
9671ae9d YW |
139 | if (fdb->vlan_id > 0) { |
140 | r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb->vlan_id); | |
b98b483b | 141 | if (r < 0) |
bd187da8 | 142 | return r; |
b98b483b AR |
143 | } |
144 | ||
af99cdf4 SS |
145 | if (fdb->outgoing_ifindex > 0) { |
146 | r = sd_netlink_message_append_u32(req, NDA_IFINDEX, fdb->outgoing_ifindex); | |
147 | if (r < 0) | |
bd187da8 | 148 | return r; |
af99cdf4 SS |
149 | } |
150 | ||
9671ae9d YW |
151 | if (in_addr_is_set(fdb->family, &fdb->destination_addr)) { |
152 | r = netlink_message_append_in_addr_union(req, NDA_DST, fdb->family, &fdb->destination_addr); | |
c2c2793f | 153 | if (r < 0) |
bd187da8 | 154 | return r; |
c2c2793f SS |
155 | } |
156 | ||
9671ae9d YW |
157 | if (fdb->vni <= VXLAN_VID_MAX) { |
158 | r = sd_netlink_message_append_u32(req, NDA_VNI, fdb->vni); | |
61b824c5 | 159 | if (r < 0) |
bd187da8 | 160 | return r; |
61b824c5 SS |
161 | } |
162 | ||
bd187da8 | 163 | return 0; |
b98b483b AR |
164 | } |
165 | ||
54ff39f7 | 166 | static int bridge_fdb_configure(BridgeFDB *fdb, Link *link, Request *req) { |
a79a8d16 YW |
167 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
168 | int r; | |
169 | ||
170 | assert(fdb); | |
171 | assert(link); | |
172 | assert(link->manager); | |
54ff39f7 | 173 | assert(req); |
a79a8d16 YW |
174 | |
175 | r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE); | |
176 | if (r < 0) | |
177 | return r; | |
178 | ||
179 | r = bridge_fdb_configure_message(fdb, link, m); | |
180 | if (r < 0) | |
181 | return r; | |
182 | ||
80d62d4f | 183 | return request_call_netlink_async(link->manager->rtnl, m, req); |
a79a8d16 YW |
184 | } |
185 | ||
af99cdf4 SS |
186 | static bool bridge_fdb_is_ready_to_configure(BridgeFDB *fdb, Link *link) { |
187 | Link *out = NULL; | |
188 | ||
189 | assert(fdb); | |
190 | assert(link); | |
191 | assert(link->manager); | |
192 | ||
193 | if (!link_is_ready_to_configure(link, false)) | |
194 | return false; | |
195 | ||
196 | if (fdb->outgoing_ifname) { | |
197 | if (link_get_by_name(link->manager, fdb->outgoing_ifname, &out) < 0) | |
198 | return false; | |
199 | ||
200 | fdb->outgoing_ifindex = out->ifindex; | |
201 | } else if (fdb->outgoing_ifindex > 0) { | |
6eab614d | 202 | if (link_get_by_index(link->manager, fdb->outgoing_ifindex, &out) < 0) |
af99cdf4 SS |
203 | return false; |
204 | } | |
205 | if (out && !link_is_ready_to_configure(out, false)) | |
206 | return false; | |
207 | ||
208 | return true; | |
209 | } | |
210 | ||
09d09207 | 211 | static int bridge_fdb_process_request(Request *req, Link *link, void *userdata) { |
ff51134c | 212 | BridgeFDB *fdb = ASSERT_PTR(userdata); |
bd187da8 ZJS |
213 | int r; |
214 | ||
e5b35bf6 | 215 | assert(req); |
ff51134c | 216 | assert(link); |
e5b35bf6 | 217 | |
a79a8d16 | 218 | if (!bridge_fdb_is_ready_to_configure(fdb, link)) |
e5b35bf6 YW |
219 | return 0; |
220 | ||
54ff39f7 | 221 | r = bridge_fdb_configure(fdb, link, req); |
bd187da8 | 222 | if (r < 0) |
a79a8d16 | 223 | return log_link_warning_errno(link, r, "Failed to configure bridge FDB: %m"); |
bd187da8 ZJS |
224 | |
225 | return 1; | |
e5b35bf6 YW |
226 | } |
227 | ||
8bed7c55 YW |
228 | int link_request_static_bridge_fdb(Link *link) { |
229 | BridgeFDB *fdb; | |
230 | int r; | |
231 | ||
232 | assert(link); | |
233 | assert(link->network); | |
234 | ||
235 | link->static_bridge_fdb_configured = false; | |
236 | ||
237 | HASHMAP_FOREACH(fdb, link->network->bridge_fdb_entries_by_section) { | |
09d09207 YW |
238 | r = link_queue_request_full(link, REQUEST_TYPE_BRIDGE_FDB, |
239 | fdb, NULL, | |
240 | trivial_hash_func, | |
241 | trivial_compare_func, | |
242 | bridge_fdb_process_request, | |
243 | &link->static_bridge_fdb_messages, | |
244 | bridge_fdb_configure_handler, | |
245 | NULL); | |
8bed7c55 YW |
246 | if (r < 0) |
247 | return log_link_error_errno(link, r, "Failed to request static bridge FDB entry: %m"); | |
248 | } | |
249 | ||
250 | if (link->static_bridge_fdb_messages == 0) { | |
251 | link->static_bridge_fdb_configured = true; | |
252 | link_check_ready(link); | |
253 | } else { | |
254 | log_link_debug(link, "Setting bridge FDB entries"); | |
255 | link_set_state(link, LINK_STATE_CONFIGURING); | |
256 | } | |
257 | ||
258 | return 0; | |
259 | } | |
260 | ||
9671ae9d YW |
261 | void network_drop_invalid_bridge_fdb_entries(Network *network) { |
262 | BridgeFDB *fdb; | |
e6ad630d YW |
263 | |
264 | assert(network); | |
265 | ||
9671ae9d YW |
266 | HASHMAP_FOREACH(fdb, network->bridge_fdb_entries_by_section) |
267 | if (section_is_invalid(fdb->section)) | |
268 | bridge_fdb_free(fdb); | |
e6ad630d YW |
269 | } |
270 | ||
b98b483b | 271 | /* parse the HW address from config files. */ |
8519d8f5 LP |
272 | int config_parse_fdb_hwaddr( |
273 | const char *unit, | |
274 | const char *filename, | |
275 | unsigned line, | |
276 | const char *section, | |
277 | unsigned section_line, | |
278 | const char *lvalue, | |
279 | int ltype, | |
280 | const char *rvalue, | |
281 | void *data, | |
282 | void *userdata) { | |
283 | ||
9671ae9d | 284 | _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL; |
b98b483b | 285 | Network *network = userdata; |
b98b483b AR |
286 | int r; |
287 | ||
288 | assert(filename); | |
289 | assert(section); | |
290 | assert(lvalue); | |
291 | assert(rvalue); | |
292 | assert(data); | |
293 | ||
9671ae9d | 294 | r = bridge_fdb_new_static(network, filename, section_line, &fdb); |
6a7a4e4d LP |
295 | if (r < 0) |
296 | return log_oom(); | |
b98b483b | 297 | |
227e9ce2 | 298 | r = parse_ether_addr(rvalue, &fdb->mac_addr); |
f5bab0d9 | 299 | if (r < 0) { |
d96edb2c | 300 | log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue); |
b98b483b AR |
301 | return 0; |
302 | } | |
303 | ||
9671ae9d | 304 | TAKE_PTR(fdb); |
b98b483b AR |
305 | return 0; |
306 | } | |
307 | ||
308 | /* parse the VLAN Id from config files. */ | |
8519d8f5 LP |
309 | int config_parse_fdb_vlan_id( |
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 | ||
9671ae9d | 321 | _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL; |
b98b483b | 322 | Network *network = userdata; |
b98b483b AR |
323 | int r; |
324 | ||
325 | assert(filename); | |
326 | assert(section); | |
327 | assert(lvalue); | |
328 | assert(rvalue); | |
329 | assert(data); | |
330 | ||
9671ae9d | 331 | r = bridge_fdb_new_static(network, filename, section_line, &fdb); |
6a7a4e4d LP |
332 | if (r < 0) |
333 | return log_oom(); | |
b98b483b | 334 | |
0e83e7a5 TJ |
335 | r = config_parse_vlanid(unit, filename, line, section, |
336 | section_line, lvalue, ltype, | |
9671ae9d | 337 | rvalue, &fdb->vlan_id, userdata); |
6a7a4e4d | 338 | if (r < 0) |
b98b483b | 339 | return r; |
b98b483b | 340 | |
9671ae9d | 341 | TAKE_PTR(fdb); |
b98b483b AR |
342 | return 0; |
343 | } | |
c2c2793f SS |
344 | |
345 | int config_parse_fdb_destination( | |
346 | const char *unit, | |
347 | const char *filename, | |
348 | unsigned line, | |
349 | const char *section, | |
350 | unsigned section_line, | |
351 | const char *lvalue, | |
352 | int ltype, | |
353 | const char *rvalue, | |
354 | void *data, | |
355 | void *userdata) { | |
356 | ||
9671ae9d | 357 | _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL; |
c2c2793f SS |
358 | Network *network = userdata; |
359 | int r; | |
360 | ||
361 | assert(filename); | |
362 | assert(section); | |
363 | assert(lvalue); | |
364 | assert(rvalue); | |
365 | assert(data); | |
366 | ||
9671ae9d | 367 | r = bridge_fdb_new_static(network, filename, section_line, &fdb); |
c2c2793f SS |
368 | if (r < 0) |
369 | return log_oom(); | |
370 | ||
9671ae9d | 371 | r = in_addr_from_string_auto(rvalue, &fdb->family, &fdb->destination_addr); |
e5f1b999 LP |
372 | if (r < 0) { |
373 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
374 | "FDB destination IP address is invalid, ignoring assignment: %s", | |
375 | rvalue); | |
376 | return 0; | |
377 | } | |
c2c2793f | 378 | |
9671ae9d | 379 | TAKE_PTR(fdb); |
c2c2793f SS |
380 | return 0; |
381 | } | |
61b824c5 SS |
382 | |
383 | int config_parse_fdb_vxlan_vni( | |
384 | const char *unit, | |
385 | const char *filename, | |
386 | unsigned line, | |
387 | const char *section, | |
388 | unsigned section_line, | |
389 | const char *lvalue, | |
390 | int ltype, | |
391 | const char *rvalue, | |
392 | void *data, | |
393 | void *userdata) { | |
394 | ||
9671ae9d | 395 | _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL; |
61b824c5 SS |
396 | Network *network = userdata; |
397 | uint32_t vni; | |
398 | int r; | |
399 | ||
400 | assert(filename); | |
401 | assert(section); | |
402 | assert(lvalue); | |
403 | assert(rvalue); | |
404 | assert(data); | |
405 | ||
9671ae9d | 406 | r = bridge_fdb_new_static(network, filename, section_line, &fdb); |
61b824c5 SS |
407 | if (r < 0) |
408 | return log_oom(); | |
409 | ||
410 | r = safe_atou32(rvalue, &vni); | |
411 | if (r < 0) { | |
d96edb2c | 412 | log_syntax(unit, LOG_WARNING, filename, line, r, |
61b824c5 SS |
413 | "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s", |
414 | rvalue); | |
415 | return 0; | |
416 | } | |
417 | ||
418 | if (vni > VXLAN_VID_MAX) { | |
d96edb2c | 419 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
61b824c5 SS |
420 | "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s", |
421 | rvalue); | |
422 | return 0; | |
423 | } | |
424 | ||
9671ae9d | 425 | fdb->vni = vni; |
61b824c5 | 426 | |
9671ae9d | 427 | TAKE_PTR(fdb); |
61b824c5 SS |
428 | return 0; |
429 | } | |
bdb397ed | 430 | |
9671ae9d | 431 | static const char* const ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = { |
75ffb011 YW |
432 | [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use", |
433 | [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self", | |
434 | [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master", | |
435 | [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router", | |
436 | }; | |
437 | ||
9671ae9d | 438 | DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(ntf_flags, NeighborCacheEntryFlags); |
75ffb011 | 439 | |
bdb397ed SS |
440 | int config_parse_fdb_ntf_flags( |
441 | const char *unit, | |
442 | const char *filename, | |
443 | unsigned line, | |
444 | const char *section, | |
445 | unsigned section_line, | |
446 | const char *lvalue, | |
447 | int ltype, | |
448 | const char *rvalue, | |
449 | void *data, | |
450 | void *userdata) { | |
451 | ||
9671ae9d | 452 | _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL; |
bdb397ed | 453 | Network *network = userdata; |
9671ae9d | 454 | NeighborCacheEntryFlags f; |
bdb397ed SS |
455 | int r; |
456 | ||
457 | assert(filename); | |
458 | assert(section); | |
459 | assert(lvalue); | |
460 | assert(rvalue); | |
461 | assert(data); | |
462 | ||
9671ae9d | 463 | r = bridge_fdb_new_static(network, filename, section_line, &fdb); |
bdb397ed SS |
464 | if (r < 0) |
465 | return log_oom(); | |
466 | ||
9671ae9d | 467 | f = ntf_flags_from_string(rvalue); |
bdb397ed | 468 | if (f < 0) { |
7211c853 | 469 | log_syntax(unit, LOG_WARNING, filename, line, f, |
bdb397ed SS |
470 | "FDB failed to parse AssociatedWith=, ignoring assignment: %s", |
471 | rvalue); | |
472 | return 0; | |
473 | } | |
474 | ||
9671ae9d | 475 | fdb->ntf_flags = f; |
bdb397ed | 476 | |
9671ae9d | 477 | TAKE_PTR(fdb); |
bdb397ed SS |
478 | return 0; |
479 | } | |
af99cdf4 SS |
480 | |
481 | int config_parse_fdb_interface( | |
482 | const char *unit, | |
483 | const char *filename, | |
484 | unsigned line, | |
485 | const char *section, | |
486 | unsigned section_line, | |
487 | const char *lvalue, | |
488 | int ltype, | |
489 | const char *rvalue, | |
490 | void *data, | |
491 | void *userdata) { | |
492 | ||
493 | _cleanup_(bridge_fdb_free_or_set_invalidp) BridgeFDB *fdb = NULL; | |
494 | Network *network = userdata; | |
495 | int r; | |
496 | ||
497 | assert(filename); | |
498 | assert(section); | |
499 | assert(lvalue); | |
500 | assert(rvalue); | |
501 | assert(data); | |
502 | ||
503 | r = bridge_fdb_new_static(network, filename, section_line, &fdb); | |
504 | if (r < 0) | |
505 | return log_oom(); | |
506 | ||
507 | if (isempty(rvalue)) { | |
508 | fdb->outgoing_ifname = mfree(fdb->outgoing_ifname); | |
509 | fdb->outgoing_ifindex = 0; | |
510 | TAKE_PTR(fdb); | |
511 | return 0; | |
512 | } | |
513 | ||
514 | r = parse_ifindex(rvalue); | |
515 | if (r > 0) { | |
516 | fdb->outgoing_ifname = mfree(fdb->outgoing_ifname); | |
517 | fdb->outgoing_ifindex = r; | |
518 | TAKE_PTR(fdb); | |
519 | return 0; | |
520 | } | |
521 | ||
522 | if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) { | |
523 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
524 | "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue); | |
525 | return 0; | |
526 | } | |
527 | ||
528 | r = free_and_strdup(&fdb->outgoing_ifname, rvalue); | |
529 | if (r < 0) | |
530 | return log_oom(); | |
531 | fdb->outgoing_ifindex = 0; | |
532 | ||
533 | TAKE_PTR(fdb); | |
534 | return 0; | |
535 | } |