Link *link;
uint16_t type;
Address *address = NULL;
+ bool is_new = false;
int ifindex, r;
assert(rtnl);
if (r < 0)
return log_oom();
+ /* First, read minimal information to make address_get() work below. */
+
r = sd_rtnl_message_addr_get_family(message, &tmp->family);
if (r < 0) {
log_link_warning(link, "rtnl: received address message without family, ignoring.");
return 0;
}
- r = sd_rtnl_message_addr_get_scope(message, &tmp->scope);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address message without scope, ignoring: %m");
- return 0;
- }
-
- r = sd_netlink_message_read_u32(message, IFA_FLAGS, &tmp->flags);
- if (r == -ENODATA) {
- unsigned char flags;
-
- /* For old kernels. */
- r = sd_rtnl_message_addr_get_flags(message, &flags);
- if (r >= 0)
- tmp->flags = flags;
- }
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address message without flags, ignoring: %m");
- return 0;
- }
-
switch (tmp->family) {
case AF_INET:
r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &tmp->in_addr.in);
tmp->in_addr_peer = IN_ADDR_NULL;
}
- r = sd_netlink_message_read_in_addr(message, IFA_BROADCAST, &tmp->broadcast);
- if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: could not get broadcast from address message, ignoring: %m");
- return 0;
- }
-
- r = sd_netlink_message_read_string_strdup(message, IFA_LABEL, &tmp->label);
- if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: could not get label from address message, ignoring: %m");
- return 0;
- } else if (r >= 0 && streq_ptr(tmp->label, link->ifname))
- tmp->label = mfree(tmp->label);
-
break;
case AF_INET6:
assert_not_reached();
}
- r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
- if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m");
- return 0;
- }
-
+ /* Then, find the managed Address object corresponding to the received address. */
(void) address_get(link, tmp, &address);
- switch (type) {
- case RTM_NEWADDR:
+ if (type == RTM_DELADDR) {
if (address) {
- /* update flags and etc. */
- r = address_equalify(address, tmp);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to update properties of address %s, ignoring: %m",
- IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
- return 0;
- }
- address->flags = tmp->flags;
- address->scope = tmp->scope;
- address_set_lifetime(m, address, &cinfo);
- address_enter_configured(address);
- log_address_debug(address, "Received updated", link);
- } else {
- address_set_lifetime(m, tmp, &cinfo);
- address_enter_configured(tmp);
- log_address_debug(tmp, "Received new", link);
+ address_enter_removed(address);
+ log_address_debug(address, address->state == 0 ? "Forgetting" : "Removed", link);
+ (void) address_drop(address);
+ } else
+ log_address_debug(tmp, "Kernel removed unknown", link);
- r = address_add(link, tmp);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to remember foreign address %s, ignoring: %m",
- IN_ADDR_PREFIX_TO_STRING(tmp->family, &tmp->in_addr, tmp->prefixlen));
- return 0;
- }
+ return 0;
+ }
- address = TAKE_PTR(tmp);
+ if (!address) {
+ /* If we did not know the address, then save it. */
+ r = address_add(link, tmp);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to remember foreign address %s, ignoring: %m",
+ IN_ADDR_PREFIX_TO_STRING(tmp->family, &tmp->in_addr, tmp->prefixlen));
+ return 0;
}
+ address = TAKE_PTR(tmp);
- /* address_update() logs internally, so we don't need to here. */
- r = address_update(address);
- if (r < 0)
- link_enter_failed(link);
+ is_new = true;
- break;
+ } else {
+ /* Otherwise, update the managed Address object with the netlink notification. */
+ address->prefixlen = tmp->prefixlen;
+ address->in_addr_peer = tmp->in_addr_peer;
+ }
- case RTM_DELADDR:
- if (address) {
- address_enter_removed(address);
- log_address_debug(address, address->state == 0 ? "Forgetting" : "Removed", link);
- (void) address_drop(address);
- } else
- log_address_debug(tmp, "Kernel removed unknown", link);
+ /* Then, update miscellaneous info. */
+ r = sd_rtnl_message_addr_get_scope(message, &address->scope);
+ if (r < 0)
+ log_link_debug_errno(link, r, "rtnl: received address message without scope, ignoring: %m");
- break;
+ if (address->family == AF_INET) {
+ _cleanup_free_ char *label = NULL;
- default:
- assert_not_reached();
+ r = sd_netlink_message_read_string_strdup(message, IFA_LABEL, &label);
+ if (r >= 0) {
+ if (!streq_ptr(label, link->ifname))
+ free_and_replace(address->label, label);
+ } else if (r != -ENODATA)
+ log_link_debug_errno(link, r, "rtnl: could not get label from address message, ignoring: %m");
+
+ r = sd_netlink_message_read_in_addr(message, IFA_BROADCAST, &address->broadcast);
+ if (r < 0 && r != -ENODATA)
+ log_link_debug_errno(link, r, "rtnl: could not get broadcast from address message, ignoring: %m");
}
+ r = sd_netlink_message_read_u32(message, IFA_FLAGS, &address->flags);
+ if (r == -ENODATA) {
+ unsigned char flags;
+
+ /* For old kernels. */
+ r = sd_rtnl_message_addr_get_flags(message, &flags);
+ if (r >= 0)
+ address->flags = flags;
+ } else if (r < 0)
+ log_link_debug_errno(link, r, "rtnl: failed to read IFA_FLAGS attribute, ignoring: %m");
+
+ r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
+ if (r >= 0)
+ address_set_lifetime(m, address, &cinfo);
+ else if (r != -ENODATA)
+ log_link_debug_errno(link, r, "rtnl: failed to read IFA_CACHEINFO attribute, ignoring: %m");
+
+ address_enter_configured(address);
+ log_address_debug(address, is_new ? "Received new": "Received updated", link);
+
+ /* address_update() logs internally, so we don't need to here. */
+ r = address_update(address);
+ if (r < 0)
+ link_enter_failed(link);
+
return 1;
}