]>
Commit | Line | Data |
---|---|---|
344b3cff YW |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
3 | #include <linux/rtnetlink.h> | |
4 | ||
5 | #include "alloc-util.h" | |
3ae6b3bf | 6 | #include "logarithm.h" |
5545f336 | 7 | #include "missing_threads.h" |
344b3cff YW |
8 | #include "networkd-address.h" |
9 | #include "networkd-link.h" | |
10 | #include "networkd-manager.h" | |
11 | #include "networkd-route-util.h" | |
12 | #include "networkd-route.h" | |
13 | #include "parse-util.h" | |
14 | #include "string-table.h" | |
15 | #include "string-util.h" | |
16 | #include "strv.h" | |
17 | #include "sysctl-util.h" | |
18 | ||
19 | #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U | |
20 | ||
21 | unsigned routes_max(void) { | |
22 | static thread_local unsigned cached = 0; | |
23 | _cleanup_free_ char *s4 = NULL, *s6 = NULL; | |
24 | unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY; | |
25 | ||
26 | if (cached > 0) | |
27 | return cached; | |
28 | ||
29 | if (sysctl_read_ip_property(AF_INET, NULL, "route/max_size", &s4) >= 0) | |
30 | if (safe_atou(s4, &val4) >= 0 && val4 == 2147483647U) | |
31 | /* This is the default "no limit" value in the kernel */ | |
32 | val4 = ROUTES_DEFAULT_MAX_PER_FAMILY; | |
33 | ||
34 | if (sysctl_read_ip_property(AF_INET6, NULL, "route/max_size", &s6) >= 0) | |
35 | (void) safe_atou(s6, &val6); | |
36 | ||
37 | cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) + | |
38 | MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6); | |
39 | return cached; | |
40 | } | |
41 | ||
b9f29e9f YW |
42 | bool route_type_is_reject(const Route *route) { |
43 | assert(route); | |
44 | ||
45 | return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW); | |
46 | } | |
47 | ||
dc32de39 YW |
48 | static bool route_lifetime_is_valid(const Route *route) { |
49 | assert(route); | |
50 | ||
51 | return | |
52 | route->lifetime_usec == USEC_INFINITY || | |
53 | route->lifetime_usec > now(CLOCK_BOOTTIME); | |
54 | } | |
55 | ||
fc35a9f8 YW |
56 | bool link_find_default_gateway(Link *link, int family, Route **gw) { |
57 | bool found = false; | |
344b3cff YW |
58 | Route *route; |
59 | ||
60 | assert(link); | |
8d01e44c | 61 | assert(link->manager); |
344b3cff | 62 | |
8d01e44c YW |
63 | SET_FOREACH(route, link->manager->routes) { |
64 | if (route->nexthop.ifindex != link->ifindex) | |
65 | continue; | |
344b3cff YW |
66 | if (!route_exists(route)) |
67 | continue; | |
68 | if (family != AF_UNSPEC && route->family != family) | |
69 | continue; | |
70 | if (route->dst_prefixlen != 0) | |
71 | continue; | |
72 | if (route->src_prefixlen != 0) | |
73 | continue; | |
74 | if (route->table != RT_TABLE_MAIN) | |
75 | continue; | |
76 | if (route->type != RTN_UNICAST) | |
77 | continue; | |
78 | if (route->scope != RT_SCOPE_UNIVERSE) | |
79 | continue; | |
054b8c28 | 80 | if (!in_addr_is_set(route->nexthop.family, &route->nexthop.gw)) |
344b3cff | 81 | continue; |
fc35a9f8 YW |
82 | |
83 | /* Found a default gateway. */ | |
84 | if (!gw) | |
85 | return true; | |
86 | ||
87 | /* If we have already found another gw, then let's compare their weight and priority. */ | |
88 | if (*gw) { | |
054b8c28 | 89 | if (route->nexthop.weight > (*gw)->nexthop.weight) |
344b3cff | 90 | continue; |
fc35a9f8 | 91 | if (route->priority >= (*gw)->priority) |
344b3cff YW |
92 | continue; |
93 | } | |
fc35a9f8 YW |
94 | |
95 | *gw = route; | |
96 | found = true; | |
344b3cff YW |
97 | } |
98 | ||
fc35a9f8 | 99 | return found; |
344b3cff YW |
100 | } |
101 | ||
102 | int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) { | |
103 | Route *gw = NULL; | |
104 | Link *link; | |
105 | ||
106 | assert(m); | |
107 | assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6)); | |
108 | ||
109 | /* Looks for a suitable "uplink", via black magic: an interface that is up and where the | |
110 | * default route with the highest priority points to. */ | |
111 | ||
112 | HASHMAP_FOREACH(link, m->links_by_index) { | |
113 | if (link == exclude) | |
114 | continue; | |
115 | ||
116 | if (link->state != LINK_STATE_CONFIGURED) | |
117 | continue; | |
118 | ||
fc35a9f8 | 119 | link_find_default_gateway(link, family, &gw); |
344b3cff YW |
120 | } |
121 | ||
122 | if (!gw) | |
123 | return -ENOENT; | |
124 | ||
8d01e44c | 125 | return link_get_by_index(m, gw->nexthop.ifindex, ret); |
344b3cff YW |
126 | } |
127 | ||
c45cbc23 | 128 | bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw) { |
344b3cff YW |
129 | Route *route; |
130 | Address *a; | |
131 | ||
132 | assert(link); | |
133 | assert(link->manager); | |
c45cbc23 YW |
134 | |
135 | if (onlink) | |
136 | return true; | |
137 | ||
138 | if (!gw || !in_addr_is_set(family, gw)) | |
139 | return true; | |
140 | ||
141 | if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6)) | |
142 | return true; | |
344b3cff | 143 | |
8d01e44c YW |
144 | SET_FOREACH(route, link->manager->routes) { |
145 | if (route->nexthop.ifindex != link->ifindex) | |
146 | continue; | |
344b3cff YW |
147 | if (!route_exists(route)) |
148 | continue; | |
dc32de39 YW |
149 | if (!route_lifetime_is_valid(route)) |
150 | continue; | |
344b3cff YW |
151 | if (route->family != family) |
152 | continue; | |
cf477495 | 153 | if (!in_addr_is_set(route->family, &route->dst) && route->dst_prefixlen == 0) |
344b3cff | 154 | continue; |
c45cbc23 | 155 | if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, gw) > 0) |
344b3cff YW |
156 | return true; |
157 | } | |
158 | ||
159 | if (link->manager->manage_foreign_routes) | |
160 | return false; | |
161 | ||
162 | /* If we do not manage foreign routes, then there may exist a prefix route we do not know, | |
163 | * which was created on configuring an address. Hence, also check the addresses. */ | |
164 | SET_FOREACH(a, link->addresses) { | |
165 | if (!address_is_ready(a)) | |
166 | continue; | |
167 | if (a->family != family) | |
168 | continue; | |
169 | if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE)) | |
170 | continue; | |
e091ed40 YW |
171 | if (in_addr_prefix_covers(a->family, |
172 | in_addr_is_set(a->family, &a->in_addr_peer) ? &a->in_addr_peer : &a->in_addr, | |
173 | a->prefixlen, gw) > 0) | |
344b3cff YW |
174 | return true; |
175 | } | |
176 | ||
177 | return false; | |
178 | } | |
179 | ||
8b7615f9 YW |
180 | static int link_address_is_reachable_internal( |
181 | Link *link, | |
182 | int family, | |
183 | const union in_addr_union *address, | |
184 | const union in_addr_union *prefsrc, /* optional */ | |
185 | Route **ret) { | |
186 | ||
187 | Route *route, *found = NULL; | |
188 | ||
189 | assert(link); | |
8d01e44c | 190 | assert(link->manager); |
8b7615f9 YW |
191 | assert(IN_SET(family, AF_INET, AF_INET6)); |
192 | assert(address); | |
193 | ||
8d01e44c YW |
194 | SET_FOREACH(route, link->manager->routes) { |
195 | if (route->nexthop.ifindex != link->ifindex) | |
196 | continue; | |
197 | ||
8b7615f9 YW |
198 | if (!route_exists(route)) |
199 | continue; | |
200 | ||
dc32de39 YW |
201 | if (!route_lifetime_is_valid(route)) |
202 | continue; | |
203 | ||
8b7615f9 YW |
204 | if (route->type != RTN_UNICAST) |
205 | continue; | |
206 | ||
207 | if (route->family != family) | |
208 | continue; | |
209 | ||
210 | if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) <= 0) | |
211 | continue; | |
212 | ||
213 | if (prefsrc && | |
214 | in_addr_is_set(family, prefsrc) && | |
215 | in_addr_is_set(family, &route->prefsrc) && | |
216 | !in_addr_equal(family, prefsrc, &route->prefsrc)) | |
217 | continue; | |
218 | ||
219 | if (found && found->priority <= route->priority) | |
220 | continue; | |
221 | ||
222 | found = route; | |
223 | } | |
224 | ||
225 | if (!found) | |
226 | return -ENOENT; | |
227 | ||
228 | if (ret) | |
229 | *ret = found; | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | int link_address_is_reachable( | |
235 | Link *link, | |
236 | int family, | |
237 | const union in_addr_union *address, | |
238 | const union in_addr_union *prefsrc, /* optional */ | |
239 | Address **ret) { | |
240 | ||
241 | Route *route; | |
242 | Address *a; | |
243 | int r; | |
244 | ||
245 | assert(link); | |
246 | assert(IN_SET(family, AF_INET, AF_INET6)); | |
247 | assert(address); | |
248 | ||
249 | /* This checks if the address is reachable, and optionally return the Address object of the | |
250 | * preferred source to access the address. */ | |
251 | ||
252 | r = link_address_is_reachable_internal(link, family, address, prefsrc, &route); | |
253 | if (r < 0) | |
254 | return r; | |
255 | ||
256 | if (!in_addr_is_set(route->family, &route->prefsrc)) { | |
257 | if (ret) | |
258 | *ret = NULL; | |
259 | return 0; | |
260 | } | |
261 | ||
262 | r = link_get_address(link, route->family, &route->prefsrc, 0, &a); | |
263 | if (r < 0) | |
264 | return r; | |
265 | ||
266 | if (!address_is_ready(a)) | |
267 | return -EBUSY; | |
268 | ||
269 | if (ret) | |
270 | *ret = a; | |
271 | ||
272 | return 0; | |
273 | } | |
274 | ||
275 | int manager_address_is_reachable( | |
276 | Manager *manager, | |
277 | int family, | |
278 | const union in_addr_union *address, | |
279 | const union in_addr_union *prefsrc, /* optional */ | |
280 | Address **ret) { | |
281 | ||
282 | Route *route, *found = NULL; | |
283 | Address *a; | |
284 | Link *link; | |
285 | int r; | |
286 | ||
287 | assert(manager); | |
288 | ||
289 | HASHMAP_FOREACH(link, manager->links_by_index) { | |
290 | if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) | |
291 | continue; | |
292 | ||
293 | if (link_address_is_reachable_internal(link, family, address, prefsrc, &route) < 0) | |
294 | continue; | |
295 | ||
296 | if (found && found->priority <= route->priority) | |
297 | continue; | |
298 | ||
299 | found = route; | |
300 | } | |
301 | ||
302 | if (!found) | |
303 | return -ENOENT; | |
304 | ||
305 | if (!in_addr_is_set(found->family, &found->prefsrc)) { | |
306 | if (ret) | |
307 | *ret = NULL; | |
308 | return 0; | |
309 | } | |
310 | ||
8d01e44c YW |
311 | r = link_get_by_index(manager, found->nexthop.ifindex, &link); |
312 | if (r < 0) | |
313 | return r; | |
314 | ||
315 | r = link_get_address(link, found->family, &found->prefsrc, 0, &a); | |
8b7615f9 YW |
316 | if (r < 0) |
317 | return r; | |
318 | ||
319 | if (!address_is_ready(a)) | |
320 | return -EBUSY; | |
321 | ||
322 | if (ret) | |
323 | *ret = a; | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
344b3cff YW |
328 | static const char * const route_type_table[__RTN_MAX] = { |
329 | [RTN_UNICAST] = "unicast", | |
330 | [RTN_LOCAL] = "local", | |
331 | [RTN_BROADCAST] = "broadcast", | |
332 | [RTN_ANYCAST] = "anycast", | |
333 | [RTN_MULTICAST] = "multicast", | |
334 | [RTN_BLACKHOLE] = "blackhole", | |
335 | [RTN_UNREACHABLE] = "unreachable", | |
336 | [RTN_PROHIBIT] = "prohibit", | |
337 | [RTN_THROW] = "throw", | |
338 | [RTN_NAT] = "nat", | |
339 | [RTN_XRESOLVE] = "xresolve", | |
340 | }; | |
341 | ||
342 | assert_cc(__RTN_MAX <= UCHAR_MAX); | |
343 | DEFINE_STRING_TABLE_LOOKUP(route_type, int); | |
344 | ||
345 | static const char * const route_scope_table[] = { | |
346 | [RT_SCOPE_UNIVERSE] = "global", | |
347 | [RT_SCOPE_SITE] = "site", | |
348 | [RT_SCOPE_LINK] = "link", | |
349 | [RT_SCOPE_HOST] = "host", | |
350 | [RT_SCOPE_NOWHERE] = "nowhere", | |
351 | }; | |
352 | ||
353 | DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_scope, int, UINT8_MAX); | |
354 | ||
355 | static const char * const route_protocol_table[] = { | |
356 | [RTPROT_KERNEL] = "kernel", | |
357 | [RTPROT_BOOT] = "boot", | |
358 | [RTPROT_STATIC] = "static", | |
359 | }; | |
360 | ||
361 | DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_protocol, int, UINT8_MAX); | |
362 | ||
363 | static const char * const route_protocol_full_table[] = { | |
364 | [RTPROT_REDIRECT] = "redirect", | |
365 | [RTPROT_KERNEL] = "kernel", | |
366 | [RTPROT_BOOT] = "boot", | |
367 | [RTPROT_STATIC] = "static", | |
368 | [RTPROT_GATED] = "gated", | |
369 | [RTPROT_RA] = "ra", | |
370 | [RTPROT_MRT] = "mrt", | |
371 | [RTPROT_ZEBRA] = "zebra", | |
372 | [RTPROT_BIRD] = "bird", | |
373 | [RTPROT_DNROUTED] = "dnrouted", | |
374 | [RTPROT_XORP] = "xorp", | |
375 | [RTPROT_NTK] = "ntk", | |
376 | [RTPROT_DHCP] = "dhcp", | |
377 | [RTPROT_MROUTED] = "mrouted", | |
378 | [RTPROT_BABEL] = "babel", | |
379 | [RTPROT_BGP] = "bgp", | |
380 | [RTPROT_ISIS] = "isis", | |
381 | [RTPROT_OSPF] = "ospf", | |
382 | [RTPROT_RIP] = "rip", | |
383 | [RTPROT_EIGRP] = "eigrp", | |
384 | }; | |
385 | ||
386 | DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_protocol_full, int, UINT8_MAX); | |
387 | ||
18b23bd4 YW |
388 | int route_flags_to_string_alloc(uint32_t flags, char **ret) { |
389 | _cleanup_free_ char *str = NULL; | |
8a7da940 ZJS |
390 | static const char* map[] = { |
391 | [LOG2U(RTNH_F_DEAD)] = "dead", /* Nexthop is dead (used by multipath) */ | |
392 | [LOG2U(RTNH_F_PERVASIVE)] = "pervasive", /* Do recursive gateway lookup */ | |
393 | [LOG2U(RTNH_F_ONLINK)] = "onlink" , /* Gateway is forced on link */ | |
394 | [LOG2U(RTNH_F_OFFLOAD)] = "offload", /* Nexthop is offloaded */ | |
395 | [LOG2U(RTNH_F_LINKDOWN)] = "linkdown", /* carrier-down on nexthop */ | |
396 | [LOG2U(RTNH_F_UNRESOLVED)] = "unresolved", /* The entry is unresolved (ipmr) */ | |
397 | [LOG2U(RTNH_F_TRAP)] = "trap", /* Nexthop is trapping packets */ | |
18b23bd4 YW |
398 | }; |
399 | ||
400 | assert(ret); | |
401 | ||
402 | for (size_t i = 0; i < ELEMENTSOF(map); i++) | |
8a7da940 ZJS |
403 | if (FLAGS_SET(flags, 1 << i) && map[i]) |
404 | if (!strextend_with_separator(&str, ",", map[i])) | |
405 | return -ENOMEM; | |
18b23bd4 YW |
406 | |
407 | *ret = TAKE_PTR(str); | |
408 | return 0; | |
409 | } | |
410 | ||
344b3cff YW |
411 | static const char * const route_table_table[] = { |
412 | [RT_TABLE_DEFAULT] = "default", | |
413 | [RT_TABLE_MAIN] = "main", | |
414 | [RT_TABLE_LOCAL] = "local", | |
415 | }; | |
416 | ||
417 | DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int); | |
418 | ||
419 | int manager_get_route_table_from_string(const Manager *m, const char *s, uint32_t *ret) { | |
420 | uint32_t t; | |
421 | int r; | |
422 | ||
423 | assert(m); | |
424 | assert(s); | |
425 | assert(ret); | |
426 | ||
427 | r = route_table_from_string(s); | |
428 | if (r >= 0) { | |
429 | *ret = (uint32_t) r; | |
430 | return 0; | |
431 | } | |
432 | ||
433 | t = PTR_TO_UINT32(hashmap_get(m->route_table_numbers_by_name, s)); | |
434 | if (t != 0) { | |
435 | *ret = t; | |
436 | return 0; | |
437 | } | |
438 | ||
439 | r = safe_atou32(s, &t); | |
440 | if (r < 0) | |
441 | return r; | |
442 | ||
443 | if (t == 0) | |
444 | return -ERANGE; | |
445 | ||
446 | *ret = t; | |
447 | return 0; | |
448 | } | |
449 | ||
f4defbdc | 450 | int manager_get_route_table_to_string(const Manager *m, uint32_t table, bool append_num, char **ret) { |
344b3cff YW |
451 | _cleanup_free_ char *str = NULL; |
452 | const char *s; | |
344b3cff YW |
453 | |
454 | assert(m); | |
455 | assert(ret); | |
456 | ||
513bed29 YW |
457 | /* Unlike manager_get_route_table_from_string(), this accepts 0, as the kernel may create routes with |
458 | * table 0. See issue #25089. */ | |
344b3cff YW |
459 | |
460 | s = route_table_to_string(table); | |
461 | if (!s) | |
462 | s = hashmap_get(m->route_table_names_by_number, UINT32_TO_PTR(table)); | |
463 | ||
f4defbdc YW |
464 | if (s && !append_num) { |
465 | str = strdup(s); | |
466 | if (!str) | |
467 | return -ENOMEM; | |
468 | ||
469 | } else if (asprintf(&str, "%s%s%" PRIu32 "%s", | |
470 | strempty(s), | |
471 | s ? "(" : "", | |
472 | table, | |
473 | s ? ")" : "") < 0) | |
344b3cff YW |
474 | return -ENOMEM; |
475 | ||
476 | *ret = TAKE_PTR(str); | |
477 | return 0; | |
478 | } | |
479 | ||
480 | int config_parse_route_table_names( | |
481 | const char *unit, | |
482 | const char *filename, | |
483 | unsigned line, | |
484 | const char *section, | |
485 | unsigned section_line, | |
486 | const char *lvalue, | |
487 | int ltype, | |
488 | const char *rvalue, | |
489 | void *data, | |
490 | void *userdata) { | |
491 | ||
99534007 | 492 | Manager *m = ASSERT_PTR(userdata); |
344b3cff YW |
493 | int r; |
494 | ||
495 | assert(filename); | |
496 | assert(lvalue); | |
497 | assert(rvalue); | |
344b3cff YW |
498 | |
499 | if (isempty(rvalue)) { | |
500 | m->route_table_names_by_number = hashmap_free(m->route_table_names_by_number); | |
501 | m->route_table_numbers_by_name = hashmap_free(m->route_table_numbers_by_name); | |
502 | return 0; | |
503 | } | |
504 | ||
505 | for (const char *p = rvalue;;) { | |
506 | _cleanup_free_ char *name = NULL; | |
507 | uint32_t table; | |
508 | char *num; | |
509 | ||
510 | r = extract_first_word(&p, &name, NULL, 0); | |
511 | if (r == -ENOMEM) | |
512 | return log_oom(); | |
513 | if (r < 0) { | |
514 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
515 | "Invalid RouteTable=, ignoring assignment: %s", rvalue); | |
516 | return 0; | |
517 | } | |
518 | if (r == 0) | |
519 | return 0; | |
520 | ||
521 | num = strchr(name, ':'); | |
522 | if (!num) { | |
523 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
524 | "Invalid route table name and number pair, ignoring assignment: %s", name); | |
525 | continue; | |
526 | } | |
527 | ||
528 | *num++ = '\0'; | |
529 | ||
d2d602f4 YW |
530 | if (isempty(name)) { |
531 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
532 | "Route table name cannot be empty. Ignoring assignment: %s:%s", name, num); | |
533 | continue; | |
534 | } | |
535 | if (in_charset(name, DIGITS)) { | |
536 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
537 | "Route table name cannot be numeric. Ignoring assignment: %s:%s", name, num); | |
538 | continue; | |
539 | } | |
e8e91a81 | 540 | if (route_table_from_string(name) >= 0) { |
344b3cff | 541 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
e8e91a81 YW |
542 | "Route table name %s is predefined for %i. Ignoring assignment: %s:%s", |
543 | name, route_table_from_string(name), name, num); | |
344b3cff YW |
544 | continue; |
545 | } | |
546 | ||
547 | r = safe_atou32(num, &table); | |
548 | if (r < 0) { | |
549 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
550 | "Failed to parse route table number '%s', ignoring assignment: %s:%s", num, name, num); | |
551 | continue; | |
552 | } | |
553 | if (table == 0) { | |
554 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
555 | "Invalid route table number, ignoring assignment: %s:%s", name, num); | |
556 | continue; | |
557 | } | |
e8e91a81 YW |
558 | if (route_table_to_string(table)) { |
559 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
560 | "Route table name for %s is predefined (%s). Ignoring assignment: %s:%s", | |
561 | num, route_table_to_string(table), name, num); | |
562 | continue; | |
563 | } | |
344b3cff YW |
564 | |
565 | r = hashmap_ensure_put(&m->route_table_numbers_by_name, &string_hash_ops_free, name, UINT32_TO_PTR(table)); | |
566 | if (r == -ENOMEM) | |
567 | return log_oom(); | |
568 | if (r == -EEXIST) { | |
569 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
570 | "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num); | |
571 | continue; | |
572 | } | |
573 | if (r < 0) { | |
574 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
575 | "Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num); | |
576 | continue; | |
577 | } | |
578 | if (r == 0) | |
579 | /* The entry is duplicated. It should not be added to route_table_names_by_number hashmap. */ | |
580 | continue; | |
581 | ||
582 | r = hashmap_ensure_put(&m->route_table_names_by_number, NULL, UINT32_TO_PTR(table), name); | |
583 | if (r < 0) { | |
584 | hashmap_remove(m->route_table_numbers_by_name, name); | |
585 | ||
586 | if (r == -ENOMEM) | |
587 | return log_oom(); | |
588 | if (r == -EEXIST) | |
589 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
590 | "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num); | |
591 | else | |
592 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
593 | "Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num); | |
594 | continue; | |
595 | } | |
596 | assert(r > 0); | |
597 | ||
598 | TAKE_PTR(name); | |
599 | } | |
600 | } |