blob: 86a563144764e0fcf90a7e31a9aa58b134b1236e [file] [log] [blame]
From 2402e69c2ccf795d67bb55d7cd073e7384d57a3d Mon Sep 17 00:00:00 2001
From: "William A. Kennington III" <william@wkennington.com>
Date: Wed, 28 Nov 2018 19:00:58 -0800
Subject: [PATCH 3/3] networkd: Static neighbor support
When using networkd we currently have no way of ensuring that static
neighbor entries are set when our link comes up. This change adds a new
section to the network definition that allows multiple static neighbors
to be set on a link.
(cherry picked from commit e4a71bf36f422c3728b902aaa5846add7bbc0eb9)
Upstream-Status: Backport
---
man/systemd.network.xml | 25 +++
src/network/meson.build | 2 +
src/network/networkd-link.c | 34 ++++
src/network/networkd-link.h | 3 +
src/network/networkd-neighbor.c | 234 +++++++++++++++++++++++
src/network/networkd-neighbor.h | 37 ++++
src/network/networkd-network-gperf.gperf | 2 +
src/network/networkd-network.c | 10 +
src/network/networkd-network.h | 4 +
9 files changed, 351 insertions(+)
create mode 100644 src/network/networkd-neighbor.c
create mode 100644 src/network/networkd-neighbor.h
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index fc8e0aea68..804419c31a 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -899,6 +899,31 @@
</variablelist>
</refsect1>
+ <refsect1>
+ <title>[Neighbor] Section Options</title>
+ <para>A <literal>[Neighbor]</literal> section accepts the
+ following keys. The neighbor section adds a permanent, static
+ entry to the neighbor table (IPv6) or ARP table (IPv4) for
+ the given hardware address on the links matched for the network.
+ Specify several <literal>[Neighbor]</literal> sections to configure
+ several static neighbors.</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Address=</varname></term>
+ <listitem>
+ <para>The IP address of the neighbor.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>MACAddress=</varname></term>
+ <listitem>
+ <para>The hardware address of the neighbor.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>[IPv6AddressLabel] Section Options</title>
diff --git a/src/network/meson.build b/src/network/meson.build
index 8f5544ea04..900130ed6a 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -62,6 +62,8 @@ sources = files('''
networkd-manager.h
networkd-ndisc.c
networkd-ndisc.h
+ networkd-neighbor.c
+ networkd-neighbor.h
networkd-radv.c
networkd-radv.h
networkd-network-bus.c
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 71b92185cd..51f95ac55e 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -17,6 +17,7 @@
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
+#include "networkd-neighbor.h"
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
#include "set.h"
@@ -731,6 +732,9 @@ void link_check_ready(Link *link) {
if (!link->addresses_configured)
return;
+ if (!link->neighbors_configured)
+ return;
+
if (!link->static_routes_configured)
return;
@@ -883,6 +887,34 @@ int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *use
return 1;
}
+static int link_request_set_neighbors(Link *link) {
+ Neighbor *neighbor;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->state != _LINK_STATE_INVALID);
+
+ link_set_state(link, LINK_STATE_CONFIGURING);
+
+ LIST_FOREACH(neighbors, neighbor, link->network->neighbors) {
+ r = neighbor_configure(neighbor, link, NULL);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set neighbor: %m");
+ link_enter_failed(link);
+ return r;
+ }
+ }
+
+ if (link->neighbor_messages == 0) {
+ link->neighbors_configured = true;
+ link_check_ready(link);
+ } else
+ log_link_debug(link, "Setting neighbors");
+
+ return 0;
+}
+
static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
_cleanup_(link_unrefp) Link *link = userdata;
int r;
@@ -1068,6 +1100,8 @@ static int link_request_set_addresses(Link *link) {
link_set_state(link, LINK_STATE_CONFIGURING);
+ link_request_set_neighbors(link);
+
LIST_FOREACH(addresses, ad, link->network->static_addresses) {
r = address_configure(ad, link, address_handler, false);
if (r < 0) {
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 5fd81be5a5..464e93d982 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -68,6 +68,7 @@ typedef struct Link {
unsigned address_messages;
unsigned address_label_messages;
+ unsigned neighbor_messages;
unsigned route_messages;
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
@@ -95,6 +96,8 @@ typedef struct Link {
bool ipv4ll_address:1;
bool ipv4ll_route:1;
+ bool neighbors_configured;
+
bool static_routes_configured;
bool routing_policy_rules_configured;
bool setting_mtu;
diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c
new file mode 100644
index 0000000000..db69d310d3
--- /dev/null
+++ b/src/network/networkd-neighbor.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "sd-netlink.h"
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "ether-addr-util.h"
+#include "hashmap.h"
+#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-neighbor.h"
+
+void neighbor_free(Neighbor *neighbor) {
+ if (!neighbor)
+ return;
+
+ if (neighbor->network) {
+ LIST_REMOVE(neighbors, neighbor->network->neighbors, neighbor);
+ assert(neighbor->network->n_neighbors > 0);
+ neighbor->network->n_neighbors--;
+
+ if (neighbor->section) {
+ hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
+ network_config_section_free(neighbor->section);
+ }
+ }
+
+ free(neighbor);
+}
+
+static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
+ _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
+ int r;
+
+ assert(network);
+ assert(ret);
+ assert(!!filename == (section_line > 0));
+
+ if (filename) {
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ neighbor = hashmap_get(network->neighbors_by_section, n);
+ if (neighbor) {
+ *ret = TAKE_PTR(neighbor);
+
+ return 0;
+ }
+ }
+
+ neighbor = new(Neighbor, 1);
+ if (!neighbor)
+ return -ENOMEM;
+
+ *neighbor = (Neighbor) {
+ .network = network,
+ .family = AF_UNSPEC,
+ };
+
+ LIST_APPEND(neighbors, network->neighbors, neighbor);
+ network->n_neighbors++;
+
+ if (filename) {
+ neighbor->section = TAKE_PTR(n);
+
+ r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(neighbor);
+
+ return 0;
+}
+
+static int neighbor_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ _cleanup_(link_unrefp) Link *link = userdata;
+ int r;
+
+ assert(link);
+ assert(link->neighbor_messages > 0);
+
+ link->neighbor_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST)
+ log_link_warning_errno(link, r, "Could not set neighbor: %m");
+
+ if (link->neighbor_messages == 0) {
+ log_link_debug(link, "Neighbors set");
+ link->neighbors_configured = true;
+ link_check_ready(link);
+ }
+
+ return 1;
+}
+
+int neighbor_configure(Neighbor *neighbor, Link *link, sd_netlink_message_handler_t callback) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ int r;
+
+ assert(neighbor);
+ assert(link);
+ assert(link->ifindex > 0);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+
+ if (neighbor->family == AF_UNSPEC)
+ return log_error_errno(EINVAL, "Neighbor without Address= configured");
+ if (!neighbor->mac_configured)
+ return log_error_errno(EINVAL, "Neighbor without MACAddress= configured");
+
+ r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH,
+ link->ifindex, neighbor->family);
+ if (r < 0)
+ return log_error_errno(r, "Could not allocate RTM_NEWNEIGH message: %m");
+
+ r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT);
+ if (r < 0)
+ return log_error_errno(r, "Could not set state: %m");
+
+ r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE);
+ if (r < 0)
+ return log_error_errno(r, "Could not set flags: %m");
+
+ r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, &neighbor->mac);
+ if (r < 0)
+ return log_error_errno(r, "Could not append NDA_LLADDR attribute: %m");
+
+ switch (neighbor->family) {
+ case AF_INET6:
+ r = sd_netlink_message_append_in6_addr(req, NDA_DST, &neighbor->in_addr.in6);
+ if (r < 0)
+ return log_error_errno(r, "Could not append NDA_DST attribute: %m");
+ break;
+ case AF_INET:
+ r = sd_netlink_message_append_in_addr(req, NDA_DST, &neighbor->in_addr.in);
+ if (r < 0)
+ return log_error_errno(r, "Could not append NDA_DST attribute: %m");
+ break;
+ default:
+ return log_error_errno(EINVAL, "Neighbor with invalid address family");
+ }
+
+ r = sd_netlink_call_async(link->manager->rtnl, req, callback ?: neighbor_handler,
+ link, 0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Could not send rtnetlink message: %m");
+
+ link->neighbor_messages++;
+ link_ref(link);
+
+ return 0;
+}
+
+int config_parse_neighbor_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_(neighbor_freep) Neighbor *n = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = neighbor_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor Address is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(n);
+
+ return 0;
+}
+
+int config_parse_neighbor_hwaddr(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_(neighbor_freep) Neighbor *n = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = neighbor_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = ether_addr_from_string(rvalue, &n->mac);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor MACAddress is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ n->mac_configured = true;
+ TAKE_PTR(n);
+
+ return 0;
+}
diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h
new file mode 100644
index 0000000000..30a1f8fe27
--- /dev/null
+++ b/src/network/networkd-neighbor.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-netlink.h"
+
+#include "conf-parser.h"
+#include "ether-addr-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "macro.h"
+
+typedef struct Neighbor Neighbor;
+
+#include "networkd-link.h"
+#include "networkd-network.h"
+
+struct Neighbor {
+ Network *network;
+ Link *link;
+ NetworkConfigSection *section;
+
+ int family;
+ union in_addr_union in_addr;
+ bool mac_configured;
+ struct ether_addr mac;
+
+ LIST_FIELDS(Neighbor, neighbors);
+};
+
+void neighbor_free(Neighbor *neighbor);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Neighbor*, neighbor_free);
+
+int neighbor_configure(Neighbor *neighbor, Link *link, sd_netlink_message_handler_t callback);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_hwaddr);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 6ad5257f79..18405149a4 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -93,6 +93,8 @@ Address.AutoJoin, config_parse_address_flags,
Address.Scope, config_parse_address_scope, 0, 0
IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
IPv6AddressLabel.Label, config_parse_address_label, 0, 0
+Neighbor.Address, config_parse_neighbor_address, 0, 0
+Neighbor.MACAddress, config_parse_neighbor_hwaddr, 0, 0
RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0
RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0
RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 429aac5e6c..a3b8f0cec4 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -158,6 +158,10 @@ static int network_load_one(Manager *manager, const char *filename) {
if (!network->fdb_entries_by_section)
return log_oom();
+ network->neighbors_by_section = hashmap_new(&network_config_hash_ops);
+ if (!network->neighbors_by_section)
+ log_oom();
+
network->address_labels_by_section = hashmap_new(&network_config_hash_ops);
if (!network->address_labels_by_section)
log_oom();
@@ -256,6 +260,7 @@ static int network_load_one(Manager *manager, const char *filename) {
"Link\0"
"Network\0"
"Address\0"
+ "Neighbor\0"
"IPv6AddressLabel\0"
"RoutingPolicyRule\0"
"Route\0"
@@ -340,6 +345,7 @@ void network_free(Network *network) {
IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
RoutingPolicyRule *rule;
FdbEntry *fdb_entry;
+ Neighbor *neighbor;
AddressLabel *label;
Prefix *prefix;
Address *address;
@@ -393,6 +399,9 @@ void network_free(Network *network) {
while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
+ while ((neighbor = network->neighbors))
+ neighbor_free(neighbor);
+
while ((label = network->address_labels))
address_label_free(label);
@@ -405,6 +414,7 @@ void network_free(Network *network) {
hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section);
hashmap_free(network->fdb_entries_by_section);
+ hashmap_free(network->neighbors_by_section);
hashmap_free(network->address_labels_by_section);
hashmap_free(network->prefixes_by_section);
hashmap_free(network->rules_by_section);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 2d46d393ac..8df18326bb 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -16,6 +16,7 @@
#include "networkd-fdb.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-tx.h"
+#include "networkd-neighbor.h"
#include "networkd-radv.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
@@ -229,6 +230,7 @@ struct Network {
LIST_HEAD(Route, static_routes);
LIST_HEAD(FdbEntry, static_fdb_entries);
LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
+ LIST_HEAD(Neighbor, neighbors);
LIST_HEAD(AddressLabel, address_labels);
LIST_HEAD(Prefix, static_prefixes);
LIST_HEAD(RoutingPolicyRule, rules);
@@ -237,6 +239,7 @@ struct Network {
unsigned n_static_routes;
unsigned n_static_fdb_entries;
unsigned n_ipv6_proxy_ndp_addresses;
+ unsigned n_neighbors;
unsigned n_address_labels;
unsigned n_static_prefixes;
unsigned n_rules;
@@ -244,6 +247,7 @@ struct Network {
Hashmap *addresses_by_section;
Hashmap *routes_by_section;
Hashmap *fdb_entries_by_section;
+ Hashmap *neighbors_by_section;
Hashmap *address_labels_by_section;
Hashmap *prefixes_by_section;
Hashmap *rules_by_section;
--
2.20.1.97.g81188d93c3-goog