blob: 86a563144764e0fcf90a7e31a9aa58b134b1236e [file] [log] [blame]
William A. Kennington III6b8d479a2019-01-15 13:31:49 -08001From 2402e69c2ccf795d67bb55d7cd073e7384d57a3d Mon Sep 17 00:00:00 2001
2From: "William A. Kennington III" <william@wkennington.com>
3Date: Wed, 28 Nov 2018 19:00:58 -0800
4Subject: [PATCH 3/3] networkd: Static neighbor support
5
6When using networkd we currently have no way of ensuring that static
7neighbor entries are set when our link comes up. This change adds a new
8section to the network definition that allows multiple static neighbors
9to be set on a link.
10
11(cherry picked from commit e4a71bf36f422c3728b902aaa5846add7bbc0eb9)
12
13Upstream-Status: Backport
14---
15 man/systemd.network.xml | 25 +++
16 src/network/meson.build | 2 +
17 src/network/networkd-link.c | 34 ++++
18 src/network/networkd-link.h | 3 +
19 src/network/networkd-neighbor.c | 234 +++++++++++++++++++++++
20 src/network/networkd-neighbor.h | 37 ++++
21 src/network/networkd-network-gperf.gperf | 2 +
22 src/network/networkd-network.c | 10 +
23 src/network/networkd-network.h | 4 +
24 9 files changed, 351 insertions(+)
25 create mode 100644 src/network/networkd-neighbor.c
26 create mode 100644 src/network/networkd-neighbor.h
27
28diff --git a/man/systemd.network.xml b/man/systemd.network.xml
29index fc8e0aea68..804419c31a 100644
30--- a/man/systemd.network.xml
31+++ b/man/systemd.network.xml
32@@ -899,6 +899,31 @@
33 </variablelist>
34 </refsect1>
35
36+ <refsect1>
37+ <title>[Neighbor] Section Options</title>
38+ <para>A <literal>[Neighbor]</literal> section accepts the
39+ following keys. The neighbor section adds a permanent, static
40+ entry to the neighbor table (IPv6) or ARP table (IPv4) for
41+ the given hardware address on the links matched for the network.
42+ Specify several <literal>[Neighbor]</literal> sections to configure
43+ several static neighbors.</para>
44+
45+ <variablelist class='network-directives'>
46+ <varlistentry>
47+ <term><varname>Address=</varname></term>
48+ <listitem>
49+ <para>The IP address of the neighbor.</para>
50+ </listitem>
51+ </varlistentry>
52+ <varlistentry>
53+ <term><varname>MACAddress=</varname></term>
54+ <listitem>
55+ <para>The hardware address of the neighbor.</para>
56+ </listitem>
57+ </varlistentry>
58+ </variablelist>
59+ </refsect1>
60+
61 <refsect1>
62 <title>[IPv6AddressLabel] Section Options</title>
63
64diff --git a/src/network/meson.build b/src/network/meson.build
65index 8f5544ea04..900130ed6a 100644
66--- a/src/network/meson.build
67+++ b/src/network/meson.build
68@@ -62,6 +62,8 @@ sources = files('''
69 networkd-manager.h
70 networkd-ndisc.c
71 networkd-ndisc.h
72+ networkd-neighbor.c
73+ networkd-neighbor.h
74 networkd-radv.c
75 networkd-radv.h
76 networkd-network-bus.c
77diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
78index 71b92185cd..51f95ac55e 100644
79--- a/src/network/networkd-link.c
80+++ b/src/network/networkd-link.c
81@@ -17,6 +17,7 @@
82 #include "networkd-lldp-tx.h"
83 #include "networkd-manager.h"
84 #include "networkd-ndisc.h"
85+#include "networkd-neighbor.h"
86 #include "networkd-radv.h"
87 #include "networkd-routing-policy-rule.h"
88 #include "set.h"
89@@ -731,6 +732,9 @@ void link_check_ready(Link *link) {
90 if (!link->addresses_configured)
91 return;
92
93+ if (!link->neighbors_configured)
94+ return;
95+
96 if (!link->static_routes_configured)
97 return;
98
99@@ -883,6 +887,34 @@ int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *use
100 return 1;
101 }
102
103+static int link_request_set_neighbors(Link *link) {
104+ Neighbor *neighbor;
105+ int r;
106+
107+ assert(link);
108+ assert(link->network);
109+ assert(link->state != _LINK_STATE_INVALID);
110+
111+ link_set_state(link, LINK_STATE_CONFIGURING);
112+
113+ LIST_FOREACH(neighbors, neighbor, link->network->neighbors) {
114+ r = neighbor_configure(neighbor, link, NULL);
115+ if (r < 0) {
116+ log_link_warning_errno(link, r, "Could not set neighbor: %m");
117+ link_enter_failed(link);
118+ return r;
119+ }
120+ }
121+
122+ if (link->neighbor_messages == 0) {
123+ link->neighbors_configured = true;
124+ link_check_ready(link);
125+ } else
126+ log_link_debug(link, "Setting neighbors");
127+
128+ return 0;
129+}
130+
131 static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
132 _cleanup_(link_unrefp) Link *link = userdata;
133 int r;
134@@ -1068,6 +1100,8 @@ static int link_request_set_addresses(Link *link) {
135
136 link_set_state(link, LINK_STATE_CONFIGURING);
137
138+ link_request_set_neighbors(link);
139+
140 LIST_FOREACH(addresses, ad, link->network->static_addresses) {
141 r = address_configure(ad, link, address_handler, false);
142 if (r < 0) {
143diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
144index 5fd81be5a5..464e93d982 100644
145--- a/src/network/networkd-link.h
146+++ b/src/network/networkd-link.h
147@@ -68,6 +68,7 @@ typedef struct Link {
148
149 unsigned address_messages;
150 unsigned address_label_messages;
151+ unsigned neighbor_messages;
152 unsigned route_messages;
153 unsigned routing_policy_rule_messages;
154 unsigned routing_policy_rule_remove_messages;
155@@ -95,6 +96,8 @@ typedef struct Link {
156 bool ipv4ll_address:1;
157 bool ipv4ll_route:1;
158
159+ bool neighbors_configured;
160+
161 bool static_routes_configured;
162 bool routing_policy_rules_configured;
163 bool setting_mtu;
164diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c
165new file mode 100644
166index 0000000000..db69d310d3
167--- /dev/null
168+++ b/src/network/networkd-neighbor.c
169@@ -0,0 +1,234 @@
170+/* SPDX-License-Identifier: LGPL-2.1+ */
171+
172+#include "sd-netlink.h"
173+
174+#include "alloc-util.h"
175+#include "conf-parser.h"
176+#include "ether-addr-util.h"
177+#include "hashmap.h"
178+#include "in-addr-util.h"
179+#include "netlink-util.h"
180+#include "networkd-link.h"
181+#include "networkd-manager.h"
182+#include "networkd-neighbor.h"
183+
184+void neighbor_free(Neighbor *neighbor) {
185+ if (!neighbor)
186+ return;
187+
188+ if (neighbor->network) {
189+ LIST_REMOVE(neighbors, neighbor->network->neighbors, neighbor);
190+ assert(neighbor->network->n_neighbors > 0);
191+ neighbor->network->n_neighbors--;
192+
193+ if (neighbor->section) {
194+ hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
195+ network_config_section_free(neighbor->section);
196+ }
197+ }
198+
199+ free(neighbor);
200+}
201+
202+static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
203+ _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
204+ _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
205+ int r;
206+
207+ assert(network);
208+ assert(ret);
209+ assert(!!filename == (section_line > 0));
210+
211+ if (filename) {
212+ r = network_config_section_new(filename, section_line, &n);
213+ if (r < 0)
214+ return r;
215+
216+ neighbor = hashmap_get(network->neighbors_by_section, n);
217+ if (neighbor) {
218+ *ret = TAKE_PTR(neighbor);
219+
220+ return 0;
221+ }
222+ }
223+
224+ neighbor = new(Neighbor, 1);
225+ if (!neighbor)
226+ return -ENOMEM;
227+
228+ *neighbor = (Neighbor) {
229+ .network = network,
230+ .family = AF_UNSPEC,
231+ };
232+
233+ LIST_APPEND(neighbors, network->neighbors, neighbor);
234+ network->n_neighbors++;
235+
236+ if (filename) {
237+ neighbor->section = TAKE_PTR(n);
238+
239+ r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor);
240+ if (r < 0)
241+ return r;
242+ }
243+
244+ *ret = TAKE_PTR(neighbor);
245+
246+ return 0;
247+}
248+
249+static int neighbor_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
250+ _cleanup_(link_unrefp) Link *link = userdata;
251+ int r;
252+
253+ assert(link);
254+ assert(link->neighbor_messages > 0);
255+
256+ link->neighbor_messages--;
257+
258+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
259+ return 1;
260+
261+ r = sd_netlink_message_get_errno(m);
262+ if (r < 0 && r != -EEXIST)
263+ log_link_warning_errno(link, r, "Could not set neighbor: %m");
264+
265+ if (link->neighbor_messages == 0) {
266+ log_link_debug(link, "Neighbors set");
267+ link->neighbors_configured = true;
268+ link_check_ready(link);
269+ }
270+
271+ return 1;
272+}
273+
274+int neighbor_configure(Neighbor *neighbor, Link *link, sd_netlink_message_handler_t callback) {
275+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
276+ int r;
277+
278+ assert(neighbor);
279+ assert(link);
280+ assert(link->ifindex > 0);
281+ assert(link->manager);
282+ assert(link->manager->rtnl);
283+
284+ if (neighbor->family == AF_UNSPEC)
285+ return log_error_errno(EINVAL, "Neighbor without Address= configured");
286+ if (!neighbor->mac_configured)
287+ return log_error_errno(EINVAL, "Neighbor without MACAddress= configured");
288+
289+ r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH,
290+ link->ifindex, neighbor->family);
291+ if (r < 0)
292+ return log_error_errno(r, "Could not allocate RTM_NEWNEIGH message: %m");
293+
294+ r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT);
295+ if (r < 0)
296+ return log_error_errno(r, "Could not set state: %m");
297+
298+ r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE);
299+ if (r < 0)
300+ return log_error_errno(r, "Could not set flags: %m");
301+
302+ r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, &neighbor->mac);
303+ if (r < 0)
304+ return log_error_errno(r, "Could not append NDA_LLADDR attribute: %m");
305+
306+ switch (neighbor->family) {
307+ case AF_INET6:
308+ r = sd_netlink_message_append_in6_addr(req, NDA_DST, &neighbor->in_addr.in6);
309+ if (r < 0)
310+ return log_error_errno(r, "Could not append NDA_DST attribute: %m");
311+ break;
312+ case AF_INET:
313+ r = sd_netlink_message_append_in_addr(req, NDA_DST, &neighbor->in_addr.in);
314+ if (r < 0)
315+ return log_error_errno(r, "Could not append NDA_DST attribute: %m");
316+ break;
317+ default:
318+ return log_error_errno(EINVAL, "Neighbor with invalid address family");
319+ }
320+
321+ r = sd_netlink_call_async(link->manager->rtnl, req, callback ?: neighbor_handler,
322+ link, 0, NULL);
323+ if (r < 0)
324+ return log_error_errno(r, "Could not send rtnetlink message: %m");
325+
326+ link->neighbor_messages++;
327+ link_ref(link);
328+
329+ return 0;
330+}
331+
332+int config_parse_neighbor_address(const char *unit,
333+ const char *filename,
334+ unsigned line,
335+ const char *section,
336+ unsigned section_line,
337+ const char *lvalue,
338+ int ltype,
339+ const char *rvalue,
340+ void *data,
341+ void *userdata) {
342+
343+ Network *network = userdata;
344+ _cleanup_(neighbor_freep) Neighbor *n = NULL;
345+ int r;
346+
347+ assert(filename);
348+ assert(section);
349+ assert(lvalue);
350+ assert(rvalue);
351+ assert(data);
352+
353+ r = neighbor_new_static(network, filename, section_line, &n);
354+ if (r < 0)
355+ return r;
356+
357+ r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
358+ if (r < 0) {
359+ log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor Address is invalid, ignoring assignment: %s", rvalue);
360+ return 0;
361+ }
362+
363+ TAKE_PTR(n);
364+
365+ return 0;
366+}
367+
368+int config_parse_neighbor_hwaddr(const char *unit,
369+ const char *filename,
370+ unsigned line,
371+ const char *section,
372+ unsigned section_line,
373+ const char *lvalue,
374+ int ltype,
375+ const char *rvalue,
376+ void *data,
377+ void *userdata) {
378+
379+ Network *network = userdata;
380+ _cleanup_(neighbor_freep) Neighbor *n = NULL;
381+ int r;
382+
383+ assert(filename);
384+ assert(section);
385+ assert(lvalue);
386+ assert(rvalue);
387+ assert(data);
388+
389+ r = neighbor_new_static(network, filename, section_line, &n);
390+ if (r < 0)
391+ return r;
392+
393+ r = ether_addr_from_string(rvalue, &n->mac);
394+ if (r < 0) {
395+ log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor MACAddress is invalid, ignoring assignment: %s", rvalue);
396+ return 0;
397+ }
398+
399+ n->mac_configured = true;
400+ TAKE_PTR(n);
401+
402+ return 0;
403+}
404diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h
405new file mode 100644
406index 0000000000..30a1f8fe27
407--- /dev/null
408+++ b/src/network/networkd-neighbor.h
409@@ -0,0 +1,37 @@
410+/* SPDX-License-Identifier: LGPL-2.1+ */
411+#pragma once
412+
413+#include "sd-netlink.h"
414+
415+#include "conf-parser.h"
416+#include "ether-addr-util.h"
417+#include "in-addr-util.h"
418+#include "list.h"
419+#include "macro.h"
420+
421+typedef struct Neighbor Neighbor;
422+
423+#include "networkd-link.h"
424+#include "networkd-network.h"
425+
426+struct Neighbor {
427+ Network *network;
428+ Link *link;
429+ NetworkConfigSection *section;
430+
431+ int family;
432+ union in_addr_union in_addr;
433+ bool mac_configured;
434+ struct ether_addr mac;
435+
436+ LIST_FIELDS(Neighbor, neighbors);
437+};
438+
439+void neighbor_free(Neighbor *neighbor);
440+
441+DEFINE_TRIVIAL_CLEANUP_FUNC(Neighbor*, neighbor_free);
442+
443+int neighbor_configure(Neighbor *neighbor, Link *link, sd_netlink_message_handler_t callback);
444+
445+CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
446+CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_hwaddr);
447diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
448index 6ad5257f79..18405149a4 100644
449--- a/src/network/networkd-network-gperf.gperf
450+++ b/src/network/networkd-network-gperf.gperf
451@@ -93,6 +93,8 @@ Address.AutoJoin, config_parse_address_flags,
452 Address.Scope, config_parse_address_scope, 0, 0
453 IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
454 IPv6AddressLabel.Label, config_parse_address_label, 0, 0
455+Neighbor.Address, config_parse_neighbor_address, 0, 0
456+Neighbor.MACAddress, config_parse_neighbor_hwaddr, 0, 0
457 RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0
458 RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0
459 RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0
460diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
461index 429aac5e6c..a3b8f0cec4 100644
462--- a/src/network/networkd-network.c
463+++ b/src/network/networkd-network.c
464@@ -158,6 +158,10 @@ static int network_load_one(Manager *manager, const char *filename) {
465 if (!network->fdb_entries_by_section)
466 return log_oom();
467
468+ network->neighbors_by_section = hashmap_new(&network_config_hash_ops);
469+ if (!network->neighbors_by_section)
470+ log_oom();
471+
472 network->address_labels_by_section = hashmap_new(&network_config_hash_ops);
473 if (!network->address_labels_by_section)
474 log_oom();
475@@ -256,6 +260,7 @@ static int network_load_one(Manager *manager, const char *filename) {
476 "Link\0"
477 "Network\0"
478 "Address\0"
479+ "Neighbor\0"
480 "IPv6AddressLabel\0"
481 "RoutingPolicyRule\0"
482 "Route\0"
483@@ -340,6 +345,7 @@ void network_free(Network *network) {
484 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
485 RoutingPolicyRule *rule;
486 FdbEntry *fdb_entry;
487+ Neighbor *neighbor;
488 AddressLabel *label;
489 Prefix *prefix;
490 Address *address;
491@@ -393,6 +399,9 @@ void network_free(Network *network) {
492 while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses))
493 ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address);
494
495+ while ((neighbor = network->neighbors))
496+ neighbor_free(neighbor);
497+
498 while ((label = network->address_labels))
499 address_label_free(label);
500
501@@ -405,6 +414,7 @@ void network_free(Network *network) {
502 hashmap_free(network->addresses_by_section);
503 hashmap_free(network->routes_by_section);
504 hashmap_free(network->fdb_entries_by_section);
505+ hashmap_free(network->neighbors_by_section);
506 hashmap_free(network->address_labels_by_section);
507 hashmap_free(network->prefixes_by_section);
508 hashmap_free(network->rules_by_section);
509diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
510index 2d46d393ac..8df18326bb 100644
511--- a/src/network/networkd-network.h
512+++ b/src/network/networkd-network.h
513@@ -16,6 +16,7 @@
514 #include "networkd-fdb.h"
515 #include "networkd-ipv6-proxy-ndp.h"
516 #include "networkd-lldp-tx.h"
517+#include "networkd-neighbor.h"
518 #include "networkd-radv.h"
519 #include "networkd-route.h"
520 #include "networkd-routing-policy-rule.h"
521@@ -229,6 +230,7 @@ struct Network {
522 LIST_HEAD(Route, static_routes);
523 LIST_HEAD(FdbEntry, static_fdb_entries);
524 LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
525+ LIST_HEAD(Neighbor, neighbors);
526 LIST_HEAD(AddressLabel, address_labels);
527 LIST_HEAD(Prefix, static_prefixes);
528 LIST_HEAD(RoutingPolicyRule, rules);
529@@ -237,6 +239,7 @@ struct Network {
530 unsigned n_static_routes;
531 unsigned n_static_fdb_entries;
532 unsigned n_ipv6_proxy_ndp_addresses;
533+ unsigned n_neighbors;
534 unsigned n_address_labels;
535 unsigned n_static_prefixes;
536 unsigned n_rules;
537@@ -244,6 +247,7 @@ struct Network {
538 Hashmap *addresses_by_section;
539 Hashmap *routes_by_section;
540 Hashmap *fdb_entries_by_section;
541+ Hashmap *neighbors_by_section;
542 Hashmap *address_labels_by_section;
543 Hashmap *prefixes_by_section;
544 Hashmap *rules_by_section;
545--
5462.20.1.97.g81188d93c3-goog
547