From c42ff3a1a7bfea66dc4655096c79bd481159091b Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" 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. --- 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 86f2e6dca9..240ab7f371 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -741,6 +741,9 @@ void link_check_ready(Link *link) { if (!link->network) return; + if (!link->addresses_configured) + return; + if (!link->static_routes_configured) return; @@ -854,6 +857,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); @@ -910,6 +915,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) if (link->address_messages == 0) { log_link_debug(link, "Addresses set"); + link->addresses_configured = true; link_enter_set_routes(link); } @@ -1181,9 +1187,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 3fdfa55b25..92ce7a2ec9 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -82,6 +82,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; From 289e6774d0daefe86771171b8f4f97b63354683e Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" 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. --- 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 240ab7f371..637bd3c554 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -719,7 +719,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"); @@ -824,9 +824,8 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { assert(link); 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--; @@ -846,7 +845,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return 1; } -static int link_enter_set_routes(Link *link) { +static int link_request_set_routes(Link *link) { enum { PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */ PHASE_GATEWAY, /* Second phase: Routes with a gateway */ @@ -859,12 +858,12 @@ 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); + + link_set_state(link, LINK_STATE_CONFIGURING); (void) link_set_routing_policy_rule(link); - link_set_state(link, LINK_STATE_SETTING_ROUTES); - /* First add the routes that enable us to talk to gateways, then add in the others that need a gateway. */ for (phase = 0; phase < _PHASE_MAX; phase++) LIST_FOREACH(routes, rt, link->network->static_routes) { @@ -899,7 +898,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) 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--; @@ -916,7 +915,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) 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; @@ -1033,7 +1032,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; @@ -1046,7 +1045,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); @@ -1189,7 +1188,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"); @@ -2276,7 +2275,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, Link *link) { @@ -2313,7 +2312,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); @@ -3426,7 +3425,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; } @@ -3509,7 +3508,7 @@ int link_update(Link *link, sd_netlink_message *m) { if (link->state == LINK_STATE_LINGER) { 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) @@ -4037,9 +4036,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 92ce7a2ec9..44f3c0bc2f 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, From e4a71bf36f422c3728b902aaa5846add7bbc0eb9 Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" 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. --- 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 | 237 ++++++++++++++++++ src/network/networkd-neighbor.h | 37 +++ src/network/networkd-network-gperf.gperf | 2 + src/network/networkd-network.c | 6 + src/network/networkd-network.h | 4 + .../25-neighbor-section.network | 6 + .../fuzz-network-parser/directives.network | 3 + .../conf/25-neighbor-section.network | 10 + test/test-network/systemd-networkd-tests.py | 12 + 13 files changed, 381 insertions(+) create mode 100644 src/network/networkd-neighbor.c create mode 100644 src/network/networkd-neighbor.h create mode 100644 test/fuzz/fuzz-network-parser/25-neighbor-section.network create mode 100644 test/test-network/conf/25-neighbor-section.network diff --git a/man/systemd.network.xml b/man/systemd.network.xml index f7234537d8..865b46f403 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -907,6 +907,31 @@ + + [Neighbor] Section Options + A [Neighbor] 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 [Neighbor] sections to configure + several static neighbors. + + + + Address= + + The IP address of the neighbor. + + + + MACAddress= + + The hardware address of the neighbor. + + + + + [IPv6AddressLabel] Section Options diff --git a/src/network/meson.build b/src/network/meson.build index d4fa27a288..8fe4854424 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -64,6 +64,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 637bd3c554..d575740e83 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -20,6 +20,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" @@ -744,6 +745,9 @@ void link_check_ready(Link *link) { if (!link->addresses_configured) return; + if (!link->neighbors_configured) + return; + if (!link->static_routes_configured) return; @@ -890,6 +894,34 @@ static int link_request_set_routes(Link *link) { return 0; } +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, Link *link) { int r; @@ -1047,6 +1079,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 44f3c0bc2f..00e68fdfaa 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -70,6 +70,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; @@ -97,6 +98,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..254a60bdc3 --- /dev/null +++ b/src/network/networkd-neighbor.c @@ -0,0 +1,237 @@ +/* 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_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + 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, Link *link) { + 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, link_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(SYNTHETIC_ERRNO(EINVAL), "Neighbor without Address= configured"); + if (!neighbor->mac_configured) + return log_error_errno(SYNTHETIC_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(SYNTHETIC_ERRNO(EINVAL), "Neighbor with invalid address family"); + } + + r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_handler, + link_netlink_destroy_callback, link); + 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..094bf7977e --- /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, link_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 945a3c5705..00605c0b92 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 178cdff82b..85340e7527 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -210,6 +210,7 @@ int network_load_one(Manager *manager, const char *filename) { "Link\0" "Network\0" "Address\0" + "Neighbor\0" "IPv6AddressLabel\0" "RoutingPolicyRule\0" "Route\0" @@ -300,6 +301,7 @@ void network_free(Network *network) { IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; RoutingPolicyRule *rule; FdbEntry *fdb_entry; + Neighbor *neighbor; AddressLabel *label; Prefix *prefix; Address *address; @@ -350,6 +352,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); @@ -362,6 +367,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 3592b563c0..7f64c38839 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -15,6 +15,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" @@ -235,6 +236,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); @@ -243,6 +245,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; @@ -250,6 +253,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; diff --git a/test/fuzz/fuzz-network-parser/25-neighbor-section.network b/test/fuzz/fuzz-network-parser/25-neighbor-section.network new file mode 100644 index 0000000000..dd750dd566 --- /dev/null +++ b/test/fuzz/fuzz-network-parser/25-neighbor-section.network @@ -0,0 +1,6 @@ +[Match] +Name=dummy98 + +[Neighbor] +Address=2004:da8:1:0:: +MACAddress=00:00:5e:00:02:00 diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 6afdd05e87..cab87bf98f 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -165,6 +165,9 @@ EmitDNS= EmitDomains= Managed= OtherInformation= +[Neighbor] +Address= +MacAddress= [IPv6AddressLabel] Label= Prefix= diff --git a/test/test-network/conf/25-neighbor-section.network b/test/test-network/conf/25-neighbor-section.network new file mode 100644 index 0000000000..d90802f44f --- /dev/null +++ b/test/test-network/conf/25-neighbor-section.network @@ -0,0 +1,10 @@ +[Match] +Name=dummy98 + +[Neighbor] +Address=192.168.10.1 +MACAddress=00:00:5e:00:02:65 + +[Neighbor] +Address=2004:da8:1:0::1 +MACAddress=00:00:5e:00:02:66 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 8fb1c7f38d..65cc43e8b6 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -525,6 +525,7 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): '25-fibrule-invert.network', '25-fibrule-port-range.network', '25-ipv6-address-label-section.network', + '25-neighbor-section.network', '25-link-section-unmanaged.network', '25-route-gateway.network', '25-route-gateway-on-link.network', @@ -775,6 +776,17 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, '2004:da8:1::/64') + def test_ipv6_neighbor(self): + self.copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev') + self.start_networkd() + + self.assertTrue(self.link_exits('dummy98')) + + output = subprocess.check_output(['ip', 'neigh', 'list']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT') + self.assertRegex(output, '2004:da8:1:0::1.*00:00:5e:00:02:66.*PERMANENT') + def test_sysctl(self): self.copy_unit_to_networkd_unit_path('25-sysctl.network', '12-dummy.netdev') self.start_networkd()