X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fnetwork%2Fnetworkd-link.c;h=660efedc61b0d0dd515b1ab1cae219e735cde7d8;hp=9296a594dbaa60e6cadbf64ee390c7129011b0b1;hb=fecc80c1ba2eed9dadb9a10c15508c356bcc5fc1;hpb=1873a3d344042e818d2a5762c0ebfc3823c8f84d diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 9296a594d..660efedc6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -21,6 +21,7 @@ #include #include +#include #include "networkd.h" #include "libudev-private.h" @@ -29,6 +30,7 @@ #include "virt.h" #include "bus-util.h" #include "network-internal.h" +#include "conf-parser.h" #include "network-util.h" #include "dhcp-lease-internal.h" @@ -77,7 +79,7 @@ static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) { r = sd_rtnl_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac); if (r < 0) - return r; + log_debug_link(link, "MAC address not found for new device, continuing without"); r = asprintf(&link->state_file, "/run/systemd/netif/links/%"PRIu64, link->ifindex); @@ -105,8 +107,6 @@ static void link_free(Link *link) { if (!link) return; - assert(link->manager); - while ((address = link->addresses)) { LIST_REMOVE(addresses, link->addresses, address); address_free(address); @@ -127,7 +127,8 @@ static void link_free(Link *link) { sd_dhcp6_client_unref(link->dhcp6_client); sd_icmp6_nd_unref(link->icmp6_router_discovery); - hashmap_remove(link->manager->links, &link->ifindex); + if (link->manager) + hashmap_remove(link->manager->links, &link->ifindex); free(link->ifname); @@ -205,7 +206,7 @@ static int link_stop_clients(Link *link) { if (!link->network) return 0; - if (link->network->dhcp) { + if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V6)) { assert(link->dhcp_client); k = sd_dhcp_client_stop(link->dhcp_client); @@ -235,7 +236,7 @@ static int link_stop_clients(Link *link) { } } - if (link->network->dhcp6) { + if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V6)) { assert(link->icmp6_router_discovery); if (link->dhcp6_client) { @@ -307,7 +308,8 @@ static int link_enter_configured(Link *link) { assert(link->network); assert(link->state == LINK_STATE_SETTING_ROUTES); - if (link->network->dhcp_server) { + if (link->network->dhcp_server && + !sd_dhcp_server_is_running(link->dhcp_server)) { struct in_addr pool_start; Address *address; @@ -364,7 +366,7 @@ static int link_enter_configured(Link *link) { } static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(link->route_messages > 0); @@ -374,10 +376,8 @@ static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { link->route_messages --; - if (IN_SET(LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) @@ -395,11 +395,54 @@ static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { link_enter_configured(link); } - link_unref(link); - return 1; } +static int link_set_dhcp_routes(Link *link) { + struct sd_dhcp_route *static_routes; + size_t static_routes_size; + int r; + unsigned i; + + assert(link); + + r = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes, &static_routes_size); + if (r < 0) { + if (r != -ENOENT) + log_warning_link(link, "DHCP error: could not get routes: %s", strerror(-r)); + return r; + } + + for (i = 0; i < static_routes_size; i++) { + _cleanup_route_free_ Route *route = NULL; + + r = route_new_dynamic(&route); + if (r < 0) { + log_error_link(link, "Could not allocate route: %s", + strerror(-r)); + return r; + } + + route->family = AF_INET; + route->in_addr.in = static_routes[i].gw_addr; + route->dst_addr.in = static_routes[i].dst_addr; + route->dst_prefixlen = static_routes[i].dst_prefixlen; + route->metrics = DHCP_STATIC_ROUTE_METRIC; + + r = route_configure(route, link, &route_handler); + if (r < 0) { + log_warning_link(link, + "could not set host route: %s", strerror(-r)); + return r; + } + + link_ref(link); + link->route_messages ++; + } + + return 0; +} + static int link_enter_set_routes(Link *link) { Route *rt; int r; @@ -472,7 +515,8 @@ static int link_enter_set_routes(Link *link) { r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); if (r < 0 && r != -ENOENT) { - log_warning_link(link, "DHCP error: %s", strerror(-r)); + log_warning_link(link, "DHCP error: could not get gateway: %s", + strerror(-r)); return r; } @@ -498,6 +542,7 @@ static int link_enter_set_routes(Link *link) { route_gw->dst_addr.in = gateway; route_gw->dst_prefixlen = 32; route_gw->scope = RT_SCOPE_LINK; + route_gw->metrics = DHCP_STATIC_ROUTE_METRIC; r = route_configure(route_gw, link, &route_handler); if (r < 0) { @@ -511,6 +556,7 @@ static int link_enter_set_routes(Link *link) { route->family = AF_INET; route->in_addr.in = gateway; + route->metrics = DHCP_STATIC_ROUTE_METRIC; r = route_configure(route, link, &route_handler); if (r < 0) { @@ -523,6 +569,9 @@ static int link_enter_set_routes(Link *link) { link_ref(link); link->route_messages ++; } + + if (link->network->dhcp_routes) + link_set_dhcp_routes(link); } if (link->route_messages == 0) { @@ -533,17 +582,15 @@ static int link_enter_set_routes(Link *link) { } static int route_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(m); assert(link); assert(link->ifname); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -ESRCH) @@ -554,13 +601,11 @@ static int route_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) "ERRNO=%d", -r, NULL); - link_unref(link); - return 0; } static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(m); @@ -572,10 +617,8 @@ static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { link->addr_messages --; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) @@ -591,14 +634,13 @@ static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { link_enter_set_routes(link); } - link_unref(link); - return 1; } static int link_enter_set_addresses(Link *link) { Address *ad; int r; + uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; assert(link); assert(link->network); @@ -675,6 +717,16 @@ static int link_enter_set_addresses(Link *link) { return r; } + if (!link->network->dhcp_critical) { + r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, + &lifetime); + if (r < 0) { + log_warning_link(link, "DHCP error: no lifetime: %s", + strerror(-r)); + return r; + } + } + r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask); if (r < 0) { log_warning_link(link, "DHCP error: no netmask: %s", @@ -693,10 +745,14 @@ static int link_enter_set_addresses(Link *link) { address->family = AF_INET; address->in_addr.in = addr; + address->cinfo.ifa_prefered = lifetime; + address->cinfo.ifa_valid = lifetime; address->prefixlen = prefixlen; address->broadcast.s_addr = addr.s_addr | ~netmask.s_addr; - r = address_configure(address, link, &address_handler); + /* use update rather than configure so that we will update the lifetime + of an existing address if it has already been configured */ + r = address_update(address, link, &address_handler); if (r < 0) { log_warning_link(link, "could not set addresses: %s", strerror(-r)); @@ -712,17 +768,15 @@ static int link_enter_set_addresses(Link *link) { } static int address_update_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(m); assert(link); assert(link->ifname); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -ENOENT) @@ -733,23 +787,19 @@ static int address_update_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userd "ERRNO=%d", -r, NULL); - link_unref(link); - return 0; } static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(m); assert(link); assert(link->ifname); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EADDRNOTAVAIL) @@ -760,28 +810,22 @@ static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdat "ERRNO=%d", -r, NULL); - link_unref(link); - return 0; } static int set_hostname_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(link); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_bus_message_get_errno(m); if (r < 0) log_warning_link(link, "Could not set hostname: %s", strerror(-r)); - link_unref(link); - return 1; } @@ -815,26 +859,26 @@ static int link_set_hostname(Link *link, const char *hostname) { return r; r = sd_bus_call_async(link->manager->bus, NULL, m, set_hostname_handler, link, 0); - if (r < 0) + if (r < 0) { log_error_link(link, "Could not set transient hostname: %s", strerror(-r)); + return r; + } link_ref(link); - return r; + return 0; } static int set_mtu_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(m); assert(link); assert(link->ifname); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_rtnl_message_get_errno(m); if (r < 0) @@ -844,8 +888,6 @@ static int set_mtu_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { "ERRNO=%d", -r, NULL); - link_unref(link); - return 1; } @@ -886,12 +928,11 @@ static int link_set_mtu(Link *link, uint32_t mtu) { static int dhcp_lease_lost(Link *link) { _cleanup_address_free_ Address *address = NULL; - _cleanup_route_free_ Route *route_gw = NULL; - _cleanup_route_free_ Route *route = NULL; struct in_addr addr; struct in_addr netmask; struct in_addr gateway; unsigned prefixlen; + unsigned i; int r; assert(link); @@ -899,10 +940,36 @@ static int dhcp_lease_lost(Link *link) { log_warning_link(link, "DHCP lease lost"); + if (link->network->dhcp_routes) { + struct sd_dhcp_route *routes; + size_t routes_size; + + r = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes, &routes_size); + if (r >= 0) { + for (i = 0; i < routes_size; i++) { + _cleanup_route_free_ Route *route = NULL; + + r = route_new_dynamic(&route); + if (r >= 0) { + route->family = AF_INET; + route->in_addr.in = routes[i].gw_addr; + route->dst_addr.in = routes[i].dst_addr; + route->dst_prefixlen = routes[i].dst_prefixlen; + + route_drop(route, link, &route_drop_handler); + link_ref(link); + } + } + } + } + r = address_new_dynamic(&address); if (r >= 0) { r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); if (r >= 0) { + _cleanup_route_free_ Route *route_gw = NULL; + _cleanup_route_free_ Route *route = NULL; + r = route_new_dynamic(&route_gw); if (r >= 0) { route_gw->family = AF_INET; @@ -966,6 +1033,25 @@ static int dhcp_lease_lost(Link *link) { return 0; } +static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { + sd_dhcp_lease *lease; + int r; + + r = sd_dhcp_client_get_lease(client, &lease); + if (r < 0) { + log_warning_link(link, "DHCP error: no lease %s", + strerror(-r)); + return r; + } + + sd_dhcp_lease_unref(link->dhcp_lease); + link->dhcp_lease = lease; + + link_enter_set_addresses(link); + + return 0; +} + static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { sd_dhcp_lease *lease; struct in_addr address; @@ -1002,7 +1088,8 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { r = sd_dhcp_lease_get_router(lease, &gateway); if (r < 0 && r != -ENOENT) { - log_warning_link(link, "DHCP error: %s", strerror(-r)); + log_warning_link(link, "DHCP error: could not get gateway: %s", + strerror(-r)); return r; } @@ -1116,6 +1203,13 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) { } } + break; + case DHCP_EVENT_RENEW: + r = dhcp_lease_renew(client, link); + if (r < 0) { + link_enter_failed(link); + return; + } break; case DHCP_EVENT_IP_ACQUIRE: r = dhcp_lease_acquired(client, link); @@ -1136,7 +1230,7 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) { break; default: if (event < 0) - log_warning_link(link, "DHCP error: %s", strerror(-event)); + log_warning_link(link, "DHCP error: client failed: %s", strerror(-event)); else log_warning_link(link, "DHCP unknown event: %d", event); break; @@ -1416,7 +1510,7 @@ static int link_acquire_conf(Link *link) { } } - if (link->network->dhcp) { + if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4)) { assert(link->dhcp_client); log_debug_link(link, "acquiring DHCPv4 lease"); @@ -1429,7 +1523,7 @@ static int link_acquire_conf(Link *link) { } } - if (link->network->dhcp6) { + if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V6)) { assert(link->icmp6_router_discovery); log_debug_link(link, "discovering IPv6 routers"); @@ -1565,15 +1659,13 @@ static int link_update_flags(Link *link, sd_rtnl_message *m) { } static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(link); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_rtnl_message_get_errno(m); if (r < 0) { @@ -1587,8 +1679,6 @@ static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { NULL); } - link_unref(link); - return 1; } @@ -1634,6 +1724,8 @@ static int link_enslaved(Link *link) { assert(link->state == LINK_STATE_ENSLAVING); assert(link->network); + log_debug_link(link, "enslaved"); + if (!(link->flags & IFF_UP)) { r = link_up(link); if (r < 0) { @@ -1642,14 +1734,11 @@ static int link_enslaved(Link *link) { } } - if (!link->network->dhcp && !link->network->ipv4ll) - return link_enter_set_addresses(link); - - return 0; + return link_enter_set_addresses(link); } static int enslave_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(link); @@ -1659,13 +1748,11 @@ static int enslave_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { link->enslaving --; - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) { - link_unref(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - } r = sd_rtnl_message_get_errno(m); - if (r < 0) { + if (r < 0 && r != -EEXIST) { log_struct_link(LOG_ERR, link, "MESSAGE=%-*s: could not enslave: %s", IFNAMSIZ, @@ -1673,17 +1760,12 @@ static int enslave_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { "ERRNO=%d", -r, NULL); link_enter_failed(link); - link_unref(link); return 1; } - log_debug_link(link, "enslaved"); - - if (link->enslaving == 0) + if (link->enslaving <= 0) link_enslaved(link); - link_unref(link); - return 1; } @@ -1885,7 +1967,7 @@ static int link_configure(Link *link) { return r; } - if (link->network->dhcp) { + if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4)) { r = sd_dhcp_client_new(&link->dhcp_client); if (r < 0) return r; @@ -1911,6 +1993,27 @@ static int link_configure(Link *link) { if (r < 0) return r; } + + if (link->network->dhcp_routes) { + r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_STATIC_ROUTE); + if (r < 0) + return r; + r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_CLASSLESS_STATIC_ROUTE); + if (r < 0) + return r; + } + + if (link->network->dhcp_sendhost) { + _cleanup_free_ char *hostname = gethostname_malloc(); + if (!hostname) + return -ENOMEM; + + if (!is_localhost(hostname)) { + r = sd_dhcp_client_set_hostname(link->dhcp_client, hostname); + if (r < 0) + return r; + } + } } if (link->network->dhcp_server) { @@ -1923,7 +2026,7 @@ static int link_configure(Link *link) { return r; } - if (link->network->dhcp6) { + if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V6)) { r = sd_icmp6_nd_new(&link->icmp6_router_discovery); if (r < 0) return r; @@ -1959,7 +2062,7 @@ static int link_configure(Link *link) { } static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; Network *network; int r; @@ -1968,14 +2071,14 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m, void * assert(link->manager); if (link->state != LINK_STATE_INITIALIZING) - return 0; + return 1; log_debug_link(link, "link state is up-to-date"); r = network_get(link->manager, link->udev_device, link->ifname, &link->mac, &network); if (r == -ENOENT) { link_enter_unmanaged(link); - return 0; + return 1; } else if (r < 0) return r; @@ -1987,7 +2090,7 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m, void * if (r < 0) return r; - return 0; + return 1; } int link_initialized(Link *link, struct udev_device *device) { @@ -2019,6 +2122,8 @@ int link_initialized(Link *link, struct udev_device *device) { if (r < 0) return r; + link_ref(link); + return 0; } @@ -2145,12 +2250,13 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use } static int link_get_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - Link *link = userdata; + _cleanup_link_unref_ Link *link = userdata; int r; assert(rtnl); assert(m); assert(link); + assert(link->manager); for (; m; m = sd_rtnl_message_next(m)) { r = sd_rtnl_message_get_errno(m); @@ -2195,6 +2301,8 @@ int link_add(Manager *m, sd_rtnl_message *message, Link **ret) { if (r < 0) return r; + link_ref(link); + if (detect_container(NULL) <= 0) { /* not in a container, udev will be around */ sprintf(ifindex_str, "n%"PRIu64, link->ifindex); @@ -2214,6 +2322,9 @@ int link_add(Manager *m, sd_rtnl_message *message, Link **ret) { if (r < 0) return r; } else { + /* we are calling a callback directly, so must take a ref */ + link_ref(link); + r = link_initialized_and_synced(m->rtnl, NULL, link); if (r < 0) return r; @@ -2456,3 +2567,55 @@ static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState); + +static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = { + [DHCP_SUPPORT_NONE] = "none", + [DHCP_SUPPORT_BOTH] = "both", + [DHCP_SUPPORT_V4] = "v4", + [DHCP_SUPPORT_V6] = "v6", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport); + +int config_parse_dhcp( + 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) { + + DHCPSupport *dhcp = data; + int k; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* Our enum shall be a superset of booleans, hence first try + * to parse as boolean, and then as enum */ + + k = parse_boolean(rvalue); + if (k > 0) + *dhcp = DHCP_SUPPORT_BOTH; + else if (k == 0) + *dhcp = DHCP_SUPPORT_NONE; + else { + DHCPSupport s; + + s = dhcp_support_from_string(rvalue); + if (s < 0){ + log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue); + return 0; + } + + *dhcp = s; + } + + return 0; +}