+
+static int link_configure(Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->state == LINK_STATE_INITIALIZING);
+
+ if (link->network->ipv4ll) {
+ uint8_t seed[8];
+ r = sd_ipv4ll_new(&link->ipv4ll);
+ if (r < 0)
+ return r;
+
+ if (link->udev_device) {
+ r = net_get_unique_predictable_data(link->udev_device, seed);
+ if (r >= 0) {
+ r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4ll_set_index(link->ipv4ll, link->ifindex);
+ if (r < 0)
+ return r;
+
+ r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
+ if (r < 0)
+ return r;
+ }
+
+ if (link->network->dhcp) {
+ r = sd_dhcp_client_new(&link->dhcp_client);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_client_set_index(link->dhcp_client, link->ifindex);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp_handler, link);
+ if (r < 0)
+ return r;
+
+ if (link->network->dhcp_mtu) {
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, 26);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return link_enter_enslave(link);
+}
+
+int link_initialized(Link *link, struct udev_device *device) {
+ Network *network;
+ unsigned flags;
+ int r;
+
+ assert(link);
+ assert(link->ifname);
+ assert(link->manager);
+
+ if (link->state != LINK_STATE_INITIALIZING)
+ return 0;
+
+ if (device)
+ link->udev_device = udev_device_ref(device);
+
+ log_debug_link(link, "link initialized");
+
+ r = network_get(link->manager, device, link->ifname, &link->mac, &network);
+ if (r < 0)
+ return r == -ENOENT ? 0 : r;
+
+ r = network_apply(link->manager, network, link);
+ if (r < 0)
+ return r;
+
+ r = link_configure(link);
+ if (r < 0)
+ return r;
+
+ /* re-trigger all state updates */
+ flags = link->flags;
+ link->flags = 0;
+ r = link_update_flags(link, flags);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int link_add(Manager *m, sd_rtnl_message *message, Link **ret) {
+ Link *link;
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ char ifindex_str[2 + DECIMAL_STR_MAX(int)];
+ int r;
+
+ assert(m);
+ assert(message);
+ assert(ret);
+
+ r = link_new(m, message, ret);
+ if (r < 0)
+ return r;
+
+ link = *ret;
+
+ log_info_link(link, "link added");
+
+ if (detect_container(NULL) <= 0) {
+ /* not in a container, udev will be around */
+ sprintf(ifindex_str, "n%"PRIu64, link->ifindex);
+ device = udev_device_new_from_device_id(m->udev, ifindex_str);
+ if (!device) {
+ log_warning_link(link, "could not find udev device");
+ return -errno;
+ }
+
+ if (udev_device_get_is_initialized(device) <= 0)
+ /* not yet ready */
+ return 0;
+ }
+
+ r = link_initialized(link, device);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int link_update(Link *link, sd_rtnl_message *m) {
+ unsigned flags;
+ struct ether_addr mac;
+ int r;
+
+ assert(link);
+ assert(m);
+
+ if (link->state == LINK_STATE_FAILED)
+ return 0;
+
+ if (!link->original_mtu) {
+ r = sd_rtnl_message_read_u16(m, IFLA_MTU, &link->original_mtu);
+ if (r >= 0)
+ log_debug_link(link, "saved original MTU: %"
+ PRIu16, link->original_mtu);
+ }
+
+ /* The kernel may broadcast NEWLINK messages without the MAC address
+ set, simply ignore them. */
+ r = sd_rtnl_message_read_ether_addr(m, IFLA_ADDRESS, &mac);
+ if (r >= 0) {
+ if (memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN)) {
+
+ memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN);
+
+ log_debug_link(link, "MAC address: "
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ mac.ether_addr_octet[0],
+ mac.ether_addr_octet[1],
+ mac.ether_addr_octet[2],
+ mac.ether_addr_octet[3],
+ mac.ether_addr_octet[4],
+ mac.ether_addr_octet[5]);
+
+ if (link->ipv4ll) {
+ r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
+ if (r < 0) {
+ log_warning_link(link, "Could not update MAC "
+ "address in IPv4LL client: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (link->dhcp_client) {
+ r = sd_dhcp_client_set_mac(link->dhcp_client, &link->mac);
+ if (r < 0) {
+ log_warning_link(link, "Could not update MAC "
+ "address in DHCP client: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+ }
+ }
+
+ r = sd_rtnl_message_link_get_flags(m, &flags);
+ if (r < 0) {
+ log_warning_link(link, "Could not get link flags");
+ return r;
+ }
+
+ return link_update_flags(link, flags);
+}
+
+int link_save(Link *link) {
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ const char *state;
+ int r;
+
+ assert(link);
+ assert(link->state_file);
+
+ state = link_state_to_string(link->state);
+ assert(state);
+
+ r = fopen_temporary(link->state_file, &f, &temp_path);
+ if (r < 0)
+ goto finish;
+
+ fchmod(fileno(f), 0644);
+
+ fprintf(f,
+ "# This is private data. Do not parse.\n"
+ "STATE=%s\n", state);
+
+ if (link->dhcp_lease) {
+ _cleanup_free_ char *lease_file = NULL;
+
+ r = asprintf(&lease_file, "/run/systemd/network/leases/%"PRIu64,
+ link->ifindex);
+ if (r < 0)
+ return -ENOMEM;
+
+ r = dhcp_lease_save(link->dhcp_lease, lease_file);
+ if (r < 0)
+ goto finish;
+
+ fprintf(f, "DHCP_LEASE=%s\n", lease_file);
+ }
+
+ fflush(f);
+
+ if (ferror(f) || rename(temp_path, link->state_file) < 0) {
+ r = -errno;
+ unlink(link->state_file);
+ unlink(temp_path);
+ }
+
+finish:
+ if (r < 0)
+ log_error("Failed to save link data %s: %s", link->state_file, strerror(-r));
+
+ return r;
+}
+
+static const char* const link_state_table[_LINK_STATE_MAX] = {
+ [LINK_STATE_INITIALIZING] = "configuring",
+ [LINK_STATE_ENSLAVING] = "configuring",
+ [LINK_STATE_SETTING_ADDRESSES] = "configuring",
+ [LINK_STATE_SETTING_ROUTES] = "configuring",
+ [LINK_STATE_CONFIGURED] = "configured",
+ [LINK_STATE_FAILED] = "failed",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState);