1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

networkd: add IPv6ProxyNDPAddress support (#5174)

IPv6 Neighbor discovery proxy is the IPv6 equivalent to proxy ARP for IPv4.
It is required when ISPs do not unconditional route IPv6 subnets
to their designated target, but expect neighbor solicitation messages
for every address on a link.

A variable IPv6ProxyNDPAddress= is introduced to the [Network] section,
each representing a IPv6 neighbour proxy entry in the neighbour table.
This commit is contained in:
Florian Klink 2017-02-11 00:47:55 +01:00 committed by Lennart Poettering
parent 680a752c83
commit a0e5c15d4f
8 changed files with 289 additions and 0 deletions

View File

@ -5756,6 +5756,8 @@ libnetworkd_core_la_SOURCES = \
src/network/networkd-link.c \
src/network/networkd-link-bus.c \
src/network/networkd-ipv4ll.c \
src/network/networkd-ipv6-proxy-ndp.h \
src/network/networkd-ipv6-proxy-ndp.c \
src/network/networkd-dhcp4.c \
src/network/networkd-dhcp6.c \
src/network/networkd-ndisc.h \

View File

@ -611,6 +611,25 @@
Defaults to unset.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv6ProxyNDPAddress=</varname></term>
<listitem><para>An IPv6 address, for which Neighbour Advertisement
messages will be proxied.
Proxy NDP (Neighbor Discovery Protocol) is a technique for IPv6 to
allow routing of addresses to a different destination when peers expect them
to be present on a certain physical link.
In this case a router answers Neighbour Advertisement messages intended for
another machine by offering its own MAC address as destination.
Unlike proxy ARP for IPv4, is not enabled globally, but will only send Neighbour
Advertisement messages for addresses in the IPv6 neighbor proxy table,
which can also be shown by <command>ip -6 neighbour show proxy</command>
This option may be specified more than once. systemd-networkd will control the
per-interface `proxy_ndp` switch for each configured interface, depending on whether
there are <option>IPv6ProxyNDPAddress=</option> entries configured and add these to
the kernels IPv6 neighbor proxy table.
Defaults to unset.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Bridge=</varname></term>
<listitem>

View File

@ -0,0 +1,209 @@
/***
This file is part of systemd.
Copyright 2017 Florian Klink <flokli@flokli.de>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <netinet/ether.h>
#include <linux/if.h>
#include <unistd.h>
#include "fileio.h"
#include "netlink-util.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
#include "string-util.h"
static bool ipv6_proxy_ndp_is_needed(Link *link) {
assert(link);
if (link->flags & IFF_LOOPBACK)
return false;
if (!link->network)
return false;
if (link->network->n_ipv6_proxy_ndp_addresses == 0)
return false;
return true;
}
static int ipv6_proxy_ndp_set(Link *link) {
const char *p = NULL;
int r, v;
assert(link);
v = ipv6_proxy_ndp_is_needed(link);
p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/proxy_ndp");
r = write_string_file(p, one_zero(v), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
if (r < 0)
log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m");
return 0;
}
int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) {
_cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
assert(network);
assert(ret);
/* allocate space for IPv6ProxyNDPAddress entry */
ipv6_proxy_ndp_address = new0(IPv6ProxyNDPAddress, 1);
if (!ipv6_proxy_ndp_address)
return -ENOMEM;
ipv6_proxy_ndp_address->network = network;
LIST_PREPEND(ipv6_proxy_ndp_addresses, network->ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address);
network->n_ipv6_proxy_ndp_addresses++;
*ret = ipv6_proxy_ndp_address;
ipv6_proxy_ndp_address = NULL;
return 0;
}
void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
if (!ipv6_proxy_ndp_address)
return;
if (ipv6_proxy_ndp_address->network) {
LIST_REMOVE(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address->network->ipv6_proxy_ndp_addresses,
ipv6_proxy_ndp_address);
assert(ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses > 0);
ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses--;
}
free(ipv6_proxy_ndp_address);
}
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) {
Network *network = userdata;
_cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
int r;
union in_addr_union buffer;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address);
if (r < 0)
return r;
r = in_addr_from_string(AF_INET6, rvalue, &buffer);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 proxy NDP address, ignoring: %s",
rvalue);
return 0;
}
r = in_addr_is_null(AF_INET6, &buffer);
if (r != 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"IPv6 proxy NDP address can not be the ANY address, ignoring: %s", rvalue);
return 0;
}
ipv6_proxy_ndp_address->in_addr = buffer.in6;
ipv6_proxy_ndp_address = NULL;
return 0;
}
static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
Link *link = userdata;
int r;
assert(link);
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST)
log_link_error_errno(link, r, "Could not add IPv6 proxy ndp address entry: %m");
return 1;
}
/* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */
int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
sd_netlink *rtnl;
int r;
assert(link);
assert(link->network);
assert(link->manager);
assert(ipv6_proxy_ndp_address);
rtnl = link->manager->rtnl;
/* create new netlink message */
r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_netlink_message_append_in6_addr(req, NDA_DST, &ipv6_proxy_ndp_address->in_addr);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_netlink_call_async(rtnl, req, set_ipv6_proxy_ndp_address_handler, link, 0, NULL);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
return 0;
}
/* configure all ipv6 proxy ndp addresses */
int ipv6_proxy_ndp_addresses_configure(Link *link) {
IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
int r;
/* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */
r = ipv6_proxy_ndp_set(link);
if (r != 0)
return r;
LIST_FOREACH(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address, link->network->ipv6_proxy_ndp_addresses) {
r = ipv6_proxy_ndp_address_configure(link, ipv6_proxy_ndp_address);
if (r != 0)
return r;
}
return 0;
}

