+ 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) {
+ struct in_addr address;
+ int r;
+
+ assert(ll);
+ assert(link);
+
+ r = sd_ipv4ll_get_address(ll, &address);
+ if (r < 0)
+ return r;
+
+ log_struct_link(LOG_INFO, link,
+ "MESSAGE=%s: IPv4 link-local address %u.%u.%u.%u",
+ link->ifname,
+ ADDRESS_FMT_VAL(address),
+ NULL);
+
+ link_enter_set_addresses(link);
+
+ return 0;
+}
+
+static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){
+ Link *link = userdata;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->manager);
+
+ switch(event) {
+ case IPV4LL_EVENT_STOP:
+ case IPV4LL_EVENT_CONFLICT:
+ r = ipv4ll_address_lost(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return;
+ }
+ break;
+ case IPV4LL_EVENT_BIND:
+ r = ipv4ll_address_claimed(ll, link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return;
+ }
+ break;
+ default:
+ if (event < 0)
+ log_warning_link(link, "IPv4 link-local error: %s", strerror(-event));
+ else
+ log_warning_link(link, "IPv4 link-local unknown event: %d", event);
+ break;
+ }
+}
+
+static int link_acquire_conf(Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(link->manager);
+ assert(link->manager->event);
+
+ if (link->network->ipv4ll) {
+ assert(link->ipv4ll);
+
+ log_debug_link(link, "acquiring IPv4 link-local address");
+
+ r = sd_ipv4ll_start(link->ipv4ll);
+ if (r < 0) {
+ log_warning_link(link, "could not acquire IPv4 "
+ "link-local address");
+ return r;
+ }
+ }
+
+ if (link->network->dhcp) {
+ assert(link->dhcp_client);
+
+ log_debug_link(link, "acquiring DHCPv4 lease");
+
+ r = sd_dhcp_client_start(link->dhcp_client);
+ if (r < 0) {
+ log_warning_link(link, "could not acquire DHCPv4 "
+ "lease");
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static bool link_has_carrier(unsigned flags, uint8_t operstate) {
+ /* see Documentation/networking/operstates.txt in the kernel sources */
+
+ if (operstate == IF_OPER_UP)
+ return true;
+
+ if (operstate == IF_OPER_UNKNOWN)
+ /* operstate may not be implemented, so fall back to flags */
+ if ((flags & IFF_LOWER_UP) && !(flags & IFF_DORMANT))
+ return true;
+
+ return false;
+}
+
+static int link_update_flags(Link *link, sd_rtnl_message *m) {
+ unsigned flags, flags_added, flags_removed, generic_flags;
+ uint8_t operstate;
+ bool carrier_gained = false, carrier_lost = false;
+ int r;
+
+ assert(link);
+
+ if (link->state == LINK_STATE_FAILED)
+ return 0;
+
+ r = sd_rtnl_message_link_get_flags(m, &flags);
+ if (r < 0) {
+ log_warning_link(link, "Could not get link flags");
+ return r;
+ }
+
+ r = sd_rtnl_message_read_u8(m, IFLA_OPERSTATE, &operstate);
+ if (r < 0)
+ /* if we got a message without operstate, take it to mean
+ the state was unchanged */
+ operstate = link->operstate;
+
+ if ((link->flags == flags) && (link->operstate == operstate))
+ return 0;
+
+ flags_added = (link->flags ^ flags) & flags;
+ flags_removed = (link->flags ^ flags) & link->flags;
+ generic_flags = ~(IFF_UP | IFF_LOWER_UP | IFF_DORMANT | IFF_DEBUG |
+ IFF_MULTICAST | IFF_BROADCAST | IFF_PROMISC |
+ IFF_NOARP | IFF_MASTER | IFF_SLAVE);
+
+ if (flags_added & IFF_UP)
+ log_debug_link(link, "link is up");
+ else if (flags_removed & IFF_UP)
+ log_debug_link(link, "link is down");
+
+ if (flags_added & IFF_LOWER_UP)
+ log_debug_link(link, "link is lower up");
+ else if (flags_removed & IFF_LOWER_UP)
+ log_debug_link(link, "link is lower down");
+
+ if (flags_added & IFF_DORMANT)
+ log_debug_link(link, "link is dormant");
+ else if (flags_removed & IFF_DORMANT)
+ log_debug_link(link, "link is not dormant");
+
+ if (flags_added & IFF_DEBUG)
+ log_debug_link(link, "debugging enabled in the kernel");
+ else if (flags_removed & IFF_DEBUG)
+ log_debug_link(link, "debugging disabled in the kernel");
+
+ if (flags_added & IFF_MULTICAST)
+ log_debug_link(link, "multicast enabled");
+ else if (flags_removed & IFF_MULTICAST)
+ log_debug_link(link, "multicast disabled");
+
+ if (flags_added & IFF_BROADCAST)
+ log_debug_link(link, "broadcast enabled");
+ else if (flags_removed & IFF_BROADCAST)
+ log_debug_link(link, "broadcast disabled");
+
+ if (flags_added & IFF_PROMISC)
+ log_debug_link(link, "promiscuous mode enabled");
+ else if (flags_removed & IFF_PROMISC)
+ log_debug_link(link, "promiscuous mode disabled");
+
+ if (flags_added & IFF_NOARP)
+ log_debug_link(link, "ARP protocol disabled");
+ else if (flags_removed & IFF_NOARP)
+ log_debug_link(link, "ARP protocol enabled");
+
+ if (flags_added & IFF_MASTER)
+ log_debug_link(link, "link is master");
+ else if (flags_removed & IFF_MASTER)
+ log_debug_link(link, "link is no longer master");
+
+ if (flags_added & IFF_SLAVE)
+ log_debug_link(link, "link is slave");
+ else if (flags_removed & IFF_SLAVE)
+ log_debug_link(link, "link is no longer slave");
+
+ /* link flags are currently at most 18 bits, let's default to printing 20 */
+ if (flags_added & generic_flags)
+ log_debug_link(link, "unknown link flags gained: %#.5x (ignoring)",
+ flags_added & generic_flags);
+
+ if (flags_removed & generic_flags)
+ log_debug_link(link, "unknown link flags lost: %#.5x (ignoring)",
+ flags_removed & generic_flags);
+
+ carrier_gained = !link_has_carrier(link->flags, link->operstate) &&
+ link_has_carrier(flags, operstate);
+ carrier_lost = link_has_carrier(link->flags, link->operstate) &&
+ !link_has_carrier(flags, operstate);