LCOV - code coverage report
Current view: top level - network - networkd-dhcp6.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 449 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 23 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : /***
       3             :   Copyright © 2014 Intel Corporation. All rights reserved.
       4             : ***/
       5             : 
       6             : #include <netinet/in.h>
       7             : #include <linux/if.h>
       8             : #include <linux/if_arp.h>
       9             : #include "sd-radv.h"
      10             : 
      11             : #include "sd-dhcp6-client.h"
      12             : 
      13             : #include "hashmap.h"
      14             : #include "hostname-util.h"
      15             : #include "missing_network.h"
      16             : #include "network-internal.h"
      17             : #include "networkd-dhcp6.h"
      18             : #include "networkd-link.h"
      19             : #include "networkd-manager.h"
      20             : #include "siphash24.h"
      21             : #include "string-util.h"
      22             : #include "radv-internal.h"
      23             : 
      24             : static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
      25             : static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
      26             : static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
      27             : static int dhcp6_prefix_remove_all(Manager *m, Link *link);
      28             : 
      29           0 : static bool dhcp6_get_prefix_delegation(Link *link) {
      30           0 :         if (!link->network)
      31           0 :                 return false;
      32             : 
      33           0 :         return IN_SET(link->network->router_prefix_delegation,
      34             :                       RADV_PREFIX_DELEGATION_DHCP6,
      35             :                       RADV_PREFIX_DELEGATION_BOTH);
      36             : }
      37             : 
      38           0 : static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
      39             :         Manager *manager;
      40             :         Link *l;
      41             :         Iterator i;
      42             : 
      43           0 :         assert(dhcp6_link);
      44             : 
      45           0 :         manager = dhcp6_link->manager;
      46           0 :         assert(manager);
      47             : 
      48           0 :         HASHMAP_FOREACH(l, manager->links, i) {
      49           0 :                 if (l == dhcp6_link)
      50           0 :                         continue;
      51             : 
      52           0 :                 if (!dhcp6_get_prefix_delegation(l))
      53           0 :                         continue;
      54             : 
      55           0 :                 return true;
      56             :         }
      57             : 
      58           0 :         return false;
      59             : }
      60             : 
      61           0 : static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
      62             :                                         Link *link) {
      63           0 :         return 0;
      64             : }
      65             : 
      66           0 : static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
      67             :                                   uint8_t prefix_len,
      68             :                                   uint32_t lifetime_preferred,
      69             :                                   uint32_t lifetime_valid) {
      70           0 :         sd_radv *radv = link->radv;
      71             :         int r;
      72           0 :         _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
      73             : 
      74           0 :         r = sd_radv_prefix_new(&p);
      75           0 :         if (r < 0)
      76           0 :                 return r;
      77             : 
      78           0 :         r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
      79           0 :         if (r < 0)
      80           0 :                 return r;
      81             : 
      82           0 :         r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
      83           0 :         if (r < 0)
      84           0 :                 return r;
      85             : 
      86           0 :         r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
      87           0 :         if (r < 0)
      88           0 :                 return r;
      89             : 
      90           0 :         r = sd_radv_stop(radv);
      91           0 :         if (r < 0)
      92           0 :                 return r;
      93             : 
      94           0 :         r = sd_radv_add_prefix(radv, p, true);
      95           0 :         if (r < 0 && r != -EEXIST)
      96           0 :                 return r;
      97             : 
      98           0 :         r = dhcp6_prefix_add(link->manager, prefix, link);
      99           0 :         if (r < 0)
     100           0 :                 return r;
     101             : 
     102           0 :         return sd_radv_start(radv);
     103             : }
     104             : 
     105           0 : static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
     106             :         int r;
     107             : 
     108           0 :         assert(link);
     109             : 
     110           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     111           0 :                 return 1;
     112             : 
     113           0 :         r = sd_netlink_message_get_errno(m);
     114           0 :         if (r < 0)
     115           0 :                 log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnet: %m");
     116             : 
     117           0 :         return 1;
     118             : }
     119             : 
     120           0 : int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
     121             :         int r;
     122             :         sd_dhcp6_lease *lease;
     123             :         union in_addr_union pd_prefix;
     124             :         uint8_t pd_prefix_len;
     125             :         uint32_t lifetime_preferred, lifetime_valid;
     126             : 
     127           0 :         r = sd_dhcp6_client_get_lease(client, &lease);
     128           0 :         if (r < 0)
     129           0 :                 return r;
     130             : 
     131           0 :         sd_dhcp6_lease_reset_pd_prefix_iter(lease);
     132             : 
     133           0 :         while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
     134             :                                      &lifetime_preferred,
     135             :                                      &lifetime_valid) >= 0) {
     136           0 :                 _cleanup_free_ char *buf = NULL;
     137             :                 Route *route;
     138             : 
     139           0 :                 if (pd_prefix_len >= 64)
     140           0 :                         continue;
     141             : 
     142           0 :                 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
     143             : 
     144           0 :                 r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, NULL, 0, 0, 0, &route);
     145           0 :                 if (r < 0) {
     146           0 :                         log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
     147             :                                                strnull(buf),
     148             :                                                pd_prefix_len);
     149           0 :                         continue;
     150             :                 }
     151             : 
     152           0 :                 route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
     153             : 
     154           0 :                 r = route_remove(route, link, dhcp6_route_remove_handler);
     155           0 :                 if (r < 0) {
     156           0 :                         log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
     157             :                                                strnull(buf),
     158             :                                                pd_prefix_len);
     159           0 :                         continue;
     160             :                 }
     161             : 
     162           0 :                 log_link_debug(link, "Removing unreachable route %s/%u",
     163             :                                strnull(buf), pd_prefix_len);
     164             :         }
     165             : 
     166           0 :         return 0;
     167             : }
     168             : 
     169           0 : static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
     170             :                                       struct in6_addr *pd_prefix,
     171             :                                       uint8_t pd_prefix_len,
     172             :                                       uint32_t lifetime_preferred,
     173             :                                       uint32_t lifetime_valid) {
     174             :         Link *link;
     175           0 :         Manager *manager = dhcp6_link->manager;
     176             :         union in_addr_union prefix;
     177           0 :         uint64_t n_prefixes, n_used = 0;
     178           0 :         _cleanup_free_ char *buf = NULL;
     179           0 :         _cleanup_free_ char *assigned_buf = NULL;
     180             :         int r;
     181             : 
     182           0 :         assert(manager);
     183           0 :         assert(pd_prefix_len <= 64);
     184             : 
     185           0 :         prefix.in6 = *pd_prefix;
     186             : 
     187           0 :         r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
     188           0 :         if (r < 0)
     189           0 :                 return r;
     190             : 
     191           0 :         n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
     192             : 
     193           0 :         (void) in_addr_to_string(AF_INET6, &prefix, &buf);
     194           0 :         log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
     195             :                        n_prefixes, strnull(buf), pd_prefix_len);
     196             : 
     197           0 :         while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
     198             :                 Link *assigned_link;
     199             : 
     200           0 :                 if (n_used == n_prefixes) {
     201           0 :                         log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
     202             :                                        n_used, n_prefixes, strnull(buf), pd_prefix_len);
     203             : 
     204           0 :                         return -EAGAIN;
     205             :                 }
     206             : 
     207           0 :                 if (link == dhcp6_link)
     208           0 :                         continue;
     209             : 
     210           0 :                 if (!dhcp6_get_prefix_delegation(link))
     211           0 :                         continue;
     212             : 
     213           0 :                 assigned_link = dhcp6_prefix_get(manager, &prefix.in6);
     214           0 :                 if (assigned_link && assigned_link != link)
     215           0 :                         continue;
     216             : 
     217           0 :                 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
     218           0 :                 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
     219             :                                            lifetime_preferred, lifetime_valid);
     220           0 :                 if (r < 0) {
     221           0 :                         log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
     222             :                                              assigned_link ? "update": "assign",
     223             :                                              strnull(assigned_buf),
     224             :                                              strnull(buf), pd_prefix_len);
     225             : 
     226           0 :                         if (!assigned_link)
     227           0 :                                 continue;
     228             : 
     229             :                 } else
     230           0 :                         log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
     231             :                                        n_used + 1, n_prefixes,
     232             :                                        strnull(assigned_buf),
     233             :                                        strnull(buf), pd_prefix_len);
     234             : 
     235           0 :                 n_used++;
     236             : 
     237           0 :                 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
     238           0 :                 if (r < 0 && n_used < n_prefixes)
     239           0 :                         return r;
     240             :         }
     241             : 
     242           0 :         return 0;
     243             : }
     244             : 
     245           0 : static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
     246             :         int r;
     247             : 
     248           0 :         assert(link);
     249             : 
     250           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     251           0 :                 return 1;
     252             : 
     253           0 :         r = sd_netlink_message_get_errno(m);
     254           0 :         if (r < 0 && r != -EEXIST)
     255           0 :                 log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
     256             : 
     257           0 :         return 1;
     258             : }
     259             : 
     260           0 : static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
     261             :         int r;
     262             :         sd_dhcp6_lease *lease;
     263             :         union in_addr_union pd_prefix;
     264             :         uint8_t pd_prefix_len;
     265             :         uint32_t lifetime_preferred, lifetime_valid;
     266           0 :         Iterator i = ITERATOR_FIRST;
     267             : 
     268           0 :         r = sd_dhcp6_client_get_lease(client, &lease);
     269           0 :         if (r < 0)
     270           0 :                 return r;
     271             : 
     272           0 :         sd_dhcp6_lease_reset_pd_prefix_iter(lease);
     273             : 
     274           0 :         while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
     275             :                                      &lifetime_preferred,
     276             :                                      &lifetime_valid) >= 0) {
     277             : 
     278           0 :                 _cleanup_free_ char *buf = NULL;
     279             : 
     280           0 :                 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
     281             : 
     282           0 :                 if (pd_prefix_len > 64) {
     283           0 :                         log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
     284             :                                        strnull(buf), pd_prefix_len);
     285           0 :                         continue;
     286             :                 }
     287             : 
     288           0 :                 if (pd_prefix_len < 48)
     289           0 :                         log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
     290             :                                        strnull(buf), pd_prefix_len);
     291             : 
     292           0 :                 if (pd_prefix_len < 64) {
     293             :                         uint32_t table;
     294             :                         Route *route;
     295             : 
     296           0 :                         table = link_get_dhcp_route_table(link);
     297             : 
     298           0 :                         r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, NULL, 0, 0, table, &route);
     299           0 :                         if (r < 0) {
     300           0 :                                 log_link_warning_errno(link, r, "Failed to add unreachable route for DHCPv6 delegated subnet %s/%u: %m",
     301             :                                                        strnull(buf),
     302             :                                                        pd_prefix_len);
     303           0 :                                 continue;
     304             :                         }
     305             : 
     306           0 :                         route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
     307             : 
     308           0 :                         r = route_configure(route, link, dhcp6_route_handler);
     309           0 :                         if (r < 0) {
     310           0 :                                 log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
     311             :                                                        strnull(buf),
     312             :                                                        pd_prefix_len);
     313           0 :                                 continue;
     314             :                         }
     315             : 
     316           0 :                         log_link_debug(link, "Configuring unreachable route for %s/%u",
     317             :                                        strnull(buf), pd_prefix_len);
     318             :                 } else
     319           0 :                         log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
     320             : 
     321           0 :                 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
     322             :                                                pd_prefix_len,
     323             :                                                lifetime_preferred,
     324             :                                                lifetime_valid);
     325           0 :                 if (r < 0 && r != -EAGAIN)
     326           0 :                         return r;
     327             : 
     328           0 :                 if (r >= 0)
     329           0 :                         i = ITERATOR_FIRST;
     330             :         }
     331             : 
     332           0 :         return 0;
     333             : }
     334             : 
     335           0 : int dhcp6_request_prefix_delegation(Link *link) {
     336             :         Link *l;
     337             :         Iterator i;
     338             : 
     339           0 :         assert_return(link, -EINVAL);
     340           0 :         assert_return(link->manager, -EOPNOTSUPP);
     341             : 
     342           0 :         if (dhcp6_get_prefix_delegation(link) <= 0)
     343           0 :                 return 0;
     344             : 
     345           0 :         log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
     346             : 
     347           0 :         HASHMAP_FOREACH(l, link->manager->links, i) {
     348             :                 int r, enabled;
     349             : 
     350           0 :                 if (l == link)
     351           0 :                         continue;
     352             : 
     353           0 :                 if (!l->dhcp6_client)
     354           0 :                         continue;
     355             : 
     356           0 :                 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
     357           0 :                 if (r < 0) {
     358           0 :                         log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
     359           0 :                         continue;
     360             :                 }
     361             : 
     362           0 :                 if (enabled == 0) {
     363           0 :                         r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
     364           0 :                         if (r < 0) {
     365           0 :                                 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
     366           0 :                                 continue;
     367             :                         }
     368             :                 }
     369             : 
     370           0 :                 r = sd_dhcp6_client_is_running(l->dhcp6_client);
     371           0 :                 if (r <= 0)
     372           0 :                         continue;
     373             : 
     374           0 :                 if (enabled != 0) {
     375           0 :                         log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
     376           0 :                         (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
     377             : 
     378           0 :                         continue;
     379             :                 }
     380             : 
     381           0 :                 r = sd_dhcp6_client_stop(l->dhcp6_client);
     382           0 :                 if (r < 0) {
     383           0 :                         log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
     384           0 :                         continue;
     385             :                 }
     386             : 
     387           0 :                 r = sd_dhcp6_client_start(l->dhcp6_client);
     388           0 :                 if (r < 0) {
     389           0 :                         log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
     390           0 :                         continue;
     391             :                 }
     392             : 
     393           0 :                 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
     394             :         }
     395             : 
     396           0 :         return 0;
     397             : }
     398             : 
     399           0 : static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
     400             :         int r;
     401             : 
     402           0 :         assert(link);
     403             : 
     404           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     405           0 :                 return 1;
     406             : 
     407           0 :         r = sd_netlink_message_get_errno(m);
     408           0 :         if (r < 0 && r != -EEXIST) {
     409           0 :                 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
     410           0 :                 link_enter_failed(link);
     411           0 :                 return 1;
     412           0 :         } else if (r >= 0)
     413           0 :                 (void) manager_rtnl_process_address(rtnl, m, link->manager);
     414             : 
     415           0 :         r = link_request_set_routes(link);
     416           0 :         if (r < 0) {
     417           0 :                 link_enter_failed(link);
     418           0 :                 return 1;
     419             :         }
     420             : 
     421           0 :         return 1;
     422             : }
     423             : 
     424           0 : static int dhcp6_address_change(
     425             :                 Link *link,
     426             :                 struct in6_addr *ip6_addr,
     427             :                 uint32_t lifetime_preferred,
     428             :                 uint32_t lifetime_valid) {
     429             : 
     430           0 :         _cleanup_(address_freep) Address *addr = NULL;
     431           0 :         _cleanup_free_ char *buffer = NULL;
     432             :         int r;
     433             : 
     434           0 :         r = address_new(&addr);
     435           0 :         if (r < 0)
     436           0 :                 return r;
     437             : 
     438           0 :         addr->family = AF_INET6;
     439           0 :         addr->in_addr.in6 = *ip6_addr;
     440           0 :         addr->flags = IFA_F_NOPREFIXROUTE;
     441           0 :         addr->prefixlen = 128;
     442           0 :         addr->cinfo.ifa_prefered = lifetime_preferred;
     443           0 :         addr->cinfo.ifa_valid = lifetime_valid;
     444             : 
     445           0 :         (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
     446           0 :         log_link_info(link,
     447             :                       "DHCPv6 address %s/%d timeout preferred %d valid %d",
     448             :                       strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
     449             : 
     450           0 :         r = address_configure(addr, link, dhcp6_address_handler, true);
     451           0 :         if (r < 0)
     452           0 :                 return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
     453             : 
     454           0 :         return 0;
     455             : }
     456             : 
     457           0 : static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
     458             :         int r;
     459             :         sd_dhcp6_lease *lease;
     460             :         struct in6_addr ip6_addr;
     461             :         uint32_t lifetime_preferred, lifetime_valid;
     462             : 
     463           0 :         r = sd_dhcp6_client_get_lease(client, &lease);
     464           0 :         if (r < 0)
     465           0 :                 return r;
     466             : 
     467           0 :         sd_dhcp6_lease_reset_address_iter(lease);
     468             : 
     469           0 :         while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
     470             :                                                  &lifetime_preferred,
     471             :                                                  &lifetime_valid) >= 0) {
     472             : 
     473           0 :                 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
     474           0 :                 if (r < 0)
     475           0 :                         return r;
     476             :         }
     477             : 
     478           0 :         return 0;
     479             : }
     480             : 
     481           0 : static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
     482             :         int r;
     483           0 :         Link *link = userdata;
     484             : 
     485           0 :         assert(link);
     486           0 :         assert(link->network);
     487             : 
     488           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     489           0 :                 return;
     490             : 
     491           0 :         switch(event) {
     492           0 :         case SD_DHCP6_CLIENT_EVENT_STOP:
     493             :         case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
     494             :         case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
     495           0 :                 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
     496           0 :                         log_link_warning(link, "DHCPv6 lease lost");
     497             : 
     498           0 :                 (void) dhcp6_lease_pd_prefix_lost(client, link);
     499           0 :                 (void) dhcp6_prefix_remove_all(link->manager, link);
     500             : 
     501           0 :                 link_dirty(link);
     502           0 :                 link->dhcp6_configured = false;
     503           0 :                 break;
     504             : 
     505           0 :         case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
     506           0 :                 r = dhcp6_lease_address_acquired(client, link);
     507           0 :                 if (r < 0) {
     508           0 :                         link_enter_failed(link);
     509           0 :                         return;
     510             :                 }
     511             : 
     512           0 :                 r = dhcp6_lease_pd_prefix_acquired(client, link);
     513           0 :                 if (r < 0)
     514           0 :                         log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
     515             : 
     516             :                 _fallthrough_;
     517             :         case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
     518           0 :                 r = dhcp6_lease_information_acquired(client, link);
     519           0 :                 if (r < 0) {
     520           0 :                         link_enter_failed(link);
     521           0 :                         return;
     522             :                 }
     523             : 
     524           0 :                 link_dirty(link);
     525           0 :                 link->dhcp6_configured = true;
     526           0 :                 break;
     527             : 
     528           0 :         default:
     529           0 :                 if (event < 0)
     530           0 :                         log_link_warning_errno(link, event, "DHCPv6 error: %m");
     531             :                 else
     532           0 :                         log_link_warning(link, "DHCPv6 unknown event: %d", event);
     533           0 :                 return;
     534             :         }
     535             : 
     536           0 :         link_check_ready(link);
     537             : }
     538             : 
     539           0 : int dhcp6_request_address(Link *link, int ir) {
     540             :         int r, inf_req, pd;
     541             :         bool running;
     542             : 
     543           0 :         assert(link);
     544           0 :         assert(link->dhcp6_client);
     545           0 :         assert(link->network);
     546           0 :         assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
     547             : 
     548           0 :         r = sd_dhcp6_client_is_running(link->dhcp6_client);
     549           0 :         if (r < 0)
     550           0 :                 return r;
     551             :         else
     552           0 :                 running = r;
     553             : 
     554           0 :         r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
     555           0 :         if (r < 0)
     556           0 :                 return r;
     557             : 
     558           0 :         if (pd && ir && link->network->dhcp6_force_pd_other_information) {
     559           0 :                 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
     560             : 
     561           0 :                 r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
     562             :                                                         false);
     563           0 :                 if (r < 0 )
     564           0 :                         return r;
     565             : 
     566           0 :                 ir = false;
     567             :         }
     568             : 
     569           0 :         if (running) {
     570           0 :                 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
     571           0 :                 if (r < 0)
     572           0 :                         return r;
     573             : 
     574           0 :                 if (inf_req == ir)
     575           0 :                         return 0;
     576             : 
     577           0 :                 r = sd_dhcp6_client_stop(link->dhcp6_client);
     578           0 :                 if (r < 0)
     579           0 :                         return r;
     580             :         } else {
     581           0 :                 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
     582           0 :                 if (r < 0)
     583           0 :                         return r;
     584             :         }
     585             : 
     586           0 :         r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
     587           0 :         if (r < 0)
     588           0 :                 return r;
     589             : 
     590           0 :         r = sd_dhcp6_client_start(link->dhcp6_client);
     591           0 :         if (r < 0)
     592           0 :                 return r;
     593             : 
     594           0 :         return 0;
     595             : }
     596             : 
     597           0 : static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
     598           0 :         _cleanup_free_ char *hostname = NULL;
     599             :         const char *hn;
     600             :         int r;
     601             : 
     602           0 :         assert(link);
     603             : 
     604           0 :         if (!link->network->dhcp_send_hostname)
     605           0 :                 hn = NULL;
     606           0 :         else if (link->network->dhcp_hostname)
     607           0 :                 hn = link->network->dhcp_hostname;
     608             :         else {
     609           0 :                 r = gethostname_strict(&hostname);
     610           0 :                 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
     611           0 :                         return r;
     612             : 
     613           0 :                 hn = hostname;
     614             :         }
     615             : 
     616           0 :         r = sd_dhcp6_client_set_fqdn(client, hn);
     617           0 :         if (r == -EINVAL && hostname)
     618             :                 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
     619           0 :                 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
     620           0 :         else if (r < 0)
     621           0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
     622             : 
     623           0 :         return 0;
     624             : }
     625             : 
     626           0 : int dhcp6_configure(Link *link) {
     627           0 :         _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
     628             :         const DUID *duid;
     629             :         int r;
     630             : 
     631           0 :         assert(link);
     632           0 :         assert(link->network);
     633             : 
     634           0 :         if (link->dhcp6_client)
     635           0 :                 return 0;
     636             : 
     637           0 :         r = sd_dhcp6_client_new(&client);
     638           0 :         if (r == -ENOMEM)
     639           0 :                 return log_oom();
     640           0 :         if (r < 0)
     641           0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
     642             : 
     643           0 :         r = sd_dhcp6_client_attach_event(client, NULL, 0);
     644           0 :         if (r < 0)
     645           0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
     646             : 
     647           0 :         r = sd_dhcp6_client_set_mac(client,
     648           0 :                                     (const uint8_t *) &link->mac,
     649             :                                     sizeof (link->mac), ARPHRD_ETHER);
     650           0 :         if (r < 0)
     651           0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
     652             : 
     653           0 :         if (link->network->iaid_set) {
     654           0 :                 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
     655           0 :                 if (r < 0)
     656           0 :                         return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
     657             :         }
     658             : 
     659           0 :         duid = link_get_duid(link);
     660           0 :         if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
     661           0 :                 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
     662             :         else
     663           0 :                 r = sd_dhcp6_client_set_duid(client,
     664           0 :                                              duid->type,
     665           0 :                                              duid->raw_data_len > 0 ? duid->raw_data : NULL,
     666           0 :                                              duid->raw_data_len);
     667           0 :         if (r < 0)
     668           0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
     669             : 
     670           0 :         r = dhcp6_set_hostname(client, link);
     671           0 :         if (r < 0)
     672           0 :                 return r;
     673             : 
     674           0 :         r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
     675           0 :         if (r < 0)
     676           0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
     677             : 
     678           0 :         if (link->network->rapid_commit) {
     679           0 :                 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
     680           0 :                 if (r < 0)
     681           0 :                         return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
     682             :         }
     683             : 
     684           0 :         r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
     685           0 :         if (r < 0)
     686           0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
     687             : 
     688           0 :         if (dhcp6_enable_prefix_delegation(link)) {
     689           0 :                 r = sd_dhcp6_client_set_prefix_delegation(client, true);
     690           0 :                 if (r < 0)
     691           0 :                         return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
     692             :         }
     693             : 
     694           0 :         link->dhcp6_client = TAKE_PTR(client);
     695             : 
     696           0 :         return 0;
     697             : }
     698             : 
     699           0 : static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
     700           0 :         assert_return(m, NULL);
     701           0 :         assert_return(addr, NULL);
     702             : 
     703           0 :         return hashmap_get(m->dhcp6_prefixes, addr);
     704             : }
     705             : 
     706           0 : static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
     707             :         int r;
     708             : 
     709           0 :         assert(link);
     710             : 
     711           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     712           0 :                 return 1;
     713             : 
     714           0 :         r = sd_netlink_message_get_errno(m);
     715           0 :         if (r < 0 && r != -EEXIST) {
     716           0 :                 log_link_debug_errno(link, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
     717           0 :                 link_enter_failed(link);
     718           0 :                 return 1;
     719             :         }
     720             : 
     721           0 :         return 1;
     722             : }
     723             : 
     724           0 : static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
     725           0 :         _cleanup_free_ struct in6_addr *a = NULL;
     726           0 :         _cleanup_free_ char *buf = NULL;
     727             :         Link *assigned_link;
     728             :         Route *route;
     729             :         int r;
     730             : 
     731           0 :         assert_return(m, -EINVAL);
     732           0 :         assert_return(addr, -EINVAL);
     733             : 
     734           0 :         r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
     735             :                       NULL, 0, 0, 0, &route);
     736           0 :         if (r < 0)
     737           0 :                 return r;
     738             : 
     739           0 :         r = route_configure(route, link, dhcp6_route_add_handler);
     740           0 :         if (r < 0)
     741           0 :                 return r;
     742             : 
     743           0 :         (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
     744           0 :         log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
     745             : 
     746           0 :         assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
     747           0 :         if (assigned_link) {
     748           0 :                 assert(assigned_link == link);
     749           0 :                 return 0;
     750             :         }
     751             : 
     752           0 :         a = newdup(struct in6_addr, addr, 1);
     753           0 :         if (!a)
     754           0 :                 return -ENOMEM;
     755             : 
     756           0 :         r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
     757           0 :         if (r < 0)
     758           0 :                 return r;
     759             : 
     760           0 :         r = hashmap_put(m->dhcp6_prefixes, a, link);
     761           0 :         if (r < 0)
     762           0 :                 return r;
     763             : 
     764           0 :         TAKE_PTR(a);
     765           0 :         link_ref(link);
     766           0 :         return 0;
     767             : }
     768             : 
     769           0 : static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
     770             :         int r;
     771             : 
     772           0 :         assert(link);
     773             : 
     774           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     775           0 :                 return 1;
     776             : 
     777           0 :         r = sd_netlink_message_get_errno(m);
     778           0 :         if (r < 0) {
     779           0 :                 log_link_debug_errno(link, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
     780           0 :                 link_enter_failed(link);
     781           0 :                 return 1;
     782             :         }
     783             : 
     784           0 :         return 1;
     785             : }
     786             : 
     787           0 : int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
     788           0 :         _cleanup_free_ struct in6_addr *a = NULL;
     789           0 :         _cleanup_(link_unrefp) Link *l = NULL;
     790           0 :         _cleanup_free_ char *buf = NULL;
     791             :         Route *route;
     792             :         int r;
     793             : 
     794           0 :         assert_return(m, -EINVAL);
     795           0 :         assert_return(addr, -EINVAL);
     796             : 
     797           0 :         l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
     798           0 :         if (!l)
     799           0 :                 return -EINVAL;
     800             : 
     801           0 :         (void) sd_radv_remove_prefix(l->radv, addr, 64);
     802           0 :         r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, NULL, 0, 0, 0, &route);
     803           0 :         if (r < 0)
     804           0 :                 return r;
     805             : 
     806           0 :         r = route_remove(route, l, dhcp6_prefix_remove_handler);
     807           0 :         if (r < 0)
     808           0 :                 return r;
     809             : 
     810           0 :         (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
     811           0 :         log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
     812             : 
     813           0 :         return 0;
     814             : }
     815             : 
     816           0 : static int dhcp6_prefix_remove_all(Manager *m, Link *link) {
     817             :         struct in6_addr *addr;
     818             :         Iterator i;
     819             :         Link *l;
     820             : 
     821           0 :         assert_return(m, -EINVAL);
     822           0 :         assert_return(link, -EINVAL);
     823             : 
     824           0 :         HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
     825           0 :                 if (l == link)
     826           0 :                         (void) dhcp6_prefix_remove(m, addr);
     827             : 
     828           0 :         return 0;
     829             : }

Generated by: LCOV version 1.14