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