networkd-ndisc.c revision 3b015d40c19d9338b66bf916d84dec601019c811
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek This file is part of systemd.
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek Copyright (C) 2014 Intel Corporation. All rights reserved.
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek under the terms of the GNU Lesser General Public License as published by
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek the Free Software Foundation; either version 2.1 of the License, or
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek (at your option) any later version.
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek systemd is distributed in the hope that it will be useful, but
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek WITHOUT ANY WARRANTY; without even the implied warranty of
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek Lesser General Public License for more details.
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek You should have received a copy of the GNU Lesser General Public License
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek along with systemd; If not, see <http://www.gnu.org/licenses/>.
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmekstatic int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek _cleanup_link_unref_ Link *link = userdata;
29fc0ddcd737af906986d4029579d4dfe838ba02Zbigniew Jędrzejewski-Szmek assert(link->ndisc_messages > 0);
799a8f39d8eb9ea725e85a598c0f5dbd658c8ba7Zbigniew Jędrzejewski-Szmek r = sd_netlink_message_get_errno(m);
799a8f39d8eb9ea725e85a598c0f5dbd658c8ba7Zbigniew Jędrzejewski-Szmek if (r < 0 && r != -EEXIST) {
50a0b0717563d08c027a16a896bff8d7754eab9eZbigniew Jędrzejewski-Szmek log_link_error_errno(link, r, "Could not set NDisc route or address: %m");
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek if (link->ndisc_messages == 0) {
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmekstatic void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen,
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek unsigned lifetime_preferred, unsigned lifetime_valid, void *userdata) {
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek _cleanup_address_free_ Address *address = NULL;
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek log_link_error_errno(link, r, "Could not allocate address: %m");
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek memcpy(&address->in_addr.in6 + 8, &link->network->ipv6_token + 8, 8);
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek /* see RFC4291 section 2.5.1 */
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0];
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1;
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1];
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[10] = link->mac.ether_addr_octet[2];
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[11] = 0xff;
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[12] = 0xfe;
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[13] = link->mac.ether_addr_octet[3];
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[14] = link->mac.ether_addr_octet[4];
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5];
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->flags = IFA_F_NOPREFIXROUTE;
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->cinfo.ifa_prefered = lifetime_preferred;
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek address->cinfo.ifa_valid = lifetime_valid;
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek r = address_configure(address, link, ndisc_netlink_handler, true);
d71839afd88589247d8dd42b2b09d024f521749dZbigniew Jędrzejewski-Szmek log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
d71839afd88589247d8dd42b2b09d024f521749dZbigniew Jędrzejewski-Szmekstatic void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) {
d71839afd88589247d8dd42b2b09d024f521749dZbigniew Jędrzejewski-Szmek _cleanup_route_free_ Route *route = NULL;
d71839afd88589247d8dd42b2b09d024f521749dZbigniew Jędrzejewski-Szmek if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
d71839afd88589247d8dd42b2b09d024f521749dZbigniew Jędrzejewski-Szmek log_link_error_errno(link, r, "Could not allocate route: %m");
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek route->dst_prefixlen = prefixlen;
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek route->lifetime = time_now + lifetime * USEC_PER_SEC;
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek r = route_configure(route, link, ndisc_netlink_handler);
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek log_link_warning_errno(link, r, "Could not set prefix route: %m");
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmekstatic void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) {
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek _cleanup_route_free_ Route *route = NULL;
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek r = sd_dhcp6_client_start(link->dhcp6_client);
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek if (r < 0 && r != -EALREADY)
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m");
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek log_link_error_errno(link, r, "Could not allocate route: %m");
722b6795655149a68277b3cffeba711e1d440e5aZbigniew Jędrzejewski-Szmek assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0);
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek route->lifetime = time_now + lifetime * USEC_PER_SEC;
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek r = route_configure(route, link, ndisc_netlink_handler);
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek log_link_warning_errno(link, r, "Could not set default route: %m");
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmekstatic void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek r = sd_dhcp6_client_start(link->dhcp6_client);
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek if (r < 0 && r != -EALREADY)
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m");
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmekint ndisc_configure(Link *link) {
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek r = sd_ndisc_new(&link->ndisc_router_discovery);
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek r = sd_ndisc_attach_event(link->ndisc_router_discovery, NULL, 0);
eacbb4d33e2bb5c54311544851140efe3dd0f774Zbigniew Jędrzejewski-Szmek r = sd_ndisc_set_mac(link->ndisc_router_discovery, &link->mac);
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek r = sd_ndisc_set_index(link->ndisc_router_discovery, link->ifindex);
3d090cc6f34e5970765dd1e7ee5e648a056d180dZbigniew Jędrzejewski-Szmek r = sd_ndisc_set_callback(link->ndisc_router_discovery,