+ log_warning("rtnl: could not get message type");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get ifindex: %m");
+ return 0;
+ } else if (ifindex <= 0) {
+ log_warning("rtnl: received address message with invalid ifindex: %d", ifindex);
+ return 0;
+ } else {
+ r = link_get(m, ifindex, &link);
+ if (r < 0 || !link) {
+ log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex);
+ return 0;
+ }
+ }
+
+ r = address_new_dynamic(&address);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_message_addr_get_family(message, &address->family);
+ if (r < 0 || !IN_SET(address->family, AF_INET, AF_INET6)) {
+ log_link_warning(link, "rtnl: received address with invalid family, ignoring");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_prefixlen(message, &address->prefixlen);
+ if (r < 0) {
+ log_link_warning(link, "rtnl: received address with invalid prefixlen, ignoring");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_scope(message, &address->scope);
+ if (r < 0) {
+ log_link_warning(link, "rtnl: received address with invalid scope, ignoring");
+ return 0;
+ }
+
+ r = sd_rtnl_message_addr_get_flags(message, &address->flags);
+ if (r < 0) {
+ log_link_warning(link, "rtnl: received address with invalid flags, ignoring");
+ return 0;
+ }
+
+ switch (address->family) {
+ case AF_INET:
+ r = sd_rtnl_message_read_in_addr(message, IFA_LOCAL, &address->in_addr.in);
+ if (r < 0) {
+ log_link_warning(link, "rtnl: received address without valid address, ignoring");
+ return 0;
+ }
+
+ break;
+
+ case AF_INET6:
+ r = sd_rtnl_message_read_in6_addr(message, IFA_ADDRESS, &address->in_addr.in6);
+ if (r < 0) {
+ log_link_warning(link, "rtnl: received address without valid address, ignoring");
+ return 0;
+ }
+
+ break;
+
+ default:
+ assert_not_reached("invalid address family");
+ }
+
+ if (!inet_ntop(address->family, &address->in_addr, buf, INET6_ADDRSTRLEN)) {
+ log_link_warning(link, "could not print address");
+ return 0;
+ }
+
+ r = sd_rtnl_message_read_cache_info(message, IFA_CACHEINFO, &address->cinfo);
+ if (r >= 0) {
+ if (address->cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME)
+ valid_str = "ever";
+ else
+ valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
+ address->cinfo.ifa_valid * USEC_PER_SEC,
+ USEC_PER_SEC);
+ }
+
+ existing = link_get_equal_address(link, address);
+
+ switch (type) {
+ case RTM_NEWADDR:
+ if (existing) {
+ log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+
+
+ existing->scope = address->scope;
+ existing->flags = address->flags;
+ existing->cinfo = address->cinfo;
+
+ } else {
+ log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+
+ LIST_PREPEND(addresses, link->addresses, address);
+ address_establish(address, link);
+
+ address = NULL;
+
+ link_save(link);
+ }
+
+ break;
+
+ case RTM_DELADDR:
+
+ if (existing) {
+ log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+ address_release(existing, link);
+ LIST_REMOVE(addresses, link->addresses, existing);
+ address_free(existing);
+ } else
+ log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+
+ break;
+ default:
+ assert_not_reached("Received invalid RTNL message type");
+ }
+
+ return 1;
+}
+
+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(m->rtnl);
+ assert(message);
+ assert(ret);
+
+ r = link_new(m, message, ret);
+ if (r < 0)
+ return r;
+
+ link = *ret;
+
+ log_link_debug(link, "link %d added", link->ifindex);
+
+ if (detect_container(NULL) <= 0) {
+ /* not in a container, udev will be around */
+ sprintf(ifindex_str, "n%d", link->ifindex);
+ device = udev_device_new_from_device_id(m->udev, ifindex_str);
+ if (!device) {
+ log_link_warning(link,
+ "could not find udev device: %m");
+ return -errno;
+ }
+
+ if (udev_device_get_is_initialized(device) <= 0) {
+ /* not yet ready */
+ log_link_debug(link, "link pending udev initialization...");
+ return 0;
+ }
+
+ r = link_initialized(link, device);
+ 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;
+ }
+
+ return 0;
+}
+
+int link_update(Link *link, sd_rtnl_message *m) {
+ struct ether_addr mac;
+ const char *ifname;
+ uint32_t mtu;
+ bool had_carrier, carrier_gained, carrier_lost;
+ int r;
+
+ assert(link);
+ assert(link->ifname);
+ assert(m);
+
+ if (link->state == LINK_STATE_LINGER) {
+ link_ref(link);
+ log_link_info(link, "link readded");
+ link->state = LINK_STATE_ENSLAVING;
+ }
+
+ r = sd_rtnl_message_read_string(m, IFLA_IFNAME, &ifname);
+ if (r >= 0 && !streq(ifname, link->ifname)) {
+ log_link_info(link, "renamed to %s", ifname);
+
+ free(link->ifname);
+ link->ifname = strdup(ifname);
+ if (!link->ifname)
+ return -ENOMEM;
+ }
+
+ r = sd_rtnl_message_read_u32(m, IFLA_MTU, &mtu);
+ if (r >= 0 && mtu > 0) {
+ link->mtu = mtu;
+ if (!link->original_mtu) {
+ link->original_mtu = mtu;
+ log_link_debug(link, "saved original MTU: %"
+ PRIu32, link->original_mtu);
+ }
+
+ if (link->dhcp_client) {
+ r = sd_dhcp_client_set_mtu(link->dhcp_client,
+ link->mtu);
+ if (r < 0) {
+ log_link_warning(link,
+ "Could not update MTU in DHCP client: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+ }
+
+ /* 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_link_debug(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_link_warning(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,
+ (const uint8_t *) &link->mac,
+ sizeof (link->mac),
+ ARPHRD_ETHER);
+ if (r < 0) {
+ log_link_warning(link,
+ "Could not update MAC address in DHCP client: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ if (link->dhcp6_client) {
+ r = sd_dhcp6_client_set_mac(link->dhcp6_client,
+ (const uint8_t *) &link->mac,
+ sizeof (link->mac),
+ ARPHRD_ETHER);
+ if (r < 0) {
+ log_link_warning(link,
+ "Could not update MAC address in DHCPv6 client: %s",
+ strerror(-r));
+ return r;
+ }
+ }
+ }
+ }
+
+ had_carrier = link_has_carrier(link);
+
+ r = link_update_flags(link, m);
+ if (r < 0)
+ return r;
+
+ carrier_gained = !had_carrier && link_has_carrier(link);
+ carrier_lost = had_carrier && !link_has_carrier(link);
+
+ if (carrier_gained) {
+ log_link_info(link, "gained carrier");
+
+ if (link->network) {
+ r = link_acquire_conf(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
+ }
+ }
+ } else if (carrier_lost) {
+ log_link_info(link, "lost carrier");
+
+ r = link_stop_clients(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static void link_update_operstate(Link *link) {
+
+ assert(link);
+
+ if (link->kernel_operstate == IF_OPER_DORMANT)
+ link->operstate = LINK_OPERSTATE_DORMANT;
+ else if (link_has_carrier(link)) {
+ Address *address;
+ uint8_t scope = RT_SCOPE_NOWHERE;
+
+ /* if we have carrier, check what addresses we have */
+ LIST_FOREACH(addresses, address, link->addresses) {
+ if (address->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
+ continue;
+
+ if (address->scope < scope)
+ scope = address->scope;
+ }
+
+ if (scope < RT_SCOPE_SITE)
+ /* universally accessible addresses found */
+ link->operstate = LINK_OPERSTATE_ROUTABLE;
+ else if (scope < RT_SCOPE_HOST)
+ /* only link or site local addresses found */
+ link->operstate = LINK_OPERSTATE_DEGRADED;
+ else
+ /* no useful addresses found */
+ link->operstate = LINK_OPERSTATE_CARRIER;
+ } else if (link->flags & IFF_UP)
+ link->operstate = LINK_OPERSTATE_NO_CARRIER;
+ else
+ link->operstate = LINK_OPERSTATE_OFF;
+}
+
+int link_save(Link *link) {
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ const char *admin_state, *oper_state;
+ int r;
+
+ assert(link);
+ assert(link->state_file);
+ assert(link->lease_file);
+ assert(link->manager);
+
+ link_update_operstate(link);
+
+ r = manager_save(link->manager);
+ if (r < 0)