diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index ccc497de09b..751bf53d64e 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -372,7 +372,7 @@ int sd_rtnl_message_new_neigh(sd_netlink *rtnl, sd_netlink_message **ret, uint16 int r; assert_return(rtnl_message_type_is_neigh(nlmsg_type), -EINVAL); - assert_return(IN_SET(ndm_family, AF_INET, AF_INET6, PF_BRIDGE), -EINVAL); + assert_return(IN_SET(ndm_family, AF_UNSPEC, AF_INET, AF_INET6, PF_BRIDGE), -EINVAL); assert_return(ret, -EINVAL); r = message_new(rtnl, ret, nlmsg_type); diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index f3e267f0076..02e9e9a26b9 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -868,6 +868,13 @@ int sd_netlink_add_match( if (r < 0) return r; + break; + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEIGH); + if (r < 0) + return r; + break; case RTM_NEWROUTE: case RTM_DELROUTE: diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5c0149492f0..d9850578850 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -699,6 +699,9 @@ static Link *link_free(Link *link) { link->routes = set_free_with_destructor(link->routes, route_free); link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free); + link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free); + link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free); + link->addresses = set_free_with_destructor(link->addresses, address_free); link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free); @@ -2347,6 +2350,22 @@ static bool link_is_static_address_configured(Link *link, Address *address) { return false; } +static bool link_is_neighbor_configured(Link *link, Neighbor *neighbor) { + Neighbor *net_neighbor; + + assert(link); + assert(neighbor); + + if (!link->network) + return false; + + LIST_FOREACH(neighbors, net_neighbor, link->network->neighbors) + if (neighbor_equal(net_neighbor, neighbor)) + return true; + + return false; +} + static bool link_is_static_route_configured(Link *link, Route *route) { Route *net_route; @@ -2392,6 +2411,7 @@ static bool link_address_is_dynamic(Link *link, Address *address) { static int link_drop_foreign_config(Link *link) { Address *address; + Neighbor *neighbor; Route *route; Iterator i; int r; @@ -2418,6 +2438,18 @@ static int link_drop_foreign_config(Link *link) { } } + SET_FOREACH(neighbor, link->neighbors_foreign, i) { + if (link_is_neighbor_configured(link, neighbor)) { + r = neighbor_add(link, neighbor->family, &neighbor->in_addr, &neighbor->lladdr, neighbor->lladdr_size, NULL); + if (r < 0) + return r; + } else { + r = neighbor_remove(neighbor, link, NULL); + if (r < 0) + return r; + } + } + SET_FOREACH(route, link->routes_foreign, i) { /* do not touch routes managed by the kernel */ if (route->protocol == RTPROT_KERNEL) @@ -2456,6 +2488,7 @@ static int link_drop_foreign_config(Link *link) { static int link_drop_config(Link *link) { Address *address, *pool_address; + Neighbor *neighbor; Route *route; Iterator i; int r; @@ -2479,6 +2512,12 @@ static int link_drop_config(Link *link) { } } + SET_FOREACH(neighbor, link->neighbors, i) { + r = neighbor_remove(neighbor, link, NULL); + if (r < 0) + return r; + } + SET_FOREACH(route, link->routes, i) { /* do not touch routes managed by the kernel */ if (route->protocol == RTPROT_KERNEL) diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index cd88388015b..5c9d0327efe 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -75,6 +75,8 @@ typedef struct Link { Set *addresses; Set *addresses_foreign; + Set *neighbors; + Set *neighbors_foreign; Set *routes; Set *routes_foreign; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 74266ff12ae..cf5e211ba10 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -488,6 +488,184 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo return 1; } +static int manager_rtnl_process_neighbor_lladdr(sd_netlink_message *message, union lladdr_union *lladdr, size_t *size, char **str) { + int r; + + assert(message); + assert(lladdr); + assert(size); + assert(str); + + *str = NULL; + + r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->ip.in6), &lladdr->ip.in6); + if (r >= 0) { + *size = sizeof(lladdr->ip.in6); + if (in_addr_to_string(AF_INET6, &lladdr->ip, str) < 0) + log_warning_errno(r, "Could not print lower address: %m"); + return r; + } + + r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->mac), &lladdr->mac); + if (r >= 0) { + *size = sizeof(lladdr->mac); + *str = new(char, ETHER_ADDR_TO_STRING_MAX); + if (!*str) { + log_oom(); + return r; + } + ether_addr_to_string(&lladdr->mac, *str); + return r; + } + + r = sd_netlink_message_read(message, NDA_LLADDR, sizeof(lladdr->ip.in), &lladdr->ip.in); + if (r >= 0) { + *size = sizeof(lladdr->ip.in); + if (in_addr_to_string(AF_INET, &lladdr->ip, str) < 0) + log_warning_errno(r, "Could not print lower address: %m"); + return r; + } + + return r; +} + +int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { + Manager *m = userdata; + Link *link = NULL; + Neighbor *neighbor = NULL; + int ifindex, family, r; + uint16_t type, state; + union in_addr_union in_addr = IN_ADDR_NULL; + _cleanup_free_ char *addr_str = NULL; + union lladdr_union lladdr; + size_t lladdr_size = 0; + _cleanup_free_ char *lladdr_str = NULL; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_warning_errno(r, "rtnl: failed to receive neighbor message, ignoring: %m"); + + return 0; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); + return 0; + } else if (!IN_SET(type, RTM_NEWNEIGH, RTM_DELNEIGH)) { + log_warning("rtnl: received unexpected message type %u when processing neighbor, ignoring.", type); + return 0; + } + + r = sd_rtnl_message_neigh_get_state(message, &state); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received neighbor message with invalid state, ignoring: %m"); + return 0; + } else if (!FLAGS_SET(state, NUD_PERMANENT)) { + log_debug("rtnl: received non-static neighbor, ignoring."); + return 0; + } + + r = sd_rtnl_message_neigh_get_ifindex(message, &ifindex); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received neighbor message with invalid ifindex %d, ignoring.", ifindex); + return 0; + } + + r = link_get(m, ifindex, &link); + if (r < 0 || !link) { + /* when enumerating we might be out of sync, but we will get the neighbor again, so just + * ignore it */ + if (!m->enumerating) + log_warning("rtnl: received neighbor for link '%d' we don't know about, ignoring.", ifindex); + return 0; + } + + r = sd_rtnl_message_neigh_get_family(message, &family); + if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) { + log_link_warning(link, "rtnl: received neighbor message with invalid family, ignoring."); + return 0; + } + + switch (family) { + case AF_INET: + r = sd_netlink_message_read_in_addr(message, NDA_DST, &in_addr.in); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m"); + return 0; + } + + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(message, NDA_DST, &in_addr.in6); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m"); + return 0; + } + + break; + + default: + assert_not_reached("Received unsupported address family"); + } + + if (in_addr_to_string(family, &in_addr, &addr_str) < 0) + log_link_warning_errno(link, r, "Could not print address: %m"); + + r = manager_rtnl_process_neighbor_lladdr(message, &lladdr, &lladdr_size, &lladdr_str); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received neighbor message with invalid lladdr, ignoring: %m"); + return 0; + } + + (void) neighbor_get(link, family, &in_addr, &lladdr, lladdr_size, &neighbor); + + switch (type) { + case RTM_NEWNEIGH: + if (neighbor) + log_link_debug(link, "Remembering neighbor: %s->%s", + strnull(addr_str), strnull(lladdr_str)); + else { + /* A neighbor appeared that we did not request */ + r = neighbor_add_foreign(link, family, &in_addr, &lladdr, lladdr_size, &neighbor); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to remember foreign neighbor %s->%s, ignoring: %m", + strnull(addr_str), strnull(lladdr_str)); + return 0; + } else + log_link_debug(link, "Remembering foreign neighbor: %s->%s", + strnull(addr_str), strnull(lladdr_str)); + } + + break; + + case RTM_DELNEIGH: + if (neighbor) { + log_link_debug(link, "Forgetting neighbor: %s->%s", + strnull(addr_str), strnull(lladdr_str)); + (void) neighbor_free(neighbor); + } else + log_link_info(link, "Kernel removed a neighbor we don't remember: %s->%s, ignoring.", + strnull(addr_str), strnull(lladdr_str)); + + break; + + default: + assert_not_reached("Received invalid RTNL message type"); + } + + return 1; +} + int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { _cleanup_free_ char *buf = NULL; Manager *m = userdata; @@ -1014,6 +1192,14 @@ static int manager_connect_rtnl(Manager *m) { if (r < 0) return r; + r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWNEIGH, &manager_rtnl_process_neighbor, NULL, m, "network-rtnl_process_neighbor"); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELNEIGH, &manager_rtnl_process_neighbor, NULL, m, "network-rtnl_process_neighbor"); + if (r < 0) + return r; + r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route"); if (r < 0) return r; @@ -1555,6 +1741,41 @@ int manager_rtnl_enumerate_addresses(Manager *m) { return r; } +int manager_rtnl_enumerate_neighbors(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *neigh; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_neigh(m->rtnl, &req, RTM_GETNEIGH, 0, AF_UNSPEC); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (neigh = reply; neigh; neigh = sd_netlink_message_next(neigh)) { + int k; + + m->enumerating = true; + + k = manager_rtnl_process_neighbor(m->rtnl, neigh, m); + if (k < 0) + r = k; + + m->enumerating = false; + } + + return r; +} + int manager_rtnl_enumerate_routes(Manager *m) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; sd_netlink_message *route; diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 5422b56dc26..f5166dad126 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -77,10 +77,12 @@ bool manager_should_reload(Manager *m); int manager_rtnl_enumerate_links(Manager *m); int manager_rtnl_enumerate_addresses(Manager *m); +int manager_rtnl_enumerate_neighbors(Manager *m); int manager_rtnl_enumerate_routes(Manager *m); int manager_rtnl_enumerate_rules(Manager *m); int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata); +int manager_rtnl_process_neighbor(sd_netlink *nl, sd_netlink_message *message, void *userdata); int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata); int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata); diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 191f6e33206..537f6be9e16 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -11,6 +11,7 @@ #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-neighbor.h" +#include "set.h" void neighbor_free(Neighbor *neighbor) { if (!neighbor) @@ -21,10 +22,15 @@ void neighbor_free(Neighbor *neighbor) { assert(neighbor->network->n_neighbors > 0); neighbor->network->n_neighbors--; - if (neighbor->section) { + if (neighbor->section) hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section); - network_config_section_free(neighbor->section); - } + } + + network_config_section_free(neighbor->section); + + if (neighbor->link) { + set_remove(neighbor->link->neighbors, neighbor); + set_remove(neighbor->link->neighbors_foreign, neighbor); } free(neighbor); @@ -81,9 +87,10 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned return 0; } -static int neighbor_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; + assert(m); assert(link); assert(link->neighbor_messages > 0); @@ -137,7 +144,7 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand if (r < 0) return log_error_errno(r, "Could not append NDA_DST attribute: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_handler, + r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_configure_handler, link_netlink_destroy_callback, link); if (r < 0) return log_error_errno(r, "Could not send rtnetlink message: %m"); @@ -145,9 +152,217 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand link->neighbor_messages++; link_ref(link); + r = neighbor_add(link, neighbor->family, &neighbor->in_addr, &neighbor->lladdr, neighbor->lladdr_size, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not add neighbor: %m"); + return 0; } +static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -ESRCH) + /* Neighbor may not exist because it already got deleted, ignore that. */ + log_link_warning_errno(link, r, "Could not remove neighbor: %m"); + + return 1; +} + +int neighbor_remove(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); + + r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_DELNEIGH, + link->ifindex, neighbor->family); + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_DELNEIGH message: %m"); + + r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr); + if (r < 0) + return log_error_errno(r, "Could not append NDA_DST attribute: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_remove_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + +static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) { + assert(neighbor); + + siphash24_compress(&neighbor->family, sizeof(neighbor->family), state); + + switch (neighbor->family) { + case AF_INET: + case AF_INET6: + /* Equality of neighbors are given by the pair (addr,lladdr) */ + siphash24_compress(&neighbor->in_addr, FAMILY_ADDRESS_SIZE(neighbor->family), state); + siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state); + break; + default: + /* treat any other address family as AF_UNSPEC */ + break; + } +} + +static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { + int r; + + r = CMP(a->family, b->family); + if (r != 0) + return r; + + r = CMP(a->lladdr_size, b->lladdr_size); + if (r != 0) + return r; + + switch (a->family) { + case AF_INET: + case AF_INET6: + r = memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family)); + if (r != 0) + return r; + } + + return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size); +} + +DEFINE_PRIVATE_HASH_OPS(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func); + +int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) { + Neighbor neighbor, *existing; + + assert(link); + assert(addr); + assert(lladdr); + + neighbor = (Neighbor) { + .family = family, + .in_addr = *addr, + .lladdr = *lladdr, + .lladdr_size = lladdr_size, + }; + + existing = set_get(link->neighbors, &neighbor); + if (existing) { + if (ret) + *ret = existing; + return 1; + } + + existing = set_get(link->neighbors_foreign, &neighbor); + if (existing) { + if (ret) + *ret = existing; + return 0; + } + + return -ENOENT; +} + +static int neighbor_add_internal(Link *link, Set **neighbors, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) { + _cleanup_(neighbor_freep) Neighbor *neighbor = NULL; + int r; + + assert(link); + assert(neighbors); + assert(addr); + assert(lladdr); + + neighbor = new(Neighbor, 1); + if (!neighbor) + return -ENOMEM; + + *neighbor = (Neighbor) { + .family = family, + .in_addr = *addr, + .lladdr = *lladdr, + .lladdr_size = lladdr_size, + }; + + r = set_ensure_allocated(neighbors, &neighbor_hash_ops); + if (r < 0) + return r; + + r = set_put(*neighbors, neighbor); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + + neighbor->link = link; + + if (ret) + *ret = neighbor; + + neighbor = NULL; + + return 0; +} + +int neighbor_add(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) { + Neighbor *neighbor; + int r; + + r = neighbor_get(link, family, addr, lladdr, lladdr_size, &neighbor); + if (r == -ENOENT) { + /* Neighbor doesn't exist, make a new one */ + r = neighbor_add_internal(link, &link->neighbors, family, addr, lladdr, lladdr_size, &neighbor); + if (r < 0) + return r; + } else if (r == 0) { + /* Neighbor is foreign, claim it as recognized */ + r = set_ensure_allocated(&link->neighbors, &neighbor_hash_ops); + if (r < 0) + return r; + + r = set_put(link->neighbors, neighbor); + if (r < 0) + return r; + + set_remove(link->neighbors_foreign, neighbor); + } else if (r == 1) { + /* Neighbor already exists */ + } else + return r; + + if (ret) + *ret = neighbor; + return 0; +} + +int neighbor_add_foreign(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) { + return neighbor_add_internal(link, &link->neighbors_foreign, family, addr, lladdr, lladdr_size, ret); +} + +bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) { + if (n1 == n2) + return true; + + if (!n1 || !n2) + return false; + + return neighbor_compare_func(n1, n2) == 0; +} + int neighbor_section_verify(Neighbor *neighbor) { if (section_is_invalid(neighbor->section)) return -EINVAL; diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index 09ccb55274e..97ee1f6d73e 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -15,6 +15,11 @@ typedef struct Neighbor Neighbor; #include "networkd-network.h" #include "networkd-util.h" +union lladdr_union { + struct ether_addr mac; + union in_addr_union ip; +}; + struct Neighbor { Network *network; Link *link; @@ -22,10 +27,7 @@ struct Neighbor { int family; union in_addr_union in_addr; - union { - struct ether_addr mac; - union in_addr_union ip; - } lladdr; + union lladdr_union lladdr; size_t lladdr_size; LIST_FIELDS(Neighbor, neighbors); @@ -36,6 +38,12 @@ void neighbor_free(Neighbor *neighbor); DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free); int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback); +int neighbor_remove(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback); + +int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret); +int neighbor_add(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret); +int neighbor_add_foreign(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret); +bool neighbor_equal(const Neighbor *n1, const Neighbor *n2); int neighbor_section_verify(Neighbor *neighbor); diff --git a/src/network/networkd.c b/src/network/networkd.c index 090f3777a5a..38bd9ff1ffe 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -95,6 +95,10 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Could not enumerate addresses: %m"); + r = manager_rtnl_enumerate_neighbors(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate neighbors: %m"); + r = manager_rtnl_enumerate_routes(m); if (r < 0) return log_error_errno(r, "Could not enumerate routes: %m"); diff --git a/test/test-network/conf/25-neighbor-next.network b/test/test-network/conf/25-neighbor-next.network new file mode 100644 index 00000000000..d0591d58758 --- /dev/null +++ b/test/test-network/conf/25-neighbor-next.network @@ -0,0 +1,9 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no + +[Neighbor] +Address=192.168.10.1 +LinkLayerAddress=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 f133bf4462f..593a9c976ab 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -1422,6 +1422,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '25-ip6gre-tunnel-remote-any.netdev', '25-ipv6-address-label-section.network', '25-neighbor-section.network', + '25-neighbor-next.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network', '25-neighbor-ip.network', @@ -1699,11 +1700,34 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): start_networkd() self.wait_online(['dummy98:degraded'], timeout='40s') + print('### ip neigh list dev dummy98') output = check_output('ip neigh list dev dummy98') print(output) self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT') self.assertRegex(output, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT') + def test_neighbor_reconfigure(self): + copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev') + start_networkd() + self.wait_online(['dummy98:degraded'], timeout='40s') + + print('### ip neigh list dev dummy98') + output = check_output('ip neigh list dev dummy98') + print(output) + self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT') + self.assertRegex(output, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT') + + remove_unit_from_networkd_path(['25-neighbor-section.network']) + copy_unit_to_networkd_unit_path('25-neighbor-next.network') + restart_networkd(3) + self.wait_online(['dummy98:degraded'], timeout='40s') + print('### ip neigh list dev dummy98') + output = check_output('ip neigh list dev dummy98') + print(output) + self.assertNotRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT') + self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:66.*PERMANENT') + self.assertNotRegex(output, '2004:da8:1::1.*PERMANENT') + def test_neighbor_gre(self): copy_unit_to_networkd_unit_path('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network', '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')