]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #13303 from yuwata/bootctl-segfaults
authorFrantisek Sumsal <frantisek@sumsal.cz>
Mon, 12 Aug 2019 19:16:51 +0000 (19:16 +0000)
committerGitHub <noreply@github.com>
Mon, 12 Aug 2019 19:16:51 +0000 (19:16 +0000)
bootctl: fix segfaults

src/libsystemd/sd-netlink/rtnl-message.c
src/libsystemd/sd-netlink/sd-netlink.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-neighbor.c
src/network/networkd-neighbor.h
src/network/networkd.c
test/test-network/conf/25-neighbor-next.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

index ccc497de09b78efcee69fbfbf3b9716d6099c35c..751bf53d64e9e49a3e1779c1087ad111726db598 100644 (file)
@@ -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);
index f3e267f00767e03f82befa5619ba1cdfb751f30e..02e9e9a26b9481f72c18300cf5b8ec12c4258b8c 100644 (file)
@@ -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:
index 5c0149492f0867e7805f3d43e52046ac6b704e4a..d9850578850e3edacd21a598ce831d42d8f45e40 100644 (file)
@@ -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)
index cd88388015b2220d9d82938ec8c28a9ffd48a81d..5c9d0327efedf6ce46036e3e847073fd007b8deb 100644 (file)
@@ -75,6 +75,8 @@ typedef struct Link {
 
         Set *addresses;
         Set *addresses_foreign;
+        Set *neighbors;
+        Set *neighbors_foreign;
         Set *routes;
         Set *routes_foreign;
 
index 74266ff12ae002f68f98999b0466f19033ee4d3a..cf5e211ba1028a1c4211d8479312743be27af341 100644 (file)
@@ -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;
index 5422b56dc265892c095c782941a6891b7e2f9248..f5166dad126cc342dc2dd206e0f1e2bd2b78b0d4 100644 (file)
@@ -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);
 
index 191f6e33206d779b7950bdb5b4a0722ca5301ce0..537f6be9e16c7b9cd4bea5083e5b4b3830a5bb1a 100644 (file)
@@ -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;
index 09ccb55274ea3a592210e2c4c3baf267c7fa0249..97ee1f6d73ea4931815dc7e7d431fdbbb6ddf475 100644 (file)
@@ -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);
 
index 090f3777a5ae2de7304c05f25c0e8119b018bf3d..38bd9ff1ffe0b2ce95f5a7120cd1fdbbe28c0590 100644 (file)
@@ -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 (file)
index 0000000..d0591d5
--- /dev/null
@@ -0,0 +1,9 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+
+[Neighbor]
+Address=192.168.10.1
+LinkLayerAddress=00:00:5e:00:02:66
index f133bf4462fc94886d817c9fe881f11fd7ab4839..593a9c976ab343767025fe8831651fdc804b77c3 100755 (executable)
@@ -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')