mirror of
https://github.com/systemd/systemd.git
synced 2026-08-05 15:40:26 +00:00
network: add IPv4ProxyARPAddress= and consolidate proxy ARP/NDP handling
This adds an IPv4 counterpart to `IPv6ProxyNDPAddress=` for adding manual entries to the kernel's IPv4 neighbour proxy table (check via `ip -4 neighbour show proxy dev <dev>`). systemd-networkd only exposed `IPv4ProxyARP=` for per-interface `proxy_arp` sysctl (automatic proxy ARP) with no way to manage manual entries from a .network file. To avoid duplicating the IPv6 proxy NDP code path, both families are now combined into a single new `networkd-neighbor-proxy` module. The IPv6 behaviour is preserved: `IPv6ProxyNDPAddress=` still implies `IPv6ProxyNDP=yes` unless `IPv6ProxyNDP=` is explicitly disabled and entries are still dropped if the kernel has no IPv6 support. The same rule is applied to `IPv4ProxyARPAddress=`. It implies `IPv4ProxyARP=yes` when the sysctl is not explicitly set and has no effect if `IPv4ProxyARP=` has been set to false. This keeps the user model symmetric and predictable across both families: a single per-address setting that turns on the matching per-interface sysctl automatically, while still letting system administrators opt out by setting the boolean explicitly to false. Note that the IPv4 manual NTF_PROXY entries installed here would actually function without `proxy_arp` (unlike IPv6, where `proxy_ndp` gates the manual entries); the implication is kept for symmetry with `IPv6ProxyNDPAddress=` and is now called out explicitly in the man page, together with the fact that enabling `proxy_arp` also activates interface-wide automatic proxy ARP for routed-toward addresses on connected subnets. Parser-time validation rejects addresses the kernel would refuse: the ANY/null address for both families, IPv4 and IPv6 multicast and the IPv4 limited broadcast 255.255.255.255. Signed-off-by: Aritra Basu <aritrbas+gh@cisco.com>
This commit is contained in:
9
NEWS
9
NEWS
@@ -47,6 +47,15 @@ CHANGES WITH 262:
|
||||
should be provisioned as far as possible without ever blocking on a
|
||||
prompt.
|
||||
|
||||
Changes in systemd-networkd:
|
||||
|
||||
* A new IPv4ProxyARPAddress= setting has been added to the [Network]
|
||||
section of .network files. It is the IPv4 counterpart to
|
||||
IPv6ProxyNDPAddress= and allows manual entries to be added to the
|
||||
kernel's IPv4 neighbor proxy table. The setting implies
|
||||
IPv4ProxyARP=yes but has no effect if IPv4ProxyARP= has been set to
|
||||
false. This mirrors the behavior of IPv6ProxyNDPAddress=.
|
||||
|
||||
CHANGES WITH 261:
|
||||
|
||||
Announcements of Future Feature Removals and Incompatible Changes:
|
||||
|
||||
@@ -1147,6 +1147,25 @@ DuplicateAddressDetection=none</programlisting></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>IPv4ProxyARPAddress=</varname></term>
|
||||
<listitem>
|
||||
<para>An IPv4 address, for which ARP requests will be proxied. This option may be specified
|
||||
more than once. <command>systemd-networkd</command> will add the
|
||||
<varname>IPv4ProxyARPAddress=</varname> entries to the kernel's IPv4 neighbor proxy table.
|
||||
When <varname>IPv4ProxyARP=</varname> is unset, this setting implies
|
||||
<varname>IPv4ProxyARP=yes</varname>: the per-interface <literal>proxy_arp</literal> sysctl
|
||||
is enabled automatically, which also activates automatic proxy ARP for every address the
|
||||
interface has a route toward on connected subnets, in addition to answering the listed
|
||||
entries. If <varname>IPv4ProxyARP=</varname> has been set to false, the
|
||||
<varname>IPv4ProxyARPAddress=</varname> entries listed here are discarded and not
|
||||
programmed into the kernel. This mirrors the behaviour of
|
||||
<varname>IPv6ProxyNDPAddress=</varname>/<varname>IPv6ProxyNDP=</varname>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v262"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>IPv6ProxyNDP=</varname></term>
|
||||
<listitem>
|
||||
|
||||
@@ -54,7 +54,6 @@ systemd_networkd_export_sources = files(
|
||||
'networkd-dns.c',
|
||||
'networkd-ipv4acd.c',
|
||||
'networkd-ipv4ll.c',
|
||||
'networkd-ipv6-proxy-ndp.c',
|
||||
'networkd-ipv6ll.c',
|
||||
'networkd-json.c',
|
||||
'networkd-link-bus.c',
|
||||
@@ -67,6 +66,7 @@ systemd_networkd_export_sources = files(
|
||||
'networkd-manager-varlink.c',
|
||||
'networkd-ndisc.c',
|
||||
'networkd-neighbor.c',
|
||||
'networkd-neighbor-proxy.c',
|
||||
'networkd-netlabel.c',
|
||||
'networkd-network-bus.c',
|
||||
'networkd-network.c',
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "sd-netlink.h"
|
||||
|
||||
#include "networkd-ipv6-proxy-ndp.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "set.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
void network_adjust_ipv6_proxy_ndp(Network *network) {
|
||||
assert(network);
|
||||
|
||||
if (set_isempty(network->ipv6_proxy_ndp_addresses))
|
||||
return;
|
||||
|
||||
if (!socket_ipv6_is_supported()) {
|
||||
log_once(LOG_WARNING,
|
||||
"%s: IPv6 proxy NDP addresses are set, but IPv6 is not supported by kernel, "
|
||||
"Ignoring IPv6 proxy NDP addresses.", network->filename);
|
||||
network->ipv6_proxy_ndp_addresses = set_free(network->ipv6_proxy_ndp_addresses);
|
||||
return;
|
||||
}
|
||||
|
||||
if (network->ipv6_proxy_ndp == 0) {
|
||||
log_warning("%s: IPv6ProxyNDP= is disabled. Ignoring IPv6ProxyNDPAddress=.", network->filename);
|
||||
network->ipv6_proxy_ndp_addresses = set_free(network->ipv6_proxy_ndp_addresses);
|
||||
}
|
||||
}
|
||||
|
||||
static int ipv6_proxy_ndp_address_configure_handler(
|
||||
sd_netlink *rtnl,
|
||||
sd_netlink_message *m,
|
||||
Request *req,
|
||||
Link *link,
|
||||
struct in6_addr *address) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0)
|
||||
log_link_message_warning_errno(link, m, r, "Could not add IPv6 proxy ndp address entry, ignoring");
|
||||
|
||||
if (link->static_ipv6_proxy_ndp_messages == 0) {
|
||||
log_link_debug(link, "IPv6 proxy NDP addresses set.");
|
||||
link->static_ipv6_proxy_ndp_configured = true;
|
||||
link_check_ready(link);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* send a request to the kernel to add an IPv6 Proxy entry to the neighbour table */
|
||||
static int ipv6_proxy_ndp_address_configure(const struct in6_addr *address, Link *link, Request *req) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(address);
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->manager->rtnl);
|
||||
assert(req);
|
||||
|
||||
/* create new netlink message */
|
||||
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, link->ifindex, AF_INET6);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_rtnl_message_neigh_set_flags(m, NTF_PROXY);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_in6_addr(m, NDA_DST, address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return request_call_netlink_async(link->manager->rtnl, m, req);
|
||||
}
|
||||
|
||||
static int ipv6_proxy_ndp_address_process_request(Request *req, Link *link, struct in6_addr *address) {
|
||||
int r;
|
||||
|
||||
assert(req);
|
||||
assert(link);
|
||||
assert(address);
|
||||
|
||||
if (!link_is_ready_to_configure(link, false))
|
||||
return 0;
|
||||
|
||||
r = ipv6_proxy_ndp_address_configure(address, link, req);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to configure IPv6 proxy NDP address: %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int link_request_static_ipv6_proxy_ndp_addresses(Link *link) {
|
||||
struct in6_addr *address;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
link->static_ipv6_proxy_ndp_configured = false;
|
||||
|
||||
SET_FOREACH(address, link->network->ipv6_proxy_ndp_addresses) {
|
||||
r = link_queue_request_safe(link, REQUEST_TYPE_IPV6_PROXY_NDP,
|
||||
address, NULL,
|
||||
in6_addr_hash_func,
|
||||
in6_addr_compare_func,
|
||||
ipv6_proxy_ndp_address_process_request,
|
||||
&link->static_ipv6_proxy_ndp_messages,
|
||||
ipv6_proxy_ndp_address_configure_handler,
|
||||
NULL);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to request IPv6 proxy NDP address: %m");
|
||||
}
|
||||
|
||||
if (link->static_ipv6_proxy_ndp_messages == 0) {
|
||||
link->static_ipv6_proxy_ndp_configured = true;
|
||||
link_check_ready(link);
|
||||
} else {
|
||||
log_link_debug(link, "Setting IPv6 proxy NDP addresses.");
|
||||
link_set_state(link, LINK_STATE_CONFIGURING);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_ipv6_proxy_ndp_address(
|
||||
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_free_ struct in6_addr *address = NULL;
|
||||
Network *network = ASSERT_PTR(userdata);
|
||||
union in_addr_union buffer;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
network->ipv6_proxy_ndp_addresses = set_free(network->ipv6_proxy_ndp_addresses);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = in_addr_from_string(AF_INET6, rvalue, &buffer);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to parse IPv6 proxy NDP address, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (in_addr_is_null(AF_INET6, &buffer)) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"IPv6 proxy NDP address cannot be the ANY address, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
address = newdup(struct in6_addr, &buffer.in6, 1);
|
||||
if (!address)
|
||||
return log_oom();
|
||||
|
||||
r = set_ensure_consume(&network->ipv6_proxy_ndp_addresses, &in6_addr_hash_ops_free, TAKE_PTR(address));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "networkd-forward.h"
|
||||
|
||||
void network_adjust_ipv6_proxy_ndp(Network *network);
|
||||
|
||||
int link_request_static_ipv6_proxy_ndp_addresses(Link *link);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_proxy_ndp_address);
|
||||
@@ -47,13 +47,13 @@
|
||||
#include "networkd-dhcp6.h"
|
||||
#include "networkd-ipv4acd.h"
|
||||
#include "networkd-ipv4ll.h"
|
||||
#include "networkd-ipv6-proxy-ndp.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-link-bus.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-neighbor.h"
|
||||
#include "networkd-neighbor-proxy.h"
|
||||
#include "networkd-nexthop.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-radv.h"
|
||||
@@ -523,12 +523,12 @@ void link_check_ready(Link *link) {
|
||||
if (!link->static_bridge_mdb_configured)
|
||||
return (void) log_link_debug(link, "%s(): static bridge MDB entries are not configured.", __func__);
|
||||
|
||||
if (!link->static_ipv6_proxy_ndp_configured)
|
||||
return (void) log_link_debug(link, "%s(): static IPv6 proxy NDP addresses are not configured.", __func__);
|
||||
|
||||
if (!link->static_neighbors_configured)
|
||||
return (void) log_link_debug(link, "%s(): static neighbors are not configured.", __func__);
|
||||
|
||||
if (!link->static_neighbor_proxy_configured)
|
||||
return (void) log_link_debug(link, "%s(): static neighbor proxy addresses are not configured.", __func__);
|
||||
|
||||
if (!link->static_nexthops_configured)
|
||||
return (void) log_link_debug(link, "%s(): static nexthops are not configured.", __func__);
|
||||
|
||||
@@ -649,11 +649,11 @@ static int link_request_static_configs(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_request_static_ipv6_proxy_ndp_addresses(link);
|
||||
r = link_request_static_neighbors(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_request_static_neighbors(link);
|
||||
r = link_request_static_neighbor_proxy_addresses(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ typedef struct Link {
|
||||
unsigned static_address_label_messages;
|
||||
unsigned static_bridge_fdb_messages;
|
||||
unsigned static_bridge_mdb_messages;
|
||||
unsigned static_ipv6_proxy_ndp_messages;
|
||||
unsigned static_neighbor_messages;
|
||||
unsigned static_neighbor_proxy_messages;
|
||||
unsigned static_nexthop_messages;
|
||||
unsigned static_route_messages;
|
||||
unsigned static_routing_policy_rule_messages;
|
||||
@@ -130,8 +130,8 @@ typedef struct Link {
|
||||
bool static_address_labels_configured:1;
|
||||
bool static_bridge_fdb_configured:1;
|
||||
bool static_bridge_mdb_configured:1;
|
||||
bool static_ipv6_proxy_ndp_configured:1;
|
||||
bool static_neighbors_configured:1;
|
||||
bool static_neighbor_proxy_configured:1;
|
||||
bool static_nexthops_configured:1;
|
||||
bool static_routes_configured:1;
|
||||
bool static_routing_policy_rules_configured:1;
|
||||
|
||||
253
src/network/networkd-neighbor-proxy.c
Normal file
253
src/network/networkd-neighbor-proxy.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "sd-netlink.h"
|
||||
|
||||
#include "in-addr-util.h"
|
||||
#include "netlink-util.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-neighbor-proxy.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "set.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
bool network_has_neighbor_proxy_address(const Network *network, int family) {
|
||||
struct in_addr_data *a;
|
||||
|
||||
assert(network);
|
||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||
|
||||
SET_FOREACH(a, network->neighbor_proxy_addresses)
|
||||
if (a->family == family)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void network_drop_neighbor_proxy_addresses(Network *network, int family) {
|
||||
struct in_addr_data *a;
|
||||
|
||||
assert(network);
|
||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||
|
||||
SET_FOREACH(a, network->neighbor_proxy_addresses)
|
||||
if (a->family == family)
|
||||
free(set_remove(network->neighbor_proxy_addresses, a));
|
||||
|
||||
if (set_isempty(network->neighbor_proxy_addresses))
|
||||
network->neighbor_proxy_addresses = set_free(network->neighbor_proxy_addresses);
|
||||
}
|
||||
|
||||
void network_adjust_neighbor_proxy(Network *network) {
|
||||
assert(network);
|
||||
|
||||
if (set_isempty(network->neighbor_proxy_addresses))
|
||||
return;
|
||||
|
||||
/* If IPv6 is not supported by the kernel, drop any IPv6 entries up front. */
|
||||
if (!socket_ipv6_is_supported() &&
|
||||
network_has_neighbor_proxy_address(network, AF_INET6)) {
|
||||
log_once(LOG_WARNING,
|
||||
"%s: IPv6 proxy NDP addresses are set, but IPv6 is not supported by kernel, "
|
||||
"ignoring IPv6 proxy NDP addresses.", network->filename);
|
||||
network_drop_neighbor_proxy_addresses(network, AF_INET6);
|
||||
}
|
||||
|
||||
/* Drop per-family entries when the corresponding proxy sysctl was explicitly disabled.
|
||||
* For IPv6 the proxy_ndp sysctl is required for manual entries to take effect; for IPv4 we
|
||||
* apply the same rule for consistency so that an explicit IPv4ProxyARP=no is respected. */
|
||||
int family;
|
||||
FOREACH_ARGUMENT(family, AF_INET, AF_INET6) {
|
||||
int tristate = family == AF_INET ? network->proxy_arp : network->ipv6_proxy_ndp;
|
||||
|
||||
if (tristate == 0 && network_has_neighbor_proxy_address(network, family)) {
|
||||
log_warning("%s: %s is disabled. Ignoring %s.",
|
||||
network->filename,
|
||||
family == AF_INET ? "IPv4ProxyARP=" : "IPv6ProxyNDP=",
|
||||
family == AF_INET ? "IPv4ProxyARPAddress=" : "IPv6ProxyNDPAddress=");
|
||||
network_drop_neighbor_proxy_addresses(network, family);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int neighbor_proxy_address_configure_handler(
|
||||
sd_netlink *rtnl,
|
||||
sd_netlink_message *m,
|
||||
Request *req,
|
||||
Link *link,
|
||||
struct in_addr_data *address) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0)
|
||||
log_link_message_warning_errno(link, m, r,
|
||||
"Could not add neighbor proxy address entry, ignoring");
|
||||
|
||||
if (link->static_neighbor_proxy_messages == 0) {
|
||||
log_link_debug(link, "Neighbor proxy addresses set.");
|
||||
link->static_neighbor_proxy_configured = true;
|
||||
link_check_ready(link);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Send a request to the kernel to add a proxy entry to the neighbour table. */
|
||||
static int neighbor_proxy_address_configure(const struct in_addr_data *address, Link *link, Request *req) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
assert(address);
|
||||
assert(IN_SET(address->family, AF_INET, AF_INET6));
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->manager->rtnl);
|
||||
assert(req);
|
||||
|
||||
/* create new netlink message */
|
||||
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, link->ifindex, address->family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_rtnl_message_neigh_set_flags(m, NTF_PROXY);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = netlink_message_append_in_addr_union(m, NDA_DST, address->family, &address->address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return request_call_netlink_async(link->manager->rtnl, m, req);
|
||||
}
|
||||
|
||||
static int neighbor_proxy_address_process_request(Request *req, Link *link, struct in_addr_data *address) {
|
||||
int r;
|
||||
|
||||
assert(req);
|
||||
assert(link);
|
||||
assert(address);
|
||||
|
||||
if (!link_is_ready_to_configure(link, false))
|
||||
return 0;
|
||||
|
||||
r = neighbor_proxy_address_configure(address, link, req);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to configure neighbor proxy address: %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int link_request_static_neighbor_proxy_addresses(Link *link) {
|
||||
struct in_addr_data *address;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
link->static_neighbor_proxy_configured = false;
|
||||
|
||||
SET_FOREACH(address, link->network->neighbor_proxy_addresses) {
|
||||
r = link_queue_request_safe(link, REQUEST_TYPE_NEIGHBOR_PROXY,
|
||||
address, NULL,
|
||||
in_addr_data_hash_func,
|
||||
in_addr_data_compare_func,
|
||||
neighbor_proxy_address_process_request,
|
||||
&link->static_neighbor_proxy_messages,
|
||||
neighbor_proxy_address_configure_handler,
|
||||
NULL);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to request neighbor proxy address: %m");
|
||||
}
|
||||
|
||||
if (link->static_neighbor_proxy_messages == 0) {
|
||||
link->static_neighbor_proxy_configured = true;
|
||||
link_check_ready(link);
|
||||
} else {
|
||||
log_link_debug(link, "Setting neighbor proxy addresses.");
|
||||
link_set_state(link, LINK_STATE_CONFIGURING);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_neighbor_proxy_address(
|
||||
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_free_ struct in_addr_data *address = NULL;
|
||||
Network *network = ASSERT_PTR(userdata);
|
||||
int family = ltype;
|
||||
union in_addr_union buffer = {};
|
||||
int r;
|
||||
|
||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
/* Drop only entries belonging to this family, so that
|
||||
* IPv4ProxyARPAddress= and IPv6ProxyNDPAddress= can be reset independently. */
|
||||
network_drop_neighbor_proxy_addresses(network, family);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = in_addr_from_string(family, rvalue, &buffer);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to parse %s, ignoring: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (in_addr_is_null(family, &buffer)) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"%s cannot be the ANY address, ignoring: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reject address classes that do not qualify as proxy targets and that the kernel would
|
||||
* reject: multicast for both families, plus the IPv4 limited broadcast 255.255.255.255. */
|
||||
if (in_addr_is_multicast(family, &buffer) > 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"%s cannot be a multicast address, ignoring: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (family == AF_INET && buffer.in.s_addr == htobe32(INADDR_BROADCAST)) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"%s cannot be the limited broadcast address, ignoring: %s",
|
||||
lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
address = new(struct in_addr_data, 1);
|
||||
if (!address)
|
||||
return log_oom();
|
||||
|
||||
*address = (struct in_addr_data) {
|
||||
.family = family,
|
||||
.address = buffer,
|
||||
};
|
||||
|
||||
r = set_ensure_consume(&network->neighbor_proxy_addresses, &in_addr_data_hash_ops_free, TAKE_PTR(address));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
src/network/networkd-neighbor-proxy.h
Normal file
12
src/network/networkd-neighbor-proxy.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "networkd-forward.h"
|
||||
|
||||
bool network_has_neighbor_proxy_address(const Network *network, int family);
|
||||
|
||||
void network_adjust_neighbor_proxy(Network *network);
|
||||
|
||||
int link_request_static_neighbor_proxy_addresses(Link *link);
|
||||
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_proxy_address);
|
||||
@@ -29,12 +29,12 @@ _Pragma("GCC diagnostic ignored \"-Wzero-as-null-pointer-constant\"")
|
||||
#include "networkd-dhcp6.h"
|
||||
#include "networkd-dns.h"
|
||||
#include "networkd-ipv4ll.h"
|
||||
#include "networkd-ipv6-proxy-ndp.h"
|
||||
#include "networkd-ipv6ll.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-neighbor.h"
|
||||
#include "networkd-neighbor-proxy.h"
|
||||
#include "networkd-nexthop.h"
|
||||
#include "networkd-ntp.h"
|
||||
#include "networkd-radv.h"
|
||||
@@ -176,7 +176,8 @@ Network.PrimarySlave, config_parse_bool,
|
||||
Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
|
||||
Network.IPv4ProxyARPPrivateVLAN, config_parse_tristate, 0, offsetof(Network, proxy_arp_pvlan)
|
||||
Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
|
||||
Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0
|
||||
Network.IPv4ProxyARPAddress, config_parse_neighbor_proxy_address, AF_INET, 0
|
||||
Network.IPv6ProxyNDPAddress, config_parse_neighbor_proxy_address, AF_INET6, 0
|
||||
Network.IPv4ReversePathFilter, config_parse_ip_reverse_path_filter, 0, offsetof(Network, ipv4_rp_filter)
|
||||
Network.MulticastIGMPVersion, config_parse_ipv4_force_igmp_version, 0, offsetof(Network, ipv4_force_igmp_version)
|
||||
Network.MPLSRouting, config_parse_tristate, 0, offsetof(Network, mpls_input)
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
#include "networkd-bridge-mdb.h"
|
||||
#include "networkd-dhcp-common.h"
|
||||
#include "networkd-dhcp-server-static-lease.h"
|
||||
#include "networkd-ipv6-proxy-ndp.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-neighbor.h"
|
||||
#include "networkd-neighbor-proxy.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-nexthop.h"
|
||||
#include "networkd-radv.h"
|
||||
@@ -230,7 +230,7 @@ int network_verify(Network *network) {
|
||||
network->ipv6ll_address_gen_mode < 0)
|
||||
network->ipv6ll_address_gen_mode = IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY;
|
||||
|
||||
network_adjust_ipv6_proxy_ndp(network);
|
||||
network_adjust_neighbor_proxy(network);
|
||||
network_adjust_ndisc(network);
|
||||
network_adjust_dhcp(network);
|
||||
network_adjust_radv(network);
|
||||
@@ -850,7 +850,7 @@ static Network *network_free(Network *network) {
|
||||
hashmap_free(network->stacked_netdevs);
|
||||
|
||||
/* static configs */
|
||||
set_free(network->ipv6_proxy_ndp_addresses);
|
||||
set_free(network->neighbor_proxy_addresses);
|
||||
ordered_hashmap_free(network->addresses_by_section);
|
||||
hashmap_free(network->routes_by_section);
|
||||
ordered_hashmap_free(network->nexthops_by_section);
|
||||
|
||||
@@ -355,7 +355,7 @@ typedef struct Network {
|
||||
IPReversePathFilter ipv4_rp_filter;
|
||||
IPv4ForceIgmpVersion ipv4_force_igmp_version;
|
||||
int ipv6_proxy_ndp;
|
||||
Set *ipv6_proxy_ndp_addresses;
|
||||
Set *neighbor_proxy_addresses;
|
||||
int mpls_input;
|
||||
|
||||
/* NDisc support */
|
||||
|
||||
@@ -369,9 +369,9 @@ static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
|
||||
[REQUEST_TYPE_DHCP_SERVER] = "DHCP server",
|
||||
[REQUEST_TYPE_DHCP4_CLIENT] = "DHCPv4 client",
|
||||
[REQUEST_TYPE_DHCP6_CLIENT] = "DHCPv6 client",
|
||||
[REQUEST_TYPE_IPV6_PROXY_NDP] = "IPv6 proxy NDP",
|
||||
[REQUEST_TYPE_NDISC] = "NDisc",
|
||||
[REQUEST_TYPE_NEIGHBOR] = "neighbor",
|
||||
[REQUEST_TYPE_NEIGHBOR_PROXY] = "neighbor proxy",
|
||||
[REQUEST_TYPE_NETDEV_INDEPENDENT] = "independent netdev",
|
||||
[REQUEST_TYPE_NETDEV_STACKED] = "stacked netdev",
|
||||
[REQUEST_TYPE_NEXTHOP] = "nexthop",
|
||||
|
||||
@@ -17,9 +17,9 @@ typedef enum RequestType {
|
||||
REQUEST_TYPE_DHCP_SERVER,
|
||||
REQUEST_TYPE_DHCP4_CLIENT,
|
||||
REQUEST_TYPE_DHCP6_CLIENT,
|
||||
REQUEST_TYPE_IPV6_PROXY_NDP,
|
||||
REQUEST_TYPE_NDISC,
|
||||
REQUEST_TYPE_NEIGHBOR,
|
||||
REQUEST_TYPE_NEIGHBOR_PROXY,
|
||||
REQUEST_TYPE_NETDEV_INDEPENDENT,
|
||||
REQUEST_TYPE_NETDEV_STACKED,
|
||||
REQUEST_TYPE_NEXTHOP,
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-neighbor-proxy.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-sysctl.h"
|
||||
#include "path-util.h"
|
||||
#include "set.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
@@ -270,16 +270,25 @@ static int link_update_ipv6_sysctl(Link *link) {
|
||||
}
|
||||
|
||||
static int link_set_proxy_arp(Link *link) {
|
||||
bool v;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
|
||||
if (!link_is_configured_for_family(link, AF_INET))
|
||||
return 0;
|
||||
|
||||
if (link->network->proxy_arp < 0)
|
||||
if (link->network->proxy_arp >= 0)
|
||||
v = link->network->proxy_arp;
|
||||
else if (network_has_neighbor_proxy_address(link->network, AF_INET))
|
||||
/* If IPv4ProxyARP= is not explicitly set, but per-address IPv4ProxyARPAddress=
|
||||
* entries are configured, implicitly enable the proxy_arp sysctl. This matches
|
||||
* the behavior of IPv6ProxyNDPAddress= which implies IPv6ProxyNDP=yes. */
|
||||
v = true;
|
||||
else
|
||||
return 0;
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0, manager_get_sysctl_shadow(link->manager));
|
||||
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", v, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
static int link_set_proxy_arp_pvlan(Link *link) {
|
||||
@@ -513,7 +522,7 @@ static int link_set_ipv6_proxy_ndp(Link *link) {
|
||||
if (link->network->ipv6_proxy_ndp >= 0)
|
||||
v = link->network->ipv6_proxy_ndp;
|
||||
else
|
||||
v = !set_isempty(link->network->ipv6_proxy_ndp_addresses);
|
||||
v = network_has_neighbor_proxy_address(link->network, AF_INET6);
|
||||
|
||||
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v, manager_get_sysctl_shadow(link->manager));
|
||||
}
|
||||
|
||||
@@ -7,5 +7,15 @@ IPv6PrivacyExtensions=true
|
||||
IPv6DuplicateAddressDetection=3
|
||||
IPv6HopLimit=5
|
||||
IPv4ProxyARP=true
|
||||
IPv4ProxyARPAddress=192.0.2.1
|
||||
IPv4ProxyARPAddress=192.0.2.2
|
||||
IPv4ProxyARPAddress=0.0.0.0
|
||||
IPv4ProxyARPAddress=224.0.0.1
|
||||
IPv4ProxyARPAddress=255.255.255.255
|
||||
IPv4ProxyARPPrivateVLAN=true
|
||||
IPv6ProxyNDP=true
|
||||
IPv6ProxyNDPAddress=2001:db8::1
|
||||
IPv6ProxyNDPAddress=2001:db8::2
|
||||
IPv6ProxyNDPAddress=::
|
||||
IPv6ProxyNDPAddress=ff02::1
|
||||
IPv6ProxyNDPAddress=fe80::1
|
||||
|
||||
@@ -478,6 +478,7 @@ IPForward=
|
||||
IPMasquerade=
|
||||
IPv4LLRoute=
|
||||
IPv4ProxyARP=
|
||||
IPv4ProxyARPAddress=
|
||||
IPv4ProxyARPPrivateVLAN=
|
||||
IPv6AcceptRA=
|
||||
IPv6DuplicateAddressDetection=
|
||||
|
||||
10
test/test-network/conf/25-ipv4-proxy-arp-disabled.network
Normal file
10
test/test-network/conf/25-ipv4-proxy-arp-disabled.network
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=dummy98
|
||||
|
||||
[Network]
|
||||
IPv4ProxyARP=no
|
||||
IPv4ProxyARPAddress=192.0.2.1
|
||||
IPv4ProxyARPAddress=192.0.2.2
|
||||
IPv6AcceptRA=no
|
||||
Address=10.0.0.1/24
|
||||
12
test/test-network/conf/25-ipv4-proxy-arp.network
Normal file
12
test/test-network/conf/25-ipv4-proxy-arp.network
Normal file
@@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=dummy98
|
||||
|
||||
[Network]
|
||||
IPv4ProxyARPAddress=192.0.2.1
|
||||
IPv4ProxyARPAddress=192.0.2.2
|
||||
IPv4ProxyARPAddress=192.0.2.3
|
||||
IPv4ProxyARPAddress=192.0.2.4
|
||||
IPv4ProxyARPAddress=192.0.2.5
|
||||
IPv6AcceptRA=no
|
||||
Address=10.0.0.1/24
|
||||
@@ -5344,6 +5344,37 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
||||
for i in range(1, 5):
|
||||
self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy')
|
||||
|
||||
def test_ipv4_proxy_arp(self):
|
||||
copy_network_unit('25-ipv4-proxy-arp.network', '12-dummy.netdev')
|
||||
start_networkd()
|
||||
|
||||
self.wait_online('dummy98:routable')
|
||||
|
||||
output = check_output('ip -4 neighbor show proxy dev dummy98')
|
||||
print(output)
|
||||
for i in range(1, 6):
|
||||
self.assertRegex(output, f'192.0.2.{i} *proxy')
|
||||
|
||||
# IPv4ProxyARPAddress= implies IPv4ProxyARP=yes, mirroring IPv6ProxyNDPAddress=.
|
||||
self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
|
||||
|
||||
# Explicit IPv4ProxyARP=no must suppress all IPv4ProxyARPAddress= entries and
|
||||
# must not force proxy_arp=1. The module is add-only (no reconcile/remove pass),
|
||||
# so phase 2 starts from a clean interface: stop networkd, delete the dummy to
|
||||
# flush the kernel neighbor-proxy table, swap the .network file, and restart.
|
||||
stop_networkd()
|
||||
remove_link('dummy98')
|
||||
remove_network_unit('25-ipv4-proxy-arp.network')
|
||||
copy_network_unit('25-ipv4-proxy-arp-disabled.network', '12-dummy.netdev')
|
||||
start_networkd()
|
||||
self.wait_online('dummy98:routable')
|
||||
|
||||
output = check_output('ip -4 neighbor show proxy dev dummy98')
|
||||
print(output)
|
||||
for i in range(1, 6):
|
||||
self.assertNotIn(f'192.0.2.{i}', output)
|
||||
self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '0')
|
||||
|
||||
def test_ipv6_neigh_retrans_time(self):
|
||||
link = 'test25'
|
||||
copy_network_unit('25-dummy.netdev', '25-dummy.network')
|
||||
|
||||
Reference in New Issue
Block a user