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