#include <netinet/ether.h>
#include <linux/if.h>
+#include <unistd.h>
#include "networkd.h"
#include "libudev-private.h"
#include "virt.h"
#include "bus-util.h"
#include "network-internal.h"
+#include "conf-parser.h"
#include "network-util.h"
#include "dhcp-lease-internal.h"
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);
if (!link)
return;
- assert(link->manager);
-
while ((address = link->addresses)) {
LIST_REMOVE(addresses, link->addresses, address);
address_free(address);
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);
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);
}
}
- 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) {
}
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);
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)
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->route_messages ++;
+ }
+
+ return 0;
+}
+
static int link_enter_set_routes(Link *link) {
Route *rt;
int r;
return r;
}
- link_ref(link);
link->route_messages ++;
}
return r;
}
- link_ref(link);
link->route_messages ++;
}
}
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;
}
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) {
return r;
}
- link_ref(link);
link->route_messages ++;
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) {
return r;
}
- link_ref(link);
link->route_messages ++;
}
+
+ if (link->network->dhcp_routes)
+ link_set_dhcp_routes(link);
}
if (link->route_messages == 0) {
}
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)
"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);
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)
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);
return r;
}
- link_ref(link);
link->addr_messages ++;
}
return r;
}
- link_ref(link);
link->addr_messages ++;
}
}
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",
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));
return r;
}
- link_ref(link);
link->addr_messages ++;
}
}
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)
"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)
"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;
}
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)
"ERRNO=%d", -r,
NULL);
- link_unref(link);
-
return 1;
}
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);
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);
+ }
+ }
+ }
+ }
+
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;
route_gw->scope = RT_SCOPE_LINK;
route_drop(route_gw, link, &route_drop_handler);
- link_ref(link);
}
r = route_new_dynamic(&route);
route->in_addr.in = gateway;
route_drop(route, link, &route_drop_handler);
- link_ref(link);
}
}
address->prefixlen = prefixlen;
address_drop(address, link, &address_drop_handler);
- link_ref(link);
}
if (link->network->dhcp_mtu) {
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;
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;
}
}
}
+ 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);
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;
address->broadcast.s_addr = address->in_addr.in.s_addr | htonl(0xfffffffflu >> address->prefixlen);
address_update(address, link, &address_update_handler);
- link_ref(link);
}
return 0;
address->scope = RT_SCOPE_LINK;
address_drop(address, link, &address_drop_handler);
- link_ref(link);
r = route_new_dynamic(&route);
if (r < 0) {
route->metrics = 99;
route_drop(route, link, &route_drop_handler);
- link_ref(link);
}
return 0;
}
}
- 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");
}
}
- 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");
}
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) {
NULL);
}
- link_unref(link);
-
return 1;
}
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) {
}
}
- 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);
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,
"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;
}
return r;
}
- link_ref(link);
link->enslaving ++;
}
return r;
}
- link_ref(link);
link->enslaving ++;
}
return r;
}
- link_ref(link);
link->enslaving ++;
}
return r;
}
- link_ref(link);
link->enslaving ++;
}
return r;
}
- link_ref(link);
link->enslaving ++;
}
return r;
}
- link_ref(link);
link->enslaving ++;
}
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;
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) {
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;
}
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;
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;
if (r < 0)
return r;
- return 0;
+ return 1;
}
int link_initialized(Link *link, struct udev_device *device) {
if (r < 0)
return r;
+ link_ref(link);
+
return 0;
}
}
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);
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);
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;
};
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;
+}