View File

@ -0,0 +1,44 @@
#pragma once
/***
This file is part of systemd.
Copyright 2017 Florian Klink <flokli@flokli.de>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "list.h"
#include "macro.h"
typedef struct Network Network;
typedef struct IPv6ProxyNDPAddress IPv6ProxyNDPAddress;
typedef struct Link Link;
struct IPv6ProxyNDPAddress {
Network *network;
struct in6_addr in_addr;
LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
};
int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress ** ipv6_proxy_ndp_address);
void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address);
int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address);
int ipv6_proxy_ndp_addresses_configure(Link *link);
DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6ProxyNDPAddress*, ipv6_proxy_ndp_address_free);
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);

View File

@ -28,6 +28,7 @@
#include "fileio.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
@ -2448,6 +2449,10 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
r = ipv6_proxy_ndp_addresses_configure(link);
if (r < 0)
return r;
r = link_set_ipv4_forward(link);
if (r < 0)
return r;

View File

@ -67,6 +67,7 @@ Network.ActiveSlave, config_parse_bool,
Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave)
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.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Address.Address, config_parse_address, 0, 0
Address.Peer, config_parse_address, 0, 0

View File

@ -70,6 +70,7 @@ static int network_load_one(Manager *manager, const char *filename) {
LIST_HEAD_INIT(network->static_addresses);
LIST_HEAD_INIT(network->static_routes);
LIST_HEAD_INIT(network->static_fdb_entries);
LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses);
network->stacked_netdevs = hashmap_new(&string_hash_ops);
if (!network->stacked_netdevs)
@ -152,6 +153,7 @@ static int network_load_one(Manager *manager, const char *filename) {
"DHCPv4\0" /* compat */
"DHCPServer\0"
"IPv6AcceptRA\0"
"IPv6NDPProxyAddress\0"
"Bridge\0"
"BridgeFDB\0"
"BridgeVLAN\0",
@ -224,6 +226,7 @@ void network_free(Network *network) {
Route *route;
Address *address;
FdbEntry *fdb_entry;
IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
Iterator i;
if (!network)
@ -268,6 +271,9 @@ void network_free(Network *network) {
while ((fdb_entry = network->static_fdb_entries))
fdb_entry_free(fdb_entry);
while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section);
hashmap_free(network->fdb_entries_by_section);

View File

@ -31,6 +31,7 @@
#include "networkd-brvlan.h"
#include "networkd-fdb.h"
#include "networkd-lldp-tx.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-route.h"
#include "networkd-util.h"
#include "netdev/netdev.h"
@ -188,10 +189,12 @@ struct Network {
LIST_HEAD(Address, static_addresses);
LIST_HEAD(Route, static_routes);
LIST_HEAD(FdbEntry, static_fdb_entries);
LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
unsigned n_static_addresses;
unsigned n_static_routes;
unsigned n_static_fdb_entries;
unsigned n_ipv6_proxy_ndp_addresses;
Hashmap *addresses_by_section;
Hashmap *routes_by_section;