+ int r = 0;
+
+ assert(link);
+ assert(link->network);
+ assert(link->manager);
+
+ if (link->state == LINK_STATE_FAILED)
+ return;
+
+ switch (event) {
+ case DHCP_EVENT_NO_LEASE:
+ log_debug_link(link, "IP address in use.");
+ break;
+ case DHCP_EVENT_EXPIRED:
+ case DHCP_EVENT_STOP:
+ case DHCP_EVENT_IP_CHANGE:
+ if (link->network->dhcp_critical) {
+ log_error_link(link, "DHCPv4 connection considered system critical, "
+ "ignoring request to reconfigure it.");
+ return;
+ }
+
+ if (link->dhcp_lease) {
+ r = dhcp_lease_lost(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return;
+ }
+ }
+
+ if (event == DHCP_EVENT_IP_CHANGE) {
+ r = dhcp_lease_acquired(client, link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return;
+ }
+ }
+
+ if (event == DHCP_EVENT_EXPIRED && link->network->ipv4ll) {
+ if (!sd_ipv4ll_is_running(link->ipv4ll))
+ r = sd_ipv4ll_start(link->ipv4ll);
+ else if (ipv4ll_is_bound(link->ipv4ll))
+ r = ipv4ll_address_update(link, false);
+ if (r < 0) {
+ link_enter_failed(link);
+ return;
+ }
+ }
+
+ break;
+ case DHCP_EVENT_IP_ACQUIRE:
+ r = dhcp_lease_acquired(client, link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return;
+ }
+ if (link->ipv4ll) {
+ if (ipv4ll_is_bound(link->ipv4ll))
+ r = ipv4ll_address_update(link, true);
+ else
+ r = sd_ipv4ll_stop(link->ipv4ll);
+ if (r < 0) {
+ link_enter_failed(link);
+ return;
+ }
+ }
+ break;
+ default:
+ if (event < 0)
+ log_warning_link(link, "DHCP error: %s", strerror(-event));
+ else
+ log_warning_link(link, "DHCP unknown event: %d", event);
+ break;
+ }
+
+ return;
+}
+
+static int ipv4ll_address_update(Link *link, bool deprecate) {
+ int r;
+ struct in_addr addr;
+
+ assert(link);
+
+ r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
+ if (r >= 0) {
+ _cleanup_address_free_ Address *address = NULL;
+
+ log_debug_link(link, "IPv4 link-local %s %u.%u.%u.%u",
+ deprecate ? "deprecate" : "approve",
+ ADDRESS_FMT_VAL(addr));
+
+ r = address_new_dynamic(&address);
+ if (r < 0) {
+ log_error_link(link, "Could not allocate address: %s", strerror(-r));
+ return r;
+ }
+
+ address->family = AF_INET;
+ address->in_addr.in = addr;
+ address->prefixlen = 16;
+ address->scope = RT_SCOPE_LINK;
+ address->cinfo.ifa_prefered = deprecate ? 0 : CACHE_INFO_INFINITY_LIFE_TIME;
+ address->broadcast.s_addr = address->in_addr.in.s_addr | htonl(0xfffffffflu >> address->prefixlen);
+
+ address_update(address, link, &address_update_handler);
+ }
+
+ return 0;
+
+}
+
+static int ipv4ll_address_lost(Link *link) {
+ int r;
+ struct in_addr addr;
+
+ assert(link);
+
+ r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
+ if (r >= 0) {
+ _cleanup_address_free_ Address *address = NULL;
+ _cleanup_route_free_ Route *route = NULL;
+
+ log_debug_link(link, "IPv4 link-local release %u.%u.%u.%u",
+ ADDRESS_FMT_VAL(addr));
+
+ r = address_new_dynamic(&address);
+ if (r < 0) {
+ log_error_link(link, "Could not allocate address: %s", strerror(-r));
+ return r;
+ }
+
+ address->family = AF_INET;
+ address->in_addr.in = addr;
+ address->prefixlen = 16;
+ address->scope = RT_SCOPE_LINK;
+
+ address_drop(address, link, &address_drop_handler);
+
+ 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->scope = RT_SCOPE_LINK;
+ route->metrics = 99;
+
+ route_drop(route, link, &route_drop_handler);
+ }
+
+ return 0;
+}
+
+static bool ipv4ll_is_bound(sd_ipv4ll *ll) {
+ int r;
+ struct in_addr addr;
+
+ assert(ll);
+
+ r = sd_ipv4ll_get_address(ll, &addr);
+ if (r < 0)
+ return false;
+ return true;
+}
+
+static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {