]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-routing-policy-rule.c
network: Use CMP() macro for comparison.
[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
6#include "alloc-util.h"
7#include "conf-parser.h"
8#include "fileio.h"
9#include "networkd-routing-policy-rule.h"
10#include "netlink-util.h"
11#include "networkd-manager.h"
12#include "parse-util.h"
13#include "socket-util.h"
14#include "string-util.h"
15
16int routing_policy_rule_new(RoutingPolicyRule **ret) {
17 RoutingPolicyRule *rule;
18
19 rule = new0(RoutingPolicyRule, 1);
20 if (!rule)
21 return -ENOMEM;
22
23 rule->family = AF_INET;
24 rule->table = RT_TABLE_MAIN;
25
26 *ret = rule;
27 return 0;
28}
29
30void routing_policy_rule_free(RoutingPolicyRule *rule) {
31
32 if (!rule)
33 return;
34
35 if (rule->network) {
36 LIST_REMOVE(rules, rule->network->rules, rule);
37 assert(rule->network->n_rules > 0);
38 rule->network->n_rules--;
39
40 if (rule->section) {
41 hashmap_remove(rule->network->rules_by_section, rule->section);
42 network_config_section_free(rule->section);
43 }
44
36e6e28b
SS
45 }
46
6964cf45
SS
47 if (rule->manager) {
48 set_remove(rule->manager->rules, rule);
49 set_remove(rule->manager->rules_foreign, rule);
bce67bbe
SS
50 }
51
762e2659
SS
52 free(rule->iif);
53 free(rule->oif);
bce67bbe
SS
54 free(rule);
55}
56
57static void routing_policy_rule_hash_func(const void *b, struct siphash *state) {
58 const RoutingPolicyRule *rule = b;
59
60 assert(rule);
61
62 siphash24_compress(&rule->family, sizeof(rule->family), state);
63
64 switch (rule->family) {
65 case AF_INET:
66 case AF_INET6:
67
68 siphash24_compress(&rule->from, FAMILY_ADDRESS_SIZE(rule->family), state);
69 siphash24_compress(&rule->from_prefixlen, sizeof(rule->from_prefixlen), state);
70
71 siphash24_compress(&rule->to, FAMILY_ADDRESS_SIZE(rule->family), state);
72 siphash24_compress(&rule->to_prefixlen, sizeof(rule->to_prefixlen), state);
73
74 siphash24_compress(&rule->tos, sizeof(rule->tos), state);
75 siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state);
76 siphash24_compress(&rule->table, sizeof(rule->table), state);
77
762e2659
SS
78 if (rule->iif)
79 siphash24_compress(&rule->iif, strlen(rule->iif), state);
80
81 if (rule->oif)
82 siphash24_compress(&rule->oif, strlen(rule->oif), state);
83
bce67bbe
SS
84 break;
85 default:
86 /* treat any other address family as AF_UNSPEC */
87 break;
88 }
89}
90
91static int routing_policy_rule_compare_func(const void *_a, const void *_b) {
92 const RoutingPolicyRule *a = _a, *b = _b;
93 int r;
94
95 if (a->family < b->family)
96 return -1;
97 if (a->family > b->family)
98 return 1;
99
100 switch (a->family) {
101 case AF_INET:
102 case AF_INET6:
103 if (a->from_prefixlen < b->from_prefixlen)
104 return -1;
105 if (a->from_prefixlen > b->from_prefixlen)
106 return 1;
107
108 if (a->to_prefixlen < b->to_prefixlen)
109 return -1;
110 if (a->to_prefixlen > b->to_prefixlen)
111 return 1;
112
113 if (a->tos < b->tos)
114 return -1;
115 if (a->tos > b->tos)
116 return 1;
117
118 if (a->fwmask < b->fwmark)
119 return -1;
120 if (a->fwmask > b->fwmark)
121 return 1;
122
123 if (a->table < b->table)
124 return -1;
125 if (a->table > b->table)
126 return 1;
127
762e2659
SS
128 r = strcmp_ptr(a->iif, b->iif);
129 if (!r)
130 return r;
131
132 r = strcmp_ptr(a->oif, b->oif);
133 if (!r)
134 return r;
135
bce67bbe
SS
136 r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family));
137 if (r != 0)
138 return r;
139
140 return memcmp(&a->to, &b->to, FAMILY_ADDRESS_SIZE(a->family));
141
142 default:
143 /* treat any other address family as AF_UNSPEC */
144 return 0;
145 }
146}
147
148const struct hash_ops routing_policy_rule_hash_ops = {
149 .hash = routing_policy_rule_hash_func,
150 .compare = routing_policy_rule_compare_func
151};
152
153int routing_policy_rule_get(Manager *m,
154 int family,
155 const union in_addr_union *from,
156 uint8_t from_prefixlen,
157 const union in_addr_union *to,
158 uint8_t to_prefixlen,
159 uint8_t tos,
160 uint32_t fwmark,
161 uint32_t table,
762e2659
SS
162 char *iif,
163 char *oif,
bce67bbe
SS
164 RoutingPolicyRule **ret) {
165
166 RoutingPolicyRule rule, *existing;
167
168 assert_return(m, -1);
169
170 rule = (RoutingPolicyRule) {
171 .family = family,
172 .from = *from,
173 .from_prefixlen = from_prefixlen,
174 .to = *to,
175 .to_prefixlen = to_prefixlen,
176 .tos = tos,
177 .fwmark = fwmark,
178 .table = table,
762e2659
SS
179 .iif = iif,
180 .oif = oif
bce67bbe
SS
181 };
182
183 if (m->rules) {
184 existing = set_get(m->rules, &rule);
185 if (existing) {
186 if (ret)
187 *ret = existing;
188 return 1;
189 }
190 }
191
192 if (m->rules_foreign) {
193 existing = set_get(m->rules_foreign, &rule);
194 if (existing) {
195 if (ret)
196 *ret = existing;
197 return 1;
198 }
199 }
200
201 return -ENOENT;
202}
203
204int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) {
205 int r;
206
207 assert(m);
208
209 if (set_contains(m->rules_foreign, rule)) {
210 set_remove(m->rules_foreign, rule);
211
212 r = set_ensure_allocated(&m->rules, &routing_policy_rule_hash_ops);
213 if (r < 0)
214 return r;
215
216 return set_put(m->rules, rule);
217 }
218
219 return -ENOENT;
220}
221
36e6e28b
SS
222static int routing_policy_rule_add_internal(Manager *m,
223 Set **rules,
bce67bbe
SS
224 int family,
225 const union in_addr_union *from,
226 uint8_t from_prefixlen,
227 const union in_addr_union *to,
228 uint8_t to_prefixlen,
229 uint8_t tos,
230 uint32_t fwmark,
231 uint32_t table,
762e2659
SS
232 char *iif,
233 char *oif,
bce67bbe
SS
234 RoutingPolicyRule **ret) {
235
8e766630 236 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
bce67bbe
SS
237 int r;
238
239 assert_return(rules, -EINVAL);
240
241 r = routing_policy_rule_new(&rule);
242 if (r < 0)
243 return r;
244
6964cf45 245 rule->manager = m;
bce67bbe
SS
246 rule->family = family;
247 rule->from = *from;
248 rule->from_prefixlen = from_prefixlen;
249 rule->to = *to;
250 rule->to_prefixlen = to_prefixlen;
251 rule->tos = tos;
252 rule->fwmark = fwmark;
253 rule->table = table;
762e2659
SS
254 rule->iif = iif;
255 rule->oif = oif;
bce67bbe
SS
256
257 r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
258 if (r < 0)
259 return r;
260
261 r = set_put(*rules, rule);
262 if (r < 0)
263 return r;
264
265 if (ret)
266 *ret = rule;
267
268 rule = NULL;
269
270 return 0;
271}
272
273int routing_policy_rule_add(Manager *m,
274 int family,
275 const union in_addr_union *from,
276 uint8_t from_prefixlen,
277 const union in_addr_union *to,
278 uint8_t to_prefixlen,
279 uint8_t tos,
280 uint32_t fwmark,
281 uint32_t table,
762e2659
SS
282 char *iif,
283 char *oif,
bce67bbe
SS
284 RoutingPolicyRule **ret) {
285
36e6e28b 286 return routing_policy_rule_add_internal(m, &m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
bce67bbe
SS
287}
288
289int routing_policy_rule_add_foreign(Manager *m,
290 int family,
291 const union in_addr_union *from,
292 uint8_t from_prefixlen,
293 const union in_addr_union *to,
294 uint8_t to_prefixlen,
295 uint8_t tos,
296 uint32_t fwmark,
297 uint32_t table,
762e2659
SS
298 char *iif,
299 char *oif,
bce67bbe 300 RoutingPolicyRule **ret) {
36e6e28b 301 return routing_policy_rule_add_internal(m, &m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret);
bce67bbe
SS
302}
303
304static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
8e766630 305 _cleanup_(link_unrefp) Link *link = userdata;
bce67bbe
SS
306 int r;
307
308 assert(m);
309 assert(link);
310 assert(link->ifname);
311
7715629e 312 link->routing_policy_rule_remove_messages--;
bce67bbe
SS
313
314 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
315 return 1;
316
317 r = sd_netlink_message_get_errno(m);
318 if (r < 0)
319 log_link_warning_errno(link, r, "Could not drop routing policy rule: %m");
320
321 return 1;
322}
323
324int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, sd_netlink_message_handler_t callback) {
325 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
326 int r;
327
328 assert(routing_policy_rule);
329 assert(link);
330 assert(link->manager);
331 assert(link->manager->rtnl);
332 assert(link->ifindex > 0);
333 assert(IN_SET(routing_policy_rule->family, AF_INET, AF_INET6));
334
335 r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_DELRULE, routing_policy_rule->family);
336 if (r < 0)
337 return log_error_errno(r, "Could not allocate RTM_DELRULE message: %m");
338
339 if (!in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->from)) {
340 if (routing_policy_rule->family == AF_INET)
341 r = sd_netlink_message_append_in_addr(m, FRA_SRC, &routing_policy_rule->from.in);
342 else
343 r = sd_netlink_message_append_in6_addr(m, FRA_SRC, &routing_policy_rule->from.in6);
344
345 if (r < 0)
346 return log_error_errno(r, "Could not append FRA_SRC attribute: %m");
347
348 r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, routing_policy_rule->from_prefixlen);
349 if (r < 0)
350 return log_error_errno(r, "Could not set source prefix length: %m");
351 }
352
353 if (!in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->to)) {
354 if (routing_policy_rule->family == AF_INET)
355 r = sd_netlink_message_append_in_addr(m, FRA_DST, &routing_policy_rule->to.in);
356 else
357 r = sd_netlink_message_append_in6_addr(m, FRA_DST, &routing_policy_rule->to.in6);
358
359 if (r < 0)
360 return log_error_errno(r, "Could not append FRA_DST attribute: %m");
361
362 r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, routing_policy_rule->to_prefixlen);
363 if (r < 0)
364 return log_error_errno(r, "Could not set destination prefix length: %m");
365 }
366
367 r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL);
368 if (r < 0)
369 return log_error_errno(r, "Could not send rtnetlink message: %m");
370
371 link_ref(link);
372
373 return 0;
374}
375
376static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) {
8e766630
LP
377 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
378 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
bce67bbe
SS
379 int r;
380
381 assert(network);
382 assert(ret);
383 assert(!!filename == (section_line > 0));
384
385 r = network_config_section_new(filename, section_line, &n);
386 if (r < 0)
387 return r;
388
389 rule = hashmap_get(network->rules_by_section, n);
390 if (rule) {
1cc6c93a 391 *ret = TAKE_PTR(rule);
bce67bbe
SS
392
393 return 0;
394 }
395
396 r = routing_policy_rule_new(&rule);
397 if (r < 0)
398 return r;
399
400 rule->section = n;
401 rule->network = network;
402 n = NULL;
403
404 r = hashmap_put(network->rules_by_section, rule->section, rule);
405 if (r < 0)
406 return r;
407
408 LIST_APPEND(rules, network->rules, rule);
409 network->n_rules++;
410
1cc6c93a 411 *ret = TAKE_PTR(rule);
bce67bbe
SS
412
413 return 0;
414}
415
416int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
8e766630 417 _cleanup_(link_unrefp) Link *link = userdata;
bce67bbe
SS
418 int r;
419
420 assert(rtnl);
421 assert(m);
422 assert(link);
423 assert(link->ifname);
7715629e 424 assert(link->routing_policy_rule_messages > 0);
bce67bbe 425
7715629e 426 link->routing_policy_rule_messages--;
bce67bbe
SS
427
428 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
429 return 1;
430
431 r = sd_netlink_message_get_errno(m);
432 if (r < 0 && r != -EEXIST)
433 log_link_warning_errno(link, r, "Could not add routing policy rule: %m");
434
7715629e 435 if (link->routing_policy_rule_messages == 0) {
bce67bbe 436 log_link_debug(link, "Routing policy rule configured");
7715629e
ST
437 link->routing_policy_rules_configured = true;
438 link_check_ready(link);
439 }
bce67bbe
SS
440
441 return 1;
442}
443
444int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlink_message_handler_t callback, bool update) {
445 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
446 int r;
447
448 assert(rule);
449 assert(link);
450 assert(link->ifindex > 0);
451 assert(link->manager);
452 assert(link->manager->rtnl);
453
454 r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family);
455 if (r < 0)
456 return log_error_errno(r, "Could not allocate RTM_NEWRULE message: %m");
457
458 if (!in_addr_is_null(rule->family, &rule->from)) {
459 if (rule->family == AF_INET)
460 r = sd_netlink_message_append_in_addr(m, FRA_SRC, &rule->from.in);
461 else
462 r = sd_netlink_message_append_in6_addr(m, FRA_SRC, &rule->from.in6);
463
464 if (r < 0)
465 return log_error_errno(r, "Could not append FRA_SRC attribute: %m");
466
467 r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen);
468 if (r < 0)
469 return log_error_errno(r, "Could not set source prefix length: %m");
470 }
471
472 if (!in_addr_is_null(rule->family, &rule->to)) {
473 if (rule->family == AF_INET)
474 r = sd_netlink_message_append_in_addr(m, FRA_DST, &rule->to.in);
475 else
476 r = sd_netlink_message_append_in6_addr(m, FRA_DST, &rule->to.in6);
477
478 if (r < 0)
479 return log_error_errno(r, "Could not append FRA_DST attribute: %m");
480
481 r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen);
482 if (r < 0)
483 return log_error_errno(r, "Could not set destination prefix length: %m");
484 }
485
486 r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
487 if (r < 0)
488 return log_error_errno(r, "Could not append FRA_PRIORITY attribute: %m");
489
490 if (rule->tos > 0) {
491 r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos);
492 if (r < 0)
493 return log_error_errno(r, "Could not set ip rule tos: %m");
494 }
495
496 if (rule->table < 256) {
497 r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table);
498 if (r < 0)
499 return log_error_errno(r, "Could not set ip rule table: %m");
500 } else {
501 r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC);
502 if (r < 0)
503 return log_error_errno(r, "Could not set ip rule table: %m");
504
505 r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table);
506 if (r < 0)
507 return log_error_errno(r, "Could not append FRA_TABLE attribute: %m");
508 }
509
510 if (rule->fwmark > 0) {
511 r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark);
512 if (r < 0)
513 return log_error_errno(r, "Could not append FRA_FWMARK attribute: %m");
514 }
515
516 if (rule->fwmask > 0) {
517 r = sd_netlink_message_append_u32(m, FRA_FWMASK, rule->fwmask);
518 if (r < 0)
519 return log_error_errno(r, "Could not append FRA_FWMASK attribute: %m");
520 }
521
762e2659
SS
522 if (rule->iif) {
523 r = sd_netlink_message_append_string(m, FRA_IFNAME, rule->iif);
524 if (r < 0)
525 return log_error_errno(r, "Could not append FRA_IFNAME attribute: %m");
526 }
527
528 if (rule->oif) {
529 r = sd_netlink_message_append_string(m, FRA_OIFNAME, rule->oif);
530 if (r < 0)
531 return log_error_errno(r, "Could not append FRA_OIFNAME attribute: %m");
532 }
533
bce67bbe
SS
534 rule->link = link;
535
536 r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL);
537 if (r < 0)
538 return log_error_errno(r, "Could not send rtnetlink message: %m");
539
540 link_ref(link);
541
542 r = routing_policy_rule_add(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
762e2659 543 rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, NULL);
bce67bbe
SS
544 if (r < 0)
545 return log_error_errno(r, "Could not add rule : %m");
546
547 return 0;
548}
549
550static int parse_fwmark_fwmask(const char *s, uint32_t *fwmark, uint32_t *fwmask) {
551 _cleanup_free_ char *f = NULL;
552 char *p;
553 int r;
554
555 assert(s);
556
557 f = strdup(s);
558 if (!f)
559 return -ENOMEM;
560
561 p = strchr(f, '/');
562 if (p)
563 *p++ = '\0';
564
565 r = safe_atou32(f, fwmark);
566 if (r < 0)
567 return log_error_errno(r, "Failed to parse RPDB rule firewall mark, ignoring: %s", f);
568
569 if (p) {
570 r = safe_atou32(p, fwmask);
571 if (r < 0)
572 return log_error_errno(r, "Failed to parse RPDB rule mask, ignoring: %s", f);
573 }
574
575 return 0;
576}
577
578int config_parse_routing_policy_rule_tos(
579 const char *unit,
580 const char *filename,
581 unsigned line,
582 const char *section,
583 unsigned section_line,
584 const char *lvalue,
585 int ltype,
586 const char *rvalue,
587 void *data,
588 void *userdata) {
589
8e766630 590 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
bce67bbe
SS
591 Network *network = userdata;
592 int r;
593
594 assert(filename);
595 assert(section);
596 assert(lvalue);
597 assert(rvalue);
598 assert(data);
599
600 r = routing_policy_rule_new_static(network, filename, section_line, &n);
601 if (r < 0)
602 return r;
603
604 r = safe_atou8(rvalue, &n->tos);
605 if (r < 0) {
606 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule tos, ignoring: %s", rvalue);
607 return 0;
608 }
609
610 n = NULL;
611
612 return 0;
613}
614
615int config_parse_routing_policy_rule_priority(
616 const char *unit,
617 const char *filename,
618 unsigned line,
619 const char *section,
620 unsigned section_line,
621 const char *lvalue,
622 int ltype,
623 const char *rvalue,
624 void *data,
625 void *userdata) {
626
8e766630 627 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
bce67bbe
SS
628 Network *network = userdata;
629 int r;
630
631 assert(filename);
632 assert(section);
633 assert(lvalue);
634 assert(rvalue);
635 assert(data);
636
637 r = routing_policy_rule_new_static(network, filename, section_line, &n);
638 if (r < 0)
639 return r;
640
641 r = safe_atou32(rvalue, &n->priority);
642 if (r < 0) {
643 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue);
644 return 0;
645 }
646
647 n = NULL;
648
649 return 0;
650}
651
652int config_parse_routing_policy_rule_table(
653 const char *unit,
654 const char *filename,
655 unsigned line,
656 const char *section,
657 unsigned section_line,
658 const char *lvalue,
659 int ltype,
660 const char *rvalue,
661 void *data,
662 void *userdata) {
663
8e766630 664 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
bce67bbe
SS
665 Network *network = userdata;
666 int r;
667
668 assert(filename);
669 assert(section);
670 assert(lvalue);
671 assert(rvalue);
672 assert(data);
673
674 r = routing_policy_rule_new_static(network, filename, section_line, &n);
675 if (r < 0)
676 return r;
677
678 r = safe_atou32(rvalue, &n->table);
679 if (r < 0) {
680 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule table, ignoring: %s", rvalue);
681 return 0;
682 }
683
684 n = NULL;
685
686 return 0;
687}
688
689int config_parse_routing_policy_rule_fwmark_mask(
690 const char *unit,
691 const char *filename,
692 unsigned line,
693 const char *section,
694 unsigned section_line,
695 const char *lvalue,
696 int ltype,
697 const char *rvalue,
698 void *data,
699 void *userdata) {
700
8e766630 701 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
bce67bbe
SS
702 Network *network = userdata;
703 int r;
704
705 assert(filename);
706 assert(section);
707 assert(lvalue);
708 assert(rvalue);
709 assert(data);
710
711 r = routing_policy_rule_new_static(network, filename, section_line, &n);
712 if (r < 0)
713 return r;
714
715 r = parse_fwmark_fwmask(rvalue, &n->fwmark, &n->fwmask);
716 if (r < 0) {
717 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue);
718 return 0;
719 }
720
721 n = NULL;
722
723 return 0;
724}
725
726int config_parse_routing_policy_rule_prefix(
727 const char *unit,
728 const char *filename,
729 unsigned line,
730 const char *section,
731 unsigned section_line,
732 const char *lvalue,
733 int ltype,
734 const char *rvalue,
735 void *data,
736 void *userdata) {
737
8e766630 738 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
bce67bbe
SS
739 Network *network = userdata;
740 union in_addr_union buffer;
741 uint8_t prefixlen;
742 int r;
743
744 assert(filename);
745 assert(section);
746 assert(lvalue);
747 assert(rvalue);
748 assert(data);
749
750 r = routing_policy_rule_new_static(network, filename, section_line, &n);
751 if (r < 0)
752 return r;
753
754 r = in_addr_prefix_from_string(rvalue, AF_INET, &buffer, &prefixlen);
755 if (r < 0) {
756 r = in_addr_prefix_from_string(rvalue, AF_INET6, &buffer, &prefixlen);
757 if (r < 0) {
758 log_syntax(unit, LOG_ERR, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue);
759 return 0;
760 }
761
762 n->family = AF_INET6;
763 } else
764 n->family = AF_INET;
765
766 if (streq(lvalue, "To")) {
767 n->to = buffer;
768 n->to_prefixlen = prefixlen;
769 } else {
770 n->from = buffer;
771 n->from_prefixlen = prefixlen;
772 }
773
774 n = NULL;
775
776 return 0;
777}
778
762e2659
SS
779int config_parse_routing_policy_rule_device(
780 const char *unit,
781 const char *filename,
782 unsigned line,
783 const char *section,
784 unsigned section_line,
785 const char *lvalue,
786 int ltype,
787 const char *rvalue,
788 void *data,
789 void *userdata) {
790
8e766630 791 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL;
762e2659
SS
792 Network *network = userdata;
793 int r;
794
795 assert(filename);
796 assert(section);
797 assert(lvalue);
798 assert(rvalue);
799 assert(data);
800
801 r = routing_policy_rule_new_static(network, filename, section_line, &n);
802 if (r < 0)
803 return r;
804
805 if (!ifname_valid(rvalue)) {
806 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s' interface name, ignoring: %s", lvalue, rvalue);
807 return 0;
808 }
809
810 if (streq(lvalue, "IncomingInterface")) {
811 r = free_and_strdup(&n->iif, rvalue);
812 if (r < 0)
813 return log_oom();
814 } else {
815 r = free_and_strdup(&n->oif, rvalue);
816 if (r < 0)
817 return log_oom();
818 }
819
820 n = NULL;
821
822 return 0;
823}
824
458d8ae3 825static int routing_policy_rule_read_full_file(const char *state_file, char **ret) {
ac097c84 826 _cleanup_free_ char *s = NULL;
bce67bbe
SS
827 size_t size;
828 int r;
829
830 assert(state_file);
831
832 r = read_full_file(state_file, &s, &size);
833 if (r == -ENOENT)
834 return -ENODATA;
835 if (r < 0)
836 return r;
837 if (size <= 0)
838 return -ENODATA;
839
ae2a15bc 840 *ret = TAKE_PTR(s);
bce67bbe
SS
841
842 return size;
843}
844
458d8ae3
ZJS
845int routing_policy_serialize_rules(Set *rules, FILE *f) {
846 RoutingPolicyRule *rule = NULL;
847 Iterator i;
848 int r;
849
850 assert(f);
851
852 SET_FOREACH(rule, rules, i) {
853 _cleanup_free_ char *from_str = NULL, *to_str = NULL;
854 bool space = false;
855
856 fputs("RULE=", f);
857
858 if (!in_addr_is_null(rule->family, &rule->from)) {
859 r = in_addr_to_string(rule->family, &rule->from, &from_str);
860 if (r < 0)
861 return r;
862
863 fprintf(f, "from=%s/%hhu",
864 from_str, rule->from_prefixlen);
865 space = true;
866 }
867
868 if (!in_addr_is_null(rule->family, &rule->to)) {
869 r = in_addr_to_string(rule->family, &rule->to, &to_str);
870 if (r < 0)
871 return r;
872
873 fprintf(f, "%sto=%s/%hhu",
874 space ? " " : "",
875 to_str, rule->to_prefixlen);
876 space = true;
877 }
878
879 if (rule->tos != 0) {
880 fprintf(f, "%stos=%hhu",
881 space ? " " : "",
882 rule->tos);
883 space = true;
884 }
885
886 if (rule->fwmark != 0) {
887 fprintf(f, "%sfwmark=%"PRIu32"/%"PRIu32,
888 space ? " " : "",
889 rule->fwmark, rule->fwmask);
890 space = true;
891 }
892
9491f55f
ZJS
893 if (rule->iif) {
894 fprintf(f, "%siif=%s",
895 space ? " " : "",
896 rule->iif);
897 space = true;
898 }
899
900 if (rule->oif) {
901 fprintf(f, "%soif=%s",
902 space ? " " : "",
903 rule->oif);
904 space = true;
905 }
906
458d8ae3
ZJS
907 fprintf(f, "%stable=%"PRIu32 "\n",
908 space ? " " : "",
909 rule->table);
910 }
911
912 return 0;
913}
914
915int routing_policy_load_rules(const char *state_file, Set **rules) {
bce67bbe
SS
916 _cleanup_strv_free_ char **l = NULL;
917 _cleanup_free_ char *data = NULL;
918 const char *p;
919 char **i;
920 int r;
921
458d8ae3
ZJS
922 assert(state_file);
923 assert(rules);
bce67bbe 924
458d8ae3 925 r = routing_policy_rule_read_full_file(state_file, &data);
bce67bbe
SS
926 if (r <= 0)
927 return r;
928
929 l = strv_split_newlines(data);
930 if (!l)
931 return -ENOMEM;
932
458d8ae3 933 r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
bce67bbe
SS
934 if (r < 0)
935 return r;
936
937 STRV_FOREACH(i, l) {
8e766630 938 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
bce67bbe
SS
939
940 p = startswith(*i, "RULE=");
941 if (!p)
942 continue;
943
bce67bbe
SS
944 r = routing_policy_rule_new(&rule);
945 if (r < 0)
946 return r;
947
948 for (;;) {
949 _cleanup_free_ char *word = NULL, *a = NULL, *b = NULL;
950 union in_addr_union buffer;
951 uint8_t prefixlen;
952
953 r = extract_first_word(&p, &word, NULL, 0);
954 if (r < 0)
955 return r;
956 if (r == 0)
957 break;
958
959 r = split_pair(word, "=", &a, &b);
960 if (r < 0)
961 continue;
962
963 if (STR_IN_SET(a, "from", "to")) {
964
965 r = in_addr_prefix_from_string(b, AF_INET, &buffer, &prefixlen);
966 if (r < 0) {
967 r = in_addr_prefix_from_string(b, AF_INET6, &buffer, &prefixlen);
968 if (r < 0) {
969 log_error_errno(r, "RPDB rule prefix is invalid, ignoring assignment: %s", b);
970 continue;
971 }
972
973 rule->family = AF_INET6;
974 } else
975 rule->family = AF_INET;
976
977 if (streq(a, "to")) {
978 rule->to = buffer;
979 rule->to_prefixlen = prefixlen;
980 } else {
981 rule->from = buffer;
982 rule->from_prefixlen = prefixlen;
983 }
984 } else if (streq(a, "tos")) {
985 r = safe_atou8(b, &rule->tos);
986 if (r < 0) {
987 log_error_errno(r, "Failed to parse RPDB rule tos, ignoring: %s", b);
988 continue;
989 }
990 } else if (streq(a, "table")) {
991 r = safe_atou32(b, &rule->table);
992 if (r < 0) {
993 log_error_errno(r, "Failed to parse RPDB rule table, ignoring: %s", b);
994 continue;
995 }
996 } else if (streq(a, "fwmark")) {
997
e4aca57d 998 r = parse_fwmark_fwmask(b, &rule->fwmark, &rule->fwmask);
bce67bbe
SS
999 if (r < 0) {
1000 log_error_errno(r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", a);
1001 continue;
1002 }
9491f55f 1003 } else if (streq(a, "iif")) {
762e2659 1004
93f9da6e 1005 if (free_and_strdup(&rule->iif, b) < 0)
762e2659 1006 return log_oom();
93f9da6e 1007
9491f55f 1008 } else if (streq(a, "oif")) {
762e2659 1009
93f9da6e 1010 if (free_and_strdup(&rule->oif, b) < 0)
762e2659 1011 return log_oom();
bce67bbe
SS
1012 }
1013 }
1014
458d8ae3 1015 r = set_put(*rules, rule);
bce67bbe
SS
1016 if (r < 0) {
1017 log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", p);
1018 continue;
1019 }
1020
1021 rule = NULL;
1022 }
1023
1024 return 0;
1025}
1026
1027void routing_policy_rule_purge(Manager *m, Link *link) {
1028 RoutingPolicyRule *rule, *existing;
1029 Iterator i;
1030 int r;
1031
1032 assert(m);
1033 assert(link);
1034
1035 SET_FOREACH(rule, m->rules_saved, i) {
1036 existing = set_get(m->rules_foreign, rule);
1037 if (existing) {
1038
1039 r = routing_policy_rule_remove(rule, link, routing_policy_rule_remove_handler);
1040 if (r < 0) {
1041 log_warning_errno(r, "Could not remove routing policy rules: %m");
1042 continue;
1043 }
1044
7715629e 1045 link->routing_policy_rule_remove_messages++;
bce67bbe
SS
1046 }
1047 }
1048}