networkd-dhcp6.c revision be3a09b7ffe62b52658e77ae4d6638d1b0dae654
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering/***
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering This file is part of systemd.
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering Copyright (C) 2014 Intel Corporation. All rights reserved.
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering systemd is free software; you can redistribute it and/or modify it
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering under the terms of the GNU Lesser General Public License as published by
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering (at your option) any later version.
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering systemd is distributed in the hope that it will be useful, but
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering Lesser General Public License for more details.
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering You should have received a copy of the GNU Lesser General Public License
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering***/
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering#include <netinet/ether.h>
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering#include <linux/if.h>
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering#include "networkd-link.h"
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering#include "network-internal.h"
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering#include "sd-icmp6-nd.h"
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering#include "sd-dhcp6-client.h"
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poetteringstatic int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poetteringstatic int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering Link *link) {
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering return 0;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering}
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poetteringstatic int dhcp6_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering void *userdata) {
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering _cleanup_link_unref_ Link *link = userdata;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering int r;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering assert(link);
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering r = sd_rtnl_message_get_errno(m);
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering if (r < 0 && r != -EEXIST) {
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering if (link->rtnl_extended_attrs) {
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism");
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering link->rtnl_extended_attrs = false;
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering dhcp6_lease_address_acquired(link->dhcp6_client, link);
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering return 1;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering }
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering log_link_error(link, "Could not set DHCPv6 address: %s",
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering strerror(-r));
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering link_enter_failed(link);
f52e61da047d7fc74e83f12dbbf87e0cbcc51c73Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering } else if (r >= 0)
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering link_rtnl_process_address(rtnl, m, link->manager);
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering return 1;
6e0684729420912df019cc64d3f8a3c8290cc5f1Lennart Poettering}
6e0684729420912df019cc64d3f8a3c8290cc5f1Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poetteringstatic int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr,
ae6a4bbf318e197813227e50c245a00de03784a2Lennart Poettering uint8_t prefixlen, uint32_t lifetime_preferred,
ae6a4bbf318e197813227e50c245a00de03784a2Lennart Poettering uint32_t lifetime_valid) {
ae6a4bbf318e197813227e50c245a00de03784a2Lennart Poettering int r;
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering _cleanup_address_free_ Address *addr = NULL;
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering r = address_new_dynamic(&addr);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen if (r < 0)
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering return r;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering addr->family = AF_INET6;
4667e00a61c2f60922558bc5e33ac9d3073a482cLennart Poettering memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr));
4667e00a61c2f60922558bc5e33ac9d3073a482cLennart Poettering
d20b1667dbab8bccf69735523a0d5fc645e81b80Tom Gundersen addr->flags = IFA_F_NOPREFIXROUTE;
4667e00a61c2f60922558bc5e33ac9d3073a482cLennart Poettering addr->prefixlen = prefixlen;
8300ba218e3cf5049496937be8bce10f22d09bbcTom Gundersen
8300ba218e3cf5049496937be8bce10f22d09bbcTom Gundersen addr->cinfo.ifa_prefered = lifetime_preferred;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen addr->cinfo.ifa_valid = lifetime_valid;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen log_link_struct(link, LOG_INFO, "MESSAGE=%-*s: DHCPv6 address "SD_ICMP6_ADDRESS_FORMAT_STR"/%d timeout preferred %d valid %d",
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering IFNAMSIZ,
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering link->ifname, SD_ICMP6_ADDRESS_FORMAT_VAL(addr->in_addr.in6),
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering addr->prefixlen, lifetime_preferred, lifetime_valid,
801ad6a6a9cd8fbd58b9f9c27f20dbb3c87d47ddLennart Poettering NULL);
801ad6a6a9cd8fbd58b9f9c27f20dbb3c87d47ddLennart Poettering
801ad6a6a9cd8fbd58b9f9c27f20dbb3c87d47ddLennart Poettering r = address_update(addr, link, dhcp6_address_handler);
801ad6a6a9cd8fbd58b9f9c27f20dbb3c87d47ddLennart Poettering if (r < 0)
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering log_link_warning(link, "Could not assign DHCPv6 address: %s",
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering strerror(-r));
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering return r;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering}
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poetteringstatic int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering int r;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering sd_dhcp6_lease *lease;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering struct in6_addr ip6_addr;
f52e61da047d7fc74e83f12dbbf87e0cbcc51c73Lennart Poettering uint32_t lifetime_preferred, lifetime_valid;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering uint8_t prefixlen;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering r = sd_dhcp6_client_get_lease(client, &lease);
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering if (r < 0)
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering return r;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering sd_dhcp6_lease_reset_address_iter(lease);
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering &lifetime_preferred,
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering &lifetime_valid) >= 0) {
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering r = sd_icmp6_ra_get_prefixlen(link->icmp6_router_discovery,
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering &ip6_addr, &prefixlen);
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering if (r < 0 && r != -EADDRNOTAVAIL) {
6e0684729420912df019cc64d3f8a3c8290cc5f1Lennart Poettering log_link_warning(link, "Could not get prefix information: %s",
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering strerror(-r));
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering return r;
3b31df8301fd7dfb78bf9eaf9227d40c9bf12182Tom Gundersen }
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering if (r == -EADDRNOTAVAIL)
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering prefixlen = 128;
ec2c5e4398f9d65e5dfe61530f2556224733d1e6Lennart Poettering
c3bc53e62459d7e566ffffeade41cd82bc6754f5Lennart Poettering r = dhcp6_address_update(link, &ip6_addr, prefixlen,
lifetime_preferred, lifetime_valid);
if (r < 0)
return r;
}
return 0;
}
static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
int r;
Link *link = userdata;
assert(link);
assert(link->network);
assert(link->manager);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
switch(event) {
case DHCP6_EVENT_STOP:
case DHCP6_EVENT_RESEND_EXPIRE:
case DHCP6_EVENT_RETRANS_MAX:
log_link_debug(link, "DHCPv6 event %d", event);
break;
case DHCP6_EVENT_IP_ACQUIRE:
r = dhcp6_lease_address_acquired(client, link);
if (r < 0) {
link_enter_failed(link);
return;
}
/* fall through */
case DHCP6_EVENT_INFORMATION_REQUEST:
r = dhcp6_lease_information_acquired(client, link);
if (r < 0) {
link_enter_failed(link);
return;
}
break;
default:
if (event < 0)
log_link_warning(link, "DHCPv6 error: %s",
strerror(-event));
else
log_link_warning(link, "DHCPv6 unknown event: %d",
event);
return;
}
}
static int dhcp6_configure(Link *link, int event) {
int r;
bool information_request;
assert_return(link, -EINVAL);
if (link->dhcp6_client) {
if (event != ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED)
return 0;
r = sd_dhcp6_client_get_information_request(link->dhcp6_client,
&information_request);
if (r < 0) {
log_link_warning(link, "Could not get DHCPv6 Information request setting: %s",
strerror(-r));
link->dhcp6_client =
sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
if (!information_request)
return r;
r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
false);
if (r < 0) {
log_link_warning(link, "Could not unset DHCPv6 Information request: %s",
strerror(-r));
link->dhcp6_client =
sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0) {
log_link_warning(link, "Could not restart DHCPv6 after enabling Information request: %s",
strerror(-r));
link->dhcp6_client =
sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
return r;
}
r = sd_dhcp6_client_new(&link->dhcp6_client);
if (r < 0)
return r;
r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0);
if (r < 0) {
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
r = sd_dhcp6_client_set_mac(link->dhcp6_client,
(const uint8_t *) &link->mac,
sizeof (link->mac), ARPHRD_ETHER);
if (r < 0) {
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex);
if (r < 0) {
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler,
link);
if (r < 0) {
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
if (event == ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER) {
r = sd_dhcp6_client_set_information_request(link->dhcp6_client,
true);
if (r < 0) {
link->dhcp6_client =
sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
}
r = sd_dhcp6_client_start(link->dhcp6_client);
if (r < 0)
link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
return r;
}
static int dhcp6_prefix_expired(Link *link) {
int r;
sd_dhcp6_lease *lease;
struct in6_addr *expired_prefix, ip6_addr;
uint8_t expired_prefixlen;
uint32_t lifetime_preferred, lifetime_valid;
r = sd_icmp6_ra_get_expired_prefix(link->icmp6_router_discovery,
&expired_prefix, &expired_prefixlen);
if (r < 0)
return r;
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &lease);
if (r < 0)
return r;
log_link_struct(link, LOG_INFO,
"MESSAGE=%-*s: IPv6 prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d expired",
IFNAMSIZ, link->ifname,
SD_ICMP6_ADDRESS_FORMAT_VAL(*expired_prefix),
expired_prefixlen, NULL);
sd_dhcp6_lease_reset_address_iter(lease);
while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
&lifetime_preferred,
&lifetime_valid) >= 0) {
r = sd_icmp6_prefix_match(expired_prefix, expired_prefixlen,
&ip6_addr);
if (r < 0)
continue;
log_link_struct(link, LOG_INFO,
"MESSAGE=%-*s: IPv6 prefix length updated "SD_ICMP6_ADDRESS_FORMAT_STR"/%d",
IFNAMSIZ, link->ifname,
SD_ICMP6_ADDRESS_FORMAT_VAL(ip6_addr), 128,
NULL);
dhcp6_address_update(link, &ip6_addr, 128, lifetime_preferred,
lifetime_valid);
}
return 0;
}
static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
Link *link = userdata;
assert(link);
assert(link->network);
assert(link->manager);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return;
switch(event) {
case ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE:
return;
case ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
case ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER:
case ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED:
dhcp6_configure(link, event);
break;
case ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED:
if (!link->rtnl_extended_attrs)
dhcp6_prefix_expired(link);
break;
default:
if (event < 0)
log_link_warning(link, "ICMPv6 error: %s",
strerror(-event));
else
log_link_warning(link, "ICMPv6 unknown event: %d",
event);
break;
}
}
int icmp6_configure(Link *link) {
int r;
assert_return(link, -EINVAL);
r = sd_icmp6_nd_new(&link->icmp6_router_discovery);
if (r < 0)
return r;
r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery, NULL, 0);
if (r < 0)
return r;
r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery, &link->mac);
if (r < 0)
return r;
r = sd_icmp6_nd_set_index(link->icmp6_router_discovery, link->ifindex);
if (r < 0)
return r;
r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery,
icmp6_router_handler, link);
return r;
}