diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 43324b8898..57f27e6f37 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -926,6 +926,18 @@ integer. Higher number means lower priority, and rules get processed in order of increasing number. + + IncomingInterface= + + Specifies incoming device to match. If the interface is loopback, the rule only matches packets originating from this host. + + + + OutgoingInterface= + + Specifies the outgoing device to match. The outgoing interface is only available for packets originating from local sockets that are bound to a device. + + diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index b8426070de..3fb7703759 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -784,7 +784,7 @@ static int link_set_routing_policy_rule(Link *link) { LIST_FOREACH(rules, rule, link->network->rules) { r = routing_policy_rule_get(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to, - rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, &rrule); + rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, &rrule); if (r == 1) { (void) routing_policy_rule_make_local(link->manager, rrule); continue; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index fc9ef74016..780f17392e 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -732,6 +732,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi union in_addr_union to, from; uint32_t fwmark = 0, table = 0; Manager *m = userdata; + char *iif, *oif; uint16_t type; int family; int r; @@ -811,13 +812,15 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi (void) sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark); (void) sd_netlink_message_read_u32(message, FRA_TABLE, &table); (void) sd_rtnl_message_routing_policy_rule_get_tos(message, &tos); + (void) sd_netlink_message_read_string(message, FRA_IIFNAME, (const char **) &iif); + (void) sd_netlink_message_read_string(message, FRA_OIFNAME, (const char **) &oif); - (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, &rule); + (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule); switch (type) { case RTM_NEWRULE: if(!rule) { - r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, &rule); + r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule); if (r < 0) { log_warning_errno(r, "Could not add rule: %m"); return 0; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index ca0d67e3d2..2040f82b9f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -90,6 +90,8 @@ RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, RoutingPolicyRule.FirewallMark, config_parse_routing_policy_rule_fwmark_mask, 0, 0 RoutingPolicyRule.From, config_parse_routing_policy_rule_prefix, 0, 0 RoutingPolicyRule.To, config_parse_routing_policy_rule_prefix, 0, 0 +RoutingPolicyRule.IncomingInterface, config_parse_routing_policy_rule_device, 0, 0 +RoutingPolicyRule.OutgoingInterface, config_parse_routing_policy_rule_device, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 Route.Source, config_parse_destination, 0, 0 diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index a2e1e15851..221ab4cb85 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -66,6 +66,8 @@ void routing_policy_rule_free(RoutingPolicyRule *rule) { } } + free(rule->iif); + free(rule->oif); free(rule); } @@ -90,6 +92,12 @@ static void routing_policy_rule_hash_func(const void *b, struct siphash *state) siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state); siphash24_compress(&rule->table, sizeof(rule->table), state); + if (rule->iif) + siphash24_compress(&rule->iif, strlen(rule->iif), state); + + if (rule->oif) + siphash24_compress(&rule->oif, strlen(rule->oif), state); + break; default: /* treat any other address family as AF_UNSPEC */ @@ -134,6 +142,14 @@ static int routing_policy_rule_compare_func(const void *_a, const void *_b) { if (a->table > b->table) return 1; + r = strcmp_ptr(a->iif, b->iif); + if (!r) + return r; + + r = strcmp_ptr(a->oif, b->oif); + if (!r) + return r; + r = memcmp(&a->from, &b->from, FAMILY_ADDRESS_SIZE(a->family)); if (r != 0) return r; @@ -160,6 +176,8 @@ int routing_policy_rule_get(Manager *m, uint8_t tos, uint32_t fwmark, uint32_t table, + char *iif, + char *oif, RoutingPolicyRule **ret) { RoutingPolicyRule rule, *existing; @@ -175,6 +193,8 @@ int routing_policy_rule_get(Manager *m, .tos = tos, .fwmark = fwmark, .table = table, + .iif = iif, + .oif = oif }; if (m->rules) { @@ -225,6 +245,8 @@ static int routing_policy_rule_add_internal(Set **rules, uint8_t tos, uint32_t fwmark, uint32_t table, + char *iif, + char *oif, RoutingPolicyRule **ret) { _cleanup_routing_policy_rule_free_ RoutingPolicyRule *rule = NULL; @@ -244,6 +266,8 @@ static int routing_policy_rule_add_internal(Set **rules, rule->tos = tos; rule->fwmark = fwmark; rule->table = table; + rule->iif = iif; + rule->oif = oif; r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops); if (r < 0) @@ -270,9 +294,11 @@ int routing_policy_rule_add(Manager *m, uint8_t tos, uint32_t fwmark, uint32_t table, + char *iif, + char *oif, RoutingPolicyRule **ret) { - return routing_policy_rule_add_internal(&m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, ret); + return routing_policy_rule_add_internal(&m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret); } int routing_policy_rule_add_foreign(Manager *m, @@ -284,8 +310,10 @@ int routing_policy_rule_add_foreign(Manager *m, uint8_t tos, uint32_t fwmark, uint32_t table, + char *iif, + char *oif, RoutingPolicyRule **ret) { - return routing_policy_rule_add_internal(&m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, ret); + return routing_policy_rule_add_internal(&m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret); } static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { @@ -505,6 +533,18 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlin return log_error_errno(r, "Could not append FRA_FWMASK attribute: %m"); } + if (rule->iif) { + r = sd_netlink_message_append_string(m, FRA_IFNAME, rule->iif); + if (r < 0) + return log_error_errno(r, "Could not append FRA_IFNAME attribute: %m"); + } + + if (rule->oif) { + r = sd_netlink_message_append_string(m, FRA_OIFNAME, rule->oif); + if (r < 0) + return log_error_errno(r, "Could not append FRA_OIFNAME attribute: %m"); + } + rule->link = link; r = sd_netlink_call_async(link->manager->rtnl, m, callback, link, 0, NULL); @@ -514,7 +554,7 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, sd_netlin link_ref(link); r = routing_policy_rule_add(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to, - rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, NULL); + rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, NULL); if (r < 0) return log_error_errno(r, "Could not add rule : %m"); @@ -750,6 +790,52 @@ int config_parse_routing_policy_rule_prefix( return 0; } +int config_parse_routing_policy_rule_device( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_routing_policy_rule_free_ RoutingPolicyRule *n = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = routing_policy_rule_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s' interface name, ignoring: %s", lvalue, rvalue); + return 0; + } + + if (streq(lvalue, "IncomingInterface")) { + r = free_and_strdup(&n->iif, rvalue); + if (r < 0) + return log_oom(); + } else { + r = free_and_strdup(&n->oif, rvalue); + if (r < 0) + return log_oom(); + } + + n = NULL; + + return 0; +} + static int routing_policy_rule_read_full_file(char *state_file, char **ret) { _cleanup_free_ char *s = NULL; size_t size; @@ -861,6 +947,16 @@ int routing_policy_rule_load(Manager *m) { log_error_errno(r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", a); continue; } + } else if (streq(a, "IncomingInterface")) { + + rule->iif = strdup(a); + if (!rule->iif) + return log_oom(); + } else if (streq(a, "OutgoingInterface")) { + + rule->oif = strdup(a); + if (!rule->oif) + return log_oom(); } } diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index 8a019ba1d2..01a7107366 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -50,6 +50,9 @@ struct RoutingPolicyRule { unsigned char to_prefixlen; unsigned char from_prefixlen; + char *iif; + char *oif; + union in_addr_union to; union in_addr_union from; @@ -68,11 +71,11 @@ int link_routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message int link_routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); int routing_policy_rule_add(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, - uint8_t tos, uint32_t fwmark, uint32_t table, RoutingPolicyRule **ret); + uint8_t tos, uint32_t fwmark, uint32_t table, char *iif, char *oif, RoutingPolicyRule **ret); int routing_policy_rule_add_foreign(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, - uint8_t tos, uint32_t fwmark, uint32_t table, RoutingPolicyRule **ret); + uint8_t tos, uint32_t fwmark, uint32_t table, char *iif, char *oif, RoutingPolicyRule **ret); int routing_policy_rule_get(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, uint8_t tos, - uint32_t fwmark, uint32_t table, RoutingPolicyRule **ret); + uint32_t fwmark, uint32_t table, char *iif, char *oif, RoutingPolicyRule **ret); int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule); int routing_policy_rule_load(Manager *m); void routing_policy_rule_purge(Manager *m, Link *link); @@ -82,3 +85,4 @@ int config_parse_routing_policy_rule_table(const char *unit, const char *filenam int config_parse_routing_policy_rule_fwmark_mask(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_routing_policy_rule_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_routing_policy_rule_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data,void *userdata); +int config_parse_routing_policy_rule_device(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data,void *userdata);