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:
</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>
'networkd-dns.c',
'networkd-ipv4acd.c',
'networkd-ipv4ll.c',
- 'networkd-ipv6-proxy-ndp.c',
'networkd-ipv6ll.c',
'networkd-json.c',
'networkd-link-bus.c',
'networkd-manager-varlink.c',
'networkd-ndisc.c',
'networkd-neighbor.c',
+ 'networkd-neighbor-proxy.c',
'networkd-netlabel.c',
'networkd-network-bus.c',
'networkd-network.c',
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/* 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);
#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"
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__);
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;
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;
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;
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
#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"
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)
#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"
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);
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);
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 */
[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",
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,
#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"
}
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) {
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));
}
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
IPMasquerade=
IPv4LLRoute=
IPv4ProxyARP=
+IPv4ProxyARPAddress=
IPv4ProxyARPPrivateVLAN=
IPv6AcceptRA=
IPv6DuplicateAddressDetection=
--- /dev/null
+# 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
--- /dev/null
+# 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
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')