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>
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 0000000..c683669
--- /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 0000000..49c4cc3
--- /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 0000000..86a5631
--- /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 213f327..0d04f28 100644
--- a/meta-phosphor/recipes-core/systemd/systemd_%.bbappend
+++ b/meta-phosphor/recipes-core/systemd/systemd_%.bbappend
@@ -9,6 +9,11 @@
 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"