diff options
author | Umut Tezduyar Lindskog <umut.tezduyar@axis.com> | 2014-02-28 16:10:20 +0100 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2014-03-03 23:24:34 +0100 |
commit | 5c1d3fc93d91384bbac29adf01074fa4375317ea (patch) | |
tree | a71f0587b742f42d110a44c2700b522bcbedaf2c /src/network/networkd-link.c | |
parent | b6b8adbff4b1a67a2fffc2c225f1b083d9e4a69e (diff) |
sd-network: IPv4 link-local support [v2]
Implements IPv4LL with respect to RFC 3927
(http://tools.ietf.org/rfc/rfc3927.txt) and integrates it
with networkd. Majority of the IPv4LL state machine is
taken from avahi (http://avahi.org/) project's autoip.
IPv4LL can be enabled by IPv4LL=yes under [Network]
section of .network file.
IPv4LL works independent of DHCP but if DHCP lease is
aquired, then LL address will be dropped.
[tomegun: removed a trailing newline and a compiler warning]
Diffstat (limited to 'src/network/networkd-link.c')
-rw-r--r-- | src/network/networkd-link.c | 312 |
1 files changed, 283 insertions, 29 deletions
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index f09fb7546d..c10d947589 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -87,6 +87,8 @@ void link_free(Link *link) { sd_dhcp_client_free(link->dhcp_client); sd_dhcp_lease_unref(link->dhcp_lease); + sd_ipv4ll_free(link->ipv4ll); + hashmap_remove(link->manager->links, &link->ifindex); free(link->ifname); @@ -196,6 +198,7 @@ static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { static int link_enter_set_routes(Link *link) { Route *rt; + struct in_addr a; int r; assert(link); @@ -204,7 +207,8 @@ static int link_enter_set_routes(Link *link) { link->state = LINK_STATE_SETTING_ROUTES; - if (!link->network->static_routes && !link->dhcp_lease) + if (!link->network->static_routes && !link->dhcp_lease && + (!link->ipv4ll || sd_ipv4ll_get_address(link->ipv4ll, &a) < 0)) return link_enter_configured(link); log_debug_link(link, "setting routes"); @@ -221,6 +225,41 @@ static int link_enter_set_routes(Link *link) { link->route_messages ++; } + if (link->ipv4ll && !link->dhcp_lease) { + _cleanup_route_free_ Route *route = NULL; + struct in_addr addr; + + r = sd_ipv4ll_get_address(link->ipv4ll, &addr); + if (r < 0 && r != -ENOENT) { + log_warning_link(link, "IPV4LL error: no address: %s", + strerror(-r)); + return r; + } + + if (r != -ENOENT) { + 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; + + r = route_configure(route, link, &route_handler); + if (r < 0) { + log_warning_link(link, + "could not set routes: %s", strerror(-r)); + link_enter_failed(link); + return r; + } + + link->route_messages ++; + } + } + if (link->dhcp_lease) { _cleanup_route_free_ Route *route = NULL; struct in_addr gateway; @@ -256,6 +295,28 @@ static int link_enter_set_routes(Link *link) { return 0; } +static int route_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { + Link *link = userdata; + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (link->state == LINK_STATE_FAILED) + return 1; + + r = sd_rtnl_message_get_errno(m); + if (r < 0 && r != -ENOENT) + log_struct_link(LOG_WARNING, link, + "MESSAGE=%s: could not drop route: %s", + link->ifname, strerror(-r), + "ERRNO=%d", -r, + NULL); + + return 0; +} + static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { Link *link = userdata; int r; @@ -289,6 +350,7 @@ static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { static int link_enter_set_addresses(Link *link) { Address *ad; + struct in_addr a; int r; assert(link); @@ -297,7 +359,8 @@ static int link_enter_set_addresses(Link *link) { link->state = LINK_STATE_SETTING_ADDRESSES; - if (!link->network->static_addresses && !link->dhcp_lease) + if (!link->network->static_addresses && !link->dhcp_lease && + (!link->ipv4ll || sd_ipv4ll_get_address(link->ipv4ll, &a) < 0)) return link_enter_set_routes(link); log_debug_link(link, "setting addresses"); @@ -314,6 +377,42 @@ static int link_enter_set_addresses(Link *link) { link->addr_messages ++; } + if (link->ipv4ll && !link->dhcp_lease) { + _cleanup_address_free_ Address *ll_addr = NULL; + struct in_addr addr; + + r = sd_ipv4ll_get_address(link->ipv4ll, &addr); + if (r < 0 && r != -ENOENT) { + log_warning_link(link, "IPV4LL error: no address: %s", + strerror(-r)); + return r; + } + + if (r != -ENOENT) { + r = address_new_dynamic(&ll_addr); + if (r < 0) { + log_error_link(link, "Could not allocate address: %s", strerror(-r)); + return r; + } + + ll_addr->family = AF_INET; + ll_addr->in_addr.in = addr; + ll_addr->prefixlen = 16; + ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htonl(0xfffffffflu >> ll_addr->prefixlen); + ll_addr->scope = RT_SCOPE_LINK; + + r = address_configure(ll_addr, link, &address_handler); + if (r < 0) { + log_warning_link(link, + "could not set addresses: %s", strerror(-r)); + link_enter_failed(link); + return r; + } + + link->addr_messages ++; + } + } + if (link->dhcp_lease) { _cleanup_address_free_ Address *address = NULL; struct in_addr addr; @@ -381,7 +480,7 @@ static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdat "ERRNO=%d", -r, NULL); - return 1; + return 0; } static int set_hostname_handler(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { @@ -505,7 +604,7 @@ static int dhcp_lease_lost(Link *link) { address->in_addr.in = addr; address->prefixlen = prefixlen; - address_drop(address, link, address_drop_handler); + address_drop(address, link, &address_drop_handler); } if (link->network->dhcp_mtu) { @@ -675,6 +774,14 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) { } } + if (event == DHCP_EVENT_EXPIRED && link->network->ipv4ll) { + r = sd_ipv4ll_start (link->ipv4ll); + if (r < 0) { + link_enter_failed(link); + return; + } + } + break; case DHCP_EVENT_IP_ACQUIRE: r = dhcp_lease_acquired(client, link); @@ -682,6 +789,13 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) { link_enter_failed(link); return; } + if (link->ipv4ll) { + r = sd_ipv4ll_stop(link->ipv4ll); + if (r < 0) { + link_enter_failed(link); + return; + } + } break; default: if (event < 0) @@ -694,48 +808,179 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) { return; } +static int ipv4ll_address_lost(sd_ipv4ll *ll, Link *link) { + int r; + struct in_addr addr; + + assert(ll); + 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 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(ll, 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->network->dhcp); assert(link->manager); assert(link->manager->event); - if (!link->dhcp_client) { - r = sd_dhcp_client_new(&link->dhcp_client); - if (r < 0) - return r; + if (link->network->ipv4ll) { + if (!link->ipv4ll) { + r = sd_ipv4ll_new(&link->ipv4ll); + if (r < 0) + return r; - r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0); - if (r < 0) - return r; + r = sd_ipv4ll_attach_event(link->ipv4ll, 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_ipv4ll_set_index(link->ipv4ll, link->ifindex); + if (r < 0) + return r; - r = sd_dhcp_client_set_mac(link->dhcp_client, &link->mac); - if (r < 0) - return r; + r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); + if (r < 0) + return r; + + r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link); + if (r < 0) + return r; + } - r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp_handler, link); + log_debug_link(link, "acquiring IPv4 link-local address"); + + r = sd_ipv4ll_start(link->ipv4ll); if (r < 0) return r; + } + + if (link->network->dhcp) { + if (!link->dhcp_client) { + 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_mac(link->dhcp_client, &link->mac); + if (r < 0) + return r; - if (link->network->dhcp_mtu) { - r = sd_dhcp_client_set_request_option(link->dhcp_client, 26); + 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; + } } - } - log_debug_link(link, "acquiring DHCPv4 lease"); + log_debug_link(link, "acquiring DHCPv4 lease"); - r = sd_dhcp_client_start(link->dhcp_client); - if (r < 0) - return r; + r = sd_dhcp_client_start(link->dhcp_client); + if (r < 0) + return r; + } return 0; } @@ -762,10 +1007,10 @@ static int link_update_flags(Link *link, unsigned flags) { if (flags & IFF_LOWER_UP) { log_info_link(link, "carrier on"); - if (link->network->dhcp) { + if (link->network->dhcp || link->network->ipv4ll) { r = link_acquire_conf(link); if (r < 0) { - log_warning_link(link, "Could not acquire DHCPv4 lease: %s", strerror(-r)); + log_warning_link(link, "Could not acquire configuration: %s", strerror(-r)); link_enter_failed(link); return r; } @@ -781,6 +1026,15 @@ static int link_update_flags(Link *link, unsigned flags) { return r; } } + + if (link->network->ipv4ll) { + r = sd_ipv4ll_stop(link->ipv4ll); + if (r < 0) { + log_warning_link(link, "Could not stop IPv4 link-local: %s", strerror(-r)); + link_enter_failed(link); + return r; + } + } } } @@ -863,7 +1117,7 @@ static int link_enslaved(Link *link) { return r; } - if (!link->network->dhcp) + if (!link->network->dhcp && !link->network->ipv4ll) return link_enter_set_addresses(link); return 0; |