diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index d6bf31efc7..0d9a57ee9e 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -862,11 +862,12 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ return 0; } -int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) { - int r; +int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data) { void *attr_data; + int r; assert_return(m, -EINVAL); + assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL); r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); if (r < 0) @@ -875,35 +876,35 @@ int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; - else if ((size_t) r < sizeof(struct in_addr)) + else if ((size_t) r < FAMILY_ADDRESS_SIZE(family)) return -EIO; if (data) - memcpy(data, attr_data, sizeof(struct in_addr)); + memcpy(data, attr_data, FAMILY_ADDRESS_SIZE(family)); return 0; } -int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) { +int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) { + union in_addr_union u; int r; - void *attr_data; - assert_return(m, -EINVAL); + r = netlink_message_read_in_addr_union(m, type, AF_INET, &u); + if (r >= 0 && data) + *data = u.in; - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; + return r; +} - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t) r < sizeof(struct in6_addr)) - return -EIO; +int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) { + union in_addr_union u; + int r; - if (data) - memcpy(data, attr_data, sizeof(struct in6_addr)); + r = netlink_message_read_in_addr_union(m, type, AF_INET6, &u); + if (r >= 0 && data) + *data = u.in6; - return 0; + return r; } int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret) { diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index ddf5686f13..fa17e7dae7 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -77,18 +77,20 @@ int rtnl_log_create_error(int r); userdata, 0, __func__); \ }) -#define netlink_add_match(nl, ret_slot, metch, callback, destroy_callback, userdata) \ +#define netlink_add_match(nl, ret_slot, match, callback, destroy_callback, userdata, description) \ ({ \ int (*_callback_)(sd_netlink *, sd_netlink_message *, typeof(userdata)) = callback; \ void (*_destroy_)(typeof(userdata)) = destroy_callback; \ sd_netlink_add_match(nl, ret_slot, match, \ (sd_netlink_message_handler_t) _callback_, \ (sd_netlink_destroy_t) _destroy_, \ - userdata, __func__); \ + userdata, description); \ }) int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data); int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data); +int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data); + void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length); int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length); diff --git a/src/network/meson.build b/src/network/meson.build index 28941b4468..c2a197162f 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -111,6 +111,8 @@ sources = files(''' networkd-speed-meter.h networkd-sriov.c networkd-sriov.h + networkd-sysctl.c + networkd-sysctl.h networkd-util.c networkd-util.h networkd-wifi.c diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c index 9d3b8d6e4c..9f0e6f25c1 100644 --- a/src/network/netdev/macsec.c +++ b/src/network/netdev/macsec.c @@ -13,7 +13,6 @@ #include "memory-util.h" #include "netlink-util.h" #include "network-internal.h" -#include "networkd-address.h" #include "networkd-manager.h" #include "path-util.h" #include "socket-util.h" diff --git a/src/network/netdev/macsec.h b/src/network/netdev/macsec.h index 2a3443a6d4..26ad2b7cca 100644 --- a/src/network/netdev/macsec.h +++ b/src/network/netdev/macsec.h @@ -4,6 +4,7 @@ #include #include +#include "ether-addr-util.h" #include "in-addr-util.h" #include "netdev.h" #include "networkd-util.h" diff --git a/src/network/networkd-address-label.c b/src/network/networkd-address-label.c index 0d53aa9042..66b192256e 100644 --- a/src/network/networkd-address-label.c +++ b/src/network/networkd-address-label.c @@ -4,31 +4,28 @@ #include #include "alloc-util.h" -#include "conf-parser.h" -#include "networkd-address-label.h" #include "netlink-util.h" +#include "networkd-address-label.h" +#include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-network.h" #include "parse-util.h" -#include "socket-util.h" -void address_label_free(AddressLabel *label) { +AddressLabel *address_label_free(AddressLabel *label) { if (!label) - return; + return NULL; if (label->network) { - LIST_REMOVE(labels, label->network->address_labels, label); - assert(label->network->n_address_labels > 0); - label->network->n_address_labels--; - - if (label->section) { - hashmap_remove(label->network->address_labels_by_section, label->section); - network_config_section_free(label->section); - } + assert(label->section); + hashmap_remove(label->network->address_labels_by_section, label->section); } - free(label); + network_config_section_free(label->section); + return mfree(label); } +DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free); + static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) { _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; _cleanup_(address_label_freep) AddressLabel *label = NULL; @@ -36,19 +33,17 @@ static int address_label_new_static(Network *network, const char *filename, unsi assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; - label = hashmap_get(network->address_labels_by_section, n); - if (label) { - *ret = TAKE_PTR(label); - - return 0; - } + label = hashmap_get(network->address_labels_by_section, n); + if (label) { + *ret = TAKE_PTR(label); + return 0; } label = new(AddressLabel, 1); @@ -57,25 +52,18 @@ static int address_label_new_static(Network *network, const char *filename, unsi *label = (AddressLabel) { .network = network, + .section = TAKE_PTR(n), }; - LIST_APPEND(labels, network->address_labels, label); - network->n_address_labels++; + r = hashmap_ensure_allocated(&network->address_labels_by_section, &network_config_hash_ops); + if (r < 0) + return r; - if (filename) { - label->section = TAKE_PTR(n); - - r = hashmap_ensure_allocated(&network->address_labels_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->address_labels_by_section, label->section, label); - if (r < 0) - return r; - } + r = hashmap_put(network->address_labels_by_section, label->section, label); + if (r < 0) + return r; *ret = TAKE_PTR(label); - return 0; } @@ -107,12 +95,7 @@ static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } -int address_label_configure( - AddressLabel *label, - Link *link, - link_netlink_message_handler_t callback, - bool update) { - +static int address_label_configure(AddressLabel *label, Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -140,7 +123,7 @@ int address_label_configure( return log_link_error_errno(link, r, "Could not append IFA_ADDRESS attribute: %m"); r = netlink_call_async(link->manager->rtnl, NULL, req, - callback ?: address_label_handler, + address_label_handler, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); @@ -150,6 +133,34 @@ int address_label_configure( return 0; } +int link_set_address_labels(Link *link) { + AddressLabel *label; + int r; + + assert(link); + assert(link->network); + + HASHMAP_FOREACH(label, link->network->address_labels_by_section) { + r = address_label_configure(label, link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set address label: %m"); + + link->address_label_messages++; + } + + return 0; +} + +void network_drop_invalid_address_labels(Network *network) { + AddressLabel *label; + + assert(network); + + HASHMAP_FOREACH(label, network->address_labels_by_section) + if (section_is_invalid(label->section)) + address_label_free(label); +} + int config_parse_address_label_prefix(const char *unit, const char *filename, unsigned line, diff --git a/src/network/networkd-address-label.h b/src/network/networkd-address-label.h index 595072a17e..b92828c72e 100644 --- a/src/network/networkd-address-label.h +++ b/src/network/networkd-address-label.h @@ -2,38 +2,28 @@ #pragma once #include -#include #include "conf-parser.h" #include "in-addr-util.h" - -typedef struct AddressLabel AddressLabel; - -#include "networkd-link.h" -#include "networkd-network.h" #include "networkd-util.h" typedef struct Network Network; typedef struct Link Link; -typedef struct NetworkConfigSection NetworkConfigSection; -struct AddressLabel { +typedef struct AddressLabel { Network *network; NetworkConfigSection *section; unsigned char prefixlen; uint32_t label; - union in_addr_union in_addr; +} AddressLabel; - LIST_FIELDS(AddressLabel, labels); -}; +AddressLabel *address_label_free(AddressLabel *label); -void address_label_free(AddressLabel *label); +void network_drop_invalid_address_labels(Network *network); -DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free); - -int address_label_configure(AddressLabel *address, Link *link, link_netlink_message_handler_t callback, bool update); +int link_set_address_labels(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_address_label); CONFIG_PARSER_PROTOTYPE(config_parse_address_label_prefix); diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index b4a94f0728..c732b6c56e 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -2,6 +2,7 @@ #include "alloc-util.h" #include "networkd-address-pool.h" +#include "networkd-address.h" #include "networkd-manager.h" #include "set.h" #include "string-util.h" @@ -10,15 +11,14 @@ static int address_pool_new( Manager *m, - AddressPool **ret, int family, const union in_addr_union *u, unsigned prefixlen) { - AddressPool *p; + _cleanup_free_ AddressPool *p = NULL; + int r; assert(m); - assert(ret); assert(u); p = new(AddressPool, 1); @@ -32,15 +32,16 @@ static int address_pool_new( .in_addr = *u, }; - LIST_PREPEND(address_pools, m->address_pools, p); + r = ordered_set_ensure_put(&m->address_pools, NULL, p); + if (r < 0) + return r; - *ret = p; + TAKE_PTR(p); return 0; } -int address_pool_new_from_string( +static int address_pool_new_from_string( Manager *m, - AddressPool **ret, int family, const char *p, unsigned prefixlen) { @@ -49,25 +50,38 @@ int address_pool_new_from_string( int r; assert(m); - assert(ret); assert(p); r = in_addr_from_string(family, p, &u); if (r < 0) return r; - return address_pool_new(m, ret, family, &u, prefixlen); + return address_pool_new(m, family, &u, prefixlen); } -void address_pool_free(AddressPool *p) { +int address_pool_setup_default(Manager *m) { + int r; - if (!p) - return; + assert(m); - if (p->manager) - LIST_REMOVE(address_pools, p->manager->address_pools, p); + /* Add in the well-known private address ranges. */ + r = address_pool_new_from_string(m, AF_INET6, "fd00::", 8); + if (r < 0) + return r; - free(p); + r = address_pool_new_from_string(m, AF_INET, "192.168.0.0", 16); + if (r < 0) + return r; + + r = address_pool_new_from_string(m, AF_INET, "172.16.0.0", 12); + if (r < 0) + return r; + + r = address_pool_new_from_string(m, AF_INET, "10.0.0.0", 8); + if (r < 0) + return r; + + return 0; } static bool address_pool_prefix_is_taken( @@ -94,7 +108,7 @@ static bool address_pool_prefix_is_taken( } /* Don't clash with addresses already pulled from the pool, but not assigned yet */ - LIST_FOREACH(addresses, a, l->pool_addresses) { + SET_FOREACH(a, l->pool_addresses) { if (a->family != p->family) continue; @@ -107,7 +121,7 @@ static bool address_pool_prefix_is_taken( ORDERED_HASHMAP_FOREACH(n, p->manager->networks) { Address *a; - LIST_FOREACH(addresses, a, n->static_addresses) { + ORDERED_HASHMAP_FOREACH(a, n->addresses_by_section) { if (a->family != p->family) continue; @@ -119,7 +133,7 @@ static bool address_pool_prefix_is_taken( return false; } -int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found) { +static int address_pool_acquire_one(AddressPool *p, int family, unsigned prefixlen, union in_addr_union *found) { union in_addr_union u; unsigned i; int r; @@ -128,6 +142,9 @@ int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union assert(prefixlen > 0); assert(found); + if (p->family != family) + return 0; + if (p->prefixlen >= prefixlen) return 0; @@ -153,3 +170,21 @@ int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union return 0; } + +int address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) { + AddressPool *p; + int r; + + assert(m); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(prefixlen > 0); + assert(found); + + ORDERED_SET_FOREACH(p, m->address_pools) { + r = address_pool_acquire_one(p, family, prefixlen, found); + if (r != 0) + return r; + } + + return 0; +} diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h index 7db1c4f26c..c53fe7407f 100644 --- a/src/network/networkd-address-pool.h +++ b/src/network/networkd-address-pool.h @@ -1,25 +1,17 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -typedef struct AddressPool AddressPool; - #include "in-addr-util.h" -#include "list.h" typedef struct Manager Manager; -struct AddressPool { +typedef struct AddressPool { Manager *manager; int family; unsigned prefixlen; - union in_addr_union in_addr; +} AddressPool; - LIST_FIELDS(AddressPool, address_pools); -}; - -int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen); -void address_pool_free(AddressPool *p); - -int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found); +int address_pool_setup_default(Manager *m); +int address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 76c91ef9dd..7407eeec9b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -3,20 +3,16 @@ #include #include "alloc-util.h" -#include "conf-parser.h" #include "firewall-util.h" #include "memory-util.h" -#include "missing_network.h" #include "netlink-util.h" +#include "networkd-address-pool.h" #include "networkd-address.h" #include "networkd-manager.h" -#include "networkd-ndisc.h" +#include "networkd-network.h" #include "parse-util.h" -#include "set.h" -#include "socket-util.h" #include "string-util.h" #include "strv.h" -#include "utf8.h" #define ADDRESSES_PER_LINK_MAX 2048U #define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U @@ -52,7 +48,6 @@ int address_new(Address **ret) { .cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME, .cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME, .duplicate_address_detection = ADDRESS_FAMILY_IPV6, - .prefix_route = true, }; *ret = TAKE_PTR(address); @@ -67,22 +62,20 @@ static int address_new_static(Network *network, const char *filename, unsigned s assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; - address = hashmap_get(network->addresses_by_section, n); - if (address) { - *ret = TAKE_PTR(address); - - return 0; - } + address = ordered_hashmap_get(network->addresses_by_section, n); + if (address) { + *ret = TAKE_PTR(address); + return 0; } - if (network->n_static_addresses >= STATIC_ADDRESSES_PER_NETWORK_MAX) + if (ordered_hashmap_size(network->addresses_by_section) >= STATIC_ADDRESSES_PER_NETWORK_MAX) return -E2BIG; r = address_new(&address); @@ -90,40 +83,30 @@ static int address_new_static(Network *network, const char *filename, unsigned s return r; address->network = network; - LIST_APPEND(addresses, network->static_addresses, address); - network->n_static_addresses++; + address->section = TAKE_PTR(n); - if (filename) { - address->section = TAKE_PTR(n); + r = ordered_hashmap_ensure_allocated(&network->addresses_by_section, &network_config_hash_ops); + if (r < 0) + return r; - r = hashmap_ensure_allocated(&network->addresses_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->addresses_by_section, address->section, address); - if (r < 0) - return r; - } + r = ordered_hashmap_put(network->addresses_by_section, address->section, address); + if (r < 0) + return r; *ret = TAKE_PTR(address); - return 0; } -void address_free(Address *address) { +Address *address_free(Address *address) { if (!address) - return; + return NULL; if (address->network) { - LIST_REMOVE(addresses, address->network->static_addresses, address); - assert(address->network->n_static_addresses > 0); - address->network->n_static_addresses--; - - if (address->section) - hashmap_remove(address->network->addresses_by_section, address->section); + assert(address->section); + ordered_hashmap_remove(address->network->addresses_by_section, address->section); } - if (address->link && !address->acd) { + if (address->link) { NDiscAddress *n; set_remove(address->link->addresses, address); @@ -149,7 +132,7 @@ void address_free(Address *address) { network_config_section_free(address->section); free(address->label); - free(address); + return mfree(address); } static uint32_t address_prefix(const Address *a) { @@ -232,29 +215,61 @@ bool address_equal(Address *a1, Address *a2) { return address_compare_func(a1, a2) == 0; } -static int address_establish(Address *address, Link *link) { - bool masq; +static int address_copy(Address *dest, const Address *src) { + int r; + + assert(dest); + assert(src); + + r = free_and_strdup(&dest->label, src->label); + if (r < 0) + return r; + + dest->family = src->family; + dest->prefixlen = src->prefixlen; + dest->scope = src->scope; + dest->flags = src->flags; + dest->broadcast = src->broadcast; + dest->cinfo = src->cinfo; + dest->in_addr = src->in_addr; + dest->in_addr_peer = src->in_addr_peer; + dest->duplicate_address_detection = src->duplicate_address_detection; + + return 0; +} + +static int address_set_masquerade(Address *address, bool add) { + union in_addr_union masked; int r; assert(address); - assert(link); + assert(address->link); - masq = link->network && - link->network->ip_masquerade && - address->family == AF_INET && - address->scope < RT_SCOPE_LINK; + if (!address->link->network) + return 0; - /* Add firewall entry if this is requested */ - if (address->ip_masquerade_done != masq) { - union in_addr_union masked = address->in_addr; - in_addr_mask(address->family, &masked, address->prefixlen); + if (!address->link->network->ip_masquerade) + return 0; - r = fw_add_masquerade(masq, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); - if (r < 0) - return r; + if (address->family != AF_INET) + return 0; - address->ip_masquerade_done = masq; - } + if (address->scope >= RT_SCOPE_LINK) + return 0; + + if (address->ip_masquerade_done == add) + return 0; + + masked = address->in_addr; + r = in_addr_mask(address->family, &masked, address->prefixlen); + if (r < 0) + return r; + + r = fw_add_masquerade(add, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); + if (r < 0) + return r; + + address->ip_masquerade_done = add; return 0; } @@ -295,11 +310,11 @@ static int address_add_internal(Link *link, Set **addresses, return 0; } -int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { +static int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { return address_add_internal(link, &link->addresses_foreign, family, in_addr, prefixlen, ret); } -int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { +static int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { Address *address; int r; @@ -328,28 +343,7 @@ int address_add(Link *link, int family, const union in_addr_union *in_addr, unsi return 0; } -static int address_release(Address *address) { - int r; - - assert(address); - assert(address->link); - - /* Remove masquerading firewall entry if it was added */ - if (address->ip_masquerade_done) { - union in_addr_union masked = address->in_addr; - in_addr_mask(address->family, &masked, address->prefixlen); - - r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); - if (r < 0) - return r; - - address->ip_masquerade_done = false; - } - - return 0; -} - -int address_update( +static int address_update( Address *address, unsigned char flags, unsigned char scope, @@ -394,7 +388,7 @@ int address_update( return 0; } -int address_drop(Address *address) { +static int address_drop(Address *address) { Link *link; bool ready; int r; @@ -404,7 +398,7 @@ int address_drop(Address *address) { ready = address_is_ready(address); link = address->link; - r = address_release(address); + r = address_set_masquerade(address, false); if (r < 0) log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m"); @@ -542,7 +536,192 @@ int address_remove( return 0; } -static int address_acquire(Link *link, Address *original, Address **ret) { +static bool link_is_static_address_configured(Link *link, Address *address) { + Address *net_address; + + assert(link); + assert(address); + + if (!link->network) + return false; + + ORDERED_HASHMAP_FOREACH(net_address, link->network->addresses_by_section) + if (address_equal(net_address, address)) + return true; + else if (address->family == AF_INET6 && net_address->family == AF_INET6 && + in_addr_equal(AF_INET6, &address->in_addr, &net_address->in_addr_peer) > 0) + return true; + + return false; +} + +static bool link_address_is_dynamic(Link *link, Address *address) { + Route *route; + + assert(link); + assert(address); + + if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME) + return true; + + /* Even when the address is leased from a DHCP server, networkd assign the address + * without lifetime when KeepConfiguration=dhcp. So, let's check that we have + * corresponding routes with RTPROT_DHCP. */ + SET_FOREACH(route, link->routes_foreign) { + if (route->protocol != RTPROT_DHCP) + continue; + + if (address->family != route->family) + continue; + + if (in_addr_equal(address->family, &address->in_addr, &route->prefsrc)) + return true; + } + + return false; +} + +static int link_enumerate_ipv6_tentative_addresses(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *addr; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + + r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_GETADDR, 0, AF_INET6); + if (r < 0) + return r; + + r = sd_netlink_call(link->manager->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (addr = reply; addr; addr = sd_netlink_message_next(addr)) { + unsigned char flags; + int ifindex; + + r = sd_rtnl_message_addr_get_ifindex(addr, &ifindex); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: invalid ifindex, ignoring: %m"); + continue; + } else if (link->ifindex != ifindex) + continue; + + r = sd_rtnl_message_addr_get_flags(addr, &flags); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address message with invalid flags, ignoring: %m"); + continue; + } else if (!(flags & IFA_F_TENTATIVE)) + continue; + + log_link_debug(link, "Found tentative ipv6 link-local address"); + (void) manager_rtnl_process_address(link->manager->rtnl, addr, link->manager); + } + + return 0; +} + +int link_drop_foreign_addresses(Link *link) { + Address *address; + int k, r = 0; + + assert(link); + + /* The kernel doesn't notify us about tentative addresses; + * so if ipv6ll is disabled, we need to enumerate them now so we can drop them below */ + if (!link_ipv6ll_enabled(link)) { + r = link_enumerate_ipv6_tentative_addresses(link); + if (r < 0) + return r; + } + + SET_FOREACH(address, link->addresses_foreign) { + /* we consider IPv6LL addresses to be managed by the kernel */ + if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link)) + continue; + + if (link_address_is_dynamic(link, address)) { + if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) + continue; + } else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) + continue; + + if (link_is_static_address_configured(link, address)) { + k = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL); + if (k < 0) { + log_link_error_errno(link, k, "Failed to add address: %m"); + if (r >= 0) + r = k; + } + } else { + k = address_remove(address, link, NULL); + if (k < 0 && r >= 0) + r = k; + } + } + + return r; +} + +static int remove_static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->ifname); + assert(link->address_remove_messages > 0); + + link->address_remove_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 != -EADDRNOTAVAIL) + log_link_message_warning_errno(link, m, r, "Could not drop address"); + else if (r >= 0) + (void) manager_rtnl_process_address(rtnl, m, link->manager); + + if (link->address_remove_messages == 0 && link->request_static_addresses) { + link_set_state(link, LINK_STATE_CONFIGURING); + r = link_set_addresses(link); + if (r < 0) + link_enter_failed(link); + } + + return 1; +} + +int link_drop_addresses(Link *link) { + Address *address, *pool_address; + int k, r = 0; + + assert(link); + + SET_FOREACH(address, link->addresses) { + /* we consider IPv6LL addresses to be managed by the kernel */ + if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link)) + continue; + + k = address_remove(address, link, remove_static_address_handler); + if (k < 0 && r >= 0) { + r = k; + continue; + } + + link->address_remove_messages++; + + SET_FOREACH(pool_address, link->pool_addresses) + if (address_equal(address, pool_address)) + address_free(set_remove(link->pool_addresses, pool_address)); + } + + return r; +} + +static int address_acquire(Link *link, const Address *original, Address **ret) { union in_addr_union in_addr = IN_ADDR_NULL; struct in_addr broadcast = {}; _cleanup_(address_freep) Address *na = NULL; @@ -554,12 +733,16 @@ static int address_acquire(Link *link, Address *original, Address **ret) { /* Something useful was configured? just use it */ r = in_addr_is_null(original->family, &original->in_addr); - if (r <= 0) + if (r < 0) return r; + if (r == 0) { + *ret = NULL; + return 0; + } /* The address is configured to be 0.0.0.0 or [::] by the user? * Then let's acquire something more useful from the pool. */ - r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr); + r = address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr); if (r < 0) return r; if (r == 0) @@ -581,27 +764,25 @@ static int address_acquire(Link *link, Address *original, Address **ret) { if (r < 0) return r; - na->family = original->family; - na->prefixlen = original->prefixlen; - na->scope = original->scope; - na->cinfo = original->cinfo; - - if (original->label) { - na->label = strdup(original->label); - if (!na->label) - return -ENOMEM; - } + r = address_copy(na, original); + if (r < 0) + return r; na->broadcast = broadcast; na->in_addr = in_addr; - LIST_PREPEND(addresses, link->pool_addresses, na); + r = set_ensure_put(&link->pool_addresses, &address_hash_ops, na); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; *ret = TAKE_PTR(na); - - return 0; + return 1; } +static int ipv4_dad_configure(Address *address); + int address_configure( Address *address, Link *link, @@ -610,7 +791,8 @@ int address_configure( Address **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - Address *a; + Address *acquired_address, *a; + uint32_t flags; int r; assert(address); @@ -627,9 +809,11 @@ int address_configure( return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG), "Too many addresses are configured, refusing: %m"); - r = address_acquire(link, address, &address); + r = address_acquire(link, address, &acquired_address); if (r < 0) return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m"); + if (acquired_address) + address = acquired_address; if (DEBUG_LOGGING) { _cleanup_free_ char *str = NULL; @@ -651,29 +835,13 @@ int address_configure( if (r < 0) return log_link_error_errno(link, r, "Could not set prefixlen: %m"); - address->flags |= IFA_F_PERMANENT; - - if (address->home_address) - address->flags |= IFA_F_HOMEADDRESS; - - if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV6)) - address->flags |= IFA_F_NODAD; - - if (address->manage_temporary_address) - address->flags |= IFA_F_MANAGETEMPADDR; - - if (!address->prefix_route) - address->flags |= IFA_F_NOPREFIXROUTE; - - if (address->autojoin) - address->flags |= IFA_F_MCAUTOJOIN; - - r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff)); + flags = address->flags | IFA_F_PERMANENT; + r = sd_rtnl_message_addr_set_flags(req, flags & 0xff); if (r < 0) return log_link_error_errno(link, r, "Could not set flags: %m"); - if (address->flags & ~0xff) { - r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags); + if (flags & ~0xff) { + r = sd_netlink_message_append_u32(req, IFA_FLAGS, flags); if (r < 0) return log_link_error_errno(link, r, "Could not set extended flags: %m"); } @@ -706,37 +874,28 @@ int address_configure( if (r < 0) return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m"); - r = address_establish(address, link); + if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer)) + r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a); + else + r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a); + if (r < 0) + return log_link_error_errno(link, r, "Could not add address: %m"); + + a->scope = address->scope; + r = address_set_masquerade(a, true); if (r < 0) log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m"); r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link); if (r < 0) { - address_release(address); + (void) address_set_masquerade(a, false); return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); } link_ref(link); - if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer)) - r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a); - else - r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a); - if (r < 0) { - address_release(address); - return log_link_error_errno(link, r, "Could not add address: %m"); - } - - if (address->acd) { - assert(address->family == AF_INET); - if (DEBUG_LOGGING) { - _cleanup_free_ char *pretty = NULL; - - (void) in_addr_to_string(address->family, &address->in_addr, &pretty); - log_link_debug(link, "Starting IPv4ACD client. Probing address %s", strna(pretty)); - } - - r = sd_ipv4acd_start(address->acd, true); + if (FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) { + r = ipv4_dad_configure(a); if (r < 0) log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m"); } @@ -747,6 +906,403 @@ int address_configure( return 1; } +static int static_address_ready_callback(Address *address) { + Address *a; + Link *link; + + assert(address); + assert(address->link); + + link = address->link; + + if (!link->addresses_configured) + return 0; + + SET_FOREACH(a, link->static_addresses) + if (!address_is_ready(a)) { + _cleanup_free_ char *str = NULL; + + (void) in_addr_to_string(a->family, &a->in_addr, &str); + log_link_debug(link, "an address %s/%u is not ready", strnull(str), a->prefixlen); + return 0; + } + + /* This should not be called again */ + SET_FOREACH(a, link->static_addresses) + a->callback = NULL; + + link->addresses_ready = true; + + return link_set_routes(link); +} + +static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->ifname); + assert(link->address_messages > 0); + assert(IN_SET(link->state, LINK_STATE_CONFIGURING, + LINK_STATE_FAILED, LINK_STATE_LINGER)); + + link->address_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_message_warning_errno(link, m, r, "Could not set address"); + link_enter_failed(link); + return 1; + } else if (r >= 0) + (void) manager_rtnl_process_address(rtnl, m, link->manager); + + if (link->address_messages == 0) { + Address *a; + + log_link_debug(link, "Addresses set"); + link->addresses_configured = true; + + /* When all static addresses are already ready, then static_address_ready_callback() + * will not be called automatically. So, call it here. */ + a = set_first(link->static_addresses); + if (!a) { + log_link_warning(link, "No static address is stored."); + link_enter_failed(link); + return 1; + } + if (!a->callback) { + log_link_warning(link, "Address ready callback is not set."); + link_enter_failed(link); + return 1; + } + r = a->callback(a); + if (r < 0) + link_enter_failed(link); + } + + return 1; +} + +static int static_address_configure(Address *address, Link *link, bool update) { + Address *ret; + int r; + + assert(address); + assert(link); + + r = address_configure(address, link, address_handler, update, &ret); + if (r < 0) + return log_link_warning_errno(link, r, "Could not configure static address: %m"); + + link->address_messages++; + + r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store static address: %m"); + + ret->callback = static_address_ready_callback; + + return 0; +} + +int link_set_addresses(Link *link) { + Address *ad; + int r; + + assert(link); + assert(link->network); + + if (link->address_remove_messages != 0) { + log_link_debug(link, "Removing old addresses, new addresses will be configured later."); + link->request_static_addresses = true; + return 0; + } + + ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) { + bool update; + + if (ad->family == AF_INET6 && !in_addr_is_null(ad->family, &ad->in_addr_peer)) + update = address_get(link, ad->family, &ad->in_addr_peer, ad->prefixlen, NULL) > 0; + else + update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0; + + r = static_address_configure(ad, link, update); + if (r < 0) + return r; + } + + if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) { + Prefix *p; + + HASHMAP_FOREACH(p, link->network->prefixes_by_section) { + _cleanup_(address_freep) Address *address = NULL; + + if (!p->assign) + continue; + + r = address_new(&address); + if (r < 0) + return log_oom(); + + r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen); + if (r < 0) + return log_link_warning_errno(link, r, "Could not get RA prefix: %m"); + + r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); + if (r < 0) + return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m"); + + address->family = AF_INET6; + r = static_address_configure(address, link, true); + if (r < 0) + return r; + } + } + + if (link->address_messages == 0) { + link->addresses_configured = true; + link->addresses_ready = true; + r = link_set_routes(link); + if (r < 0) + return r; + } else { + log_link_debug(link, "Setting addresses"); + link_set_state(link, LINK_STATE_CONFIGURING); + } + + return 0; +} + +int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + _cleanup_free_ char *buf = NULL; + Link *link = NULL; + uint16_t type; + unsigned char flags, prefixlen, scope; + union in_addr_union in_addr = IN_ADDR_NULL; + struct ifa_cacheinfo cinfo; + Address *address = NULL; + char valid_buf[FORMAT_TIMESPAN_MAX]; + const char *valid_str = NULL; + int ifindex, family, r; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive address message, ignoring"); + + 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_NEWADDR, RTM_DELADDR)) { + log_warning("rtnl: received unexpected message type %u when processing address, ignoring.", type); + return 0; + } + + r = sd_rtnl_message_addr_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 address 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 address again, so just + * ignore it */ + if (!m->enumerating) + log_warning("rtnl: received address for link '%d' we don't know about, ignoring.", ifindex); + return 0; + } + + r = sd_rtnl_message_addr_get_family(message, &family); + if (r < 0) { + log_link_warning(link, "rtnl: received address message without family, ignoring."); + return 0; + } else if (!IN_SET(family, AF_INET, AF_INET6)) { + log_link_debug(link, "rtnl: received address message with invalid family '%i', ignoring.", family); + return 0; + } + + r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address message without prefixlen, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_addr_get_scope(message, &scope); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address message without scope, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_addr_get_flags(message, &flags); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address message without flags, ignoring: %m"); + return 0; + } + + switch (family) { + case AF_INET: + r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m"); + return 0; + } + + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m"); + return 0; + } + + break; + + default: + assert_not_reached("Received unsupported address family"); + } + + (void) in_addr_to_string(family, &in_addr, &buf); + + r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m"); + return 0; + } else if (r >= 0 && cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) + valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, + cinfo.ifa_valid * USEC_PER_SEC, + USEC_PER_SEC); + + (void) address_get(link, family, &in_addr, prefixlen, &address); + + switch (type) { + case RTM_NEWADDR: + if (address) + log_link_debug(link, "Remembering updated address: %s/%u (valid %s%s)", + strnull(buf), prefixlen, + valid_str ? "for " : "forever", strempty(valid_str)); + else { + /* An address appeared that we did not request */ + r = address_add_foreign(link, family, &in_addr, prefixlen, &address); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to remember foreign address %s/%u, ignoring: %m", + strnull(buf), prefixlen); + return 0; + } else + log_link_debug(link, "Remembering foreign address: %s/%u (valid %s%s)", + strnull(buf), prefixlen, + valid_str ? "for " : "forever", strempty(valid_str)); + } + + /* address_update() logs internally, so we don't need to here. */ + r = address_update(address, flags, scope, &cinfo); + if (r < 0) + link_enter_failed(link); + + break; + + case RTM_DELADDR: + if (address) { + log_link_debug(link, "Forgetting address: %s/%u (valid %s%s)", + strnull(buf), prefixlen, + valid_str ? "for " : "forever", strempty(valid_str)); + (void) address_drop(address); + } else + log_link_debug(link, "Kernel removed an address we don't remember: %s/%u (valid %s%s), ignoring.", + strnull(buf), prefixlen, + valid_str ? "for " : "forever", strempty(valid_str)); + + break; + + default: + assert_not_reached("Received invalid RTNL message type"); + } + + return 1; +} + +int link_serialize_addresses(Link *link, FILE *f) { + bool space = false; + Address *a; + + assert(link); + + fputs("ADDRESSES=", f); + SET_FOREACH(a, link->addresses) { + _cleanup_free_ char *address_str = NULL; + + if (in_addr_to_string(a->family, &a->in_addr, &address_str) < 0) + continue; + + fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen); + space = true; + } + fputc('\n', f); + + return 0; +} + +int link_deserialize_addresses(Link *link, const char *addresses) { + int r; + + assert(link); + + for (const char *p = addresses;; ) { + _cleanup_free_ char *address_str = NULL; + union in_addr_union address; + unsigned char prefixlen; + char *prefixlen_str; + int family; + + r = extract_first_word(&p, &address_str, NULL, 0); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to parse ADDRESSES=: %m"); + if (r == 0) + return 0; + + prefixlen_str = strchr(address_str, '/'); + if (!prefixlen_str) { + log_link_debug(link, "Failed to parse address and prefix length, ignoring: %s", address_str); + continue; + } + *prefixlen_str++ = '\0'; + + r = sscanf(prefixlen_str, "%hhu", &prefixlen); + if (r != 1) { + log_link_debug(link, "Failed to parse prefixlen: %s", prefixlen_str); + continue; + } + + r = in_addr_from_string_auto(address_str, &family, &address); + if (r < 0) { + log_link_debug_errno(link, r, "Failed to parse address: %s", address_str); + continue; + } + + r = address_add(link, family, &address, prefixlen, NULL); + if (r < 0) + log_link_debug_errno(link, r, "Failed to add address: %m"); + } + + return 0; +} + static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { _cleanup_free_ char *pretty = NULL; Address *address; @@ -788,29 +1344,37 @@ static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { return; } -int configure_ipv4_duplicate_address_detection(Link *link, Address *address) { +static int ipv4_dad_configure(Address *address) { int r; - assert(link); assert(address); - assert(address->family == AF_INET); - assert(!address->link && address->network); + assert(address->link); - address->link = link; + if (address->family != AF_INET) + return 0; - r = sd_ipv4acd_new(&address->acd); + if (DEBUG_LOGGING) { + _cleanup_free_ char *pretty = NULL; + + (void) in_addr_to_string(address->family, &address->in_addr, &pretty); + log_link_debug(address->link, "Starting IPv4ACD client. Probing address %s", strna(pretty)); + } + + if (!address->acd) { + r = sd_ipv4acd_new(&address->acd); + if (r < 0) + return r; + + r = sd_ipv4acd_attach_event(address->acd, address->link->manager->event, 0); + if (r < 0) + return r; + } + + r = sd_ipv4acd_set_ifindex(address->acd, address->link->ifindex); if (r < 0) return r; - r = sd_ipv4acd_attach_event(address->acd, NULL, 0); - if (r < 0) - return r; - - r = sd_ipv4acd_set_ifindex(address->acd, link->ifindex); - if (r < 0) - return r; - - r = sd_ipv4acd_set_mac(address->acd, &link->mac); + r = sd_ipv4acd_set_mac(address->acd, &address->link->mac); if (r < 0) return r; @@ -822,9 +1386,72 @@ int configure_ipv4_duplicate_address_detection(Link *link, Address *address) { if (r < 0) return r; + return sd_ipv4acd_start(address->acd, true); +} + +static int ipv4_dad_update_mac_one(Address *address) { + bool running; + int r; + + assert(address); + + if (!address->acd) + return 0; + + running = sd_ipv4acd_is_running(address->acd); + + if (running) { + r = sd_ipv4acd_stop(address->acd); + if (r < 0) + return r; + } + + r = sd_ipv4acd_set_mac(address->acd, &address->link->mac); + if (r < 0) + return r; + + if (running) { + r = sd_ipv4acd_start(address->acd, true); + if (r < 0) + return r; + } + return 0; } +int ipv4_dad_update_mac(Link *link) { + Address *address; + int k, r = 0; + + assert(link); + + SET_FOREACH(address, link->addresses) { + k = ipv4_dad_update_mac_one(address); + if (k < 0 && r >= 0) + r = k; + } + + return r; +} + +int ipv4_dad_stop(Link *link) { + Address *address; + int k, r = 0; + + assert(link); + + SET_FOREACH(address, link->addresses) { + if (!address->acd) + continue; + + k = sd_ipv4acd_stop(address->acd); + if (k < 0 && r >= 0) + r = k; + } + + return r; +} + int config_parse_broadcast( const char *unit, const char *filename, @@ -875,7 +1502,8 @@ int config_parse_broadcast( return 0; } -int config_parse_address(const char *unit, +int config_parse_address( + const char *unit, const char *filename, unsigned line, const char *section, @@ -898,11 +1526,10 @@ int config_parse_address(const char *unit, assert(rvalue); assert(data); - if (streq(section, "Network")) { - /* we are not in an Address section, so treat - * this as the special '0' section */ - r = address_new_static(network, NULL, 0, &n); - } else + if (streq(section, "Network")) + /* we are not in an Address section, so use line number instead. */ + r = address_new_static(network, filename, line, &n); + else r = address_new_static(network, filename, section_line, &n); if (r == -ENOMEM) return log_oom(); @@ -1006,16 +1633,18 @@ int config_parse_label( return 0; } -int config_parse_lifetime(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) { +int config_parse_lifetime( + 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_(address_free_or_set_invalidp) Address *n = NULL; uint32_t k; @@ -1053,16 +1682,18 @@ int config_parse_lifetime(const char *unit, return 0; } -int config_parse_address_flags(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) { +int config_parse_address_flags( + 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_(address_free_or_set_invalidp) Address *n = NULL; int r; @@ -1089,33 +1720,27 @@ int config_parse_address_flags(const char *unit, return 0; } - if (streq(lvalue, "HomeAddress")) - n->home_address = r; - else if (streq(lvalue, "ManageTemporaryAddress")) - n->manage_temporary_address = r; - else if (streq(lvalue, "PrefixRoute")) - n->prefix_route = !r; - else if (streq(lvalue, "AddPrefixRoute")) - n->prefix_route = r; - else if (streq(lvalue, "AutoJoin")) - n->autojoin = r; - else - assert_not_reached("Invalid address flag type."); + if (streq(lvalue, "AddPrefixRoute")) + r = !r; + + SET_FLAG(n->flags, ltype, r); n = NULL; return 0; } -int config_parse_address_scope(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) { +int config_parse_address_scope( + 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_(address_free_or_set_invalidp) Address *n = NULL; int r; @@ -1166,6 +1791,7 @@ int config_parse_duplicate_address_detection( const char *rvalue, void *data, void *userdata) { + Network *network = userdata; _cleanup_(address_free_or_set_invalidp) Address *n = NULL; AddressFamily a; @@ -1215,7 +1841,7 @@ bool address_is_ready(const Address *a) { return !(a->flags & IFA_F_TENTATIVE); } -int address_section_verify(Address *address) { +static int address_section_verify(Address *address) { if (section_is_invalid(address->section)) return -EINVAL; @@ -1228,8 +1854,32 @@ int address_section_verify(Address *address) { address->section->filename, address->section->line); } - if (!address->scope_set && in_addr_is_localhost(address->family, &address->in_addr) > 0) + if (in_addr_is_localhost(address->family, &address->in_addr) > 0 && + (address->family == AF_INET || !address->scope_set)) { + /* For IPv4, scope must be always RT_SCOPE_HOST. + * For IPv6, use RT_SCOPE_HOST only when it is not explicitly specified. */ + + if (address->scope_set && address->scope != RT_SCOPE_HOST) + log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: non-host scope is set in the [Address] section from line %u. " + "Ignoring Scope= setting.", + address->section->filename, address->section->line); + address->scope = RT_SCOPE_HOST; + } + + if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV6)) + address->flags |= IFA_F_NODAD; return 0; } + +void network_drop_invalid_addresses(Network *network) { + Address *address; + + assert(network); + + ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) + if (address_section_verify(address) < 0) + address_free(address); +} diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 1378901b8b..431e507851 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -3,26 +3,22 @@ #include #include - -#include "conf-parser.h" -#include "in-addr-util.h" - -typedef struct Address Address; - -#include "networkd-link.h" -#include "networkd-network.h" -#include "networkd-util.h" +#include #include "sd-ipv4acd.h" +#include "conf-parser.h" +#include "in-addr-util.h" +#include "networkd-link.h" +#include "networkd-util.h" + #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU +typedef struct Manager Manager; typedef struct Network Network; -typedef struct Link Link; -typedef struct NetworkConfigSection NetworkConfigSection; typedef int (*address_ready_callback_t)(Address *address); -struct Address { +typedef struct Address { Network *network; NetworkConfigSection *section; @@ -42,39 +38,40 @@ struct Address { bool scope_set:1; bool ip_masquerade_done:1; - bool manage_temporary_address:1; - bool home_address:1; - bool prefix_route:1; - bool autojoin:1; AddressFamily duplicate_address_detection; /* Called when address become ready */ address_ready_callback_t callback; sd_ipv4acd *acd; - - LIST_FIELDS(Address, addresses); -}; +} Address; int address_new(Address **ret); -void address_free(Address *address); -int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); -int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); +Address *address_free(Address *address); int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); bool address_exists(Link *link, int family, const union in_addr_union *in_addr); -int address_update(Address *address, unsigned char flags, unsigned char scope, const struct ifa_cacheinfo *cinfo); -int address_drop(Address *address); int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update, Address **ret); int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback); bool address_equal(Address *a1, Address *a2); bool address_is_ready(const Address *a); -int address_section_verify(Address *a); -int configure_ipv4_duplicate_address_detection(Link *link, Address *address); int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret); DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free); +int link_set_addresses(Link *link); +int link_drop_addresses(Link *link); +int link_drop_foreign_addresses(Link *link); +int link_serialize_addresses(Link *link, FILE *f); +int link_deserialize_addresses(Link *link, const char *addresses); + +int ipv4_dad_stop(Link *link); +int ipv4_dad_update_mac(Link *link); + +int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, Manager *m); + +void network_drop_invalid_addresses(Network *network); + void address_hash_func(const Address *a, struct siphash *state); int address_compare_func(const Address *a1, const Address *a2); extern const struct hash_ops address_hash_ops; diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c index bbe01037d1..23ca4f9fac 100644 --- a/src/network/networkd-brvlan.c +++ b/src/network/networkd-brvlan.c @@ -146,26 +146,26 @@ static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *lin return 1; } -int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap) { +int link_set_bridge_vlan(Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - sd_netlink *rtnl; - uint16_t flags; int r; assert(link); assert(link->manager); - assert(br_vid_bitmap); - assert(br_untagged_bitmap); assert(link->network); - /* pvid might not be in br_vid_bitmap yet */ - if (pvid) - set_bit(pvid, br_vid_bitmap); + if (!link->network->use_br_vlan) + return 0; - rtnl = link->manager->rtnl; + if (!link->network->bridge && !streq_ptr(link->kind, "bridge")) + return 0; + + /* pvid might not be in br_vid_bitmap yet */ + if (link->network->pvid) + set_bit(link->network->pvid, link->network->br_vid_bitmap); /* create new RTM message */ - r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, link->ifindex); + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); if (r < 0) return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); @@ -179,14 +179,14 @@ int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32 /* master needs flag self */ if (!link->network->bridge) { - flags = BRIDGE_FLAGS_SELF; - r = sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(uint16_t)); + uint16_t flags = BRIDGE_FLAGS_SELF; + r = sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(flags)); if (r < 0) return log_link_error_errno(link, r, "Could not open IFLA_BRIDGE_FLAGS: %m"); } /* add vlan info */ - r = append_vlan_info_data(link, req, pvid, br_vid_bitmap, br_untagged_bitmap); + r = append_vlan_info_data(link, req, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap); if (r < 0) return log_link_error_errno(link, r, "Could not append VLANs: %m"); @@ -195,7 +195,7 @@ int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32 return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m"); /* send message to the kernel */ - r = netlink_call_async(rtnl, NULL, req, set_brvlan_handler, + r = netlink_call_async(link->manager->rtnl, NULL, req, set_brvlan_handler, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); diff --git a/src/network/networkd-brvlan.h b/src/network/networkd-brvlan.h index 7533034021..d13ea30151 100644 --- a/src/network/networkd-brvlan.h +++ b/src/network/networkd-brvlan.h @@ -5,8 +5,6 @@ Copyright © 2016 BISDN GmbH. All rights reserved. ***/ -#include - #include "conf-parser.h" #define BRIDGE_VLAN_BITMAP_MAX 4096 @@ -14,7 +12,7 @@ typedef struct Link Link; -int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap); +int link_set_bridge_vlan(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_pvid); CONFIG_PARSER_PROTOTYPE(config_parse_brvlan_vlan); diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index ecf9bcea85..d3053d0354 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -1,15 +1,231 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include + #include "dhcp-internal.h" #include "dhcp6-internal.h" #include "escape.h" #include "in-addr-util.h" #include "networkd-dhcp-common.h" +#include "networkd-link.h" +#include "networkd-manager.h" #include "networkd-network.h" #include "parse-util.h" +#include "socket-util.h" #include "string-table.h" #include "strv.h" +bool link_dhcp_enabled(Link *link, int family) { + assert(link); + assert(IN_SET(family, AF_INET, AF_INET6)); + + if (family == AF_INET6 && !socket_ipv6_is_supported()) + return false; + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->network->bond) + return false; + + if (link->iftype == ARPHRD_CAN) + return false; + + return link->network->dhcp & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6); +} + +DUID* link_get_duid(Link *link) { + if (link->network->duid.type != _DUID_TYPE_INVALID) + return &link->network->duid; + else + return &link->manager->duid; +} + +static int duid_set_uuid(DUID *duid, sd_id128_t uuid) { + assert(duid); + + if (duid->raw_data_len > 0) + return 0; + + if (duid->type != DUID_TYPE_UUID) + return -EINVAL; + + memcpy(&duid->raw_data, &uuid, sizeof(sd_id128_t)); + duid->raw_data_len = sizeof(sd_id128_t); + + return 1; +} + +static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + Manager *manager = userdata; + const sd_bus_error *e; + const void *a; + size_t sz; + DUID *duid; + Link *link; + int r; + + assert(m); + assert(manager); + + e = sd_bus_message_get_error(m); + if (e) { + log_error_errno(sd_bus_error_get_errno(e), + "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s", + e->message); + goto configure; + } + + r = sd_bus_message_read_array(m, 'y', &a, &sz); + if (r < 0) + goto configure; + + if (sz != sizeof(sd_id128_t)) { + log_error("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID."); + goto configure; + } + + memcpy(&manager->product_uuid, a, sz); + while ((duid = set_steal_first(manager->duids_requesting_uuid))) + (void) duid_set_uuid(duid, manager->product_uuid); + + manager->duids_requesting_uuid = set_free(manager->duids_requesting_uuid); + +configure: + while ((link = set_steal_first(manager->links_requesting_uuid))) { + link_unref(link); + + r = link_configure(link); + if (r < 0) + link_enter_failed(link); + } + + manager->links_requesting_uuid = set_free(manager->links_requesting_uuid); + + /* To avoid calling GetProductUUID() bus method so frequently, set the flag below + * even if the method fails. */ + manager->has_product_uuid = true; + + return 1; +} + +int manager_request_product_uuid(Manager *m, Link *link) { + int r; + + assert(m); + + if (m->has_product_uuid) + return 0; + + log_debug("Requesting product UUID"); + + if (link) { + DUID *duid; + + assert_se(duid = link_get_duid(link)); + + r = set_ensure_put(&m->links_requesting_uuid, NULL, link); + if (r < 0) + return log_oom(); + if (r > 0) + link_ref(link); + + r = set_ensure_put(&m->duids_requesting_uuid, NULL, duid); + if (r < 0) + return log_oom(); + } + + if (!m->bus || sd_bus_is_ready(m->bus) <= 0) { + log_debug("Not connected to system bus, requesting product UUID later."); + return 0; + } + + r = sd_bus_call_method_async( + m->bus, + NULL, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "GetProductUUID", + get_product_uuid_handler, + m, + "b", + false); + if (r < 0) + return log_warning_errno(r, "Failed to get product UUID: %m"); + + return 0; +} + +static bool link_requires_uuid(Link *link) { + const DUID *duid; + + assert(link); + assert(link->manager); + assert(link->network); + + duid = link_get_duid(link); + if (duid->type != DUID_TYPE_UUID || duid->raw_data_len != 0) + return false; + + if (link_dhcp4_enabled(link) && IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY)) + return true; + + if (link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link)) + return true; + + return false; +} + +int link_configure_duid(Link *link) { + Manager *m; + DUID *duid; + int r; + + assert(link); + assert(link->manager); + assert(link->network); + + m = link->manager; + duid = link_get_duid(link); + + if (!link_requires_uuid(link)) + return 1; + + if (m->has_product_uuid) { + (void) duid_set_uuid(duid, m->product_uuid); + return 1; + } + + if (!m->links_requesting_uuid) { + r = manager_request_product_uuid(m, link); + if (r < 0) { + if (r == -ENOMEM) + return r; + + log_link_warning_errno(link, r, + "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m"); + return 1; + } + } else { + r = set_put(m->links_requesting_uuid, link); + if (r < 0) + return log_oom(); + if (r > 0) + link_ref(link); + + r = set_put(m->duids_requesting_uuid, duid); + if (r < 0) + return log_oom(); + } + + return 0; +} + int config_parse_dhcp( const char* unit, const char *filename, diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index 01400a2385..e3982a7ef1 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -7,6 +7,9 @@ #define DHCP_ROUTE_METRIC 1024 +typedef struct Link Link; +typedef struct Manager Manager; + typedef enum DHCPUseDomains { DHCP_USE_DOMAINS_NO, DHCP_USE_DOMAINS_YES, @@ -35,6 +38,18 @@ typedef struct DUID { usec_t llt_time; } DUID; +bool link_dhcp_enabled(Link *link, int family); +static inline bool link_dhcp4_enabled(Link *link) { + return link_dhcp_enabled(link, AF_INET); +} +static inline bool link_dhcp6_enabled(Link *link) { + return link_dhcp_enabled(link, AF_INET6); +} + +DUID* link_get_duid(Link *link); +int link_configure_duid(Link *link); +int manager_request_product_uuid(Manager *m, Link *link); + const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_; DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_; diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index 890d9b55a1..0155ab2de2 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -1,9 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include "sd-dhcp-server.h" #include "fd-util.h" #include "fileio.h" +#include "networkd-address.h" #include "networkd-dhcp-server.h" #include "networkd-link.h" #include "networkd-manager.h" @@ -14,6 +19,24 @@ #include "string-util.h" #include "strv.h" +static bool link_dhcp4_server_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->network->bond) + return false; + + if (link->iftype == ARPHRD_CAN) + return false; + + return link->network->dhcp_server; +} + static Address* link_find_dhcp_server_address(Link *link) { Address *address; @@ -21,13 +44,13 @@ static Address* link_find_dhcp_server_address(Link *link) { assert(link->network); /* The first statically configured address if there is any */ - LIST_FOREACH(addresses, address, link->network->static_addresses) + ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section) if (address->family == AF_INET && !in_addr_is_null(address->family, &address->in_addr)) return address; /* If that didn't work, find a suitable address we got from the pool */ - LIST_FOREACH(addresses, address, link->pool_addresses) + SET_FOREACH(address, link->pool_addresses) if (address->family == AF_INET) return address; @@ -230,6 +253,24 @@ int dhcp4_server_configure(Link *link) { Address *address; int r; + assert(link); + + if (!link_dhcp4_server_enabled(link)) + return 0; + + if (!(link->flags & IFF_UP)) + return 0; + + if (!link->dhcp_server) { + r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex); + if (r < 0) + return r; + + r = sd_dhcp_server_attach_event(link->dhcp_server, link->manager->event, 0); + if (r < 0) + return r; + } + address = link_find_dhcp_server_address(link); if (!address) return log_link_error_errno(link, SYNTHETIC_ERRNO(EBUSY), @@ -341,6 +382,8 @@ int dhcp4_server_configure(Link *link) { r = sd_dhcp_server_start(link->dhcp_server); if (r < 0) return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m"); + + log_link_debug(link, "Offering DHCPv4 leases"); } return 0; diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index eddeb0d5ef..ed4d7c34e4 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -11,6 +11,7 @@ #include "hostname-util.h" #include "parse-util.h" #include "network-internal.h" +#include "networkd-address.h" #include "networkd-dhcp4.h" #include "networkd-link.h" #include "networkd-manager.h" @@ -384,7 +385,7 @@ static int link_set_dhcp_routes(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not set router: %m"); - LIST_FOREACH(routes, rt, link->network->static_routes) { + HASHMAP_FOREACH(rt, link->network->routes_by_section) { if (!rt->gateway_from_dhcp) continue; @@ -622,7 +623,7 @@ static int configure_dhcpv4_duplicate_address_detection(Link *link) { if (r < 0) return r; - r = sd_ipv4acd_attach_event(link->network->dhcp_acd, NULL, 0); + r = sd_ipv4acd_attach_event(link->network->dhcp_acd, link->manager->event, 0); if (r < 0) return r; @@ -698,7 +699,7 @@ static int dhcp4_address_ready_callback(Address *address) { return r; /* Reconfigure static routes as kernel may remove some routes when lease expires. */ - r = link_request_set_routes(link); + r = link_set_routes(link); if (r < 0) return r; @@ -755,9 +756,9 @@ static int dhcp4_update_address(Link *link, bool announce) { link_set_state(link, LINK_STATE_CONFIGURING); link->dhcp4_configured = false; - /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). Before they - * are called, the related flags must be cleared. Otherwise, the link becomes configured state - * before routes are configured. */ + /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are called, the + * related flags must be cleared. Otherwise, the link becomes configured state before routes + * are configured. */ link->static_routes_configured = false; link->static_nexthops_configured = false; @@ -814,7 +815,7 @@ static int dhcp4_update_address(Link *link, bool announce) { addr->cinfo.ifa_valid = lifetime; addr->prefixlen = prefixlen; addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr; - addr->prefix_route = link_prefixroute(link); + SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link)); /* allow reusing an existing address and simply update its lifetime * in case it already exists */ @@ -1156,12 +1157,10 @@ static bool promote_secondaries_enabled(const char *ifname) { * the primary one expires it relies on the kernel to promote the * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */ -int dhcp4_set_promote_secondaries(Link *link) { +static int dhcp4_set_promote_secondaries(Link *link) { int r; assert(link); - assert(link->network); - assert(link->network->dhcp & ADDRESS_FAMILY_IPV4); /* check if the kernel has promote_secondaries enabled for our * interface. If it is not globally enabled or enabled for the @@ -1181,7 +1180,7 @@ int dhcp4_set_promote_secondaries(Link *link) { return 0; } -int dhcp4_set_client_identifier(Link *link) { +static int dhcp4_set_client_identifier(Link *link) { int r; assert(link); @@ -1240,6 +1239,25 @@ int dhcp4_set_client_identifier(Link *link) { return 0; } +static int dhcp4_init(Link *link) { + int r; + + assert(link); + + if (link->dhcp_client) + return 0; + + r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize); + if (r < 0) + return r; + + r = sd_dhcp_client_attach_event(link->dhcp_client, link->manager->event, 0); + if (r < 0) + return r; + + return 0; +} + int dhcp4_configure(Link *link) { sd_dhcp_option *send_option; void *request_options; @@ -1247,19 +1265,17 @@ int dhcp4_configure(Link *link) { assert(link); assert(link->network); - assert(link->network->dhcp & ADDRESS_FAMILY_IPV4); - if (!link->dhcp_client) { - r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to create DHCP4 client: %m"); + if (!link_dhcp4_enabled(link)) + return 0; - r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0); - if (r < 0) - return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to attach event: %m"); - } + r = dhcp4_set_promote_secondaries(link); + if (r < 0) + return r; + + r = dhcp4_init(link); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to initialize DHCP4 client: %m"); r = sd_dhcp_client_set_mac(link->dhcp_client, (const uint8_t *) &link->mac, @@ -1419,6 +1435,49 @@ int dhcp4_configure(Link *link) { return dhcp4_set_client_identifier(link); } +int dhcp4_update_mac(Link *link) { + int r; + + assert(link); + + if (!link->dhcp_client) + return 0; + + r = sd_dhcp_client_set_mac(link->dhcp_client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); + if (r < 0) + return r; + + r = dhcp4_set_client_identifier(link); + if (r < 0) + return r; + + return 0; +} + +int link_deserialize_dhcp4(Link *link, const char *dhcp4_address) { + union in_addr_union address; + int r; + + assert(link); + + if (isempty(dhcp4_address)) + return 0; + + r = in_addr_from_string(AF_INET, dhcp4_address, &address); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to parse DHCPv4 address: %s", dhcp4_address); + + r = dhcp4_init(link); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to initialize DHCPv4 client: %m"); + + r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to set initial DHCPv4 address %s: %m", dhcp4_address); + + return 0; +} + int config_parse_dhcp_max_attempts( const char *unit, const char *filename, diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h index 7a80897ffc..8aa6ac9453 100644 --- a/src/network/networkd-dhcp4.h +++ b/src/network/networkd-dhcp4.h @@ -18,8 +18,9 @@ typedef enum DHCPClientIdentifier { } DHCPClientIdentifier; int dhcp4_configure(Link *link); -int dhcp4_set_client_identifier(Link *link); -int dhcp4_set_promote_secondaries(Link *link); +int dhcp4_update_mac(Link *link); + +int link_deserialize_dhcp4(Link *link, const char *dhcp4_address); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_acl_ip_address); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 7996e825c2..ea331c95db 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -14,6 +14,7 @@ #include "hostname-util.h" #include "missing_network.h" #include "network-internal.h" +#include "networkd-address.h" #include "networkd-dhcp6.h" #include "networkd-link.h" #include "networkd-manager.h" @@ -24,6 +25,15 @@ #include "radv-internal.h" #include "web-util.h" +bool link_dhcp6_pd_is_enabled(Link *link) { + assert(link); + + if (!link->network) + return false; + + return link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_DHCP6; +} + static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { uint32_t lifetime_preferred, lifetime_valid; union in_addr_union pd_prefix; @@ -180,6 +190,9 @@ int dhcp6_pd_remove(Link *link) { assert(link); assert(link->manager); + if (!link_dhcp6_pd_is_enabled(link)) + return 0; + link->dhcp6_pd_address_configured = false; link->dhcp6_pd_route_configured = false; @@ -342,7 +355,7 @@ static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin return 1; } - r = link_request_set_routes(link); + r = link_set_routes(link); if (r < 0) { link_enter_failed(link); return 1; @@ -425,13 +438,6 @@ static int dhcp6_pd_assign_prefix(Link *link, const union in_addr_union *prefix, return 0; } -bool link_dhcp6_pd_is_enabled(Link *link) { - if (!link->network) - return false; - - return link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_DHCP6; -} - static bool link_has_preferred_subnet_id(Link *link) { if (!link->network) return false; @@ -607,9 +613,9 @@ static int dhcp6_pd_finalize(Link *link) { link->dhcp6_pd_address_configured = true; } else { log_link_debug(link, "Setting DHCPv6 PD addresses"); - /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). - * Before they are called, the related flags must be cleared. Otherwise, the link - * becomes configured state before routes are configured. */ + /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are + * called, the related flags must be cleared. Otherwise, the link becomes configured + * state before routes are configured. */ link->static_routes_configured = false; link->static_nexthops_configured = false; } @@ -643,9 +649,6 @@ static void dhcp6_pd_prefix_lost(Link *dhcp6_link) { if (link == dhcp6_link) continue; - if (!link_dhcp6_pd_is_enabled(link)) - continue; - r = dhcp6_pd_remove(link); if (r < 0) link_enter_failed(link); @@ -952,7 +955,7 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } - r = link_request_set_routes(link); + r = link_set_routes(link); if (r < 0) { link_enter_failed(link); return 1; @@ -1075,9 +1078,9 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) { link->dhcp6_address_configured = true; else { log_link_debug(link, "Setting DHCPv6 addresses"); - /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). - * Before they are called, the related flags must be cleared. Otherwise, the link - * becomes configured state before routes are configured. */ + /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are + * called, the related flags must be cleared. Otherwise, the link becomes configured + * state before routes are configured. */ link->static_routes_configured = false; link->static_nexthops_configured = false; } @@ -1343,40 +1346,22 @@ static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { return false; } -int dhcp6_configure(Link *link) { - _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; - sd_dhcp6_option *vendor_option; - sd_dhcp6_option *send_option; - void *request_options; +static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) { const DUID *duid; int r; assert(link); assert(link->network); + assert(client); - if (link->dhcp6_client) - return 0; - - r = sd_dhcp6_client_new(&client); - if (r == -ENOMEM) - return log_oom(); + r = sd_dhcp6_client_set_mac(client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) - return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m"); - - r = sd_dhcp6_client_attach_event(client, NULL, 0); - if (r < 0) - return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m"); - - r = sd_dhcp6_client_set_mac(client, - (const uint8_t *) &link->mac, - sizeof (link->mac), ARPHRD_ETHER); - if (r < 0) - return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m"); + return r; if (link->network->iaid_set) { r = sd_dhcp6_client_set_iaid(client, link->network->iaid); if (r < 0) - return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m"); + return r; } duid = link_get_duid(link); @@ -1388,7 +1373,40 @@ int dhcp6_configure(Link *link) { duid->raw_data_len > 0 ? duid->raw_data : NULL, duid->raw_data_len); if (r < 0) - return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m"); + return r; + + return 0; +} + +int dhcp6_configure(Link *link) { + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + sd_dhcp6_option *vendor_option; + sd_dhcp6_option *send_option; + void *request_options; + int r; + + assert(link); + assert(link->network); + + if (!link_dhcp6_enabled(link) && !link_ipv6_accept_ra_enabled(link)) + return 0; + + if (link->dhcp6_client) + return 0; + + r = sd_dhcp6_client_new(&client); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m"); + + r = sd_dhcp6_client_attach_event(client, link->manager->event, 0); + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m"); + + r = dhcp6_set_identifier(link, client); + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set identifier: %m"); ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options) { r = sd_dhcp6_client_add_option(client, send_option); @@ -1471,6 +1489,57 @@ int dhcp6_configure(Link *link) { return 0; } +int dhcp6_update_mac(Link *link) { + bool restart; + int r; + + assert(link); + + if (!link->dhcp6_client) + return 0; + + restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0; + + if (restart) { + r = sd_dhcp6_client_stop(link->dhcp6_client); + if (r < 0) + return r; + } + + r = dhcp6_set_identifier(link, link->dhcp6_client); + if (r < 0) + return r; + + if (restart) { + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0) + return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m"); + } + + return 0; +} + +int link_serialize_dhcp6_client(Link *link, FILE *f) { + _cleanup_free_ char *duid = NULL; + uint32_t iaid; + int r; + + assert(link); + + if (!link->dhcp6_client) + return 0; + + r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid); + if (r >= 0) + fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid); + + r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid); + if (r >= 0) + fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid); + + return 0; +} + int config_parse_dhcp6_pd_hint( const char* unit, const char *filename, diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index 214456096d..4956c90915 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -29,9 +29,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free); bool link_dhcp6_pd_is_enabled(Link *link); int dhcp6_pd_remove(Link *link); int dhcp6_configure(Link *link); +int dhcp6_update_mac(Link *link); int dhcp6_request_address(Link *link, int ir); int dhcp6_request_prefix_delegation(Link *link); +int link_serialize_dhcp6_client(Link *link, FILE *f); + CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode); diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index 628c3988ac..2e35f7d1bf 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -8,27 +8,33 @@ #include "alloc-util.h" #include "bridge.h" -#include "conf-parser.h" #include "netlink-util.h" #include "networkd-fdb.h" +#include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-network.h" #include "parse-util.h" -#include "string-util.h" #include "string-table.h" -#include "util.h" #include "vlan-util.h" #include "vxlan.h" #define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U -static const char* const fdb_ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = { - [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use", - [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self", - [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master", - [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router", -}; +/* remove and FDB entry. */ +FdbEntry *fdb_entry_free(FdbEntry *fdb_entry) { + if (!fdb_entry) + return NULL; -DEFINE_STRING_TABLE_LOOKUP(fdb_ntf_flags, NeighborCacheEntryFlags); + if (fdb_entry->network) { + assert(fdb_entry->section); + hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section); + } + + network_config_section_free(fdb_entry->section); + return mfree(fdb_entry); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free); /* create a new FDB entry or get an existing one. */ static int fdb_entry_new_static( @@ -43,23 +49,21 @@ static int fdb_entry_new_static( assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); + + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; /* search entry in hashmap first. */ - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; - - fdb_entry = hashmap_get(network->fdb_entries_by_section, n); - if (fdb_entry) { - *ret = TAKE_PTR(fdb_entry); - - return 0; - } + fdb_entry = hashmap_get(network->fdb_entries_by_section, n); + if (fdb_entry) { + *ret = TAKE_PTR(fdb_entry); + return 0; } - if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX) + if (hashmap_size(network->fdb_entries_by_section) >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX) return -E2BIG; /* allocate space for and FDB entry. */ @@ -70,24 +74,18 @@ static int fdb_entry_new_static( /* init FDB structure. */ *fdb_entry = (FdbEntry) { .network = network, + .section = TAKE_PTR(n), .vni = VXLAN_VID_MAX + 1, .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF, }; - LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry); - network->n_static_fdb_entries++; + r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops); + if (r < 0) + return r; - if (filename) { - fdb_entry->section = TAKE_PTR(n); - - r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry); - if (r < 0) - return r; - } + r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry); + if (r < 0) + return r; /* return allocated FDB structure. */ *ret = TAKE_PTR(fdb_entry); @@ -114,7 +112,7 @@ static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) } /* send a request to the kernel to add a FDB entry in its static MAC table. */ -int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) { +static int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -171,22 +169,30 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) { return 1; } -/* remove and FDB entry. */ -void fdb_entry_free(FdbEntry *fdb_entry) { - if (!fdb_entry) - return; +int link_set_bridge_fdb(Link *link) { + FdbEntry *fdb_entry; + int r; - if (fdb_entry->network) { - LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry); - assert(fdb_entry->network->n_static_fdb_entries > 0); - fdb_entry->network->n_static_fdb_entries--; + assert(link); + assert(link->network); - if (fdb_entry->section) - hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section); + HASHMAP_FOREACH(fdb_entry, link->network->fdb_entries_by_section) { + r = fdb_entry_configure(link, fdb_entry); + if (r < 0) + return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); } - network_config_section_free(fdb_entry->section); - free(fdb_entry); + return 0; +} + +void network_drop_invalid_fdb_entries(Network *network) { + FdbEntry *fdb_entry; + + assert(network); + + HASHMAP_FOREACH(fdb_entry, network->fdb_entries_by_section) + if (section_is_invalid(fdb_entry->section)) + fdb_entry_free(fdb_entry); } /* parse the HW address from config files. */ @@ -352,6 +358,15 @@ int config_parse_fdb_vxlan_vni( return 0; } +static const char* const fdb_ntf_flags_table[_NEIGHBOR_CACHE_ENTRY_FLAGS_MAX] = { + [NEIGHBOR_CACHE_ENTRY_FLAGS_USE] = "use", + [NEIGHBOR_CACHE_ENTRY_FLAGS_SELF] = "self", + [NEIGHBOR_CACHE_ENTRY_FLAGS_MASTER] = "master", + [NEIGHBOR_CACHE_ENTRY_FLAGS_ROUTER] = "router", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(fdb_ntf_flags, NeighborCacheEntryFlags); + int config_parse_fdb_ntf_flags( const char *unit, const char *filename, diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h index 5e24ad6aee..935406e022 100644 --- a/src/network/networkd-fdb.h +++ b/src/network/networkd-fdb.h @@ -5,17 +5,16 @@ Copyright © 2014 Intel Corporation. All rights reserved. ***/ +#include #include #include "conf-parser.h" -#include "list.h" -#include "macro.h" +#include "ether-addr-util.h" +#include "in-addr-util.h" #include "networkd-util.h" typedef struct Network Network; -typedef struct FdbEntry FdbEntry; typedef struct Link Link; -typedef struct NetworkConfigSection NetworkConfigSection; typedef enum NeighborCacheEntryFlags { NEIGHBOR_CACHE_ENTRY_FLAGS_USE = NTF_USE, @@ -26,7 +25,7 @@ typedef enum NeighborCacheEntryFlags { _NEIGHBOR_CACHE_ENTRY_FLAGS_INVALID = -1, } NeighborCacheEntryFlags; -struct FdbEntry { +typedef struct FdbEntry { Network *network; NetworkConfigSection *section; @@ -38,17 +37,13 @@ struct FdbEntry { struct ether_addr mac_addr; union in_addr_union destination_addr; NeighborCacheEntryFlags fdb_ntf_flags; +} FdbEntry; - LIST_FIELDS(FdbEntry, static_fdb_entries); -}; +FdbEntry *fdb_entry_free(FdbEntry *fdb_entry); -void fdb_entry_free(FdbEntry *fdb_entry); -int fdb_entry_configure(Link *link, FdbEntry *fdb_entry); +void network_drop_invalid_fdb_entries(Network *network); -DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free); - -const char* fdb_ntf_flags_to_string(NeighborCacheEntryFlags i) _const_; -NeighborCacheEntryFlags fdb_ntf_flags_from_string(const char *s) _pure_; +int link_set_bridge_fdb(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_fdb_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vlan_id); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index e844799b57..b0e0a97ad8 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -142,23 +142,37 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { } } +static int ipv4ll_init(Link *link) { + int r; + + assert(link); + + if (link->ipv4ll) + return 0; + + r = sd_ipv4ll_new(&link->ipv4ll); + if (r < 0) + return r; + + r = sd_ipv4ll_attach_event(link->ipv4ll, link->manager->event, 0); + if (r < 0) + return r; + + return 0; +} + int ipv4ll_configure(Link *link) { uint64_t seed; int r; assert(link); - assert(link->network); - assert(link->network->link_local & (ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)); - if (!link->ipv4ll) { - r = sd_ipv4ll_new(&link->ipv4ll); - if (r < 0) - return r; + if (!link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) + return 0; - r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0); - if (r < 0) - return r; - } + r = ipv4ll_init(link); + if (r < 0) + return r; if (link->sd_device && net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) { @@ -182,6 +196,82 @@ int ipv4ll_configure(Link *link) { return 0; } +int ipv4ll_update_mac(Link *link) { + bool restart; + int r; + + assert(link); + + if (!link->ipv4ll) + return 0; + + restart = sd_ipv4ll_is_running(link->ipv4ll) > 0; + + if (restart) { + r = sd_ipv4ll_stop(link->ipv4ll); + if (r < 0) + return r; + } + + r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); + if (r < 0) + return r; + + if (restart) { + r = sd_ipv4ll_start(link->ipv4ll); + if (r < 0) + return r; + } + + return 0; +} + +int link_serialize_ipv4ll(Link *link, FILE *f) { + struct in_addr address; + int r; + + assert(link); + + if (!link->ipv4ll) + return 0; + + r = sd_ipv4ll_get_address(link->ipv4ll, &address); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + fputs("IPV4LL_ADDRESS=", f); + serialize_in_addrs(f, &address, 1, false, NULL); + fputc('\n', f); + + return 0; +} + +int link_deserialize_ipv4ll(Link *link, const char *ipv4ll_address) { + union in_addr_union address; + int r; + + assert(link); + + if (isempty(ipv4ll_address)) + return 0; + + r = in_addr_from_string(AF_INET, ipv4ll_address, &address); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to parse IPv4LL address: %s", ipv4ll_address); + + r = ipv4ll_init(link); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to initialize IPv4LL client: %m"); + + r = sd_ipv4ll_set_address(link->ipv4ll, &address.in); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to set initial IPv4LL address %s: %m", ipv4ll_address); + + return 0; +} + int config_parse_ipv4ll( const char* unit, const char *filename, diff --git a/src/network/networkd-ipv4ll.h b/src/network/networkd-ipv4ll.h index 49b6fb56ad..4833e304b6 100644 --- a/src/network/networkd-ipv4ll.h +++ b/src/network/networkd-ipv4ll.h @@ -8,5 +8,8 @@ typedef struct Link Link; int ipv4ll_configure(Link *link); +int ipv4ll_update_mac(Link *link); +int link_serialize_ipv4ll(Link *link, FILE *f); +int link_deserialize_ipv4ll(Link *link, const char *ipv4ll_address); CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll); diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c index 7051ba9dac..fb8464ff2f 100644 --- a/src/network/networkd-ipv6-proxy-ndp.c +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -2,9 +2,7 @@ #include #include -#include -#include "fileio.h" #include "netlink-util.h" #include "networkd-ipv6-proxy-ndp.h" #include "networkd-link.h" @@ -14,6 +12,50 @@ #include "string-util.h" #include "sysctl-util.h" +static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_message_warning_errno(link, m, r, "Could not add IPv6 proxy ndp address entry, ignoring"); + + return 1; +} + +/* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */ +static int ipv6_proxy_ndp_address_configure(Link *link, const struct in6_addr *address) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(address); + + /* create new netlink message */ + r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6); + if (r < 0) + return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m"); + + r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY); + if (r < 0) + return log_link_error_errno(link, r, "Could not set neighbor flags: %m"); + + r = sd_netlink_message_append_in6_addr(req, NDA_DST, address); + if (r < 0) + return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, set_ipv6_proxy_ndp_address_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + static bool ipv6_proxy_ndp_is_needed(Link *link) { assert(link); @@ -26,10 +68,7 @@ static bool ipv6_proxy_ndp_is_needed(Link *link) { if (link->network->ipv6_proxy_ndp >= 0) return link->network->ipv6_proxy_ndp; - if (link->network->n_ipv6_proxy_ndp_addresses == 0) - return false; - - return true; + return !set_isempty(link->network->ipv6_proxy_ndp_addresses); } static int ipv6_proxy_ndp_set(Link *link) { @@ -45,47 +84,31 @@ static int ipv6_proxy_ndp_set(Link *link) { r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v); if (r < 0) - log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m"); + return log_link_warning_errno(link, r, "Cannot configure proxy NDP for the interface: %m"); - return 0; + return v; } -static int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) { - _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL; +/* configure all ipv6 proxy ndp addresses */ +int link_set_ipv6_proxy_ndp_addresses(Link *link) { + struct in6_addr *address; + int r; - assert(network); - assert(ret); + assert(link); + assert(link->network); - /* allocate space for IPv6ProxyNDPAddress entry */ - ipv6_proxy_ndp_address = new(IPv6ProxyNDPAddress, 1); - if (!ipv6_proxy_ndp_address) - return -ENOMEM; + /* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */ + r = ipv6_proxy_ndp_set(link); + if (r <= 0) + return r; - *ipv6_proxy_ndp_address = (IPv6ProxyNDPAddress) { - .network = network, - }; - - LIST_PREPEND(ipv6_proxy_ndp_addresses, network->ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address); - network->n_ipv6_proxy_ndp_addresses++; - - *ret = TAKE_PTR(ipv6_proxy_ndp_address); - - return 0; -} - -void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) { - if (!ipv6_proxy_ndp_address) - return; - - if (ipv6_proxy_ndp_address->network) { - LIST_REMOVE(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address->network->ipv6_proxy_ndp_addresses, - ipv6_proxy_ndp_address); - - assert(ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses > 0); - ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses--; + SET_FOREACH(address, link->network->ipv6_proxy_ndp_addresses) { + r = ipv6_proxy_ndp_address_configure(link, address); + if (r < 0) + return r; } - free(ipv6_proxy_ndp_address); + return 0; } int config_parse_ipv6_proxy_ndp_address( @@ -100,26 +123,24 @@ int config_parse_ipv6_proxy_ndp_address( void *data, void *userdata) { + _cleanup_free_ struct in6_addr *address = NULL; Network *network = userdata; - _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL; - int r; union in_addr_union buffer; + int r; assert(filename); - assert(section); - assert(lvalue); assert(rvalue); - assert(data); + assert(network); - r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address); - if (r < 0) - return log_oom(); + if (isempty(rvalue)) { + network->ipv6_proxy_ndp_addresses = set_free_free(network->ipv6_proxy_ndp_addresses); + return 0; + } r = in_addr_from_string(AF_INET6, rvalue, &buffer); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse IPv6 proxy NDP address, ignoring: %s", - rvalue); + "Failed to parse IPv6 proxy NDP address, ignoring: %s", rvalue); return 0; } @@ -129,76 +150,15 @@ int config_parse_ipv6_proxy_ndp_address( return 0; } - ipv6_proxy_ndp_address->in_addr = buffer.in6; - ipv6_proxy_ndp_address = NULL; + address = newdup(struct in6_addr, &buffer.in6, 1); + if (!address) + return log_oom(); + + r = set_ensure_put(&network->ipv6_proxy_ndp_addresses, &in6_addr_hash_ops, address); + if (r < 0) + return log_oom(); + if (r > 0) + TAKE_PTR(address); return 0; } - -static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) - log_link_message_warning_errno(link, m, r, "Could not add IPv6 proxy ndp address entry, ignoring"); - - return 1; -} - -/* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */ -int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - sd_netlink *rtnl; - int r; - - assert(link); - assert(link->network); - assert(link->manager); - assert(ipv6_proxy_ndp_address); - - rtnl = link->manager->rtnl; - - /* create new netlink message */ - r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6); - if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m"); - - r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY); - if (r < 0) - return log_link_error_errno(link, r, "Could not set neighbor flags: %m"); - - r = sd_netlink_message_append_in6_addr(req, NDA_DST, &ipv6_proxy_ndp_address->in_addr); - if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); - - r = netlink_call_async(rtnl, NULL, req, set_ipv6_proxy_ndp_address_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 0; -} - -/* configure all ipv6 proxy ndp addresses */ -int ipv6_proxy_ndp_addresses_configure(Link *link) { - IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; - int r; - - assert(link); - - /* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */ - r = ipv6_proxy_ndp_set(link); - if (r != 0) - return r; - - LIST_FOREACH(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address, link->network->ipv6_proxy_ndp_addresses) { - r = ipv6_proxy_ndp_address_configure(link, ipv6_proxy_ndp_address); - if (r != 0) - return r; - } - return 0; -} diff --git a/src/network/networkd-ipv6-proxy-ndp.h b/src/network/networkd-ipv6-proxy-ndp.h index d6666beab5..e58b17ec94 100644 --- a/src/network/networkd-ipv6-proxy-ndp.h +++ b/src/network/networkd-ipv6-proxy-ndp.h @@ -2,24 +2,9 @@ #pragma once #include "conf-parser.h" -#include "list.h" -#include "macro.h" -typedef struct Network Network; -typedef struct IPv6ProxyNDPAddress IPv6ProxyNDPAddress; typedef struct Link Link; -struct IPv6ProxyNDPAddress { - Network *network; - struct in6_addr in_addr; - - LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); -}; - -void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address); -int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address); -int ipv6_proxy_ndp_addresses_configure(Link *link); - -DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6ProxyNDPAddress*, ipv6_proxy_ndp_address_free); +int link_set_ipv6_proxy_ndp_addresses(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_proxy_ndp_address); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 0cdd2b5121..b32a436be5 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -20,19 +20,25 @@ #include "missing_network.h" #include "netlink-util.h" #include "network-internal.h" +#include "networkd-address-label.h" +#include "networkd-address.h" #include "networkd-can.h" #include "networkd-dhcp-server.h" #include "networkd-dhcp4.h" #include "networkd-dhcp6.h" +#include "networkd-fdb.h" #include "networkd-ipv4ll.h" #include "networkd-ipv6-proxy-ndp.h" #include "networkd-link-bus.h" #include "networkd-link.h" #include "networkd-lldp-tx.h" #include "networkd-manager.h" +#include "networkd-mdb.h" #include "networkd-ndisc.h" #include "networkd-neighbor.h" +#include "networkd-nexthop.h" #include "networkd-sriov.h" +#include "networkd-sysctl.h" #include "networkd-radv.h" #include "networkd-routing-policy-rule.h" #include "networkd-wifi.h" @@ -49,88 +55,6 @@ #include "util.h" #include "vrf.h" -uint32_t link_get_vrf_table(Link *link) { - return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN; -} - -uint32_t link_get_dhcp_route_table(Link *link) { - /* When the interface is part of an VRF use the VRFs routing table, unless - * another table is explicitly specified. */ - if (link->network->dhcp_route_table_set) - return link->network->dhcp_route_table; - return link_get_vrf_table(link); -} - -uint32_t link_get_ipv6_accept_ra_route_table(Link *link) { - if (link->network->ipv6_accept_ra_route_table_set) - return link->network->ipv6_accept_ra_route_table; - return link_get_vrf_table(link); -} - -DUID* link_get_duid(Link *link) { - if (link->network->duid.type != _DUID_TYPE_INVALID) - return &link->network->duid; - else - return &link->manager->duid; -} - -static bool link_dhcp6_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->network->bond) - return false; - - if (link->iftype == ARPHRD_CAN) - return false; - - return link->network->dhcp & ADDRESS_FAMILY_IPV6; -} - -static bool link_dhcp4_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->network->bond) - return false; - - if (link->iftype == ARPHRD_CAN) - return false; - - return link->network->dhcp & ADDRESS_FAMILY_IPV4; -} - -static bool link_dhcp4_server_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->network->bond) - return false; - - if (link->iftype == ARPHRD_CAN) - return false; - - return link->network->dhcp_server; -} - bool link_ipv4ll_enabled(Link *link, AddressFamily mask) { assert(link); assert((mask & ~(ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) == 0); @@ -159,7 +83,7 @@ bool link_ipv4ll_enabled(Link *link, AddressFamily mask) { return link->network->link_local & mask; } -static bool link_ipv6ll_enabled(Link *link) { +bool link_ipv6ll_enabled(Link *link) { assert(link); if (!socket_ipv6_is_supported()) @@ -183,7 +107,7 @@ static bool link_ipv6ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV6; } -static bool link_ipv6_enabled(Link *link) { +bool link_ipv6_enabled(Link *link) { assert(link); if (!socket_ipv6_is_supported()) @@ -205,127 +129,6 @@ static bool link_ipv6_enabled(Link *link) { return false; } -static bool link_radv_enabled(Link *link) { - assert(link); - - if (!link_ipv6ll_enabled(link)) - return false; - - return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE; -} - -static bool link_ipv4_forward_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->network->ip_forward == _ADDRESS_FAMILY_INVALID) - return false; - - return link->network->ip_forward & ADDRESS_FAMILY_IPV4; -} - -static bool link_ipv6_forward_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->network->ip_forward == _ADDRESS_FAMILY_INVALID) - return false; - - return link->network->ip_forward & ADDRESS_FAMILY_IPV6; -} - -static bool link_proxy_arp_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->network->proxy_arp < 0) - return false; - - return true; -} - -static bool link_ipv6_accept_ra_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (!link_ipv6ll_enabled(link)) - return false; - - /* If unset use system default (enabled if local forwarding is disabled. - * disabled if local forwarding is enabled). - * If set, ignore or enforce RA independent of local forwarding state. - */ - if (link->network->ipv6_accept_ra < 0) - /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ - return !link_ipv6_forward_enabled(link); - else if (link->network->ipv6_accept_ra > 0) - /* accept RA even if ip_forward is enabled */ - return true; - else - /* ignore RA */ - return false; -} - -static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return _IPV6_PRIVACY_EXTENSIONS_INVALID; - - if (link->flags & IFF_LOOPBACK) - return _IPV6_PRIVACY_EXTENSIONS_INVALID; - - if (!link->network) - return _IPV6_PRIVACY_EXTENSIONS_INVALID; - - return link->network->ipv6_privacy_extensions; -} - -static int link_update_ipv6_sysctl(Link *link) { - bool enabled; - int r; - - if (link->flags & IFF_LOOPBACK) - return 0; - - enabled = link_ipv6_enabled(link); - if (enabled) { - r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false); - if (r < 0) - return log_link_warning_errno(link, r, "Cannot enable IPv6: %m"); - - log_link_info(link, "IPv6 successfully enabled"); - } - - return 0; -} - static bool link_is_enslaved(Link *link) { if (link->flags & IFF_SLAVE) /* Even if the link is not managed by networkd, honor IFF_SLAVE flag. */ @@ -703,8 +506,6 @@ static void link_free_engines(Link *link) { } static Link *link_free(Link *link) { - Address *address; - assert(link); link_ntp_settings_clear(link); @@ -728,6 +529,7 @@ static Link *link_free(Link *link) { link->addresses = set_free(link->addresses); link->addresses_foreign = set_free(link->addresses_foreign); + link->pool_addresses = set_free(link->pool_addresses); link->static_addresses = set_free(link->static_addresses); link->dhcp6_addresses = set_free(link->dhcp6_addresses); link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old); @@ -735,11 +537,6 @@ static Link *link_free(Link *link) { link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old); link->ndisc_addresses = set_free(link->ndisc_addresses); - while ((address = link->pool_addresses)) { - LIST_REMOVE(addresses, link->pool_addresses, address); - address_free(address); - } - link_lldp_emit_stop(link); link_free_engines(link); free(link->lease_file); @@ -809,7 +606,6 @@ static void link_enter_unmanaged(Link *link) { int link_stop_clients(Link *link, bool may_keep_dhcp) { int r = 0, k; - Address *ad; assert(link); assert(link->manager); @@ -832,13 +628,9 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) { r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m"); } - if (link->network) - LIST_FOREACH(addresses, ad, link->network->static_addresses) - if (ad->acd && sd_ipv4acd_is_running(ad->acd) == 0) { - k = sd_ipv4acd_stop(ad->acd); - if (k < 0) - r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m"); - } + k = ipv4_dad_stop(link); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m"); if (link->dhcp6_client) { k = sd_dhcp6_client_stop(link->dhcp6_client); @@ -846,11 +638,9 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) { r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); } - if (link_dhcp6_pd_is_enabled(link)) { - k = dhcp6_pd_remove(link); - if (k < 0) - r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m"); - } + k = dhcp6_pd_remove(link); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m"); if (link->ndisc) { k = sd_ndisc_stop(link->ndisc); @@ -925,178 +715,6 @@ static void link_enter_configured(Link *link) { link_dirty(link); } -static int link_request_set_routing_policy_rule(Link *link) { - RoutingPolicyRule *rule, *rrule = NULL; - int r; - - assert(link); - assert(link->network); - - link->routing_policy_rules_configured = false; - - LIST_FOREACH(rules, rule, link->network->rules) { - r = routing_policy_rule_get(link->manager, rule, &rrule); - if (r >= 0) { - if (r == 0) - (void) routing_policy_rule_make_local(link->manager, rrule); - continue; - } - - r = routing_policy_rule_configure(rule, link, NULL); - if (r < 0) - return log_link_warning_errno(link, r, "Could not set routing policy rules: %m"); - if (r > 0) - link->routing_policy_rule_messages++; - } - - routing_policy_rule_purge(link->manager, link); - if (link->routing_policy_rule_messages == 0) - link->routing_policy_rules_configured = true; - else { - log_link_debug(link, "Setting routing policy rules"); - link_set_state(link, LINK_STATE_CONFIGURING); - } - - return 0; -} - -static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - assert(link->nexthop_messages > 0); - assert(IN_SET(link->state, LINK_STATE_CONFIGURING, - LINK_STATE_FAILED, LINK_STATE_LINGER)); - - link->nexthop_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_message_warning_errno(link, m, r, "Could not set nexthop"); - link_enter_failed(link); - return 1; - } - - if (link->nexthop_messages == 0) { - log_link_debug(link, "Nexthop set"); - link->static_nexthops_configured = true; - link_check_ready(link); - } - - return 1; -} - -static int link_request_set_nexthop(Link *link) { - NextHop *nh; - int r; - - link->static_nexthops_configured = false; - - LIST_FOREACH(nexthops, nh, link->network->static_nexthops) { - r = nexthop_configure(nh, link, nexthop_handler); - if (r < 0) - return log_link_warning_errno(link, r, "Could not set nexthop: %m"); - if (r > 0) - link->nexthop_messages++; - } - - if (link->nexthop_messages == 0) { - link->static_nexthops_configured = true; - link_check_ready(link); - } else { - log_link_debug(link, "Setting nexthop"); - link_set_state(link, LINK_STATE_CONFIGURING); - } - - return 1; -} - -static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - assert(link->route_messages > 0); - assert(IN_SET(link->state, LINK_STATE_CONFIGURING, - LINK_STATE_FAILED, LINK_STATE_LINGER)); - - link->route_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_message_warning_errno(link, m, r, "Could not set route"); - link_enter_failed(link); - return 1; - } - - if (link->route_messages == 0) { - log_link_debug(link, "Routes set"); - link->static_routes_configured = true; - link_request_set_nexthop(link); - } - - return 1; -} - -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 */ - _PHASE_MAX - } phase; - Route *rt; - int r; - - assert(link); - assert(link->network); - assert(link->state != _LINK_STATE_INVALID); - - link->static_routes_configured = false; - - if (!link->addresses_ready) - return 0; - - if (!link_has_carrier(link) && !link->network->configure_without_carrier) - /* During configuring addresses, the link lost its carrier. As networkd is dropping - * the addresses now, let's not configure the routes either. */ - return 0; - - r = link_request_set_routing_policy_rule(link); - if (r < 0) - return r; - - /* 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) { - if (rt->gateway_from_dhcp) - continue; - - if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY)) - continue; - - r = route_configure(rt, link, route_handler, NULL); - if (r < 0) - return log_link_warning_errno(link, r, "Could not set routes: %m"); - if (r > 0) - link->route_messages++; - } - - if (link->route_messages == 0) { - link->static_routes_configured = true; - link_request_set_nexthop(link); - } else { - log_link_debug(link, "Setting routes"); - link_set_state(link, LINK_STATE_CONFIGURING); - } - - return 0; -} - void link_check_ready(Link *link) { Address *a; @@ -1219,165 +837,13 @@ void link_check_ready(Link *link) { return; } -static int link_request_set_neighbors(Link *link) { - Neighbor *neighbor; +static int link_set_static_configs(Link *link) { int r; assert(link); assert(link->network); assert(link->state != _LINK_STATE_INVALID); - link->neighbors_configured = false; - - LIST_FOREACH(neighbors, neighbor, link->network->neighbors) { - r = neighbor_configure(neighbor, link, NULL); - if (r < 0) - return log_link_warning_errno(link, r, "Could not set neighbor: %m"); - } - - if (link->neighbor_messages == 0) { - link->neighbors_configured = true; - link_check_ready(link); - } else { - log_link_debug(link, "Setting neighbors"); - link_set_state(link, LINK_STATE_CONFIGURING); - } - - return 0; -} - -static int link_set_bridge_fdb(Link *link) { - FdbEntry *fdb_entry; - int r; - - LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { - r = fdb_entry_configure(link, fdb_entry); - if (r < 0) - return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); - } - - return 0; -} - -static int static_address_ready_callback(Address *address) { - Address *a; - Link *link; - - assert(address); - assert(address->link); - - link = address->link; - - if (!link->addresses_configured) - return 0; - - SET_FOREACH(a, link->static_addresses) - if (!address_is_ready(a)) { - _cleanup_free_ char *str = NULL; - - (void) in_addr_to_string(a->family, &a->in_addr, &str); - log_link_debug(link, "an address %s/%u is not ready", strnull(str), a->prefixlen); - return 0; - } - - /* This should not be called again */ - SET_FOREACH(a, link->static_addresses) - a->callback = NULL; - - link->addresses_ready = true; - - return link_request_set_routes(link); -} - -static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(rtnl); - assert(m); - assert(link); - assert(link->ifname); - assert(link->address_messages > 0); - assert(IN_SET(link->state, LINK_STATE_CONFIGURING, - LINK_STATE_FAILED, LINK_STATE_LINGER)); - - link->address_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_message_warning_errno(link, m, r, "Could not set address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); - - if (link->address_messages == 0) { - Address *a; - - log_link_debug(link, "Addresses set"); - link->addresses_configured = true; - - /* When all static addresses are already ready, then static_address_ready_callback() - * will not be called automatically. So, call it here. */ - a = set_first(link->static_addresses); - if (!a) { - log_link_warning(link, "No static address is stored."); - link_enter_failed(link); - return 1; - } - if (!a->callback) { - log_link_warning(link, "Address ready callback is not set."); - link_enter_failed(link); - return 1; - } - r = a->callback(a); - if (r < 0) - link_enter_failed(link); - } - - return 1; -} - -static int static_address_configure(Address *address, Link *link, bool update) { - Address *ret; - int r; - - assert(address); - assert(link); - - r = address_configure(address, link, address_handler, update, &ret); - if (r < 0) - return log_link_warning_errno(link, r, "Could not configure static address: %m"); - - link->address_messages++; - - r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to store static address: %m"); - - ret->callback = static_address_ready_callback; - - return 0; -} - -static int link_request_set_addresses(Link *link) { - AddressLabel *label; - Address *ad; - Prefix *p; - int r; - - assert(link); - assert(link->network); - assert(link->state != _LINK_STATE_INVALID); - - if (link->address_remove_messages != 0) { - log_link_debug(link, "Removing old addresses, new addresses will be configured later."); - link->request_static_addresses = true; - return 0; - } - /* Reset all *_configured flags we are configuring. */ link->request_static_addresses = false; link->addresses_configured = false; @@ -1395,97 +861,22 @@ static int link_request_set_addresses(Link *link) { if (r < 0) return r; - r = link_request_set_neighbors(link); + r = link_set_neighbors(link); if (r < 0) return r; - LIST_FOREACH(addresses, ad, link->network->static_addresses) { - bool update; + r = link_set_addresses(link); + if (r < 0) + return r; - if (ad->family == AF_INET6 && !in_addr_is_null(ad->family, &ad->in_addr_peer)) - update = address_get(link, ad->family, &ad->in_addr_peer, ad->prefixlen, NULL) > 0; - else - update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0; - - r = static_address_configure(ad, link, update); - if (r < 0) - return r; - } - - if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) - LIST_FOREACH(prefixes, p, link->network->static_prefixes) { - _cleanup_(address_freep) Address *address = NULL; - - if (!p->assign) - continue; - - r = address_new(&address); - if (r < 0) - return log_oom(); - - r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen); - if (r < 0) - return log_link_warning_errno(link, r, "Could not get RA prefix: %m"); - - r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); - if (r < 0) - return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m"); - - address->family = AF_INET6; - r = static_address_configure(address, link, true); - if (r < 0) - return r; - } - - LIST_FOREACH(labels, label, link->network->address_labels) { - r = address_label_configure(label, link, NULL, false); - if (r < 0) - return log_link_warning_errno(link, r, "Could not set address label: %m"); - - link->address_label_messages++; - } + r = link_set_address_labels(link); + if (r < 0) + return r; /* now that we can figure out a default address for the dhcp server, start it */ - if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) { - r = dhcp4_server_configure(link); - if (r < 0) - return r; - log_link_debug(link, "Offering DHCPv4 leases"); - } - - if (link->address_messages == 0) { - link->addresses_configured = true; - link->addresses_ready = true; - r = link_request_set_routes(link); - if (r < 0) - return r; - } else { - log_link_debug(link, "Setting addresses"); - link_set_state(link, LINK_STATE_CONFIGURING); - } - - return 0; -} - -static int link_set_bridge_vlan(Link *link) { - int r; - - r = br_vlan_configure(link, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap); + r = dhcp4_server_configure(link); if (r < 0) - log_link_error_errno(link, r, "Failed to assign VLANs to bridge port: %m"); - - return r; -} - -static int link_set_proxy_arp(Link *link) { - int r; - - if (!link_proxy_arp_enabled(link)) - return 0; - - r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0); - if (r < 0) - log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m"); + return r; return 0; } @@ -1679,9 +1070,7 @@ static int link_acquire_ipv6_conf(Link *link) { assert(link); - if (link_ipv6_accept_ra_enabled(link)) { - assert(link->ndisc); - + if (link->ndisc) { log_link_debug(link, "Discovering IPv6 routers"); r = sd_ndisc_start(link->ndisc); @@ -1689,7 +1078,7 @@ static int link_acquire_ipv6_conf(Link *link) { return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); } - if (link_radv_enabled(link)) { + if (link->radv) { assert(link->radv); assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); @@ -1741,9 +1130,7 @@ static int link_acquire_ipv4_conf(Link *link) { return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); } - if (link_dhcp4_enabled(link)) { - assert(link->dhcp_client); - + if (link->dhcp_client) { log_link_debug(link, "Acquiring DHCPv4 lease"); r = sd_dhcp_client_start(link->dhcp_client); @@ -1769,11 +1156,9 @@ static int link_acquire_conf(Link *link) { return r; } - if (link_lldp_emit_enabled(link)) { - r = link_lldp_emit_start(link); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); - } + r = link_lldp_emit_start(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); return 0; } @@ -2362,12 +1747,9 @@ static int link_joined(Link *link) { log_link_error_errno(link, r, "Failed to add to bond master's slave list: %m"); } - if (link->network->use_br_vlan && - (link->network->bridge || streq_ptr("bridge", link->kind))) { - r = link_set_bridge_vlan(link); - if (r < 0) - log_link_error_errno(link, r, "Could not set bridge vlan: %m"); - } + r = link_set_bridge_vlan(link); + if (r < 0) + log_link_error_errno(link, r, "Could not set bridge vlan: %m"); /* Skip setting up addresses until it gets carrier, or it would try to set addresses twice, @@ -2376,7 +1758,7 @@ static int link_joined(Link *link) { return 0; link_set_state(link, LINK_STATE_CONFIGURING); - return link_request_set_addresses(link); + return link_set_static_configs(link); } static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -2516,519 +1898,41 @@ static int link_enter_join_netdev(Link *link) { return 0; } -static int link_set_ipv4_forward(Link *link) { - int r; - - if (!link_ipv4_forward_enabled(link)) - return 0; - - /* We propagate the forwarding flag from one interface to the - * global setting one way. This means: as long as at least one - * interface was configured at any time that had IP forwarding - * enabled the setting will stay on for good. We do this - * primarily to keep IPv4 and IPv6 packet forwarding behaviour - * somewhat in sync (see below). */ - - r = sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1"); - if (r < 0) - log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m"); - - return 0; -} - -static int link_set_ipv6_forward(Link *link) { - int r; - - if (!link_ipv6_forward_enabled(link)) - return 0; - - /* On Linux, the IPv6 stack does not know a per-interface - * packet forwarding setting: either packet forwarding is on - * for all, or off for all. We hence don't bother with a - * per-interface setting, but simply propagate the interface - * flag, if it is set, to the global flag, one-way. Note that - * while IPv4 would allow a per-interface flag, we expose the - * same behaviour there and also propagate the setting from - * one to all, to keep things simple (see above). */ - - r = sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1"); - if (r < 0) - log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m"); - - return 0; -} - -static int link_set_ipv6_privacy_extensions(Link *link) { - IPv6PrivacyExtensions s; - int r; - - s = link_ipv6_privacy_extensions(link); - if (s < 0) - return 0; - - r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) link->network->ipv6_privacy_extensions); - if (r < 0) - log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m"); - - return 0; -} - -static int link_set_ipv6_accept_ra(Link *link) { - int r; - - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) - return 0; - - r = sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0"); - if (r < 0) - log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m"); - - return 0; -} - -static int link_set_ipv6_dad_transmits(Link *link) { - int r; - - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) - return 0; - - if (link->network->ipv6_dad_transmits < 0) - return 0; - - r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits); - if (r < 0) - log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m"); - - return 0; -} - -static int link_set_ipv6_hop_limit(Link *link) { - int r; - - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) - return 0; - - if (link->network->ipv6_hop_limit < 0) - return 0; - - r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit); - if (r < 0) - log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m"); - - return 0; -} - -static int link_set_ipv6_mtu(Link *link) { - int r; - - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (link->network->ipv6_mtu == 0) - return 0; - - /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes - * on the interface. Bump up IPv6 MTU bytes to IPV6_MTU_MIN. */ - if (link->network->ipv6_mtu < IPV6_MIN_MTU) { - log_link_notice(link, "Bumping IPv6 MTU to "STRINGIFY(IPV6_MIN_MTU)" byte minimum required"); - link->network->ipv6_mtu = IPV6_MIN_MTU; - } - - r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu); - if (r < 0) { - if (link->mtu < link->network->ipv6_mtu) - log_link_warning(link, "Cannot set IPv6 MTU %"PRIu32" higher than device MTU %"PRIu32, - link->network->ipv6_mtu, link->mtu); - else - log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface: %m"); - } - - link->ipv6_mtu_set = true; - - return 0; -} - -static int link_set_ipv4_accept_local(Link *link) { - int r; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (link->network->ipv4_accept_local < 0) - return 0; - - r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local); - if (r < 0) - log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface: %m"); - - return 0; -} - -static bool link_is_static_address_configured(Link *link, Address *address) { - Address *net_address; - - assert(link); - assert(address); - - if (!link->network) - return false; - - LIST_FOREACH(addresses, net_address, link->network->static_addresses) - if (address_equal(net_address, address)) - return true; - else if (address->family == AF_INET6 && net_address->family == AF_INET6 && - in_addr_equal(AF_INET6, &address->in_addr, &net_address->in_addr_peer) > 0) - return true; - - 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; - - assert(link); - assert(route); - - if (!link->network) - return false; - - LIST_FOREACH(routes, net_route, link->network->static_routes) - if (route_equal(net_route, route)) - return true; - - return false; -} - -static bool link_address_is_dynamic(Link *link, Address *address) { - Route *route; - - assert(link); - assert(address); - - if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME) - return true; - - /* Even when the address is leased from a DHCP server, networkd assign the address - * without lifetime when KeepConfiguration=dhcp. So, let's check that we have - * corresponding routes with RTPROT_DHCP. */ - SET_FOREACH(route, link->routes_foreign) { - if (route->protocol != RTPROT_DHCP) - continue; - - if (address->family != route->family) - continue; - - if (in_addr_equal(address->family, &address->in_addr, &route->prefsrc)) - return true; - } - - return false; -} - -static int link_enumerate_ipv6_tentative_addresses(Link *link) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *addr; - int r; - - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - - r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_GETADDR, 0, AF_INET6); - if (r < 0) - return r; - - r = sd_netlink_call(link->manager->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (addr = reply; addr; addr = sd_netlink_message_next(addr)) { - unsigned char flags; - int ifindex; - - r = sd_rtnl_message_addr_get_ifindex(addr, &ifindex); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: invalid ifindex, ignoring: %m"); - continue; - } else if (link->ifindex != ifindex) - continue; - - r = sd_rtnl_message_addr_get_flags(addr, &flags); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address message with invalid flags, ignoring: %m"); - continue; - } else if (!(flags & IFA_F_TENTATIVE)) - continue; - - log_link_debug(link, "Found tentative ipv6 link-local address"); - (void) manager_rtnl_process_address(link->manager->rtnl, addr, link->manager); - } - - return 0; -} - static int link_drop_foreign_config(Link *link) { - Address *address; - Neighbor *neighbor; - Route *route; int r; - /* The kernel doesn't notify us about tentative addresses; - * so if ipv6ll is disabled, we need to enumerate them now so we can drop them below */ - if (!link_ipv6ll_enabled(link)) { - r = link_enumerate_ipv6_tentative_addresses(link); - if (r < 0) - return r; - } + r = link_drop_foreign_addresses(link); + if (r < 0) + return r; - SET_FOREACH(address, link->addresses_foreign) { - /* we consider IPv6LL addresses to be managed by the kernel */ - if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link)) - continue; + r = link_drop_foreign_neighbors(link); + if (r < 0) + return r; - if (link_address_is_dynamic(link, address)) { - if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) - continue; - } else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) - continue; - - if (link_is_static_address_configured(link, address)) { - r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Failed to add address: %m"); - } else { - r = address_remove(address, link, NULL); - if (r < 0) - return r; - } - } - - SET_FOREACH(neighbor, link->neighbors_foreign) { - 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) { - /* do not touch routes managed by the kernel */ - if (route->protocol == RTPROT_KERNEL) - continue; - - /* do not touch multicast route added by kernel */ - /* FIXME: Why the kernel adds this route with protocol RTPROT_BOOT??? We need to investigate that. - * https://tools.ietf.org/html/rfc4862#section-5.4 may explain why. */ - if (route->protocol == RTPROT_BOOT && - route->family == AF_INET6 && - route->dst_prefixlen == 8 && - in_addr_equal(AF_INET6, &route->dst, &(union in_addr_union) { .in6 = {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}} })) - continue; - - if (route->protocol == RTPROT_STATIC && link->network && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) - continue; - - if (route->protocol == RTPROT_DHCP && link->network && - FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) - continue; - - if (link_is_static_route_configured(link, route)) { - r = route_add(link, route, NULL); - if (r < 0) - return r; - } else { - r = route_remove(route, link, NULL); - if (r < 0) - return r; - } - } - - return 0; -} - -static int remove_static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(m); - assert(link); - assert(link->ifname); - assert(link->address_remove_messages > 0); - - link->address_remove_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 != -EADDRNOTAVAIL) - log_link_message_warning_errno(link, m, r, "Could not drop address"); - else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); - - if (link->address_remove_messages == 0 && link->request_static_addresses) { - link_set_state(link, LINK_STATE_CONFIGURING); - r = link_request_set_addresses(link); - if (r < 0) - link_enter_failed(link); - } - - return 1; + return link_drop_foreign_routes(link); } static int link_drop_config(Link *link) { - Address *address, *pool_address; - Neighbor *neighbor; - Route *route; int r; - SET_FOREACH(address, link->addresses) { - /* we consider IPv6LL addresses to be managed by the kernel */ - if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link)) - continue; + r = link_drop_addresses(link); + if (r < 0) + return r; - r = address_remove(address, link, remove_static_address_handler); - if (r < 0) - return r; + r = link_drop_neighbors(link); + if (r < 0) + return r; - link->address_remove_messages++; - - /* If this address came from an address pool, clean up the pool */ - LIST_FOREACH(addresses, pool_address, link->pool_addresses) - if (address_equal(address, pool_address)) { - LIST_REMOVE(addresses, link->pool_addresses, pool_address); - address_free(pool_address); - break; - } - } - - SET_FOREACH(neighbor, link->neighbors) { - r = neighbor_remove(neighbor, link, NULL); - if (r < 0) - return r; - } - - SET_FOREACH(route, link->routes) { - /* do not touch routes managed by the kernel */ - if (route->protocol == RTPROT_KERNEL) - continue; - - r = route_remove(route, link, NULL); - if (r < 0) - return r; - } + r = link_drop_routes(link); + if (r < 0) + return r; ndisc_flush(link); return 0; } -static int link_configure_ipv4_dad(Link *link) { - Address *address; - int r; - - assert(link); - assert(link->network); - - LIST_FOREACH(addresses, address, link->network->static_addresses) - if (address->family == AF_INET && - FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) { - r = configure_ipv4_duplicate_address_detection(link, address); - if (r < 0) - return log_link_error_errno(link, r, "Failed to configure IPv4ACD: %m"); - } - - return 0; -} - -static int link_configure_traffic_control(Link *link) { - TrafficControl *tc; - int r; - - link->tc_configured = false; - link->tc_messages = 0; - - ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section) { - r = traffic_control_configure(link, tc); - if (r < 0) - return r; - } - - if (link->tc_messages == 0) - link->tc_configured = true; - else - log_link_debug(link, "Configuring traffic control"); - - return 0; -} - -static int link_configure_sr_iov(Link *link) { - SRIOV *sr_iov; - int r; - - link->sr_iov_configured = false; - link->sr_iov_messages = 0; - - ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) { - r = sr_iov_configure(link, sr_iov); - if (r < 0) - return r; - } - - if (link->sr_iov_messages == 0) - link->sr_iov_configured = true; - else - log_link_debug(link, "Configuring SR-IOV"); - - return 0; -} - -static int link_configure(Link *link) { +int link_configure(Link *link) { int r; assert(link); @@ -3046,43 +1950,11 @@ static int link_configure(Link *link) { if (link->iftype == ARPHRD_CAN) return link_configure_can(link); - /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled - * for this interface, then enable IPv6 */ - (void) link_update_ipv6_sysctl(link); - - r = link_set_proxy_arp(link); - if (r < 0) - return r; - - r = ipv6_proxy_ndp_addresses_configure(link); + r = link_set_sysctl(link); if (r < 0) return r; - r = link_set_ipv4_forward(link); - if (r < 0) - return r; - - r = link_set_ipv6_forward(link); - if (r < 0) - return r; - - r = link_set_ipv6_privacy_extensions(link); - if (r < 0) - return r; - - r = link_set_ipv6_accept_ra(link); - if (r < 0) - return r; - - r = link_set_ipv6_dad_transmits(link); - if (r < 0) - return r; - - r = link_set_ipv6_hop_limit(link); - if (r < 0) - return r; - - r = link_set_ipv4_accept_local(link); + r = link_set_ipv6_proxy_ndp_addresses(link); if (r < 0) return r; @@ -3094,56 +1966,29 @@ static int link_configure(Link *link) { if (r < 0) return r; - if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) { - r = ipv4ll_configure(link); - if (r < 0) - return r; - } + r = ipv4ll_configure(link); + if (r < 0) + return r; - if (link_dhcp4_enabled(link)) { - r = dhcp4_set_promote_secondaries(link); - if (r < 0) - return r; + r = dhcp4_configure(link); + if (r < 0) + return r; - r = dhcp4_configure(link); - if (r < 0) - return r; - } + r = dhcp6_configure(link); + if (r < 0) + return r; - if (link_dhcp4_server_enabled(link)) { - r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex); - if (r < 0) - return r; + r = ndisc_configure(link); + if (r < 0) + return r; - r = sd_dhcp_server_attach_event(link->dhcp_server, NULL, 0); - if (r < 0) - return r; - } + r = radv_configure(link); + if (r < 0) + return r; - if (link_dhcp6_enabled(link) || - link_ipv6_accept_ra_enabled(link)) { - r = dhcp6_configure(link); - if (r < 0) - return r; - } - - if (link_ipv6_accept_ra_enabled(link)) { - r = ndisc_configure(link); - if (r < 0) - return r; - } - - if (link_radv_enabled(link)) { - r = radv_configure(link); - if (r < 0) - return r; - } - - if (link_lldp_rx_enabled(link)) { - r = link_lldp_rx_configure(link); - if (r < 0) - return r; - } + r = link_lldp_rx_configure(link); + if (r < 0) + return r; r = link_configure_mtu(link); if (r < 0) @@ -3153,10 +1998,6 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = link_configure_ipv4_dad(link); - if (r < 0) - return r; - return link_configure_continue(link); } @@ -3195,7 +2036,7 @@ static int link_configure_continue(Link *link) { * we must set this here, after we've set device mtu */ r = link_set_ipv6_mtu(link); if (r < 0) - return r; + log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface, ignoring: %m"); if (link_has_carrier(link) || link->network->configure_without_carrier) { r = link_acquire_conf(link); @@ -3206,139 +2047,6 @@ static int link_configure_continue(Link *link) { return link_enter_join_netdev(link); } -static int duid_set_uuid(DUID *duid, sd_id128_t uuid) { - assert(duid); - - if (duid->raw_data_len > 0) - return 0; - - if (duid->type != DUID_TYPE_UUID) - return -EINVAL; - - memcpy(&duid->raw_data, &uuid, sizeof(sd_id128_t)); - duid->raw_data_len = sizeof(sd_id128_t); - - return 1; -} - -int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - Manager *manager = userdata; - const sd_bus_error *e; - const void *a; - size_t sz; - DUID *duid; - Link *link; - int r; - - assert(m); - assert(manager); - - e = sd_bus_message_get_error(m); - if (e) { - log_error_errno(sd_bus_error_get_errno(e), - "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s", - e->message); - goto configure; - } - - r = sd_bus_message_read_array(m, 'y', &a, &sz); - if (r < 0) - goto configure; - - if (sz != sizeof(sd_id128_t)) { - log_error("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID."); - goto configure; - } - - memcpy(&manager->product_uuid, a, sz); - while ((duid = set_steal_first(manager->duids_requesting_uuid))) - (void) duid_set_uuid(duid, manager->product_uuid); - - manager->duids_requesting_uuid = set_free(manager->duids_requesting_uuid); - -configure: - while ((link = set_steal_first(manager->links_requesting_uuid))) { - link_unref(link); - - r = link_configure(link); - if (r < 0) - link_enter_failed(link); - } - - manager->links_requesting_uuid = set_free(manager->links_requesting_uuid); - - /* To avoid calling GetProductUUID() bus method so frequently, set the flag below - * even if the method fails. */ - manager->has_product_uuid = true; - - return 1; -} - -static bool link_requires_uuid(Link *link) { - const DUID *duid; - - assert(link); - assert(link->manager); - assert(link->network); - - duid = link_get_duid(link); - if (duid->type != DUID_TYPE_UUID || duid->raw_data_len != 0) - return false; - - if (link_dhcp4_enabled(link) && IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY)) - return true; - - if (link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link)) - return true; - - return false; -} - -static int link_configure_duid(Link *link) { - Manager *m; - DUID *duid; - int r; - - assert(link); - assert(link->manager); - assert(link->network); - - m = link->manager; - duid = link_get_duid(link); - - if (!link_requires_uuid(link)) - return 1; - - if (m->has_product_uuid) { - (void) duid_set_uuid(duid, m->product_uuid); - return 1; - } - - if (!m->links_requesting_uuid) { - r = manager_request_product_uuid(m, link); - if (r < 0) { - if (r == -ENOMEM) - return r; - - log_link_warning_errno(link, r, - "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m"); - return 1; - } - } else { - r = set_put(m->links_requesting_uuid, link); - if (r < 0) - return log_oom(); - if (r > 0) - link_ref(link); - - r = set_put(m->duids_requesting_uuid, duid); - if (r < 0) - return log_oom(); - } - - return 0; -} - static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool force) { Network *network; int r; @@ -3380,7 +2088,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for if (r < 0) return r; - if (link_dhcp4_server_enabled(link)) + if (link->dhcp_server) (void) sd_dhcp_server_stop(link->dhcp_server); r = link_drop_config(link); @@ -3612,7 +2320,6 @@ static int link_load(Link *link) { *routes = NULL, *dhcp4_address = NULL, *ipv4ll_address = NULL; - union in_addr_union address; int r; assert(link); @@ -3651,145 +2358,21 @@ static int link_load(Link *link) { network_file_fail: - for (const char *p = addresses; p; ) { - _cleanup_free_ char *address_str = NULL; - char *prefixlen_str; - int family; - unsigned char prefixlen; + r = link_deserialize_addresses(link, addresses); + if (r < 0) + log_link_warning_errno(link, r, "Failed to load addresses from %s, ignoring: %m", link->state_file); - r = extract_first_word(&p, &address_str, NULL, 0); - if (r < 0) - log_link_warning_errno(link, r, "failed to parse ADDRESSES: %m"); - if (r <= 0) - break; + r = link_deserialize_routes(link, routes); + if (r < 0) + log_link_warning_errno(link, r, "Failed to load routes from %s, ignoring: %m", link->state_file); - prefixlen_str = strchr(address_str, '/'); - if (!prefixlen_str) { - log_link_debug(link, "Failed to parse address and prefix length %s", address_str); - continue; - } - *prefixlen_str++ = '\0'; + r = link_deserialize_dhcp4(link, dhcp4_address); + if (r < 0) + log_link_warning_errno(link, r, "Failed to load DHCPv4 address from %s, ignoring: %m", link->state_file); - r = sscanf(prefixlen_str, "%hhu", &prefixlen); - if (r != 1) { - log_link_error(link, "Failed to parse prefixlen %s", prefixlen_str); - continue; - } - - r = in_addr_from_string_auto(address_str, &family, &address); - if (r < 0) { - log_link_debug_errno(link, r, "Failed to parse address %s: %m", address_str); - continue; - } - - r = address_add(link, family, &address, prefixlen, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Failed to add address: %m"); - } - - for (const char *p = routes; p; ) { - _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL; - _cleanup_(route_freep) Route *tmp = NULL; - _cleanup_free_ char *route_str = NULL; - char *prefixlen_str; - Route *route; - - r = extract_first_word(&p, &route_str, NULL, 0); - if (r < 0) - log_link_debug_errno(link, r, "failed to parse ROUTES: %m"); - if (r <= 0) - break; - - prefixlen_str = strchr(route_str, '/'); - if (!prefixlen_str) { - log_link_debug(link, "Failed to parse route %s", route_str); - continue; - } - *prefixlen_str++ = '\0'; - - r = route_new(&tmp); - if (r < 0) - return log_oom(); - - r = sscanf(prefixlen_str, - "%hhu/%hhu/%"SCNu32"/%"PRIu32"/"USEC_FMT, - &tmp->dst_prefixlen, - &tmp->tos, - &tmp->priority, - &tmp->table, - &tmp->lifetime); - if (r != 5) { - log_link_debug(link, - "Failed to parse destination prefix length, tos, priority, table or expiration %s", - prefixlen_str); - continue; - } - - r = in_addr_from_string_auto(route_str, &tmp->family, &tmp->dst); - if (r < 0) { - log_link_debug_errno(link, r, "Failed to parse route destination %s: %m", route_str); - continue; - } - - r = route_add(link, tmp, &route); - if (r < 0) - return log_link_error_errno(link, r, "Failed to add route: %m"); - - if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) { - r = sd_event_add_time(link->manager->event, &expire, - clock_boottime_or_monotonic(), - route->lifetime, 0, route_expire_handler, route); - if (r < 0) - log_link_warning_errno(link, r, "Could not arm route expiration handler: %m"); - } - - sd_event_source_unref(route->expire); - route->expire = TAKE_PTR(expire); - } - - if (dhcp4_address) { - r = in_addr_from_string(AF_INET, dhcp4_address, &address); - if (r < 0) { - log_link_debug_errno(link, r, "Failed to parse DHCPv4 address %s: %m", dhcp4_address); - goto dhcp4_address_fail; - } - - r = sd_dhcp_client_new(&link->dhcp_client, link->network ? link->network->dhcp_anonymize : 0); - if (r < 0) - return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m"); - - r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0); - if (r < 0) - return log_link_error_errno(link, r, "Failed to attach DHCPv4 event: %m"); - - r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in); - if (r < 0) - return log_link_error_errno(link, r, "Failed to set initial DHCPv4 address %s: %m", dhcp4_address); - } - -dhcp4_address_fail: - - if (ipv4ll_address) { - r = in_addr_from_string(AF_INET, ipv4ll_address, &address); - if (r < 0) { - log_link_debug_errno(link, r, "Failed to parse IPv4LL address %s: %m", ipv4ll_address); - goto ipv4ll_address_fail; - } - - r = sd_ipv4ll_new(&link->ipv4ll); - if (r < 0) - return log_link_error_errno(link, r, "Failed to create IPv4LL client: %m"); - - r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0); - if (r < 0) - return log_link_error_errno(link, r, "Failed to attach IPv4LL event: %m"); - - r = sd_ipv4ll_set_address(link->ipv4ll, &address.in); - if (r < 0) - return log_link_error_errno(link, r, "Failed to set initial IPv4LL address %s: %m", ipv4ll_address); - } - -ipv4ll_address_fail: + r = link_deserialize_ipv4ll(link, ipv4ll_address); + if (r < 0) + log_link_warning_errno(link, r, "Failed to load IPv4LL address from %s, ignoring: %m", link->state_file); return 0; } @@ -3907,7 +2490,7 @@ static int link_carrier_gained(Link *link) { } link_set_state(link, LINK_STATE_CONFIGURING); - r = link_request_set_addresses(link); + r = link_set_static_configs(link); if (r < 0) return r; } @@ -3958,7 +2541,7 @@ static int link_carrier_lost(Link *link) { return r; } - if (link_dhcp4_server_enabled(link)) + if (link->dhcp_server) (void) sd_dhcp_server_stop(link->dhcp_server); r = link_drop_config(link); @@ -4093,101 +2676,31 @@ int link_update(Link *link, sd_netlink_message *m) { mac.ether_addr_octet[4], mac.ether_addr_octet[5]); - if (link->ipv4ll) { - bool restart = sd_ipv4ll_is_running(link->ipv4ll) > 0; + r = ipv4ll_update_mac(link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m"); - if (restart) { - r = sd_ipv4ll_stop(link->ipv4ll); - if (r < 0) - return log_link_warning_errno(link, r, "Could not stop IPv4LL client: %m"); - } + r = dhcp4_update_mac(link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); - r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m"); + r = dhcp6_update_mac(link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); - if (restart) { - r = sd_ipv4ll_start(link->ipv4ll); - if (r < 0) - return log_link_warning_errno(link, r, "Could not restart IPv4LL client: %m"); - } - } - - if (link->dhcp_client) { - r = sd_dhcp_client_set_mac(link->dhcp_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), - ARPHRD_ETHER); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); - - r = dhcp4_set_client_identifier(link); - if (r < 0) - return log_link_warning_errno(link, r, "Could not set DHCP client identifier: %m"); - } - - if (link->dhcp6_client) { - const DUID* duid = link_get_duid(link); - bool restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0; - - if (restart) { - r = sd_dhcp6_client_stop(link->dhcp6_client); - if (r < 0) - return log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"); - } - - r = sd_dhcp6_client_set_mac(link->dhcp6_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), - ARPHRD_ETHER); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); - - if (link->network->iaid_set) { - r = sd_dhcp6_client_set_iaid(link->dhcp6_client, link->network->iaid); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); - } - - r = sd_dhcp6_client_set_duid(link->dhcp6_client, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m"); - - if (restart) { - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m"); - } - } - - if (link->radv) { - bool restart = sd_radv_is_running(link->radv); - - if (restart) { - r = sd_radv_stop(link->radv); - if (r < 0) - return log_link_warning_errno(link, r, "Could not stop Router Advertisement: %m"); - } - - r = sd_radv_set_mac(link->radv, &link->mac); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m"); - - if (restart) { - r = sd_radv_start(link->radv); - if (r < 0) - return log_link_warning_errno(link, r, "Could not restart Router Advertisement: %m"); - } - } + r = dhcp6_update_mac(link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address for Router Advertisement: %m"); if (link->ndisc) { r = sd_ndisc_set_mac(link->ndisc, &link->mac); if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC for NDisc: %m"); } + + r = ipv4_dad_update_mac(link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in IPv4 ACD client: %m"); } old_master = link->master_ifindex; @@ -4327,8 +2840,6 @@ int link_save(Link *link) { const char *admin_state, *oper_state, *carrier_state, *address_state; _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; - Route *route; - Address *a; int r; assert(link); @@ -4546,38 +3057,15 @@ int link_save(Link *link) { /************************************************************/ - fputs("ADDRESSES=", f); - space = false; - SET_FOREACH(a, link->addresses) { - _cleanup_free_ char *address_str = NULL; - - r = in_addr_to_string(a->family, &a->in_addr, &address_str); - if (r < 0) - goto fail; - - fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen); - space = true; - } - fputc('\n', f); + r = link_serialize_addresses(link, f); + if (r < 0) + goto fail; /************************************************************/ - fputs("ROUTES=", f); - space = false; - SET_FOREACH(route, link->routes) { - _cleanup_free_ char *route_str = NULL; - - r = in_addr_to_string(route->family, &route->dst, &route_str); - if (r < 0) - goto fail; - - fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%"PRIu32"/"USEC_FMT, - space ? " " : "", route_str, - route->dst_prefixlen, route->tos, route->priority, route->table, route->lifetime); - space = true; - } - - fputc('\n', f); + r = link_serialize_routes(link, f); + if (r < 0) + goto fail; } print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links); @@ -4594,29 +3082,13 @@ int link_save(Link *link) { } else (void) unlink(link->lease_file); - if (link->ipv4ll) { - struct in_addr address; + r = link_serialize_ipv4ll(link, f); + if (r < 0) + goto fail; - r = sd_ipv4ll_get_address(link->ipv4ll, &address); - if (r >= 0) { - fputs("IPV4LL_ADDRESS=", f); - serialize_in_addrs(f, &address, 1, false, NULL); - fputc('\n', f); - } - } - - if (link->dhcp6_client) { - _cleanup_free_ char *duid = NULL; - uint32_t iaid; - - r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid); - if (r >= 0) - fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid); - - r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid); - if (r >= 0) - fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid); - } + r = link_serialize_dhcp6_client(link, f); + if (r < 0) + goto fail; r = fflush_and_check(f); if (r < 0) diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 1550db8a23..08cfc60c1f 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -15,7 +15,6 @@ #include "sd-radv.h" #include "sd-netlink.h" -#include "list.h" #include "log-link.h" #include "network-util.h" #include "networkd-util.h" @@ -89,6 +88,7 @@ typedef struct Link { Set *addresses; Set *addresses_foreign; + Set *pool_addresses; Set *static_addresses; Set *neighbors; Set *neighbors_foreign; @@ -127,8 +127,6 @@ typedef struct Link { bool ipv6_mtu_set:1; bool bridge_mdb_configured:1; - LIST_HEAD(Address, pool_addresses); - sd_dhcp_server *dhcp_server; sd_ndisc *ndisc; @@ -193,9 +191,6 @@ typedef struct Link { typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*); -DUID *link_get_duid(Link *link); -int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); - void link_ntp_settings_clear(Link *link); void link_dns_settings_clear(Link *link); Link *link_unref(Link *link); @@ -226,6 +221,8 @@ int link_save_and_clean(Link *link); int link_carrier_reset(Link *link); bool link_has_carrier(Link *link); +bool link_ipv6_enabled(Link *link); +bool link_ipv6ll_enabled(Link *link); int link_ipv6ll_gained(Link *link, const struct in6_addr *address); int link_set_mtu(Link *link, uint32_t mtu); @@ -237,11 +234,7 @@ int link_stop_clients(Link *link, bool may_keep_dhcp); const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; -uint32_t link_get_vrf_table(Link *link); -uint32_t link_get_dhcp_route_table(Link *link); -uint32_t link_get_ipv6_accept_ra_route_table(Link *link); -int link_request_set_routes(Link *link); - +int link_configure(Link *link); int link_reconfigure(Link *link, bool force); int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg); diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c index fe88777977..65a8a314d6 100644 --- a/src/network/networkd-lldp-rx.c +++ b/src/network/networkd-lldp-rx.c @@ -9,6 +9,7 @@ #include "networkd-link.h" #include "networkd-lldp-rx.h" #include "networkd-lldp-tx.h" +#include "networkd-manager.h" #include "networkd-network.h" #include "string-table.h" #include "string-util.h" @@ -25,7 +26,7 @@ static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); -bool link_lldp_rx_enabled(Link *link) { +static bool link_lldp_rx_enabled(Link *link) { assert(link); if (link->flags & IFF_LOOPBACK) @@ -68,9 +69,18 @@ static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n int link_lldp_rx_configure(Link *link) { int r; - r = sd_lldp_new(&link->lldp); - if (r < 0) - return r; + if (!link_lldp_rx_enabled(link)) + return 0; + + if (!link->lldp) { + r = sd_lldp_new(&link->lldp); + if (r < 0) + return r; + + r = sd_lldp_attach_event(link->lldp, link->manager->event, 0); + if (r < 0) + return r; + } r = sd_lldp_set_ifindex(link->lldp, link->ifindex); if (r < 0) @@ -87,10 +97,6 @@ int link_lldp_rx_configure(Link *link) { if (r < 0) return r; - r = sd_lldp_attach_event(link->lldp, NULL, 0); - if (r < 0) - return r; - r = sd_lldp_set_callback(link->lldp, lldp_handler, link); if (r < 0) return r; diff --git a/src/network/networkd-lldp-rx.h b/src/network/networkd-lldp-rx.h index 12f512f628..8e1a6a0b62 100644 --- a/src/network/networkd-lldp-rx.h +++ b/src/network/networkd-lldp-rx.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include - #include "conf-parser.h" typedef struct Link Link; @@ -15,7 +13,6 @@ typedef enum LLDPMode { _LLDP_MODE_INVALID = -1, } LLDPMode; -bool link_lldp_rx_enabled(Link *link); int link_lldp_rx_configure(Link *link); int link_update_lldp(Link *link); int link_lldp_save(Link *link); diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index 2be7c27e18..c8e56a5fec 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -367,7 +367,7 @@ int link_lldp_emit_start(Link *link) { assert(link); - if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) { + if (!link_lldp_emit_enabled(link)) { link_lldp_emit_stop(link); return 0; } diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index dbbc6b64bc..e8d270987d 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -24,12 +24,16 @@ #include "local-addresses.h" #include "netlink-util.h" #include "network-internal.h" +#include "networkd-address-pool.h" #include "networkd-dhcp-server-bus.h" #include "networkd-dhcp6.h" #include "networkd-link-bus.h" #include "networkd-manager-bus.h" #include "networkd-manager.h" +#include "networkd-neighbor.h" #include "networkd-network-bus.h" +#include "networkd-nexthop.h" +#include "networkd-routing-policy-rule.h" #include "networkd-speed-meter.h" #include "ordered-set.h" #include "path-lookup.h" @@ -45,40 +49,6 @@ /* use 128 MB for receive socket kernel queue. */ #define RCVBUF_SIZE (128*1024*1024) -static int log_message_warning_errno(sd_netlink_message *m, int err, const char *msg) { - const char *err_msg = NULL; - - (void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg); - return log_warning_errno(err, "%s: %s%s%m", msg, strempty(err_msg), err_msg ? " " : ""); -} - -static int setup_default_address_pool(Manager *m) { - AddressPool *p; - int r; - - assert(m); - - /* Add in the well-known private address ranges. */ - - r = address_pool_new_from_string(m, &p, AF_INET6, "fd00::", 8); - if (r < 0) - return r; - - r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8); - if (r < 0) - return r; - - r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12); - if (r < 0) - return r; - - r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16); - if (r < 0) - return r; - - return 0; -} - static int manager_reset_all(Manager *m) { Link *link; int r; @@ -289,607 +259,7 @@ static int manager_connect_udev(Manager *m) { return 0; } -int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { - _cleanup_(route_freep) Route *tmp = NULL; - Route *route = NULL; - Manager *m = userdata; - Link *link = NULL; - uint32_t ifindex; - uint16_t type; - unsigned char table; - int r; - - assert(rtnl); - assert(message); - assert(m); - - if (sd_netlink_message_is_error(message)) { - r = sd_netlink_message_get_errno(message); - if (r < 0) - log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring"); - - 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_NEWROUTE, RTM_DELROUTE)) { - log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type); - return 0; - } - - r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex); - if (r == -ENODATA) { - log_debug("rtnl: received route message without ifindex, ignoring"); - return 0; - } else if (r < 0) { - log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m"); - return 0; - } else if (ifindex <= 0) { - log_warning("rtnl: received route 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 route again, so just ignore it */ - if (!m->enumerating) - log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex); - return 0; - } - - r = route_new(&tmp); - if (r < 0) - return log_oom(); - - r = sd_rtnl_message_route_get_family(message, &tmp->family); - if (r < 0) { - log_link_warning(link, "rtnl: received route message without family, ignoring"); - return 0; - } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { - log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family); - return 0; - } - - r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol); - if (r < 0) { - log_warning_errno(r, "rtnl: received route message without route protocol: %m"); - return 0; - } - - switch (tmp->family) { - case AF_INET: - r = sd_netlink_message_read_in_addr(message, RTA_DST, &tmp->dst.in); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &tmp->gw.in); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_in_addr(message, RTA_SRC, &tmp->src.in); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &tmp->prefsrc.in); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m"); - return 0; - } - - break; - - case AF_INET6: - r = sd_netlink_message_read_in6_addr(message, RTA_DST, &tmp->dst.in6); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &tmp->gw.in6); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &tmp->src.in6); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &tmp->prefsrc.in6); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m"); - return 0; - } - - break; - - default: - assert_not_reached("Received route message with unsupported address family"); - return 0; - } - - r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_route_get_scope(message, &tmp->scope); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_route_get_tos(message, &tmp->tos); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_route_get_type(message, &tmp->type); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_route_get_table(message, &table); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m"); - return 0; - } - tmp->table = table; - - r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_enter_container(message, RTA_METRICS); - if (r < 0 && r != -ENODATA) { - log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m"); - return 0; - } - if (r >= 0) { - r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_exit_container(message); - if (r < 0) { - log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container: %m"); - return 0; - } - } - - (void) route_get(link, tmp, &route); - - if (DEBUG_LOGGING) { - _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL, - *buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL; - char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX], - buf_protocol[ROUTE_PROTOCOL_STR_MAX]; - - if (!in_addr_is_null(tmp->family, &tmp->dst)) { - (void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst); - (void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen); - } - if (!in_addr_is_null(tmp->family, &tmp->src)) - (void) in_addr_to_string(tmp->family, &tmp->src, &buf_src); - if (!in_addr_is_null(tmp->family, &tmp->gw)) - (void) in_addr_to_string(tmp->family, &tmp->gw, &buf_gw); - if (!in_addr_is_null(tmp->family, &tmp->prefsrc)) - (void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc); - - log_link_debug(link, - "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s", - (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" : - type == RTM_DELROUTE ? "Forgetting" : - route ? "Received remembered" : "Remembering", - strna(buf_dst), strempty(buf_dst_prefixlen), - strna(buf_src), strna(buf_gw), strna(buf_prefsrc), - format_route_scope(tmp->scope, buf_scope, sizeof buf_scope), - format_route_table(tmp->table, buf_table, sizeof buf_table), - format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol), - strna(route_type_to_string(tmp->type))); - } - - switch (type) { - case RTM_NEWROUTE: - if (!route && link->manager->manage_foreign_routes) { - /* A route appeared that we did not request */ - r = route_add_foreign(link, tmp, &route); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m"); - return 0; - } - } - - break; - - case RTM_DELROUTE: - route_free(route); - break; - - default: - assert_not_reached("Received route message with invalid RTNL message type"); - } - - 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_message_warning_errno(message, r, "rtnl: failed to receive neighbor message, ignoring"); - - 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) { - log_link_warning(link, "rtnl: received neighbor message without family, ignoring."); - return 0; - } else if (!IN_SET(family, AF_INET, AF_INET6)) { - log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", family); - 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, "Received remembered 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_debug(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; - Link *link = NULL; - uint16_t type; - unsigned char flags, prefixlen, scope; - union in_addr_union in_addr = IN_ADDR_NULL; - struct ifa_cacheinfo cinfo; - Address *address = NULL; - char valid_buf[FORMAT_TIMESPAN_MAX]; - const char *valid_str = NULL; - int ifindex, family, r; - - assert(rtnl); - assert(message); - assert(m); - - if (sd_netlink_message_is_error(message)) { - r = sd_netlink_message_get_errno(message); - if (r < 0) - log_message_warning_errno(message, r, "rtnl: failed to receive address message, ignoring"); - - 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_NEWADDR, RTM_DELADDR)) { - log_warning("rtnl: received unexpected message type %u when processing address, ignoring.", type); - return 0; - } - - r = sd_rtnl_message_addr_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 address 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 address again, so just - * ignore it */ - if (!m->enumerating) - log_warning("rtnl: received address for link '%d' we don't know about, ignoring.", ifindex); - return 0; - } - - r = sd_rtnl_message_addr_get_family(message, &family); - if (r < 0) { - log_link_warning(link, "rtnl: received address message without family, ignoring."); - return 0; - } else if (!IN_SET(family, AF_INET, AF_INET6)) { - log_link_debug(link, "rtnl: received address message with invalid family '%i', ignoring.", family); - return 0; - } - - r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address message with invalid prefixlen, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_addr_get_scope(message, &scope); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address message with invalid scope, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_addr_get_flags(message, &flags); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address message with invalid flags, ignoring: %m"); - return 0; - } - - switch (family) { - case AF_INET: - r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m"); - return 0; - } - - break; - - case AF_INET6: - r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m"); - return 0; - } - - break; - - default: - assert_not_reached("Received unsupported address family"); - } - - r = in_addr_to_string(family, &in_addr, &buf); - if (r < 0) - log_link_warning_errno(link, r, "Could not print address: %m"); - - r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); - if (r < 0 && r != -ENODATA) { - log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m"); - return 0; - } else if (r >= 0 && cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) - valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, - cinfo.ifa_valid * USEC_PER_SEC, - USEC_PER_SEC); - - (void) address_get(link, family, &in_addr, prefixlen, &address); - - switch (type) { - case RTM_NEWADDR: - if (address) - log_link_debug(link, "Remembering updated address: %s/%u (valid %s%s)", - strnull(buf), prefixlen, - valid_str ? "for " : "forever", strempty(valid_str)); - else { - /* An address appeared that we did not request */ - r = address_add_foreign(link, family, &in_addr, prefixlen, &address); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to remember foreign address %s/%u, ignoring: %m", - strnull(buf), prefixlen); - return 0; - } else - log_link_debug(link, "Remembering foreign address: %s/%u (valid %s%s)", - strnull(buf), prefixlen, - valid_str ? "for " : "forever", strempty(valid_str)); - } - - /* address_update() logs internally, so we don't need to here. */ - r = address_update(address, flags, scope, &cinfo); - if (r < 0) - link_enter_failed(link); - - break; - - case RTM_DELADDR: - if (address) { - log_link_debug(link, "Forgetting address: %s/%u (valid %s%s)", - strnull(buf), prefixlen, - valid_str ? "for " : "forever", strempty(valid_str)); - (void) address_drop(address); - } else - log_link_debug(link, "Kernel removed an address we don't remember: %s/%u (valid %s%s), ignoring.", - strnull(buf), prefixlen, - valid_str ? "for " : "forever", strempty(valid_str)); - - break; - - default: - assert_not_reached("Received invalid RTNL message type"); - } - - return 1; -} - -static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { - Manager *m = userdata; +static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { Link *link = NULL; NetDev *netdev = NULL; uint16_t type; @@ -976,358 +346,6 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa return 1; } -int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; - _cleanup_free_ char *from = NULL, *to = NULL; - RoutingPolicyRule *rule = NULL; - const char *iif = NULL, *oif = NULL; - uint32_t suppress_prefixlen; - Manager *m = userdata; - unsigned flags; - uint16_t type; - int r; - - assert(rtnl); - assert(message); - assert(m); - - if (sd_netlink_message_is_error(message)) { - r = sd_netlink_message_get_errno(message); - if (r < 0) - log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring"); - - 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_NEWRULE, RTM_DELRULE)) { - log_warning("rtnl: received unexpected message type %u when processing rule, ignoring.", type); - return 0; - } - - r = routing_policy_rule_new(&tmp); - if (r < 0) { - log_oom(); - return 0; - } - - r = sd_rtnl_message_get_family(message, &tmp->family); - if (r < 0) { - log_warning_errno(r, "rtnl: could not get rule family, ignoring: %m"); - return 0; - } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { - log_debug("rtnl: received rule message with invalid family %d, ignoring.", tmp->family); - return 0; - } - - switch (tmp->family) { - case AF_INET: - r = sd_netlink_message_read_in_addr(message, FRA_SRC, &tmp->from.in); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m"); - return 0; - } else if (r >= 0) { - r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen); - if (r < 0) { - log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m"); - return 0; - } - } - - r = sd_netlink_message_read_in_addr(message, FRA_DST, &tmp->to.in); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m"); - return 0; - } else if (r >= 0) { - r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen); - if (r < 0) { - log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m"); - return 0; - } - } - - break; - - case AF_INET6: - r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &tmp->from.in6); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m"); - return 0; - } else if (r >= 0) { - r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen); - if (r < 0) { - log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m"); - return 0; - } - } - - r = sd_netlink_message_read_in6_addr(message, FRA_DST, &tmp->to.in6); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m"); - return 0; - } else if (r >= 0) { - r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen); - if (r < 0) { - log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m"); - return 0; - } - } - - break; - - default: - assert_not_reached("Received rule message with unsupported address family"); - } - - r = sd_rtnl_message_routing_policy_rule_get_flags(message, &flags); - if (r < 0) { - log_warning_errno(r, "rtnl: received rule message without valid flag, ignoring: %m"); - return 0; - } - tmp->invert_rule = flags & FIB_RULE_INVERT; - - r = sd_netlink_message_read_u32(message, FRA_FWMARK, &tmp->fwmark); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_u32(message, FRA_FWMASK, &tmp->fwmask); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_FWMASK attribute, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_u32(message, FRA_PRIORITY, &tmp->priority); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_PRIORITY attribute, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_u32(message, FRA_TABLE, &tmp->table); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_TABLE attribute, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tmp->tos); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get ip rule TOS, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_string(message, FRA_IIFNAME, &iif); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m"); - return 0; - } - r = free_and_strdup(&tmp->iif, iif); - if (r < 0) - return log_oom(); - - r = sd_netlink_message_read_string(message, FRA_OIFNAME, &oif); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m"); - return 0; - } - r = free_and_strdup(&tmp->oif, oif); - if (r < 0) - return log_oom(); - - r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &tmp->protocol); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(tmp->sport), &tmp->sport); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(tmp->dport), &tmp->dport); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read(message, FRA_UID_RANGE, sizeof(tmp->uid_range), &tmp->uid_range); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_UID_RANGE attribute, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_u32(message, FRA_SUPPRESS_PREFIXLEN, &suppress_prefixlen); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get FRA_SUPPRESS_PREFIXLEN attribute, ignoring: %m"); - return 0; - } - if (r >= 0) - tmp->suppress_prefixlen = (int) suppress_prefixlen; - - (void) routing_policy_rule_get(m, tmp, &rule); - - if (DEBUG_LOGGING) { - (void) in_addr_to_string(tmp->family, &tmp->from, &from); - (void) in_addr_to_string(tmp->family, &tmp->to, &to); - } - - switch (type) { - case RTM_NEWRULE: - if (rule) - log_debug("Received remembered routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, - tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table); - else { - log_debug("Remembering foreign routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, - tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table); - r = routing_policy_rule_add_foreign(m, tmp, &rule); - if (r < 0) { - log_warning_errno(r, "Could not remember foreign rule, ignoring: %m"); - return 0; - } - } - break; - case RTM_DELRULE: - if (rule) { - log_debug("Forgetting routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, - tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table); - routing_policy_rule_free(rule); - } else - log_debug("Kernel removed a routing policy rule we don't remember: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32", ignoring.", - tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table); - break; - - default: - assert_not_reached("Received invalid RTNL message type"); - } - - return 1; -} - -int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { - _cleanup_(nexthop_freep) NextHop *tmp = NULL; - _cleanup_free_ char *gateway = NULL; - NextHop *nexthop = NULL; - Manager *m = userdata; - Link *link = NULL; - uint16_t type; - int r; - - assert(rtnl); - assert(message); - assert(m); - - if (sd_netlink_message_is_error(message)) { - r = sd_netlink_message_get_errno(message); - if (r < 0) - log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring"); - - 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_NEWNEXTHOP, RTM_DELNEXTHOP)) { - log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type); - return 0; - } - - r = nexthop_new(&tmp); - if (r < 0) - return log_oom(); - - r = sd_rtnl_message_get_family(message, &tmp->family); - if (r < 0) { - log_warning_errno(r, "rtnl: could not get nexthop family, ignoring: %m"); - return 0; - } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { - log_debug("rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family); - return 0; - } - - switch (tmp->family) { - case AF_INET: - r = sd_netlink_message_read_in_addr(message, NHA_GATEWAY, &tmp->gw.in); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m"); - return 0; - } - break; - - case AF_INET6: - r = sd_netlink_message_read_in6_addr(message, NHA_GATEWAY, &tmp->gw.in6); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m"); - return 0; - } - break; - - default: - assert_not_reached("Received rule message with unsupported address family"); - } - - r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m"); - return 0; - } - - r = sd_netlink_message_read_u32(message, NHA_OIF, &tmp->oif); - if (r < 0 && r != -ENODATA) { - log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m"); - return 0; - } else if (tmp->oif <= 0) { - log_warning("rtnl: received nexthop message with invalid ifindex %d, ignoring.", tmp->oif); - return 0; - } - - r = link_get(m, tmp->oif, &link); - if (r < 0 || !link) { - if (!m->enumerating) - log_warning("rtnl: received nexthop message for link (%d) we do not know about, ignoring", tmp->oif); - return 0; - } - - (void) nexthop_get(link, tmp, &nexthop); - - if (DEBUG_LOGGING) - (void) in_addr_to_string(tmp->family, &tmp->gw, &gateway); - - switch (type) { - case RTM_NEWNEXTHOP: - if (nexthop) - log_link_debug(link, "Received remembered nexthop: %s, oif: %d, id: %d", strna(gateway), tmp->oif, tmp->id); - else { - log_link_debug(link, "Remembering foreign nexthop: %s, oif: %d, id: %d", strna(gateway), tmp->oif, tmp->id); - r = nexthop_add_foreign(link, tmp, &nexthop); - if (r < 0) { - log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m"); - return 0; - } - } - break; - case RTM_DELNEXTHOP: - if (nexthop) { - log_link_debug(link, "Forgetting nexthop: %s, oif: %d, id: %d", strna(gateway), tmp->oif, tmp->id); - nexthop_free(nexthop); - } else - log_link_debug(link, "Kernel removed a nexthop we don't remember: %s, oif: %d, id: %d, ignoring.", - strna(gateway), tmp->oif, tmp->id); - break; - - default: - assert_not_reached("Received invalid RTNL message type"); - } - - return 1; -} - static int systemd_netlink_fd(void) { int n, fd, rtnl_fd = -EINVAL; @@ -1393,51 +411,51 @@ static int manager_connect_rtnl(Manager *m) { if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link"); + r = netlink_add_match(m->rtnl, NULL, RTM_NEWLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link"); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link"); + r = netlink_add_match(m->rtnl, NULL, RTM_DELLINK, &manager_rtnl_process_link, NULL, m, "network-rtnl_process_link"); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address"); + r = netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address"); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address"); + r = netlink_add_match(m->rtnl, NULL, RTM_DELADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address"); 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"); + r = 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"); + r = 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"); + r = netlink_add_match(m->rtnl, NULL, RTM_NEWROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route"); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route"); + r = netlink_add_match(m->rtnl, NULL, RTM_DELROUTE, &manager_rtnl_process_route, NULL, m, "network-rtnl_process_route"); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule"); + r = netlink_add_match(m->rtnl, NULL, RTM_NEWRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule"); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule"); + r = netlink_add_match(m->rtnl, NULL, RTM_DELRULE, &manager_rtnl_process_rule, NULL, m, "network-rtnl_process_rule"); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop"); + r = netlink_add_match(m->rtnl, NULL, RTM_NEWNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop"); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop"); + r = netlink_add_match(m->rtnl, NULL, RTM_DELNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop"); if (r < 0) return r; @@ -1824,7 +842,7 @@ int manager_new(Manager **ret) { if (r < 0) return r; - r = setup_default_address_pool(m); + r = address_pool_setup_default(m); if (r < 0) return r; @@ -1838,7 +856,6 @@ int manager_new(Manager **ret) { } void manager_free(Manager *m) { - AddressPool *pool; Link *link; if (!m) @@ -1861,8 +878,7 @@ void manager_free(Manager *m) { m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref); - while ((pool = m->address_pools)) - address_pool_free(pool); + ordered_set_free_free(m->address_pools); /* routing_policy_rule_free() access m->rules and m->rules_foreign. * So, it is necessary to set NULL after the sets are freed. */ @@ -1932,9 +948,51 @@ bool manager_should_reload(Manager *m) { return paths_check_timestamp(NETWORK_DIRS, &m->network_dirs_ts_usec, false); } -int manager_rtnl_enumerate_links(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *link; +static int manager_enumerate_internal( + Manager *m, + sd_netlink_message *req, + int (*process)(sd_netlink *, sd_netlink_message *, Manager *), + const char *name) { + + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL; + int r; + + assert(m); + assert(m->rtnl); + assert(req); + assert(process); + + 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) { + if (r == -EOPNOTSUPP && name) { + log_debug_errno(r, "%s are not supported by the kernel. Ignoring.", name); + return 0; + } + + return r; + } + + for (sd_netlink_message *reply_one = reply; reply_one; reply_one = sd_netlink_message_next(reply_one)) { + int k; + + m->enumerating = true; + + k = process(m->rtnl, reply_one, m); + if (k < 0 && r >= 0) + r = k; + + m->enumerating = false; + } + + return r; +} + +static int manager_enumerate_links(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; assert(m); @@ -1944,32 +1002,11 @@ int manager_rtnl_enumerate_links(Manager *m) { 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 (link = reply; link; link = sd_netlink_message_next(link)) { - int k; - - m->enumerating = true; - - k = manager_rtnl_process_link(m->rtnl, link, m); - if (k < 0) - r = k; - - m->enumerating = false; - } - - return r; + return manager_enumerate_internal(m, req, manager_rtnl_process_link, NULL); } -int manager_rtnl_enumerate_addresses(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *addr; +static int manager_enumerate_addresses(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; assert(m); @@ -1979,32 +1016,11 @@ int manager_rtnl_enumerate_addresses(Manager *m) { 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 (addr = reply; addr; addr = sd_netlink_message_next(addr)) { - int k; - - m->enumerating = true; - - k = manager_rtnl_process_address(m->rtnl, addr, m); - if (k < 0) - r = k; - - m->enumerating = false; - } - - return r; + return manager_enumerate_internal(m, req, manager_rtnl_process_address, NULL); } -int manager_rtnl_enumerate_neighbors(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *neigh; +static int manager_enumerate_neighbors(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; assert(m); @@ -2014,32 +1030,11 @@ int manager_rtnl_enumerate_neighbors(Manager *m) { 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; + return manager_enumerate_internal(m, req, manager_rtnl_process_neighbor, NULL); } -int manager_rtnl_enumerate_routes(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *route; +static int manager_enumerate_routes(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; assert(m); @@ -2052,32 +1047,11 @@ int manager_rtnl_enumerate_routes(Manager *m) { 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 (route = reply; route; route = sd_netlink_message_next(route)) { - int k; - - m->enumerating = true; - - k = manager_rtnl_process_route(m->rtnl, route, m); - if (k < 0) - r = k; - - m->enumerating = false; - } - - return r; + return manager_enumerate_internal(m, req, manager_rtnl_process_route, NULL); } -int manager_rtnl_enumerate_rules(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *rule; +static int manager_enumerate_rules(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; assert(m); @@ -2087,38 +1061,11 @@ int manager_rtnl_enumerate_rules(Manager *m) { 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) { - if (r == -EOPNOTSUPP) { - log_debug("FIB Rules are not supported by the kernel. Ignoring."); - return 0; - } - - return r; - } - - for (rule = reply; rule; rule = sd_netlink_message_next(rule)) { - int k; - - m->enumerating = true; - - k = manager_rtnl_process_rule(m->rtnl, rule, m); - if (k < 0) - r = k; - - m->enumerating = false; - } - - return r; + return manager_enumerate_internal(m, req, manager_rtnl_process_rule, "Routing policy rules"); } -int manager_rtnl_enumerate_nexthop(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *nexthop; +static int manager_enumerate_nexthop(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; assert(m); @@ -2128,51 +1075,35 @@ int manager_rtnl_enumerate_nexthop(Manager *m) { 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) { - if (r == -EOPNOTSUPP) { - log_debug("Nexthop are not supported by the kernel. Ignoring."); - return 0; - } - - return r; - } - - for (nexthop = reply; nexthop; nexthop = sd_netlink_message_next(nexthop)) { - int k; - - m->enumerating = true; - - k = manager_rtnl_process_nexthop(m->rtnl, nexthop, m); - if (k < 0) - r = k; - - m->enumerating = false; - } - - return r; + return manager_enumerate_internal(m, req, manager_rtnl_process_nexthop, "Nexthop rules"); } -int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) { - AddressPool *p; +int manager_enumerate(Manager *m) { int r; - assert(m); - assert(prefixlen > 0); - assert(found); + r = manager_enumerate_links(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate links: %m"); - LIST_FOREACH(address_pools, p, m->address_pools) { - if (p->family != family) - continue; + r = manager_enumerate_addresses(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate addresses: %m"); - r = address_pool_acquire(p, prefixlen, found); - if (r != 0) - return r; - } + r = manager_enumerate_neighbors(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate neighbors: %m"); + + r = manager_enumerate_routes(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate routes: %m"); + + r = manager_enumerate_rules(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate routing policy rules: %m"); + + r = manager_enumerate_nexthop(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate nexthop rules: %m"); return 0; } @@ -2313,51 +1244,3 @@ int manager_set_timezone(Manager *m, const char *tz) { return 0; } - -int manager_request_product_uuid(Manager *m, Link *link) { - int r; - - assert(m); - - if (m->has_product_uuid) - return 0; - - log_debug("Requesting product UUID"); - - if (link) { - DUID *duid; - - assert_se(duid = link_get_duid(link)); - - r = set_ensure_put(&m->links_requesting_uuid, NULL, link); - if (r < 0) - return log_oom(); - if (r > 0) - link_ref(link); - - r = set_ensure_put(&m->duids_requesting_uuid, NULL, duid); - if (r < 0) - return log_oom(); - } - - if (!m->bus || sd_bus_is_ready(m->bus) <= 0) { - log_debug("Not connected to system bus, requesting product UUID later."); - return 0; - } - - r = sd_bus_call_method_async( - m->bus, - NULL, - "org.freedesktop.hostname1", - "/org/freedesktop/hostname1", - "org.freedesktop.hostname1", - "GetProductUUID", - get_product_uuid_handler, - m, - "b", - false); - if (r < 0) - return log_warning_errno(r, "Failed to get product UUID: %m"); - - return 0; -} diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 255a0d965f..142a6eb358 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -10,12 +10,11 @@ #include "dhcp-identifier.h" #include "hashmap.h" -#include "list.h" -#include "time-util.h" - -#include "networkd-address-pool.h" #include "networkd-link.h" #include "networkd-network.h" +#include "ordered-set.h" +#include "set.h" +#include "time-util.h" struct Manager { sd_netlink *rtnl; @@ -45,7 +44,7 @@ struct Manager { OrderedHashmap *networks; Hashmap *dhcp6_prefixes; Set *dhcp6_pd_prefixes; - LIST_HEAD(AddressPool, address_pools); + OrderedSet *address_pools; usec_t network_dirs_ts_usec; @@ -82,27 +81,13 @@ int manager_start(Manager *m); int manager_load_config(Manager *m); 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_enumerate_nexthop(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); -int manager_rtnl_process_nexthop(sd_netlink *nl, sd_netlink_message *message, void *userdata); +int manager_enumerate(Manager *m); void manager_dirty(Manager *m); -int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found); - Link* manager_find_uplink(Manager *m, Link *exclude); int manager_set_hostname(Manager *m, const char *hostname); int manager_set_timezone(Manager *m, const char *timezone); -int manager_request_product_uuid(Manager *m, Link *link); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); diff --git a/src/network/networkd-mdb.c b/src/network/networkd-mdb.c index 542ba75ad0..bca3e620dd 100644 --- a/src/network/networkd-mdb.c +++ b/src/network/networkd-mdb.c @@ -3,13 +3,32 @@ #include #include "netlink-util.h" +#include "networkd-link.h" #include "networkd-manager.h" #include "networkd-mdb.h" +#include "networkd-network.h" #include "string-util.h" #include "vlan-util.h" #define STATIC_MDB_ENTRIES_PER_NETWORK_MAX 1024U +/* remove MDB entry. */ +MdbEntry *mdb_entry_free(MdbEntry *mdb_entry) { + if (!mdb_entry) + return NULL; + + if (mdb_entry->network) { + assert(mdb_entry->section); + hashmap_remove(mdb_entry->network->mdb_entries_by_section, mdb_entry->section); + } + + network_config_section_free(mdb_entry->section); + + return mfree(mdb_entry); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(MdbEntry, mdb_entry_free); + /* create a new MDB entry or get an existing one. */ static int mdb_entry_new_static( Network *network, @@ -23,22 +42,21 @@ static int mdb_entry_new_static( assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); + + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; /* search entry in hashmap first. */ - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; - - mdb_entry = hashmap_get(network->mdb_entries_by_section, n); - if (mdb_entry) { - *ret = TAKE_PTR(mdb_entry); - return 0; - } + mdb_entry = hashmap_get(network->mdb_entries_by_section, n); + if (mdb_entry) { + *ret = TAKE_PTR(mdb_entry); + return 0; } - if (network->n_static_mdb_entries >= STATIC_MDB_ENTRIES_PER_NETWORK_MAX) + if (hashmap_size(network->mdb_entries_by_section) >= STATIC_MDB_ENTRIES_PER_NETWORK_MAX) return -E2BIG; /* allocate space for an MDB entry. */ @@ -49,48 +67,22 @@ static int mdb_entry_new_static( /* init MDB structure. */ *mdb_entry = (MdbEntry) { .network = network, + .section = TAKE_PTR(n), }; - LIST_PREPEND(static_mdb_entries, network->static_mdb_entries, mdb_entry); - network->n_static_mdb_entries++; + r = hashmap_ensure_allocated(&network->mdb_entries_by_section, &network_config_hash_ops); + if (r < 0) + return r; - if (filename) { - mdb_entry->section = TAKE_PTR(n); - - r = hashmap_ensure_allocated(&network->mdb_entries_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->mdb_entries_by_section, mdb_entry->section, mdb_entry); - if (r < 0) - return r; - } + r = hashmap_put(network->mdb_entries_by_section, mdb_entry->section, mdb_entry); + if (r < 0) + return r; /* return allocated MDB structure. */ *ret = TAKE_PTR(mdb_entry); - return 0; } -/* remove and MDB entry. */ -MdbEntry *mdb_entry_free(MdbEntry *mdb_entry) { - if (!mdb_entry) - return NULL; - - if (mdb_entry->network) { - LIST_REMOVE(static_mdb_entries, mdb_entry->network->static_mdb_entries, mdb_entry); - assert(mdb_entry->network->n_static_mdb_entries > 0); - mdb_entry->network->n_static_mdb_entries--; - - if (mdb_entry->section) - hashmap_remove(mdb_entry->network->mdb_entries_by_section, mdb_entry->section); - } - - network_config_section_free(mdb_entry->section); - - return mfree(mdb_entry); -} - static int set_mdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -212,7 +204,7 @@ int link_set_bridge_mdb(Link *link) { if (!link->network) return 0; - if (LIST_IS_EMPTY(link->network->static_mdb_entries)) + if (hashmap_isempty(link->network->mdb_entries_by_section)) goto finish; if (!link_has_carrier(link)) @@ -236,7 +228,7 @@ int link_set_bridge_mdb(Link *link) { goto finish; } - LIST_FOREACH(static_mdb_entries, mdb_entry, link->network->static_mdb_entries) { + HASHMAP_FOREACH(mdb_entry, link->network->mdb_entries_by_section) { r = mdb_entry_configure(link, mdb_entry); if (r < 0) return log_link_error_errno(link, r, "Failed to add MDB entry to multicast group database: %m"); @@ -253,6 +245,49 @@ finish: return 0; } +static int mdb_entry_verify(MdbEntry *mdb_entry) { + if (section_is_invalid(mdb_entry->section)) + return -EINVAL; + + if (mdb_entry->family == AF_UNSPEC) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: [BridgeMDB] section without MulticastGroupAddress= field configured. " + "Ignoring [BridgeMDB] section from line %u.", + mdb_entry->section->filename, mdb_entry->section->line); + + if (!in_addr_is_multicast(mdb_entry->family, &mdb_entry->group_addr)) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: MulticastGroupAddress= is not a multicast address. " + "Ignoring [BridgeMDB] section from line %u.", + mdb_entry->section->filename, mdb_entry->section->line); + + if (mdb_entry->family == AF_INET) { + if (in4_addr_is_local_multicast(&mdb_entry->group_addr.in)) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: MulticastGroupAddress= is a local multicast address. " + "Ignoring [BridgeMDB] section from line %u.", + mdb_entry->section->filename, mdb_entry->section->line); + } else { + if (in6_addr_is_link_local_all_nodes(&mdb_entry->group_addr.in6)) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: MulticastGroupAddress= is the multicast all nodes address. " + "Ignoring [BridgeMDB] section from line %u.", + mdb_entry->section->filename, mdb_entry->section->line); + } + + return 0; +} + +void network_drop_invalid_mdb_entries(Network *network) { + MdbEntry *mdb_entry; + + assert(network); + + HASHMAP_FOREACH(mdb_entry, network->mdb_entries_by_section) + if (mdb_entry_verify(mdb_entry) < 0) + mdb_entry_free(mdb_entry); +} + /* parse the VLAN Id from config files. */ int config_parse_mdb_vlan_id( const char *unit, @@ -328,36 +363,3 @@ int config_parse_mdb_group_address( return 0; } - -int mdb_entry_verify(MdbEntry *mdb_entry) { - if (section_is_invalid(mdb_entry->section)) - return -EINVAL; - - if (mdb_entry->family == AF_UNSPEC) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: [BridgeMDB] section without MulticastGroupAddress= field configured. " - "Ignoring [BridgeMDB] section from line %u.", - mdb_entry->section->filename, mdb_entry->section->line); - - if (!in_addr_is_multicast(mdb_entry->family, &mdb_entry->group_addr)) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: MulticastGroupAddress= is not a multicast address. " - "Ignoring [BridgeMDB] section from line %u.", - mdb_entry->section->filename, mdb_entry->section->line); - - if (mdb_entry->family == AF_INET) { - if (in4_addr_is_local_multicast(&mdb_entry->group_addr.in)) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: MulticastGroupAddress= is a local multicast address. " - "Ignoring [BridgeMDB] section from line %u.", - mdb_entry->section->filename, mdb_entry->section->line); - } else { - if (in6_addr_is_link_local_all_nodes(&mdb_entry->group_addr.in6)) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: MulticastGroupAddress= is the multicast all nodes address. " - "Ignoring [BridgeMDB] section from line %u.", - mdb_entry->section->filename, mdb_entry->section->line); - } - - return 0; -} diff --git a/src/network/networkd-mdb.h b/src/network/networkd-mdb.h index d46ab4a50e..563a9e6d99 100644 --- a/src/network/networkd-mdb.h +++ b/src/network/networkd-mdb.h @@ -1,32 +1,29 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include + #include "conf-parser.h" -#include "list.h" -#include "macro.h" +#include "in-addr-util.h" #include "networkd-util.h" typedef struct Network Network; -typedef struct MdbEntry MdbEntry; typedef struct Link Link; -typedef struct NetworkConfigSection NetworkConfigSection; -struct MdbEntry { +typedef struct MdbEntry { Network *network; NetworkConfigSection *section; int family; union in_addr_union group_addr; uint16_t vlan_id; +} MdbEntry; - LIST_FIELDS(MdbEntry, static_mdb_entries); -}; - -int mdb_entry_verify(MdbEntry *mdb_entry); MdbEntry *mdb_entry_free(MdbEntry *mdb_entry); -int link_set_bridge_mdb(Link *link); -DEFINE_NETWORK_SECTION_FUNCTIONS(MdbEntry, mdb_entry_free); +void network_drop_invalid_mdb_entries(Network *network); + +int link_set_bridge_mdb(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_mdb_group_address); CONFIG_PARSER_PROTOTYPE(config_parse_mdb_vlan_id); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 68a66649f0..2599ec2232 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -3,16 +3,18 @@ Copyright © 2014 Intel Corporation. All rights reserved. ***/ -#include #include +#include +#include #include "sd-ndisc.h" #include "missing_network.h" +#include "networkd-address.h" #include "networkd-dhcp6.h" #include "networkd-manager.h" #include "networkd-ndisc.h" -#include "networkd-route.h" +#include "networkd-sysctl.h" #include "string-table.h" #include "string-util.h" #include "strv.h" @@ -35,6 +37,36 @@ #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e) +bool link_ipv6_accept_ra_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (!link_ipv6ll_enabled(link)) + return false; + + /* If unset use system default (enabled if local forwarding is disabled. + * disabled if local forwarding is enabled). + * If set, ignore or enforce RA independent of local forwarding state. + */ + if (link->network->ipv6_accept_ra < 0) + /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ + return !link_ip_forward_enabled(link, AF_INET6); + else if (link->network->ipv6_accept_ra > 0) + /* accept RA even if ip_forward is enabled */ + return true; + else + /* ignore RA */ + return false; +} + static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force); static int ndisc_address_callback(Address *address) { @@ -368,7 +400,7 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } - r = link_request_set_routes(link); + r = link_set_routes(link); if (r < 0) { link_enter_failed(link); return 1; @@ -490,7 +522,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { return log_link_error_errno(link, r, "Could not set default route: %m"); Route *route_gw; - LIST_FOREACH(routes, route_gw, link->network->static_routes) { + HASHMAP_FOREACH(route_gw, link->network->routes_by_section) { if (!route_gw->gateway_from_dhcp) continue; @@ -1133,9 +1165,9 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { else { log_link_debug(link, "Setting SLAAC addresses."); - /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). - * Before they are called, the related flags must be cleared. Otherwise, the link - * becomes configured state before routes are configured. */ + /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are + * called, the related flags must be cleared. Otherwise, the link becomes configured + * state before routes are configured. */ link->static_routes_configured = false; link->static_nexthops_configured = false; } @@ -1194,13 +1226,18 @@ int ndisc_configure(Link *link) { assert(link); - r = sd_ndisc_new(&link->ndisc); - if (r < 0) - return r; + if (!link_ipv6_accept_ra_enabled(link)) + return 0; - r = sd_ndisc_attach_event(link->ndisc, NULL, 0); - if (r < 0) - return r; + if (!link->ndisc) { + r = sd_ndisc_new(&link->ndisc); + if (r < 0) + return r; + + r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0); + if (r < 0) + return r; + } r = sd_ndisc_set_mac(link->ndisc, &link->mac); if (r < 0) diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index 927f555bc6..fd7bb0e97f 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -69,6 +69,8 @@ static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) { return ((char*) n) + ALIGN(sizeof(NDiscDNSSL)); } +bool link_ipv6_accept_ra_enabled(Link *link); + int ndisc_configure(Link *link); void ndisc_vacuum(Link *link); void ndisc_flush(Link *link); diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 09ddb9c8a5..b553f1707e 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -1,29 +1,21 @@ /* 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" +#include "networkd-network.h" #include "set.h" -void neighbor_free(Neighbor *neighbor) { +Neighbor *neighbor_free(Neighbor *neighbor) { if (!neighbor) - return; + return NULL; 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); + assert(neighbor->section); + hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section); } network_config_section_free(neighbor->section); @@ -33,9 +25,11 @@ void neighbor_free(Neighbor *neighbor) { set_remove(neighbor->link->neighbors_foreign, neighbor); } - free(neighbor); + return mfree(neighbor); } +DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free); + 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; @@ -43,19 +37,17 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; + 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 = hashmap_get(network->neighbors_by_section, n); + if (neighbor) { + *ret = TAKE_PTR(neighbor); + return 0; } neighbor = new(Neighbor, 1); @@ -65,143 +57,18 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned *neighbor = (Neighbor) { .network = network, .family = AF_UNSPEC, + .section = TAKE_PTR(n), }; - LIST_APPEND(neighbors, network->neighbors, neighbor); - network->n_neighbors++; + r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops); + if (r < 0) + return r; - 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; - } + r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor); + if (r < 0) + return r; *ret = TAKE_PTR(neighbor); - - return 0; -} - -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); - - 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) - /* Neighbor may not exist yet. So, do not enter failed state here. */ - log_link_message_warning_errno(link, m, r, "Could not set neighbor, ignoring"); - - 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); - - r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, - link->ifindex, neighbor->family); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_NEWNEIGH message: %m"); - - r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT); - if (r < 0) - return log_link_error_errno(link, r, "Could not set state: %m"); - - r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE); - if (r < 0) - return log_link_error_errno(link, r, "Could not set flags: %m"); - - r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr, neighbor->lladdr_size); - if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m"); - - r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr); - if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); - - r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_configure_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - 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_message_warning_errno(link, m, r, "Could not remove neighbor"); - - 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_link_error_errno(link, 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_link_error_errno(link, 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_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - return 0; } @@ -249,28 +116,20 @@ static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free); -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; +static int neighbor_get(Link *link, const Neighbor *in, Neighbor **ret) { + Neighbor *existing; assert(link); - assert(addr); - assert(lladdr); + assert(in); - neighbor = (Neighbor) { - .family = family, - .in_addr = *addr, - .lladdr = *lladdr, - .lladdr_size = lladdr_size, - }; - - existing = set_get(link->neighbors, &neighbor); + existing = set_get(link->neighbors, in); if (existing) { if (ret) *ret = existing; return 1; } - existing = set_get(link->neighbors_foreign, &neighbor); + existing = set_get(link->neighbors_foreign, in); if (existing) { if (ret) *ret = existing; @@ -280,24 +139,23 @@ int neighbor_get(Link *link, int family, const union in_addr_union *addr, const 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) { +static int neighbor_add_internal(Link *link, Set **neighbors, const Neighbor *in, Neighbor **ret) { _cleanup_(neighbor_freep) Neighbor *neighbor = NULL; int r; assert(link); assert(neighbors); - assert(addr); - assert(lladdr); + assert(in); neighbor = new(Neighbor, 1); if (!neighbor) return -ENOMEM; *neighbor = (Neighbor) { - .family = family, - .in_addr = *addr, - .lladdr = *lladdr, - .lladdr_size = lladdr_size, + .family = in->family, + .in_addr = in->in_addr, + .lladdr = in->lladdr, + .lladdr_size = in->lladdr_size, }; r = set_ensure_put(neighbors, &neighbor_hash_ops, neighbor); @@ -310,19 +168,19 @@ static int neighbor_add_internal(Link *link, Set **neighbors, int family, const if (ret) *ret = neighbor; - TAKE_PTR(neighbor); + TAKE_PTR(neighbor); 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) { +static int neighbor_add(Link *link, const Neighbor *in, Neighbor **ret) { Neighbor *neighbor; int r; - r = neighbor_get(link, family, addr, lladdr, lladdr_size, &neighbor); + r = neighbor_get(link, in, &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); + r = neighbor_add_internal(link, &link->neighbors, in, &neighbor); if (r < 0) return r; } else if (r == 0) { @@ -342,11 +200,11 @@ int neighbor_add(Link *link, int family, const union in_addr_union *addr, const 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); +static int neighbor_add_foreign(Link *link, const Neighbor *in, Neighbor **ret) { + return neighbor_add_internal(link, &link->neighbors_foreign, in, ret); } -bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) { +static bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) { if (n1 == n2) return true; @@ -356,7 +214,365 @@ bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) { return neighbor_compare_func(n1, n2) == 0; } -int neighbor_section_verify(Neighbor *neighbor) { +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); + + 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) + /* Neighbor may not exist yet. So, do not enter failed state here. */ + log_link_message_warning_errno(link, m, r, "Could not set neighbor, ignoring"); + + if (link->neighbor_messages == 0) { + log_link_debug(link, "Neighbors set"); + link->neighbors_configured = true; + link_check_ready(link); + } + + return 1; +} + +static int neighbor_configure(Neighbor *neighbor, Link *link) { + _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_NEWNEIGH, + link->ifindex, neighbor->family); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_NEWNEIGH message: %m"); + + r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT); + if (r < 0) + return log_link_error_errno(link, r, "Could not set state: %m"); + + r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE); + if (r < 0) + return log_link_error_errno(link, r, "Could not set flags: %m"); + + r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr, neighbor->lladdr_size); + if (r < 0) + return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m"); + + r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr); + if (r < 0) + return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, neighbor_configure_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link->neighbor_messages++; + link_ref(link); + + r = neighbor_add(link, neighbor, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not add neighbor: %m"); + + return 0; +} + +int link_set_neighbors(Link *link) { + Neighbor *neighbor; + int r; + + assert(link); + assert(link->network); + assert(link->state != _LINK_STATE_INVALID); + + link->neighbors_configured = false; + + HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) { + r = neighbor_configure(neighbor, link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set neighbor: %m"); + } + + if (link->neighbor_messages == 0) { + link->neighbors_configured = true; + link_check_ready(link); + } else { + log_link_debug(link, "Setting neighbors"); + link_set_state(link, LINK_STATE_CONFIGURING); + } + + 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_message_warning_errno(link, m, r, "Could not remove neighbor"); + + return 1; +} + +static int neighbor_remove(Neighbor *neighbor, Link *link) { + _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_link_error_errno(link, 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_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, neighbor_remove_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + +static bool link_is_neighbor_configured(Link *link, Neighbor *neighbor) { + Neighbor *net_neighbor; + + assert(link); + assert(neighbor); + + if (!link->network) + return false; + + HASHMAP_FOREACH(net_neighbor, link->network->neighbors_by_section) + if (neighbor_equal(net_neighbor, neighbor)) + return true; + + return false; +} + +int link_drop_foreign_neighbors(Link *link) { + Neighbor *neighbor; + int r; + + assert(link); + + SET_FOREACH(neighbor, link->neighbors_foreign) + if (link_is_neighbor_configured(link, neighbor)) { + r = neighbor_add(link, neighbor, NULL); + if (r < 0) + return r; + } else { + r = neighbor_remove(neighbor, link); + if (r < 0) + return r; + } + + return 0; +} + +int link_drop_neighbors(Link *link) { + Neighbor *neighbor; + int k, r = 0; + + assert(link); + + SET_FOREACH(neighbor, link->neighbors) { + k = neighbor_remove(neighbor, link); + if (k < 0 && r >= 0) + r = k; + } + + return r; +} + +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, Manager *m) { + _cleanup_(neighbor_freep) Neighbor *tmp = NULL; + _cleanup_free_ char *addr_str = NULL, *lladdr_str = NULL; + Neighbor *neighbor = NULL; + uint16_t type, state; + int ifindex, r; + Link *link; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive neighbor message, ignoring"); + + 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_warning_errno(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; + } + + tmp = new0(Neighbor, 1); + + r = sd_rtnl_message_neigh_get_family(message, &tmp->family); + if (r < 0) { + log_link_warning(link, "rtnl: received neighbor message without family, ignoring."); + return 0; + } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { + log_link_debug(link, "rtnl: received neighbor message with invalid family '%i', ignoring.", tmp->family); + return 0; + } + + r = netlink_message_read_in_addr_union(message, NDA_DST, tmp->family, &tmp->in_addr); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received neighbor message without valid address, ignoring: %m"); + return 0; + } + + if (in_addr_to_string(tmp->family, &tmp->in_addr, &addr_str) < 0) + log_link_warning_errno(link, r, "Could not print address: %m"); + + r = manager_rtnl_process_neighbor_lladdr(message, &tmp->lladdr, &tmp->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, tmp, &neighbor); + + switch (type) { + case RTM_NEWNEIGH: + if (neighbor) + log_link_debug(link, "Received remembered neighbor: %s->%s", + strnull(addr_str), strnull(lladdr_str)); + else { + /* A neighbor appeared that we did not request */ + r = neighbor_add_foreign(link, tmp, NULL); + 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_debug(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; +} + +static int neighbor_section_verify(Neighbor *neighbor) { if (section_is_invalid(neighbor->section)) return -EINVAL; @@ -375,6 +591,17 @@ int neighbor_section_verify(Neighbor *neighbor) { return 0; } +void network_drop_invalid_neighbors(Network *network) { + Neighbor *neighbor; + + assert(network); + + HASHMAP_FOREACH(neighbor, network->neighbors_by_section) + if (neighbor_section_verify(neighbor) < 0) + neighbor_free(neighbor); +} + + int config_parse_neighbor_address( const char *unit, const char *filename, diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index 97ee1f6d73..bb403ef2da 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -1,26 +1,25 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include + #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" #include "networkd-util.h" +typedef Manager Manager; +typedef Network Network; +typedef Link Link; + union lladdr_union { struct ether_addr mac; union in_addr_union ip; }; -struct Neighbor { +typedef struct Neighbor { Network *network; Link *link; NetworkConfigSection *section; @@ -29,23 +28,17 @@ struct Neighbor { union in_addr_union in_addr; union lladdr_union lladdr; size_t lladdr_size; +} Neighbor; - LIST_FIELDS(Neighbor, neighbors); -}; +Neighbor *neighbor_free(Neighbor *neighbor); -void neighbor_free(Neighbor *neighbor); +void network_drop_invalid_neighbors(Network *network); -DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free); +int link_set_neighbors(Link *link); +int link_drop_neighbors(Link *link); +int link_drop_foreign_neighbors(Link *link); -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); +int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); 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 1258203adf..21d2e820e9 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -6,15 +6,25 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "conf-parser.h" #include "netem.h" #include "network-internal.h" +#include "networkd-address-label.h" +#include "networkd-address.h" #include "networkd-can.h" #include "networkd-conf.h" #include "networkd-dhcp-common.h" #include "networkd-dhcp-server.h" #include "networkd-dhcp4.h" #include "networkd-dhcp6.h" +#include "networkd-fdb.h" #include "networkd-ipv4ll.h" +#include "networkd-ipv6-proxy-ndp.h" +#include "networkd-mdb.h" #include "networkd-ndisc.h" #include "networkd-network.h" +#include "networkd-neighbor.h" +#include "networkd-nexthop.h" +#include "networkd-radv.h" +#include "networkd-route.h" +#include "networkd-routing-policy-rule.h" #include "networkd-sriov.h" #include "qdisc.h" #include "tclass.h" @@ -121,11 +131,11 @@ Address.Peer, config_parse_address, Address.Broadcast, config_parse_broadcast, 0, 0 Address.Label, config_parse_label, 0, 0 Address.PreferredLifetime, config_parse_lifetime, 0, 0 -Address.HomeAddress, config_parse_address_flags, 0, 0 -Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0 -Address.PrefixRoute, config_parse_address_flags, 0, 0 /* deprecated */ -Address.AddPrefixRoute, config_parse_address_flags, 0, 0 -Address.AutoJoin, config_parse_address_flags, 0, 0 +Address.HomeAddress, config_parse_address_flags, IFA_F_HOMEADDRESS, 0 +Address.ManageTemporaryAddress, config_parse_address_flags, IFA_F_MANAGETEMPADDR, 0 +Address.PrefixRoute, config_parse_address_flags, IFA_F_NOPREFIXROUTE, 0 /* deprecated */ +Address.AddPrefixRoute, config_parse_address_flags, IFA_F_NOPREFIXROUTE, 0 +Address.AutoJoin, config_parse_address_flags, IFA_F_MCAUTOJOIN, 0 Address.DuplicateAddressDetection, config_parse_duplicate_address_detection, 0, 0 Address.Scope, config_parse_address_scope, 0, 0 IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 49e6d62abb..1ef3a6a6d8 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -14,8 +14,16 @@ #include "in-addr-util.h" #include "networkd-dhcp-server.h" #include "network-internal.h" +#include "networkd-address-label.h" +#include "networkd-address.h" +#include "networkd-fdb.h" #include "networkd-manager.h" +#include "networkd-mdb.h" +#include "networkd-neighbor.h" #include "networkd-network.h" +#include "networkd-nexthop.h" +#include "networkd-radv.h" +#include "networkd-routing-policy-rule.h" #include "networkd-sriov.h" #include "parse-util.h" #include "path-lookup.h" @@ -148,19 +156,6 @@ static int network_resolve_stacked_netdevs(Network *network) { } int network_verify(Network *network) { - RoutePrefix *route_prefix, *route_prefix_next; - RoutingPolicyRule *rule, *rule_next; - Neighbor *neighbor, *neighbor_next; - AddressLabel *label, *label_next; - NextHop *nexthop, *nextnop_next; - Address *address, *address_next; - Prefix *prefix, *prefix_next; - Route *route, *route_next; - FdbEntry *fdb, *fdb_next; - MdbEntry *mdb, *mdb_next; - TrafficControl *tc; - SRIOV *sr_iov; - assert(network); assert(network->filename); @@ -213,18 +208,15 @@ int network_verify(Network *network) { network->filename); network->dhcp_server = false; } - if (network->n_static_addresses > 0) { + if (!ordered_hashmap_isempty(network->addresses_by_section)) log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.", network->filename); - while ((address = network->static_addresses)) - address_free(address); - } - if (network->n_static_routes > 0) { + if (!hashmap_isempty(network->routes_by_section)) log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.", network->filename); - while ((route = network->static_routes)) - route_free(route); - } + + network->addresses_by_section = ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free); + network->routes_by_section = hashmap_free_with_destructor(network->routes_by_section, route_free); } if (network->link_local < 0) @@ -290,54 +282,23 @@ int network_verify(Network *network) { if (network->keep_configuration < 0) network->keep_configuration = KEEP_CONFIGURATION_NO; - LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses) - if (address_section_verify(address) < 0) - address_free(address); + if (network->ipv6_proxy_ndp == 0 && !set_isempty(network->ipv6_proxy_ndp_addresses)) { + log_warning("%s: IPv6ProxyNDP= is disabled. Ignoring IPv6ProxyNDPAddress=.", network->filename); + network->ipv6_proxy_ndp_addresses = set_free_free(network->ipv6_proxy_ndp_addresses); + } - LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes) - if (route_section_verify(route, network) < 0) - route_free(route); - - LIST_FOREACH_SAFE(nexthops, nexthop, nextnop_next, network->static_nexthops) - if (nexthop_section_verify(nexthop) < 0) - nexthop_free(nexthop); - - LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries) - if (section_is_invalid(fdb->section)) - fdb_entry_free(fdb); - - LIST_FOREACH_SAFE(static_mdb_entries, mdb, mdb_next, network->static_mdb_entries) - if (mdb_entry_verify(mdb) < 0) - mdb_entry_free(mdb); - - LIST_FOREACH_SAFE(neighbors, neighbor, neighbor_next, network->neighbors) - if (neighbor_section_verify(neighbor) < 0) - neighbor_free(neighbor); - - LIST_FOREACH_SAFE(labels, label, label_next, network->address_labels) - if (section_is_invalid(label->section)) - address_label_free(label); - - LIST_FOREACH_SAFE(prefixes, prefix, prefix_next, network->static_prefixes) - if (section_is_invalid(prefix->section)) - prefix_free(prefix); - - LIST_FOREACH_SAFE(route_prefixes, route_prefix, route_prefix_next, network->static_route_prefixes) - if (section_is_invalid(route_prefix->section)) - route_prefix_free(route_prefix); - - LIST_FOREACH_SAFE(rules, rule, rule_next, network->rules) - if (routing_policy_rule_section_verify(rule) < 0) - routing_policy_rule_free(rule); - - bool has_root = false, has_clsact = false; - ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section) - if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0) - traffic_control_free(tc); - - ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section) - if (sr_iov_section_verify(sr_iov) < 0) - sr_iov_free(sr_iov); + network_drop_invalid_addresses(network); + network_drop_invalid_routes(network); + network_drop_invalid_nexthops(network); + network_drop_invalid_fdb_entries(network); + network_drop_invalid_mdb_entries(network); + network_drop_invalid_neighbors(network); + network_drop_invalid_address_labels(network); + network_drop_invalid_prefixes(network); + network_drop_invalid_route_prefixes(network); + network_drop_invalid_routing_policy_rules(network); + network_drop_invalid_traffic_control(network); + network_drop_invalid_sr_iov(network); return 0; } @@ -644,18 +605,6 @@ failure: } static Network *network_free(Network *network) { - IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; - RoutePrefix *route_prefix; - RoutingPolicyRule *rule; - AddressLabel *label; - FdbEntry *fdb_entry; - MdbEntry *mdb_entry; - Neighbor *neighbor; - Address *address; - NextHop *nexthop; - Prefix *prefix; - Route *route; - if (!network) return NULL; @@ -711,49 +660,17 @@ static Network *network_free(Network *network) { netdev_unref(network->vrf); hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref); - while ((route = network->static_routes)) - route_free(route); - - while ((nexthop = network->static_nexthops)) - nexthop_free(nexthop); - - while ((address = network->static_addresses)) - address_free(address); - - while ((fdb_entry = network->static_fdb_entries)) - fdb_entry_free(fdb_entry); - - while ((mdb_entry = network->static_mdb_entries)) - mdb_entry_free(mdb_entry); - - 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); - - while ((prefix = network->static_prefixes)) - prefix_free(prefix); - - while ((route_prefix = network->static_route_prefixes)) - route_prefix_free(route_prefix); - - while ((rule = network->rules)) - routing_policy_rule_free(rule); - - hashmap_free(network->addresses_by_section); - hashmap_free(network->routes_by_section); - hashmap_free(network->nexthops_by_section); - hashmap_free(network->fdb_entries_by_section); - hashmap_free(network->mdb_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->route_prefixes_by_section); - hashmap_free(network->rules_by_section); + set_free_free(network->ipv6_proxy_ndp_addresses); + ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free); + hashmap_free_with_destructor(network->routes_by_section, route_free); + hashmap_free_with_destructor(network->nexthops_by_section, nexthop_free); + hashmap_free_with_destructor(network->fdb_entries_by_section, fdb_entry_free); + hashmap_free_with_destructor(network->mdb_entries_by_section, mdb_entry_free); + hashmap_free_with_destructor(network->neighbors_by_section, neighbor_free); + hashmap_free_with_destructor(network->address_labels_by_section, address_label_free); + hashmap_free_with_destructor(network->prefixes_by_section, prefix_free); + hashmap_free_with_destructor(network->route_prefixes_by_section, route_prefix_free); + hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free); ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free); ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free); @@ -866,30 +783,33 @@ bool network_has_static_ipv6_configurations(Network *network) { assert(network); - LIST_FOREACH(addresses, address, network->static_addresses) + ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) if (address->family == AF_INET6) return true; - LIST_FOREACH(routes, route, network->static_routes) + HASHMAP_FOREACH(route, network->routes_by_section) if (route->family == AF_INET6) return true; - LIST_FOREACH(static_fdb_entries, fdb, network->static_fdb_entries) + HASHMAP_FOREACH(fdb, network->fdb_entries_by_section) if (fdb->family == AF_INET6) return true; - LIST_FOREACH(static_mdb_entries, mdb, network->static_mdb_entries) + HASHMAP_FOREACH(mdb, network->mdb_entries_by_section) if (mdb->family == AF_INET6) return true; - LIST_FOREACH(neighbors, neighbor, network->neighbors) + HASHMAP_FOREACH(neighbor, network->neighbors_by_section) if (neighbor->family == AF_INET6) return true; - if (!LIST_IS_EMPTY(network->address_labels)) + if (!hashmap_isempty(network->address_labels_by_section)) return true; - if (!LIST_IS_EMPTY(network->static_prefixes)) + if (!hashmap_isempty(network->prefixes_by_section)) + return true; + + if (!hashmap_isempty(network->route_prefixes_by_section)) return true; return false; @@ -1026,95 +946,6 @@ int config_parse_domains( } } -int config_parse_ipv6token( - 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) { - - union in_addr_union buffer; - struct in6_addr *token = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(token); - - r = in_addr_from_string(AF_INET6, rvalue, &buffer); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse IPv6 token, ignoring: %s", rvalue); - return 0; - } - - if (in_addr_is_null(AF_INET6, &buffer)) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "IPv6 token cannot be the ANY address, ignoring: %s", rvalue); - return 0; - } - - if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue); - return 0; - } - - *token = buffer.in6; - - return 0; -} - -static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = { - [IPV6_PRIVACY_EXTENSIONS_NO] = "no", - [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public", - [IPV6_PRIVACY_EXTENSIONS_YES] = "yes", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions, - IPV6_PRIVACY_EXTENSIONS_YES); - -int config_parse_ipv6_privacy_extensions( - 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) { - - IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(ipv6_privacy_extensions); - - s = ipv6_privacy_extensions_from_string(rvalue); - if (s < 0) { - if (streq(rvalue, "kernel")) - s = _IPV6_PRIVACY_EXTENSIONS_INVALID; - else { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); - return 0; - } - } - - *ipv6_privacy_extensions = s; - - return 0; -} - int config_parse_hostname( const char *unit, const char *filename, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 5ba0bc705d..cbcbb73872 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -12,38 +12,21 @@ #include "conf-parser.h" #include "hashmap.h" #include "netdev.h" -#include "networkd-address-label.h" -#include "networkd-address.h" #include "networkd-brvlan.h" #include "networkd-dhcp-common.h" #include "networkd-dhcp4.h" #include "networkd-dhcp6.h" #include "networkd-dhcp-server.h" -#include "networkd-fdb.h" -#include "networkd-ipv6-proxy-ndp.h" #include "networkd-lldp-rx.h" #include "networkd-lldp-tx.h" -#include "networkd-mdb.h" #include "networkd-ndisc.h" -#include "networkd-neighbor.h" -#include "networkd-nexthop.h" #include "networkd-radv.h" -#include "networkd-route.h" -#include "networkd-routing-policy-rule.h" +#include "networkd-sysctl.h" #include "networkd-util.h" #include "ordered-set.h" #include "resolve-util.h" #include "socket-netlink.h" -typedef enum IPv6PrivacyExtensions { - /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */ - IPV6_PRIVACY_EXTENSIONS_NO, - IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC, - IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */ - _IPV6_PRIVACY_EXTENSIONS_MAX, - _IPV6_PRIVACY_EXTENSIONS_INVALID = -1, -} IPv6PrivacyExtensions; - typedef enum KeepConfiguration { KEEP_CONFIGURATION_NO = 0, KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0, @@ -245,6 +228,7 @@ struct Network { int ipv6_dad_transmits; int ipv6_hop_limit; int ipv6_proxy_ndp; + Set *ipv6_proxy_ndp_addresses; int proxy_arp; uint32_t ipv6_mtu; @@ -285,31 +269,7 @@ struct Network { LLDPEmit lldp_emit; /* LLDP transmission */ char *lldp_mud; /* LLDP MUD URL */ - LIST_HEAD(Address, static_addresses); - LIST_HEAD(Route, static_routes); - LIST_HEAD(NextHop, static_nexthops); - LIST_HEAD(FdbEntry, static_fdb_entries); - LIST_HEAD(MdbEntry, static_mdb_entries); - LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); - LIST_HEAD(Neighbor, neighbors); - LIST_HEAD(AddressLabel, address_labels); - LIST_HEAD(Prefix, static_prefixes); - LIST_HEAD(RoutePrefix, static_route_prefixes); - LIST_HEAD(RoutingPolicyRule, rules); - - unsigned n_static_addresses; - unsigned n_static_routes; - unsigned n_static_nexthops; - unsigned n_static_fdb_entries; - unsigned n_static_mdb_entries; - unsigned n_ipv6_proxy_ndp_addresses; - unsigned n_neighbors; - unsigned n_address_labels; - unsigned n_static_prefixes; - unsigned n_static_route_prefixes; - unsigned n_rules; - - Hashmap *addresses_by_section; + OrderedHashmap *addresses_by_section; Hashmap *routes_by_section; Hashmap *nexthops_by_section; Hashmap *fdb_entries_by_section; @@ -360,8 +320,6 @@ bool network_has_static_ipv6_configurations(Network *network); CONFIG_PARSER_PROTOTYPE(config_parse_stacked_netdev); CONFIG_PARSER_PROTOTYPE(config_parse_tunnel); -CONFIG_PARSER_PROTOTYPE(config_parse_ipv6token); -CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions); CONFIG_PARSER_PROTOTYPE(config_parse_domains); CONFIG_PARSER_PROTOTYPE(config_parse_dns); CONFIG_PARSER_PROTOTYPE(config_parse_hostname); @@ -374,9 +332,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode); const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length); -const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_; -IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; - const char* keep_configuration_to_string(KeepConfiguration i) _const_; KeepConfiguration keep_configuration_from_string(const char *s) _pure_; diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index 6d89be1a25..6edeaabf88 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -5,17 +5,37 @@ #include #include "alloc-util.h" -#include "conf-parser.h" -#include "in-addr-util.h" #include "netlink-util.h" +#include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-network.h" #include "networkd-nexthop.h" #include "parse-util.h" #include "set.h" #include "string-util.h" -#include "util.h" -int nexthop_new(NextHop **ret) { +NextHop *nexthop_free(NextHop *nexthop) { + if (!nexthop) + return NULL; + + if (nexthop->network) { + assert(nexthop->section); + hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section); + } + + network_config_section_free(nexthop->section); + + if (nexthop->link) { + set_remove(nexthop->link->nexthops, nexthop); + set_remove(nexthop->link->nexthops_foreign, nexthop); + } + + return mfree(nexthop); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free); + +static int nexthop_new(NextHop **ret) { _cleanup_(nexthop_freep) NextHop *nexthop = NULL; nexthop = new(NextHop, 1); @@ -38,19 +58,17 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; - nexthop = hashmap_get(network->nexthops_by_section, n); - if (nexthop) { - *ret = TAKE_PTR(nexthop); - - return 0; - } + nexthop = hashmap_get(network->nexthops_by_section, n); + if (nexthop) { + *ret = TAKE_PTR(nexthop); + return 0; } r = nexthop_new(&nexthop); @@ -59,55 +77,24 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s nexthop->protocol = RTPROT_STATIC; nexthop->network = network; - LIST_PREPEND(nexthops, network->static_nexthops, nexthop); - network->n_static_nexthops++; + nexthop->section = TAKE_PTR(n); - if (filename) { - nexthop->section = TAKE_PTR(n); + r = hashmap_ensure_allocated(&network->nexthops_by_section, &network_config_hash_ops); + if (r < 0) + return r; - r = hashmap_ensure_allocated(&network->nexthops_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->nexthops_by_section, nexthop->section, nexthop); - if (r < 0) - return r; - } + r = hashmap_put(network->nexthops_by_section, nexthop->section, nexthop); + if (r < 0) + return r; *ret = TAKE_PTR(nexthop); - return 0; } -void nexthop_free(NextHop *nexthop) { - if (!nexthop) - return; - - if (nexthop->network) { - LIST_REMOVE(nexthops, nexthop->network->static_nexthops, nexthop); - - assert(nexthop->network->n_static_nexthops > 0); - nexthop->network->n_static_nexthops--; - - if (nexthop->section) - hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section); - } - - network_config_section_free(nexthop->section); - - if (nexthop->link) { - set_remove(nexthop->link->nexthops, nexthop); - set_remove(nexthop->link->nexthops_foreign, nexthop); - } - - free(nexthop); -} - static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) { assert(nexthop); siphash24_compress(&nexthop->id, sizeof(nexthop->id), state); - siphash24_compress(&nexthop->oif, sizeof(nexthop->oif), state); siphash24_compress(&nexthop->family, sizeof(nexthop->family), state); switch (nexthop->family) { @@ -129,27 +116,14 @@ static int nexthop_compare_func(const NextHop *a, const NextHop *b) { if (r != 0) return r; - r = CMP(a->oif, b->oif); - if (r != 0) - return r; - r = CMP(a->family, b->family); if (r != 0) return r; - switch (a->family) { - case AF_INET: - case AF_INET6: + if (IN_SET(a->family, AF_INET, AF_INET6)) + return memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family)); - r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family)); - if (r != 0) - return r; - - return 0; - default: - /* treat any other address family as AF_UNSPEC */ - return 0; - } + return 0; } DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( @@ -159,17 +133,7 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( nexthop_compare_func, nexthop_free); -bool nexthop_equal(NextHop *r1, NextHop *r2) { - if (r1 == r2) - return true; - - if (!r1 || !r2) - return false; - - return nexthop_compare_func(r1, r2) == 0; -} - -int nexthop_get(Link *link, NextHop *in, NextHop **ret) { +static int nexthop_get(Link *link, NextHop *in, NextHop **ret) { NextHop *existing; assert(link); @@ -205,7 +169,6 @@ static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop return r; nexthop->id = in->id; - nexthop->oif = in->oif; nexthop->family = in->family; nexthop->gw = in->gw; @@ -225,11 +188,11 @@ static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop return 0; } -int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret) { +static int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret) { return nexthop_add_internal(link, &link->nexthops_foreign, in, ret); } -int nexthop_add(Link *link, NextHop *in, NextHop **ret) { +static int nexthop_add(Link *link, NextHop *in, NextHop **ret) { NextHop *nexthop; int r; @@ -258,26 +221,34 @@ int nexthop_add(Link *link, NextHop *in, NextHop **ret) { return 0; } -static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; - assert(m); assert(link); - assert(link->ifname); + assert(link->nexthop_messages > 0); + + link->nexthop_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 != -ESRCH) - log_link_message_warning_errno(link, m, r, "Could not drop nexthop, ignoring"); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Could not set nexthop"); + link_enter_failed(link); + return 1; + } + + if (link->nexthop_messages == 0) { + log_link_debug(link, "Nexthop set"); + link->static_nexthops_configured = true; + link_check_ready(link); + } return 1; } -int nexthop_remove(NextHop *nexthop, Link *link, - link_netlink_message_handler_t callback) { - +static int nexthop_configure(NextHop *nexthop, Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -287,52 +258,6 @@ int nexthop_remove(NextHop *nexthop, Link *link, assert(link->ifindex > 0); assert(IN_SET(nexthop->family, AF_INET, AF_INET6)); - r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req, - RTM_DELNEXTHOP, nexthop->family, - nexthop->protocol); - if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m"); - - if (DEBUG_LOGGING) { - _cleanup_free_ char *gw = NULL; - - if (!in_addr_is_null(nexthop->family, &nexthop->gw)) - (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw); - - log_link_debug(link, "Removing nexthop: gw: %s", strna(gw)); - } - - if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) { - r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, nexthop->family, &nexthop->gw); - if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m"); - } - - r = netlink_call_async(link->manager->rtnl, NULL, req, - callback ?: nexthop_remove_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 0; -} - -int nexthop_configure( - NextHop *nexthop, - Link *link, - link_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - assert(link->ifindex > 0); - assert(IN_SET(nexthop->family, AF_INET, AF_INET6)); - assert(callback); - if (DEBUG_LOGGING) { _cleanup_free_ char *gw = NULL; @@ -366,7 +291,7 @@ int nexthop_configure( return log_link_error_errno(link, r, "Could not set nexthop family: %m"); } - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, + r = netlink_call_async(link->manager->rtnl, NULL, req, nexthop_handler, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); @@ -380,7 +305,141 @@ int nexthop_configure( return 1; } -int nexthop_section_verify(NextHop *nh) { +int link_set_nexthop(Link *link) { + NextHop *nh; + int r; + + assert(link); + assert(link->network); + + link->static_nexthops_configured = false; + + HASHMAP_FOREACH(nh, link->network->nexthops_by_section) { + r = nexthop_configure(nh, link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set nexthop: %m"); + + link->nexthop_messages++; + } + + if (link->nexthop_messages == 0) { + link->static_nexthops_configured = true; + link_check_ready(link); + } else { + log_link_debug(link, "Setting nexthop"); + link_set_state(link, LINK_STATE_CONFIGURING); + } + + return 1; +} + +int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + _cleanup_(nexthop_freep) NextHop *tmp = NULL; + _cleanup_free_ char *gateway = NULL; + NextHop *nexthop = NULL; + uint32_t ifindex; + uint16_t type; + Link *link; + int r; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring"); + + 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_NEWNEXTHOP, RTM_DELNEXTHOP)) { + log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type); + return 0; + } + + r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex); + if (r == -ENODATA) { + log_warning_errno(r, "rtnl: received nexthop message without NHA_OIF attribute, ignoring: %m"); + return 0; + } else if (r < 0) { + log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex); + return 0; + } + + r = link_get(m, ifindex, &link); + if (r < 0 || !link) { + if (!m->enumerating) + log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex); + return 0; + } + + r = nexthop_new(&tmp); + if (r < 0) + return log_oom(); + + r = sd_rtnl_message_get_family(message, &tmp->family); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m"); + return 0; + } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) + return log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family); + + r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: could not get NHA_ID attribute, ignoring: %m"); + return 0; + } + + (void) nexthop_get(link, tmp, &nexthop); + + if (DEBUG_LOGGING) + (void) in_addr_to_string(tmp->family, &tmp->gw, &gateway); + + switch (type) { + case RTM_NEWNEXTHOP: + if (nexthop) + log_link_debug(link, "Received remembered nexthop: %s, id: %d", strna(gateway), tmp->id); + else { + log_link_debug(link, "Remembering foreign nexthop: %s, id: %d", strna(gateway), tmp->id); + r = nexthop_add_foreign(link, tmp, &nexthop); + if (r < 0) { + log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m"); + return 0; + } + } + break; + case RTM_DELNEXTHOP: + if (nexthop) { + log_link_debug(link, "Forgetting nexthop: %s, id: %d", strna(gateway), tmp->id); + nexthop_free(nexthop); + } else + log_link_debug(link, "Kernel removed a nexthop we don't remember: %s, id: %d, ignoring.", + strna(gateway), tmp->id); + break; + + default: + assert_not_reached("Received invalid RTNL message type"); + } + + return 1; +} + +static int nexthop_section_verify(NextHop *nh) { if (section_is_invalid(nh->section)) return -EINVAL; @@ -390,6 +449,16 @@ int nexthop_section_verify(NextHop *nh) { return 0; } +void network_drop_invalid_nexthops(Network *network) { + NextHop *nh; + + assert(network); + + HASHMAP_FOREACH(nh, network->nexthops_by_section) + if (nexthop_section_verify(nh) < 0) + nexthop_free(nh); +} + int config_parse_nexthop_id( const char *unit, const char *filename, diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index 28cbdad738..3cdb068efd 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -4,16 +4,19 @@ #pragma once +#include + +#include "sd-netlink.h" + #include "conf-parser.h" -#include "macro.h" - -typedef struct NextHop NextHop; -typedef struct NetworkConfigSection NetworkConfigSection; - -#include "networkd-network.h" +#include "in-addr-util.h" #include "networkd-util.h" -struct NextHop { +typedef struct Link Link; +typedef struct Manager Manager; +typedef struct Network Network; + +typedef struct NextHop { Network *network; NetworkConfigSection *section; @@ -21,30 +24,18 @@ struct NextHop { unsigned char protocol; - int family; - uint32_t oif; uint32_t id; - + int family; union in_addr_union gw; +} NextHop; - LIST_FIELDS(NextHop, nexthops); -}; +NextHop *nexthop_free(NextHop *nexthop); -extern const struct hash_ops nexthop_hash_ops; +void network_drop_invalid_nexthops(Network *network); -int nexthop_new(NextHop **ret); -void nexthop_free(NextHop *nexthop); -int nexthop_configure(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback); -int nexthop_remove(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback); +int link_set_nexthop(Link *link); -int nexthop_get(Link *link, NextHop *in, NextHop **ret); -int nexthop_add(Link *link, NextHop *in, NextHop **ret); -int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret); -bool nexthop_equal(NextHop *r1, NextHop *r2); - -int nexthop_section_verify(NextHop *nexthop); - -DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free); +int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 003a50b68b..1167f2865f 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -7,35 +7,32 @@ #include #include "dns-domain.h" -#include "networkd-address.h" +#include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-network.h" #include "networkd-radv.h" #include "parse-util.h" -#include "sd-radv.h" #include "string-util.h" #include "string-table.h" #include "strv.h" -void prefix_free(Prefix *prefix) { +Prefix *prefix_free(Prefix *prefix) { if (!prefix) - return; + return NULL; if (prefix->network) { - LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix); - assert(prefix->network->n_static_prefixes > 0); - prefix->network->n_static_prefixes--; - - if (prefix->section) - hashmap_remove(prefix->network->prefixes_by_section, - prefix->section); + assert(prefix->section); + hashmap_remove(prefix->network->prefixes_by_section, prefix->section); } network_config_section_free(prefix->section); sd_radv_prefix_unref(prefix->radv_prefix); - free(prefix); + return mfree(prefix); } +DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free); + static int prefix_new(Prefix **ret) { _cleanup_(prefix_freep) Prefix *prefix = NULL; @@ -51,29 +48,24 @@ static int prefix_new(Prefix **ret) { return 0; } -static int prefix_new_static(Network *network, const char *filename, - unsigned section_line, Prefix **ret) { +static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) { _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; _cleanup_(prefix_freep) Prefix *prefix = NULL; int r; assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; - if (section_line) { - prefix = hashmap_get(network->prefixes_by_section, n); - if (prefix) { - *ret = TAKE_PTR(prefix); - - return 0; - } - } + prefix = hashmap_get(network->prefixes_by_section, n); + if (prefix) { + *ret = TAKE_PTR(prefix); + return 0; } r = prefix_new(&prefix); @@ -81,26 +73,38 @@ static int prefix_new_static(Network *network, const char *filename, return r; prefix->network = network; - LIST_APPEND(prefixes, network->static_prefixes, prefix); - network->n_static_prefixes++; + prefix->section = TAKE_PTR(n); - if (filename) { - prefix->section = TAKE_PTR(n); + r = hashmap_ensure_allocated(&network->prefixes_by_section, &network_config_hash_ops); + if (r < 0) + return r; - r = hashmap_ensure_allocated(&network->prefixes_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->prefixes_by_section, prefix->section, prefix); - if (r < 0) - return r; - } + r = hashmap_put(network->prefixes_by_section, prefix->section, prefix); + if (r < 0) + return r; *ret = TAKE_PTR(prefix); return 0; } +RoutePrefix *route_prefix_free(RoutePrefix *prefix) { + if (!prefix) + return NULL; + + if (prefix->network) { + assert(prefix->section); + hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section); + } + + network_config_section_free(prefix->section); + sd_radv_route_prefix_unref(prefix->radv_route_prefix); + + return mfree(prefix); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free); + static int route_prefix_new(RoutePrefix **ret) { _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL; @@ -116,49 +120,24 @@ static int route_prefix_new(RoutePrefix **ret) { return 0; } -void route_prefix_free(RoutePrefix *prefix) { - if (!prefix) - return; - - if (prefix->network) { - LIST_REMOVE(route_prefixes, prefix->network->static_route_prefixes, prefix); - assert(prefix->network->n_static_route_prefixes > 0); - prefix->network->n_static_route_prefixes--; - - if (prefix->section) - hashmap_remove(prefix->network->route_prefixes_by_section, - prefix->section); - } - - network_config_section_free(prefix->section); - sd_radv_route_prefix_unref(prefix->radv_route_prefix); - - free(prefix); -} - -static int route_prefix_new_static(Network *network, const char *filename, - unsigned section_line, RoutePrefix **ret) { +static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) { _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL; int r; assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; - if (section_line) { - prefix = hashmap_get(network->route_prefixes_by_section, n); - if (prefix) { - *ret = TAKE_PTR(prefix); - - return 0; - } - } + prefix = hashmap_get(network->route_prefixes_by_section, n); + if (prefix) { + *ret = TAKE_PTR(prefix); + return 0; } r = route_prefix_new(&prefix); @@ -166,36 +145,52 @@ static int route_prefix_new_static(Network *network, const char *filename, return r; prefix->network = network; - LIST_APPEND(route_prefixes, network->static_route_prefixes, prefix); - network->n_static_route_prefixes++; + prefix->section = TAKE_PTR(n); - if (filename) { - prefix->section = TAKE_PTR(n); + r = hashmap_ensure_allocated(&network->route_prefixes_by_section, &network_config_hash_ops); + if (r < 0) + return r; - r = hashmap_ensure_allocated(&network->route_prefixes_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->route_prefixes_by_section, prefix->section, prefix); - if (r < 0) - return r; - } + r = hashmap_put(network->route_prefixes_by_section, prefix->section, prefix); + if (r < 0) + return r; *ret = TAKE_PTR(prefix); return 0; } -int config_parse_prefix(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) { +void network_drop_invalid_prefixes(Network *network) { + Prefix *prefix; + + assert(network); + + HASHMAP_FOREACH(prefix, network->prefixes_by_section) + if (section_is_invalid(prefix->section)) + prefix_free(prefix); +} + +void network_drop_invalid_route_prefixes(Network *network) { + RoutePrefix *prefix; + + assert(network); + + HASHMAP_FOREACH(prefix, network->route_prefixes_by_section) + if (section_is_invalid(prefix->section)) + route_prefix_free(prefix); +} + +int config_parse_prefix( + 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_(prefix_free_or_set_invalidp) Prefix *p = NULL; @@ -230,16 +225,18 @@ int config_parse_prefix(const char *unit, return 0; } -int config_parse_prefix_flags(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) { +int config_parse_prefix_flags( + 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_(prefix_free_or_set_invalidp) Prefix *p = NULL; int r; @@ -274,16 +271,18 @@ int config_parse_prefix_flags(const char *unit, return 0; } -int config_parse_prefix_lifetime(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) { +int config_parse_prefix_lifetime( + 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_(prefix_free_or_set_invalidp) Prefix *p = NULL; usec_t usec; @@ -362,16 +361,17 @@ int config_parse_prefix_assign( return 0; } -int config_parse_route_prefix(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) { +int config_parse_route_prefix( + 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_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL; @@ -406,16 +406,18 @@ int config_parse_route_prefix(const char *unit, return 0; } -int config_parse_route_prefix_lifetime(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) { +int config_parse_route_prefix_lifetime( + 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_(route_prefix_free_or_set_invalidp) RoutePrefix *p = NULL; usec_t usec; @@ -451,16 +453,15 @@ int config_parse_route_prefix_lifetime(const char *unit, return 0; } -static int radv_get_ip6dns(Network *network, struct in6_addr **dns, - size_t *n_dns) { +static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) { _cleanup_free_ struct in6_addr *addresses = NULL; - size_t i, n_addresses = 0, n_allocated = 0; + size_t n_addresses = 0, n_allocated = 0; assert(network); - assert(dns); - assert(n_dns); + assert(ret_addresses); + assert(ret_size); - for (i = 0; i < network->n_dns; i++) { + for (size_t i = 0; i < network->n_dns; i++) { union in_addr_union *addr; if (network->dns[i]->family != AF_INET6) @@ -479,11 +480,8 @@ static int radv_get_ip6dns(Network *network, struct in6_addr **dns, addresses[n_addresses++] = addr->in6; } - if (addresses) { - *dns = TAKE_PTR(addresses); - - *n_dns = n_addresses; - } + *ret_addresses = TAKE_PTR(addresses); + *ret_size = n_addresses; return n_addresses; } @@ -520,7 +518,7 @@ static int radv_set_dns(Link *link, Link *uplink) { lifetime_usec = SD_RADV_DEFAULT_DNS_LIFETIME_USEC; - r = radv_get_ip6dns(link->network, &dns, &n_dns); + r = network_get_ipv6_dns(link->network, &dns, &n_dns); if (r > 0) goto set_dns; @@ -530,7 +528,7 @@ static int radv_set_dns(Link *link, Link *uplink) { return 0; } - r = radv_get_ip6dns(uplink->network, &dns, &n_dns); + r = network_get_ipv6_dns(uplink->network, &dns, &n_dns); if (r > 0) goto set_dns; } @@ -604,19 +602,29 @@ int radv_emit_dns(Link *link) { return 0; } +static bool link_radv_enabled(Link *link) { + assert(link); + + if (!link_ipv6ll_enabled(link)) + return false; + + return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE; +} + int radv_configure(Link *link) { - RoutePrefix *q; - Prefix *p; int r; assert(link); assert(link->network); + if (!link_radv_enabled(link)) + return 0; + r = sd_radv_new(&link->radv); if (r < 0) return r; - r = sd_radv_attach_event(link->radv, NULL, 0); + r = sd_radv_attach_event(link->radv, link->manager->event, 0); if (r < 0) return r; @@ -651,7 +659,10 @@ int radv_configure(Link *link) { } if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) { - LIST_FOREACH(prefixes, p, link->network->static_prefixes) { + RoutePrefix *q; + Prefix *p; + + HASHMAP_FOREACH(p, link->network->prefixes_by_section) { r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); if (r == -EEXIST) continue; @@ -663,7 +674,7 @@ int radv_configure(Link *link) { return r; } - LIST_FOREACH(route_prefixes, q, link->network->static_route_prefixes) { + HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) { r = sd_radv_add_route_prefix(link->radv, q->radv_route_prefix, false); if (r == -EEXIST) continue; @@ -675,8 +686,43 @@ int radv_configure(Link *link) { return 0; } -int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, - uint32_t lifetime_preferred, uint32_t lifetime_valid) { +int radv_update_mac(Link *link) { + bool restart; + int r; + + assert(link); + + if (!link->radv) + return 0; + + restart = sd_radv_is_running(link->radv); + + if (restart) { + r = sd_radv_stop(link->radv); + if (r < 0) + return r; + } + + r = sd_radv_set_mac(link->radv, &link->mac); + if (r < 0) + return r; + + if (restart) { + r = sd_radv_start(link->radv); + if (r < 0) + return r; + } + + return 0; +} + +int radv_add_prefix( + Link *link, + const struct in6_addr *prefix, + uint8_t prefix_len, + uint32_t lifetime_preferred, + uint32_t lifetime_valid) { + _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; int r; @@ -822,10 +868,10 @@ int config_parse_radv_search_domains( } static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = { - [RADV_PREFIX_DELEGATION_NONE] = "no", + [RADV_PREFIX_DELEGATION_NONE] = "no", [RADV_PREFIX_DELEGATION_STATIC] = "static", - [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6", - [RADV_PREFIX_DELEGATION_BOTH] = "yes", + [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6", + [RADV_PREFIX_DELEGATION_BOTH] = "yes", }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN( @@ -833,21 +879,24 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN( RADVPrefixDelegation, RADV_PREFIX_DELEGATION_BOTH); -DEFINE_CONFIG_PARSE_ENUM(config_parse_router_prefix_delegation, - radv_prefix_delegation, - RADVPrefixDelegation, - "Invalid router prefix delegation"); +DEFINE_CONFIG_PARSE_ENUM( + config_parse_router_prefix_delegation, + radv_prefix_delegation, + RADVPrefixDelegation, + "Invalid router prefix delegation"); + +int config_parse_router_preference( + 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) { -int config_parse_router_preference(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; assert(filename); diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index 496ef97adc..fbb93a9acc 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -5,13 +5,17 @@ Copyright © 2017 Intel Corporation. All rights reserved. ***/ +#include +#include + +#include "sd-radv.h" + +#include "in-addr-util.h" #include "conf-parser.h" -#include "networkd-address.h" -#include "networkd-link.h" #include "networkd-util.h" -typedef struct Prefix Prefix; -typedef struct RoutePrefix RoutePrefix; +typedef struct Network Network; +typedef struct Link Link; typedef enum RADVPrefixDelegation { RADV_PREFIX_DELEGATION_NONE = 0, @@ -22,36 +26,31 @@ typedef enum RADVPrefixDelegation { _RADV_PREFIX_DELEGATION_INVALID = -1, } RADVPrefixDelegation; -struct Prefix { +typedef struct Prefix { Network *network; NetworkConfigSection *section; sd_radv_prefix *radv_prefix; bool assign; +} Prefix; - LIST_FIELDS(Prefix, prefixes); -}; - -struct RoutePrefix { +typedef struct RoutePrefix { Network *network; NetworkConfigSection *section; sd_radv_route_prefix *radv_route_prefix; +} RoutePrefix; - LIST_FIELDS(RoutePrefix, route_prefixes); -}; +Prefix *prefix_free(Prefix *prefix); +RoutePrefix *route_prefix_free(RoutePrefix *prefix); -void prefix_free(Prefix *prefix); - -DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free); - -void route_prefix_free(RoutePrefix *prefix); - -DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free); +void network_drop_invalid_prefixes(Network *network); +void network_drop_invalid_route_prefixes(Network *network); int radv_emit_dns(Link *link); int radv_configure(Link *link); +int radv_update_mac(Link *link); int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 2610b24c82..659fecbf2e 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -3,16 +3,14 @@ #include #include "alloc-util.h" -#include "conf-parser.h" -#include "in-addr-util.h" -#include "missing_network.h" #include "netlink-util.h" #include "networkd-ipv4ll.h" #include "networkd-manager.h" -#include "networkd-ndisc.h" +#include "networkd-network.h" +#include "networkd-nexthop.h" #include "networkd-route.h" +#include "networkd-routing-policy-rule.h" #include "parse-util.h" -#include "set.h" #include "socket-netlink.h" #include "string-table.h" #include "string-util.h" @@ -23,6 +21,134 @@ #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U +static uint32_t link_get_vrf_table(Link *link) { + return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN; +} + +uint32_t link_get_dhcp_route_table(Link *link) { + /* When the interface is part of an VRF use the VRFs routing table, unless + * another table is explicitly specified. */ + if (link->network->dhcp_route_table_set) + return link->network->dhcp_route_table; + return link_get_vrf_table(link); +} + +uint32_t link_get_ipv6_accept_ra_route_table(Link *link) { + if (link->network->ipv6_accept_ra_route_table_set) + return link->network->ipv6_accept_ra_route_table; + return link_get_vrf_table(link); +} + +static const char * const route_type_table[__RTN_MAX] = { + [RTN_UNICAST] = "unicast", + [RTN_LOCAL] = "local", + [RTN_BROADCAST] = "broadcast", + [RTN_ANYCAST] = "anycast", + [RTN_MULTICAST] = "multicast", + [RTN_BLACKHOLE] = "blackhole", + [RTN_UNREACHABLE] = "unreachable", + [RTN_PROHIBIT] = "prohibit", + [RTN_THROW] = "throw", + [RTN_NAT] = "nat", + [RTN_XRESOLVE] = "xresolve", +}; + +assert_cc(__RTN_MAX <= UCHAR_MAX); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_type, int); + +static const char * const route_scope_table[] = { + [RT_SCOPE_UNIVERSE] = "global", + [RT_SCOPE_SITE] = "site", + [RT_SCOPE_LINK] = "link", + [RT_SCOPE_HOST] = "host", + [RT_SCOPE_NOWHERE] = "nowhere", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int); + +#define ROUTE_SCOPE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("nowhere") + 1) +static const char *format_route_scope(int scope, char *buf, size_t size) { + const char *s; + char *p = buf; + + s = route_scope_to_string(scope); + if (s) + strpcpy(&p, size, s); + else + strpcpyf(&p, size, "%d", scope); + + return buf; +} + +static const char * const route_table_table[] = { + [RT_TABLE_DEFAULT] = "default", + [RT_TABLE_MAIN] = "main", + [RT_TABLE_LOCAL] = "local", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int); + +#define ROUTE_TABLE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("default") + 1) +static const char *format_route_table(int table, char *buf, size_t size) { + const char *s; + char *p = buf; + + s = route_table_to_string(table); + if (s) + strpcpy(&p, size, s); + else + strpcpyf(&p, size, "%d", table); + + return buf; +} + +static const char * const route_protocol_table[] = { + [RTPROT_KERNEL] = "kernel", + [RTPROT_BOOT] = "boot", + [RTPROT_STATIC] = "static", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_protocol, int); + +static const char * const route_protocol_full_table[] = { + [RTPROT_REDIRECT] = "redirect", + [RTPROT_KERNEL] = "kernel", + [RTPROT_BOOT] = "boot", + [RTPROT_STATIC] = "static", + [RTPROT_GATED] = "gated", + [RTPROT_RA] = "ra", + [RTPROT_MRT] = "mrt", + [RTPROT_ZEBRA] = "zebra", + [RTPROT_BIRD] = "bird", + [RTPROT_DNROUTED] = "dnrouted", + [RTPROT_XORP] = "xorp", + [RTPROT_NTK] = "ntk", + [RTPROT_DHCP] = "dhcp", + [RTPROT_MROUTED] = "mrouted", + [RTPROT_BABEL] = "babel", + [RTPROT_BGP] = "bgp", + [RTPROT_ISIS] = "isis", + [RTPROT_OSPF] = "ospf", + [RTPROT_RIP] = "rip", + [RTPROT_EIGRP] = "eigrp", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(route_protocol_full, int); + +#define ROUTE_PROTOCOL_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("redirect") + 1) +static const char *format_route_protocol(int protocol, char *buf, size_t size) { + const char *s; + char *p = buf; + + s = route_protocol_full_to_string(protocol); + if (s) + strpcpy(&p, size, s); + else + strpcpyf(&p, size, "%d", protocol); + + return buf; +} + static unsigned routes_max(void) { static thread_local unsigned cached = 0; @@ -82,22 +208,20 @@ static int route_new_static(Network *network, const char *filename, unsigned sec assert(network); assert(ret); - assert(!!filename == (section_line > 0)); + assert(filename); + assert(section_line > 0); - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; - route = hashmap_get(network->routes_by_section, n); - if (route) { - *ret = TAKE_PTR(route); - - return 0; - } + route = hashmap_get(network->routes_by_section, n); + if (route) { + *ret = TAKE_PTR(route); + return 0; } - if (network->n_static_routes >= routes_max()) + if (hashmap_size(network->routes_by_section) >= routes_max()) return -E2BIG; r = route_new(&route); @@ -106,38 +230,27 @@ static int route_new_static(Network *network, const char *filename, unsigned sec route->protocol = RTPROT_STATIC; route->network = network; - LIST_PREPEND(routes, network->static_routes, route); - network->n_static_routes++; + route->section = TAKE_PTR(n); - if (filename) { - route->section = TAKE_PTR(n); + r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops); + if (r < 0) + return r; - r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->routes_by_section, route->section, route); - if (r < 0) - return r; - } + r = hashmap_put(network->routes_by_section, route->section, route); + if (r < 0) + return r; *ret = TAKE_PTR(route); - return 0; } -void route_free(Route *route) { +Route *route_free(Route *route) { if (!route) - return; + return NULL; if (route->network) { - LIST_REMOVE(routes, route->network->static_routes, route); - - assert(route->network->n_static_routes > 0); - route->network->n_static_routes--; - - if (route->section) - hashmap_remove(route->network->routes_by_section, route->section); + assert(route->section); + hashmap_remove(route->network->routes_by_section, route->section); } network_config_section_free(route->section); @@ -162,7 +275,7 @@ void route_free(Route *route) { sd_event_source_unref(route->expire); - free(route); + return mfree(route); } void route_hash_func(const Route *route, struct siphash *state) { @@ -280,7 +393,7 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( route_compare_func, route_free); -bool route_equal(Route *r1, Route *r2) { +static bool route_equal(Route *r1, Route *r2) { if (r1 == r2) return true; @@ -290,7 +403,7 @@ bool route_equal(Route *r1, Route *r2) { return route_compare_func(r1, r2) == 0; } -int route_get(Link *link, Route *in, Route **ret) { +static int route_get(Link *link, Route *in, Route **ret) { Route *existing; @@ -360,11 +473,11 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) return 0; } -int route_add_foreign(Link *link, Route *in, Route **ret) { +static int route_add_foreign(Link *link, Route *in, Route **ret) { return route_add_internal(link, &link->routes_foreign, in, ret); } -int route_add(Link *link, Route *in, Route **ret) { +static int route_add(Link *link, Route *in, Route **ret) { Route *route; int r; @@ -509,7 +622,81 @@ int route_remove(Route *route, Link *link, return 0; } -int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { +static bool link_is_static_route_configured(Link *link, Route *route) { + Route *net_route; + + assert(link); + assert(route); + + if (!link->network) + return false; + + HASHMAP_FOREACH(net_route, link->network->routes_by_section) + if (route_equal(net_route, route)) + return true; + + return false; +} + +int link_drop_foreign_routes(Link *link) { + Route *route; + int k, r = 0; + + assert(link); + + SET_FOREACH(route, link->routes_foreign) { + /* do not touch routes managed by the kernel */ + if (route->protocol == RTPROT_KERNEL) + continue; + + /* do not touch multicast route added by kernel */ + /* FIXME: Why the kernel adds this route with protocol RTPROT_BOOT??? We need to investigate that. + * https://tools.ietf.org/html/rfc4862#section-5.4 may explain why. */ + if (route->protocol == RTPROT_BOOT && + route->family == AF_INET6 && + route->dst_prefixlen == 8 && + in_addr_equal(AF_INET6, &route->dst, &(union in_addr_union) { .in6 = {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}} })) + continue; + + if (route->protocol == RTPROT_STATIC && link->network && + FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) + continue; + + if (route->protocol == RTPROT_DHCP && link->network && + FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) + continue; + + if (link_is_static_route_configured(link, route)) + k = route_add(link, route, NULL); + else + k = route_remove(route, link, NULL); + if (k < 0 && r >= 0) + r = k; + } + + return r; +} + +int link_drop_routes(Link *link) { + Route *route; + int k, r = 0; + + assert(link); + + SET_FOREACH(route, link->routes) { + /* do not touch routes managed by the kernel */ + if (route->protocol == RTPROT_KERNEL) + continue; + + k = route_remove(route, link, NULL); + if (k < 0 && r >= 0) + r = k; + } + + return r; +} + +static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { Route *route = userdata; int r; @@ -821,8 +1008,437 @@ int route_configure( return 1; } +static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->route_messages > 0); + assert(IN_SET(link->state, LINK_STATE_CONFIGURING, + LINK_STATE_FAILED, LINK_STATE_LINGER)); + + link->route_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_message_warning_errno(link, m, r, "Could not set route"); + link_enter_failed(link); + return 1; + } + + if (link->route_messages == 0) { + log_link_debug(link, "Routes set"); + link->static_routes_configured = true; + link_set_nexthop(link); + } + + return 1; +} + +int link_set_routes(Link *link) { + enum { + PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */ + PHASE_GATEWAY, /* Second phase: Routes with a gateway */ + _PHASE_MAX + } phase; + Route *rt; + int r; + + assert(link); + assert(link->network); + assert(link->state != _LINK_STATE_INVALID); + + link->static_routes_configured = false; + + if (!link->addresses_ready) + return 0; + + if (!link_has_carrier(link) && !link->network->configure_without_carrier) + /* During configuring addresses, the link lost its carrier. As networkd is dropping + * the addresses now, let's not configure the routes either. */ + return 0; + + r = link_set_routing_policy_rules(link); + if (r < 0) + return r; + + /* 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++) + HASHMAP_FOREACH(rt, link->network->routes_by_section) { + if (rt->gateway_from_dhcp) + continue; + + if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY)) + continue; + + r = route_configure(rt, link, route_handler, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set routes: %m"); + if (r > 0) + link->route_messages++; + } + + if (link->route_messages == 0) { + link->static_routes_configured = true; + link_set_nexthop(link); + } else { + log_link_debug(link, "Setting routes"); + link_set_state(link, LINK_STATE_CONFIGURING); + } + + return 0; +} + +int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + _cleanup_(route_freep) Route *tmp = NULL; + Route *route = NULL; + Link *link = NULL; + uint32_t ifindex; + uint16_t type; + unsigned char table; + int r; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring"); + + 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_NEWROUTE, RTM_DELROUTE)) { + log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type); + return 0; + } + + r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex); + if (r == -ENODATA) { + log_debug("rtnl: received route message without ifindex, ignoring"); + return 0; + } else if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received route 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 route again, so just ignore it */ + if (!m->enumerating) + log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex); + return 0; + } + + r = route_new(&tmp); + if (r < 0) + return log_oom(); + + r = sd_rtnl_message_route_get_family(message, &tmp->family); + if (r < 0) { + log_link_warning(link, "rtnl: received route message without family, ignoring"); + return 0; + } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { + log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family); + return 0; + } + + r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol); + if (r < 0) { + log_warning_errno(r, "rtnl: received route message without route protocol: %m"); + return 0; + } + + switch (tmp->family) { + case AF_INET: + r = sd_netlink_message_read_in_addr(message, RTA_DST, &tmp->dst.in); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &tmp->gw.in); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_in_addr(message, RTA_SRC, &tmp->src.in); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &tmp->prefsrc.in); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m"); + return 0; + } + + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(message, RTA_DST, &tmp->dst.in6); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &tmp->gw.in6); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &tmp->src.in6); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &tmp->prefsrc.in6); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m"); + return 0; + } + + break; + + default: + assert_not_reached("Received route message with unsupported address family"); + return 0; + } + + r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_route_get_scope(message, &tmp->scope); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_route_get_tos(message, &tmp->tos); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_route_get_type(message, &tmp->type); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_route_get_table(message, &table); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m"); + return 0; + } + tmp->table = table; + + r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_enter_container(message, RTA_METRICS); + if (r < 0 && r != -ENODATA) { + log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m"); + return 0; + } + if (r >= 0) { + r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_exit_container(message); + if (r < 0) { + log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container: %m"); + return 0; + } + } + + (void) route_get(link, tmp, &route); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL, + *buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL; + char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX], + buf_protocol[ROUTE_PROTOCOL_STR_MAX]; + + if (!in_addr_is_null(tmp->family, &tmp->dst)) { + (void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst); + (void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen); + } + if (!in_addr_is_null(tmp->family, &tmp->src)) + (void) in_addr_to_string(tmp->family, &tmp->src, &buf_src); + if (!in_addr_is_null(tmp->family, &tmp->gw)) + (void) in_addr_to_string(tmp->family, &tmp->gw, &buf_gw); + if (!in_addr_is_null(tmp->family, &tmp->prefsrc)) + (void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc); + + log_link_debug(link, + "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s", + (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" : + type == RTM_DELROUTE ? "Forgetting" : + route ? "Received remembered" : "Remembering", + strna(buf_dst), strempty(buf_dst_prefixlen), + strna(buf_src), strna(buf_gw), strna(buf_prefsrc), + format_route_scope(tmp->scope, buf_scope, sizeof buf_scope), + format_route_table(tmp->table, buf_table, sizeof buf_table), + format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol), + strna(route_type_to_string(tmp->type))); + } + + switch (type) { + case RTM_NEWROUTE: + if (!route && link->manager->manage_foreign_routes) { + /* A route appeared that we did not request */ + r = route_add_foreign(link, tmp, &route); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m"); + return 0; + } + } + + break; + + case RTM_DELROUTE: + route_free(route); + break; + + default: + assert_not_reached("Received route message with invalid RTNL message type"); + } + + return 1; +} + +int link_serialize_routes(Link *link, FILE *f) { + bool space = false; + Route *route; + + assert(link); + assert(link->network); + assert(f); + + fputs("ROUTES=", f); + SET_FOREACH(route, link->routes) { + _cleanup_free_ char *route_str = NULL; + + if (in_addr_to_string(route->family, &route->dst, &route_str) < 0) + continue; + + fprintf(f, "%s%s/%hhu/%hhu/%"PRIu32"/%"PRIu32"/"USEC_FMT, + space ? " " : "", route_str, + route->dst_prefixlen, route->tos, route->priority, route->table, route->lifetime); + space = true; + } + fputc('\n', f); + + return 0; +} + +int link_deserialize_routes(Link *link, const char *routes) { + int r; + + assert(link); + + for (const char *p = routes;; ) { + _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL; + _cleanup_(route_freep) Route *tmp = NULL; + _cleanup_free_ char *route_str = NULL; + char *prefixlen_str; + Route *route; + + r = extract_first_word(&p, &route_str, NULL, 0); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to parse ROUTES=: %m"); + if (r == 0) + return 0; + + prefixlen_str = strchr(route_str, '/'); + if (!prefixlen_str) { + log_link_debug(link, "Failed to parse route, ignoring: %s", route_str); + continue; + } + *prefixlen_str++ = '\0'; + + r = route_new(&tmp); + if (r < 0) + return log_oom(); + + r = sscanf(prefixlen_str, + "%hhu/%hhu/%"SCNu32"/%"PRIu32"/"USEC_FMT, + &tmp->dst_prefixlen, + &tmp->tos, + &tmp->priority, + &tmp->table, + &tmp->lifetime); + if (r != 5) { + log_link_debug(link, + "Failed to parse destination prefix length, tos, priority, table or expiration: %s", + prefixlen_str); + continue; + } + + r = in_addr_from_string_auto(route_str, &tmp->family, &tmp->dst); + if (r < 0) { + log_link_debug_errno(link, r, "Failed to parse route destination %s: %m", route_str); + continue; + } + + r = route_add(link, tmp, &route); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to add route: %m"); + + if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) { + r = sd_event_add_time(link->manager->event, &expire, + clock_boottime_or_monotonic(), + route->lifetime, 0, route_expire_handler, route); + if (r < 0) + log_link_debug_errno(link, r, "Could not arm route expiration handler: %m"); + } + + sd_event_source_unref(route->expire); + route->expire = TAKE_PTR(expire); + } +} + int network_add_ipv4ll_route(Network *network) { _cleanup_(route_free_or_set_invalidp) Route *n = NULL; + unsigned section_line; int r; assert(network); @@ -830,8 +1446,10 @@ int network_add_ipv4ll_route(Network *network) { if (!network->ipv4ll_route) return 0; + section_line = hashmap_find_free_section_line(network->routes_by_section); + /* IPv4LLRoute= is in [Network] section. */ - r = route_new_static(network, NULL, 0, &n); + r = route_new_static(network, network->filename, section_line, &n); if (r < 0) return r; @@ -853,6 +1471,7 @@ int network_add_ipv4ll_route(Network *network) { int network_add_default_route_on_device(Network *network) { _cleanup_(route_free_or_set_invalidp) Route *n = NULL; + unsigned section_line; int r; assert(network); @@ -860,8 +1479,10 @@ int network_add_default_route_on_device(Network *network) { if (!network->default_route_on_device) return 0; + section_line = hashmap_find_free_section_line(network->routes_by_section); + /* DefaultRouteOnDevice= is in [Network] section. */ - r = route_new_static(network, NULL, 0, &n); + r = route_new_static(network, network->filename, section_line, &n); if (r < 0) return r; @@ -874,113 +1495,6 @@ int network_add_default_route_on_device(Network *network) { return 0; } -static const char * const route_type_table[__RTN_MAX] = { - [RTN_UNICAST] = "unicast", - [RTN_LOCAL] = "local", - [RTN_BROADCAST] = "broadcast", - [RTN_ANYCAST] = "anycast", - [RTN_MULTICAST] = "multicast", - [RTN_BLACKHOLE] = "blackhole", - [RTN_UNREACHABLE] = "unreachable", - [RTN_PROHIBIT] = "prohibit", - [RTN_THROW] = "throw", - [RTN_NAT] = "nat", - [RTN_XRESOLVE] = "xresolve", -}; - -assert_cc(__RTN_MAX <= UCHAR_MAX); -DEFINE_STRING_TABLE_LOOKUP(route_type, int); - -static const char * const route_scope_table[] = { - [RT_SCOPE_UNIVERSE] = "global", - [RT_SCOPE_SITE] = "site", - [RT_SCOPE_LINK] = "link", - [RT_SCOPE_HOST] = "host", - [RT_SCOPE_NOWHERE] = "nowhere", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int); - -const char *format_route_scope(int scope, char *buf, size_t size) { - const char *s; - char *p = buf; - - s = route_scope_to_string(scope); - if (s) - strpcpy(&p, size, s); - else - strpcpyf(&p, size, "%d", scope); - - return buf; -} - -static const char * const route_table_table[] = { - [RT_TABLE_DEFAULT] = "default", - [RT_TABLE_MAIN] = "main", - [RT_TABLE_LOCAL] = "local", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int); - -const char *format_route_table(int table, char *buf, size_t size) { - const char *s; - char *p = buf; - - s = route_table_to_string(table); - if (s) - strpcpy(&p, size, s); - else - strpcpyf(&p, size, "%d", table); - - return buf; -} - -static const char * const route_protocol_table[] = { - [RTPROT_KERNEL] = "kernel", - [RTPROT_BOOT] = "boot", - [RTPROT_STATIC] = "static", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_protocol, int); - -static const char * const route_protocol_full_table[] = { - [RTPROT_REDIRECT] = "redirect", - [RTPROT_KERNEL] = "kernel", - [RTPROT_BOOT] = "boot", - [RTPROT_STATIC] = "static", - [RTPROT_GATED] = "gated", - [RTPROT_RA] = "ra", - [RTPROT_MRT] = "mrt", - [RTPROT_ZEBRA] = "zebra", - [RTPROT_BIRD] = "bird", - [RTPROT_DNROUTED] = "dnrouted", - [RTPROT_XORP] = "xorp", - [RTPROT_NTK] = "ntk", - [RTPROT_DHCP] = "dhcp", - [RTPROT_MROUTED] = "mrouted", - [RTPROT_BABEL] = "babel", - [RTPROT_BGP] = "bgp", - [RTPROT_ISIS] = "isis", - [RTPROT_OSPF] = "ospf", - [RTPROT_RIP] = "rip", - [RTPROT_EIGRP] = "eigrp", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(route_protocol_full, int); - -const char *format_route_protocol(int protocol, char *buf, size_t size) { - const char *s; - char *p = buf; - - s = route_protocol_full_to_string(protocol); - if (s) - strpcpy(&p, size, s); - else - strpcpyf(&p, size, "%d", protocol); - - return buf; -} - int config_parse_gateway( const char *unit, const char *filename, @@ -1004,9 +1518,8 @@ int config_parse_gateway( assert(data); if (streq(section, "Network")) { - /* we are not in an Route section, so treat - * this as the special '0' section */ - r = route_new_static(network, NULL, 0, &n); + /* we are not in an Route section, so use line number instead */ + r = route_new_static(network, filename, line, &n); if (r == -ENOMEM) return log_oom(); if (r < 0) { @@ -1658,7 +2171,7 @@ int config_parse_multipath_route( return 0; } -int route_section_verify(Route *route, Network *network) { +static int route_section_verify(Route *route, Network *network) { if (section_is_invalid(route->section)) return -EINVAL; @@ -1687,7 +2200,7 @@ int route_section_verify(Route *route, Network *network) { route->scope = RT_SCOPE_LINK; } - if (network->n_static_addresses == 0 && + if (ordered_hashmap_isempty(network->addresses_by_section) && in_addr_is_null(route->family, &route->gw) == 0 && route->gateway_onlink < 0) { log_warning("%s: Gateway= without static address configured. " @@ -1698,3 +2211,13 @@ int route_section_verify(Route *route, Network *network) { return 0; } + +void network_drop_invalid_routes(Network *network) { + Route *route; + + assert(network); + + HASHMAP_FOREACH(route, network->routes_by_section) + if (route_section_verify(route, network) < 0) + route_free(route); +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 75651fa512..3347c7c57b 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -1,15 +1,20 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include +#include +#include + +#include "sd-netlink.h" + #include "conf-parser.h" -#include "macro.h" - -typedef struct Route Route; -typedef struct NetworkConfigSection NetworkConfigSection; - -#include "networkd-network.h" +#include "in-addr-util.h" +#include "networkd-link.h" #include "networkd-util.h" +typedef struct Manager Manager; +typedef struct Network Network; + typedef struct MultipathRouteVia { uint16_t family; union in_addr_union address; @@ -21,7 +26,7 @@ typedef struct MultipathRoute { uint32_t weight; } MultipathRoute; -struct Route { +typedef struct Route { Network *network; NetworkConfigSection *section; @@ -58,43 +63,33 @@ struct Route { usec_t lifetime; sd_event_source *expire; - - LIST_FIELDS(Route, routes); -}; +} Route; void route_hash_func(const Route *route, struct siphash *state); int route_compare_func(const Route *a, const Route *b); extern const struct hash_ops route_hash_ops; int route_new(Route **ret); -void route_free(Route *route); +Route *route_free(Route *route); +DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free); + int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret); int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback); -int route_get(Link *link, Route *in, Route **ret); -int route_add(Link *link, Route *in, Route **ret); -int route_add_foreign(Link *link, Route *in, Route **ret); -bool route_equal(Route *r1, Route *r2); +int link_set_routes(Link *link); +int link_drop_routes(Link *link); +int link_drop_foreign_routes(Link *link); +int link_serialize_routes(Link *link, FILE *f); +int link_deserialize_routes(Link *link, const char *routes); -int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata); -int route_section_verify(Route *route, Network *network); +uint32_t link_get_dhcp_route_table(Link *link); +uint32_t link_get_ipv6_accept_ra_route_table(Link *link); -DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free); +int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); int network_add_ipv4ll_route(Network *network); int network_add_default_route_on_device(Network *network); - -const char* route_type_to_string(int t) _const_; -int route_type_from_string(const char *s) _pure_; - -#define ROUTE_SCOPE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("nowhere") + 1) -const char *format_route_scope(int scope, char *buf, size_t size); - -#define ROUTE_TABLE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("default") + 1) -const char *format_route_table(int table, char *buf, size_t size); - -#define ROUTE_PROTOCOL_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("redirect") + 1) -const char *format_route_protocol(int protocol, char *buf, size_t size); +void network_drop_invalid_routes(Network *network); CONFIG_PARSER_PROTOTYPE(config_parse_gateway); CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src); diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 3b95ea76b0..de60bd9555 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -9,9 +9,9 @@ #include "fileio.h" #include "format-util.h" #include "ip-protocol-list.h" -#include "networkd-routing-policy-rule.h" #include "netlink-util.h" #include "networkd-manager.h" +#include "networkd-routing-policy-rule.h" #include "networkd-util.h" #include "parse-util.h" #include "socket-util.h" @@ -19,7 +19,32 @@ #include "strv.h" #include "user-util.h" -int routing_policy_rule_new(RoutingPolicyRule **ret) { +RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule) { + if (!rule) + return NULL; + + if (rule->network) { + assert(rule->section); + hashmap_remove(rule->network->rules_by_section, rule->section); + } + + if (rule->manager) { + if (set_get(rule->manager->rules, rule) == rule) + set_remove(rule->manager->rules, rule); + if (set_get(rule->manager->rules_foreign, rule) == rule) + set_remove(rule->manager->rules_foreign, rule); + } + + network_config_section_free(rule->section); + free(rule->iif); + free(rule->oif); + + return mfree(rule); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free); + +static int routing_policy_rule_new(RoutingPolicyRule **ret) { RoutingPolicyRule *rule; rule = new(RoutingPolicyRule, 1); @@ -37,31 +62,43 @@ int routing_policy_rule_new(RoutingPolicyRule **ret) { return 0; } -void routing_policy_rule_free(RoutingPolicyRule *rule) { +static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) { + _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; + _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + int r; - if (!rule) - return; + assert(network); + assert(ret); + assert(filename); + assert(section_line > 0); - if (rule->network) { - LIST_REMOVE(rules, rule->network->rules, rule); - assert(rule->network->n_rules > 0); - rule->network->n_rules--; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; - if (rule->section) - hashmap_remove(rule->network->rules_by_section, rule->section); + rule = hashmap_get(network->rules_by_section, n); + if (rule) { + *ret = TAKE_PTR(rule); + return 0; } - if (rule->manager) { - if (set_get(rule->manager->rules, rule) == rule) - set_remove(rule->manager->rules, rule); - if (set_get(rule->manager->rules_foreign, rule) == rule) - set_remove(rule->manager->rules_foreign, rule); - } + r = routing_policy_rule_new(&rule); + if (r < 0) + return r; - network_config_section_free(rule->section); - free(rule->iif); - free(rule->oif); - free(rule); + rule->network = network; + rule->section = TAKE_PTR(n); + + r = hashmap_ensure_allocated(&network->rules_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = hashmap_put(network->rules_by_section, rule->section, rule); + if (r < 0) + return r; + + *ret = TAKE_PTR(rule); + return 0; } static int routing_policy_rule_copy(RoutingPolicyRule *dest, RoutingPolicyRule *src) { @@ -234,7 +271,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( routing_policy_rule_compare_func, routing_policy_rule_free); -int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) { +static int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) { RoutingPolicyRule *existing; @@ -257,31 +294,15 @@ int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRu return -ENOENT; } -int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) { - int r; - - assert(m); - - if (set_contains(m->rules_foreign, rule)) { - set_remove(m->rules_foreign, rule); - - r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule); - if (r < 0) - return r; - if (r == 0) - routing_policy_rule_free(rule); - } - - return -ENOENT; -} - -static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPolicyRule *in, RoutingPolicyRule **ret) { +static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPolicyRule *in, int family, RoutingPolicyRule **ret) { _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; int r; assert(m); assert(rules); assert(in); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(in->family == AF_UNSPEC || in->family == family); r = routing_policy_rule_new(&rule); if (r < 0) @@ -293,6 +314,8 @@ static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPoli if (r < 0) return r; + rule->family = family; + r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule); if (r < 0) return r; @@ -306,191 +329,20 @@ static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPoli return 0; } -static int routing_policy_rule_add(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) { - return routing_policy_rule_add_internal(m, &m->rules, rule, ret); +static int routing_policy_rule_add(Manager *m, RoutingPolicyRule *rule, int family, RoutingPolicyRule **ret) { + return routing_policy_rule_add_internal(m, &m->rules, rule, family, ret); } -int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) { - return routing_policy_rule_add_internal(m, &m->rules_foreign, rule, ret); +static int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) { + return routing_policy_rule_add_internal(m, &m->rules_foreign, rule, rule->family, ret); } -static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(m); - assert(link); - assert(link->ifname); - - link->routing_policy_rule_remove_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - log_link_message_warning_errno(link, m, r, "Could not drop routing policy rule"); - - return 1; -} - -int routing_policy_rule_remove(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; +static int routing_policy_rule_set_netlink_message(RoutingPolicyRule *rule, sd_netlink_message *m, Link *link) { int r; assert(rule); - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - assert(link->ifindex > 0); - assert(IN_SET(rule->family, AF_INET, AF_INET6)); - - if (DEBUG_LOGGING) { - _cleanup_free_ char *from = NULL, *to = NULL; - - (void) in_addr_to_string(rule->family, &rule->from, &from); - (void) in_addr_to_string(rule->family, &rule->to, &to); - - log_link_debug(link, - "Removing routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, - rule->priority, strna(from), rule->from_prefixlen, strna(to), rule->to_prefixlen, strna(rule->iif), strna(rule->oif), rule->table); - } - - r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_DELRULE, rule->family); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_DELRULE message: %m"); - - if (in_addr_is_null(rule->family, &rule->from) == 0) { - r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from); - if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_SRC attribute: %m"); - - r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen); - if (r < 0) - return log_link_error_errno(link, r, "Could not set source prefix length: %m"); - } - - if (in_addr_is_null(rule->family, &rule->to) == 0) { - r = netlink_message_append_in_addr_union(m, FRA_DST, rule->family, &rule->to); - if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_DST attribute: %m"); - - r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen); - if (r < 0) - return log_link_error_errno(link, r, "Could not set destination prefix length: %m"); - } - - r = netlink_call_async(link->manager->rtnl, NULL, m, - callback ?: routing_policy_rule_remove_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 0; -} - -static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; - _cleanup_(network_config_section_freep) NetworkConfigSection *n = 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; - - rule = hashmap_get(network->rules_by_section, n); - if (rule) { - *ret = TAKE_PTR(rule); - - return 0; - } - } - - r = routing_policy_rule_new(&rule); - if (r < 0) - return r; - - rule->network = network; - LIST_APPEND(rules, network->rules, rule); - network->n_rules++; - - if (filename) { - rule->section = TAKE_PTR(n); - - r = hashmap_ensure_allocated(&network->rules_by_section, &network_config_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(network->rules_by_section, rule->section, rule); - if (r < 0) - return r; - } - - *ret = TAKE_PTR(rule); - - return 0; -} - -static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(rtnl); assert(m); assert(link); - assert(link->ifname); - assert(link->routing_policy_rule_messages > 0); - - link->routing_policy_rule_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_message_warning_errno(link, m, r, "Could not add routing policy rule"); - link_enter_failed(link); - return 1; - } - - if (link->routing_policy_rule_messages == 0) { - log_link_debug(link, "Routing policy rule configured"); - link->routing_policy_rules_configured = true; - link_check_ready(link); - } - - return 1; -} - -int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - assert(rule); - assert(link); - assert(link->ifindex > 0); - assert(link->manager); - assert(link->manager->rtnl); - - if (DEBUG_LOGGING) { - _cleanup_free_ char *from = NULL, *to = NULL; - - (void) in_addr_to_string(rule->family, &rule->from, &from); - (void) in_addr_to_string(rule->family, &rule->to, &to); - - log_link_debug(link, - "Configuring routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, - rule->priority, strna(from), rule->from_prefixlen, strna(to), rule->to_prefixlen, strna(rule->iif), strna(rule->oif), rule->table); - } - - r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_NEWRULE message: %m"); if (in_addr_is_null(rule->family, &rule->from) == 0) { r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from); @@ -592,67 +444,488 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl return log_link_error_errno(link, r, "Could not append FRA_SUPPRESS_PREFIXLEN attribute: %m"); } - rule->link = link; + return 0; +} + +static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->ifname); + + link->routing_policy_rule_remove_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_message_warning_errno(link, m, r, "Could not drop routing policy rule"); + + return 1; +} + +static int routing_policy_rule_remove(RoutingPolicyRule *rule, Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(rule); + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + assert(link->ifindex > 0); + assert(IN_SET(rule->family, AF_INET, AF_INET6)); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *from = NULL, *to = NULL; + + (void) in_addr_to_string(rule->family, &rule->from, &from); + (void) in_addr_to_string(rule->family, &rule->to, &to); + + log_link_debug(link, + "Removing routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, + rule->priority, strna(from), rule->from_prefixlen, strna(to), rule->to_prefixlen, strna(rule->iif), strna(rule->oif), rule->table); + } + + r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_DELRULE, rule->family); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_DELRULE message: %m"); + + r = routing_policy_rule_set_netlink_message(rule, m, link); + if (r < 0) + return r; r = netlink_call_async(link->manager->rtnl, NULL, m, - callback ?: routing_policy_rule_handler, + routing_policy_rule_remove_handler, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); - r = routing_policy_rule_add(link->manager, rule, NULL); + return 0; +} + +static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->ifname); + assert(link->routing_policy_rule_messages > 0); + + link->routing_policy_rule_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_message_warning_errno(link, m, r, "Could not add routing policy rule"); + link_enter_failed(link); + return 1; + } + + if (link->routing_policy_rule_messages == 0) { + log_link_debug(link, "Routing policy rule configured"); + link->routing_policy_rules_configured = true; + link_check_ready(link); + } + + return 1; +} + +static int routing_policy_rule_configure_internal(RoutingPolicyRule *rule, int family, Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(rule); + assert(link); + assert(link->ifindex > 0); + assert(link->manager); + assert(link->manager->rtnl); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *from = NULL, *to = NULL; + + (void) in_addr_to_string(family, &rule->from, &from); + (void) in_addr_to_string(family, &rule->to, &to); + + log_link_debug(link, + "Configuring routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, + rule->priority, strna(from), rule->from_prefixlen, strna(to), rule->to_prefixlen, strna(rule->iif), strna(rule->oif), rule->table); + } + + r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, family); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_NEWRULE message: %m"); + + r = routing_policy_rule_set_netlink_message(rule, m, link); + if (r < 0) + return r; + + r = netlink_call_async(link->manager->rtnl, NULL, m, + routing_policy_rule_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + link->routing_policy_rule_messages++; + + r = routing_policy_rule_add(link->manager, rule, family, NULL); if (r < 0) return log_link_error_errno(link, r, "Could not add rule: %m"); return 1; } -int routing_policy_rule_section_verify(RoutingPolicyRule *rule) { +static int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link) { int r; - if (section_is_invalid(rule->section)) - return -EINVAL; + if (IN_SET(rule->family, AF_INET, AF_INET6)) + return routing_policy_rule_configure_internal(rule, rule->family, link); - if ((rule->family == AF_INET && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) || - (rule->family == AF_INET6 && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4))) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: address family specified by Family= conflicts with the address " - "specified by To= or From=. Ignoring [RoutingPolicyRule] section from line %u.", - rule->section->filename, rule->section->line); - - if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6)) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule6 = NULL; - - assert(rule->family == AF_UNSPEC); - - /* When Family=both, we need to copy the section, AF_INET and AF_INET6. */ - - r = routing_policy_rule_new_static(rule->network, NULL, 0, &rule6); + if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)) { + r = routing_policy_rule_configure_internal(rule, AF_INET, link); if (r < 0) return r; - - r = routing_policy_rule_copy(rule6, rule); - if (r < 0) - return r; - - rule->family = AF_INET; - rule6->family = AF_INET6; - - TAKE_PTR(rule6); } - if (rule->family == AF_UNSPEC) { - if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) - rule->family = AF_INET6; - else - rule->family = AF_INET; + if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) { + r = routing_policy_rule_configure_internal(rule, AF_INET6, link); + if (r < 0) + return r; } return 0; } +static bool manager_links_have_routing_policy_rule(Manager *m, RoutingPolicyRule *rule) { + Link *link; + + assert(m); + assert(rule); + + HASHMAP_FOREACH(link, m->links) { + RoutingPolicyRule *link_rule; + + if (!link->network) + continue; + + HASHMAP_FOREACH(link_rule, link->network->rules_by_section) + if (routing_policy_rule_compare_func(link_rule, rule) == 0) + return true; + } + + return false; +} + +static void routing_policy_rule_purge(Manager *m, Link *link) { + RoutingPolicyRule *rule; + int r; + + assert(m); + assert(link); + + SET_FOREACH(rule, m->rules_saved) { + RoutingPolicyRule *existing; + + existing = set_get(m->rules_foreign, rule); + if (!existing) + continue; /* Saved rule does not exist anymore. */ + + if (manager_links_have_routing_policy_rule(m, existing)) + continue; /* Existing links have the saved rule. */ + + /* Existing links do not have the saved rule. Let's drop the rule now, and re-configure it + * later when it is requested. */ + + r = routing_policy_rule_remove(existing, link); + if (r < 0) { + log_warning_errno(r, "Could not remove routing policy rules: %m"); + continue; + } + + link->routing_policy_rule_remove_messages++; + + assert_se(set_remove(m->rules_foreign, existing) == existing); + routing_policy_rule_free(existing); + } +} + +int link_set_routing_policy_rules(Link *link) { + RoutingPolicyRule *rule; + int r; + + assert(link); + assert(link->network); + + link->routing_policy_rules_configured = false; + + HASHMAP_FOREACH(rule, link->network->rules_by_section) { + RoutingPolicyRule *existing; + + r = routing_policy_rule_get(link->manager, rule, &existing); + if (r > 0) + continue; + if (r == 0) { + r = set_ensure_put(&link->manager->rules, &routing_policy_rule_hash_ops, existing); + if (r < 0) + return log_link_warning_errno(link, r, "Could not store existing routing policy rule: %m"); + + set_remove(link->manager->rules_foreign, existing); + continue; + } + + r = routing_policy_rule_configure(rule, link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set routing policy rule: %m"); + } + + routing_policy_rule_purge(link->manager, link); + if (link->routing_policy_rule_messages == 0) + link->routing_policy_rules_configured = true; + else { + log_link_debug(link, "Setting routing policy rules"); + link_set_state(link, LINK_STATE_CONFIGURING); + } + + return 0; +} + +int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL; + _cleanup_free_ char *from = NULL, *to = NULL; + RoutingPolicyRule *rule = NULL; + const char *iif = NULL, *oif = NULL; + uint32_t suppress_prefixlen; + unsigned flags; + uint16_t type; + int r; + + assert(rtnl); + assert(message); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring"); + + 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_NEWRULE, RTM_DELRULE)) { + log_warning("rtnl: received unexpected message type %u when processing rule, ignoring.", type); + return 0; + } + + r = routing_policy_rule_new(&tmp); + if (r < 0) { + log_oom(); + return 0; + } + + r = sd_rtnl_message_get_family(message, &tmp->family); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get rule family, ignoring: %m"); + return 0; + } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { + log_debug("rtnl: received rule message with invalid family %d, ignoring.", tmp->family); + return 0; + } + + switch (tmp->family) { + case AF_INET: + r = sd_netlink_message_read_in_addr(message, FRA_SRC, &tmp->from.in); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { + r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen); + if (r < 0) { + log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m"); + return 0; + } + } + + r = sd_netlink_message_read_in_addr(message, FRA_DST, &tmp->to.in); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { + r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen); + if (r < 0) { + log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m"); + return 0; + } + } + + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &tmp->from.in6); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { + r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen); + if (r < 0) { + log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m"); + return 0; + } + } + + r = sd_netlink_message_read_in6_addr(message, FRA_DST, &tmp->to.in6); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { + r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen); + if (r < 0) { + log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m"); + return 0; + } + } + + break; + + default: + assert_not_reached("Received rule message with unsupported address family"); + } + + r = sd_rtnl_message_routing_policy_rule_get_flags(message, &flags); + if (r < 0) { + log_warning_errno(r, "rtnl: received rule message without valid flag, ignoring: %m"); + return 0; + } + tmp->invert_rule = flags & FIB_RULE_INVERT; + + r = sd_netlink_message_read_u32(message, FRA_FWMARK, &tmp->fwmark); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, FRA_FWMASK, &tmp->fwmask); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_FWMASK attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, FRA_PRIORITY, &tmp->priority); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_PRIORITY attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, FRA_TABLE, &tmp->table); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_TABLE attribute, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tmp->tos); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get ip rule TOS, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_string(message, FRA_IIFNAME, &iif); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m"); + return 0; + } + r = free_and_strdup(&tmp->iif, iif); + if (r < 0) + return log_oom(); + + r = sd_netlink_message_read_string(message, FRA_OIFNAME, &oif); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m"); + return 0; + } + r = free_and_strdup(&tmp->oif, oif); + if (r < 0) + return log_oom(); + + r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &tmp->protocol); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(tmp->sport), &tmp->sport); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(tmp->dport), &tmp->dport); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read(message, FRA_UID_RANGE, sizeof(tmp->uid_range), &tmp->uid_range); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_UID_RANGE attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, FRA_SUPPRESS_PREFIXLEN, &suppress_prefixlen); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_SUPPRESS_PREFIXLEN attribute, ignoring: %m"); + return 0; + } + if (r >= 0) + tmp->suppress_prefixlen = (int) suppress_prefixlen; + + (void) routing_policy_rule_get(m, tmp, &rule); + + if (DEBUG_LOGGING) { + (void) in_addr_to_string(tmp->family, &tmp->from, &from); + (void) in_addr_to_string(tmp->family, &tmp->to, &to); + } + + switch (type) { + case RTM_NEWRULE: + if (rule) + log_debug("Received remembered routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, + tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table); + else { + log_debug("Remembering foreign routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, + tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table); + r = routing_policy_rule_add_foreign(m, tmp, &rule); + if (r < 0) { + log_warning_errno(r, "Could not remember foreign rule, ignoring: %m"); + return 0; + } + } + break; + case RTM_DELRULE: + if (rule) { + log_debug("Forgetting routing policy rule: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32, + tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table); + routing_policy_rule_free(rule); + } else + log_debug("Kernel removed a routing policy rule we don't remember: priority: %"PRIu32", %s/%u -> %s/%u, iif: %s, oif: %s, table: %"PRIu32", ignoring.", + tmp->priority, strna(from), tmp->from_prefixlen, strna(to), tmp->to_prefixlen, strna(tmp->iif), strna(tmp->oif), tmp->table); + break; + + default: + assert_not_reached("Received invalid RTNL message type"); + } + + return 1; +} + static int parse_fwmark_fwmask(const char *s, uint32_t *ret_fwmark, uint32_t *ret_fwmask) { _cleanup_free_ char *fwmark_str = NULL; uint32_t fwmark, fwmask = 0; @@ -1183,14 +1456,40 @@ int config_parse_routing_policy_rule_suppress_prefixlen( return 0; } +static int routing_policy_rule_section_verify(RoutingPolicyRule *rule) { + if (section_is_invalid(rule->section)) + return -EINVAL; + + if ((rule->family == AF_INET && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) || + (rule->family == AF_INET6 && FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4))) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: address family specified by Family= conflicts with the address " + "specified by To= or From=. Ignoring [RoutingPolicyRule] section from line %u.", + rule->section->filename, rule->section->line); + + if (rule->family == AF_UNSPEC && rule->address_family == ADDRESS_FAMILY_NO) + rule->family = AF_INET; + + return 0; +} + +void network_drop_invalid_routing_policy_rules(Network *network) { + RoutingPolicyRule *rule; + + assert(network); + + HASHMAP_FOREACH(rule, network->rules_by_section) + if (routing_policy_rule_section_verify(rule) < 0) + routing_policy_rule_free(rule); +} + int routing_policy_serialize_rules(Set *rules, FILE *f) { - RoutingPolicyRule *rule = NULL; + RoutingPolicyRule *rule; int r; assert(f); SET_FOREACH(rule, rules) { - _cleanup_free_ char *from_str = NULL, *to_str = NULL; const char *family_str; bool space = false; @@ -1204,24 +1503,28 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) { } if (!in_addr_is_null(rule->family, &rule->from)) { - r = in_addr_to_string(rule->family, &rule->from, &from_str); + _cleanup_free_ char *str = NULL; + + r = in_addr_to_string(rule->family, &rule->from, &str); if (r < 0) return r; fprintf(f, "%sfrom=%s/%hhu", space ? " " : "", - from_str, rule->from_prefixlen); + str, rule->from_prefixlen); space = true; } if (!in_addr_is_null(rule->family, &rule->to)) { - r = in_addr_to_string(rule->family, &rule->to, &to_str); + _cleanup_free_ char *str = NULL; + + r = in_addr_to_string(rule->family, &rule->to, &str); if (r < 0) return r; fprintf(f, "%sto=%s/%hhu", space ? " " : "", - to_str, rule->to_prefixlen); + str, rule->to_prefixlen); space = true; } @@ -1307,31 +1610,31 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) { return 0; } -static int routing_policy_rule_read_full_file(const char *state_file, char **ret) { +static int routing_policy_rule_read_full_file(const char *state_file, char ***ret) { + _cleanup_strv_free_ char **lines = NULL; _cleanup_free_ char *s = NULL; - size_t size; int r; assert(state_file); - r = read_full_file(state_file, &s, &size); - if (r == -ENOENT) - return -ENODATA; + r = read_full_file(state_file, &s, NULL); + if (r == -ENOENT) { + *ret = NULL; + return 0; + } if (r < 0) return r; - if (size <= 0) - return -ENODATA; - *ret = TAKE_PTR(s); + lines = strv_split_newlines(s); + if (!lines) + return -ENOMEM; - return size; + *ret = TAKE_PTR(lines); + return 0; } int routing_policy_load_rules(const char *state_file, Set **rules) { - _cleanup_strv_free_ char **l = NULL; - _cleanup_free_ char *data = NULL; - uint16_t low = 0, high = 0; - const char *p; + _cleanup_strv_free_ char **data = NULL; char **i; int r; @@ -1339,15 +1642,12 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { assert(rules); r = routing_policy_rule_read_full_file(state_file, &data); - if (r <= 0) - return r; + if (r < 0) + return log_warning_errno(r, "Failed to read %s, ignoring: %m", state_file); - l = strv_split_newlines(data); - if (!l) - return -ENOMEM; - - STRV_FOREACH(i, l) { + STRV_FOREACH(i, data) { _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; + const char *p; p = startswith(*i, "RULE="); if (!p) @@ -1355,7 +1655,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { r = routing_policy_rule_new(&rule); if (r < 0) - return r; + return log_oom(); for (;;) { _cleanup_free_ char *a = NULL; @@ -1363,7 +1663,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { r = extract_first_word(&p, &a, NULL, 0); if (r < 0) - return r; + return log_oom(); if (r == 0) break; @@ -1385,7 +1685,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { continue; } rule->family = r; - } if (STR_IN_SET(a, "from", "to")) { + } else if (STR_IN_SET(a, "from", "to")) { union in_addr_union *buffer; uint8_t *prefixlen; @@ -1444,6 +1744,8 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { continue; } } else if (streq(a, "sourceport")) { + uint16_t low, high; + r = parse_ip_port_range(b, &low, &high); if (r < 0) { log_warning_errno(r, "Invalid routing policy rule source port range, ignoring assignment: '%s'", b); @@ -1453,6 +1755,8 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { rule->sport.start = low; rule->sport.end = high; } else if (streq(a, "destinationport")) { + uint16_t low, high; + r = parse_ip_port_range(b, &low, &high); if (r < 0) { log_warning_errno(r, "Invalid routing policy rule destination port range, ignoring assignment: '%s'", b); @@ -1495,7 +1799,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule); if (r < 0) { - log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", p); + log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", *i); continue; } if (r > 0) @@ -1504,53 +1808,3 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { return 0; } - -static bool manager_links_have_routing_policy_rule(Manager *m, RoutingPolicyRule *rule) { - RoutingPolicyRule *link_rule; - Link *link; - - assert(m); - assert(rule); - - HASHMAP_FOREACH(link, m->links) { - if (!link->network) - continue; - - LIST_FOREACH(rules, link_rule, link->network->rules) - if (routing_policy_rule_compare_func(link_rule, rule) == 0) - return true; - } - - return false; -} - -void routing_policy_rule_purge(Manager *m, Link *link) { - RoutingPolicyRule *rule, *existing; - int r; - - assert(m); - assert(link); - - SET_FOREACH(rule, m->rules_saved) { - existing = set_get(m->rules_foreign, rule); - if (!existing) - continue; /* Saved rule does not exist anymore. */ - - if (manager_links_have_routing_policy_rule(m, existing)) - continue; /* Existing links have the saved rule. */ - - /* Existing links do not have the saved rule. Let's drop the rule now, and re-configure it - * later when it is requested. */ - - r = routing_policy_rule_remove(existing, link, NULL); - if (r < 0) { - log_warning_errno(r, "Could not remove routing policy rules: %m"); - continue; - } - - link->routing_policy_rule_remove_messages++; - - assert_se(set_remove(m->rules_foreign, existing) == existing); - routing_policy_rule_free(existing); - } -} diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index af954e8fb5..bc1193a111 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -2,28 +2,22 @@ #pragma once #include -#include #include #include +#include -#include "in-addr-util.h" #include "conf-parser.h" - -typedef struct RoutingPolicyRule RoutingPolicyRule; - -#include "networkd-link.h" -#include "networkd-network.h" +#include "in-addr-util.h" #include "networkd-util.h" +#include "set.h" typedef struct Network Network; typedef struct Link Link; -typedef struct NetworkConfigSection NetworkConfigSection; typedef struct Manager Manager; -struct RoutingPolicyRule { +typedef struct RoutingPolicyRule { Manager *manager; Network *network; - Link *link; NetworkConfigSection *section; bool invert_rule; @@ -52,25 +46,18 @@ struct RoutingPolicyRule { struct fib_rule_uid_range uid_range; int suppress_prefixlen; +} RoutingPolicyRule; - LIST_FIELDS(RoutingPolicyRule, rules); -}; +RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule); -int routing_policy_rule_new(RoutingPolicyRule **ret); -void routing_policy_rule_free(RoutingPolicyRule *rule); +void network_drop_invalid_routing_policy_rules(Network *network); -DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free); -int routing_policy_rule_section_verify(RoutingPolicyRule *rule); +int link_set_routing_policy_rules(Link *link); -int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback); -int routing_policy_rule_remove(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback); +int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); -int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret); -int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret); -int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule); int routing_policy_serialize_rules(Set *rules, FILE *f); int routing_policy_load_rules(const char *state_file, Set **rules); -void routing_policy_rule_purge(Manager *m, Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_tos); CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_table); diff --git a/src/network/networkd-sriov.c b/src/network/networkd-sriov.c index 7d99707343..61382a8993 100644 --- a/src/network/networkd-sriov.c +++ b/src/network/networkd-sriov.c @@ -108,7 +108,7 @@ static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return 1; } -int sr_iov_configure(Link *link, SRIOV *sr_iov) { +static int sr_iov_configure(Link *link, SRIOV *sr_iov) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -226,7 +226,28 @@ int sr_iov_configure(Link *link, SRIOV *sr_iov) { return 0; } -int sr_iov_section_verify(SRIOV *sr_iov) { +int link_configure_sr_iov(Link *link) { + SRIOV *sr_iov; + int r; + + link->sr_iov_configured = false; + link->sr_iov_messages = 0; + + ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) { + r = sr_iov_configure(link, sr_iov); + if (r < 0) + return r; + } + + if (link->sr_iov_messages == 0) + link->sr_iov_configured = true; + else + log_link_debug(link, "Configuring SR-IOV"); + + return 0; +} + +static int sr_iov_section_verify(SRIOV *sr_iov) { assert(sr_iov); if (section_is_invalid(sr_iov->section)) @@ -241,6 +262,16 @@ int sr_iov_section_verify(SRIOV *sr_iov) { return 0; } +void network_drop_invalid_sr_iov(Network *network) { + SRIOV *sr_iov; + + assert(network); + + ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section) + if (sr_iov_section_verify(sr_iov) < 0) + sr_iov_free(sr_iov); +} + int config_parse_sr_iov_uint32( const char *unit, const char *filename, diff --git a/src/network/networkd-sriov.h b/src/network/networkd-sriov.h index a545d1292a..8a48545d64 100644 --- a/src/network/networkd-sriov.h +++ b/src/network/networkd-sriov.h @@ -5,6 +5,7 @@ #include #include "conf-parser.h" +#include "ether-addr-util.h" #include "networkd-link.h" #include "networkd-network.h" #include "networkd-util.h" @@ -33,9 +34,8 @@ typedef struct SRIOV { } SRIOV; SRIOV *sr_iov_free(SRIOV *sr_iov); - -int sr_iov_configure(Link *link, SRIOV *sr_iov); -int sr_iov_section_verify(SRIOV *sr_iov); +int link_configure_sr_iov(Link *link); +void network_drop_invalid_sr_iov(Network *network); DEFINE_NETWORK_SECTION_FUNCTIONS(SRIOV, sr_iov_free); diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c new file mode 100644 index 0000000000..c8facbf831 --- /dev/null +++ b/src/network/networkd-sysctl.c @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include + +#include "missing_network.h" +#include "networkd-link.h" +#include "networkd-network.h" +#include "networkd-sysctl.h" +#include "socket-util.h" +#include "string-table.h" +#include "sysctl-util.h" + +static int link_update_ipv6_sysctl(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link_ipv6_enabled(link)) + return 0; + + return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false); +} + +static int link_set_proxy_arp(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + if (link->network->proxy_arp < 0) + return 0; + + return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0); +} + +bool link_ip_forward_enabled(Link *link, int family) { + assert(link); + assert(IN_SET(family, AF_INET, AF_INET6)); + + if (family == AF_INET6 && !socket_ipv6_is_supported()) + return false; + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6); +} + +static int link_set_ipv4_forward(Link *link) { + assert(link); + + if (!link_ip_forward_enabled(link, AF_INET)) + return 0; + + /* We propagate the forwarding flag from one interface to the + * global setting one way. This means: as long as at least one + * interface was configured at any time that had IP forwarding + * enabled the setting will stay on for good. We do this + * primarily to keep IPv4 and IPv6 packet forwarding behaviour + * somewhat in sync (see below). */ + + return sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1"); +} + +static int link_set_ipv6_forward(Link *link) { + assert(link); + + if (!link_ip_forward_enabled(link, AF_INET6)) + return 0; + + /* On Linux, the IPv6 stack does not know a per-interface + * packet forwarding setting: either packet forwarding is on + * for all, or off for all. We hence don't bother with a + * per-interface setting, but simply propagate the interface + * flag, if it is set, to the global flag, one-way. Note that + * while IPv4 would allow a per-interface flag, we expose the + * same behaviour there and also propagate the setting from + * one to all, to keep things simple (see above). */ + + return sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1"); +} + +static int link_set_ipv6_privacy_extensions(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return 0; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) link->network->ipv6_privacy_extensions); +} + +static int link_set_ipv6_accept_ra(Link *link) { + assert(link); + + /* Make this a NOP if IPv6 is not available */ + if (!socket_ipv6_is_supported()) + return 0; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0"); +} + +static int link_set_ipv6_dad_transmits(Link *link) { + assert(link); + + /* Make this a NOP if IPv6 is not available */ + if (!socket_ipv6_is_supported()) + return 0; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + if (link->network->ipv6_dad_transmits < 0) + return 0; + + return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits); +} + +static int link_set_ipv6_hop_limit(Link *link) { + assert(link); + + /* Make this a NOP if IPv6 is not available */ + if (!socket_ipv6_is_supported()) + return 0; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + if (link->network->ipv6_hop_limit < 0) + return 0; + + return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit); +} + +static int link_set_ipv4_accept_local(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (link->network->ipv4_accept_local < 0) + return 0; + + return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0); +} + +int link_set_sysctl(Link *link) { + int r; + + assert(link); + + /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled + * for this interface, then enable IPv6 */ + r = link_update_ipv6_sysctl(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot enable IPv6, ignoring: %m"); + + r = link_set_proxy_arp(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface, ignoring: %m"); + + r = link_set_ipv4_forward(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m"); + + r = link_set_ipv6_forward(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");; + + r = link_set_ipv6_privacy_extensions(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface, ignorign: %m"); + + r = link_set_ipv6_accept_ra(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m"); + + r = link_set_ipv6_dad_transmits(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface, ignoring: %m"); + + r = link_set_ipv6_hop_limit(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface, ignoring: %m"); + + r = link_set_ipv4_accept_local(link); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface, ignoring: %m"); + + return 0; +} + +int link_set_ipv6_mtu(Link *link) { + int r; + + assert(link); + + /* Make this a NOP if IPv6 is not available */ + if (!socket_ipv6_is_supported()) + return 0; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (link->network->ipv6_mtu == 0) + return 0; + + r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu); + if (r < 0) + return r; + + link->ipv6_mtu_set = true; + + return 0; +} + +static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = { + [IPV6_PRIVACY_EXTENSIONS_NO] = "no", + [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public", + [IPV6_PRIVACY_EXTENSIONS_YES] = "yes", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions, + IPV6_PRIVACY_EXTENSIONS_YES); + +int config_parse_ipv6_privacy_extensions( + 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) { + + IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(ipv6_privacy_extensions); + + s = ipv6_privacy_extensions_from_string(rvalue); + if (s < 0) { + if (streq(rvalue, "kernel")) + s = _IPV6_PRIVACY_EXTENSIONS_INVALID; + else { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); + return 0; + } + } + + *ipv6_privacy_extensions = s; + + return 0; +} diff --git a/src/network/networkd-sysctl.h b/src/network/networkd-sysctl.h new file mode 100644 index 0000000000..a409d8f54f --- /dev/null +++ b/src/network/networkd-sysctl.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "conf-parser.h" + +typedef struct Link Link; + +typedef enum IPv6PrivacyExtensions { + /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */ + IPV6_PRIVACY_EXTENSIONS_NO, + IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC, + IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */ + _IPV6_PRIVACY_EXTENSIONS_MAX, + _IPV6_PRIVACY_EXTENSIONS_INVALID = -1, +} IPv6PrivacyExtensions; + +bool link_ip_forward_enabled(Link *link, int family); +int link_set_sysctl(Link *link); +int link_set_ipv6_mtu(Link *link); + +const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_; +IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; + +CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index ce9319d942..bae4ee5cba 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -151,3 +151,15 @@ int network_config_section_new(const char *filename, unsigned line, NetworkConfi void network_config_section_free(NetworkConfigSection *cs) { free(cs); } + +unsigned hashmap_find_free_section_line(Hashmap *hashmap) { + NetworkConfigSection *cs; + unsigned n = 0; + void *entry; + + HASHMAP_FOREACH_KEY(entry, cs, hashmap) + if (n < cs->line) + n = cs->line; + + return n + 1; +} diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index 0433f883a3..ce169fa731 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -2,10 +2,13 @@ #pragma once #include "sd-dhcp-lease.h" +#include "sd-netlink.h" #include "conf-parser.h" -#include "hash-funcs.h" +#include "hashmap.h" +#include "log.h" #include "macro.h" +#include "string-util.h" typedef enum AddressFamily { /* This is a bitmask, though it usually doesn't feel that way! */ @@ -49,6 +52,7 @@ int network_config_section_new(const char *filename, unsigned line, NetworkConfi void network_config_section_free(NetworkConfigSection *network); DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free); extern const struct hash_ops network_config_hash_ops; +unsigned hashmap_find_free_section_line(Hashmap *hashmap); static inline bool section_is_invalid(NetworkConfigSection *section) { /* If this returns false, then it does _not_ mean the section is valid. */ @@ -70,3 +74,10 @@ static inline bool section_is_invalid(NetworkConfigSection *section) { } \ DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \ DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid); + +static inline int log_message_warning_errno(sd_netlink_message *m, int err, const char *msg) { + const char *err_msg = NULL; + + (void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg); + return log_warning_errno(err, "%s: %s%s%m", msg, strempty(err_msg), err_msg ? " " : ""); +} diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c index 14a8687458..53b65286b3 100644 --- a/src/network/networkd-wifi.c +++ b/src/network/networkd-wifi.c @@ -6,6 +6,7 @@ #include "sd-bus.h" #include "bus-util.h" +#include "ether-addr-util.h" #include "netlink-internal.h" #include "netlink-util.h" #include "networkd-link.h" diff --git a/src/network/networkd.c b/src/network/networkd.c index 445aee16ad..f78ff9db54 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -88,29 +88,9 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Could not load configuration files: %m"); - r = manager_rtnl_enumerate_links(m); + r = manager_enumerate(m); if (r < 0) - return log_error_errno(r, "Could not enumerate links: %m"); - - r = manager_rtnl_enumerate_addresses(m); - 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"); - - r = manager_rtnl_enumerate_rules(m); - if (r < 0) - return log_error_errno(r, "Could not enumerate rules: %m"); - - r = manager_rtnl_enumerate_nexthop(m); - if (r < 0) - return log_error_errno(r, "Could not enumerate nexthop: %m"); + return r; r = manager_start(m); if (r < 0) diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c index 30a00133d6..974bb8c822 100644 --- a/src/network/tc/tc.c +++ b/src/network/tc/tc.c @@ -21,7 +21,7 @@ void traffic_control_free(TrafficControl *tc) { } } -int traffic_control_configure(Link *link, TrafficControl *tc) { +static int traffic_control_configure(Link *link, TrafficControl *tc) { assert(link); assert(tc); @@ -35,7 +35,28 @@ int traffic_control_configure(Link *link, TrafficControl *tc) { } } -int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) { +int link_configure_traffic_control(Link *link) { + TrafficControl *tc; + int r; + + link->tc_configured = false; + link->tc_messages = 0; + + ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section) { + r = traffic_control_configure(link, tc); + if (r < 0) + return r; + } + + if (link->tc_messages == 0) + link->tc_configured = true; + else + log_link_debug(link, "Configuring traffic control"); + + return 0; +} + +static int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) { assert(tc); switch(tc->kind) { @@ -47,3 +68,14 @@ int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, boo assert_not_reached("Invalid traffic control type"); } } + +void network_drop_invalid_traffic_control(Network *network) { + bool has_root = false, has_clsact = false; + TrafficControl *tc; + + assert(network); + + ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section) + if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0) + traffic_control_free(tc); +} diff --git a/src/network/tc/tc.h b/src/network/tc/tc.h index defa0b774a..916ad3300a 100644 --- a/src/network/tc/tc.h +++ b/src/network/tc/tc.h @@ -28,5 +28,5 @@ typedef struct TrafficControl { #define TC(tc) (&(tc)->meta) void traffic_control_free(TrafficControl *tc); -int traffic_control_configure(Link *link, TrafficControl *tc); -int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact); +int link_configure_traffic_control(Link *link); +void network_drop_invalid_traffic_control(Network *network); diff --git a/src/network/test-network.c b/src/network/test-network.c index 2ac47e72f5..e93e8c0cc0 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -7,6 +7,7 @@ #include "alloc-util.h" #include "dhcp-lease-internal.h" +#include "ether-addr-util.h" #include "hostname-util.h" #include "network-internal.h" #include "networkd-manager.h" @@ -252,6 +253,6 @@ int main(void) { test_network_get(manager, loopback); - assert_se(manager_rtnl_enumerate_links(manager) >= 0); + assert_se(manager_enumerate(manager) >= 0); return 0; } diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index 5d338e6f1a..030e50688a 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -176,14 +176,16 @@ static void test_config_parse_address_one(const char *rvalue, int family, unsign assert_se(network->filename = strdup("hogehoge.network")); assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match_name, network) == 0); assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0); - assert_se(network->n_static_addresses == 1); + assert_se(ordered_hashmap_size(network->addresses_by_section) == 1); assert_se(network_verify(network) >= 0); - assert_se(network->n_static_addresses == n_addresses); + assert_se(ordered_hashmap_size(network->addresses_by_section) == n_addresses); if (n_addresses > 0) { - assert_se(network->static_addresses); - assert_se(network->static_addresses->prefixlen == prefixlen); - assert_se(network->static_addresses->family == family); - assert_se(in_addr_equal(family, &network->static_addresses->in_addr, u)); + Address *a; + + assert_se(a = ordered_hashmap_first(network->addresses_by_section)); + assert_se(a->prefixlen == prefixlen); + assert_se(a->family == family); + assert_se(in_addr_equal(family, &a->in_addr, u)); /* TODO: check Address.in_addr and Address.broadcast */ } } diff --git a/src/network/test-routing-policy-rule.c b/src/network/test-routing-policy-rule.c index 78755927c7..40341d6073 100644 --- a/src/network/test-routing-policy-rule.c +++ b/src/network/test-routing-policy-rule.c @@ -1,13 +1,8 @@ -/*** - SPDX-License-Identifier: LGPL-2.1+ -***/ +/* SPDX-License-Identifier: LGPL-2.1+ */ #include "fd-util.h" #include "fileio.h" -#include "log.h" -#include "macro.h" -#include "network-internal.h" -#include "networkd-manager.h" +#include "networkd-routing-policy-rule.h" #include "string-util.h" #include "tests.h" #include "tmpfile-util.h" diff --git a/test/test-network/conf/25-address-static.network b/test/test-network/conf/25-address-static.network index f8119d13ee..506cdd2264 100644 --- a/test/test-network/conf/25-address-static.network +++ b/test/test-network/conf/25-address-static.network @@ -155,7 +155,6 @@ Address=10.3.3.98/16 Address=10.3.3.99/16 Address=10.3.3.100/16 Address=10.3.3.101/16 -Address=10.3.3.101/16 Address=10.3.3.102/16 Address=10.3.3.103/16 Address=10.3.3.104/16 diff --git a/test/test-network/conf/25-sriov.network b/test/test-network/conf/25-sriov.network index c962c3d845..099331db50 100644 --- a/test/test-network/conf/25-sriov.network +++ b/test/test-network/conf/25-sriov.network @@ -3,6 +3,7 @@ Name=eni99np1 [Network] Address=192.168.100.100/24 +IPv6AcceptRA=no [SR-IOV] VirtualFunction=0 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 3e57b335ce..8058122e46 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -382,6 +382,9 @@ def remove_routing_policy_rule_tables(tables): rc = 0 while rc == 0: rc = call('ip rule del table', table, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + rc = 0 + while rc == 0: + rc = call('ip -6 rule del table', table, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) def remove_routes(routes): for route_type, addr in routes: @@ -745,6 +748,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): links = [ '6rdtun99', 'bareudp99', + 'bond98', 'bond99', 'bridge99', 'dropin-test', @@ -870,6 +874,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-sit-tunnel.netdev', '25-tap.netdev', '25-tun.netdev', + '25-tunnel-any-any.network', '25-tunnel-local-any.network', '25-tunnel-remote-any.network', '25-tunnel.network',