summaryrefslogtreecommitdiff
path: root/meta-phosphor/recipes-core
diff options
context:
space:
mode:
authorWilliam A. Kennington III <wak@google.com>2019-01-16 00:31:49 +0300
committerBrad Bishop <bradleyb@fuzziesquirrel.com>2019-01-16 16:18:21 +0300
commit6b8d479ae1016f5df9aca37b3e6c78d32558d287 (patch)
treee612db44e39355ae60f3927aca00325aae523b46 /meta-phosphor/recipes-core
parentfa46cac0fccd27e4555dd1fe0008f39f0d5a4565 (diff)
downloadopenbmc-6b8d479ae1016f5df9aca37b3e6c78d32558d287.tar.xz
systemd: Add support for static neighbors
These are backports of changes I contributed to systemd v240. We need these until we get v240 from oe core. Tested: Ran a romulus image and provisioned static neighbors with systemd networkd. (From meta-phosphor rev: 8b84385e3c40d1827b06d0612def2275d3ad4faf) Change-Id: I5720a3b1626e15d4e4cfc630ce24f5818b294d8a Signed-off-by: William A. Kennington III <wak@google.com> Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
Diffstat (limited to 'meta-phosphor/recipes-core')
-rw-r--r--meta-phosphor/recipes-core/systemd/systemd/0001-networkd-Track-address-configuration.patch77
-rw-r--r--meta-phosphor/recipes-core/systemd/systemd/0002-networkd-Use-only-a-generic-CONFIGURING-state.patch176
-rw-r--r--meta-phosphor/recipes-core/systemd/systemd/0003-networkd-Static-neighbor-support.patch547
-rw-r--r--meta-phosphor/recipes-core/systemd/systemd_%.bbappend5
4 files changed, 805 insertions, 0 deletions
diff --git a/meta-phosphor/recipes-core/systemd/systemd/0001-networkd-Track-address-configuration.patch b/meta-phosphor/recipes-core/systemd/systemd/0001-networkd-Track-address-configuration.patch
new file mode 100644
index 000000000..c6836697f
--- /dev/null
+++ b/meta-phosphor/recipes-core/systemd/systemd/0001-networkd-Track-address-configuration.patch
@@ -0,0 +1,77 @@
+From a24d0d95df1929912c0f767f5ab1353552464c84 Mon Sep 17 00:00:00 2001
+From: "William A. Kennington III" <william@wkennington.com>
+Date: Fri, 30 Nov 2018 15:54:45 -0800
+Subject: [PATCH 1/3] networkd: Track address configuration
+
+This will be useful to assert that our static route configuration always
+happens after address configuration once our individual configure state
+goes away.
+
+(cherry picked from commit c42ff3a1a7bfea66dc4655096c79bd481159091b)
+
+Upstream-Status: Backport
+---
+ src/network/networkd-link.c | 11 +++++++++--
+ src/network/networkd-link.h | 2 ++
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
+index 4afcf843bd..0faf337069 100644
+--- a/src/network/networkd-link.c
++++ b/src/network/networkd-link.c
+@@ -728,6 +728,9 @@ void link_check_ready(Link *link) {
+ if (!link->network)
+ return;
+
++ if (!link->addresses_configured)
++ return;
++
+ if (!link->static_routes_configured)
+ return;
+
+@@ -835,6 +838,8 @@ static int link_enter_set_routes(Link *link) {
+
+ assert(link);
+ assert(link->network);
++ assert(link->addresses_configured);
++ assert(link->address_messages == 0);
+ assert(link->state == LINK_STATE_SETTING_ADDRESSES);
+
+ (void) link_set_routing_policy_rule(link);
+@@ -904,6 +909,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
+
+ if (link->address_messages == 0) {
+ log_link_debug(link, "Addresses set");
++ link->addresses_configured = true;
+ link_enter_set_routes(link);
+ }
+
+@@ -1204,9 +1210,10 @@ static int link_enter_set_addresses(Link *link) {
+ log_link_debug(link, "Offering DHCPv4 leases");
+ }
+
+- if (link->address_messages == 0)
++ if (link->address_messages == 0) {
++ link->addresses_configured = true;
+ link_enter_set_routes(link);
+- else
++ } else
+ log_link_debug(link, "Setting addresses");
+
+ return 0;
+diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
+index 7e22dfd504..2f37cc1598 100644
+--- a/src/network/networkd-link.h
++++ b/src/network/networkd-link.h
+@@ -80,6 +80,8 @@ typedef struct Link {
+ Set *routes;
+ Set *routes_foreign;
+
++ bool addresses_configured;
++
+ sd_dhcp_client *dhcp_client;
+ sd_dhcp_lease *dhcp_lease;
+ char *lease_file;
+--
+2.20.1.97.g81188d93c3-goog
+
diff --git a/meta-phosphor/recipes-core/systemd/systemd/0002-networkd-Use-only-a-generic-CONFIGURING-state.patch b/meta-phosphor/recipes-core/systemd/systemd/0002-networkd-Use-only-a-generic-CONFIGURING-state.patch
new file mode 100644
index 000000000..49c4cc35f
--- /dev/null
+++ b/meta-phosphor/recipes-core/systemd/systemd/0002-networkd-Use-only-a-generic-CONFIGURING-state.patch
@@ -0,0 +1,176 @@
+From 3283d15fe33da392b7174eb4b82a5ad1ce03a6e4 Mon Sep 17 00:00:00 2001
+From: "William A. Kennington III" <william@wkennington.com>
+Date: Fri, 30 Nov 2018 15:36:33 -0800
+Subject: [PATCH 2/3] networkd: Use only a generic CONFIGURING state
+
+This allows us to convey that we are performing multiple link
+configuration changes in parallel. This is needed to support configuring
+neighbors while simultaneously configuring addresses and routes.
+
+(cherry picked from commit 289e6774d0daefe86771171b8f4f97b63354683e)
+
+Upstream-Status: Backport
+---
+ src/network/networkd-link.c | 37 +++++++++++++++++--------------------
+ src/network/networkd-link.h | 4 +---
+ 2 files changed, 18 insertions(+), 23 deletions(-)
+
+diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
+index 0faf337069..71b92185cd 100644
+--- a/src/network/networkd-link.c
++++ b/src/network/networkd-link.c
+@@ -706,7 +706,7 @@ static void link_enter_configured(Link *link) {
+ assert(link);
+ assert(link->network);
+
+- if (link->state != LINK_STATE_SETTING_ROUTES)
++ if (link->state != LINK_STATE_CONFIGURING)
+ return;
+
+ log_link_info(link, "Configured");
+@@ -810,9 +810,8 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata
+ int r;
+
+ assert(link->route_messages > 0);
+- assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES,
+- LINK_STATE_SETTING_ROUTES, LINK_STATE_FAILED,
+- LINK_STATE_LINGER));
++ assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
++ LINK_STATE_FAILED, LINK_STATE_LINGER));
+
+ link->route_messages--;
+
+@@ -832,7 +831,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata
+ return 1;
+ }
+
+-static int link_enter_set_routes(Link *link) {
++static int link_request_set_routes(Link *link) {
+ Route *rt;
+ int r;
+
+@@ -840,11 +839,11 @@ static int link_enter_set_routes(Link *link) {
+ assert(link->network);
+ assert(link->addresses_configured);
+ assert(link->address_messages == 0);
+- assert(link->state == LINK_STATE_SETTING_ADDRESSES);
++ assert(link->state != _LINK_STATE_INVALID);
+
+- (void) link_set_routing_policy_rule(link);
++ link_set_state(link, LINK_STATE_CONFIGURING);
+
+- link_set_state(link, LINK_STATE_SETTING_ROUTES);
++ (void) link_set_routing_policy_rule(link);
+
+ LIST_FOREACH(routes, rt, link->network->static_routes) {
+ r = route_configure(rt, link, route_handler);
+@@ -893,7 +892,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
+ assert(link);
+ assert(link->ifname);
+ assert(link->address_messages > 0);
+- assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES,
++ assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
+ LINK_STATE_FAILED, LINK_STATE_LINGER));
+
+ link->address_messages--;
+@@ -910,7 +909,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda
+ if (link->address_messages == 0) {
+ log_link_debug(link, "Addresses set");
+ link->addresses_configured = true;
+- link_enter_set_routes(link);
++ link_request_set_routes(link);
+ }
+
+ return 1;
+@@ -1054,7 +1053,7 @@ static int link_set_bridge_fdb(Link *link) {
+ return 0;
+ }
+
+-static int link_enter_set_addresses(Link *link) {
++static int link_request_set_addresses(Link *link) {
+ AddressLabel *label;
+ Address *ad;
+ int r;
+@@ -1067,7 +1066,7 @@ static int link_enter_set_addresses(Link *link) {
+ if (r < 0)
+ return r;
+
+- link_set_state(link, LINK_STATE_SETTING_ADDRESSES);
++ link_set_state(link, LINK_STATE_CONFIGURING);
+
+ LIST_FOREACH(addresses, ad, link->network->static_addresses) {
+ r = address_configure(ad, link, address_handler, false);
+@@ -1212,7 +1211,7 @@ static int link_enter_set_addresses(Link *link) {
+
+ if (link->address_messages == 0) {
+ link->addresses_configured = true;
+- link_enter_set_routes(link);
++ link_request_set_routes(link);
+ } else
+ log_link_debug(link, "Setting addresses");
+
+@@ -2313,7 +2312,7 @@ static int link_joined(Link *link) {
+ if (!link_has_carrier(link) && !link->network->configure_without_carrier)
+ return 0;
+
+- return link_enter_set_addresses(link);
++ return link_request_set_addresses(link);
+ }
+
+ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+@@ -2351,7 +2350,7 @@ static int link_enter_join_netdev(Link *link) {
+ assert(link->network);
+ assert(link->state == LINK_STATE_PENDING);
+
+- link_set_state(link, LINK_STATE_ENSLAVING);
++ link_set_state(link, LINK_STATE_CONFIGURING);
+
+ link_dirty(link);
+
+@@ -3264,7 +3263,7 @@ static int link_carrier_gained(Link *link) {
+ return r;
+ }
+
+- r = link_enter_set_addresses(link);
++ r = link_request_set_addresses(link);
+ if (r < 0)
+ return r;
+ }
+@@ -3348,7 +3347,7 @@ int link_update(Link *link, sd_netlink_message *m) {
+ if (link->state == LINK_STATE_LINGER) {
+ link_ref(link);
+ log_link_info(link, "Link readded");
+- link_set_state(link, LINK_STATE_ENSLAVING);
++ link_set_state(link, LINK_STATE_CONFIGURING);
+
+ r = link_new_carrier_maps(link);
+ if (r < 0)
+@@ -3911,9 +3910,7 @@ void link_clean(Link *link) {
+
+ static const char* const link_state_table[_LINK_STATE_MAX] = {
+ [LINK_STATE_PENDING] = "pending",
+- [LINK_STATE_ENSLAVING] = "configuring",
+- [LINK_STATE_SETTING_ADDRESSES] = "configuring",
+- [LINK_STATE_SETTING_ROUTES] = "configuring",
++ [LINK_STATE_CONFIGURING] = "configuring",
+ [LINK_STATE_CONFIGURED] = "configured",
+ [LINK_STATE_UNMANAGED] = "unmanaged",
+ [LINK_STATE_FAILED] = "failed",
+diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
+index 2f37cc1598..5fd81be5a5 100644
+--- a/src/network/networkd-link.h
++++ b/src/network/networkd-link.h
+@@ -19,9 +19,7 @@
+
+ typedef enum LinkState {
+ LINK_STATE_PENDING,
+- LINK_STATE_ENSLAVING,
+- LINK_STATE_SETTING_ADDRESSES,
+- LINK_STATE_SETTING_ROUTES,
++ LINK_STATE_CONFIGURING,
+ LINK_STATE_CONFIGURED,
+ LINK_STATE_UNMANAGED,
+ LINK_STATE_FAILED,
+--
+2.20.1.97.g81188d93c3-goog
+
diff --git a/meta-phosphor/recipes-core/systemd/systemd/0003-networkd-Static-neighbor-support.patch b/meta-phosphor/recipes-core/systemd/systemd/0003-networkd-Static-neighbor-support.patch
new file mode 100644
index 000000000..86a563144
--- /dev/null
+++ b/meta-phosphor/recipes-core/systemd/systemd/0003-networkd-Static-neighbor-support.patch
@@ -0,0 +1,547 @@
+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
+
diff --git a/meta-phosphor/recipes-core/systemd/systemd_%.bbappend b/meta-phosphor/recipes-core/systemd/systemd_%.bbappend
index 213f32797..0d04f28f7 100644
--- a/meta-phosphor/recipes-core/systemd/systemd_%.bbappend
+++ b/meta-phosphor/recipes-core/systemd/systemd_%.bbappend
@@ -9,6 +9,11 @@ SRC_URI += "file://default.network"
SRC_URI += "file://0001-sd-bus-Don-t-automatically-add-ObjectManager.patch"
SRC_URI += "file://0006-core-fix-the-check-if-CONFIG_CGROUP_BPF-is-on.patch"
+# Static neighbor support included in v240 so remove when updated
+SRC_URI += "file://0001-networkd-Track-address-configuration.patch"
+SRC_URI += "file://0002-networkd-Use-only-a-generic-CONFIGURING-state.patch"
+SRC_URI += "file://0003-networkd-Static-neighbor-support.patch"
+
RRECOMMENDS_${PN} += "obmc-targets"
FILES_${PN} += "${systemd_unitdir}/network/default.network"