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