diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 959ce08314f..56c02e18dbb 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -776,6 +776,22 @@ Table=1234 + + IPv4ReversePathFilter= + + Configure IPv4 Reverse Path Filtering. If enabled, when an IPv4 packet is received, the machine will first check + whether the source of the packet would be routed through the interface it came in. If there is no + route to the source on that interface, the machine will drop the packet. Takes one of + no, strict, or loose. When no, + no source validation will be done. When strict, mode each incoming packet is tested against the FIB and + if the incoming interface is not the best reverse path, the packet check will fail. By default failed packets are discarded. + When loose, mode each incoming packet's source address is tested against the FIB. The packet is dropped + only if the source address is not reachable via any interface on that router. + See RFC 3704. + When unset, the kernel's default will be used. + + + IPv4AcceptLocal= diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index a5d328e0378..4049993beb4 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -140,6 +140,7 @@ Network.PrimarySlave, config_parse_bool, Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0 +Network.IPv4ReversePathFilter, config_parse_ip_reverse_path_filter, 0, offsetof(Network, ipv4_rp_filter) Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier) Network.IgnoreCarrierLoss, config_parse_ignore_carrier_loss, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index d4f4a9ed978..40148429851 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -474,6 +474,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .ipv6_hop_limit = -1, .ipv6_proxy_ndp = -1, .proxy_arp = -1, + .ipv4_rp_filter = _IP_REVERSE_PATH_FILTER_INVALID, .ipv6_accept_ra = -1, .ipv6_accept_ra_use_dns = true, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 886729cd5a1..0b9775f0148 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -307,6 +307,7 @@ struct Network { int proxy_arp; uint32_t ipv6_mtu; IPv6PrivacyExtensions ipv6_privacy_extensions; + IPReversePathFilter ipv4_rp_filter; int ipv6_proxy_ndp; Set *ipv6_proxy_ndp_addresses; diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c index 7c9a83ba579..0b8169a0176 100644 --- a/src/network/networkd-sysctl.c +++ b/src/network/networkd-sysctl.c @@ -89,6 +89,21 @@ static int link_set_ipv6_forward(Link *link) { return sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1"); } +static int link_set_ipv4_rp_filter(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + if (link->network->ipv4_rp_filter < 0) + return 0; + + return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter); +} + static int link_set_ipv6_privacy_extensions(Link *link) { IPv6PrivacyExtensions val; @@ -302,6 +317,10 @@ int link_set_sysctl(Link *link) { if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m"); + r = link_set_ipv4_rp_filter(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m"); + /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not * changes between leases. The kernel will remove all secondary IP addresses of an interface * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a @@ -325,3 +344,13 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExte IPV6_PRIVACY_EXTENSIONS_YES); DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_privacy_extensions, ipv6_privacy_extensions, IPv6PrivacyExtensions, "Failed to parse IPv6 privacy extensions option"); + +static const char* const ip_reverse_path_filter_table[_IP_REVERSE_PATH_FILTER_MAX] = { + [IP_REVERSE_PATH_FILTER_NO] = "no", + [IP_REVERSE_PATH_FILTER_STRICT] = "strict", + [IP_REVERSE_PATH_FILTER_LOOSE] = "loose", +}; + +DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter, IPReversePathFilter); +DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter, ip_reverse_path_filter, IPReversePathFilter, + "Failed to parse IP reverse path filter option"); diff --git a/src/network/networkd-sysctl.h b/src/network/networkd-sysctl.h index df5d6e08ffa..ba6915719a1 100644 --- a/src/network/networkd-sysctl.h +++ b/src/network/networkd-sysctl.h @@ -17,10 +17,22 @@ typedef enum IPv6PrivacyExtensions { _IPV6_PRIVACY_EXTENSIONS_INVALID = -EINVAL, } IPv6PrivacyExtensions; +typedef enum IPReversePathFilter { + IP_REVERSE_PATH_FILTER_NO, + IP_REVERSE_PATH_FILTER_STRICT, + IP_REVERSE_PATH_FILTER_LOOSE, + _IP_REVERSE_PATH_FILTER_MAX, + _IP_REVERSE_PATH_FILTER_INVALID = -EINVAL, +} IPReversePathFilter; + int link_set_sysctl(Link *link); int link_set_ipv6_mtu(Link *link); const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_; IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; +const char* ip_reverse_path_filter_to_string(IPReversePathFilter i) _const_; +IPReversePathFilter ip_reverse_path_filter_from_string(const char *s) _pure_; + CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions); +CONFIG_PARSER_PROTOTYPE(config_parse_ip_reverse_path_filter);