]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-routing-policy-rule.c
network: add missing entry in serialization/deserialization
[thirdparty/systemd.git] / src / network / networkd-routing-policy-rule.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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"
da96ad5a 10#include "ip-protocol-list.h"
bce67bbe
SS
11#include "networkd-routing-policy-rule.h"
12#include "netlink-util.h"
13#include "networkd-manager.h"
14#include "parse-util.h"
15#include "socket-util.h"
16#include "string-util.h"
51517f9e 17#include "strv.h"
bce67bbe
SS
18
19int routing_policy_rule_new(RoutingPolicyRule **ret) {
20 RoutingPolicyRule *rule;
21
9d66b48c 22 rule = new(RoutingPolicyRule, 1);
bce67bbe
SS
23 if (!rule)
24 return -ENOMEM;
25
9d66b48c 26 *rule = (RoutingPolicyRule) {
9d66b48c
YW
27 .table = RT_TABLE_MAIN,
28 };
bce67bbe
SS
29
30 *ret = rule;
31 return 0;
32}
33
34void routing_policy_rule_free(RoutingPolicyRule *rule) {
35
36 if (!rule)
37 return;
38
39 if (rule->network) {
40 LIST_REMOVE(rules, rule->network->rules, rule);
41 assert(rule->network->n_rules > 0);
42 rule->network->n_rules--;
43
0f7f2769 44 if (rule->section)
bce67bbe 45 hashmap_remove(rule->network->rules_by_section, rule->section);
36e6e28b
SS
46 }
47
6964cf45 48 if (rule->manager) {
9ee92e7e
YW
49 if (set_get(rule->manager->rules, rule) == rule)
50 set_remove(rule->manager->rules, rule);
51 if (set_get(rule->manager->rules_foreign, rule) == rule)
52 set_remove(rule->manager->rules_foreign, rule);
bce67bbe
SS
53 }
54
0f7f2769 55 network_config_section_free(rule->section);
762e2659
SS
56 free(rule->iif);
57 free(rule->oif);
bce67bbe
SS
58 free(rule);
59}
60
7a08d314 61static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash *state) {
bce67bbe
SS
62 assert(rule);
63
64 siphash24_compress(&rule->family, sizeof(rule->family), state);
65
66 switch (rule->family) {
67 case AF_INET:
68 case AF_INET6:
69
70 siphash24_compress(&rule->from, FAMILY_ADDRESS_SIZE(rule->family), state);
71 siphash24_compress(&rule->from_prefixlen, sizeof(rule->from_prefixlen), state);
72
73 siphash24_compress(&rule->to, FAMILY_ADDRESS_SIZE(rule->family), state);
74 siphash24_compress(&rule->to_prefixlen, sizeof(rule->to_prefixlen), state);
75
b80a511b
YW
76 siphash24_compress_boolean(rule->invert_rule, state);
77
bce67bbe
SS
78 siphash24_compress(&rule->tos, sizeof(rule->tos), state);
79 siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state);
b80a511b
YW
80 siphash24_compress(&rule->fwmask, sizeof(rule->fwmask), state);
81 siphash24_compress(&rule->priority, sizeof(rule->priority), state);
bce67bbe
SS
82 siphash24_compress(&rule->table, sizeof(rule->table), state);
83
926062f0
SS
84 siphash24_compress(&rule->protocol, sizeof(rule->protocol), state);
85 siphash24_compress(&rule->sport, sizeof(rule->sport), state);
86 siphash24_compress(&rule->dport, sizeof(rule->dport), state);
87
762e2659 88 if (rule->iif)
0d7febd0 89 siphash24_compress(rule->iif, strlen(rule->iif), state);
762e2659
SS
90
91 if (rule->oif)
0d7febd0 92 siphash24_compress(rule->oif, strlen(rule->oif), state);
762e2659 93
bce67bbe
SS
94 break;
95 default:
96 /* treat any other address family as AF_UNSPEC */
97 break;
98 }
99}
100
7a08d314 101static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPolicyRule *b) {
bce67bbe
SS
102 int r;
103
a0edd02e
FB
104 r = CMP(a->family, b->family);
105 if (r != 0)
106 return r;
bce67bbe
SS
107
108 switch (a->family) {
109 case AF_INET:
110 case AF_INET6:
a0edd02e
FB
111 r = CMP(a->from_prefixlen, b->from_prefixlen);
112 if (r != 0)
113 return r;
114
115 r = CMP(a->to_prefixlen, b->to_prefixlen);
116 if (r != 0)
117 return r;
118
b80a511b
YW
119 r = CMP(a->invert_rule, b->invert_rule);
120 if (r != 0)
121 return r;
122
a0edd02e
FB
123 r = CMP(a->tos, b->tos);
124 if (r != 0)
125 return r;
126
b80a511b
YW
127 r = CMP(a->fwmark, b->fwmark);
128 if (r != 0)
129 return r;
130
a0edd02e
FB
131 r = CMP(a->fwmask, b->fwmask);
132 if (r != 0)
133 return r;
134
b80a511b
YW
135 r = CMP(a->priority, b->priority);
136 if (r != 0)
137 return r;
138
a0edd02e
FB
139 r = CMP(a->table, b->table);
140 if (r != 0)
141 return r;
bce67bbe 142
762e2659
SS
143 r = strcmp_ptr(a->iif, b->iif);
144 if (!r)
145 return r;
146
147 r = strcmp_ptr(a->oif, b->oif);
148 if (!r)
149 return r;
150
926062f0
SS
151 r = CMP(a->protocol, b->protocol);
152 if (r != 0)
153 return r;
154
155 r = memcmp(&a->sport, &b->sport, sizeof(a->sport));
156 if (r != 0)
157 return r;
158
159 r = memcmp(&a->dport, &b->dport, sizeof(a->dport));
160 if (r != 0)
161 return r;
162
bce67bbe
SS
163 r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
164 if (r != 0)
165 return r;
166
167 return memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family));
168
169 default:
170 /* treat any other address family as AF_UNSPEC */
171 return 0;
172 }
173}
174
7a08d314 175DEFINE_PRIVATE_HASH_OPS(routing_policy_rule_hash_ops, RoutingPolicyRule, routing_policy_rule_hash_func, routing_policy_rule_compare_func);
bce67bbe 176
b80a511b 177int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
bce67bbe 178
b80a511b 179 RoutingPolicyRule *existing;
bce67bbe 180
b80a511b 181 assert(m);
bce67bbe 182
b80a511b 183 existing = set_get(m->rules, rule);
b87dadcd
ZJS
184 if (existing) {
185 if (ret)
186 *ret = existing;
187 return 1;
bce67bbe
SS
188 }
189
b80a511b 190 existing = set_get(m->rules_foreign, rule);
b87dadcd
ZJS
191 if (existing) {
192 if (ret)
193 *ret = existing;
e6b65ab7 194 return 0;
bce67bbe
SS
195 }
196
197 return -ENOENT;
198}
199
200int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) {
201 int r;
202
203 assert(m);
204
205 if (set_contains(m->rules_foreign, rule)) {
206 set_remove(m->rules_foreign, rule);
207
208 r = set_ensure_allocated(&m->rules, &routing_policy_rule_hash_ops);
209 if (r < 0)
210 return r;
211
75a302b5
YW
212 r = set_put(m->rules, rule);
213 if (r < 0)
214 return r;
215 if (r == 0)
216 routing_policy_rule_free(rule);
bce67bbe
SS
217 }
218
219 return -ENOENT;
220}
221
b80a511b 222static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPolicyRule *in, RoutingPolicyRule **ret) {
8e766630 223 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
eeab051b 224 _cleanup_free_ char *iif = NULL, *oif = NULL;
bce67bbe
SS
225 int r;
226
b80a511b
YW
227 assert(m);
228 assert(rules);
229 assert(in);
bce67bbe 230
b80a511b
YW
231 if (in->iif) {
232 iif = strdup(in->iif);
eeab051b
YW
233 if (!iif)
234 return -ENOMEM;
235 }
236
b80a511b
YW
237 if (in->oif) {
238 oif = strdup(in->oif);
eeab051b
YW
239 if (!oif)
240 return -ENOMEM;
241 }
242
bce67bbe
SS
243 r = routing_policy_rule_new(&rule);
244 if (r < 0)
245 return r;
246
6964cf45 247 rule->manager = m;
b80a511b
YW
248 rule->family = in->family;
249 rule->from = in->from;
250 rule->from_prefixlen = in->from_prefixlen;
251 rule->to = in->to;
252 rule->to_prefixlen = in->to_prefixlen;
253 rule->invert_rule = in->invert_rule;
254 rule->tos = in->tos;
255 rule->fwmark = in->fwmark;
256 rule->fwmask = in->fwmask;
257 rule->priority = in->priority;
258 rule->table = in->table;
d26267dd
YW
259 rule->iif = TAKE_PTR(iif);
260 rule->oif = TAKE_PTR(oif);
b80a511b
YW
261 rule->protocol = in->protocol;
262 rule->sport = in->sport;
263 rule->dport = in->dport;
bce67bbe
SS
264
265 r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
266 if (r < 0)
267 return r;
268
269 r = set_put(*rules, rule);
270 if (r < 0)
271 return r;
75a302b5
YW
272 if (r == 0)
273 return -EEXIST;
bce67bbe
SS
274
275 if (ret)
276 *ret = rule;
277
d26267dd 278 TAKE_PTR(rule);
bce67bbe
SS
279 return 0;
280}
281
b80a511b
YW
282static int routing_policy_rule_add(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
283 return routing_policy_rule_add_internal(m, &m->rules, rule, ret);
bce67bbe
SS
284}
285
b80a511b
YW
286int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
287 return routing_policy_rule_add_internal(m, &m->rules_foreign, rule, ret);
bce67bbe
SS
288}
289
302a796f 290static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
bce67bbe
SS
291 int r;
292
293 assert(m);
294 assert(link);
295 assert(link->ifname);
296
7715629e 297 link->routing_policy_rule_remove_messages--;
bce67bbe
SS
298
299 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
300 return 1;
301
302 r = sd_netlink_message_get_errno(m);
303 if (r < 0)
304 log_link_warning_errno(link, r, "Could not drop routing policy rule: %m");
305
306 return 1;
307}
308
302a796f 309int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, link_netlink_message_handler_t callback) {
bce67bbe
SS
310 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
311 int r;
312
313 assert(routing_policy_rule);
314 assert(link);
315 assert(link->manager);
316 assert(link->manager->rtnl);
317 assert(link->ifindex > 0);
318 assert(IN_SET(routing_policy_rule->family, AF_INET, AF_INET6));
319
320 r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_DELRULE, routing_policy_rule->family);
321 if (r < 0)
322 return log_error_errno(r, "Could not allocate RTM_DELRULE message: %m");
323
d40b01e4 324 if (in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->from) == 0) {
43409486 325 r = netlink_message_append_in_addr_union(m, FRA_SRC, routing_policy_rule->family, &routing_policy_rule->from);
bce67bbe
SS
326 if (r < 0)
327 return log_error_errno(r, "Could not append FRA_SRC attribute: %m");
328
329 r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, routing_policy_rule->from_prefixlen);
330 if (r < 0)
331 return log_error_errno(r, "Could not set source prefix length: %m");
332 }
333
d40b01e4 334 if (in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->to) == 0) {
43409486 335 r = netlink_message_append_in_addr_union(m, FRA_DST, routing_policy_rule->family, &routing_policy_rule->to);
bce67bbe
SS
336 if (r < 0)
337 return log_error_errno(r, "Could not append FRA_DST attribute: %m");
338
339 r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, routing_policy_rule->to_prefixlen);
340 if (r < 0)
341 return log_error_errno(r, "Could not set destination prefix length: %m");
342 }
343
302a796f
YW
344 r = netlink_call_async(link->manager->rtnl, NULL, m,
345 callback ?: routing_policy_rule_remove_handler,
346 link_netlink_destroy_callback, link);
bce67bbe
SS
347 if (r < 0)
348 return log_error_errno(r, "Could not send rtnetlink message: %m");
349
350 link_ref(link);
351
352 return 0;
353}
354
355static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) {
8e766630
LP
356 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
357 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
bce67bbe
SS
358 int r;
359
360 assert(network);
361 assert(ret);
362 assert(!!filename == (section_line > 0));
363
0f7f2769
YW
364 if (filename) {
365 r = network_config_section_new(filename, section_line, &n);
366 if (r < 0)
367 return r;
bce67bbe 368
0f7f2769
YW
369 rule = hashmap_get(network->rules_by_section, n);
370 if (rule) {
371 *ret = TAKE_PTR(rule);
bce67bbe 372
0f7f2769
YW
373 return 0;
374 }
bce67bbe
SS
375 }
376
377 r = routing_policy_rule_new(&rule);
378 if (r < 0)
379 return r;
380
bce67bbe 381 rule->network = network;
bce67bbe
SS
382 LIST_APPEND(rules, network->rules, rule);
383 network->n_rules++;
384
0f7f2769
YW
385 if (filename) {
386 rule->section = TAKE_PTR(n);
387
3e570042
YW
388 r = hashmap_ensure_allocated(&network->rules_by_section, &network_config_hash_ops);
389 if (r < 0)
390 return r;
391
0f7f2769
YW
392 r = hashmap_put(network->rules_by_section, rule->section, rule);
393 if (r < 0)
394 return r;
395 }
396
1cc6c93a 397 *ret = TAKE_PTR(rule);
bce67bbe
SS
398
399 return 0;
400}
401
302a796f 402static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
bce67bbe
SS
403 int r;
404
405 assert(rtnl);
406 assert(m);
407 assert(link);
408 assert(link->ifname);
7715629e 409 assert(link->routing_policy_rule_messages > 0);
bce67bbe 410
7715629e 411 link->routing_policy_rule_messages--;
bce67bbe
SS
412
413 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
414 return 1;
415
416 r = sd_netlink_message_get_errno(m);
4ff296b0 417 if (r < 0 && r != -EEXIST) {
bce67bbe 418 log_link_warning_errno(link, r, "Could not add routing policy rule: %m");
4ff296b0
YW
419 link_enter_failed(link);
420 return 1;
421 }
bce67bbe 422
7715629e 423 if (link->routing_policy_rule_messages == 0) {
bce67bbe 424 log_link_debug(link, "Routing policy rule configured");
7715629e
ST
425 link->routing_policy_rules_configured = true;
426 link_check_ready(link);
427 }
bce67bbe
SS
428
429 return 1;
430}
431
9f08a578 432int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback) {
bce67bbe
SS
433 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
434 int r;
435
436 assert(rule);
437 assert(link);
438 assert(link->ifindex > 0);
439 assert(link->manager);
440 assert(link->manager->rtnl);
441
bafa9641 442 if (rule->family == AF_INET6 && link_sysctl_ipv6_enabled(link) == 0) {
7ef7e550
YW
443 log_link_warning(link, "An IPv6 routing policy rule is requested, but IPv6 is disabled by sysctl, ignoring.");
444 return 0;
445 }
446
bce67bbe
SS
447 r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family);
448 if (r < 0)
449 return log_error_errno(r, "Could not allocate RTM_NEWRULE message: %m");
450
d40b01e4 451 if (in_addr_is_null(rule->family, &rule->from) == 0) {
43409486 452 r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from);
bce67bbe
SS
453 if (r < 0)
454 return log_error_errno(r, "Could not append FRA_SRC attribute: %m");
455
456 r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen);
457 if (r < 0)
458 return log_error_errno(r, "Could not set source prefix length: %m");
459 }
460
d40b01e4 461 if (in_addr_is_null(rule->family, &rule->to) == 0) {
43409486 462 r = netlink_message_append_in_addr_union(m, FRA_DST, rule->family, &rule->to);
bce67bbe
SS
463 if (r < 0)
464 return log_error_errno(r, "Could not append FRA_DST attribute: %m");
465
466 r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen);
467 if (r < 0)
468 return log_error_errno(r, "Could not set destination prefix length: %m");
469 }
470
471 r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
472 if (r < 0)
473 return log_error_errno(r, "Could not append FRA_PRIORITY attribute: %m");
474
475 if (rule->tos > 0) {
476 r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos);
477 if (r < 0)
478 return log_error_errno(r, "Could not set ip rule tos: %m");
479 }
480
481 if (rule->table < 256) {
482 r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table);
483 if (r < 0)
484 return log_error_errno(r, "Could not set ip rule table: %m");
485 } else {
486 r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC);
487 if (r < 0)
488 return log_error_errno(r, "Could not set ip rule table: %m");
489
490 r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table);
491 if (r < 0)
492 return log_error_errno(r, "Could not append FRA_TABLE attribute: %m");
493 }
494
495 if (rule->fwmark > 0) {
496 r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark);
497 if (r < 0)
498 return log_error_errno(r, "Could not append FRA_FWMARK attribute: %m");
499 }
500
501 if (rule->fwmask > 0) {
502 r = sd_netlink_message_append_u32(m, FRA_FWMASK, rule->fwmask);
503 if (r < 0)
504 return log_error_errno(r, "Could not append FRA_FWMASK attribute: %m");
505 }
506
762e2659
SS
507 if (rule->iif) {
508 r = sd_netlink_message_append_string(m, FRA_IFNAME, rule->iif);
509 if (r < 0)
510 return log_error_errno(r, "Could not append FRA_IFNAME attribute: %m");
511 }
512
513 if (rule->oif) {
514 r = sd_netlink_message_append_string(m, FRA_OIFNAME, rule->oif);
515 if (r < 0)
516 return log_error_errno(r, "Could not append FRA_OIFNAME attribute: %m");
517 }
518
926062f0
SS
519 r = sd_netlink_message_append_u8(m, FRA_IP_PROTO, rule->protocol);
520 if (r < 0)
521 return log_error_errno(r, "Could not append FRA_IP_PROTO attribute: %m");
522
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)
526 return log_error_errno(r, "Could not append FRA_SPORT_RANGE attribute: %m");
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)
532 return log_error_errno(r, "Could not append FRA_DPORT_RANGE attribute: %m");
533 }
534
8b220643
SS
535 if (rule->invert_rule) {
536 r = sd_rtnl_message_routing_policy_rule_set_flags(m, FIB_RULE_INVERT);
537 if (r < 0)
538 return log_error_errno(r, "Could not append FIB_RULE_INVERT attribute: %m");
539 }
540
bce67bbe
SS
541 rule->link = link;
542
302a796f
YW
543 r = netlink_call_async(link->manager->rtnl, NULL, m,
544 callback ?: routing_policy_rule_handler,
545 link_netlink_destroy_callback, link);
bce67bbe
SS
546 if (r < 0)
547 return log_error_errno(r, "Could not send rtnetlink message: %m");
548
549 link_ref(link);
550
b80a511b 551 r = routing_policy_rule_add(link->manager, rule, NULL);
bce67bbe 552 if (r < 0)
cb700a11 553 return log_error_errno(r, "Could not add rule: %m");
bce67bbe 554
7ef7e550 555 return 1;
bce67bbe
SS
556}
557
0aabccc8
YW
558int routing_policy_rule_section_verify(RoutingPolicyRule *rule) {
559 if (section_is_invalid(rule->section))
560 return -EINVAL;
561
562 if (rule->family == AF_UNSPEC)
563 rule->family = AF_INET;
564
565 return 0;
566}
567
bce67bbe
SS
568static int parse_fwmark_fwmask(const char *s, uint32_t *fwmark, uint32_t *fwmask) {
569 _cleanup_free_ char *f = NULL;
570 char *p;
571 int r;
572
573 assert(s);
574
575 f = strdup(s);
576 if (!f)
577 return -ENOMEM;
578
579 p = strchr(f, '/');
580 if (p)
581 *p++ = '\0';
582
583 r = safe_atou32(f, fwmark);
584 if (r < 0)
585 return log_error_errno(r, "Failed to parse RPDB rule firewall mark, ignoring: %s", f);
586
587 if (p) {
588 r = safe_atou32(p, fwmask);
589 if (r < 0)
590 return log_error_errno(r, "Failed to parse RPDB rule mask, ignoring: %s", f);
591 }
592
593 return 0;
594}
595
596int config_parse_routing_policy_rule_tos(
597 const char *unit,
598 const char *filename,
599 unsigned line,
600 const char *section,
601 unsigned section_line,
602 const char *lvalue,
603 int ltype,
604 const char *rvalue,
605 void *data,
606 void *userdata) {
607
fcbf4cb7 608 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
bce67bbe
SS
609 Network *network = userdata;
610 int r;
611
612 assert(filename);
613 assert(section);
614 assert(lvalue);
615 assert(rvalue);
616 assert(data);
617
618 r = routing_policy_rule_new_static(network, filename, section_line, &n);
619 if (r < 0)
620 return r;
621
622 r = safe_atou8(rvalue, &n->tos);
623 if (r < 0) {
624 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule tos, ignoring: %s", rvalue);
625 return 0;
626 }
627
628 n = NULL;
629
630 return 0;
631}
632
633int config_parse_routing_policy_rule_priority(
634 const char *unit,
635 const char *filename,
636 unsigned line,
637 const char *section,
638 unsigned section_line,
639 const char *lvalue,
640 int ltype,
641 const char *rvalue,
642 void *data,
643 void *userdata) {
644
fcbf4cb7 645 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
bce67bbe
SS
646 Network *network = userdata;
647 int r;
648
649 assert(filename);
650 assert(section);
651 assert(lvalue);
652 assert(rvalue);
653 assert(data);
654
655 r = routing_policy_rule_new_static(network, filename, section_line, &n);
656 if (r < 0)
657 return r;
658
659 r = safe_atou32(rvalue, &n->priority);
660 if (r < 0) {
661 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue);
662 return 0;
663 }
664
665 n = NULL;
666
667 return 0;
668}
669
670int config_parse_routing_policy_rule_table(
671 const char *unit,
672 const char *filename,
673 unsigned line,
674 const char *section,
675 unsigned section_line,
676 const char *lvalue,
677 int ltype,
678 const char *rvalue,
679 void *data,
680 void *userdata) {
681
fcbf4cb7 682 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
bce67bbe
SS
683 Network *network = userdata;
684 int r;
685
686 assert(filename);
687 assert(section);
688 assert(lvalue);
689 assert(rvalue);
690 assert(data);
691
692 r = routing_policy_rule_new_static(network, filename, section_line, &n);
693 if (r < 0)
694 return r;
695
696 r = safe_atou32(rvalue, &n->table);
697 if (r < 0) {
698 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule table, ignoring: %s", rvalue);
699 return 0;
700 }
701
702 n = NULL;
703
704 return 0;
705}
706
707int config_parse_routing_policy_rule_fwmark_mask(
708 const char *unit,
709 const char *filename,
710 unsigned line,
711 const char *section,
712 unsigned section_line,
713 const char *lvalue,
714 int ltype,
715 const char *rvalue,
716 void *data,
717 void *userdata) {
718
fcbf4cb7 719 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
bce67bbe
SS
720 Network *network = userdata;
721 int r;
722
723 assert(filename);
724 assert(section);
725 assert(lvalue);
726 assert(rvalue);
727 assert(data);
728
729 r = routing_policy_rule_new_static(network, filename, section_line, &n);
730 if (r < 0)
731 return r;
732
733 r = parse_fwmark_fwmask(rvalue, &n->fwmark, &n->fwmask);
734 if (r < 0) {
735 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue);
736 return 0;
737 }
738
739 n = NULL;
740
741 return 0;
742}
743
744int config_parse_routing_policy_rule_prefix(
745 const char *unit,
746 const char *filename,
747 unsigned line,
748 const char *section,
749 unsigned section_line,
750 const char *lvalue,
751 int ltype,
752 const char *rvalue,
753 void *data,
754 void *userdata) {
755
fcbf4cb7 756 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
bce67bbe 757 Network *network = userdata;
7934dede
YW
758 union in_addr_union *buffer;
759 uint8_t *prefixlen;
bce67bbe
SS
760 int r;
761
762 assert(filename);
763 assert(section);
764 assert(lvalue);
765 assert(rvalue);
766 assert(data);
767
768 r = routing_policy_rule_new_static(network, filename, section_line, &n);
769 if (r < 0)
770 return r;
771
bce67bbe 772 if (streq(lvalue, "To")) {
7934dede
YW
773 buffer = &n->to;
774 prefixlen = &n->to_prefixlen;
bce67bbe 775 } else {
7934dede
YW
776 buffer = &n->from;
777 prefixlen = &n->from_prefixlen;
778 }
779
0aabccc8
YW
780 if (n->family == AF_UNSPEC)
781 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
782 else
783 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
7934dede
YW
784 if (r < 0) {
785 log_syntax(unit, LOG_ERR, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue);
786 return 0;
bce67bbe
SS
787 }
788
789 n = NULL;
790
791 return 0;
792}
793
762e2659
SS
794int config_parse_routing_policy_rule_device(
795 const char *unit,
796 const char *filename,
797 unsigned line,
798 const char *section,
799 unsigned section_line,
800 const char *lvalue,
801 int ltype,
802 const char *rvalue,
803 void *data,
804 void *userdata) {
805
fcbf4cb7 806 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
762e2659
SS
807 Network *network = userdata;
808 int r;
809
810 assert(filename);
811 assert(section);
812 assert(lvalue);
813 assert(rvalue);
814 assert(data);
815
816 r = routing_policy_rule_new_static(network, filename, section_line, &n);
817 if (r < 0)
818 return r;
819
820 if (!ifname_valid(rvalue)) {
821 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s' interface name, ignoring: %s", lvalue, rvalue);
822 return 0;
823 }
824
825 if (streq(lvalue, "IncomingInterface")) {
826 r = free_and_strdup(&n->iif, rvalue);
827 if (r < 0)
828 return log_oom();
829 } else {
830 r = free_and_strdup(&n->oif, rvalue);
831 if (r < 0)
832 return log_oom();
833 }
834
835 n = NULL;
836
837 return 0;
838}
839
926062f0
SS
840int config_parse_routing_policy_rule_port_range(
841 const char *unit,
842 const char *filename,
843 unsigned line,
844 const char *section,
845 unsigned section_line,
846 const char *lvalue,
847 int ltype,
848 const char *rvalue,
849 void *data,
850 void *userdata) {
fcbf4cb7 851 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
926062f0
SS
852 Network *network = userdata;
853 uint16_t low, high;
854 int r;
855
856 assert(filename);
857 assert(section);
858 assert(lvalue);
859 assert(rvalue);
860 assert(data);
861
862 r = routing_policy_rule_new_static(network, filename, section_line, &n);
863 if (r < 0)
864 return r;
865
866 r = parse_ip_port_range(rvalue, &low, &high);
867 if (r < 0) {
868 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue);
869 return 0;
870 }
871
872 if (streq(lvalue, "SourcePort")) {
873 n->sport.start = low;
874 n->sport.end = high;
875 } else {
876 n->dport.start = low;
877 n->dport.end = high;
878 }
879
880 n = NULL;
881
882 return 0;
883}
884
97f9df9e 885int config_parse_routing_policy_rule_ip_protocol(
926062f0
SS
886 const char *unit,
887 const char *filename,
888 unsigned line,
889 const char *section,
890 unsigned section_line,
891 const char *lvalue,
892 int ltype,
893 const char *rvalue,
894 void *data,
895 void *userdata) {
896
fcbf4cb7 897 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
926062f0
SS
898 Network *network = userdata;
899 int r;
900
901 assert(filename);
902 assert(section);
903 assert(lvalue);
904 assert(rvalue);
905 assert(data);
906
907 r = routing_policy_rule_new_static(network, filename, section_line, &n);
908 if (r < 0)
909 return r;
910
3a269dcf 911 r = parse_ip_protocol(rvalue);
926062f0 912 if (r < 0) {
3a269dcf 913 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IP protocol '%s' for routing policy rule, ignoring: %m", rvalue);
926062f0
SS
914 return 0;
915 }
916
917 n->protocol = r;
918
919 n = NULL;
920
921 return 0;
922}
923
8b220643
SS
924int config_parse_routing_policy_rule_invert(
925 const char *unit,
926 const char *filename,
927 unsigned line,
928 const char *section,
929 unsigned section_line,
930 const char *lvalue,
931 int ltype,
932 const char *rvalue,
933 void *data,
934 void *userdata) {
935
fcbf4cb7 936 _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
8b220643
SS
937 Network *network = userdata;
938 int r;
939
940 assert(filename);
941 assert(section);
942 assert(lvalue);
943 assert(rvalue);
944 assert(data);
945
946 r = routing_policy_rule_new_static(network, filename, section_line, &n);
947 if (r < 0)
948 return r;
949
950 r = parse_boolean(rvalue);
951 if (r < 0) {
952 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule invert, ignoring: %s", rvalue);
953 return 0;
954 }
955
956 n->invert_rule = r;
957
958 n = NULL;
959
960 return 0;
961}
962
458d8ae3 963static int routing_policy_rule_read_full_file(const char *state_file, char **ret) {
ac097c84 964 _cleanup_free_ char *s = NULL;
bce67bbe
SS
965 size_t size;
966 int r;
967
968 assert(state_file);
969
970 r = read_full_file(state_file, &s, &size);
971 if (r == -ENOENT)
972 return -ENODATA;
973 if (r < 0)
974 return r;
975 if (size <= 0)
976 return -ENODATA;
977
ae2a15bc 978 *ret = TAKE_PTR(s);
bce67bbe
SS
979
980 return size;
981}
982
458d8ae3
ZJS
983int routing_policy_serialize_rules(Set *rules, FILE *f) {
984 RoutingPolicyRule *rule = NULL;
985 Iterator i;
986 int r;
987
988 assert(f);
989
990 SET_FOREACH(rule, rules, i) {
991 _cleanup_free_ char *from_str = NULL, *to_str = NULL;
992 bool space = false;
43e08c78 993 const char *family_str;
458d8ae3
ZJS
994
995 fputs("RULE=", f);
996
997 if (!in_addr_is_null(rule->family, &rule->from)) {
998 r = in_addr_to_string(rule->family, &rule->from, &from_str);
999 if (r < 0)
1000 return r;
1001
1002 fprintf(f, "from=%s/%hhu",
1003 from_str, rule->from_prefixlen);
1004 space = true;
1005 }
1006
1007 if (!in_addr_is_null(rule->family, &rule->to)) {
1008 r = in_addr_to_string(rule->family, &rule->to, &to_str);
1009 if (r < 0)
1010 return r;
1011
1012 fprintf(f, "%sto=%s/%hhu",
1013 space ? " " : "",
1014 to_str, rule->to_prefixlen);
1015 space = true;
1016 }
1017
43e08c78
YW
1018 family_str = af_to_name(rule->family);
1019 if (family_str)
1020 fprintf(f, "%sfamily=%s",
1021 space ? " " : "",
1022 family_str);
1023
458d8ae3
ZJS
1024 if (rule->tos != 0) {
1025 fprintf(f, "%stos=%hhu",
1026 space ? " " : "",
1027 rule->tos);
1028 space = true;
1029 }
1030
ec863cdc
YW
1031 fprintf(f, "%spriority=%"PRIu32,
1032 space ? " " : "",
1033 rule->priority);
1034
458d8ae3
ZJS
1035 if (rule->fwmark != 0) {
1036 fprintf(f, "%sfwmark=%"PRIu32"/%"PRIu32,
1037 space ? " " : "",
1038 rule->fwmark, rule->fwmask);
1039 space = true;
1040 }
1041
9491f55f
ZJS
1042 if (rule->iif) {
1043 fprintf(f, "%siif=%s",
1044 space ? " " : "",
1045 rule->iif);
1046 space = true;
1047 }
1048
1049 if (rule->oif) {
1050 fprintf(f, "%soif=%s",
1051 space ? " " : "",
1052 rule->oif);
1053 space = true;
1054 }
1055
926062f0
SS
1056 if (rule->protocol != 0) {
1057 fprintf(f, "%sprotocol=%hhu",
1058 space ? " " : "",
1059 rule->protocol);
1060 space = true;
1061 }
1062
1063 if (rule->sport.start != 0 || rule->sport.end != 0) {
58a02e4c 1064 fprintf(f, "%ssourcesport=%"PRIu16"-%"PRIu16,
926062f0
SS
1065 space ? " " : "",
1066 rule->sport.start, rule->sport.end);
1067 space = true;
1068 }
1069
1070 if (rule->dport.start != 0 || rule->dport.end != 0) {
58a02e4c 1071 fprintf(f, "%sdestinationport=%"PRIu16"-%"PRIu16,
926062f0
SS
1072 space ? " " : "",
1073 rule->dport.start, rule->dport.end);
1074 space = true;
1075 }
1076
458d8ae3
ZJS
1077 fprintf(f, "%stable=%"PRIu32 "\n",
1078 space ? " " : "",
1079 rule->table);
1080 }
1081
1082 return 0;
1083}
1084
1085int routing_policy_load_rules(const char *state_file, Set **rules) {
bce67bbe
SS
1086 _cleanup_strv_free_ char **l = NULL;
1087 _cleanup_free_ char *data = NULL;
926062f0 1088 uint16_t low = 0, high = 0;
bce67bbe
SS
1089 const char *p;
1090 char **i;
1091 int r;
1092
458d8ae3
ZJS
1093 assert(state_file);
1094 assert(rules);
bce67bbe 1095
458d8ae3 1096 r = routing_policy_rule_read_full_file(state_file, &data);
bce67bbe
SS
1097 if (r <= 0)
1098 return r;
1099
1100 l = strv_split_newlines(data);
1101 if (!l)
1102 return -ENOMEM;
1103
458d8ae3 1104 r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
bce67bbe
SS
1105 if (r < 0)
1106 return r;
1107
1108 STRV_FOREACH(i, l) {
8e766630 1109 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
bce67bbe
SS
1110
1111 p = startswith(*i, "RULE=");
1112 if (!p)
1113 continue;
1114
bce67bbe
SS
1115 r = routing_policy_rule_new(&rule);
1116 if (r < 0)
1117 return r;
1118
1119 for (;;) {
1120 _cleanup_free_ char *word = NULL, *a = NULL, *b = NULL;
bce67bbe
SS
1121
1122 r = extract_first_word(&p, &word, NULL, 0);
1123 if (r < 0)
1124 return r;
1125 if (r == 0)
1126 break;
1127
1128 r = split_pair(word, "=", &a, &b);
1129 if (r < 0)
1130 continue;
1131
1132 if (STR_IN_SET(a, "from", "to")) {
7934dede
YW
1133 union in_addr_union *buffer;
1134 uint8_t *prefixlen;
bce67bbe
SS
1135
1136 if (streq(a, "to")) {
7934dede
YW
1137 buffer = &rule->to;
1138 prefixlen = &rule->to_prefixlen;
bce67bbe 1139 } else {
7934dede
YW
1140 buffer = &rule->from;
1141 prefixlen = &rule->from_prefixlen;
1142 }
1143
1144 r = in_addr_prefix_from_string_auto(b, &rule->family, buffer, prefixlen);
1145 if (r < 0) {
1146 log_error_errno(r, "RPDB rule prefix is invalid, ignoring assignment: %s", b);
1147 continue;
bce67bbe 1148 }
7934dede 1149
43e08c78
YW
1150 } else if (streq(a, "family")) {
1151 r = af_from_name(b);
1152 if (r < 0) {
1153 log_error_errno(r, "Failed to parse RPDB rule family, ignoring: %s", b);
1154 continue;
1155 }
1156 rule->family = r;
bce67bbe
SS
1157 } else if (streq(a, "tos")) {
1158 r = safe_atou8(b, &rule->tos);
1159 if (r < 0) {
1160 log_error_errno(r, "Failed to parse RPDB rule tos, ignoring: %s", b);
1161 continue;
1162 }
1163 } else if (streq(a, "table")) {
1164 r = safe_atou32(b, &rule->table);
1165 if (r < 0) {
1166 log_error_errno(r, "Failed to parse RPDB rule table, ignoring: %s", b);
1167 continue;
1168 }
ec863cdc
YW
1169 } else if (streq(a, "priority")) {
1170 r = safe_atou32(b, &rule->priority);
1171 if (r < 0) {
1172 log_error_errno(r, "Failed to parse RPDB rule priority, ignoring: %s", b);
1173 continue;
1174 }
bce67bbe
SS
1175 } else if (streq(a, "fwmark")) {
1176
e4aca57d 1177 r = parse_fwmark_fwmask(b, &rule->fwmark, &rule->fwmask);
bce67bbe
SS
1178 if (r < 0) {
1179 log_error_errno(r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", a);
1180 continue;
1181 }
9491f55f 1182 } else if (streq(a, "iif")) {
762e2659 1183
93f9da6e 1184 if (free_and_strdup(&rule->iif, b) < 0)
762e2659 1185 return log_oom();
93f9da6e 1186
9491f55f 1187 } else if (streq(a, "oif")) {
762e2659 1188
93f9da6e 1189 if (free_and_strdup(&rule->oif, b) < 0)
762e2659 1190 return log_oom();
926062f0
SS
1191 } else if (streq(a, "protocol")) {
1192 r = safe_atou8(b, &rule->protocol);
1193 if (r < 0) {
1194 log_error_errno(r, "Failed to parse RPDB rule protocol, ignoring: %s", b);
1195 continue;
1196 }
1197 } else if (streq(a, "sourceport")) {
1198
1199 r = parse_ip_port_range(b, &low, &high);
1200 if (r < 0) {
1201 log_error_errno(r, "Invalid routing policy rule source port range, ignoring assignment:'%s'", b);
1202 continue;
1203 }
1204
1205 rule->sport.start = low;
1206 rule->sport.end = high;
1207
1208 } else if (streq(a, "destinationport")) {
1209
1210 r = parse_ip_port_range(b, &low, &high);
1211 if (r < 0) {
1212 log_error_errno(r, "Invalid routing policy rule destination port range, ignoring assignment:'%s'", b);
1213 continue;
1214 }
1215
1216 rule->dport.start = low;
1217 rule->dport.end = high;
bce67bbe
SS
1218 }
1219 }
1220
458d8ae3 1221 r = set_put(*rules, rule);
bce67bbe
SS
1222 if (r < 0) {
1223 log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", p);
1224 continue;
1225 }
75a302b5
YW
1226 if (r > 0)
1227 rule = NULL;
bce67bbe
SS
1228 }
1229
1230 return 0;
1231}
1232
031fb59a
YW
1233static bool manager_links_have_routing_policy_rule(Manager *m, RoutingPolicyRule *rule) {
1234 RoutingPolicyRule *link_rule;
1235 Iterator i;
1236 Link *link;
1237
1238 assert(m);
1239 assert(rule);
1240
1241 HASHMAP_FOREACH(link, m->links, i) {
1242 if (!link->network)
1243 continue;
1244
1245 LIST_FOREACH(rules, link_rule, link->network->rules)
1246 if (routing_policy_rule_compare_func(link_rule, rule) == 0)
1247 return true;
1248 }
1249
1250 return false;
1251}
1252
bce67bbe
SS
1253void routing_policy_rule_purge(Manager *m, Link *link) {
1254 RoutingPolicyRule *rule, *existing;
1255 Iterator i;
1256 int r;
1257
1258 assert(m);
1259 assert(link);
1260
1261 SET_FOREACH(rule, m->rules_saved, i) {
1262 existing = set_get(m->rules_foreign, rule);
92cd00b9
YW
1263 if (!existing)
1264 continue; /* Saved rule does not exist anymore. */
bce67bbe 1265
031fb59a
YW
1266 if (manager_links_have_routing_policy_rule(m, existing))
1267 continue; /* Existing links have the saved rule. */
1268
1269 /* Existing links do not have the saved rule. Let's drop the rule now, and re-configure it
1270 * later when it is requested. */
1271
92cd00b9
YW
1272 r = routing_policy_rule_remove(existing, link, NULL);
1273 if (r < 0) {
1274 log_warning_errno(r, "Could not remove routing policy rules: %m");
1275 continue;
bce67bbe 1276 }
92cd00b9
YW
1277
1278 link->routing_policy_rule_remove_messages++;
1279
1280 assert_se(set_remove(m->rules_foreign, existing) == existing);
1281 routing_policy_rule_free(existing);
bce67bbe
SS
1282 }
1283}