]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
bce67bbe SS |
2 | |
3 | #include <net/if.h> | |
4 | #include <linux/fib_rules.h> | |
5 | ||
43e08c78 | 6 | #include "af-list.h" |
bce67bbe SS |
7 | #include "alloc-util.h" |
8 | #include "conf-parser.h" | |
9 | #include "fileio.h" | |
ea471a46 | 10 | #include "format-util.h" |
c038ce46 | 11 | #include "hashmap.h" |
da96ad5a | 12 | #include "ip-protocol-list.h" |
bce67bbe SS |
13 | #include "netlink-util.h" |
14 | #include "networkd-manager.h" | |
0e5ef6be | 15 | #include "networkd-queue.h" |
344b3cff | 16 | #include "networkd-route-util.h" |
ca183bf8 | 17 | #include "networkd-routing-policy-rule.h" |
f6c6ff97 | 18 | #include "networkd-util.h" |
bce67bbe SS |
19 | #include "parse-util.h" |
20 | #include "socket-util.h" | |
d7d1d18f | 21 | #include "string-table.h" |
bce67bbe | 22 | #include "string-util.h" |
51517f9e | 23 | #include "strv.h" |
ea471a46 | 24 | #include "user-util.h" |
bce67bbe | 25 | |
d7d1d18f SS |
26 | static const char *const fr_act_type_table[__FR_ACT_MAX] = { |
27 | [FR_ACT_BLACKHOLE] = "blackhole", | |
28 | [FR_ACT_UNREACHABLE] = "unreachable", | |
29 | [FR_ACT_PROHIBIT] = "prohibit", | |
30 | }; | |
31 | ||
96515305 YW |
32 | static const char *const fr_act_type_full_table[__FR_ACT_MAX] = { |
33 | [FR_ACT_TO_TBL] = "table", | |
34 | [FR_ACT_GOTO] = "goto", | |
35 | [FR_ACT_NOP] = "nop", | |
36 | [FR_ACT_BLACKHOLE] = "blackhole", | |
37 | [FR_ACT_UNREACHABLE] = "unreachable", | |
38 | [FR_ACT_PROHIBIT] = "prohibit", | |
39 | }; | |
40 | ||
d7d1d18f SS |
41 | assert_cc(__FR_ACT_MAX <= UINT8_MAX); |
42 | DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(fr_act_type, int); | |
96515305 | 43 | DEFINE_STRING_TABLE_LOOKUP_TO_STRING(fr_act_type_full, int); |
d7d1d18f | 44 | |
f1828a22 YW |
45 | RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule) { |
46 | if (!rule) | |
47 | return NULL; | |
48 | ||
49 | if (rule->network) { | |
50 | assert(rule->section); | |
51 | hashmap_remove(rule->network->rules_by_section, rule->section); | |
52 | } | |
53 | ||
eb72fa3a | 54 | if (rule->manager) |
57fe5a42 | 55 | set_remove(rule->manager->rules, rule); |
f1828a22 | 56 | |
307fe3cd | 57 | config_section_free(rule->section); |
f1828a22 YW |
58 | free(rule->iif); |
59 | free(rule->oif); | |
60 | ||
61 | return mfree(rule); | |
62 | } | |
63 | ||
307fe3cd | 64 | DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free); |
f1828a22 YW |
65 | |
66 | static int routing_policy_rule_new(RoutingPolicyRule **ret) { | |
bce67bbe SS |
67 | RoutingPolicyRule *rule; |
68 | ||
9d66b48c | 69 | rule = new(RoutingPolicyRule, 1); |
bce67bbe SS |
70 | if (!rule) |
71 | return -ENOMEM; | |
72 | ||
9d66b48c | 73 | *rule = (RoutingPolicyRule) { |
9d66b48c | 74 | .table = RT_TABLE_MAIN, |
ea471a46 YW |
75 | .uid_range.start = UID_INVALID, |
76 | .uid_range.end = UID_INVALID, | |
53e1ba28 | 77 | .suppress_prefixlen = -1, |
af493fb7 | 78 | .suppress_ifgroup = -1, |
1e5fd321 | 79 | .protocol = RTPROT_UNSPEC, |
d7d1d18f | 80 | .type = FR_ACT_TO_TBL, |
9d66b48c | 81 | }; |
bce67bbe SS |
82 | |
83 | *ret = rule; | |
84 | return 0; | |
85 | } | |
86 | ||
7532b888 YW |
87 | static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) { |
88 | _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; | |
307fe3cd | 89 | _cleanup_(config_section_freep) ConfigSection *n = NULL; |
7532b888 YW |
90 | int r; |
91 | ||
92 | assert(network); | |
93 | assert(ret); | |
ca183bf8 YW |
94 | assert(filename); |
95 | assert(section_line > 0); | |
7532b888 | 96 | |
307fe3cd | 97 | r = config_section_new(filename, section_line, &n); |
ca183bf8 YW |
98 | if (r < 0) |
99 | return r; | |
7532b888 | 100 | |
ca183bf8 YW |
101 | rule = hashmap_get(network->rules_by_section, n); |
102 | if (rule) { | |
103 | *ret = TAKE_PTR(rule); | |
104 | return 0; | |
7532b888 YW |
105 | } |
106 | ||
107 | r = routing_policy_rule_new(&rule); | |
108 | if (r < 0) | |
109 | return r; | |
110 | ||
111 | rule->network = network; | |
ca183bf8 | 112 | rule->section = TAKE_PTR(n); |
eb72fa3a | 113 | rule->source = NETWORK_CONFIG_SOURCE_STATIC; |
1e5fd321 | 114 | rule->protocol = RTPROT_STATIC; |
7532b888 | 115 | |
307fe3cd | 116 | r = hashmap_ensure_put(&network->rules_by_section, &config_section_hash_ops, rule->section, rule); |
ca183bf8 YW |
117 | if (r < 0) |
118 | return r; | |
7532b888 YW |
119 | |
120 | *ret = TAKE_PTR(rule); | |
7532b888 YW |
121 | return 0; |
122 | } | |
123 | ||
e80509a9 YW |
124 | static int routing_policy_rule_dup(const RoutingPolicyRule *src, RoutingPolicyRule **ret) { |
125 | _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *dest = NULL; | |
01fc8e4f | 126 | |
01fc8e4f | 127 | assert(src); |
e80509a9 YW |
128 | assert(ret); |
129 | ||
130 | dest = newdup(RoutingPolicyRule, src, 1); | |
131 | if (!dest) | |
132 | return -ENOMEM; | |
133 | ||
134 | /* Unset all pointers */ | |
135 | dest->manager = NULL; | |
136 | dest->network = NULL; | |
137 | dest->section = NULL; | |
138 | dest->iif = dest->oif = NULL; | |
01fc8e4f YW |
139 | |
140 | if (src->iif) { | |
e80509a9 YW |
141 | dest->iif = strdup(src->iif); |
142 | if (!dest->iif) | |
01fc8e4f YW |
143 | return -ENOMEM; |
144 | } | |
145 | ||
146 | if (src->oif) { | |
e80509a9 YW |
147 | dest->oif = strdup(src->oif); |
148 | if (!dest->oif) | |
01fc8e4f YW |
149 | return -ENOMEM; |
150 | } | |
151 | ||
e80509a9 | 152 | *ret = TAKE_PTR(dest); |
01fc8e4f YW |
153 | return 0; |
154 | } | |
155 | ||
09d09207 | 156 | static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash *state) { |
bce67bbe SS |
157 | assert(rule); |
158 | ||
c01a5c05 | 159 | siphash24_compress_typesafe(rule->family, state); |
bce67bbe SS |
160 | |
161 | switch (rule->family) { | |
162 | case AF_INET: | |
163 | case AF_INET6: | |
c01a5c05 YW |
164 | in_addr_hash_func(&rule->from, rule->family, state); |
165 | siphash24_compress_typesafe(rule->from_prefixlen, state); | |
bce67bbe | 166 | |
c01a5c05 YW |
167 | in_addr_hash_func(&rule->to, rule->family, state); |
168 | siphash24_compress_typesafe(rule->to_prefixlen, state); | |
bce67bbe | 169 | |
b80a511b YW |
170 | siphash24_compress_boolean(rule->invert_rule, state); |
171 | ||
c01a5c05 YW |
172 | siphash24_compress_typesafe(rule->tos, state); |
173 | siphash24_compress_typesafe(rule->type, state); | |
174 | siphash24_compress_typesafe(rule->fwmark, state); | |
175 | siphash24_compress_typesafe(rule->fwmask, state); | |
176 | siphash24_compress_typesafe(rule->priority, state); | |
177 | siphash24_compress_typesafe(rule->table, state); | |
178 | siphash24_compress_typesafe(rule->suppress_prefixlen, state); | |
179 | siphash24_compress_typesafe(rule->suppress_ifgroup, state); | |
180 | ||
181 | siphash24_compress_typesafe(rule->ipproto, state); | |
182 | siphash24_compress_typesafe(rule->protocol, state); | |
183 | siphash24_compress_typesafe(rule->sport, state); | |
184 | siphash24_compress_typesafe(rule->dport, state); | |
185 | siphash24_compress_typesafe(rule->uid_range, state); | |
926062f0 | 186 | |
f281fc1e YW |
187 | siphash24_compress_string(rule->iif, state); |
188 | siphash24_compress_string(rule->oif, state); | |
762e2659 | 189 | |
bce67bbe SS |
190 | break; |
191 | default: | |
192 | /* treat any other address family as AF_UNSPEC */ | |
193 | break; | |
194 | } | |
195 | } | |
196 | ||
09d09207 | 197 | static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPolicyRule *b) { |
bce67bbe SS |
198 | int r; |
199 | ||
a0edd02e FB |
200 | r = CMP(a->family, b->family); |
201 | if (r != 0) | |
202 | return r; | |
bce67bbe SS |
203 | |
204 | switch (a->family) { | |
205 | case AF_INET: | |
206 | case AF_INET6: | |
a0edd02e FB |
207 | r = CMP(a->from_prefixlen, b->from_prefixlen); |
208 | if (r != 0) | |
209 | return r; | |
210 | ||
67e05dd8 ZJS |
211 | r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family)); |
212 | if (r != 0) | |
213 | return r; | |
214 | ||
a0edd02e FB |
215 | r = CMP(a->to_prefixlen, b->to_prefixlen); |
216 | if (r != 0) | |
217 | return r; | |
218 | ||
67e05dd8 ZJS |
219 | r = memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family)); |
220 | if (r != 0) | |
221 | return r; | |
222 | ||
b80a511b YW |
223 | r = CMP(a->invert_rule, b->invert_rule); |
224 | if (r != 0) | |
225 | return r; | |
226 | ||
a0edd02e FB |
227 | r = CMP(a->tos, b->tos); |
228 | if (r != 0) | |
229 | return r; | |
230 | ||
d7d1d18f SS |
231 | r = CMP(a->type, b->type); |
232 | if (r != 0) | |
233 | return r; | |
234 | ||
b80a511b YW |
235 | r = CMP(a->fwmark, b->fwmark); |
236 | if (r != 0) | |
237 | return r; | |
238 | ||
a0edd02e FB |
239 | r = CMP(a->fwmask, b->fwmask); |
240 | if (r != 0) | |
241 | return r; | |
242 | ||
eb72fa3a | 243 | r = CMP(a->priority, b->priority); |
b80a511b YW |
244 | if (r != 0) |
245 | return r; | |
246 | ||
a0edd02e FB |
247 | r = CMP(a->table, b->table); |
248 | if (r != 0) | |
249 | return r; | |
bce67bbe | 250 | |
53e1ba28 NF |
251 | r = CMP(a->suppress_prefixlen, b->suppress_prefixlen); |
252 | if (r != 0) | |
253 | return r; | |
254 | ||
af493fb7 SB |
255 | r = CMP(a->suppress_ifgroup, b->suppress_ifgroup); |
256 | if (r != 0) | |
257 | return r; | |
258 | ||
1e5fd321 YW |
259 | r = CMP(a->ipproto, b->ipproto); |
260 | if (r != 0) | |
261 | return r; | |
262 | ||
926062f0 SS |
263 | r = CMP(a->protocol, b->protocol); |
264 | if (r != 0) | |
265 | return r; | |
266 | ||
267 | r = memcmp(&a->sport, &b->sport, sizeof(a->sport)); | |
268 | if (r != 0) | |
269 | return r; | |
270 | ||
271 | r = memcmp(&a->dport, &b->dport, sizeof(a->dport)); | |
272 | if (r != 0) | |
273 | return r; | |
274 | ||
ea471a46 YW |
275 | r = memcmp(&a->uid_range, &b->uid_range, sizeof(a->uid_range)); |
276 | if (r != 0) | |
277 | return r; | |
278 | ||
67e05dd8 | 279 | r = strcmp_ptr(a->iif, b->iif); |
314ed4f9 | 280 | if (r != 0) |
bce67bbe SS |
281 | return r; |
282 | ||
67e05dd8 | 283 | r = strcmp_ptr(a->oif, b->oif); |
314ed4f9 | 284 | if (r != 0) |
67e05dd8 | 285 | return r; |
bce67bbe | 286 | |
67e05dd8 | 287 | return 0; |
bce67bbe SS |
288 | default: |
289 | /* treat any other address family as AF_UNSPEC */ | |
290 | return 0; | |
291 | } | |
292 | } | |
293 | ||
40424f1a YW |
294 | static bool routing_policy_rule_equal(const RoutingPolicyRule *rule1, const RoutingPolicyRule *rule2) { |
295 | if (rule1 == rule2) | |
296 | return true; | |
297 | ||
298 | if (!rule1 || !rule2) | |
299 | return false; | |
300 | ||
301 | return routing_policy_rule_compare_func(rule1, rule2) == 0; | |
302 | } | |
303 | ||
8eec0b9d YW |
304 | DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( |
305 | routing_policy_rule_hash_ops, | |
306 | RoutingPolicyRule, | |
307 | routing_policy_rule_hash_func, | |
308 | routing_policy_rule_compare_func, | |
309 | routing_policy_rule_free); | |
bce67bbe | 310 | |
eb72fa3a YW |
311 | static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *in, RoutingPolicyRule **ret) { |
312 | RoutingPolicyRule *rule; | |
bce67bbe | 313 | |
b80a511b | 314 | assert(m); |
eb72fa3a | 315 | assert(in); |
bce67bbe | 316 | |
eb72fa3a YW |
317 | rule = set_get(m->rules, in); |
318 | if (rule) { | |
b87dadcd | 319 | if (ret) |
eb72fa3a | 320 | *ret = rule; |
e6b65ab7 | 321 | return 0; |
bce67bbe SS |
322 | } |
323 | ||
eb72fa3a YW |
324 | if (in->priority_set) |
325 | return -ENOENT; | |
c4f7a347 | 326 | |
eb72fa3a YW |
327 | /* Also find rules configured without priority. */ |
328 | SET_FOREACH(rule, m->rules) { | |
329 | uint32_t priority; | |
330 | bool found; | |
331 | ||
332 | if (rule->priority_set) | |
333 | /* The rule is configured with priority. */ | |
334 | continue; | |
c4f7a347 | 335 | |
eb72fa3a YW |
336 | priority = rule->priority; |
337 | rule->priority = 0; | |
338 | found = routing_policy_rule_equal(rule, in); | |
339 | rule->priority = priority; | |
c4f7a347 | 340 | |
eb72fa3a | 341 | if (found) { |
c4f7a347 | 342 | if (ret) |
eb72fa3a YW |
343 | *ret = rule; |
344 | return 0; | |
c4f7a347 YW |
345 | } |
346 | } | |
347 | ||
bce67bbe SS |
348 | return -ENOENT; |
349 | } | |
350 | ||
eb72fa3a | 351 | static int routing_policy_rule_add(Manager *m, RoutingPolicyRule *rule) { |
c1934a8f YW |
352 | int r; |
353 | ||
354 | assert(m); | |
355 | assert(rule); | |
356 | assert(IN_SET(rule->family, AF_INET, AF_INET6)); | |
357 | ||
eb72fa3a YW |
358 | r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule); |
359 | if (r < 0) | |
c1934a8f | 360 | return r; |
eb72fa3a YW |
361 | if (r == 0) |
362 | return -EEXIST; | |
bce67bbe | 363 | |
c1934a8f | 364 | rule->manager = m; |
eb72fa3a | 365 | return 0; |
bce67bbe SS |
366 | } |
367 | ||
eb72fa3a YW |
368 | static int routing_policy_rule_acquire_priority(Manager *manager, RoutingPolicyRule *rule) { |
369 | _cleanup_set_free_ Set *priorities = NULL; | |
370 | RoutingPolicyRule *tmp; | |
371 | uint32_t priority; | |
372 | Network *network; | |
c4f7a347 YW |
373 | int r; |
374 | ||
eb72fa3a | 375 | assert(manager); |
c4f7a347 | 376 | assert(rule); |
eb72fa3a | 377 | assert(IN_SET(rule->family, AF_INET, AF_INET6)); |
c4f7a347 YW |
378 | |
379 | if (rule->priority_set) | |
380 | return 0; | |
381 | ||
eb72fa3a YW |
382 | /* Find the highest unused priority. Note that 32766 is already used by kernel. |
383 | * See kernel_rules[] below. */ | |
c4f7a347 | 384 | |
eb72fa3a YW |
385 | SET_FOREACH(tmp, manager->rules) { |
386 | if (tmp->family != rule->family) | |
387 | continue; | |
388 | if (tmp->priority == 0 || tmp->priority > 32765) | |
389 | continue; | |
390 | r = set_ensure_put(&priorities, NULL, UINT32_TO_PTR(tmp->priority)); | |
391 | if (r < 0) | |
392 | return r; | |
c4f7a347 YW |
393 | } |
394 | ||
eb72fa3a YW |
395 | ORDERED_HASHMAP_FOREACH(network, manager->networks) |
396 | HASHMAP_FOREACH(tmp, network->rules_by_section) { | |
397 | if (tmp->family != AF_UNSPEC && tmp->family != rule->family) | |
398 | continue; | |
399 | if (!tmp->priority_set) | |
400 | continue; | |
401 | if (tmp->priority == 0 || tmp->priority > 32765) | |
402 | continue; | |
403 | r = set_ensure_put(&priorities, NULL, UINT32_TO_PTR(tmp->priority)); | |
404 | if (r < 0) | |
405 | return r; | |
406 | } | |
407 | ||
408 | for (priority = 32765; priority > 0; priority--) | |
409 | if (!set_contains(priorities, UINT32_TO_PTR(priority))) | |
410 | break; | |
411 | ||
412 | rule->priority = priority; | |
413 | return 0; | |
c4f7a347 YW |
414 | } |
415 | ||
0e5ef6be | 416 | static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, const char *str, const Link *link, const Manager *m) { |
c71384a9 | 417 | _cleanup_free_ char *state = NULL, *table = NULL; |
7653a9dc | 418 | |
ea81208f | 419 | assert(rule); |
0e5ef6be | 420 | assert(IN_SET(rule->family, AF_INET, AF_INET6)); |
ea81208f | 421 | assert(str); |
552b90a2 | 422 | assert(m); |
ea81208f YW |
423 | |
424 | /* link may be NULL. */ | |
425 | ||
7653a9dc YW |
426 | if (!DEBUG_LOGGING) |
427 | return; | |
ea81208f | 428 | |
eb72fa3a | 429 | (void) network_config_state_to_string_alloc(rule->state, &state); |
f4defbdc | 430 | (void) manager_get_route_table_to_string(m, rule->table, /* append_num = */ true, &table); |
ea81208f | 431 | |
7653a9dc | 432 | log_link_debug(link, |
eb72fa3a YW |
433 | "%s %s routing policy rule (%s): priority: %"PRIu32", %s -> %s, iif: %s, oif: %s, table: %s", |
434 | str, strna(network_config_source_to_string(rule->source)), strna(state), | |
c71384a9 ZJS |
435 | rule->priority, |
436 | IN_ADDR_PREFIX_TO_STRING(rule->family, &rule->from, rule->from_prefixlen), | |
437 | IN_ADDR_PREFIX_TO_STRING(rule->family, &rule->to, rule->to_prefixlen), | |
7653a9dc | 438 | strna(rule->iif), strna(rule->oif), strna(table)); |
ea81208f YW |
439 | } |
440 | ||
3141c817 | 441 | static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule, sd_netlink_message *m, Link *link) { |
d08ed5a1 YW |
442 | int r; |
443 | ||
444 | assert(rule); | |
445 | assert(m); | |
18f2ee33 YW |
446 | |
447 | /* link may be NULL. */ | |
d08ed5a1 | 448 | |
2e8a32af | 449 | if (rule->from_prefixlen > 0) { |
d08ed5a1 YW |
450 | r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from); |
451 | if (r < 0) | |
7bd36e49 | 452 | return r; |
d08ed5a1 | 453 | |
b43dfb6e | 454 | r = sd_rtnl_message_routing_policy_rule_set_fib_src_prefixlen(m, rule->from_prefixlen); |
d08ed5a1 | 455 | if (r < 0) |
7bd36e49 | 456 | return r; |
d08ed5a1 YW |
457 | } |
458 | ||
2e8a32af | 459 | if (rule->to_prefixlen > 0) { |
d08ed5a1 YW |
460 | r = netlink_message_append_in_addr_union(m, FRA_DST, rule->family, &rule->to); |
461 | if (r < 0) | |
7bd36e49 | 462 | return r; |
d08ed5a1 | 463 | |
b43dfb6e | 464 | r = sd_rtnl_message_routing_policy_rule_set_fib_dst_prefixlen(m, rule->to_prefixlen); |
d08ed5a1 | 465 | if (r < 0) |
7bd36e49 | 466 | return r; |
d08ed5a1 YW |
467 | } |
468 | ||
eb72fa3a YW |
469 | r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority); |
470 | if (r < 0) | |
7bd36e49 | 471 | return r; |
d08ed5a1 YW |
472 | |
473 | if (rule->tos > 0) { | |
474 | r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos); | |
475 | if (r < 0) | |
7bd36e49 | 476 | return r; |
d08ed5a1 YW |
477 | } |
478 | ||
479 | if (rule->table < 256) { | |
480 | r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table); | |
481 | if (r < 0) | |
7bd36e49 | 482 | return r; |
d08ed5a1 YW |
483 | } else { |
484 | r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC); | |
485 | if (r < 0) | |
7bd36e49 | 486 | return r; |
d08ed5a1 YW |
487 | |
488 | r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table); | |
489 | if (r < 0) | |
7bd36e49 | 490 | return r; |
d08ed5a1 YW |
491 | } |
492 | ||
493 | if (rule->fwmark > 0) { | |
494 | r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark); | |
495 | if (r < 0) | |
7bd36e49 | 496 | return r; |
d08ed5a1 YW |
497 | |
498 | r = sd_netlink_message_append_u32(m, FRA_FWMASK, rule->fwmask); | |
499 | if (r < 0) | |
7bd36e49 | 500 | return r; |
d08ed5a1 YW |
501 | } |
502 | ||
503 | if (rule->iif) { | |
504 | r = sd_netlink_message_append_string(m, FRA_IIFNAME, rule->iif); | |
505 | if (r < 0) | |
7bd36e49 | 506 | return r; |
d08ed5a1 YW |
507 | } |
508 | ||
509 | if (rule->oif) { | |
510 | r = sd_netlink_message_append_string(m, FRA_OIFNAME, rule->oif); | |
511 | if (r < 0) | |
7bd36e49 | 512 | return r; |
d08ed5a1 YW |
513 | } |
514 | ||
1e5fd321 | 515 | r = sd_netlink_message_append_u8(m, FRA_IP_PROTO, rule->ipproto); |
d08ed5a1 | 516 | if (r < 0) |
7bd36e49 | 517 | return r; |
d08ed5a1 | 518 | |
1e5fd321 YW |
519 | r = sd_netlink_message_append_u8(m, FRA_PROTOCOL, rule->protocol); |
520 | if (r < 0) | |
7bd36e49 | 521 | return r; |
1e5fd321 | 522 | |
d08ed5a1 YW |
523 | if (rule->sport.start != 0 || rule->sport.end != 0) { |
524 | r = sd_netlink_message_append_data(m, FRA_SPORT_RANGE, &rule->sport, sizeof(rule->sport)); | |
525 | if (r < 0) | |
7bd36e49 | 526 | return r; |
d08ed5a1 YW |
527 | } |
528 | ||
529 | if (rule->dport.start != 0 || rule->dport.end != 0) { | |
530 | r = sd_netlink_message_append_data(m, FRA_DPORT_RANGE, &rule->dport, sizeof(rule->dport)); | |
531 | if (r < 0) | |
7bd36e49 | 532 | return r; |
d08ed5a1 YW |
533 | } |
534 | ||
535 | if (rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID) { | |
536 | r = sd_netlink_message_append_data(m, FRA_UID_RANGE, &rule->uid_range, sizeof(rule->uid_range)); | |
537 | if (r < 0) | |
7bd36e49 | 538 | return r; |
d08ed5a1 YW |
539 | } |
540 | ||
541 | if (rule->invert_rule) { | |
542 | r = sd_rtnl_message_routing_policy_rule_set_flags(m, FIB_RULE_INVERT); | |
543 | if (r < 0) | |
7bd36e49 | 544 | return r; |
d08ed5a1 YW |
545 | } |
546 | ||
547 | if (rule->suppress_prefixlen >= 0) { | |
548 | r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_PREFIXLEN, (uint32_t) rule->suppress_prefixlen); | |
549 | if (r < 0) | |
7bd36e49 | 550 | return r; |
d08ed5a1 YW |
551 | } |
552 | ||
af493fb7 SB |
553 | if (rule->suppress_ifgroup >= 0) { |
554 | r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_IFGROUP, (uint32_t) rule->suppress_ifgroup); | |
555 | if (r < 0) | |
7bd36e49 | 556 | return r; |
af493fb7 SB |
557 | } |
558 | ||
3ca61906 YW |
559 | r = sd_rtnl_message_routing_policy_rule_set_fib_type(m, rule->type); |
560 | if (r < 0) | |
7bd36e49 | 561 | return r; |
d7d1d18f | 562 | |
d08ed5a1 YW |
563 | return 0; |
564 | } | |
565 | ||
eb72fa3a | 566 | static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { |
bce67bbe SS |
567 | int r; |
568 | ||
569 | assert(m); | |
bce67bbe SS |
570 | |
571 | r = sd_netlink_message_get_errno(m); | |
572 | if (r < 0) | |
18f2ee33 | 573 | log_message_warning_errno(m, r, "Could not drop routing policy rule"); |
bce67bbe SS |
574 | |
575 | return 1; | |
576 | } | |
577 | ||
eb72fa3a | 578 | static int routing_policy_rule_remove(RoutingPolicyRule *rule) { |
bce67bbe SS |
579 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
580 | int r; | |
581 | ||
d85b0d69 | 582 | assert(rule); |
eb72fa3a YW |
583 | assert(rule->manager); |
584 | assert(rule->manager->rtnl); | |
d85b0d69 YW |
585 | assert(IN_SET(rule->family, AF_INET, AF_INET6)); |
586 | ||
eb72fa3a | 587 | log_routing_policy_rule_debug(rule, "Removing", NULL, rule->manager); |
bce67bbe | 588 | |
eb72fa3a | 589 | r = sd_rtnl_message_new_routing_policy_rule(rule->manager->rtnl, &m, RTM_DELRULE, rule->family); |
bce67bbe | 590 | if (r < 0) |
a79a8d16 | 591 | return log_warning_errno(r, "Could not allocate netlink message: %m"); |
bce67bbe | 592 | |
18f2ee33 | 593 | r = routing_policy_rule_set_netlink_message(rule, m, NULL); |
d08ed5a1 | 594 | if (r < 0) |
a79a8d16 | 595 | return log_warning_errno(r, "Could not create netlink message: %m"); |
bce67bbe | 596 | |
eb72fa3a | 597 | r = netlink_call_async(rule->manager->rtnl, NULL, m, |
0e5ef6be | 598 | routing_policy_rule_remove_handler, |
eb72fa3a | 599 | NULL, NULL); |
bce67bbe | 600 | if (r < 0) |
a79a8d16 | 601 | return log_warning_errno(r, "Could not send netlink message: %m"); |
bce67bbe | 602 | |
eb72fa3a | 603 | routing_policy_rule_enter_removing(rule); |
bce67bbe SS |
604 | return 0; |
605 | } | |
606 | ||
54ff39f7 | 607 | static int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, Request *req) { |
bce67bbe SS |
608 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
609 | int r; | |
610 | ||
611 | assert(rule); | |
0e5ef6be | 612 | assert(IN_SET(rule->family, AF_INET, AF_INET6)); |
bce67bbe SS |
613 | assert(link); |
614 | assert(link->ifindex > 0); | |
615 | assert(link->manager); | |
616 | assert(link->manager->rtnl); | |
54ff39f7 | 617 | assert(req); |
bce67bbe | 618 | |
0e5ef6be | 619 | log_routing_policy_rule_debug(rule, "Configuring", link, link->manager); |
15201512 | 620 | |
0e5ef6be | 621 | r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family); |
bce67bbe | 622 | if (r < 0) |
a79a8d16 | 623 | return r; |
bce67bbe | 624 | |
d08ed5a1 | 625 | r = routing_policy_rule_set_netlink_message(rule, m, link); |
926062f0 | 626 | if (r < 0) |
a79a8d16 | 627 | return r; |
53e1ba28 | 628 | |
80d62d4f | 629 | return request_call_netlink_async(link->manager->rtnl, m, req); |
bce67bbe SS |
630 | } |
631 | ||
b4564f4e | 632 | static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Link *except) { |
eb72fa3a | 633 | RoutingPolicyRule *rule; |
7532b888 YW |
634 | Link *link; |
635 | ||
636 | assert(m); | |
ca183bf8 | 637 | |
eb72fa3a YW |
638 | /* First, mark all existing rules. */ |
639 | SET_FOREACH(rule, m->rules) { | |
640 | /* Do not touch rules managed by kernel. */ | |
641 | if (rule->protocol == RTPROT_KERNEL) | |
0b81225e YW |
642 | continue; |
643 | ||
a0e99a37 YW |
644 | /* When 'foreign' is true, mark only foreign rules, and vice versa. */ |
645 | if (foreign != (rule->source == NETWORK_CONFIG_SOURCE_FOREIGN)) | |
7532b888 YW |
646 | continue; |
647 | ||
eb72fa3a YW |
648 | /* Ignore rules not assigned yet or already removing. */ |
649 | if (!routing_policy_rule_exists(rule)) | |
650 | continue; | |
a75466ed | 651 | |
eb72fa3a | 652 | routing_policy_rule_mark(rule); |
7532b888 YW |
653 | } |
654 | ||
eb72fa3a YW |
655 | /* Then, unmark all rules requested by active links. */ |
656 | HASHMAP_FOREACH(link, m->links_by_index) { | |
657 | if (link == except) | |
0b81225e YW |
658 | continue; |
659 | ||
eb72fa3a | 660 | if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) |
0b81225e YW |
661 | continue; |
662 | ||
eb72fa3a YW |
663 | HASHMAP_FOREACH(rule, link->network->rules_by_section) { |
664 | RoutingPolicyRule *existing; | |
c4f7a347 | 665 | |
eb72fa3a YW |
666 | if (IN_SET(rule->family, AF_INET, AF_INET6)) { |
667 | if (routing_policy_rule_get(m, rule, &existing) >= 0) | |
668 | routing_policy_rule_unmark(existing); | |
669 | } else { | |
670 | /* The case Family=both. */ | |
671 | rule->family = AF_INET; | |
672 | if (routing_policy_rule_get(m, rule, &existing) >= 0) | |
673 | routing_policy_rule_unmark(existing); | |
c4f7a347 | 674 | |
eb72fa3a YW |
675 | rule->family = AF_INET6; |
676 | if (routing_policy_rule_get(m, rule, &existing) >= 0) | |
677 | routing_policy_rule_unmark(existing); | |
c4f7a347 | 678 | |
eb72fa3a | 679 | rule->family = AF_UNSPEC; |
c4f7a347 YW |
680 | } |
681 | } | |
eb72fa3a | 682 | } |
b4564f4e YW |
683 | } |
684 | ||
685 | int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except) { | |
686 | RoutingPolicyRule *rule; | |
1339b950 | 687 | int r = 0; |
b4564f4e YW |
688 | |
689 | assert(m); | |
690 | ||
691 | manager_mark_routing_policy_rules(m, foreign, except); | |
c4f7a347 | 692 | |
eb72fa3a YW |
693 | SET_FOREACH(rule, m->rules) { |
694 | if (!routing_policy_rule_is_marked(rule)) | |
695 | continue; | |
696 | ||
1339b950 | 697 | RET_GATHER(r, routing_policy_rule_remove(rule)); |
0b81225e YW |
698 | } |
699 | ||
700 | return r; | |
701 | } | |
702 | ||
b4564f4e YW |
703 | void link_foreignize_routing_policy_rules(Link *link) { |
704 | RoutingPolicyRule *rule; | |
705 | ||
706 | assert(link); | |
707 | assert(link->manager); | |
708 | ||
709 | manager_mark_routing_policy_rules(link->manager, /* foreign = */ false, link); | |
710 | ||
711 | SET_FOREACH(rule, link->manager->rules) { | |
712 | if (!routing_policy_rule_is_marked(rule)) | |
713 | continue; | |
714 | ||
715 | rule->source = NETWORK_CONFIG_SOURCE_FOREIGN; | |
716 | } | |
717 | } | |
718 | ||
09d09207 | 719 | static int routing_policy_rule_process_request(Request *req, Link *link, RoutingPolicyRule *rule) { |
8bed7c55 YW |
720 | int r; |
721 | ||
722 | assert(req); | |
ff51134c YW |
723 | assert(link); |
724 | assert(rule); | |
8bed7c55 YW |
725 | |
726 | if (!link_is_ready_to_configure(link, false)) | |
727 | return 0; | |
728 | ||
54ff39f7 | 729 | r = routing_policy_rule_configure(rule, link, req); |
8bed7c55 YW |
730 | if (r < 0) |
731 | return log_link_warning_errno(link, r, "Failed to configure routing policy rule: %m"); | |
732 | ||
733 | routing_policy_rule_enter_configuring(rule); | |
734 | return 1; | |
735 | } | |
736 | ||
80d62d4f YW |
737 | static int static_routing_policy_rule_configure_handler( |
738 | sd_netlink *rtnl, | |
739 | sd_netlink_message *m, | |
740 | Request *req, | |
741 | Link *link, | |
742 | RoutingPolicyRule *rule) { | |
743 | ||
7ad3e909 YW |
744 | int r; |
745 | ||
7ad3e909 YW |
746 | assert(m); |
747 | assert(link); | |
7ad3e909 YW |
748 | |
749 | r = sd_netlink_message_get_errno(m); | |
750 | if (r < 0 && r != -EEXIST) { | |
751 | log_link_message_warning_errno(link, m, r, "Could not add routing policy rule"); | |
752 | link_enter_failed(link); | |
753 | return 1; | |
754 | } | |
755 | ||
0e5ef6be | 756 | if (link->static_routing_policy_rule_messages == 0) { |
7ad3e909 | 757 | log_link_debug(link, "Routing policy rule configured"); |
0e5ef6be | 758 | link->static_routing_policy_rules_configured = true; |
7ad3e909 YW |
759 | link_check_ready(link); |
760 | } | |
761 | ||
762 | return 1; | |
763 | } | |
764 | ||
80d62d4f | 765 | static int link_request_routing_policy_rule(Link *link, RoutingPolicyRule *rule) { |
eb72fa3a YW |
766 | RoutingPolicyRule *existing; |
767 | int r; | |
768 | ||
0e5ef6be YW |
769 | assert(link); |
770 | assert(link->manager); | |
771 | assert(rule); | |
eb72fa3a | 772 | assert(rule->source != NETWORK_CONFIG_SOURCE_FOREIGN); |
0e5ef6be | 773 | |
eb72fa3a | 774 | if (routing_policy_rule_get(link->manager, rule, &existing) < 0) { |
0e5ef6be YW |
775 | _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; |
776 | ||
777 | r = routing_policy_rule_dup(rule, &tmp); | |
778 | if (r < 0) | |
779 | return r; | |
780 | ||
eb72fa3a | 781 | r = routing_policy_rule_acquire_priority(link->manager, tmp); |
7ad3e909 YW |
782 | if (r < 0) |
783 | return r; | |
7ad3e909 | 784 | |
eb72fa3a | 785 | r = routing_policy_rule_add(link->manager, tmp); |
0e5ef6be YW |
786 | if (r < 0) |
787 | return r; | |
788 | ||
eb72fa3a YW |
789 | existing = TAKE_PTR(tmp); |
790 | } else | |
791 | existing->source = rule->source; | |
0e5ef6be | 792 | |
eb72fa3a | 793 | log_routing_policy_rule_debug(existing, "Requesting", link, link->manager); |
09d09207 YW |
794 | r = link_queue_request_safe(link, REQUEST_TYPE_ROUTING_POLICY_RULE, |
795 | existing, NULL, | |
796 | routing_policy_rule_hash_func, | |
797 | routing_policy_rule_compare_func, | |
798 | routing_policy_rule_process_request, | |
799 | &link->static_routing_policy_rule_messages, | |
800 | static_routing_policy_rule_configure_handler, | |
801 | NULL); | |
eb72fa3a YW |
802 | if (r <= 0) |
803 | return r; | |
804 | ||
805 | routing_policy_rule_enter_requesting(existing); | |
806 | return 1; | |
807 | } | |
808 | ||
809 | static int link_request_static_routing_policy_rule(Link *link, RoutingPolicyRule *rule) { | |
810 | int r; | |
811 | ||
812 | if (IN_SET(rule->family, AF_INET, AF_INET6)) | |
80d62d4f | 813 | return link_request_routing_policy_rule(link, rule); |
eb72fa3a YW |
814 | |
815 | rule->family = AF_INET; | |
80d62d4f | 816 | r = link_request_routing_policy_rule(link, rule); |
eb72fa3a YW |
817 | if (r < 0) { |
818 | rule->family = AF_UNSPEC; | |
819 | return r; | |
7ad3e909 YW |
820 | } |
821 | ||
eb72fa3a | 822 | rule->family = AF_INET6; |
80d62d4f | 823 | r = link_request_routing_policy_rule(link, rule); |
eb72fa3a YW |
824 | rule->family = AF_UNSPEC; |
825 | return r; | |
7ad3e909 YW |
826 | } |
827 | ||
0e5ef6be | 828 | int link_request_static_routing_policy_rules(Link *link) { |
ddc9df31 | 829 | RoutingPolicyRule *rule; |
c0ec4746 YW |
830 | int r; |
831 | ||
832 | assert(link); | |
833 | assert(link->network); | |
834 | ||
0e5ef6be | 835 | link->static_routing_policy_rules_configured = false; |
c0ec4746 | 836 | |
ca183bf8 | 837 | HASHMAP_FOREACH(rule, link->network->rules_by_section) { |
0e5ef6be | 838 | r = link_request_static_routing_policy_rule(link, rule); |
c0ec4746 | 839 | if (r < 0) |
0e5ef6be | 840 | return log_link_warning_errno(link, r, "Could not request routing policy rule: %m"); |
c0ec4746 YW |
841 | } |
842 | ||
0e5ef6be YW |
843 | if (link->static_routing_policy_rule_messages == 0) { |
844 | link->static_routing_policy_rules_configured = true; | |
845 | link_check_ready(link); | |
846 | } else { | |
847 | log_link_debug(link, "Requesting routing policy rules"); | |
c0ec4746 YW |
848 | link_set_state(link, LINK_STATE_CONFIGURING); |
849 | } | |
850 | ||
851 | return 0; | |
852 | } | |
853 | ||
569eeb0c | 854 | static const RoutingPolicyRule kernel_rules[] = { |
af493fb7 SB |
855 | { .family = AF_INET, .priority_set = true, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, }, |
856 | { .family = AF_INET, .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, }, | |
857 | { .family = AF_INET, .priority_set = true, .priority = 32767, .table = RT_TABLE_DEFAULT, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, }, | |
858 | { .family = AF_INET6, .priority_set = true, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, }, | |
859 | { .family = AF_INET6, .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, }, | |
569eeb0c YW |
860 | }; |
861 | ||
862 | static bool routing_policy_rule_is_created_by_kernel(const RoutingPolicyRule *rule) { | |
863 | assert(rule); | |
864 | ||
e737dce5 YW |
865 | if (rule->l3mdev > 0) |
866 | /* Currently, [RoutingPolicyRule] does not explicitly set FRA_L3MDEV. So, if the flag | |
867 | * is set, it is safe to treat the rule as created by kernel. */ | |
868 | return true; | |
869 | ||
569eeb0c YW |
870 | for (size_t i = 0; i < ELEMENTSOF(kernel_rules); i++) |
871 | if (routing_policy_rule_equal(rule, &kernel_rules[i])) | |
872 | return true; | |
873 | ||
874 | return false; | |
875 | } | |
876 | ||
51a0dc4a YW |
877 | int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { |
878 | _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; | |
51a0dc4a | 879 | RoutingPolicyRule *rule = NULL; |
569eeb0c | 880 | bool adjust_protocol = false; |
51a0dc4a YW |
881 | uint16_t type; |
882 | int r; | |
883 | ||
884 | assert(rtnl); | |
885 | assert(message); | |
886 | ||
887 | if (sd_netlink_message_is_error(message)) { | |
888 | r = sd_netlink_message_get_errno(message); | |
889 | if (r < 0) | |
890 | log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring"); | |
891 | ||
892 | return 0; | |
893 | } | |
894 | ||
895 | r = sd_netlink_message_get_type(message, &type); | |
896 | if (r < 0) { | |
897 | log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); | |
898 | return 0; | |
899 | } else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) { | |
900 | log_warning("rtnl: received unexpected message type %u when processing rule, ignoring.", type); | |
901 | return 0; | |
902 | } | |
903 | ||
904 | r = routing_policy_rule_new(&tmp); | |
905 | if (r < 0) { | |
906 | log_oom(); | |
907 | return 0; | |
908 | } | |
909 | ||
910 | r = sd_rtnl_message_get_family(message, &tmp->family); | |
911 | if (r < 0) { | |
912 | log_warning_errno(r, "rtnl: could not get rule family, ignoring: %m"); | |
913 | return 0; | |
914 | } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { | |
915 | log_debug("rtnl: received rule message with invalid family %d, ignoring.", tmp->family); | |
916 | return 0; | |
917 | } | |
918 | ||
c18c53c3 YW |
919 | r = netlink_message_read_in_addr_union(message, FRA_SRC, tmp->family, &tmp->from); |
920 | if (r < 0 && r != -ENODATA) { | |
921 | log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m"); | |
922 | return 0; | |
923 | } else if (r >= 0) { | |
b43dfb6e | 924 | r = sd_rtnl_message_routing_policy_rule_get_fib_src_prefixlen(message, &tmp->from_prefixlen); |
c18c53c3 YW |
925 | if (r < 0) { |
926 | log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m"); | |
51a0dc4a | 927 | return 0; |
51a0dc4a | 928 | } |
c18c53c3 | 929 | } |
51a0dc4a | 930 | |
c18c53c3 YW |
931 | r = netlink_message_read_in_addr_union(message, FRA_DST, tmp->family, &tmp->to); |
932 | if (r < 0 && r != -ENODATA) { | |
933 | log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m"); | |
934 | return 0; | |
935 | } else if (r >= 0) { | |
b43dfb6e | 936 | r = sd_rtnl_message_routing_policy_rule_get_fib_dst_prefixlen(message, &tmp->to_prefixlen); |
c18c53c3 YW |
937 | if (r < 0) { |
938 | log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m"); | |
51a0dc4a | 939 | return 0; |
51a0dc4a | 940 | } |
51a0dc4a YW |
941 | } |
942 | ||
fccf662c | 943 | unsigned flags; |
51a0dc4a YW |
944 | r = sd_rtnl_message_routing_policy_rule_get_flags(message, &flags); |
945 | if (r < 0) { | |
946 | log_warning_errno(r, "rtnl: received rule message without valid flag, ignoring: %m"); | |
947 | return 0; | |
948 | } | |
949 | tmp->invert_rule = flags & FIB_RULE_INVERT; | |
950 | ||
951 | r = sd_netlink_message_read_u32(message, FRA_FWMARK, &tmp->fwmark); | |
952 | if (r < 0 && r != -ENODATA) { | |
953 | log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m"); | |
954 | return 0; | |
955 | } | |
956 | ||
957 | r = sd_netlink_message_read_u32(message, FRA_FWMASK, &tmp->fwmask); | |
958 | if (r < 0 && r != -ENODATA) { | |
959 | log_warning_errno(r, "rtnl: could not get FRA_FWMASK attribute, ignoring: %m"); | |
960 | return 0; | |
961 | } | |
962 | ||
963 | r = sd_netlink_message_read_u32(message, FRA_PRIORITY, &tmp->priority); | |
964 | if (r < 0 && r != -ENODATA) { | |
965 | log_warning_errno(r, "rtnl: could not get FRA_PRIORITY attribute, ignoring: %m"); | |
966 | return 0; | |
967 | } | |
c4f7a347 YW |
968 | /* The kernel does not send priority if priority is zero. So, the flag below must be always set |
969 | * even if the message does not contain FRA_PRIORITY. */ | |
970 | tmp->priority_set = true; | |
51a0dc4a YW |
971 | |
972 | r = sd_netlink_message_read_u32(message, FRA_TABLE, &tmp->table); | |
973 | if (r < 0 && r != -ENODATA) { | |
974 | log_warning_errno(r, "rtnl: could not get FRA_TABLE attribute, ignoring: %m"); | |
975 | return 0; | |
976 | } | |
977 | ||
978 | r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tmp->tos); | |
979 | if (r < 0 && r != -ENODATA) { | |
d7d1d18f SS |
980 | log_warning_errno(r, "rtnl: could not get FIB rule TOS, ignoring: %m"); |
981 | return 0; | |
982 | } | |
983 | ||
984 | r = sd_rtnl_message_routing_policy_rule_get_fib_type(message, &tmp->type); | |
985 | if (r < 0 && r != -ENODATA) { | |
986 | log_warning_errno(r, "rtnl: could not get FIB rule type, ignoring: %m"); | |
51a0dc4a YW |
987 | return 0; |
988 | } | |
989 | ||
fccf662c | 990 | r = sd_netlink_message_read_string_strdup(message, FRA_IIFNAME, &tmp->iif); |
51a0dc4a YW |
991 | if (r < 0 && r != -ENODATA) { |
992 | log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m"); | |
993 | return 0; | |
994 | } | |
51a0dc4a | 995 | |
fccf662c | 996 | r = sd_netlink_message_read_string_strdup(message, FRA_OIFNAME, &tmp->oif); |
51a0dc4a YW |
997 | if (r < 0 && r != -ENODATA) { |
998 | log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m"); | |
999 | return 0; | |
1000 | } | |
51a0dc4a | 1001 | |
1e5fd321 | 1002 | r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &tmp->ipproto); |
51a0dc4a YW |
1003 | if (r < 0 && r != -ENODATA) { |
1004 | log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m"); | |
1005 | return 0; | |
1006 | } | |
1007 | ||
1e5fd321 | 1008 | r = sd_netlink_message_read_u8(message, FRA_PROTOCOL, &tmp->protocol); |
569eeb0c YW |
1009 | if (r == -ENODATA) |
1010 | /* If FRA_PROTOCOL is supported by kernel, then the attribute is always appended. | |
1011 | * When the received message does not have FRA_PROTOCOL, then we need to adjust the | |
1012 | * protocol of the rule later. */ | |
1013 | adjust_protocol = true; | |
1014 | else if (r < 0) { | |
1e5fd321 YW |
1015 | log_warning_errno(r, "rtnl: could not get FRA_PROTOCOL attribute, ignoring: %m"); |
1016 | return 0; | |
1017 | } | |
1018 | ||
e737dce5 YW |
1019 | r = sd_netlink_message_read_u8(message, FRA_L3MDEV, &tmp->l3mdev); |
1020 | if (r < 0 && r != -ENODATA) { | |
1021 | log_warning_errno(r, "rtnl: could not get FRA_L3MDEV attribute, ignoring: %m"); | |
1022 | return 0; | |
1023 | } | |
1024 | ||
51a0dc4a YW |
1025 | r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(tmp->sport), &tmp->sport); |
1026 | if (r < 0 && r != -ENODATA) { | |
1027 | log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m"); | |
1028 | return 0; | |
1029 | } | |
1030 | ||
1031 | r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(tmp->dport), &tmp->dport); | |
1032 | if (r < 0 && r != -ENODATA) { | |
1033 | log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m"); | |
1034 | return 0; | |
1035 | } | |
1036 | ||
1037 | r = sd_netlink_message_read(message, FRA_UID_RANGE, sizeof(tmp->uid_range), &tmp->uid_range); | |
1038 | if (r < 0 && r != -ENODATA) { | |
1039 | log_warning_errno(r, "rtnl: could not get FRA_UID_RANGE attribute, ignoring: %m"); | |
1040 | return 0; | |
1041 | } | |
1042 | ||
fccf662c | 1043 | uint32_t suppress_prefixlen; |
51a0dc4a YW |
1044 | r = sd_netlink_message_read_u32(message, FRA_SUPPRESS_PREFIXLEN, &suppress_prefixlen); |
1045 | if (r < 0 && r != -ENODATA) { | |
1046 | log_warning_errno(r, "rtnl: could not get FRA_SUPPRESS_PREFIXLEN attribute, ignoring: %m"); | |
1047 | return 0; | |
1048 | } | |
b2174e29 | 1049 | if (r >= 0) |
af493fb7 | 1050 | tmp->suppress_prefixlen = (int32_t) suppress_prefixlen; |
af493fb7 SB |
1051 | |
1052 | uint32_t suppress_ifgroup; | |
1053 | r = sd_netlink_message_read_u32(message, FRA_SUPPRESS_IFGROUP, &suppress_ifgroup); | |
1054 | if (r < 0 && r != -ENODATA) { | |
1055 | log_warning_errno(r, "rtnl: could not get FRA_SUPPRESS_IFGROUP attribute, ignoring: %m"); | |
1056 | return 0; | |
1057 | } | |
b2174e29 | 1058 | if (r >= 0) |
af493fb7 | 1059 | tmp->suppress_ifgroup = (int32_t) suppress_ifgroup; |
51a0dc4a | 1060 | |
569eeb0c YW |
1061 | if (adjust_protocol) |
1062 | /* As .network files does not have setting to specify protocol, we can assume the | |
1063 | * protocol of the received rule is RTPROT_KERNEL or RTPROT_STATIC. */ | |
1064 | tmp->protocol = routing_policy_rule_is_created_by_kernel(tmp) ? RTPROT_KERNEL : RTPROT_STATIC; | |
1065 | ||
eb72fa3a | 1066 | (void) routing_policy_rule_get(m, tmp, &rule); |
51a0dc4a | 1067 | |
51a0dc4a YW |
1068 | switch (type) { |
1069 | case RTM_NEWRULE: | |
c4f7a347 | 1070 | if (rule) { |
eb72fa3a YW |
1071 | routing_policy_rule_enter_configured(rule); |
1072 | log_routing_policy_rule_debug(rule, "Received remembered", NULL, m); | |
1073 | } else if (!m->manage_foreign_rules) { | |
1074 | routing_policy_rule_enter_configured(tmp); | |
1075 | log_routing_policy_rule_debug(tmp, "Ignoring received", NULL, m); | |
1076 | } else { | |
1077 | routing_policy_rule_enter_configured(tmp); | |
1078 | log_routing_policy_rule_debug(tmp, "Remembering", NULL, m); | |
1079 | r = routing_policy_rule_add(m, tmp); | |
1080 | if (r < 0) { | |
51a0dc4a | 1081 | log_warning_errno(r, "Could not remember foreign rule, ignoring: %m"); |
eb72fa3a YW |
1082 | return 0; |
1083 | } | |
1084 | TAKE_PTR(tmp); | |
51a0dc4a YW |
1085 | } |
1086 | break; | |
1087 | case RTM_DELRULE: | |
1088 | if (rule) { | |
eb72fa3a YW |
1089 | routing_policy_rule_enter_removed(rule); |
1090 | if (rule->state == 0) { | |
1091 | log_routing_policy_rule_debug(rule, "Forgetting", NULL, m); | |
1092 | routing_policy_rule_free(rule); | |
1093 | } else | |
1094 | log_routing_policy_rule_debug(rule, "Removed", NULL, m); | |
51a0dc4a | 1095 | } else |
0e5ef6be | 1096 | log_routing_policy_rule_debug(tmp, "Kernel removed unknown", NULL, m); |
51a0dc4a YW |
1097 | break; |
1098 | ||
1099 | default: | |
04499a70 | 1100 | assert_not_reached(); |
51a0dc4a YW |
1101 | } |
1102 | ||
1103 | return 1; | |
1104 | } | |
1105 | ||
c2d6fcb1 YW |
1106 | static int parse_fwmark_fwmask(const char *s, uint32_t *ret_fwmark, uint32_t *ret_fwmask) { |
1107 | _cleanup_free_ char *fwmark_str = NULL; | |
1108 | uint32_t fwmark, fwmask = 0; | |
1109 | const char *slash; | |
bce67bbe SS |
1110 | int r; |
1111 | ||
1112 | assert(s); | |
c2d6fcb1 YW |
1113 | assert(ret_fwmark); |
1114 | assert(ret_fwmask); | |
bce67bbe | 1115 | |
c2d6fcb1 YW |
1116 | slash = strchr(s, '/'); |
1117 | if (slash) { | |
1118 | fwmark_str = strndup(s, slash - s); | |
1119 | if (!fwmark_str) | |
1120 | return -ENOMEM; | |
1121 | } | |
bce67bbe | 1122 | |
c2d6fcb1 | 1123 | r = safe_atou32(fwmark_str ?: s, &fwmark); |
bce67bbe | 1124 | if (r < 0) |
c2d6fcb1 | 1125 | return r; |
bce67bbe | 1126 | |
bd1000b4 YW |
1127 | if (fwmark > 0) { |
1128 | if (slash) { | |
1129 | r = safe_atou32(slash + 1, &fwmask); | |
1130 | if (r < 0) | |
1131 | return r; | |
1132 | } else | |
1133 | fwmask = UINT32_MAX; | |
bce67bbe SS |
1134 | } |
1135 | ||
c2d6fcb1 YW |
1136 | *ret_fwmark = fwmark; |
1137 | *ret_fwmask = fwmask; | |
1138 | ||
bce67bbe SS |
1139 | return 0; |
1140 | } | |
1141 | ||
1142 | int config_parse_routing_policy_rule_tos( | |
1143 | const char *unit, | |
1144 | const char *filename, | |
1145 | unsigned line, | |
1146 | const char *section, | |
1147 | unsigned section_line, | |
1148 | const char *lvalue, | |
1149 | int ltype, | |
1150 | const char *rvalue, | |
1151 | void *data, | |
1152 | void *userdata) { | |
1153 | ||
fcbf4cb7 | 1154 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
bce67bbe SS |
1155 | Network *network = userdata; |
1156 | int r; | |
1157 | ||
1158 | assert(filename); | |
1159 | assert(section); | |
1160 | assert(lvalue); | |
1161 | assert(rvalue); | |
1162 | assert(data); | |
1163 | ||
1164 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1165 | if (r < 0) | |
d96edb2c | 1166 | return log_oom(); |
bce67bbe SS |
1167 | |
1168 | r = safe_atou8(rvalue, &n->tos); | |
1169 | if (r < 0) { | |
d96edb2c | 1170 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule TOS, ignoring: %s", rvalue); |
bce67bbe SS |
1171 | return 0; |
1172 | } | |
1173 | ||
8eeffefb | 1174 | TAKE_PTR(n); |
bce67bbe SS |
1175 | return 0; |
1176 | } | |
1177 | ||
1178 | int config_parse_routing_policy_rule_priority( | |
1179 | const char *unit, | |
1180 | const char *filename, | |
1181 | unsigned line, | |
1182 | const char *section, | |
1183 | unsigned section_line, | |
1184 | const char *lvalue, | |
1185 | int ltype, | |
1186 | const char *rvalue, | |
1187 | void *data, | |
1188 | void *userdata) { | |
1189 | ||
fcbf4cb7 | 1190 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
bce67bbe SS |
1191 | Network *network = userdata; |
1192 | int r; | |
1193 | ||
1194 | assert(filename); | |
1195 | assert(section); | |
1196 | assert(lvalue); | |
1197 | assert(rvalue); | |
1198 | assert(data); | |
1199 | ||
1200 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1201 | if (r < 0) | |
d96edb2c | 1202 | return log_oom(); |
bce67bbe | 1203 | |
c4f7a347 YW |
1204 | if (isempty(rvalue)) { |
1205 | n->priority = 0; | |
1206 | n->priority_set = false; | |
1207 | TAKE_PTR(n); | |
1208 | return 0; | |
1209 | } | |
1210 | ||
bce67bbe SS |
1211 | r = safe_atou32(rvalue, &n->priority); |
1212 | if (r < 0) { | |
d96edb2c | 1213 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue); |
bce67bbe SS |
1214 | return 0; |
1215 | } | |
c4f7a347 | 1216 | n->priority_set = true; |
bce67bbe | 1217 | |
8eeffefb | 1218 | TAKE_PTR(n); |
bce67bbe SS |
1219 | return 0; |
1220 | } | |
1221 | ||
1222 | int config_parse_routing_policy_rule_table( | |
1223 | const char *unit, | |
1224 | const char *filename, | |
1225 | unsigned line, | |
1226 | const char *section, | |
1227 | unsigned section_line, | |
1228 | const char *lvalue, | |
1229 | int ltype, | |
1230 | const char *rvalue, | |
1231 | void *data, | |
1232 | void *userdata) { | |
1233 | ||
fcbf4cb7 | 1234 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
bce67bbe SS |
1235 | Network *network = userdata; |
1236 | int r; | |
1237 | ||
1238 | assert(filename); | |
1239 | assert(section); | |
1240 | assert(lvalue); | |
1241 | assert(rvalue); | |
1242 | assert(data); | |
1243 | ||
1244 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1245 | if (r < 0) | |
d96edb2c | 1246 | return log_oom(); |
bce67bbe | 1247 | |
552b90a2 | 1248 | r = manager_get_route_table_from_string(network->manager, rvalue, &n->table); |
bce67bbe | 1249 | if (r < 0) { |
c038ce46 | 1250 | log_syntax(unit, LOG_WARNING, filename, line, r, |
29de4f73 | 1251 | "Could not parse RPDB rule route table \"%s\", ignoring assignment: %m", rvalue); |
bce67bbe SS |
1252 | return 0; |
1253 | } | |
1254 | ||
8eeffefb | 1255 | TAKE_PTR(n); |
bce67bbe SS |
1256 | return 0; |
1257 | } | |
1258 | ||
1259 | int config_parse_routing_policy_rule_fwmark_mask( | |
1260 | const char *unit, | |
1261 | const char *filename, | |
1262 | unsigned line, | |
1263 | const char *section, | |
1264 | unsigned section_line, | |
1265 | const char *lvalue, | |
1266 | int ltype, | |
1267 | const char *rvalue, | |
1268 | void *data, | |
1269 | void *userdata) { | |
1270 | ||
fcbf4cb7 | 1271 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
bce67bbe SS |
1272 | Network *network = userdata; |
1273 | int r; | |
1274 | ||
1275 | assert(filename); | |
1276 | assert(section); | |
1277 | assert(lvalue); | |
1278 | assert(rvalue); | |
1279 | assert(data); | |
1280 | ||
1281 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1282 | if (r < 0) | |
d96edb2c | 1283 | return log_oom(); |
bce67bbe SS |
1284 | |
1285 | r = parse_fwmark_fwmask(rvalue, &n->fwmark, &n->fwmask); | |
1286 | if (r < 0) { | |
d96edb2c | 1287 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue); |
bce67bbe SS |
1288 | return 0; |
1289 | } | |
1290 | ||
8eeffefb | 1291 | TAKE_PTR(n); |
bce67bbe SS |
1292 | return 0; |
1293 | } | |
1294 | ||
1295 | int config_parse_routing_policy_rule_prefix( | |
1296 | const char *unit, | |
1297 | const char *filename, | |
1298 | unsigned line, | |
1299 | const char *section, | |
1300 | unsigned section_line, | |
1301 | const char *lvalue, | |
1302 | int ltype, | |
1303 | const char *rvalue, | |
1304 | void *data, | |
1305 | void *userdata) { | |
1306 | ||
fcbf4cb7 | 1307 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
bce67bbe | 1308 | Network *network = userdata; |
7934dede YW |
1309 | union in_addr_union *buffer; |
1310 | uint8_t *prefixlen; | |
bce67bbe SS |
1311 | int r; |
1312 | ||
1313 | assert(filename); | |
1314 | assert(section); | |
1315 | assert(lvalue); | |
1316 | assert(rvalue); | |
1317 | assert(data); | |
1318 | ||
1319 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1320 | if (r < 0) | |
d96edb2c | 1321 | return log_oom(); |
bce67bbe | 1322 | |
bce67bbe | 1323 | if (streq(lvalue, "To")) { |
7934dede YW |
1324 | buffer = &n->to; |
1325 | prefixlen = &n->to_prefixlen; | |
bce67bbe | 1326 | } else { |
7934dede YW |
1327 | buffer = &n->from; |
1328 | prefixlen = &n->from_prefixlen; | |
1329 | } | |
1330 | ||
0aabccc8 YW |
1331 | if (n->family == AF_UNSPEC) |
1332 | r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen); | |
1333 | else | |
1334 | r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen); | |
7934dede | 1335 | if (r < 0) { |
d96edb2c | 1336 | log_syntax(unit, LOG_WARNING, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue); |
7934dede | 1337 | return 0; |
bce67bbe SS |
1338 | } |
1339 | ||
8eeffefb | 1340 | TAKE_PTR(n); |
bce67bbe SS |
1341 | return 0; |
1342 | } | |
1343 | ||
762e2659 SS |
1344 | int config_parse_routing_policy_rule_device( |
1345 | const char *unit, | |
1346 | const char *filename, | |
1347 | unsigned line, | |
1348 | const char *section, | |
1349 | unsigned section_line, | |
1350 | const char *lvalue, | |
1351 | int ltype, | |
1352 | const char *rvalue, | |
1353 | void *data, | |
1354 | void *userdata) { | |
1355 | ||
fcbf4cb7 | 1356 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
762e2659 SS |
1357 | Network *network = userdata; |
1358 | int r; | |
1359 | ||
1360 | assert(filename); | |
1361 | assert(section); | |
1362 | assert(lvalue); | |
1363 | assert(rvalue); | |
1364 | assert(data); | |
1365 | ||
1366 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1367 | if (r < 0) | |
d96edb2c | 1368 | return log_oom(); |
762e2659 SS |
1369 | |
1370 | if (!ifname_valid(rvalue)) { | |
893e3ffe YW |
1371 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
1372 | "Invalid interface name '%s' in %s=, ignoring assignment.", rvalue, lvalue); | |
762e2659 SS |
1373 | return 0; |
1374 | } | |
1375 | ||
893e3ffe YW |
1376 | r = free_and_strdup(streq(lvalue, "IncomingInterface") ? &n->iif : &n->oif, rvalue); |
1377 | if (r < 0) | |
1378 | return log_oom(); | |
762e2659 | 1379 | |
8eeffefb | 1380 | TAKE_PTR(n); |
762e2659 SS |
1381 | return 0; |
1382 | } | |
1383 | ||
926062f0 SS |
1384 | int config_parse_routing_policy_rule_port_range( |
1385 | const char *unit, | |
1386 | const char *filename, | |
1387 | unsigned line, | |
1388 | const char *section, | |
1389 | unsigned section_line, | |
1390 | const char *lvalue, | |
1391 | int ltype, | |
1392 | const char *rvalue, | |
1393 | void *data, | |
1394 | void *userdata) { | |
893e3ffe | 1395 | |
fcbf4cb7 | 1396 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
926062f0 SS |
1397 | Network *network = userdata; |
1398 | uint16_t low, high; | |
1399 | int r; | |
1400 | ||
1401 | assert(filename); | |
1402 | assert(section); | |
1403 | assert(lvalue); | |
1404 | assert(rvalue); | |
1405 | assert(data); | |
1406 | ||
1407 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1408 | if (r < 0) | |
d96edb2c | 1409 | return log_oom(); |
926062f0 | 1410 | |
dcfac3a3 | 1411 | r = parse_ip_port_range(rvalue, &low, &high, /* allow_zero = */ false); |
926062f0 | 1412 | if (r < 0) { |
d96edb2c | 1413 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue); |
926062f0 SS |
1414 | return 0; |
1415 | } | |
1416 | ||
1417 | if (streq(lvalue, "SourcePort")) { | |
1418 | n->sport.start = low; | |
1419 | n->sport.end = high; | |
1420 | } else { | |
1421 | n->dport.start = low; | |
1422 | n->dport.end = high; | |
1423 | } | |
1424 | ||
8eeffefb | 1425 | TAKE_PTR(n); |
926062f0 SS |
1426 | return 0; |
1427 | } | |
1428 | ||
97f9df9e | 1429 | int config_parse_routing_policy_rule_ip_protocol( |
926062f0 SS |
1430 | const char *unit, |
1431 | const char *filename, | |
1432 | unsigned line, | |
1433 | const char *section, | |
1434 | unsigned section_line, | |
1435 | const char *lvalue, | |
1436 | int ltype, | |
1437 | const char *rvalue, | |
1438 | void *data, | |
1439 | void *userdata) { | |
1440 | ||
fcbf4cb7 | 1441 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
926062f0 SS |
1442 | Network *network = userdata; |
1443 | int r; | |
1444 | ||
1445 | assert(filename); | |
1446 | assert(section); | |
1447 | assert(lvalue); | |
1448 | assert(rvalue); | |
1449 | assert(data); | |
1450 | ||
1451 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1452 | if (r < 0) | |
d96edb2c | 1453 | return log_oom(); |
926062f0 | 1454 | |
3a269dcf | 1455 | r = parse_ip_protocol(rvalue); |
926062f0 | 1456 | if (r < 0) { |
d96edb2c | 1457 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IP protocol '%s' for routing policy rule, ignoring: %m", rvalue); |
926062f0 SS |
1458 | return 0; |
1459 | } | |
1460 | ||
1e5fd321 | 1461 | n->ipproto = r; |
926062f0 | 1462 | |
8eeffefb | 1463 | TAKE_PTR(n); |
926062f0 SS |
1464 | return 0; |
1465 | } | |
1466 | ||
8b220643 SS |
1467 | int config_parse_routing_policy_rule_invert( |
1468 | const char *unit, | |
1469 | const char *filename, | |
1470 | unsigned line, | |
1471 | const char *section, | |
1472 | unsigned section_line, | |
1473 | const char *lvalue, | |
1474 | int ltype, | |
1475 | const char *rvalue, | |
1476 | void *data, | |
1477 | void *userdata) { | |
1478 | ||
fcbf4cb7 | 1479 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; |
8b220643 SS |
1480 | Network *network = userdata; |
1481 | int r; | |
1482 | ||
1483 | assert(filename); | |
1484 | assert(section); | |
1485 | assert(lvalue); | |
1486 | assert(rvalue); | |
1487 | assert(data); | |
1488 | ||
1489 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1490 | if (r < 0) | |
d96edb2c | 1491 | return log_oom(); |
8b220643 SS |
1492 | |
1493 | r = parse_boolean(rvalue); | |
1494 | if (r < 0) { | |
d96edb2c | 1495 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule invert, ignoring: %s", rvalue); |
8b220643 SS |
1496 | return 0; |
1497 | } | |
1498 | ||
1499 | n->invert_rule = r; | |
1500 | ||
8eeffefb | 1501 | TAKE_PTR(n); |
8b220643 SS |
1502 | return 0; |
1503 | } | |
1504 | ||
f6c6ff97 YW |
1505 | int config_parse_routing_policy_rule_family( |
1506 | const char *unit, | |
1507 | const char *filename, | |
1508 | unsigned line, | |
1509 | const char *section, | |
1510 | unsigned section_line, | |
1511 | const char *lvalue, | |
1512 | int ltype, | |
1513 | const char *rvalue, | |
1514 | void *data, | |
1515 | void *userdata) { | |
1516 | ||
1517 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; | |
1518 | Network *network = userdata; | |
1519 | AddressFamily a; | |
1520 | int r; | |
1521 | ||
1522 | assert(filename); | |
1523 | assert(section); | |
1524 | assert(lvalue); | |
1525 | assert(rvalue); | |
1526 | assert(data); | |
1527 | ||
1528 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1529 | if (r < 0) | |
d96edb2c | 1530 | return log_oom(); |
f6c6ff97 YW |
1531 | |
1532 | a = routing_policy_rule_address_family_from_string(rvalue); | |
1533 | if (a < 0) { | |
b98680b2 | 1534 | log_syntax(unit, LOG_WARNING, filename, line, a, |
f6c6ff97 YW |
1535 | "Invalid address family '%s', ignoring.", rvalue); |
1536 | return 0; | |
1537 | } | |
1538 | ||
1539 | n->address_family = a; | |
f6c6ff97 | 1540 | |
8eeffefb | 1541 | TAKE_PTR(n); |
f6c6ff97 YW |
1542 | return 0; |
1543 | } | |
1544 | ||
ea471a46 YW |
1545 | int config_parse_routing_policy_rule_uid_range( |
1546 | const char *unit, | |
1547 | const char *filename, | |
1548 | unsigned line, | |
1549 | const char *section, | |
1550 | unsigned section_line, | |
1551 | const char *lvalue, | |
1552 | int ltype, | |
1553 | const char *rvalue, | |
1554 | void *data, | |
1555 | void *userdata) { | |
1556 | ||
1557 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; | |
1558 | Network *network = userdata; | |
1559 | uid_t start, end; | |
1560 | int r; | |
1561 | ||
1562 | assert(filename); | |
1563 | assert(section); | |
1564 | assert(lvalue); | |
1565 | assert(rvalue); | |
1566 | assert(data); | |
1567 | ||
1568 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1569 | if (r < 0) | |
d96edb2c | 1570 | return log_oom(); |
ea471a46 YW |
1571 | |
1572 | r = get_user_creds(&rvalue, &start, NULL, NULL, NULL, 0); | |
1573 | if (r >= 0) | |
1574 | end = start; | |
1575 | else { | |
1576 | r = parse_uid_range(rvalue, &start, &end); | |
1577 | if (r < 0) { | |
d96edb2c | 1578 | log_syntax(unit, LOG_WARNING, filename, line, r, |
ea471a46 YW |
1579 | "Invalid uid or uid range '%s', ignoring: %m", rvalue); |
1580 | return 0; | |
1581 | } | |
1582 | } | |
1583 | ||
1584 | n->uid_range.start = start; | |
1585 | n->uid_range.end = end; | |
53e1ba28 | 1586 | |
8eeffefb | 1587 | TAKE_PTR(n); |
53e1ba28 NF |
1588 | return 0; |
1589 | } | |
1590 | ||
1591 | int config_parse_routing_policy_rule_suppress_prefixlen( | |
1592 | const char *unit, | |
1593 | const char *filename, | |
1594 | unsigned line, | |
1595 | const char *section, | |
1596 | unsigned section_line, | |
1597 | const char *lvalue, | |
1598 | int ltype, | |
1599 | const char *rvalue, | |
1600 | void *data, | |
1601 | void *userdata) { | |
1602 | ||
1603 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; | |
1604 | Network *network = userdata; | |
1605 | int r; | |
1606 | ||
1607 | assert(filename); | |
1608 | assert(section); | |
1609 | assert(lvalue); | |
1610 | assert(rvalue); | |
1611 | assert(data); | |
1612 | ||
1613 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1614 | if (r < 0) | |
d96edb2c | 1615 | return log_oom(); |
53e1ba28 NF |
1616 | |
1617 | r = parse_ip_prefix_length(rvalue, &n->suppress_prefixlen); | |
1618 | if (r == -ERANGE) { | |
d96edb2c | 1619 | log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix length outside of valid range 0-128, ignoring: %s", rvalue); |
53e1ba28 NF |
1620 | return 0; |
1621 | } | |
1622 | if (r < 0) { | |
d96edb2c | 1623 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", rvalue); |
53e1ba28 NF |
1624 | return 0; |
1625 | } | |
1626 | ||
8eeffefb | 1627 | TAKE_PTR(n); |
ea471a46 YW |
1628 | return 0; |
1629 | } | |
1630 | ||
af493fb7 SB |
1631 | int config_parse_routing_policy_rule_suppress_ifgroup( |
1632 | const char *unit, | |
1633 | const char *filename, | |
1634 | unsigned line, | |
1635 | const char *section, | |
1636 | unsigned section_line, | |
1637 | const char *lvalue, | |
1638 | int ltype, | |
1639 | const char *rvalue, | |
1640 | void *data, | |
1641 | void *userdata) { | |
1642 | ||
1643 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; | |
1644 | Network *network = userdata; | |
1645 | int32_t suppress_ifgroup; | |
1646 | int r; | |
1647 | ||
1648 | assert(filename); | |
1649 | assert(section); | |
1650 | assert(lvalue); | |
1651 | assert(rvalue); | |
1652 | assert(data); | |
1653 | ||
1654 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1655 | if (r < 0) | |
1656 | return log_oom(); | |
1657 | ||
1658 | if (isempty(rvalue)) { | |
1659 | n->suppress_ifgroup = -1; | |
1660 | return 0; | |
1661 | } | |
1662 | ||
1663 | r = safe_atoi32(rvalue, &suppress_ifgroup); | |
1664 | if (r < 0) { | |
1665 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1666 | "Failed to parse SuppressInterfaceGroup=, ignoring assignment: %s", rvalue); | |
1667 | return 0; | |
1668 | } | |
1669 | if (suppress_ifgroup < 0) { | |
1670 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1671 | "Value of SuppressInterfaceGroup= must be in the range 0…2147483647, ignoring assignment: %s", rvalue); | |
1672 | return 0; | |
1673 | } | |
1674 | n->suppress_ifgroup = suppress_ifgroup; | |
1675 | TAKE_PTR(n); | |
1676 | return 0; | |
1677 | } | |
1678 | ||
d7d1d18f SS |
1679 | int config_parse_routing_policy_rule_type( |
1680 | const char *unit, | |
1681 | const char *filename, | |
1682 | unsigned line, | |
1683 | const char *section, | |
1684 | unsigned section_line, | |
1685 | const char *lvalue, | |
1686 | int ltype, | |
1687 | const char *rvalue, | |
1688 | void *data, | |
1689 | void *userdata) { | |
1690 | ||
1691 | _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; | |
1692 | Network *network = userdata; | |
1693 | int r, t; | |
1694 | ||
1695 | assert(filename); | |
1696 | assert(section); | |
1697 | assert(lvalue); | |
1698 | assert(rvalue); | |
1699 | assert(data); | |
1700 | ||
1701 | r = routing_policy_rule_new_static(network, filename, section_line, &n); | |
1702 | if (r < 0) | |
1703 | return log_oom(); | |
1704 | ||
1705 | t = fr_act_type_from_string(rvalue); | |
1706 | if (t < 0) { | |
b98680b2 | 1707 | log_syntax(unit, LOG_WARNING, filename, line, t, |
d7d1d18f SS |
1708 | "Could not parse FIB rule type \"%s\", ignoring assignment: %m", rvalue); |
1709 | return 0; | |
1710 | } | |
1711 | ||
1712 | n->type = (uint8_t) t; | |
d7d1d18f | 1713 | |
8eeffefb | 1714 | TAKE_PTR(n); |
d7d1d18f SS |
1715 | return 0; |
1716 | } | |
1717 | ||
50a3682f | 1718 | static int routing_policy_rule_section_verify(RoutingPolicyRule *rule) { |
7532b888 YW |
1719 | if (section_is_invalid(rule->section)) |
1720 | return -EINVAL; | |
1721 | ||
1722 | if ((rule->family == AF_INET && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) || | |
1723 | (rule->family == AF_INET6 && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4))) | |
1724 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
1725 | "%s: address family specified by Family= conflicts with the address " | |
1726 | "specified by To= or From=. Ignoring [RoutingPolicyRule] section from line %u.", | |
1727 | rule->section->filename, rule->section->line); | |
1728 | ||
49de8d5c YW |
1729 | if (rule->family == AF_UNSPEC) { |
1730 | if (IN_SET(rule->address_family, ADDRESS_FAMILY_IPV4, ADDRESS_FAMILY_NO)) | |
1731 | rule->family = AF_INET; | |
1732 | else if (rule->address_family == ADDRESS_FAMILY_IPV6) | |
1733 | rule->family = AF_INET6; | |
1734 | /* rule->family can be AF_UNSPEC only when Family=both. */ | |
1735 | } | |
7532b888 | 1736 | |
e737dce5 YW |
1737 | /* Currently, [RoutingPolicyRule] does not have a setting to set FRA_L3MDEV flag. Please also |
1738 | * update routing_policy_rule_is_created_by_kernel() when a new setting which sets the flag is | |
1739 | * added in the future. */ | |
1740 | if (rule->l3mdev > 0) | |
04499a70 | 1741 | assert_not_reached(); |
e737dce5 | 1742 | |
7532b888 YW |
1743 | return 0; |
1744 | } | |
1745 | ||
13ffa39f | 1746 | void network_drop_invalid_routing_policy_rules(Network *network) { |
50a3682f YW |
1747 | RoutingPolicyRule *rule; |
1748 | ||
1749 | assert(network); | |
1750 | ||
1751 | HASHMAP_FOREACH(rule, network->rules_by_section) | |
1752 | if (routing_policy_rule_section_verify(rule) < 0) | |
1753 | routing_policy_rule_free(rule); | |
1754 | } |