]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: add IPv6ProxyNDPAddress support (#5174)
authorFlorian Klink <flokli@flokli.de>
Fri, 10 Feb 2017 23:47:55 +0000 (00:47 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 10 Feb 2017 23:47:55 +0000 (00:47 +0100)
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.

Makefile.am
man/systemd.network.xml
src/network/networkd-ipv6-proxy-ndp.c [new file with mode: 0644]
src/network/networkd-ipv6-proxy-ndp.h [new file with mode: 0644]
src/network/networkd-link.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h

index 2d913bd7d716733b6e76f8474da1a2eec7a7ab73..957794b2dce94519ba1b4cd14930a1277ef33569 100644 (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 \
index 7818dfff815531c3a7ed5b7bf7675a6806879871..b807ebf29b8040d31a5c7176145c37757023126e 100644 (file)
           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>
diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c
new file mode 100644 (file)
index 0000000..11c1cd9
--- /dev/null
@@ -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;
+}
diff --git a/src/network/networkd-ipv6-proxy-ndp.h b/src/network/networkd-ipv6-proxy-ndp.h
new file mode 100644 (file)
index 0000000..f09169f
--- /dev/null
@@ -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);
index b993d27c2f54de09add6a8dbd2711a2480ff4c10..0c1229336b87c8d4b8db7e106ef504e2d68f0af1 100644 (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;
index 7b54e81fb89814e308247079bafda9bd38afb344..68052ba54439840b2bac6dc3a8b20db51d160653 100644 (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
index bc4dc95ff9d48182cc99e9919058e14fea4c1361..92062ca00ce78e8b116430fdcba58fca46d8ac00 100644 (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);
index b7da9d22d407643bd12ca3b03da7e53e1da07287..f06828a8993d5053f9401e34a8d7be2930ee1725 100644 (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;